防水网站的外链如何找,做网站开麻烦吗,网站登录 退出怎么做,python编程是干嘛的文章目录 week 25 ConvLSTM摘要Abstract一、李宏毅机器学习二、文献阅读1. 题目2. abstract3. 网络架构3.1降水预报问题的建模3.2Convolutional LSTM3.3编码-预测结构 4. 文献解读4.1 Introduction4.2 创新点4.3 实验过程4.3.1Moving-MNIST Dataset4.3.2雷达回波数据集 4.4 结论… 文章目录 week 25 ConvLSTM摘要Abstract一、李宏毅机器学习二、文献阅读1. 题目2. abstract3. 网络架构3.1降水预报问题的建模3.2Convolutional LSTM3.3编码-预测结构 4. 文献解读4.1 Introduction4.2 创新点4.3 实验过程4.3.1Moving-MNIST Dataset4.3.2雷达回波数据集 4.4 结论 三、基于pytorch实现ConvLSTM1.实验内容2.实验结果3.实验数据集3.1数据集处理 4.模型及训练过程实现小结参考文献 week 25 ConvLSTM
摘要
本文主要讨论ConvLSTM的模型。本文简要介绍了自注意力机制运行逻辑。其次本文展示了题为Convolutional LSTM Network: A Machine Learning Approach for Precipitation Nowcasting的论文主要内容。该论文将降水预报问题建模为时空序列预测问题并根据FC-LSTM结构进行扩展提出了ConvLSTM。该结构改善了FC-LSTM的缺点通过其局部邻域的输入和过去状态来确定网格中某个单元的未来状态。该文在多个数据集上进行实验从数据角度证明了该网络的优越性。最后本文基于pytorch实现了ConvLSTM模型并在KTH数据集上进行验证。
Abstract
This article mainly discusses the model of ConvLSTM. This article briefly introduces the operating logic of the self-attention mechanism. Secondly, this paper presents the main content of the paper entitled Convolutional LSTM Network: A Machine Learning Approach for Precipitation Nowcasting. In this paper, this paper models the precipitation prediction problem as a spatiotemporal series prediction problem, and proposes the ConvLSTM by extending it according to the FC-LSTM structure. This structure improves on the shortcomings of FC-LSTM by determining the future state of an element in the grid through the input and past state of its local neighborhood. In this paper, experiments are carried out on multiple datasets to prove the superiority of the network from the perspective of data. Finally, this article implements the ConvLSTM model based on pytorch and validates on the KTH dataset.
一、李宏毅机器学习 二、文献阅读
1. 题目
题目Convolutional LSTM Network: A Machine Learning Approach for Precipitation Nowcasting
作者Xingjian Shi, Zhourong Chen, Hao Wang, Dit-Yan Yeung, Wai-kin Wong, Wang-chun Woo
链接https://arxiv.org/pdf/1506.04214.pdf
发布NIPS’15: Proceedings of the 28th International Conference on Neural Information Processing Systems - Volume 1December 2015Pages 802–810
2. abstract
在本文中将降水临近预报表述为时空序列预测问题其中输入和预测目标都是时空序列。通过扩展全连接 LSTM (FC-LSTM) 以在输入到状态和状态到状态转换中都具有卷积结构提出了卷积 LSTM (ConvLSTM) 并用它来构建端到端降水临近预报问题的可训练模型。实验表明ConvLSTM 网络可以更好地捕获时空相关性并且始终优于 FC-LSTM 和最先进的降水临近预报操作 ROVER 算法。
This paper formulates precipitation nowcasting as a spatiotemporal sequence forecasting problem in which both the input and the prediction target are spatiotemporal sequences. By extending the fully connected LSTM (FC-LSTM) to have convolutional structures in both the input-to-state and state-to-state transitions, this paper proposes the convolutional LSTM (ConvLSTM) and uses it to build an end-to-end trainable model for the precipitation nowcasting problem. Experiments show that ConvLSTM network captures spatiotemporal correlations better and consistently outperforms FC-LSTM and the state-of-the-art operational ROVER algorithm for precipitation nowcasting.
3. 网络架构
3.1降水预报问题的建模
对流降水临近预报一直是天气预报领域的一个重要问题。该任务的目标是准确、及时地预测局部区域在相对较短的时间内例如0-6小时的降雨强度。
假设在一个由M行和N列组成的 M × N M\times N M×N网格表示的空间区域内观测到一个动态系统。在网络个的每个单元格内都有随着时间变化的P个观测值。因此任何时刻的观测值都可以使用张量 X ∈ R P × M × N \mathcal X\in \mathbf R^{P\times M\times N} X∈RP×M×N来表示其中 R \mathbf R R表示观测到的特征的域。如果定期记录观测值将得到 X ^ 1 , X ^ 2 , … , X ^ t \mathcal {\hat X}_1,\mathcal {\hat X}_2,\dots, \mathcal {\hat X}_t X^1,X^2,…,X^t。时空序列预测问题是在给定前J个观测值的情况下预测未来最可能的长度K序列 X ^ t 1 , … , X ^ t K argmax X t 1 , … , X t K p ( X t 1 , … , X t K ∣ X ^ t − J 1 , X ^ t − J 2 , … , X t ) (1) \mathcal {\hat X}_{t1},\dots,\mathcal {\hat X}_{tK}\text{argmax}_{\mathcal X_{t1},\dots, \mathcal X_{tK}}p(\mathcal {X_{t1}},\dots, \mathcal{X}_{tK}|\mathcal {\hat X}_{t-J1},\mathcal {\hat X}_{t-J2},\dots, \mathcal {X}_t) \tag{1} X^t1,…,X^tKargmaxXt1,…,XtKp(Xt1,…,XtK∣X^t−J1,X^t−J2,…,Xt)(1) 对于降水预报每个时间戳的观测都是一幅二维雷达回波图。若将地图划分为平铺的、不重叠的贴片并将贴片内的像素作为其测量值。
LSTM不再赘述直接描述网络结构
3.2Convolutional LSTM
FC-LSTM在处理时空数据中的主要缺点是它在输入到状态和状态到状态转换中的全连接的使用其中没有空间信息被编码。为了克服这个问题进行针对性设计
输入是 X 1 , X 2 , … , X t \mathcal {X}_1,\mathcal {X}_2,\dots, \mathcal {X}_t X1,X2,…,Xt单元输出 C 1 , C 2 , … , C t \mathcal C_1,\mathcal C_2,\dots, \mathcal C_t C1,C2,…,Ct隐藏状态 H 1 , … , H t \mathcal H_1,\dots, \mathcal H_t H1,…,HtConvLSTM的门 i t , f t , o t i_t,f_t,o_t it,ft,ot
以上均为3D张量其最后两个维度是空间维度 ConvLSTM 通过其局域邻域的输入和过去状态来确定网格中某个单元的未来状态。通过在状态到状态和输入到状态转换中使用卷积算子轻松实现如上图。ConvLSTM的关键方程如下面*为卷积算子 ∘ \circ ∘为卷积算子 若将状态视为移动对象的隐式表示则具有较大内核的ConvLSTM应该能够捕获更快的运动而具有较小卷积核的可以捕获较慢的运动。此外FC-LSTM可以看作所有特征均在一个单元格上的ConvLSTM。
为了确保状态具有与输入相同的行数和相同数量的列在应用卷积操作之前需要padding。在这里隐藏状态的embedding在可以将边界点视为使用外部世界的状态进行计算。 3.3编码-预测结构
ConvLSTM可以作为更为复杂结构的模块。对于时空序列预测问题使用下图所示的结构。两个网络、一个编码网络和一个预测网络。预测网络的初始状态和单元输出均是从编码网络的最后一个状态复制得到。这两个网络都是通过堆叠几个ConvLSTM层来形成的由于预测目标具有与输入相同的位数故将预测网 4. 文献解读
4.1 Introduction
对流降水临近预报一直是天气预报领域的一个重要问题。该任务的目标是准确、及时地预测局部区域在相对较短的时间内例如0-6小时的降雨强度。ROVER算法是领域现有的最先进算法。深度学习的最新进展特别是循环神经网络 (RNN) 和长短期记忆 (LSTM) 模型提供了一些关于如何解决这个问题的途径。[2]中提出的开创性的 LSTM 编码器-解码器框架通过训练时间级联的 LSTM。但以往的模型均采用的全连接LSTMFC-LSTM层没有考虑空间相关性。
在本文中提出了一种用于降水临近预报的新型ConvLSTM网络。将降水临近预报表述为一个时空序列预测问题可以在[2]中提出的通用序列到序列学习框架下解决。为了更好地模拟时空关系将FC-LSTM的思想扩展到在输入-状态和状态-状态转换两者中具有卷积结构的COVLSTM。当对合成Moving-MNIST数据集和雷达回波数据集进行评估时ConvLSTM模型始终优于FC-LSTM和最先进的操作ROVER算法。
4.2 创新点
将降水预报问题建模为时空序列预测问题从而在[2]中提出的框架内解决。根据FC-LSTM结构进行扩展提出了ConvLSTM该结构改善了FC-LSTM的缺点通过其局部邻域的输入和过去状态来确定网格中某个单元的未来状态。将ConvLSTM网络与一个合成MovingMNIST数据集中的FC-LSTM网络进行比较。建立了一个新的雷达回波数据集并将该模型与基于几种常用降水预报指标的最先进的ROVER算法进行了比较。
4.3 实验过程
首先将ConvLSTM网络与一个合成MovingMNIST数据集中的FC-LSTM网络进行比较以获得对模型的行为的一些基本理解。用不同的规格运行模型根据[3]中的层和内核大小并研究一些“域外域”情况。为了验证模型在更具有挑战性的降水预报问题上的有效性建立了一个新的雷达回波数据集并将模型与基于几种常用降水预报指标的最先进的ROVER算法进行了比较。
在python环境下实现了该模型并在配备了单个NVIDIA K20 GPU的计算机上运行所有实验。
4.3.1Moving-MNIST Dataset
数据预处理以及部分训练参数使用类似[3]中描述的生成过程。数据集中所有数据实例长20帧输入10帧预测10帧并包含在 64 × 64 64\times 64 64×64patch内的手写数字。移动数字从MNIST数据集中500位子集中随机选择。开始位置和速度方向都是随机选择的速度振幅是随机选择的。该生成过程重复了15000次得到了包含10000条训练序列、2000条验证序列和3000条测试序列的数据集。通过使用反向传播时间(BPTT)最小化交叉熵损失来训练所有LSTM模型并且RMSProp学习速率为10×3衰减率为0.9。此外在验证集上执行early-stop。
实验中使用的网络框架对于FC-LSTM网络使用了与[3]中具有两个2048节点的LSTM层的无条件未来预测器模型相同的结构。对于ConvLSTM网络将patch size设置为4×4这样每个64×64帧由16×16×16张量表示。用不同的层数测试模型的三种变体。
1层网络包含一个包含256个隐藏状态的ConvLSTM层2层网络有两个ConvLSTM层每个层有128个隐藏状态3层网络在三个ConvLSTM层中分别有128、64和64隐藏状态
所有输入到状态和状态到状态的内核大小为5×5
实验表明ConvLSTM 网络的性能始终优于 FC-LSTM 网络更深层的网络结构可以提供更好的结果 上图为ConvLSTM 网络与 FC-LSTM 网络在 Moving-MNIST 数据集上的比较结果即各算法在测试集上的平均交叉熵损失。‘-5x5’和‘-1x1’表示相应的状态到状态内核大小即5×5或1×1。 “256”、“128”和“64”指的是 ConvLSTM 层中隐藏状态的数量。 ‘(5x5)’和‘(9x9)’表示输入到状态的内核大小。
可以看出ConvLSTM在参数更少的情况下达到了更好的结果
4.3.2雷达回波数据集
所使用的雷达回波数据集是2011年至2013年在香港收集的3年天气雷达强度的子集。由于不是每天都下雨预报目标是降水选择前97个雨天形成数据集。
数据预处理首先通过设置 P Z − min ( Z ) max ( Z ) − min ( Z ) P\frac{Z-\min(Z)}{\max(Z)-\min(Z)} Pmax(Z)−min(Z)Z−min(Z)将强度值Z转换为灰度像素P并在中心 330 × 330 330\times 330 330×330区域裁剪雷达图。之后将带有半径10的磁盘过滤器应用至 100 × 100 100\times 100 100×100范围内并调整雷达图的大小。为了减少测量仪器带来的噪声进一步去除了一些噪声区域的像素值这些区域是通过将K-Means聚类方法应用于每月像素平均值来确定的。天气雷达数据每6分钟记录一次因此每天有240帧。为了获得不相交的训练、测试和验证子集将每个日序列划分为40个不重叠的帧块并随机分配4个块进行训练1块用于测试并且1块用于验证。数据实例是使用一个20帧宽的滑动窗口从这些块中分割出来的。因此雷达回波数据集包含8148个训练序列2037个测试序列和2037个验证序列所有序列都有20帧长(输入5帧预测15帧)。
训练以及模型参数设置 patch size设置为2并训练一个包含64个隐藏状态和3×3个内核的2层ConvLSTM网络 对于ROVER算法在验证集上调整光流估计器的参数并使用最佳参数报告测试结果。 此外还尝试了三种不同的Rover初始化方案 ROVER 1计算最后两个观测帧的光流然后进行半拉格朗日平流ROVER 2以最后两个流场的平均值初始化速度ROVER 3给出最后三个流场的加权平均值(权重分别为0.7、0.2和0.1)初始化 此外还训练了一个具有两个 2000 节点 LS TM层的 FC-LSTM 网络。
无论是 ConvLSTM 网络还是 FC-LSTM 网络都优化了 15 个预测的交叉
使用几种常用的降水预报指标即雨量均方误差(雨量均方误差)、关键成功指数(CSI)、虚警率(Far)、检测概率(POD)以及相关性来评价这些方法。
降雨量 MSE 指标定义为预测降雨量与实际降雨量之间的平均平方误差。
三个技能分数定义为 C S I h i t s h i t s m i s s e s f a l s e a l a r m s CSI\frac{hits}{hitsmissesfalsealarms} CSIhitsmissesfalsealarmshits F A R f a l s e a l a r m s h i t s f a l s e a l a r m s FAR\frac{falsealarms}{hitsfalsealarms} FARhitsfalsealarmsfalsealarms P O D h i t s h i t s m i s s e s POD\frac{hits}{hitsmisses} PODhitsmisseshits
预测框架P与地面真框架T的相关性定义为 ∑ i , j P i , j T i . j ( ∑ i , j P i , j 2 ) ( ∑ i , j T i , j 2 ) ϵ \frac{\sum_{i,j}P_{i,j}T_{i.j}}{\sqrt{(\sum_{i,j}P_{i,j}^2)(\sum_{i,j}T_{i,j}^2)\epsilon}} (∑i,jPi,j2)(∑i,jTi,j2)ϵ ∑i,jPi,jTi.j 其中 ϵ 1 0 − 9 \epsilon10^{-9} ϵ10−9
下图为比较不同模型 15 个预测步骤的平均得分 下图为基于四种降水临近预报指标的不同模型随时间的比较结果 ConvLSTM能够更准确地预测未来的降水等高线特别是在边界上。虽然 ROVER2 可以给出比 ConvLSTM 更清晰的预测但它会触发更多的虚假警报而且通常比 ConvLSTM 更不精确。另外ConvLSTM的模糊效应可能是由于任务本身的不确定性造成的。
4.4 结论
在这两个数据集上进行的实验结果得出以下结论
ConvLSTM在处理时空相关性方面优于FC-LSTM.使得状态到状态卷积核的大小大于1对于捕获时空运动模式是必要的。更深层次的模型可以较少的参数产生更好的结果。ConvLSTM在降水预报方面的性能优于ROVER。
三、基于pytorch实现ConvLSTM
1.实验内容
基于pytorch实现ConvLSTM并使用KTH数据集进行测试
2.实验结果
训练过程如下
Epochs[1/50]--batch[0/402]--Acc: 0.1562--loss: 1.7924
Epochs[1/50]--batch[50/402]--Acc: 0.4375--loss: 1.6179
Epochs[1/50]--batch[100/402]--Acc: 0.375--loss: 1.3734
Epochs[1/50]--batch[150/402]--Acc: 0.3438--loss: 1.2532
Epochs[1/50]--batch[200/402]--Acc: 0.4375--loss: 1.2269
Epochs[1/50]--batch[250/402]--Acc: 0.5625--loss: 0.925
Epochs[1/50]--batch[300/402]--Acc: 0.5938--loss: 0.8918
Epochs[1/50]--batch[350/402]--Acc: 0.5--loss: 1.085
Epochs[1/50]--Acc on val 0.5182
Epochs[30/50]--Acc on val 0.65513.实验数据集
本实验使用KTH数据集共有六个类别包括Boxing拳击、Handclapping鼓掌、Handwaving挥手、Jogging慢跑、Running快跑和Walking行走。共计600个视频文件。
3.1数据集处理
is_gray是否转换为灰度图
frame_len以该长度对视频进行分割
transforms进行图像增强
__init__初始化操作
def load_avi_frames数据载入
创建一个视频捕获对象用于读取视频文件循环读取视频帧直到视频结束检查是否成功读取到帧图像数据将原始图片转换为灰度图因为后续数据集用作分类所以转换为单通道的灰度图可以降低计算量返回得到一个4维数组
def data_process样本构建
缓存预处理结果的修饰器循环遍历每个目录下的视频文件并得到该目录下所有视频文件的名称开始遍历当前文件夹中的每个视频文件根据文件名获取对应的人物编号读取得到原始的视频数据根据每个视频以固定长度进行采样构造样本其中sub_frames的形状为[frame_len,120,160,channels]返回最后构造完成的样本数据。
def generate_batch实现一个辅助函数来处理每个小批量样本的数据
遍历小批量样本中的每个样本循环对视频里的每一帧进行图像增强其中frame的形状为[height, width, channels]在进行图像增强经过ToTensor()变换后形状会变成[channels,height,width]且每个像素值的范围会被缩放至将所有样本堆叠构造得到一个小批量标准数据其形状为[batch_size, frame_len, channels, height, width]。
def load_train_val_test_data编码实现迭代器的构建
返回data_process方法采样得到的原始样本数据构建得到测试集对应的迭代器其中generate_batch方法将作为参数传入到类DataLoader中进行使用构建得到训练集和验证集对应的迭代器。
class KTHData(object):载入KTH数据集下载地址https://www.csc.kth.se/cvap/actions/ 一共包含6个zip压缩包DATA_DIR os.path.join(DATA_HOME, kth)CATEGORIES [boxing, handclapping, handwaving, jogging, running, walking]TRAIN_PEOPLE_ID [1, 2, 4, 5, 6, 7, 9, 11, 12, 15, 17, 18, 20, 21, 22, 23, 24] # 25*0.7 17VAL_PEOPLE_ID [3, 8, 10, 19, 25] # 25*0.2 5TEST_PEOPLE_ID [13, 14, 16] # 25*0.1 3FILE_PATH os.path.join(DATA_DIR, kth.pt)def __init__(self, frame_len15,batch_size4,is_sample_shuffleTrue,is_grayTrue,transformsNone):self.frame_len frame_len # 即time_step 以FRAME_LEN为长度进行分割self.batch_size batch_sizeself.is_sample_shuffle is_sample_shuffleself.is_gray is_grayself.transforms transformsstaticmethoddef load_avi_frames(pathNone, is_grayFalse):用来读取每一个.avi格式的文件:param path::return:import cv2logging.info(f ## 正在读取原始文件: {path}并划分数据)video cv2.VideoCapture(path)frames []while video.isOpened():ret, frame video.read() # frame: (120, 160, 3) class numpy.ndarrayif not ret: # ret是一个布尔值表示是否成功读取帧图像的数据frame是读取到的帧图像数据。breakif is_gray:frame Image.fromarray(frame)frame frame.convert(L)frame np.array(frame.getdata()).reshape((120, 160, 1))frames.append(frame)logging.info(f ## 该视频一共有{len(frames)}帧)return np.array(frames, dtypenp.uint8) # [n, height, width, channels]# 必须要转换成np.uint8类型否则transforms.ToTensor()中的标准化会无效process_cache(unique_key[frame_len, is_gray])def data_process(self, file_pathNone):train_data, val_data, test_data [], [], []for label, dir_name in enumerate(self.CATEGORIES): # 遍历每个文件夹video_dir os.path.join(self.DATA_DIR, dir_name) # 构造每个文件夹的路径video_names os.listdir(video_dir) # 列出当前文件夹的所有文件for name in video_names: # 遍历当前文件夹中的每个视频people_id int(name[6:8]) # 取人员编号video_path os.path.join(video_dir, name) # 得到文件的绝对路径frames self.load_avi_frames(video_path, self.is_gray) # 读取该文件s_idx, e_idx 0, self.frame_lenwhile e_idx len(frames): # 开始采样样本logging.info(f ## 截取帧子序列 [{s_idx}:{e_idx}])sub_frames frames[s_idx:e_idx] # [frame_len, 120, 160, channels]if people_id in self.TRAIN_PEOPLE_ID:train_data.append((sub_frames, label))elif people_id in self.VAL_PEOPLE_ID:val_data.append((sub_frames, label))elif people_id in self.TEST_PEOPLE_ID:test_data.append((sub_frames, label))else:raise ValueError(fpeople id {people_id} 有误)s_idx, e_idx e_idx, e_idx self.frame_lenlogging.info(f ## 原始数据划分完毕训练集、验证集和测试集的数量分别为: f{len(train_data)}-{len(val_data)}-{len(test_data)})data {train_data: train_data, val_data: val_data, test_data: test_data}return datadef generate_batch(self, data_batch)::param data_batch::return: 每个batch的形状[batch_size, frame_len, channels, height, width][batch_size, ]batch_frames, batch_label [], []for (frames, label) in data_batch: # 开始对一个batch中的每一个样本进行处理。# frames的形状为 [frame_len, height, width,channels]if self.transforms is not None:# 遍历序列里的每一帧frame的形状[height, width, channels]# 经过transforms.ToTensor()后的形状为[channels, height, width]frames torch.stack([self.transforms(frame) for frame in frames],dim0) # [frame_len, channels, height, width]else:frames torch.tensor(frames.transpose(0, 3, 1, 2)) # [frame_len, channels, height, width]logging.info(f{frames.shape})batch_frames.append(frames) # [[frame_len, channels, height, width], [], []]batch_label.append(label)batch_frames torch.stack(batch_frames, dim0) # [batch_size, frame_len, channels, height, width]batch_label torch.tensor(batch_label, dtypetorch.long)return batch_frames, batch_labeldef load_train_val_test_data(self, is_trainFalse):data self.data_process(file_pathself.FILE_PATH)if not is_train:test_data data[test_data]test_iter DataLoader(test_data, batch_sizeself.batch_size,shuffleTrue, collate_fnself.generate_batch)logging.info(f ## 测试集构建完毕一共{len(test_data)}个样本)return test_itertrain_data, val_data data[train_data], data[val_data]train_iter DataLoader(train_data, batch_sizeself.batch_size, # 构造DataLoadershuffleself.is_sample_shuffle,collate_fnself.generate_batch)val_iter DataLoader(val_data, batch_sizeself.batch_size,shuffleFalse, collate_fnself.generate_batch)logging.info(f ## 训练集和验证集构建完毕样本数量为{len(train_data)}:{len(val_data)})return train_iter, val_iterdef show_example(self, file_pathNone, row3, col5, begin_id10):可视化:param file_path::param row::param col::param begin_id::return:import matplotlib.pyplot as pltif file_path is None:file_path os.path.join(self.DATA_DIR, self.CATEGORIES[0])file_path os.path.join(file_path, person01_boxing_d1_uncomp.avi)frames self.load_avi_frames(file_path)fig, ax plt.subplots(row, col)for i, axi in enumerate(ax.flat): # , figsize(18, 10)image frames[i begin_id]axi.set_xlabel(fFrame{i begin_id})axi.imshow(image)axi.set(xticks[], yticks[])plt.tight_layout()plt.show()
4.模型及训练过程实现
参照论文实现ConvLSTM
ConvLSTMCell记忆单元
class ConvLSTMCell(nn.Module):def __init__(self, in_channels, out_channels, kernel_size, bias):Initialize ConvLSTM cell.Parameters----------in_channels: int 输入特征图的通道数out_channels: int 输出特征图的通道数kernel_size: (int, int) 卷积核的宽和高bias: bool 是否使用偏置super(ConvLSTMCell, self).__init__()self.in_channels in_channelsself.out_channels out_channelsself.kernel_size kernel_sizeself.padding kernel_size[0] // 2, kernel_size[1] // 2# 需要强制进行padding以保证每次卷积后形状不发生变化# 根据之前第4.3.2节内容的介绍在stride1的情况下padding kernel_size // 2# 如卷积核为3×3则需要padding1即可# 在下面的卷积操作中stride使用的是默认值1self.bias biasself.conv nn.Conv2d(in_channelsself.in_channels self.out_channels,out_channels4 * self.out_channels,kernel_sizeself.kernel_size,paddingself.padding,biasself.bias)def forward(self, input_tensor, last_state)::param input_tensor: 当前时刻的输入x_t, 形状为[batch_size, in_channels, height, width]:param last_state: 上一时刻的状态c_{t-1}和h_{t-1}, 形状均为 [batch_size, out_channels, height, width]:return:h_last, c_last last_statecombined_input torch.cat([input_tensor, h_last], dim1)# [batch_size, in_channelsout_channels, height, width]combined_conv self.conv(combined_input) # [batch_size, 4 * out_channels, height, width]cc_i, cc_f, cc_o, cc_g torch.split(combined_conv, self.out_channels, dim1)# 分割得到每个门对应的卷积计算结果形状均为 [batch_size, out_channels, height, width]i torch.sigmoid(cc_i)f torch.sigmoid(cc_f)o torch.sigmoid(cc_o)g torch.tanh(cc_g)c_next f * c_last i * g # [batch_size, out_channels, height, width]h_next o * torch.tanh(c_next) # [batch_size, out_channels, height, width]return h_next, c_nextdef init_hidden(self, batch_size, image_size):初始化记忆单元的C和H:param batch_size::param image_size::return:height, width image_sizereturn (torch.zeros(batch_size, self.out_channels, height, width, deviceself.conv.weight.device),torch.zeros(batch_size, self.out_channels, height, width, deviceself.conv.weight.device))ConvLSTM模型
class ConvLSTM(nn.Module):Parameters:in_channels: 输入特征图的通道数为整型out_channels: 每一层输出特征图的通道数可为整型也可以是列表为整型时表示每一层的输出通道数均相等为列表时则列表的长度必须等于num_layer例如 out_channels [32,64,128] 表示3层ConvLSTM的输出特征图通道数分别为32、64和128且此时的num_layer也必须为3kernel_size: 每一层中卷积核的长和宽可以为一个tuple如(3,3)表示每一层的卷积核窗口大小均为3x3也可以是一个列表分别用来指定每一层卷积核的大小如[(3,3),(5,5),(7,7)]表示3层卷积各种的窗口大小此时需要注意的是如果为列表也报保证其长度等于num_layernum_layers: ConvLSTM堆叠的层数batch_first: 输入数据的第1个维度是否为批大小bias: 卷积中是否使用偏置return_all_layers: 是否返回每一层各个时刻的输出结果Input:A tensor of size B, T, C, H, W or T, B, C, H, W[Batch_size, Time_step, Channels, Height, Width] or [Time_step, Batch_size, Channels, Height, Width]Output:当return_all_layers 为 True 时layer_output_list: 每一层的输出结果包含有num_layer个元素的列表每个元素的形状为[batch_size, time_step, out_channels, height, width]last_states: 每一层最后一个时刻的输出结果同样是包含有num_layer个元素的列表列表中的每个元素均为一个包含有两个张量的列表如last_states[-1][0]和last_states[-1][1]分别表示最后一层最后一个时刻的h和clayer_output_list[-1][:, -1] last_states[-1][0]shape: [Batch_size, Channels, Height, Width]当return_all_layers 为 False 时layer_output_list: 最后一层每个时刻的输出形状为 [batch_size, time_step, out_channels, height, width]last_states: 最后一层最后一个时刻的输出形状为 [batch_size, out_channels, height, width]Example: model ConvLSTM(in_channels3,out_channels2,kernel_size(3, 3),num_layers3,batch_firstTrue,biasTrue,return_all_layersTrue)x torch.rand((1, 4, 3, 5, 5)) # [batch_size, time_step, channels, height, width]layer_output_list, last_states model(x)def __init__(self, in_channels, out_channels, kernel_size, num_layers,batch_firstFalse, biasTrue, return_all_layersFalse):super(ConvLSTM, self).__init__()self._check_kernel_size_consistency(kernel_size)# 检查kernel_size是否符合上面说的取值情况# Make sure that both kernel_size and out_channels are lists having len num_layerskernel_size self._extend_for_multilayer(kernel_size, num_layers)out_channels self._extend_for_multilayer(out_channels, num_layers)# 将kernel_size和out_channels扩展到多层时的情况if not len(kernel_size) len(out_channels) num_layers:raise ValueError(len(kernel_size) len(out_channels) num_layers 三者的值必须相等)self.in_channels in_channelsself.out_channels out_channelsself.kernel_size kernel_sizeself.num_layers num_layersself.batch_first batch_firstself.bias biasself.return_all_layers return_all_layerscell_list []for i in range(0, self.num_layers): # 实例化每一层的ConvLSTM记忆单cur_in_channels self.in_channels if i 0 else self.out_channels[i - 1]# 当前层的输入通道数除了第一层为self.in_channels之外其它的均为上一层的输出通道数cell_list.append(ConvLSTMCell(in_channelscur_in_channels, out_channelsself.out_channels[i],kernel_sizeself.kernel_size[i], biasself.bias))self.cell_list nn.ModuleList(cell_list)# 必须要放到nn.ModuleList否则在GPU上云运行时会报错张量不在同一个设备上的问题def forward(self, input_tensor, hidden_stateNone):Parameters----------input_tensor: todo5-D Tensor: [Batch_size, Time_step, Channels, Height, Width] or[Time_step, Batch_size, Channels, Height, Width]hidden_state: todoNone. todo implement statefulReturns-------last_state_list, layer_outputif not self.batch_first:# 将(t, b, c, h, w) 转为 (b, t, c, h, w)input_tensor input_tensor.permute(1, 0, 2, 3, 4)batch_size, time_step, _, height, width input_tensor.size()# Implement stateful ConvLSTMif hidden_state is not None:raise NotImplementedError()else:# Since the init is done in forward. Can send image size herehidden_state self._init_hidden(batch_sizebatch_size,image_size(height, width))layer_output_list [] # 保存每一层的输出h每个元素的形状为[batch_size, time_step, out_channels, height, width]last_state_list [] # 保存每一层最后一个时刻的输出h和c即[(h,c),(h,c)...]cur_layer_input input_tensor # [batch_size, time_step, in_channels, height, width]for layer_idx in range(self.num_layers):h, c hidden_state[layer_idx] # 开始遍历每一层的ConvLSTM记忆单元并取对应的初始值# h 和 c 的形状均为[batch_size, out_channels, height, width]output_inner []cur_layer_cell self.cell_list[layer_idx] # 为一个ConvLSTMCell记忆单元for t in range(time_step): # 对于每一层的记忆单元按照时间维度展开进行计算h, c cur_layer_cell(input_tensorcur_layer_input[:, t, :, :, :], last_state[h, c])output_inner.append(h) # 当前层每个时刻的输出h, 形状为 [batch_size, out_channels, height, width]layer_output torch.stack(output_inner, dim1) # [batch_size, time_step, out_channels, height, width]cur_layer_input layer_output # 当前层的输出h作为下一层的输入layer_output_list.append(layer_output)last_state_list.append([h, c])if not self.return_all_layers:layer_output_list layer_output_list[-1:]last_state_list last_state_list[-1:]return layer_output_list, last_state_listdef _init_hidden(self, batch_size, image_size):init_states中的每个元素为一个tuple包含C和H两个部分如 [(h,c),(h,c)...]形状均为 [batch_size, out_channels, height, width]:param batch_size::param image_size::return:init_states []for i in range(self.num_layers): # 初始化每一层的初始值init_states.append(self.cell_list[i].init_hidden(batch_size, image_size))return init_statesstaticmethoddef _check_kernel_size_consistency(kernel_size):if not (isinstance(kernel_size, tuple) or(isinstance(kernel_size, list) and all([isinstance(elem, tuple) for elem in kernel_size]))):raise ValueError(kernel_size must be tuple or list of tuples)staticmethoddef _extend_for_multilayer(param, num_layers):if not isinstance(param, list):param [param] * num_layersreturn paramConvLSTMKTH针对数据集进行改进
class ConvLSTMKTH(nn.Module):def __init__(self, configNone):super().__init__()self.conv_lstm ConvLSTM(config.in_channels, config.out_channels,config.kernel_size, config.num_layers, config.batch_first)self.max_pool nn.MaxPool2d(kernel_size(5, 5), stride2, padding2)self.hidden_dim (config.width * config.height) // 4 * self.conv_lstm.out_channels[-1]# 除以4是因为长宽均要除以stride, 使用self.conv_lstm.out_channels[-1]# 主要是为了兼容out_channels传入整型或列表的情况因为传入整型的话在ConvLSTM的初始化方法中_extend_for_multilayer()# 方法也会将其扩充一个listself.classifier nn.Sequential(nn.Flatten(),nn.Linear(self.hidden_dim, config.num_classes))def forward(self, x, labelsNone)::param x: [batch_size, time_step, channels, height, width]:param labels: [batch_size,]:return: logits: [batch_size, num_classes]_, layer_output self.conv_lstm(x)# layer_output: [h:[batch_size, out_channels, height, width], c:[batch_size, out_channels, height, width]]pool_output self.max_pool(layer_output[-1][0]) # [batch_size, out_channels, height//2, width//2]logits self.classifier(pool_output) # [batch_size, num_classes]if labels is not None:loss_fct nn.CrossEntropyLoss(reductionmean)loss loss_fct(logits, labels)return loss, logitselse:return logitsModelConfig模型参数设置
class ModelConfig(object):def __init__(self):self.batch_size 32self.epochs 30self.learning_rate 3e-3self.num_classes 6self.in_channels 1self.out_channels [32,32]self.kernel_size [(3, 3), (3, 3)]self.num_layers len(self.out_channels)self.height 60 # 原始大小为120self.width 80 # 原始大小为160self.time_step 15self.num_warmup_steps 200self.model_save_path model.ptself.summary_writer_dir runs/modelself.device torch.device(cuda:0 if torch.cuda.is_available() else cpu)# 判断是否存在GPU设备其中0表示指定第0块设备logging.info(### 将当前配置打印到日志文件中 )for key, value in self.__dict__.items():logging.info(f### {key} {value})训练过程
def train(config):trans transforms.Compose([transforms.ToTensor(),transforms.Resize((config.height, config.width)),transforms.RandomHorizontalFlip(0.5)])data_load KTHData(frame_lenconfig.time_step,batch_sizeconfig.batch_size,transformstrans)train_iter, val_iter data_load.load_train_val_test_data(is_trainTrue)model ConvLSTMKTH(config)if os.path.exists(config.model_save_path):logging.info(f # 载入模型{config.model_save_path}进行追加训练...)checkpoint torch.load(config.model_save_path)model.load_state_dict(checkpoint)optimizer torch.optim.Adam(model.parameters(), lrconfig.learning_rate)writer SummaryWriter(config.summary_writer_dir)model model.to(config.device)max_test_acc 0steps len(train_iter) * config.epochsscheduler optimization.get_cosine_schedule_with_warmup(optimizer, num_warmup_stepsconfig.num_warmup_steps,num_training_stepssteps, num_cycles2)for epoch in range(config.epochs):for i, (x, y) in enumerate(train_iter):x, y x.to(config.device), y.to(config.device)loss, logits model(x, y)optimizer.zero_grad()loss.backward()optimizer.step() # 执行梯度下降scheduler.step()if i % 50 0:acc (logits.argmax(1) y).float().mean()logging.info(fEpochs[{epoch 1}/{config.epochs}]--batch[{i}/{len(train_iter)}]f--Acc: {round(acc.item(), 4)}--loss: {round(loss.item(), 4)})writer.add_scalar(Training/Accuracy, acc, scheduler.last_epoch)writer.add_scalar(Training/Loss, loss.item(), scheduler.last_epoch)test_acc evaluate(val_iter, model, config.device)logging.info(fEpochs[{epoch 1}/{config.epochs}]--Acc on val {test_acc})writer.add_scalar(Testing/Accuracy, test_acc, scheduler.last_epoch)if test_acc max_test_acc:max_test_acc test_accstate_dict deepcopy(model.state_dict())torch.save(state_dict, config.model_save_path)模型评估
def evaluate(data_iter, model, device):model.eval()with torch.no_grad():acc_sum, n 0.0, 0for x, y in data_iter:x, y x.to(device), y.to(device)logits model(x)acc_sum (logits.argmax(1) y).float().sum().item()n len(y)model.train()return acc_sum / n使用模型进行预测
def inference(config, ):trans transforms.Compose([transforms.ToTensor(),transforms.Resize((config.height, config.width)),transforms.RandomHorizontalFlip(0.5)])data_load KTHData(frame_lenconfig.time_step,batch_sizeconfig.batch_size,transformstrans)test_iter data_load.load_train_val_test_data(is_trainFalse)model ConvLSTMKTH(config)model.to(config.device)model.eval()if os.path.exists(config.model_save_path):logging.info(f # 载入模型进行推理……)checkpoint torch.load(config.model_save_path)model.load_state_dict(checkpoint)else:raise ValueError(f # 模型{config.model_save_path}不存在)first_batch next(iter(test_iter))with torch.no_grad():logits model(first_batch[0].to(config.device))y_pred logits.argmax(1)logging.info(f真实标签为{first_batch[1]})logging.info(f预测标签为{y_pred})小结
本文主要介绍了自注意力机制以及ConvLSTM在上周的学习中论文将二者结合从而实现了时空序列预测领域中较好的结果。本文在KTH数据集上实现了该结构根据数据集构造了迭代器以及进行了模型重构。最后在该环境下进行了模型有效性验证得到了较好的结果。
下周将继续阅读序列预测相关论文
参考文献
[1] Shi, X.; Chen, Z.; Wang, H.; Yeung, D.-Y.; Wong, W.-K.;and Woo, W.-c. 2015. Convolutional lstm network: A machine learning approach for precipitation nowcasting. In NIPS 2015, 802–810.
[2]I. Sutskever, O. Vinyals, and Q. V. Le. Sequence to sequence learning with neural networks. In NIPS, pages 3104–3112, 2014.
[3]N. Srivastava, E. Mansimov, and R. Salakhutdinov. Unsupervised learning of video representations using lstms. In ICML, 2015.