当前位置:   article > 正文

LSTNet项目代码解析-pytorch

lstnet

参考资料:

时间序列预测-LSTNet模型
LSTNet详解
LSTNet时间序列预测
多元时间序列预测之(二)LSTNet模型
LSTNet学习
源码
数据集

导读

在这里插入图片描述
LSTnet文章提出该模型针对多元时间序列预测问题,在文章中,主要应用在太阳能发电厂能量输出,电力消耗和交通堵塞等情况的预测。
提供的源码项目里就是对这些内容的实战例子。

直接引用参考文章里的解释:LSTNet有啥特点呢?它利用卷积层的优势发现多维输入变量之间的局部依赖模式,并循环层捕获复杂的长期依赖模式(It leverages the strengths of both the convolutional layer to discover the local dependency patterns among multi-dimensional input variables and the recurrent layer to captures complex long-term dependencies)。还要啥其他的创新吗?有,循环跳跃(Recurrent-skip):其被设计用来捕获特别长期依赖的模式,并利用了输入时间序列信号的周期性使优化变得更容易(designed for capturing very long-term dependence patterns and making the optimization easier as it utilizes the periodic property of the input time series signals)。

创新点就是将时序数据通过卷积层进行特征提取,用于捕捉短期局部信息,使用LSTM捕捉长期宏观信息。
整个模型的思想其实很简单,正如上图所示,先将多维时序数据通过CNN进行提取,然后通过RNN网络进行常规的LSTM模型训练,最后将最初的CNN提取内容跟经过LSTM的结果进行合并输出预测结果。

项目配置

在参考文章LSTNet详解中,介绍了基于不同框架的LSTnet的项目,这里的源码用的是pytorch框架的。
下载完源码后,在安装配置完pytorch环境后,打开项目,如下图:

在这里插入图片描述
其中,画圈的是作者提示的项目运行指令,如solar.sh中的内容如下:
python main.py --gpu 0 --data data/solar_AL.txt --save save/solar_AL.pt --hidSkip 10 --output_fun Linear
直接在终端输入这条指令即可运行整个项目。
这里要做Debug,所以换一个方式运行,打开main.py文件,在空白处右键,然后打开修改运行配置在这里插入图片描述
将作者提示的形参传入下图红框部分--gpu 0 --data data/solar_AL.txt --save save/solar_AL.pt --hidSkip 10 --output_fun Linear
源代码里--gpu 4,这里的电脑只用1给gpu,所以设置为0。不用多卡训练的花,这个部分可以删了,不传入形参。系统调用cpu训练。
在这里插入图片描述
然后开始运行,在运行前需要创建save文件夹,用来保存权重的,将数据集放到项目里,创建data文件夹,然后将数据集对应的丢进来:

如果出行以下错误:在这里插入图片描述
原因是torch的版本跟项目的版本不一样导致的,将data[0]改为data.item()即可。
在这里插入图片描述

参数配置

每个形参后都有英文介绍

parser = argparse.ArgumentParser(description='PyTorch Time series forecasting')
parser.add_argument('--data', type=str, required=True,
                    help='location of the data file')
parser.add_argument('--model', type=str, default='LSTNet',
                    help='')
parser.add_argument('--hidCNN', type=int, default=100,
                    help='number of CNN hidden units')
parser.add_argument('--hidRNN', type=int, default=100,
                    help='number of RNN hidden units')
parser.add_argument('--window', type=int, default=24 * 7,
                    help='window size')
parser.add_argument('--CNN_kernel', type=int, default=6,
                    help='the kernel size of the CNN layers')
parser.add_argument('--highway_window', type=int, default=24,
                    help='The window size of the highway component')
parser.add_argument('--clip', type=float, default=10.,
                    help='gradient clipping')
parser.add_argument('--epochs', type=int, default=100,
                    help='upper epoch limit')
parser.add_argument('--batch_size', type=int, default=128, metavar='N',
                    help='batch size')
parser.add_argument('--dropout', type=float, default=0.2,
                    help='dropout applied to layers (0 = no dropout)')
parser.add_argument('--seed', type=int, default=54321,
                    help='random seed')
parser.add_argument('--gpu', type=int, default=None)
parser.add_argument('--log_interval', type=int, default=2000, metavar='N',
                    help='report interval')
parser.add_argument('--save', type=str,  default='model/model.pt',
                    help='path to save the final model')
parser.add_argument('--cuda', type=str, default=True)
parser.add_argument('--optim', type=str, default='adam')
parser.add_argument('--lr', type=float, default=0.001)
parser.add_argument('--horizon', type=int, default=12)
parser.add_argument('--skip', type=float, default=24)
parser.add_argument('--hidSkip', type=int, default=5)
parser.add_argument('--L1Loss', type=bool, default=True)
parser.add_argument('--normalize', type=int, default=2)
parser.add_argument('--output_fun', type=str, default='sigmoid')
args = parser.parse_args()

args.cuda = args.gpu is not None
if args.cuda:
    torch.cuda.set_device(args.gpu)
# Set the random seed manually for reproducibility.
#希望结果复现,通过设置随机数种子的方法来实现。
torch.manual_seed(args.seed)
if torch.cuda.is_available():
    if not args.cuda:
        print("WARNING: You have a CUDA device, so you should probably run with --cuda")
    else:
        torch.cuda.manual_seed(args.seed)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

读取数据

Data = Data_utility(args.data, 0.6, 0.2, args.cuda, args.horizon, args.window, args.normalize);

  • 1
  • 2

Data_utility类的行参:
def init(self, file_name, train, valid, cuda, horizon, window, normalize = 2):

  • file_name 文件的路径
  • train 训练集的占比
  • valid 验证集占比
  • cuda 是否使用GPU训练
  • horizon 时间戳的理想界限
  • window 滑动窗口
  • normalize 使用标准化的模式供3种标准化模式,normalize = 2是按行方向的最大值归一化。

引用LSTNet时间序列预测的解释:

在这篇文章中,我们关注多变量时间预测的任务。正式的说,给定一系列观察到的时间序列信号Y = {y(1),y(2),…,y(T)},其中,yt∈Rn, n为变量维数,我们的目标是以滚动预测的方式预测一系列未来信号。为了预测y(T+h),我们需要提供{y(1),y(2),…,y(T)}的数据,其中h是当前时间戳的理想界限(horizon我不知道该如何翻译,暂且认为是一个极限值)。同样的,为了预测下一个时间戳的值y(T+h+1),需要提供{y(1),y(2),…,y(T),y(T+1)}。因此我们把时间戳T的输入矩阵表示为X(T)={y1,y2,…,yT},这个矩阵的维度是R(n*T)。

class Data_utility(object):
    # train and valid is the ratio of training set and validation set. test = 1 - train - valid
    def __init__(self, file_name, train, valid, cuda, horizon, window, normalize = 2):
        self.cuda = cuda;
        self.P = window;
        self.h = horizon
        #打开数据
        fin = open(file_name);
        #读取数据
        self.rawdat = np.loadtxt(fin,delimiter=',');
        self.dat = np.zeros(self.rawdat.shape);
        self.n, self.m = self.dat.shape;
        self.normalize = 2
        self.scale = np.ones(self.m);
        #数据标准化
        self._normalized(normalize);
        #训练集跟验证集分割
        self._split(int(train * self.n), int((train+valid) * self.n), self.n);
        #浮点型便于运算
        #时序数据每个维度上的所有数,例如100组时序数据,1个时序数据有10维,self.scale获得的是这10个维度上所有的数。
        self.scale = torch.from_numpy(self.scale).float();
        tmp = self.test[1] * self.scale.expand(self.test[1].size(0), self.m);

        if self.cuda:
            self.scale = self.scale.cuda();
        self.scale = Variable(self.scale);
        #标准化
        self.rse = normal_std(tmp);
        #相对绝对值误差
        self.rae = torch.mean(torch.abs(tmp - torch.mean(tmp)));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
'
运行

数据标准化

    def _normalized(self, normalize):
        #normalized by the maximum value of entire matrix.
       #不做标准化处理
        if (normalize == 0):
            self.dat = self.rawdat
        #找到数据集中的最大值,以它做标标准化
        if (normalize == 1):
            self.dat = self.rawdat / np.max(self.rawdat);
            
        #normlized by the maximum value of each row(sensor).
        #行方向最大值归一化
        if (normalize == 2):
            for i in range(self.m):
                #取出这一列的数值
                self.scale[i] = np.max(np.abs(self.rawdat[:,i]));
                #找到这一列的最大值,将这一列上的每个值都做相对这一列最大值的标准化
                self.dat[:,i] = self.rawdat[:,i] / np.max(np.abs(self.rawdat[:,i]));
            
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
'
运行

数据集分类

给定一系列观察到的时间序列信号Y = {y(1),y(2),…,y(T)},其中,yt∈Rn, n为变量维数,我们的目标是以滚动预测的方式预测一系列未来信号。为了预测y(T+h),我们需要提供{y(1),y(2),…,y(T)}的数据,其中h是当前时间戳的理想界限(horizon我不知道该如何翻译,暂且认为是一个极限值)。同样的,为了预测下一个时间戳的值y(T+h+1),需要提供{y(1),y(2),…,y(T),y(T+1)}。因此我们把时间戳T的输入矩阵表示为X(T)={y1,y2,…,yT},这个矩阵的维度是R(n*T)。

图画的有点潦草,大致就是,多维序列(图中是2维)取一个window的数据作为输入数据,再取window长度+horizon处的数据,作为训练时求解损失函数的真实值,这样子训练出来的模型,也是那window中的数据,预测经过horizon数据戳后的数据值的情况。数据集按这种方式,向下滑动,window统一向下移动一格,对应的真实值部分也向下移动一格,如途中蓝色框所示。
在这里插入图片描述

        self._split(int(train * self.n), int((train+valid) * self.n), self.n);
  • 1
    def _split(self, train, valid, test):
        
        train_set = range(self.P+self.h-1, train); #时间戳的极限+滑动窗口开始
        valid_set = range(train, valid);
        test_set = range(valid, self.n);
        self.train = self._batchify(train_set, self.h);
        self.valid = self._batchify(valid_set, self.h);
        self.test = self._batchify(test_set, self.h);
        
        
    def _batchify(self, idx_set, horizon):
        
        n = len(idx_set);
        X = torch.zeros((n,self.P,self.m));#先做个容器,大小为【window,时序数据的维度】
        Y = torch.zeros((n,self.m));#y是求损失函数时用到的ground truth 大小为【总时序数据集数量,时序数据的维度】
        #将数据按滑动窗口【这里滑动窗口取24*7=168个】的形式一组组的装起来,
        for i in range(n):
            end = idx_set[i] - self.h + 1;
            start = end - self.P;
            X[i,:,:] = torch.from_numpy(self.dat[start:end, :]);
            #Y这里取的是时间戳horizon里的数据
            Y[i,:] = torch.from_numpy(self.dat[idx_set[i], :]);

        return [X, Y];
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

分类好的训练集如下图所示,输入数据为168个时序数据,数据维度为137,对应的真实值为这组序列第168个数据往后挪动horizon的数据,数据长度为1,数据维度为137.:
在这里插入图片描述

模型搭建

本模型具有卷积组件、递归组件、递归-跳过层、时间注意层、自回归组件这五大模块。具体解读可以看多元时间序列预测之(二)LSTNet模型
作者在LSTnet.py__init__将要用到的函数全部搭建后,模型训练时候,向前传播,调用这些函数进行模型计算,这些函数就组成上述的几大模块。
输入尺寸为[128,168,137],128批时序数据,每批时序数据中有168组时序数据,每组时序数据有137维。下列是模型每一层输出情况:

===============================================================
out torch.Size([128, 137])
model Model(
  (conv1): Conv2d(1, 100, kernel_size=(6, 137), stride=(1, 1))
  (GRU1): GRU(100, 100)
  (dropout): Dropout(p=0.2, inplace=False)
  (GRUskip): GRU(100, 5)
  (linear1): Linear(in_features=220, out_features=137, bias=True)
  (highway): Linear(in_features=24, out_features=1, bias=True)
)
==========================================================================================
Layer (type:depth-idx)                   Output Shape              Param #
==========================================================================================
Model                                    [128, 137]                --
├─Conv2d: 1-1                            [128, 100, 163, 1]        82,300
├─Dropout: 1-2                           [128, 100, 163, 1]        --
├─GRU: 1-3                               [163, 128, 100]           60,600
├─Dropout: 1-4                           [128, 100]                --
├─GRU: 1-5                               [6, 3072, 5]              1,605
├─Dropout: 1-6                           [128, 120]                --
├─Linear: 1-7                            [128, 137]                30,277
├─Linear: 1-8                            [17536, 1]                25
==========================================================================================
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

每一层的输出分别对应着论文的示意图:
在这里插入图片描述

下列代码是训练前的常规准备:

#eval()去引号,其实就是LSTNet.Model(args, Data)
model = eval(args.model).Model(args, Data);

if args.cuda:
    model.cuda()
#模型总参数计算
nParams = sum([p.nelement() for p in model.parameters()])
print('* number of parameters: %d' % nParams)
#损失函数求解方法
if args.L1Loss:
    criterion = nn.L1Loss(size_average=False);
else:
    criterion = nn.MSELoss(size_average=False);
evaluateL2 = nn.MSELoss(size_average=False);
evaluateL1 = nn.L1Loss(size_average=False)
if args.cuda:
    criterion = criterion.cuda()
    evaluateL1 = evaluateL1.cuda();
    evaluateL2 = evaluateL2.cuda();
    
    
best_val = 10000000;
#优化器策略
optim = Optim.Optim(
    model.parameters(), args.optim, args.lr, args.clip,
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

模型训练与验证

try:
    print('begin training');
    #开始训练
    for epoch in range(1, args.epochs+1):
        epoch_start_time = time.time() #统计时间
        #正式开始训练,并返回训练损失值。
        train_loss = train(Data, Data.train[0], Data.train[1], model, criterion, optim, args.batch_size)
        #验证集的损失值,相对误差,错误率求解
        val_loss, val_rae, val_corr = evaluate(Data, Data.valid[0], Data.valid[1], model, evaluateL2, evaluateL1, args.batch_size);
        print('| end of epoch {:3d} | time: {:5.2f}s | train_loss {:5.4f} | valid rse {:5.4f} | valid rae {:5.4f} | valid corr  {:5.4f}'.format(epoch, (time.time() - epoch_start_time), train_loss, val_loss, val_rae, val_corr))
        # Save the model if the validation loss is the best we've seen so far.
        #保存最佳的模型
        if val_loss < best_val:
            with open(args.save, 'wb') as f:
                torch.save(model, f)
            best_val = val_loss
        #每五轮进行一次测试,计算测试集的准确率等
        if epoch % 5 == 0:
            test_acc, test_rae, test_corr  = evaluate(Data, Data.test[0], Data.test[1], model, evaluateL2, evaluateL1, args.batch_size);
            print ("test rse {:5.4f} | test rae {:5.4f} | test corr {:5.4f}".format(test_acc, test_rae, test_corr))
#通过键盘退出训练
except KeyboardInterrupt:
    print('-' * 89)
    print('Exiting from training early')
#训练到最后一轮时候计算模型的训练性能
# Load the best saved model.
with open(args.save, 'rb') as f:
    model = torch.load(f)
test_acc, test_rae, test_corr  = evaluate(Data, Data.test[0], Data.test[1], model, evaluateL2, evaluateL1, args.batch_size);
print ("test rse {:5.4f} | test rae {:5.4f} | test corr {:5.4f}".format(test_acc, test_rae, test_corr))

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

开始训练

模型通过上述代码中的
train_loss = train(Data, Data.train[0], Data.train[1], model, criterion, optim, args.batch_size)进行训练,解读train函数:

def train(data, X, Y, model, criterion, optim, batch_size):
    model.train();#训练模式,会进行梯度下降
    total_loss = 0;#本轮训练的总损失
    n_samples = 0;#样本总数
    for X, Y in data.get_batches(X, Y, batch_size, True):#通过迭代器data.get_batches()往外给处每批训练数据
        model.zero_grad();
        output = model(X);#模型输出对应批次的预测结果
        scale = data.scale.expand(output.size(0), data.m)
        loss = criterion(output * scale, Y * scale);#损失求解 默认用的L1loss 可修改
        loss.backward();#反向传播
        grad_norm = optim.step();#根据参数的梯度和超参数(如学习率)来更新模型的参数,从而使得损失函数最小化
        total_loss += loss.data.item();#将损失存起来
        n_samples += (output.size(0) * data.m);
    return total_loss / n_samples
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
'
运行

下面是模型向前传播代码以及传播操作示意图。

    def forward(self, x):
        batch_size = x.size(0);
        
        #CNN  [batchsize,C,H,W]
        c = x.view(-1, 1, self.P, self.m);
        c = F.relu(self.conv1(c));
        c = self.dropout(c);
        c = torch.squeeze(c, 3);
        
        # RNN 
        r = c.permute(2, 0, 1).contiguous();
        _, r = self.GRU1(r);
        r = self.dropout(torch.squeeze(r,0));

        
        #skip-rnn
        
        if (self.skip > 0):
            s = c[:,:, int(-self.pt * self.skip):].contiguous();
            s = s.view(batch_size, self.hidC, self.pt, self.skip);
            s = s.permute(2,0,3,1).contiguous();
            s = s.view(self.pt, batch_size * self.skip, self.hidC);
            _, s = self.GRUskip(s);
            s = s.view(batch_size, self.skip * self.hidS);
            s = self.dropout(s);
            r = torch.cat((r,s),1);
        
        res = self.linear1(r);
        
        #highway
        if (self.hw > 0):
            z = x[:, -self.hw:, :];
            z = z.permute(0,2,1).contiguous().view(-1, self.hw);
            z = self.highway(z);
            z = z.view(-1,self.m);
            res = res + z;
            
        if (self.output):
            res = self.output(res);
        return res;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
'
运行

在这里插入图片描述
把模型结构这一部分放一起:

===============================================================
out torch.Size([128, 137])
model Model(
  (conv1): Conv2d(1, 100, kernel_size=(6, 137), stride=(1, 1))
  (GRU1): GRU(100, 100)
  (dropout): Dropout(p=0.2, inplace=False)
  (GRUskip): GRU(100, 5)
  (linear1): Linear(in_features=220, out_features=137, bias=True)
  (highway): Linear(in_features=24, out_features=1, bias=True)
)
==========================================================================================
Layer (type:depth-idx)                   Output Shape              Param #
==========================================================================================
Model                                    [128, 137]                --
├─Conv2d: 1-1                            [128, 100, 163, 1]        82,300
├─Dropout: 1-2                           [128, 100, 163, 1]        --
├─GRU: 1-3                               [163, 128, 100]           60,600
├─Dropout: 1-4                           [128, 100]                --
├─GRU: 1-5                               [6, 3072, 5]              1,605
├─Dropout: 1-6                           [128, 120]                --
├─Linear: 1-7                            [128, 137]                30,277
├─Linear: 1-8                            [17536, 1]                25
==========================================================================================
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

每一层的输出分别对应着论文的示意图:
在这里插入图片描述

预测阶段

预测阶段其实就是向数据输入模型,然后得到值,将值保存起来,这些得到的值就是预测值,相对训练阶段,少了一个梯度下降,反向传播过程。就略过。

        val_loss, val_rae, val_corr = evaluate(Data, Data.valid[0], Data.valid[1], model, evaluateL2, evaluateL1, args.batch_size);

  • 1
  • 2
def evaluate(data, X, Y, model, evaluateL2, evaluateL1, batch_size):
    model.eval();
    total_loss = 0;
    total_loss_l1 = 0;
    n_samples = 0;
    predict = None;
    test = None;
    
    for X, Y in data.get_batches(X, Y, batch_size, False):
        output = model(X);
        if predict is None:
            predict = output;
            test = Y;
        else:
            predict = torch.cat((predict,output));
            test = torch.cat((test, Y));
        
        scale = data.scale.expand(output.size(0), data.m)
        total_loss += evaluateL2(output * scale, Y * scale).data.item()
        total_loss_l1 += evaluateL1(output * scale, Y * scale).data.item()
        n_samples += (output.size(0) * data.m);
    rse = math.sqrt(total_loss / n_samples)/data.rse
    rae = (total_loss_l1/n_samples)/data.rae
    
    predict = predict.data.cpu().numpy();
    Ytest = test.data.cpu().numpy();
    sigma_p = (predict).std(axis = 0);
    sigma_g = (Ytest).std(axis = 0);
    mean_p = predict.mean(axis = 0)
    mean_g = Ytest.mean(axis = 0)
    index = (sigma_g!=0);
    correlation = ((predict - mean_p) * (Ytest - mean_g)).mean(axis = 0)/(sigma_p * sigma_g);
    correlation = (correlation[index]).mean();
    return rse, rae, correlation;

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
'
运行

模型可视化

上面生成的图片是直接在LSTnet.py文件中调用torchviztorchinfo库来实现,直接运行这个.py文件即可生成可视化图

import torch
import torch.nn as nn
import torch.nn.functional as F
from torchviz import make_dot
import argparse
from torchinfo import summary
class Model(nn.Module):
    def __init__(self, args, data):
        super(Model, self).__init__()
        self.use_cuda = args.cuda
        self.P = args.window;
        self.m = data.m
        # self.m = 137  #配合该文件if __name__ == '__main__':中可视化生成图的时候开启
        self.hidR = args.hidRNN;#隐藏称RNN的数量
        self.hidC = args.hidCNN;#隐藏称CNN的数量
        self.hidS = args.hidSkip;
        self.Ck = args.CNN_kernel;
        self.skip = args.skip;
        self.pt = int((self.P - self.Ck)/self.skip)
        self.hw = args.highway_window
        self.conv1 = nn.Conv2d(1, self.hidC, kernel_size = (self.Ck, self.m));
        self.GRU1 = nn.GRU(self.hidC, self.hidR);
        self.dropout = nn.Dropout(p = args.dropout);
        if (self.skip > 0):
            self.GRUskip = nn.GRU(self.hidC, self.hidS);
            self.linear1 = nn.Linear(self.hidR + self.skip * self.hidS, self.m);
        else:
            self.linear1 = nn.Linear(self.hidR, self.m);
        if (self.hw > 0):
            self.highway = nn.Linear(self.hw, 1);
        self.output = None;
        if (args.output_fun == 'sigmoid'):
            self.output = F.sigmoid;
        if (args.output_fun == 'tanh'):
            self.output = F.tanh;
 
    def forward(self, x):
        batch_size = x.size(0);
        
        #CNN  [batchsize,C,H,W]
        c = x.view(-1, 1, self.P, self.m);
        c = F.relu(self.conv1(c));
        c = self.dropout(c);
        c = torch.squeeze(c, 3);
        
        # RNN 
        r = c.permute(2, 0, 1).contiguous();
        _, r = self.GRU1(r);
        r = self.dropout(torch.squeeze(r,0));

        
        #skip-rnn
        
        if (self.skip > 0):
            s = c[:,:, int(-self.pt * self.skip):].contiguous();
            s = s.view(batch_size, self.hidC, self.pt, self.skip);
            s = s.permute(2,0,3,1).contiguous();
            s = s.view(self.pt, batch_size * self.skip, self.hidC);
            _, s = self.GRUskip(s);
            s = s.view(batch_size, self.skip * self.hidS);
            s = self.dropout(s);
            r = torch.cat((r,s),1);
        
        res = self.linear1(r);
        
        #highway
        if (self.hw > 0):
            z = x[:, -self.hw:, :];
            z = z.permute(0,2,1).contiguous().view(-1, self.hw);
            z = self.highway(z);
            z = z.view(-1,self.m);
            res = res + z;
            
        if (self.output):
            res = self.output(res);
        return res;

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='PyTorch Time series forecasting')
    parser.add_argument('--hidCNN', type=int, default=100,
                        help='number of CNN hidden units')
    parser.add_argument('--hidRNN', type=int, default=100,
                        help='number of RNN hidden units')
    parser.add_argument('--window', type=int, default=24 * 7,
                        help='window size')
    parser.add_argument('--CNN_kernel', type=int, default=6,
                        help='the kernel size of the CNN layers')
    parser.add_argument('--highway_window', type=int, default=24,
                        help='The window size of the highway component')
    parser.add_argument('--clip', type=float, default=10.,
                        help='gradient clipping')
    parser.add_argument('--epochs', type=int, default=100,
                        help='upper epoch limit')
    parser.add_argument('--batch_size', type=int, default=128, metavar='N',
                        help='batch size')
    parser.add_argument('--dropout', type=float, default=0.2,
                        help='dropout applied to layers (0 = no dropout)')
    parser.add_argument('--seed', type=int, default=54321,
                        help='random seed')

    parser.add_argument('--log_interval', type=int, default=2000, metavar='N',
                        help='report interval')

    parser.add_argument('--optim', type=str, default='adam')
    parser.add_argument('--lr', type=float, default=0.001)
    parser.add_argument('--horizon', type=int, default=12)
    parser.add_argument('--skip', type=float, default=24)
    parser.add_argument('--hidSkip', type=int, default=5)
    parser.add_argument('--L1Loss', type=bool, default=True)
    parser.add_argument('--normalize', type=int, default=2)
    parser.add_argument('--output_fun', type=str, default='sigmoid')
    args = parser.parse_args()

    x = torch.randn(128,168,137)
    x = x.cuda()
    model = Model(args, x)
    model = model.cuda()
    y = model(x)
    #生成模型结构图
    vise = make_dot(y, params=dict(model.named_parameters()))
    vise.view()
    #查看模型输出尺寸信息
    # input = torch.from_numpy(input).to('cuda:1').to(torch.float32).requires_grad_()
    print('===============================================================')
    print('out', y.shape)
    print('model', model)
    summary(model=model, input_size=(128,168,137), device="cpu")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/999215
推荐阅读
相关标签
  

闽ICP备14008679号