第十三周周报
- 摘要
- Abstract
- 一、机器学习——Transformer(上)
- 1. Sequence to Sequence(Seq 2 Seq,序列到序列模型) 的应用
- 2. Transformer的结构
- 2.1 Transformer encoder(Transformer 编码器)
- 二、Pytorch学习
- 1. 网络模型的保存与读取
- 三、数学拓展
- 1. 标量、向量、张量求导的推导
- 总结
摘要
本周周报在机器学习的理论内容中,描述了Sequence to Sequence model的重要性以及使用的普遍性,其应用场景非常广泛,比如:语音识别、文本翻译、语音翻译、聊天机器人、Q&A(NPL)、语法辨析、多标签分类中的应用。文章通过介绍Seq2Seq,引出了Seq2Seq最常用的模型——Transformer,然后介绍了Transformer的结构以及深入探讨了Transformer encoder,其中包括encoder的功能、encoder的结构,此外还提及了残差连接(residual connection)与layer layer normalization的概念以及细节内容。在代码实战的部分,本周描述了如何在pytorch通过代码实现保存与读取网络模型。最后,本周报还展示了标量、向量、张量求导的推导过程。
Abstract
This week’s report focused on the importance and widespread applications of Sequence to Sequence (Seq2Seq) models in the field of machine learning. Various application scenarios were highlighted, including speech recognition, text translation, speech translation, chatbots, Q&A (NLP), grammar analysis, and multi-label classification. The introduction of Seq2Seq led to a discussion of the most commonly used model——Transformer. In addition,The structure of the Transformer was introduced, with an in-depth exploration of the Transformer encoder that included the encoder’s functions and architecture. Additionally, the concepts of residual connections and layer normalization were mentioned along with detailed explanations. In the practical coding section, the implementation of saving and loading neural network models using PyTorch was described. Finally, the derivation processes for scalars, vectors, and tensors were presented.
一、机器学习——Transformer(上)
Transformer 是一个基于自注意力的序列到序列(Sequence to Sequence)的模型
Transformer 与基于循环神经网络的序列到序列模型不同,其可以能够并行计算。
1. Sequence to Sequence(Seq 2 Seq,序列到序列模型) 的应用
序列到序列模型输入和输出都是一个序列,输入与输出序列长度之间的关系有两种情况。
第一种情况下,输入跟输出的长度一样
第二种情况下,机器决定输出的长度。
序列到序列模型有广泛的应用,通过这些应用可以更好地了解序列到序列模型。
应用场景如下:
1、语音识别:
输入是声音信号,输出是语音识别的结果(即与输入声音信号对应的文本)
我们用圆圈来表示文本,例如,每个圆圈代表中文中的一个方形字符。
输入和输出的长度之间存在某种关系,但没有绝对关系
输入声音信号的长度为T,并且不能基于T获得输出长度N。
事实上,输出的长度可以由机器本身决定,机器可以通过听这个声音信号的内容来确定输出的语音识别结果。
2、机器翻译:
机器输入一个语言的句子,输出另外一个语言的句子。
输入句子的长度为N,输出句子的长度也为N’。
输入四个单词“machine learning”,输出两个英文单词:“machine learning”
(然而,并非所有中英文关系都涉及输出占输入的一半)
N 跟 N′ 之间的关系由机器决定。
3、语音翻译:
我们对机器说一句话,比如“机器学习”,机器直接将它听到的英语声音信号翻译成中文。
既然将语音识别系统与机器翻译系统连接可以实现语音翻译的效果,为什么我们需要做语音翻译?
因为世界上许多语言都没有文字,因此无法进行语音识别。
因此,有必要对这些语言进行语音翻译,直接将其翻译成文本
以闽南语的语音识别为例,闽南语书面语不是很受欢迎,可能不容易被公众理解。
因此,我们想通过用闽南语对机器说一个句子来进行语音翻译,机器直接输出一个意思相同的普通话句子,让普通人能够理解。
我们可以训练一个神经网络,接收某种语言的声音信号,输出另一种语言的文本
我们需要了解闽南语声音信号与白话文之间的对应关系。YouTube上有很多地方电视剧,都有闽南语发音和白话字幕。因此,只要你下载他们的闽南语发音和白话字幕,闽南语声音信号和白话汉语之间就会有对应关系。你可以训练一个模型来进行闽南语语音识别:输入闽南语,输出白话。
李宏毅的实验室从当地电视剧中下载了1500小时的数据,并用它来训练语音识别系统。
可能存在一些问题,例如乡村戏剧中存在大量噪音和音乐,字幕可能不一定与声音相对应。
您可以忽略这些问题,直接用声音信号作为输入,白话文本作为输出来训练模型。
这样,训练就可以创建一个闽南语语音识别系统。
4、语音合成:
输入文字、输出声音信号就是语音合成(Text-To-Speech,TTS)。
那时还没有真的做端到端(end-to-end)的模型,以闽南语的语音合成为例,其使用的模型还是分成两阶
首先模型会先把白话文的文字转成闽南语的拼音
再把闽南语的拼音转成声音信号。
从闽南语的拼音转成声音信号这一段是通过序列到序列模型 echotron 实现的。
5、聊天机器人(ChatBot)
除了语音以外,文本也很广泛的使用了序列到序列模型。
例如,使用序列到序列模型可用于训练聊天机器人。
聊天机器人是当我们对它说一句话,它会给出回应。
由于聊天机器人的输入和输出都是文本,这是一个向量序列,因此可以使用序列到序列模型来创建聊天机器人。
我们可以收集大量来自人们的对话(如电视剧、电影等中的台词)
假设在对话里面有出现,一个人说:“Hi”,另外一个人说:“Hello! How are you today?”。我们可以教机器,看到输入是“Hi”,输出就要跟“Hello! How are you today?”越接近越好。
6、问答任务(Seq2Seq在NLP上的应用)
序列到序列模型在自然语言处理(NLP,Nature Language Process)的领域的应用很广泛。
而很多自然语言处理的任务都可以想成是问答(Question Answering,QA)的任务
问答就是给机器读一段文字,问机器一个问题,希望它可以给出一个正确的答案。
比如下面是一些例子:
- 翻译:机器阅读的文章是一个英语句子,问题是这个句子的德语翻译是什么?输出答案为德语。
- 自动摘要:向机器阅读一篇长篇文章,并要求它识别文章的关键点(即给机器一段文本,问题是这段的摘要是什么)
- 情绪分析:机器需要自动判断一个句子是积极的还是消极的。如果将情绪分析视为问答问题,那么问题是给定的句子是积极的还是消极的,答案由机器提供。
只要是输入一个序列,输出是一个序列,序列到序列模型就可以解。
因此,各种自然语言处理问题通常可以被视为问答问题,可以使用序列到序列模型来解决。
以上的例子中,序列到序列模型的输入是一篇文章和一个问题,输出就是问题的答案。问题加文章合起来是一段很长的文字,答案是一段文字。
尽管使用序列到序列模型可以解决各种自然语言处理问题,但对于大多数自然语言处理任务或语音相关任务,为这些任务定制模型通常会产生更好的结果。
序列到序列模型就像一把瑞士刀,瑞士刀可以解决各种各样的问题,比如砍木头或切蔬菜,但它不一定是最好的。
因此,为各种任务定制的模型通常比只使用序列到序列模型的模型更好。
用于谷歌Pixel 4手机语音识别的模型不是序列到序列模型,而是RNN Transformer模型,它是针对语音的某些特征设计的,性能更好。
7、句法解析(syntactic parsing)
很多问题都可以用序列到序列模型来解决,以句法解析为例
为机器提供一段文本,例如“深度学习非常强大”,它将生成一个句法树。
句法树显示:
(1)deep 加 learning的组合形成一个名词短语
(2)very 加 powerful 的组合则形成一个形容词短语,在形容词短语中添加“is”可以将其转换为动词短语
(3)动词短语和名词短语的组合构成一个句子
如下图所示:
在句法分析的任务中,输入是一段文字,输出是一个树状的结构
而一个树状的结构可以看成一个序列,该序列代表了这个树的结构
如下图所示:
1、(NP deep learning):表示deep learning 为名词短语
2、(ADJV very powerful) :表示very powerful为形容词短语
3、(VP is (ADJV very powerful) ) :表示is very powerful 为动词短语
4、(S(NP deep learning)(VP is (ADJV very powerful) ) ):表示deep learning is very powerful为一个sentence(句子)
由此就可以把树转换成一个序列结构
把树的结构转成一个序列以后,我们就可以用序列到序列模型来做句法分析
8、多标签分类
多标签分类(multi-label classification)任务也可以用序列到序列模型。
多类的分类跟多标签的分类是不一样的。
多分类问题(multi-class classification)是指分类的类别数大于 2。
而多标签分类是指同一个东西可以属于多个类。
在做文章分类的时候,同一篇文章可能属于多个类,文章 1 属于类 1 和类 3,文章 3 属于类 3、9、17。
多标签分类问题不能作为多分类问题直接解决。
例如,如果将这些文章放入一个分类器中,该分类器最初只输出得分最高的答案,如果直接设定阈值,则只输出得分最高的前三个答案。
这种方法不可行,因为每篇文章对应的类别数量根本不同
有些文章对应的数目。对应的class数目是两个,有的是一个,有的是三个。
因此,有必要使用序列到序列模型,如图所示:
输入文章时,输出是类别,机器确定输出类别的数量。
综合上述的例子,我们可以看到sequence to sequence model的应用场景非常广泛,基本上可以解决我们生活中的事情。
sequence to sequence model它是一个很powerful的model,它是一个很有用的model,那么现在就是要来学怎么做?
2. Transformer的结构
一般的sequence to sequence model会分成编码器和解码器
编码器负责处理输入的序列,再把处理好的结果“丢”给解码器,由解码器决定要输出的序列。
如下图所示:
sequence to sequence model典型的模型就是 Transformer,其有一个编码器架构和一个解码器架构
Transformer架构图如下图示:
2.1 Transformer encoder(Transformer 编码器)
sequence to sequence model encoder要做的事情就是给一排向量输出,另外一排向量。
self-attention(注意力机制)、RNN(循环神经网络)、CNN(卷积神经网络)都能做到输入一排向量,输出一排向量。
而Transformer的编码器使用的是自注意力,输入一排向量,输出另外一个同样长度的向量。
编码器里面会分成很多的block(块)
每一个块都是输入一排向量,输出一排向量。
输入一排向量到第一个块,第一个块输出另外一排向量,以此类推,最后一个块会输出最终的向量序列。
Transformer encoder的结构图如下图所示:
Transformer 的编码器的每个block(块)并不是神经网络的一个layer(层)
每个block的结构如图所示:
在每个block中,输入一排vector后,经过注意机制,考虑整个sequence的信息,并输出另一排vector。
然后,这排vector将被“抛出”到fully connected的网络中,并输出另一排vector,这是block的输出。
那事实上在原来的transformer里面,他做的事情是更复杂的。
那在我们刚才讲self-attention的时候,输入一排vector就输出一排vector,这边的每一个vector它是考虑的所有的input以后所得到的结果
但是Transformer 里面加入了 残差连接(residual connection) 的设计
如下图所示:
最左边的向量 b 输入到自注意力层后得到向量 a,输出向量 a 加上其输入向量 b 得到新的输出。
这种就叫做残差连接(residual connection)
得到残差的结果以后,再做层归一化(layer normalization)。
层归一化(layer normalization)比批量归一化(batch normalization)更简单,其不需要考虑批量的信息,而批量归一化需要考虑批量的信息。
如下图所示:
层归一化输入一个向量,输出另外一个向量。
那这个layer normalization做的事情是什么呢?
layer normalization会计算输入向量的平均值和标准差。
批量归一化是对不同样本不同特征的同一个维度去计算均值跟标准差。
而层归一化是对同一个特征、同一个样本里面不同的维度去计算均值跟标准差,接着做个归一化。
计算如下:
输入向量 x 里面每一个维度减掉均值 m 再除以标准差 σ 以后得到 x′ 。x′ 就是层归一化的输出。
公式如下:
x i ′ = x i − m σ {\color{Red} x_{i}^{\prime}=\frac{x_{i}-m}{\sigma} } xi′=σxi−m
1、得到层归一化的输出以后,该输出才是fully connected network的输入。
2、输入到fully connected network,还有一个residual connection,把fully connected network的输入跟它的输出加起来得到新的输出。
3、接着把residual connection的结果再做一次layer normalization 得到的输出才是 Transformer encoder里面一个block的输出。
其过程如下图所示:
Transformer encoder里面一个block的输入到输出整个过程图如下图所示:
二、Pytorch学习
1. 网络模型的保存与读取
网络模型的保存与读取——方式1
先创建一个model_save(保存)与model_load(加载)的python文件
在model_save 中输入如下代码:
import torch
import torchvisionvgg16 = torchvision.models.vgg16(pretrained=False)# 保存方式1
torch.save(vgg16, "vgg16_method1.pth")
print(vgg16)
可以看到已经生成了一个vgg_16_method1.pth的文件
接下来我们在model_load的python文件中,对其进行读取,然后输出模型,看看是否跟save的一致。
model_load输入如下代码:
import torch# 读取保存方式1
model = torch.load("vgg16_method1.pth")
print(model)
网络模型的保存与读取——方式2(字典法,官方推介)
接下来介绍第二种保存方式,这种方式是通过字典的方式保存(字典是python中的一种数据格式)
这种保存方式只保存模型的参数,优点就是占用的空间小
在model_save的代码如下:
import torch
import torchvision# 保存方式2
torch.save(vgg16.state_dict(), "vgg16_method2.pth")
model_load输入如下代码:
import torch# 读取保存方式2
model2 = torch.load("vgg16_method2.pth")
print(model2)
其输出结果如下:
可以看到其保存的是一种字典形式,而不再是一个网络模型:
如果我们想输出的是模型,我们可以输入如下代码:
其原理就是将vgg16保存的字典(参数),直接导入到模型中。
但是前提是我们要声明一个vgg16的初始化模型
import torch
import torchvision# 读取保存方式2
vgg16 = torchvision.models.vgg16(pretrained=False)
vgg16.load_state_dict(torch.load("vgg16_method2.pth"))
print(vgg16)
输出结果如下:
保存与读取的陷阱(自己创建的网络模型)
下面来特别声明一个陷阱,就是自己编写的网络模型保存与读取时,会出现的错误,以及要注意的事项。
错误如下:
首先在model_save中输入如下代码:
import torch# 陷阱
class Net(torch.nn.Module):def __init__(self):super(Net, self).__init__()self.conv1 = torch.nn.Conv2d(3, 6, 5)def forward(self, x):x = self.conv1(x)return xnet = Net()
torch.save(net, "net_method3.pth")
在model_load中输入如下代码:
import torch# 读取自己的网络模型
model2 = torch.load("net_method3.pth")
print(model2)
可以看到结果直接报错,其原因是找不到名为Net模型在这个python文件中
(因为vgg16是现有公开的模型,而这个Net是我们自己创建的。所以我们这里python是找不出来的)
正确用法:
在model_load中引入model_save文件
from p21_model_save import *# 读取自己的网络模型
model2 = torch.load("net_method3.pth")
print(model2)
可以看到输出没有报错了
三、数学拓展
1. 标量、向量、张量求导的推导
本小节参照文章:Vector, Matrix, and Tensor Derivatives
author:Erik Learned-Miller
目的是帮助你学习对向量、矩阵和高阶张量(具有三维或更多维度的数组)求导并帮助你对向量、矩阵和高阶张量求导。
首先来说明一下为什么我们要学习求导,因为我们在深度学习的优化参数中,反向传播是必不可少的步骤,而反向传播又是基于梯度的基础上完成的,梯度又涉及到求偏导。
我们之前学习的求导,都是基于一个一元函数或者二元函数(标量)对其中变量进行求导/求偏导的。
例如
y = 2 x ; y 对 x 求导得 d y d x = 2 y=2x;y对x求导得\frac{\mathrm{d} y}{\mathrm{d} x} = 2 y=2x;y对x求导得dxdy=2
亦或
z = 2 x + 2 y ; z 对 x 求偏导得 ∂ z ∂ x = 2 z=2x+2y;z对x求偏导得\frac{\mathrm{\partial z } }{\mathrm \partial x } = 2 z=2x+2y;z对x求偏导得∂x∂z=2
那么我们如果使用向量、张量进行求导要如何做呢?
在求涉及数组的导数时,很多困惑源于试图同时做太多事情。这些“事情”包括同时对多
个分量求导,在求和符号存在的情况下求导,以及应用链式法则。通过同时做所有这些事
情,我们更容易犯错误。
将符号展开为每个分量的显式和和方程
为了简化给定的计算,将输出的单个标量元素的显式公式只用标量变量来表示通常是有用的。
假设我们有一个长度为C的列向量y,它是通过形成一个矩阵W (C行× D列)与长度为D的列向量x的乘积来计算的:
y ⃗ = W x ⃗ \vec{y}=W \vec{x} y=Wx
假设我们感兴趣的是y对x的导数。这个导数的完整表征需要y的每个分量相对于x的每个分量的(偏)导数,比如说,y的第3个分量相对于x的第7个分量。
也就是说,我们要计算
∂ y 3 ⃗ ∂ x 7 ⃗ \frac{\partial \vec{y_3}}{\partial \vec{x_7}} ∂x7∂y3
而
y ⃗ 3 = ∑ j = 1 D W 3 , j x ⃗ j \vec{y}_{3}=\sum_{j=1}^{D} W_{3, j} \vec{x}_{j} y3=j=1∑DW3,jxj
我们将y这个列向量,通过取第三个分量,将其变为标量。
Removing summation notation(去除求和符号)
人们在微分包含求和符号(Σ)或乘积符号(∏)的表达式时经常会犯错误,所以我们干脆把式子展开,可以让我们更少的在计算的过程中犯错误。
我们把y3展开为
y ⃗ 3 = W 3 , 1 x ⃗ 1 + W 3 , 2 x ⃗ 2 + … + W 3 , 7 x ⃗ 7 + … + W 3 , D x ⃗ D \vec{y}_{3}=W_{3,1} \vec{x}_{1}+W_{3,2} \vec{x}_{2}+\ldots+W_{3,7} \vec{x}_{7}+\ldots+W_{3, D} \vec{x}_{D} y3=W3,1x1+W3,2x2+…+W3,7x7+…+W3,DxD
于是便可以求得
∂ y ⃗ 3 ∂ x ⃗ 7 = ∂ ∂ x ⃗ 7 [ W 3 , 1 x ⃗ 1 + W 3 , 2 x ⃗ 2 + … + W 3 , 7 x ⃗ 7 + … + W 3 , D x ⃗ D ] = 0 + 0 + … + ∂ ∂ x ⃗ 7 [ W 3 , 7 x ⃗ 7 ] + … + 0 = ∂ ∂ x ⃗ 7 [ W 3 , 7 x ⃗ 7 ] = W 3 , 7 \begin{aligned}\frac{\partial \vec{y}_{3}}{\partial \vec{x}_{7}} & =\frac{\partial}{\partial \vec{x}_{7}}\left[W_{3,1} \vec{x}_{1}+W_{3,2} \vec{x}_{2}+\ldots+W_{3,7} \vec{x}_{7}+\ldots+W_{3, D} \vec{x}_{D}\right] \\& =0+0+\ldots+\frac{\partial}{\partial \vec{x}_{7}}\left[W_{3,7} \vec{x}_{7}\right]+\ldots+0 \\& =\frac{\partial}{\partial \vec{x}_{7}}\left[W_{3,7} \vec{x}_{7}\right] \\& =W_{3,7}\end{aligned} ∂x7∂y3=∂x7∂[W3,1x1+W3,2x2+…+W3,7x7+…+W3,DxD]=0+0+…+∂x7∂[W3,7x7]+…+0=∂x7∂[W3,7x7]=W3,7
通过观察y的一个分量和x的每一个分量,使计算尽可能地简单。
Completing the derivative: the Jacobian matrix(完成导数:雅可比矩阵)
Row vectors instead of column vectors(行向量代替列向量)
在使用不同的神经网络包时,密切关注权重矩阵、数据矩阵等的排列是很重要的。例如,如果一
个数据矩阵X包含许多不同的向量,每个向量代表一个输入,那么每个数据向量是数据矩阵X的一
行还是一列?
我们上面的例子x与y是列向量。然而,当x是行向量时,要怎么表示w呢?
可以看到,相比较不过是w的下标位置转变罢了
Dealing with more than two dimensions(处理张量的导数)
假设y和W的一个分量求偏导(注意,此时y与x是行向量)
在其计算中所起的作用。在这种情况下,y沿一个坐标变化,而W沿两个坐标变化。
因此,整个导数最自然地包含在一个三维数组中。
我们避免使用“三维矩阵”这个术语,因为不清楚如何在三维数组上定义矩阵乘法和其他矩阵运算。
处理三维数组时,寻找一种显示它们的方法可能会带来更多的麻烦。相反,我们应该简单地将我们的结果定义为公式,这些公式可用于计算所需三维数组的任何元素的结果。
现在我们想要一个用标量值表示y3的方程对W7,8求偏导
可以观察到
在yi与wj,k,当i ≠ k时,结果为0,否则则为xj
如果我们定义一个新的二维数组G来储存这些非0的值,就变为了二维数组,完成了降维
(因为只有i = k时,才会有值)
Multiple data points(矩阵之间的求导)
展开每个分量可以得到:
Y i , j = ∑ k = 1 D X i , k W k , j Y_{i, j}=\sum_{k=1}^{D} X_{i, k} W_{k, j} Yi,j=k=1∑DXi,kWk,j
我们可以从这个方程中立即看出,在导数中
∂ Y a , b ∂ X c , d , 除非 a = c ,否则结果都是零。 \frac{\partial Y_{a,b}}{\partial X_{c,d}} ,除非a = c,否则结果都是零。 ∂Xc,d∂Ya,b,除非a=c,否则结果都是零。
The chain rule in combination with vectors and matrices(结合向量和矩阵的链式法则)
现在我们已经完成了几个基本的例子,让我们将这些思想与链式法则的一个例子结合起来
然而,我们想要通过使用链式法则来定义中间结果的过程,这样我们就可以看到链式法则在非标量导数的情况下是如何应用的。
这就是vw的分量表达式,问题的原始答案。
总结一下,我们可以在向量和矩阵导数中使用链式法则
但要注意一下几点:
•清楚地说明中间结果和用来表示它们的变量。
•表示最终导数的各个分量的链式法则。
•对链式法则表达式中的中间结果进行适当的求和。
总结
本周的进度稍微有点拖沓,但是总体还是在计划之中的。
在机器学习的理论部分,这一周主要学习了Transformer,其中了解了Sequence to Sequence 的应用场景,因为Seq2Seq model的input与output只有两种情况,一是input与output的vector长度是对等的;二是output的长度是由机器决定的。而我们在日常生活的场景中变数总是很多的,所以都是二的情形比较多,比如:语音识别(输入是声音信号,输出是语音识别的结果)、机器翻译(机器输入一个语言的句子,输出另外一个语言的句子)、语音翻译(机器直接将它听到的英语声音信号翻译成所需的文字)、语音合成(输入文字、输出声音信号)、聊天机器人、问答任务(序列到序列模型在自然语言处理的领域的应用)、句法解析(输入是一段文字,输出是一个树状的结构)、多标签分类(同一个东西可以属于多个类)。此外,还学习了Transformer的结构,一般的sequence to sequence model会分成encoder(编码器)和decoder(解码器)。在Transformer encoder的学习中,还了解到了残差连接(residual connection),其是输出向量加上其输入向量得到新的输出。
然后还了解到了,layer normalization(层归一化),其会计算输入向量的平均值和标准差(输入向量 x 里面每一个维度减掉均值 m 再除以标准差 σ 以后得到 x′ )。最后,了解了Transformer encoder里面一个block的输入到输出整个过程,就是经过数个残差连接和fully connection以及layer normalization完成的。
在代码实践部分,我学习了网络模型的保存与读取。学会了2网络模型的保存与读取方式,分别是:
方式1:直接保存模型;方式2:字典法(保存模型的参数权重),最后通过load pth文件完成加载模型。此外,学习到了自己创建的模型种保存与读取的一些陷阱(即save后无法直接load),需要通过import对应的模型python文件来解决。
在数学理论部分,学习了标量、向量、张量求导的推导。我们之前学习的求导,都是基于一个一元函数或者二元函数(标量)对其中变量进行求导/求偏导的,那么我们如果使用向量、张量进行求导要怎么做?就是基于标量的推导衍生的,通过对论文的参考,首先对向量与向量之间的求导进行了推导;然后对向量与矩阵(张量)之间进行了求导的推导;最后,对矩阵与矩阵之间的求导进行了推导。除此之外,还结合向量和矩阵对其链式法则进行了衍生学习。
下一周计划继续学习Transformer,了解decoder以及一些数学推理。代码部分开始进入套路化训练的学习。尽可能把Transformer学好,打下一个良好的基础。