PyTorch深度学习实战:构建神经网络模型(上)
本文继PyTorch深度学习实战:数据读取,基于时间序列预测 Autoformer 源码深入研习深度学习流程管道进行拆解,按照从数据获取与处理,构建模型,模型训练和预测以及其他的一些模块的顺序进行深入研习。
一般情况下,可以使用torch.nn包来构建神经网络。即使用 PyTorch 构建神经网络的一种简单方法是创建一个继承自
torch.nn.Module
的类。这里将
nn.Module
子类化(它本身是一个类并且能够跟踪状态)。在这种情况下,我们要创建一个类,该类包含初始化模型和向前传播forward步骤的。nn.Module
具有许多我们将要使用的属性和方法(例如.parameters()和.zero_grad()
)。classNet(nn.Module):
"""
示意函数
"""
def__init__(self, n_features):
super(Net, self).__init__()
self.encoder = Encoder()
self.decoder = Decoder()
self.enc_embedding = DataEmbedding_wo_pos()
self.dec_embedding = DataEmbedding_wo_pos()
defforward(self, x_enc,x_dec,x_mark_enc,x_mark_dec):
enc_out = self.enc_embedding(x_enc, x_mark_enc)
enc_out = self.encoder(enc_out)
dec_out = self.dec_embedding(x_dec, x_mark_dec)
seasonal_part, trend_part = self.decoder(dec_out, enc_out)
return seasonal_part + trend_part
我们只需要定义
forward
函数,backward
函数会在使用 autograd
时自动定义,backward
函数用来计算导数。我们可以在 forward
函数中使用任何针对张量的操作和计算。Autoformer.py
接下来,一步一步地深入研究Autoformer模型架构构建方法与细节。
上面是Autoformer模型的整体架构,首先先从宏观角度看下该架构的情况,并从那个Autoformer.py模型的完整代码中理论结合实践学习。
首先导入我们所需要的python库。
# Autoformer.py
import torch
import torch.nn as nn
import torch.nn.functional as F
from layers.Embed import DataEmbedding, DataEmbedding_wo_pos
from layers.AutoCorrelation import AutoCorrelation, AutoCorrelationLayer
from layers.Autoformer_EncDec import Encoder, Decoder, EncoderLayer, DecoderLayer, my_Layernorm, series_decomp
import math
import numpy as np
其中,以
layers
为首的网络层结构,是本次Autoformer模型中网络架构层,等下在后面会详细介绍。这里我们把它当作一个已知(已完成)的神经网络模型。初始化模型
首先是初始化该模型,定义模型所需要的几本参数
self.seq_len
,self.label_len
,self.pred_len
,然后定义序列分解单元结构self.decomp
。定义embedding
层,使用DataEmbedding_wo_pos()
方法分别定义编码嵌入层self.enc_embedding
和解码嵌入层self.dec_embedding
。接下来就是定义编码器
self.encoder
和解码器 self.decoder
# Autoformer.py 删减
classModel(nn.Module):
"""
Autoformer is the first method to achieve the series-wise connection,
with inherent O(LlogL) complexity
"""
def__init__(self, configs):
super(Model, self).__init__()
self.seq_len = configs.seq_len # 96
self.label_len = configs.label_len # 48
self.pred_len = configs.pred_len # 24
self.output_attention = configs.output_attention # True
# Decomp
kernel_size = configs.moving_avg # default=25 'window size of moving average'
self.decomp = series_decomp(kernel_size)
# Embedding
# The series-wise connection inherently contains the sequential information.
# Thus, we can discard the position embedding of transformers.
self.enc_embedding = DataEmbedding_wo_pos(...)
self.dec_embedding = DataEmbedding_wo_pos(...)
# Encoder
self.encoder = Encoder(...)
# Decoder
self.decoder = Decoder(...)
向前传播 forward
接下来就是定义向前传播函数
forward()
编写向前传播函数,需要大致了解一下 Autoformer 架构
# Autoformer.py
classModel(nn.Module):
defforward(self, x_enc, # [32, 96, 7]
x_mark_enc, # [32, 96, 4]
x_dec, # [32, 72, 7] --> cat[0:lable_len , -pred_len:] dim=1 48 + 24
x_mark_dec, # [32, 72, 4]
enc_self_mask=None, dec_self_mask=None, dec_enc_mask=None):
# 首先初始化序列分解 [32, 7] -> [32,1,7] -> [32,24,7]
mean = torch.mean(x_enc, dim=1).unsqueeze(1).repeat(1, self.pred_len, 1)
# unsqueeze()返回一个插入到指定位置的尺寸为 1 的新张量。
zeros = torch.zeros([x_dec.shape[0], self.pred_len, x_dec.shape[2]],
device=x_enc.device) # [32,24,7]
seasonal_init, trend_init = self.decomp(x_enc) # [32,96,7], [32,96,7]
# decoder input
# start token length
trend_init = torch.cat([trend_init[:, -self.label_len:, :], mean], dim=1) # [32,48+24,7]
seasonal_init = torch.cat([seasonal_init[:, -self.label_len:, :], zeros], dim=1) # [32,48+24,7]
# enc
enc_out = self.enc_embedding(x_enc, x_mark_enc)
enc_out, attns = self.encoder(enc_out, attn_mask=enc_self_mask)
# dec
dec_out = self.dec_embedding(seasonal_init, x_mark_dec)
seasonal_part, trend_part = self.decoder(dec_out, enc_out,
x_mask=dec_self_mask,
cross_mask=dec_enc_mask,
trend=trend_init)
# final
dec_out = trend_part + seasonal_part
if self.output_attention:
return dec_out[:, -self.pred_len:, :], attns
else:
return dec_out[:, -self.pred_len:, :] # [B, L, D]
模型输入
研究完Autoformer模型(初始化模型__init__、forward两个函数)的源码后,继续深入。
模型输入分为两个部分:
- 编码器Encoder输入输入历史时间序列,即过去时间段的
- 解码器Decoder输入其输入包括趋势项和季节项两个部分。
其中趋势项和季节项均是由两部分组成,第一部分都是历史时间序列后半部分 经过序列分解单元分解出来的趋势项和季节项作为初始化,这样做的目的是获取最近的信息,它是抽取编码器输入 中倒数长度为 的信息。
序列分解单元输出包括季节性和趋势性两个部分。除此之外,趋势项的另一部分是用标量0 填充占位,即目前尚不知道的未来时间序列趋势项。季节项的另一部分是用历史时间序列均值 填充。
用代码实现即为向前传播函数
forward()
中的。# Autoformer.py forward 稍作修改
seasonal_init, trend_init = self.decomp(x_enc) # x_enc: X_en [32,96,7], [32,96,7]
X_ent = trend_init[:, -self.label_len:, :]
X_ens = seasonal_init[:, -self.label_len:, :]
X_0 = torch.zeros([x_dec.shape[0], self.pred_len, x_dec.shape[2]], device=x_enc.device)
X_mean = torch.mean(x_enc, dim=1).unsqueeze(1).repeat(1, self.pred_len, 1)
Embedding
数据经过输入层后,需要对其进行编码。而Autoformer使用的embedding方法是
TokenEmbedding
及TemporalEmbedding
,由于序列中已经包含了顺序信息,因此位置编码可以省略。# Embed.py
classDataEmbedding_wo_pos(nn.Module):
def__init__(self, c_in, d_model, embed_type='fixed', freq='h', dropout=0.1):
super(DataEmbedding_wo_pos, self).__init__()
self.value_embedding = TokenEmbedding(c_in=c_in, d_model=d_model)
# self.position_embedding = PositionalEmbedding(d_model=d_model)
self.temporal_embedding = TemporalEmbedding(d_model=d_model, embed_type=embed_type,
freq=freq) if embed_type != 'timeF'else TimeFeatureEmbedding(
d_model=d_model, embed_type=embed_type, freq=freq)
self.dropout = nn.Dropout(p=dropout)
defforward(self, x, x_mark):
x = self.value_embedding(x) + self.temporal_embedding(x_mark)
return self.dropout(x)
接下来,我们一起看下TokenEmbedding及TemporalEmbedding两种embedding的源码,看看其实现逻辑。
TokenEmbedding
从源码可以看出TokenEmbedding是使用一个3*3的卷积核进行卷积计算。
# Embed.py
classTokenEmbedding(nn.Module):
def__init__(self, c_in, d_model):
super(TokenEmbedding, self).__init__()
padding = 1if torch.__version__ >= '1.5.0'else2
self.tokenConv = nn.Conv1d(in_channels=c_in, out_channels=d_model,
kernel_size=3, padding=padding,
padding_mode='circular', bias=False)
for m in self.modules():
if isinstance(m, nn.Conv1d):
nn.init.kaiming_normal_(m.weight, mode='fan_in',
nonlinearity='leaky_relu')
defforward(self, x):
x = self.tokenConv(x.permute(0, 2, 1)).transpose(1, 2)
return x
TemporalEmbedding
从源码可以看出针对于时间序列数据,月日周小时四个特征分别进行embedding,最后将其使用加法组合。其中如果embedding方法选用预设混合模式,则使用类似于transformer的位置编码,使用正余弦值作为embedding的权重。
# Embed.py
classTemporalEmbedding(nn.Module):
def__init__(self, d_model, embed_type='fixed', freq='h'):
super(TemporalEmbedding, self).__init__()
minute_size = 4
hour_size = 24
weekday_size = 7
day_size = 32
month_size = 13
Embed = FixedEmbedding if embed_type == 'fixed'else nn.Embedding
if freq == 't':
self.minute_embed = Embed(minute_size, d_model)
self.hour_embed = Embed(hour_size, d_model)
self.weekday_embed = Embed(weekday_size, d_model)
self.day_embed = Embed(day_size, d_model)
self.month_embed = Embed(month_size, d_model)
defforward(self, x):
x = x.long()
minute_x = self.minute_embed(x[:, :, 4]) if hasattr(self, 'minute_embed') else0.
hour_x = self.hour_embed(x[:, :, 3])
weekday_x = self.weekday_embed(x[:, :, 2])
day_x = self.day_embed(x[:, :, 1])
month_x = self.month_embed(x[:, :, 0])
return hour_x + weekday_x + day_x + month_x + minute_x
classFixedEmbedding(nn.Module):
def__init__(self, c_in, d_model):
super(FixedEmbedding, self).__init__()
w = torch.zeros(c_in, d_model).float()
w.require_grad = False
position = torch.arange(0, c_in).float().unsqueeze(1)
div_term = (torch.arange(0, d_model, 2).float() * -(math.log(10000.0) / d_model)).exp()
w[:, 0::2] = torch.sin(position * div_term)
w[:, 1::2] = torch.cos(position * div_term)
self.emb = nn.Embedding(c_in, d_model)
self.emb.weight = nn.Parameter(w, requires_grad=False)
defforward(self, x):
return self.emb(x).detach()
到这里,我们对于
embedding
层的细节源码已经熟悉了,现在回到宏观层面查看源码 Autoformer.py 中是如和使用的:# Autoformer.py 提取
def__init__(self):
super(Model, self).__init__()
self.enc_embedding = DataEmbedding_wo_pos(configs.enc_in, configs.d_model,
configs.embed, configs.freq, configs.dropout)
self.dec_embedding = DataEmbedding_wo_pos(configs.dec_in, configs.d_model,
configs.embed, configs.freq, configs.dropout)
defforward(self, x_enc, x_mark_enc, x_dec, x_mark_dec)
enc_out = self.enc_embedding(x_enc, x_mark_enc) # [32,96,512]
dec_out = self.dec_embedding(seasonal_init, x_mark_dec)
[未完待续...]
构建神经网络模型下部分主要包括模型架构架构部分详细介绍 --> series_decomp 编码器 解码器 自相关机制。敬请期待!
参考资料
https://zhuanlan.zhihu.com/p/385066440
[2]https://arxiv.org/abs/2106.13008
🏴☠️宝藏级🏴☠️原创公众号『机器学习研习院』,公众号专注分享机器学习|深度学习领域的原创文章,一起研习,共同进步!
长按👇关注- 机器学习研习院 -设为星标,干货速递
关键词
模型
函数
方法
部分
编码
最新评论
推荐文章
作者最新文章
你可能感兴趣的文章
Copyright Disclaimer: The copyright of contents (including texts, images, videos and audios) posted above belong to the User who shared or the third-party website which the User shared from. If you found your copyright have been infringed, please send a DMCA takedown notice to [email protected]. For more detail of the source, please click on the button "Read Original Post" below. For other communications, please send to [email protected].
版权声明:以上内容为用户推荐收藏至CareerEngine平台,其内容(含文字、图片、视频、音频等)及知识版权均属用户或用户转发自的第三方网站,如涉嫌侵权,请通知[email protected]进行信息删除。如需查看信息来源,请点击“查看原文”。如需洽谈其它事宜,请联系[email protected]。
版权声明:以上内容为用户推荐收藏至CareerEngine平台,其内容(含文字、图片、视频、音频等)及知识版权均属用户或用户转发自的第三方网站,如涉嫌侵权,请通知[email protected]进行信息删除。如需查看信息来源,请点击“查看原文”。如需洽谈其它事宜,请联系[email protected]。