LSTM 的备胎,⽤卷积处理时间序列——TCN 与因果卷积(理论+Python 实践)什么是TCN
TCN全称Temporal Convolutional Network,时序卷积⽹络,是在2018年提出的⼀个卷积模型,但是可以⽤来处理时间序列。卷积如何处理时间序列
时间序列预测,最容易想到的就是那个马尔可夫模型:
就是计算某⼀个时刻的输出值,已知条件就是这个时刻之前的所有特征值。上⾯公式中,P表⽰概率,可以不⽤管这个,表⽰k时刻的输出值(标签),表⽰k时刻的特征值。
如果使⽤LSTM或者是GRU这样的RNN模型,⾃然是可以处理这样的时间序列模型的,毕竟RNN⽣来就是为了这个的。
但是这个时间序列模型,宏观上思考的话,其实就是对这个这个时刻之前的数据做某个操作,然后⽣成⼀个标签,回想⼀下在卷积在图像中的操作,其实有异曲同⼯。(这⾥不理解也⽆妨,因为我之前搞了⼀段时间图像处理,所以对卷积相对熟悉⼀点)。
⼀维卷积
假设有⼀个时间序列,总共有五个时间点,⽐⽅说股市,有⼀个股票的价格波动:[10,13,12,14,15]:
TCN中,或者说因果卷积中,使⽤的卷积核⼤⼩都是2,我也不知道为啥不⽤更⼤的卷积核,看论⽂中好像没有说明这个,如果有⼩伙伴知道原因或者有猜想,可以下⽅评论处⼀起讨论讨论。
aspice等级卷积核是2,那么可想⽽知,对上⾯5个数据做⼀个卷积核为2的卷积是什么样⼦的:
五个数据经过⼀次卷积,可以变成四个数据,但是每⼀个卷积后的数据都是基于两个原始数据得到的,所以说,⽬前卷积的视野域是2。可以看到是输⼊是5个数据,但是经过卷积,变成4个数据了,在图像中有⼀个概念是通过padding来保证卷积前后特征图尺⼨不变,所以
在时间序列中,依然使⽤padding来保证尺⼨不变:
padding是左右两头都增加0,如果padding是1的话,就是上图的效果,其实会产⽣6个新数据,但是秉着:“输⼊输出尺⼨相同”和“我们不能知道未来的数据”,所以最后边那个未来的padding,就省略掉了,之后再代码中会体现出来。
P (y ∣x ,x ,...,x )
k k k −11y k x k
总之,现在我们⼤概能理解,对时间序列卷积的⼤致流程了,也就是对⼀维数据卷积的过程(图像卷积算是⼆维)。
下⾯看如何使⽤Pytorch来实现⼀维卷积:
net = nn.Conv1d(in_channels=1,out_channels=1,kernel_size=2,stride=1,padding=1,dilation=1)
其中的参数跟⼆维卷积⾮常类似,也是有通道的概念的。这个好好品⼀下,⼀维数据的通道跟图像的通道⼀样,是根据不同的卷积核从相同的输⼊中抽取出来不同的特征。kernel_size=2之前也说过了,padding=1也没问题,不过这个公式中假如输⼊5个数据+padding=1,会得到6个数据,最后⼀个数据被舍弃掉。dilation是膨胀系数,下⾯的下⾯会讲。
因果卷积
因果卷积是在wavenet这个⽹络中提出的,之后被⽤在了TCN中。
TCN的:
因果卷积应为就是:Causal Convolutions。
之前已经讲了⼀维卷积的过程了,那么因果卷积,其实就是⼀维卷积的⼀种应⽤吧算是。
假设想⽤上⾯讲到的概念,做⼀个股票的预测决策模型,然后希望决策模型可以考虑到这个时间点之前的4个时间点的股票价格进⾏决策,总共有3种决策:
0:不操作,1:买⼊,2:卖出
所以其实就是⼀个分类问题。因为要求视野域是4,所以按照上⾯的设想,要堆积3个卷积核为2的1维卷积层:
三次卷积,可以让最后的输出,拥有4个视野域。就像是上图中红⾊的部分,就是做出⼀个决策的过程。
股票数据,往往是按照分钟记录的,那少说也是⼗万、百万的数据量,我们决策,想要考虑之前1000个时间点呢?视野域要是1000,那意味着要999层卷积?啥计算机吃得消这样的计算。所以引⼊了膨胀因果卷积。
膨胀因果卷积
英⽂是Dilated Causal Convolution。这个其实就是空洞卷积啦,不确定在之前的博⽂中有没有讲过这个概念(最近别⼈要求在写⼀个⾮常长的教程,和博客中的博⽂可能会有记忆混乱的情况2333)
反正就是,这个空洞卷积、或者叫扩张卷积、或者叫膨胀卷积就是操作dilation这个参数。
viewstub
如图,这个就是dilation=2的时候的情况,与之前的区别有两个:
java代码混淆技术看红⾊区域:可以看到卷积核⼤⼩依然是2,但是卷积核之间变得空洞了,隔过去了⼀个数据;如果dilation=3的话,那么可以想⽽知,这个卷积核中间会空的更⼤,会隔过去两个数据。
用ps切刀切图怎么弄看淡绿⾊数据:因为dilation变⼤了,所以相应的padding的数量从1变成了2,所以为了保证输⼊输出的特征维度相同,padding的数值等于dalition的数值(在卷积核是2的情况下,严格说说:padding=(kernel_size-1)*dilation)
然后我们依然实现上⾯那个例⼦,每次决策想要视野域为4:
可以看到,第⼀次卷积使⽤dilation=1的卷积,然后第⼆次使⽤dilation=2的卷积,这样通过两次卷积就可以实现视野域是4.
那么假设事业域要是8呢?那就再加⼀个dilation=4的卷积。dilation的值是2的次⽅,然后视野域也是2的次⽅的增长,那么就算是要
1000视野域,那⼗层⼤概就⾏了。
这⾥有⼀个动图,挺好看的:
TCN结构
TCN基本就是⼀个膨胀因果卷积的过程,只是上⾯我们实现因果卷积就只有⼀个卷积层。⽽TCN的稍微复杂⼀点(但是不难!)卷积结束后会因为padding导致卷积之后的新数据的尺⼨B>输⼊数据的尺⼨A,所以只保留输出数据中前⾯A个数据;
卷积之后加上个ReLU和Dropout层,不过分吧这要求。
然后TCN中并不是每⼀次卷积都会扩⼤⼀倍的dilation,⽽是每两次扩⼤⼀倍的dilation
总之TCN中的基本组件:TemporalBlock()是两个dilation相同的卷积层,卷积+修改数据尺⼨+relu+dropout+卷积+修改数据尺⼨+relu+dropout
之后弄⼀个Resnet残差连接来避免梯度消失,结束!
关于Resnet的内容:其实不看也⾏,不妨碍理解TCN
模型的PyTorch实现(最好了解⼀点PyTorch)
如果不了解的话,emm,我要安利我的博⽂了2333:
# 导⼊库
import torch
as nn
utils import weight_norm
# 这个函数是⽤来修剪卷积之后的数据的尺⼨,让其与输⼊数据尺⼨相同。
class Chomp1d(nn.Module):
def__init__(self, chomp_size):
super(Chomp1d, self).__init__()
self.chomp_size = chomp_size
def forward(self, x):
return x[:,:,:-self.chomp_size].contiguous()
可以看出来,这个函数就是第⼀个数据到倒数第chomp_size的数据,这个chomp_size就是padding的值。⽐⽅说输⼊数据是5,padding 是1,那么会产⽣6个数据没错吧,那么就是保留前5个数字。
# 这个就是TCN 的基本模块,包含8个部分,两个(卷积+修剪+relu+dropout )
# ⾥⾯提到的downsample 就是下采样,其实就是实现残差链接的部分。不理解的可以⽆视这个
class TemporalBlock (nn .Module ):
def __init__(self , n_inputs , n_outputs , kernel_size , stride , dilation , padding , dropout =0.2):
super (TemporalBlock , self ).__init__()
self .conv1 = weight_norm (nn .Conv1d (n_inputs , n_outputs , kernel_size ,
stride =stride , padding =padding , dilation =dilation ))
self .chomp1 = Chomp1d (padding )
self .relu1 = nn .ReLU ()
self .dropout1 = nn .Dropout (dropout )
self .conv2 = weight_norm (nn .Conv1d (n_outputs , n_outputs , kernel_size ,
stride =stride , padding =padding , dilation =dilation ))
self .chomp2 = Chomp1d (padding )最好的python入门教材
self .relu2 = nn .ReLU ()
self .dropout2 = nn .Dropout (dropout )
self = nn .Sequential (self .conv1, self .chomp1, self .relu1, self .dropout1,
self .conv2, self .chomp2, self .relu2, self .dropout2)
self .downsample = nn .Conv1d (n_inputs , n_outputs , 1) if n_inputs != n_outputs else None
self .relu = nn .ReLU ()
self .init_weights ()
def init_weights (self ):
self .conv1.weight .data .normal_(0, 0.01)
self .conv2.weight .data .normal_(0, 0.01)
if self .downsample is not None :
self .downsample .weight .data .normal_(0, 0.01)
def forward (self , x ):
out = self (x )
res = x if self .downsample is None else self .downsample (x )
return self .relu (out + res )
最后就是TCN的主⽹络了:
class TemporalConvNet (nn .Module ):
def __init__(self , num_inputs , num_channels , kernel_size =2, dropout =0.2):
super (TemporalConvNet , self ).__init__()
layers = []
num_levels = len (num_channels )
for i in range (num_levels ):
dilation_size = 2 ** i
in_channels = num_inputs if i == 0 else num_channels [i -1]
out_channels = num_channels [i ]
php源码共享茶室layers += [TemporalBlock (in_channels , out_channels , kernel_size , stride =1, dilation =dilation_size ,
padding =(kernel_size -1) * dilation_size , dropout =dropout )]
self work = nn .Sequential (*layers )
def forward (self , x ):
return self work (x )
咋⽤的呢?就是num_inputs就是输⼊数据的通道数,⼀般就是1; num_channels应该是个列表,其他的np.array也⾏,⽐⽅说是[2,1]。那么整个TCN模型包含两个TemporalBlock,整个模型共有4个卷积层,第⼀个TemporalBlock的两个卷积层的膨胀系数,第⼆个TemporalBlock的两个卷积层的膨胀系数是.
没了,整个TCN挺简单的,如果之前学过PyTorch和图像处理的⼀些内容,然后⽤TCN来上⼿时间序列,效果会和LGM差不多。(根据最近做的⼀个⽐赛),没有跟Wavenet⽐较过,Wavenet的pytorc看起来怪复杂的,因为wavenet是⽤来处理⾳频⽣成的,会更加复杂⼀点。
总之TCN就这么多,谢谢⼤家。
回复【下载】有精选的免费机器学习学习资料。 每天会更新⼀个机器学习、深度学习的⼩知识,都是⾯试官会问的知识点哦~dilation =2=01dilation =2=12
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论