本篇文章5841字,读完约15分钟
雷锋。这篇文章是英伟达教你用pytorch从原理到实战构建rnn的第二部分。请点击这里阅读第一部分。这篇文章最初发表在英伟达的博客上,由雷锋编辑。
代码实现在开始创建神经网络之前,我需要设置一个数据加载器。对于深度学习,在一批数据样本上运行模型是非常常见的,这可以通过并行计算加速训练,并且在每一步都有更平滑的梯度。现在让我们开始,下面将解释如何对前一章中描述的堆栈操作进行批处理。pytorch文本库的内置系统可以组合相似长度的样本来自动生成批处理,下面的python代码将一些数据加载到系统中。运行这些代码后,train_iter、dev_iter和test_iter中的迭代器将在snli培训、验证和测试期间分批循环。
从torchtext导入数据,数据集
text = dataset . snli . parsedtextfield(lower = true)
transitions = dataset . snli . shift reduce field()
标签=数据.字段(顺序=假)
train,dev,test = dataset . snli . splits(
文本、过渡、标签、wv_type=手套. 42b)
text.build_vocab(培训、开发、测试)
train_iter,dev_iter,test _ ITER = data . bucket ITER . splits(
(培训、开发、测试),批次大小=64)
您可以在train.py中找到其余的代码,包括训练循环和测量精度。现在我们来谈谈模型。如上所述,旋转编码器包括一个参数化的简化层和一个可选的循环跟踪器来跟踪上下文。这是通过每次神经网络读取一个单词或应用reduce时更新隐藏状态来实现的。下面的代码实际上表明创建一个spinn只意味着创建这两个子模块,并将它们放在一个容器中以备将来使用。
进口火炬
来自火炬进口nn
#从pytorch的神经网络包中子类化模块类
类别微调(nn.module):
def __init__(自我,配置):
超级(旋转,自我)。__init__()
self.config = config
self . reduce = reduce(config . d _ hidden,config.d_tracker)
如果config.d_tracker不是无:
self . tracker = tracker(config . d _ hidden,config.d_tracker)
创建模型时,旋转。__init__被调用一次。它分配和初始化参数,但不执行任何神经网络操作,也不涉及计算图的创建。在spinn中定义的每一批新数据上运行的代码。在pytorch中,用户自定义模型前馈通道的方法称为“正向”。事实上,它是上面提到的堆栈操作算法的实现。在普通的python中,它运行在缓冲区和堆栈批处理上——其中一个用于每个样本。迭代转换过程中包含的“移位”和“缩减”操作(如果存在),运行跟踪器,并批量运行每个样本以应用“移位”操作,或者加入需要“缩减”操作的样本列表。然后对列表中的所有样本运行缩减层,并将结果推回到相关堆栈。
向前定义(自、缓冲、转换):
#输入以单词嵌入的单一张量形式出现;
#我需要它是一个堆栈列表,每个堆栈对应一个示例
#这一批,我们可以独立推出。中的单词
#每个例子都被颠倒了,所以他们可以
#通过从每一个的末尾弹出来从左到右读取
#列表;它们的前缀也是空值。
缓冲区=[列表(torch.split(b .挤压(1),1,0))
对于b in torch.split(缓冲液,1,1)]
#我们还需要在每个堆栈的底部有两个空值,
#因此我们可以从输入中的空值复制;这些明渠
#都是必需的,这样即使
#缓冲区或堆栈为空
堆栈= [[buf[0],buf[0]]用于缓冲区中的buf]
如果有(自我,追踪器):
self.tracker.reset_state()
对于转换中的trans_batch:
如果有(自我,追踪器):
#我之前描述追踪器是用4
#个参数(context_t,b,s1,s2),但这里我
#将堆栈内容作为单个参数提供
#在跟踪器中存储上下文时
#对象本身。
tracker_states,_ = self.tracker(缓冲区、堆栈)
否则:
tracker _ States = ITER tools . repeat(无)
左,右,轨迹= [],[],[]
batch = zip(trans_batch,缓冲区,堆栈,跟踪器_states)
对于过渡、buf、堆栈、批量跟踪:
如果转换== shift:
stack.append(buf.pop())
elif跃迁==减少:
rights.append(stack.pop())
lefts.append(stack.pop())
trackings.append(跟踪)
如果权利:
reduce = ITER(self . reduce(left,rights,trackings))
对于过渡,以zip格式堆叠(trans_batch,堆叠):
如果转换==减少:
stack.append(下一个(缩减))
返回[stack.pop()(堆栈中的堆栈)
调用self.tracker或self.reduce将相应地在tracker或reduce子模块中运行“前进”模式。这需要一个样本列表来执行运算。所有数学运算密集,gpu加速,和基于批处理的运算发生在追踪器和减少。因此,在主“转发”模式下,它在不同的样本上分别运行;为批次中的每个样本保留单独的缓冲区和堆栈是有意义的。为了更干净地编写这些函数,我将使用一些辅助工具将这些样本列表转换成批处理张量,反之亦然。
我倾向于让reduce模块自动批处理参数以加快计算速度,然后取消它们的匹配,以便它们可以在以后被分别推送和弹出。将每组左右子短语放在一起,以显示父短语的合成功能是treelstm,它是常规lstm的变体。这个复合函数要求所有子树的状态应该由两个张量组成,一个隐藏状态h和一个存储单元状态c。有两个因素定义这个函数:在子树的隐藏状态中运行的两个线性层(nn.linear)和非线性合成函数tree_lstm,它将线性层的结果与子树的存储单元的状态相结合。在spinn中,这是通过添加第三个运行在跟踪器隐藏状态的线性层来扩展的。
def tree_lstm(c1,c2,lstm_in):
#将两个子代的存储单元状态(c1,c2)取为
#以及孩子们的线性变换的总和
#隐藏状态(lstm_in)
#转换后的隐藏状态的总和被分解为
#候选输出a和四个门(I、f1、f2和o)。
a,I,f1,f2,o = lstm_in.chunk(5,1)
c = a . tanh()* I . sigmoid()+f1 . sigmoid()* C1+F2 . sigmoid()* C2
h = o.sigmoid() * c.tanh()
返回h,c
类别缩减(nn.module):
def _ _ init _ _(自我,大小,跟踪器_大小=无):
超级(减少,自我)。__init__()
self.left = nn.linear(尺寸,5 *尺寸)
self.right = nn.linear(size,5 * size,bias=false)
如果tracker_size不是无:
self . track = nn . linear(tracker _ size,5 * size,bias=false)
向前定义(自身,左入,右入,跟踪=无):
左,右=批次(左_入),批次(右_入)
跟踪=批处理(跟踪)
lstm_in = self.left(left[0])
lstm_in += self.right(right[0])
如果有(自我,跟踪):
lstm_in += self.track(跟踪[0])
返回不平衡(tree_lstm(左[1],右[1],lstm_in))
由于缩减层和跟踪器以相似的方式在lstm上运行,批处理和取消批处理辅助功能将在一对隐藏和内存状态下运行。
def批处理(状态):
如果states为none:
不返回
状态=元组(状态)
如果状态[0]为无:
不返回
# states是维度(1,2h)的b张量列表
#这将返回两个维度(b,h)的张量
返回torch.cat(状态,0)。组块(2,1)
def unbatch(状态):
如果状态为无:
返回itertools.repeat(无)
# state是一对维数为(b,h)的张量
#这将返回维度(1,2h)的b张量列表
返回火炬。分离(火炬。卡特彼勒(状态,1),1,0)
这就是全部的实际解释。包括跟踪器在内的其余代码都在spinn.py中..至于从两个句子代码计算snli类别,并将结果与目标进行比较以给出最终损失变量的分类层,在模型中,py Spinn的“向前”代码及其子模块产生极其复杂的计算图表(如下),最终导致损失。数据集中的每一批的细节都完全不同,但是每次你都可以简单地调用loss.backward()进行自动反向传播,而且它的成本非常低。Loss.backward()是pytorch的一个内置函数,它可以在计算图的任何一点进行反向传播。
完整代码中的模型和超参数的性能可以与原始spinn论文进行比较。但是在gpu上,它要快几倍--它的实现充分利用了批处理和pytorch的高效率。最初的spinn编译计算图表花了21分钟(这意味着执行期间的bug修复周期至少是原来的21分钟),培训花了大约5天。本文描述的这个版本没有便宜的步骤。在特斯拉k40 gpu上训练只需要13个小时,相当于在quadro gp100上训练9个小时。
集成强化学习,即上述模型的无跟踪器版本,实际上特别适用于张量流的tf.fold,这是张量流的一种新的特殊语言,用于动态计算图的特殊情况。包含跟踪器的版本更难实现。这背后的原因是加入跟踪器意味着从递归模式切换到基于堆栈的模式。在上面的代码中,这以最直观的形式显示,它根据输入值使用条件分支。Fold没有内置的条件分支op,因此模型中的图结构只取决于输入结构,而不是值。此外,几乎不可能创建一个追踪器决定如何解析输入语句的微调器。这是因为图的结构是折叠的——虽然它们依赖于输入样本的结构,但是在加载输入样本之后,它必须是完全固定的。
deepmind和谷歌大脑的研究人员正在探索一个类似的模型。他们使用强化学习来训练一个旋转跟踪器来解析输入语句,而不需要任何外部解析数据。本质上,这个模型从随机猜测开始,当它的分析在整个分类任务中产生更好的准确性时,它奖励自己去学习。研究人员写道,他们“使用批量1,因为根据策略网络(tracker)的样本,对于每个样本,计算图需要在每次迭代后重新构建。”然而,即使是像这样复杂的随机结构的神经网络,研究人员也只能在pytorch上批量训练。
Pytorch也是第一个在算法库中内置强化学习的框架,即它的随机计算图。这使得策略梯度强化学习像反向传播一样容易使用。如果您想将其添加到上述模型中,您只需要重写主spinn代码的前几行,生成如下所示的相同循环,并让跟踪器定义进行任何解析器转换的概率。
!# nn.functional包含不带参数的神经网络操作
从torch.nn导入功能为f
过渡= []
对于范围内的I(len(buffers[0])* 2-3):#我们知道有多少步
#获取每种解析器转换的原始分数
tracker_states,transition_scores = self.tracker(缓冲区,堆栈)
#使用softmax函数将分数标准化为概率,
#然后从这些概率定义的分布中取样
transition _ batch = f . softmax(transition _ scores)。多项式()
transitions . append(transition _ batch
当批处理一直向下运行并且模型知道其类别预测的准确性时,除了反向传播之外,我可以通过图的其余部分以传统方式将奖励信号发送回随机计算图的这些节点:
#每例损失应包含一个损失,而均值和标准差
#代表许多批次的平均值
奖励=(-损失-平均值)/标准
对于过渡中的过渡:
过渡。加强(奖励)
#将随机节点连接到最终损失变量
#这样反向传播就可以找到它们,乘以零
#因为这个技巧不应该改变损失值
损耗=损耗。平均值()+ 0 *总和(跃迁)。总和()
#通过确定性节点执行反向传播
#随机节点的策略梯度rl
损失。向后()
谷歌研究人员从spin+获得的增强学习报告的结果比从snli获得的原始spin稍好,尽管它的增强学习版本没有预先计算语法树。深度强化学习在自然语言处理中的应用是一个全新的领域,其研究问题非常广泛。通过将强化学习整合到框架中,pytorch大大降低了使用门槛。
由维安维达和雷锋编辑。
“张量流&神经网络算法高级应用类”即将开始!
从初级到高级,理论+实战,一站式深入了解张量流!
本课程面向深入学习的开发人员,教授如何使用张量流解决特定问题,如图像识别和文本分析。为期10周的课程将从张量流的原理和基本实践技能开始,逐步教会学生如何在张量流上构建cnn、自编码、rnn、gan等模型,最终掌握一套基于张量流的深度学习和发展的专业技能。
作为思想工作的高级技术专家,童达和白华川两位教师在构建大数据平台和开发深度学习系统方面有着丰富的经验。
时间:每周二和周四晚上20: 00到21: 00
课程时长:共20小时,10周完成,每周2次,每次1小时
在线教学地址:lae iphone/special/custom/mooc 04
相关文章:
从原则到实战,英伟达教你用pytorch(1)构建rnn
Pytorch的预训练,是时候学习了
甘很复杂吗?如何用不到50行代码训练gan(基于pytorch)
雷锋文章版权所有。严禁擅自转载。详情请参考转载说明。
来源:搜狐微门户
标题:从原理到实战 英伟达教你用PyTorch搭建RNN(下)
地址:http://www.shwmhw.com/shxw/63188.html