您的位置:首页 > 理论基础 > 计算机网络

pytorch从头开始实现一个RNN(循环神经网络)

2017-11-14 17:12 931 查看
tensorflow有google的背书,强大的社区,加上开源的时机,alphago的名气等等吧,确实占了先机,但对于深度学习的初学者,真的是噩梦。是一个非得把简单的东西,搞得无比复杂的系统。



比如,tf看似为了灵活,它的示例里,所有的variable都需要你自己去初始化。比如,你可以初始化为0或全1,或者用正态分布随机,还可以设定均值,方差,当然还可以用均匀分布来初始化。但,我想说的是,实践证明,这些设定,与网络训练结果的影响基本没有!!!

只在人群中看了pytorch一眼,就深深着迷了。

其实,就写一个神经网络的训练任务,同样数据准备花的时间,远比搭网络要多得多。但其实这和我们学习与理解深度学习,没有太大的联系。

本文就想写写,直面网络,抛开各种参数设定,数据准备,学习本质。

对于一个网络,肯定有一个input,我们要确定好它的维度就好了。

1,RNN的input,如果就一步计算而言,是二维的,也就是1*vocab_size。如果char-rnn,也就是字母级别的,如果只看小写英文字母,这个vocab_size就是26。也就是说,每一个字母都表示为[1,26]的tensor(这就是传说中的one-hot,比如a = [1,0,0,....0],b=[0,1,0,....0])。

2,rnn的输入,需要有一个input_size=vocab_size,一个hidden_size,这个是rnn的神经元数,数量随意,越大越慢,但信息表达能力越强,然后还有一个output_size,就是rnn输出的维度。最终input经过rnn,由1*vocab_size,就成1*output_size

3.考虑批量实现,也就是一次输入N个char,也就是我们常说的sequence。在名字分类的任务,一个名字串,比如alice,就是一个seq。如果是句子情感分析,一个句子就是一个seq。如果是文章情感分析,一篇文章就是一个seq。

RNN的批量输出要处理成[seq_len, N, vocab_size]。这个新手很容易晕。一般我们处理原始数据,得到[N,seqlen]的数据。比如N个名字,N条句子或N篇文章等。这时候要转置成[seq_len,N],然后再embedding成[seq_len,N,vocab_size]。

这是因为,在RNN或LSTM的计算单元,是按for i in range(seq_len):来计算每一个输出,一个个往写进,后一批使用前一批的hidden_status。

import torch
import torch.nn as nn
from torch.autograd import Variable

class RNN(nn.Module):
#这里自己实现一个RNN.
#input_size就是char_vacab_size=26,hidden_size随意,就是隐层神经元数,output_size要分成categories类
def __init__(self, input_size, hidden_size, output_size):
super(RNN, self).__init__()

self.hidden_size = hidden_size

self.i2h = nn.Linear(input_size + hidden_size, hidden_size)
self.i2o = nn.Linear(input_size + hidden_size, output_size)
self.softmax = nn.LogSoftmax()

def forward(self, input, hidden):
combined = torch.cat((input, hidden), 1)#input:[N,26]+[N,hidden_size]=N,26+hidden_size -> N*hidden_size
hidden = self.i2h(combined) #N*hidden_size,这里计算了一个hidden,hidden会带来下一个combined里
output = self.i2o(combined) # N*output_size,就是一个普通全连接层
output = self.softmax(output)#softmax
return output, hidden

def initHidden(self):
return Variable(torch.zeros(1, self.hidden_size))#hidden=[1,hidden_size]

n_hidden = 128
target_size = 10#分类问题,分成几类
rnn = RNN(26, n_hidden, target_size)

hidden = rnn.initHidden()

#如果输入input,是一个char,按one-hot编码的,假设char空间是26(小写英文字母)
input = Variable(torch.zeros((1,26)))#[torch.FloatTensor of size 1x26],成员都是0
print(input)

output, next_hidden = rnn(input, hidden)#得到[1*10,1*128]
print(output.data.size(),next_hidden.data.size())

如上只是写了单步的运算,但确定了这个就好办了。比如依照需要,我们要实现对“英文名字字符串”的分类。每个名字构成一个seq。

每一个seq结束就应该初始化一次hidden。然后对名字里的每个char进行循环。计算loss,并反向求导。然后在导数方向上对参数进行调整。如果是batch,那每个batch结束yi

关于作者:魏佳斌,互联网产品/技术总监,北京大学光华管理学院(MBA),特许金融分析师(CFA),资深产品经理/码农。偏爱python,深度关注互联网趋势,人工智能,AI金融量化。致力于使用最前沿的认知技术去理解这个复杂的世界。

扫描下方二维码,关注:AI量化实验室(ailabx),了解AI量化最前沿技术、资讯。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: