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

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

640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1

比如,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量化最前沿技术、资讯。640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1

转载于:https://my.oschina.net/u/1996852/blog/1573507

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值