(王树森老师课程)【强推】RNN模型和NLP应用


由于Gitee图床被forbid,导致图片显示异常,后续有空修复

一、数据处理

如何将计算机不认识的转化为数字

特征分类

  • 类别特征(Categorical Feature)

    有限集合,当没有大小之分,是并列权重的

  • 数值特征

    如年龄、工资等,有大小之分

image-20220916193354943

由于计算机只能处理数值型特征,因此需要将Gender列和Nationality列转化为计算机能识别的数字

此处Age为数字特征,性别为二元特征(Female:0,Male:1),国籍则是多元的类别特征


疑问:二元特征,也算一种特殊的类别特征,为什么不用二维向量表示?


类别特征转化为数字流程(以Nationality为例)

  1. 构建一个字典(US->1,China->2,India->3,German->4,...)

  2. 将特征转化为OneHot编码的向量(假设有197种国籍)

    则每个国籍可以表示为197*1的向量,使用1中的字典查找国际Index(如Index(China)=2),则向量第二维为1,其它为0

    "China"->2->[0,1,...,0]
    
  3. 将每一列的特征(Age,Gender,Nationality)转化为数字后,可以得到:

    如:(Number,[Vector dim=1],[Vector dim=197])
    (35,Male,China)=(35,1,[0,1,...,0])
    

为什么第一个要从1开始?

因为要保存0的编码来代表未知或缺失的国籍,即[0,...,0]来代表

为什么要用OneHot来表示Categorical特征?

因若不使用Onehot而用1,2,3,...代表US,China,India,...,则在此列特征运算时,”US“+"China"="India",显然是不合理的,而US:[1,0,0,...],China:[0,1,0,...]

US+China=[1,1,0,0,...]更能保留两种类型特征相加的信息

处理文本数据

我们知道了如何处理类别型特征,但对于文本来说,又该如何处理文本文本数据呢?处理文本数据总体可以概括为以下几个步骤:

  • 分词

    以英文为例,将一个句子分为包含一个个的单词的列表

    before:
    S="... to be or not to be...".
    after:
    L=[...,to,be,or,not,to,be,...]
    
  • 统计词频

    1.构建一个字典,初始化一个空字典

    image-20220916202349441
    • 若词\(w\)未出现在字典当中,则\(add(w,1)\)添加入字典
    • 否则\(dict[w]=dict[w]+1\)

    2.从而可以得到一个字典

    image-20220916202721774

    3.将字典中单词词频从大到小排序

    image-20220916202829300

    4.再将词频转化为索引Index,转化完后的字典被称为词汇表"vocabulary"

    image-20220916202959558

    由于词汇表通常会添加入一些不寻常的词,如名字、拼写错误的词等,因此需要对词汇表选择,可以在步骤3时截断出现次数\(time>1\)的单词,或设定词表大小为\(V\)

    词表Index通常从1开始计数,原因是为OOV未登录词预留位置,出现时使用0编码

    为什么要去除词表中多余的词?

    1. 加快计算
    2. 减少后续词嵌入的参数
  • One-Hot编码

    得到了vacab即可将分词后的文本转化为向量

    image-20220925121817685

    例如:

    words:[to,be,or,not,to,be]
    indices:[2,4,8,1,2,4]
    再做OneHot
    
    • onehot向量的维度即为Vacab的维度
    • Vacab为含不同词的字典

二、文本处理与词嵌入

IMDB数据集:

文本转化为序列

四个步骤:

  • 分词
  • 构建字典
  • One-Hot编码
  • 序列对齐

分词

分词前需要做的几件事:

  • 大小写
  • 去除停用词
  • 文本纠错

构建字典

image-20220916212530131

One-Hot编码

将文本转化为数字序列

image-20220916212807971

再将序列转化为矩阵

image-20220925121817685

序列对齐

在对句子进行One-Hot编码后会存在一个问题,句子序列长度不一,有的长有的短

image-20220916212951761

因此大于固定长度则截断,小于则补齐

词嵌入

一个单词可以用onehot向量表示(即只有一维为1,其它全为0),词向量的维度即为词表的长度。但由于OneHot向量的维度过高,会导致很多问题,因此需要做词嵌入来降低词向量的维度,做词嵌入有以下几步:

  1. 有一个已经构建的词表
    image-20220925124850135
  2. 将OneHot向量映射到低维度的向量上
    image-20220925124915260

所以词嵌入的关键之处在于如何通过参数矩阵\(\rm{P}\)将OneHot向量映射,此参数矩阵是可以训练的,具体见Word2Vec的历史,词嵌入矩阵是实现上下文语义理解任务时的“副产物”

如下图为一个词嵌入矩阵:

image-20220925124824244

三、SimpleRNN

为什么要使用RNN(Recurrent Neural Networks)?

  • 传统DL模型有全连接网络FC卷积神经网络CNN
  • 上述模型具有局限性
    • 只能一次全部输入来处理数据
    • 输入大小固定
    • 输出大小固定
    • 典型的是One2One模型
      image-20220917134622000
  • 序列数据往往是传统模型不适合处理的(如文本、音频、时间序列)
    image-20220917134742554

RNN模型的基本结构

image-20220917135005687

几个组件:

  • 输入$$x_i(i=0,1,2,...,t)$$
  • 矩阵\(\rm{A}\)
  • 状态向量$$h_i(i=0,1,2,...,t)$$

初步工作描述:

  1. 输入一句预处理后的句子\(s_1=[the,cat,sat,...,mat]\)
  2. 词嵌入,即将句子转化为句向量\(e_1[embedding\_size*1]\)
  3. 随机初始化矩阵$$A$$和状态向量\(h_i(i=0,1,2,...,t)\)

Simple RNN

image-20220917135448218

向量拼接和矩阵初始化

SimpleRNN的核心运算如下:

image-20220917140822162

\(h\)\(x\)为向量\(h_t\)\(x_t\)的维度,\(rand\_matrix\)函数随机初始化一个\(size=(h,h+x)\)的矩阵

\(A=rand\_matrix(matrix\_size=(h,h+x))\)

\(concat\)函数目的是将\(h_{t-1}\)\(x_t\)拼接

\(h_t=\tanh(A·concat(h_{t-1},x_t))\)

即参数更新过程可视为以下函数:

\(h_t=func(h_{t-1},x_t)\)

\(\tanh\)函数

image-20220917140856549

函数公式:

\(tanh(x)=\frac{\sinh{x}}{\cosh{x}}=\frac{e^x-e^{-x}}{e^x+e^{-x}}\)

\(tanh\)函数的特点:

  • \((-\infty,+\infty)\)的输入压缩到\((-1,1)\)
  • 输出结果符号不变
  • 单调递增的奇函数,且定义域内可导

若没有\(\tanh\)函数会导致什么?

image-20220917142916581

导致梯度消失或梯度爆炸,在不断地序列输入后最终状态向量会趋于0或区域无穷!

四、LSTM

源论文:Hochreiter and Schmidhuber. Long short-term memory. Neural computation, 1997.

LSTM网络架构图与RNN对比

image-20220917154507126

从结构图上的区别:

  • 内部更为复杂了
  • 外部还是输出状态向量,但与此同时多了个“箭头”,这个“箭头”是传送带

LSTM的几个重要部件:

  • 传送带(\(C\)
  • 遗忘门(\(f_t\)
  • 输入门(\(i_t\)
  • New Value(\(\widetilde{C_t}\)
  • 输出门(\(o_t\)
  • 状态向量(\(h\))

LSTM:Conveyor Belt

结构图:

image-20220917161428427

传输带做的事:过去的信息通过传输带送到下一个时刻。可以看到传输带上连接了几个朝上的箭头,因此其将几个组件组合起来运算

传输带的重要作用:通过传输带来避免梯度消失的问题

LSTM:Forget Gate

由总到分展开:

Part1对位相乘:

\(f=sigmoid(a)\)此处\(a\)为一个函数

\(output\_1=c\circ{f}\)(对位相乘)

image-20220917164221524

\(Sigmoid\)函数:

image-20220917164420087

特点:

  • \((-\infty,+\infty)\)内的值压缩到\((0,1)\)
  • 函数单调递增可导

Part2:遗忘门(f)

结构图:

image-20220918151458918
  • \(f\)维度和\(c\)\(h\)相同
  • 向量中Value为0则不让任何“信息”通过(数字代表信息)
  • 向量中Value为1则让任何“信息”通过
  • 遗忘门\(f\)有选择的让\(c\)中的值通过

例如:

image-20220917165511105

Part3:\(W_f\)和拼接向量

\(a\)可以拆解为如下:

image-20220917165809731

\(a=W_f\cdot{concat(h_{t-1},x_t)}\)

其中\(W_f\)可以被学习

总结

数学公式总和一起就是如下:

\[output\_1=c\cdot{f}\\ f=sigmoid(a)\\ a=W_f\cdot{concat(h_{t-1},x_t)} \]

这一过程可以概括为一个函数:

\(output\_forget=func(h_{t-1},x_t,C_{t-1})\)

LSTM:Input Gate

输入门\(i_t\)决定了传送带上哪个值会被更新

Input gate结构图

image-20220917174142729

\(i_t=sigmoid(W_t\cdot{concat(h_{t-1},x_t)})\)

其中\(W_t\)可以被学习

LSTM:New Value

结构图:

image-20220918153118755

\(\widetilde{C_t}=\tanh({W_c\cdot{concat(h_{t-1},x_t)}})\)

其中\(W_c\)可以被学习

LSTM:Update the Conveyor Belt\(C\)

结构图:

image-20220917181956541

\(C_t=f_t\circ{C_{t-1}}+i_t\circ{\widetilde{c_t}}\)

  • 里面集成了遗忘门、输入门和NewValue
  • \(+\)左边部分有选择的让\(C_{t-1}\)信息遗忘\(+\)右边部分添加新的信息

LSTM:Output Gate

image-20220917180038589

输出门\(o_t\)决定\(C_{t-1}\)流向\(h_t\)的信息

其中\(W_o\)可以被学习

\(o_t=sigmoid(W_o\cdot{concat(h_{t-1},x_t)})\)

LSTM:Update State

image-20220917180825771

\(h_t=o_t\circ\tanh(C_t)\)

\(h_t\)被拷贝成两份,一份成此轮LSTM的输出,另一份被传送到下一步

\(C_t\)中包含了遗忘和增加的信息,因此和输出门结合产生最终的状态向量

最终\(x_t\)向量的所有信息积累在\(h_t\)

参数数量

image-20220918153710479

LSTM在IMDB上的处理

image-20220918212759779

最后输出状态向量\(h_t\)后接上全连接层再做\(softmax\)输出最终的概率分布

总结

  • LSTM有四个参数矩阵 \(W_f\),\(W_i\),\(W_c\),\(W_o\),参数矩阵经过激活函数后生成对应的“门”

    • \(sigmoid\):\(W_f\),\(W_i\),\(W_o\)
    • \(tanh\):\(W_c\)
    • 参数矩阵维度都是(h,x+h)
    • 补充:状态向量计算时对\(C_t\)的激活使用了\(tanh\)
  • 在传送带\(C_{t-1}\)\(C_t\)间,涉及了哪几个部分?

    • 遗忘门

    • 输入门

    • New Value值

  • 从结构图中可以看出,\(C_{t-1}\)\(C_t\)的更新过程中,涉及了几个输出\(h_{t-1}\),\(x_t\),\(C_{t-1}\),其中W矩阵有\(W_f\),\(W_i\),\(W_c\)

五、多层RNN、双向RNN、预训练

多层RNN

架构图:

image-20220918212010540

特点:

\(i(i\in{(0,n]})\)层的所有状态向量\(h_{ij}(j\in{[0,t]})\)输出后是第\(i+1\)层RNN的输入

双向RNN

人阅读是在脑子里积累阅读信息,RNN“阅读”则积累在状态向量\(h_t\)当中,而人类通常是从前往后阅读的习惯,但并不影响从后往前阅读来判断一个电影评论的情绪,因此双向RNN借助这种思想,悬链两条RNN,一条从前往后,另一条从后往前。

两条RNN不共享参数,各自输出其状态向量,然后将两条向量做\(concat\)操作记作向量\(y\)

  • 若有多层RNN,则将向量\(y\)作为接下来层的输入
  • 若单层RNN,则返回\([h_t,{h_t}\prime]\)
image-20220918212411243

双向RNN为什么比单向的好?

RNN单向时会存在遗忘,即从左到右,容易遗忘左边的,反之也是

但实验表明,此处单向和双向RNN结果区别不大,原因如下:

  • Embedding层参数过多导致Overfitting
  • 训练数据少

解决方法:

  • Pretrain
  • 增加训练数据量

预训练

预训练的流程:

  1. 在一个大型数据集训练一个模型
    • 可以是不同任务(最好接近情感分析的任务,任务越相似,Transfer越好)
    • 可以是不同模型
  2. 在大数据集上训练神经网络,不一定要RNN,只要神经网络有Embedding即可。只保留Embedding和模型参数
  3. 训练一个新的LSTM网络,设置Embedding层不训练,只训练其它层
    image-20220918214937334

六、文本自动生成

文本生成主要思想

image-20220918235716305

此处为以字符级输入为例,输入若干字符,输出下一个字符的概率

具体例子如下:

image-20220919000121615

再次将生成的输出字符t继续输入,下一预测可以为'.'则结束生成。

如何训练

step1.准备数据

image-20220919000448885

现有上面一段文字,策略是截取一段连续的字符序列\(charseq\)输入为\(x=charseq[:-1]\),输出为\(y=charseq[-1:]\),可以设定\(seg\_len=40\ stride=3\)

现在数据有了,生成问题可以理解为一个多分类问题

生成文本会根据输入的输出产生,如丢一本莎士比亚的书训练,会生成莎士比亚写作风格的文本,虽然可能不通顺或出现语法单词等错误。

准备好的数据就像以下这样:

image-20220919001802557

step2.字符向量化

image-20220919002013464

因为英文字符的数量较少,因此直接构建字符字典{Token:Index}

然后将输入的字符使用one-hot编码

step3.构建神经网络

image-20220919002457783

因为生成是有顺序的,因此LSTM用单向

step4.训练神经网络

image-20220919002539782

image-20220919002606450

选择预测策略

问题:如果输出最后概率分布,如何选择下一个字符?

  • Option1:贪心选择,选最大概率分布的字符输出

    next_index=np.argmax(pred)
    
    • 确定性的,没有随机性
  • Option2:多项式分布随机抽取

    next_onehot=np.random.multinomial(1,pred,1)
    next_index=np.argmax(next_onehot)
    
    • 抽样过于随机,易出现拼写错误
  • Option3:调整多项式分布

    pred=pred**(1/temperature)
    pred=pred/np.sum(pred)
    
    • 作用结果,大的概率会变大,小的变小

      image-20220919214013658

    • 减小temperature则会大的变的更大,小的变的更小

    • 继续降低则会变的更极端,使得变成确定性

文本生成的例子

image-20220919214930529

总结

  • 生成文本需要训练一个循环神经网络,需要切分的数据\((segment,next\_char)\)
  • one-hot编码字符
    • 字符\((v*1\ vector)\)
    • 片段\((l*v\ matrix)\)
  • 构建神经网络
    • \(l*v\ matrix\Longrightarrow{LSTM}\Longrightarrow{Dense}\Longrightarrow{v*1\ vetor}\)
  • 生成文本
    • 初始化种子序列
    • 重复
      • 将片段one-hot输入到神经网络中
      • 输出概率
      • 根据概率抽样
      • 将下一字符添加到片段中

七、机器翻译与Seq2Seq模型

神经机器翻译需要用到Seq2Seq模型架构

本文做神经机器翻译的是英语和德语两个语言

例:

image-20220922171147770

构建一个神经机器翻译的Seq2Seq模型,需要做一下工作:

  • 数据集准备+预处理
  • 训练Seq2Seq模型
  • Seq2Seq模型的推断

数据集准备+数据处理

数据集下载

包含翻译对的网站:http://www.manythings.org/anki/

image-20220923191801649

一个英语句子如果可以match多个德语句子则正确

预处理

  • 转化为小写
  • 去除停用词
  • 去除标点等

数据处理

  • 分词构建词典
  • One-Hot编码

分词、构建操作时,分为字符级单词级,由于两种语言的特性不同,分词的方式也不同,因此需要使用两种分词器,构建两种字典

image-20220922185825313

在构建词典时,对目标语言需要设置开始符号,和截止符,以在生成目标语言时开始和结束

image-20220922190111621

One-Hot编码:

编码过程需要对英文和德语分别编码

image-20220922190645660

image-20220922190733848

One-Hot编码的图形表示:

每个字符用一个向量表示,每个句子用一个矩阵表示,矩阵为RNN的输入。

image-20220922191156066

训练Seq2Seq模型

Encoder

image-20220923195643108

Encoder将输入的句子提取特征,丢弃,最终输出状态向量\(h\)和传输带\(c\)

Decoder

Decoder用于生成目标翻译文本,Decoder的初始状态就是Encoder的最终状态。

image-20220923201133884

不断重复训练过程,直到生成终止字符

image-20220923202353887

后半部分相当于上一节的文本自动生成

Seq2Seq in Keras

image-20220923202558653

总结

总体结构图

image-20220923203002565

如何提升

  • Encoder使用BiLSTM(Encoder Only 文本生成器Decoder则不能)

  • 使用Word-Level分词

    • 英文平均4.5个单词
    • 单词级更不易遗忘
    • Word-Level需要更大的数据集
    • 或对Embedding进行预训练
  • 多任务学习

    • 英语-英语本身,Encoder只有一个,而训练数据多了一倍。即Encoder都是英语的,可以拿多个语言训练来更新Encoder

    • 添加更多任务,添加更多Decoder生成各种语言,虽然德语的Decoder没有改进,但翻译的效果还是会变好

      image-20220923203706287
    • Attention(注意力机制,能够大大提升效果,在下一节!)

八、Attention

为什么使用Attention

由于Seq2Seq有明显的缺陷,输入的句子很长的话会“记不住”句子,Encoder的最终状态会漏掉一些信息,则Decoder无从的到正确的句子,会缺少信息

image-20220923211833420

Attention原论文:

Bahdanau, Cho, & Bengio. Neural machine translation by jointly learning to align and translate. In ICLR, 2015.

具有Attention的Seq2Seq模型:

  • 极大的提升了Seq2Seq性能
  • Seq2Seq不会遗忘源输入,Decoder更新状态时都会看一遍Encoder所有状态
  • Attention告诉Decoder关注什么地方
  • 缺点:计算量大

SimpleRNN+Attention

Attention在Encoder结束后,和Decoder同步工作

Step1:计算Weight \(a_i\)

权重\(a_i\)即为相关性\(a_1,a_2,...,a_m\),是\(\rm{h_1,h_2,...,h_m}\)\(\rm{s_0}\)的相关性

image-20220923234321841 $$ a_i=align(h_i,s_0) $$

计算\(a_i\)有两种选择

Option1(使用的是原始paper的方法):

  1. 计算\(\widetilde{a_i}\)(是个实数)
image-20220923234743568 $$ \widetilde{a_i}=\rm{v}^\mit{T}\cdot{\tanh{\left[\begin{matrix}\rm{w}\cdot{concat(h_i,s_0)}\end{matrix} \right]}} $$ 其中$\rm{v^\mit{T}}$和$\rm{w}$是可训练的参数 2. 计算$a_i(i=1,...,m)$

\[{\left[\begin{matrix}a_1,...,a_m\end{matrix} \right]}=Softmax({\left[\begin{matrix}\widetilde{a_1},...,\widetilde{a_m}\end{matrix} \right]})\\ \]

Option2(更为常用,与Transformer相同)

  1. 线性变换
    • \({\rm{k}}_i={\rm{W}}_K\cdot{{\rm{h}}_i},for\ i=1\ to\ m.\)
    • \({\rm{q}}_0={\rm{W}}_Q\cdot{{\rm{s}}_0}\)
  2. 内积
    • \(\widetilde{a_i}={{\rm{k_\mit{i}^{\mit{T}}}}}\cdot{\rm{q_0}},for\ i=1\ to\ m.\)
  3. 标准化
    • \({\left[\begin{matrix}a_1,...,a_m\end{matrix} \right]}=Softmax({\left[\begin{matrix}\widetilde{a_1},...,\widetilde{a_m}\end{matrix} \right]})\\\)

Step2:计算Context Vector

此处以\(\rm{c_0}\)为例子,为\(\rm{a}\)\(\rm{h}\)的加权平均

\[\rm{c_0=a_1h_1+...+a_mh_m.} \]

Step3:计算新的状态向量\(\rm{s_i}\)

计算新的状态\(\rm{s_1}\)(注意此处加了和没加Attention的区别)

image-20220924154607718

此处为一轮\(\rm{c}\)的计算

由此可以总结,因为\(a\)是关于\(\rm{h}\)\(\rm{s_i}\)的函数,而\(\rm{c}\)是关于\(\rm{a}\)\(\rm{h}\)的函数,因此可以视为\(\rm{c_i=f(h,s_i)}\)

则新状态计算公式为:\(\rm{s_i=g({x\prime}_i,s_{i-1},c_{i-1})}\)

持续更新\(\rm{c_i}\)

image-20220924161659565

Attention的参数

Attention总共有多少个参数呢?

image-20220924162027701

Attention的可视化

image-20220924162049085

颜色越深权重越大,每当Decoder生成新状态时,都会看一遍Encoder的所有状态,权重告诉Decoder应该关注的地方

总结

  • 标准的Seq2Seq模型,Decoder只关注当前状态
  • 添加了Attention:Decoder关注错有Encoder的状态
  • Decoder知道生成新状态时应该关注Encoder的哪个状态
  • 缺点:计算量非常大
  • 标准Seq2Seq时间复杂度:\(O(m+t)\),Seq2Seq+Attention时间复杂度:\(O(mt)\)\(m\)为源序列长度,\(t\)为目标序列长度

九、SelfAttention

Attention的论文

Self-Attention [2]: attention [1] beyond Seq2Seq models.

  • The original self-attention paper uses LSTM.
  • To make teaching easy, I replace LSTM by SimpleRNN.
  1. Bahdanau, Cho, & Bengio. Neural machine translation by jointly learning to align and translate. In ICLR, 2015.
  2. Cheng, Dong, & Lapata. Long Short-Term Memory-Networks for Machine Reading. In EMNLP, 2016.

Attention不仅仅局限于Seq2Seq模型,实际上可以用于所有RNN架构上。此处使用RNN简化讲解:

初始时\(\rm{c_0}\)\(\rm{h_0}\)都为全0向量

image-20220924171646661

\(current\)为当前最新轮次,以\(\rm{x}\)为例,最新输入为\(\rm{x_{current}}\)

\(\rm{h_{current}}\)如何计算?

传统的RNN更新\(h_{current}\)依赖于旧的状态\(\rm{h_{current-1}}\)和新的输入\(\rm{x_{current}}\),通过两者的拼接再计算。而自注意力机制则使用\(\rm{c_{current-1}}\)替代\(\rm{h_{current-1}}\),实际上将\(\rm{x_1}\),\(\rm{c_{current-1}}\)\(\rm{h_{current-1}}\)拼接也可以。由于初始状态\(\rm{h_0}\)为全零向量,因此此处加权平均可以忽略\(\rm{h_0}\)

\(\rm{c_{current}}\)如何计算?

\(\rm{c_{current}}\)\(\rm{h_0}\)到当前状态向量\(h_{current}\)与各自的权重\(a_{self}\)的加权平均

\({a_{current}}\)如何计算?

此处需要计算的是第\(current\)轮次的\(a\),即将最新的\(\rm{h_{current}}\)与其它的\(\rm{h}\)\(\rm{align}\)计算各自的权重。

综上则可以总结:

\[\rm{h_n=function(x_n,c_{n-1})}\\ a_i=\rm{aligh(h_i,h_{current})}\\ \rm{c_n=a_1h_1+a_2h_2+...+a_nh_n} \]

计算\(\rm{c_1}\)为例子,因为\(\rm{h_0}\)为全零向量,又因为将\(\rm{h_1}\)做相关性计算,与自己就是1,因此\(\rm{c_1=h_1}\)

image-20220924181817327 image-20220924174509883 image-20220924174412220 image-20220924174435856

总结

  • 自注意解决了不局限于Seq2Seq模型
  • 相比于RNN,在\(\rm{h}\)的计算上不一样
  • 同时最新的状态与前面状态做\(\rm{aligh}\)计算得出各自状态向量权重,从而与状态向量做加权平均得到当前的Context Vector

参考资料

王树森老师DeepLearning视频课件:

https://github.com/wangshusen/DeepLearning

posted on 2022-09-18 15:41  waterpaperer  阅读(284)  评论(0编辑  收藏  举报

导航