动手深度学习之从线性回归到循环神经网络

参考伯禹学习平台《动手学深度》课程内容内容撰写的学习笔记
原文链接:https://www.boyuai.com/elites/course/cZu18YmweLv10OeV
感谢伯禹平台,Datawhale,和鲸,AWS给我们提供的免费学习机会!!
总的学习感受:伯禹的课程做的很好,理论非常系统,但本人基础较差,故学习起来还是有难度,建议基础较差的同学可以学习伯禹的其他课程:
数学基础:https://www.boyuai.com/elites/course/D91JM0bv72Zop1D3
机器学习基础:https://www.boyuai.com/elites/course/5ICEBwpbHVwwnK3C
提纲
线性回归、Softmax与分类模型、多层感知机、文本预处理、语言模型与数据集
、循环神经网络

1、线性回归

主要内容包括:

  1. 线性回归的基本要素
  2. 线性回归模型从零开始的实现
  3. 线性回归模型使用pytorch的简洁实现

线性回归的基本要素

模型

为了简单起见,这里我们假设价格只取决于房屋状况的两个因素,即面积(平方米)和房龄(年)。接下来我们希望探索价格与这两个因素的具体关系。线性回归假设输出与各个输入之间是线性关系:

p r i c e = w a r e a a r e a + w a g e a g e + b \mathrm{price} = w_{\mathrm{area}} \cdot \mathrm{area} + w_{\mathrm{age}} \cdot \mathrm{age} + b

数据集

我们通常收集一系列的真实数据,例如多栋房屋的真实售出价格和它们对应的面积和房龄。我们希望在这个数据上面寻找模型参数来使模型的预测价格与真实价格的误差最小。在机器学习术语里,该数据集被称为训练数据集(training data set)或训练集(training set),一栋房屋被称为一个样本(sample),其真实售出价格叫作标签(label),用来预测标签的两个因素叫作特征(feature)。特征用来表征样本的特点。

损失函数

在模型训练中,我们需要衡量价格预测值与真实值之间的误差。通常我们会选取一个非负数作为误差,且数值越小表示误差越小。一个常用的选择是平方函数。 它在评估索引为 i i 的样本误差的表达式为

l ( i ) ( w , b ) = 1 2 ( y ^ ( i ) y ( i ) ) 2 , l^{(i)}(\mathbf{w}, b) = \frac{1}{2} \left(\hat{y}^{(i)} - y^{(i)}\right)^2,

L ( w , b ) = 1 n i = 1 n l ( i ) ( w , b ) = 1 n i = 1 n 1 2 ( w x ( i ) + b y ( i ) ) 2 . L(\mathbf{w}, b) =\frac{1}{n}\sum_{i=1}^n l^{(i)}(\mathbf{w}, b) =\frac{1}{n} \sum_{i=1}^n \frac{1}{2}\left(\mathbf{w}^\top \mathbf{x}^{(i)} + b - y^{(i)}\right)^2.

优化函数 - 随机梯度下降

当模型和损失函数形式较为简单时,上面的误差最小化问题的解可以直接用公式表达出来。这类解叫作解析解(analytical solution)。本节使用的线性回归和平方误差刚好属于这个范畴。然而,大多数深度学习模型并没有解析解,只能通过优化算法有限次迭代模型参数来尽可能降低损失函数的值。这类解叫作数值解(numerical solution)。

在求数值解的优化算法中,小批量随机梯度下降(mini-batch stochastic gradient descent)在深度学习中被广泛使用。它的算法很简单:先选取一组模型参数的初始值,如随机选取;接下来对参数进行多次迭代,使每次迭代都可能降低损失函数的值。在每次迭代中,先随机均匀采样一个由固定数目训练数据样本所组成的小批量(mini-batch) B \mathcal{B} ,然后求小批量中数据样本的平均损失有关模型参数的导数(梯度),最后用此结果与预先设定的一个正数的乘积作为模型参数在本次迭代的减小量。

( w , b ) ( w , b ) η B i B ( w , b ) l ( i ) ( w , b ) (\mathbf{w},b) \leftarrow (\mathbf{w},b) - \frac{\eta}{|\mathcal{B}|} \sum_{i \in \mathcal{B}} \partial_{(\mathbf{w},b)} l^{(i)}(\mathbf{w},b)

学习率: η \eta 代表在每次优化中,能够学习的步长的大小
批量大小: B \mathcal{B} 是小批量计算中的批量大小batch size

总结一下,优化函数的有以下两个步骤:

  • (i)初始化模型参数,一般来说使用随机初始化;
  • (ii)我们在数据上迭代多次,通过在负梯度方向移动参数来更新每个参数。
  • 矢量计算

在模型训练或预测时,我们常常会同时处理多个数据样本并用到矢量计算。在介绍线性回归的矢量计算表达式之前,让我们先考虑对两个向量相加的两种方法。

  1. 向量相加的一种方法是,将这两个向量按元素逐一做标量加法。
  2. 向量相加的另一种方法是,将这两个向量直接做矢量加法。
    注:矢量加法速度远快于标量加法,故在编程时建议采用矢量加法。

2、softmax和分类模型

内容包含:

  1. softmax回归的基本概念
  2. 如何获取Fashion-MNIST数据集和读取数据
  3. softmax回归模型的从零开始实现,实现一个对Fashion-MNIST训练集中的图像数据进行分类的模型
  4. 使用pytorch重新实现softmax回归模型

softmax的基本概念

  • 分类问题
    一个简单的图像分类问题,输入图像的高和宽均为2像素,色彩为灰度。
    图像中的4像素分别记为 x 1 , x 2 , x 3 , x 4 x_1, x_2, x_3, x_4
    假设真实标签为狗、猫或者鸡,这些标签对应的离散值为 y 1 , y 2 , y 3 y_1, y_2, y_3
    我们通常使用离散的数值来表示类别,例如 y 1 = 1 , y 2 = 2 , y 3 = 3 y_1=1, y_2=2, y_3=3

  • 权重矢量
    o 1 = x 1 w 11 + x 2 w 21 + x 3 w 31 + x 4 w 41 + b 1 \begin{aligned} o_1 &= x_1 w_{11} + x_2 w_{21} + x_3 w_{31} + x_4 w_{41} + b_1 \end{aligned}

o 2 = x 1 w 12 + x 2 w 22 + x 3 w 32 + x 4 w 42 + b 2 \begin{aligned} o_2 &= x_1 w_{12} + x_2 w_{22} + x_3 w_{32} + x_4 w_{42} + b_2 \end{aligned}

o 3 = x 1 w 13 + x 2 w 23 + x 3 w 33 + x 4 w 43 + b 3 \begin{aligned} o_3 &= x_1 w_{13} + x_2 w_{23} + x_3 w_{33} + x_4 w_{43} + b_3 \end{aligned}

  • 神经网络图
    下图用神经网络图描绘了上面的计算。softmax回归同线性回归一样,也是一个单层神经网络。由于每个输出 o 1 , o 2 , o 3 o_1, o_2, o_3 的计算都要依赖于所有的输入 x 1 , x 2 , x 3 , x 4 x_1, x_2, x_3, x_4 ,softmax回归的输出层也是一个全连接层。

Image Name

s o f t m a x \begin{aligned}softmax回归是一个单层神经网络\end{aligned}

既然分类问题需要得到离散的预测输出,一个简单的办法是将输出值 o i o_i 当作预测类别是 i i 的置信度,并将值最大的输出所对应的类作为预测输出,即输出 arg max i o i \underset{i}{\arg\max} o_i 。例如,如果 o 1 , o 2 , o 3 o_1,o_2,o_3 分别为 0.1 , 10 , 0.1 0.1,10,0.1 ,由于 o 2 o_2 最大,那么预测类别为2,其代表猫。

  • 输出问题
    直接使用输出层的输出有两个问题:
    1. 一方面,由于输出层的输出值的范围不确定,我们难以直观上判断这些值的意义。例如,刚才举的例子中的输出值10表示“很置信”图像类别为猫,因为该输出值是其他两类的输出值的100倍。但如果 o 1 = o 3 = 1 0 3 o_1=o_3=10^3 ,那么输出值10却又表示图像类别为猫的概率很低。
    2. 另一方面,由于真实标签是离散值,这些离散值与不确定范围的输出值之间的误差难以衡量。

softmax运算符(softmax operator)解决了以上两个问题。它通过下式将输出值变换成值为正且和为1的概率分布:

y ^ 1 , y ^ 2 , y ^ 3 = softmax ( o 1 , o 2 , o 3 ) \hat{y}_1, \hat{y}_2, \hat{y}_3 = \text{softmax}(o_1, o_2, o_3)

其中

y ^ 1 = exp ( o 1 ) i = 1 3 exp ( o i ) , y ^ 2 = exp ( o 2 ) i = 1 3 exp ( o i ) , y ^ 3 = exp ( o 3 ) i = 1 3 exp ( o i ) . \hat{y}1 = \frac{ \exp(o_1)}{\sum_{i=1}^3 \exp(o_i)},\quad \hat{y}2 = \frac{ \exp(o_2)}{\sum_{i=1}^3 \exp(o_i)},\quad \hat{y}3 = \frac{ \exp(o_3)}{\sum_{i=1}^3 \exp(o_i)}.

容易看出 y ^ 1 + y ^ 2 + y ^ 3 = 1 \hat{y}_1 + \hat{y}_2 + \hat{y}_3 = 1 0 y ^ 1 , y ^ 2 , y ^ 3 1 0 \leq \hat{y}_1, \hat{y}_2, \hat{y}_3 \leq 1 ,因此 y ^ 1 , y ^ 2 , y ^ 3 \hat{y}_1, \hat{y}_2, \hat{y}_3 是一个合法的概率分布。这时候,如果 y ^ 2 = 0.8 \hat{y}_2=0.8 ,不管 y ^ 1 \hat{y}_1 y ^ 3 \hat{y}_3 的值是多少,我们都知道图像类别为猫的概率是80%。此外,我们注意到

arg max i o i = arg max i y ^ i \underset{i}{\arg\max} o_i = \underset{i}{\arg\max} \hat{y}_i

因此softmax运算不改变预测类别输出。

  • 计算效率
    • 单样本矢量计算表达式
      为了提高计算效率,我们可以将单样本分类通过矢量计算来表达。在上面的图像分类问题中,假设softmax回归的权重和偏差参数分别为

W = [ w 11 w 12 w 13 w 21 w 22 w 23 w 31 w 32 w 33 w 41 w 42 w 43 ] , b = [ b 1 b 2 b 3 ] , \boldsymbol{W} = \begin{bmatrix} w_{11} & w_{12} & w_{13} \\ w_{21} & w_{22} & w_{23} \\ w_{31} & w_{32} & w_{33} \\ w_{41} & w_{42} & w_{43} \end{bmatrix},\quad \boldsymbol{b} = \begin{bmatrix} b_1 & b_2 & b_3 \end{bmatrix},

设高和宽分别为2个像素的图像样本 i i 的特征为

x ( i ) = [ x 1 ( i ) x 2 ( i ) x 3 ( i ) x 4 ( i ) ] , \boldsymbol{x}^{(i)} = \begin{bmatrix}x_1^{(i)} & x_2^{(i)} & x_3^{(i)} & x_4^{(i)}\end{bmatrix},

输出层的输出为

o ( i ) = [ o 1 ( i ) o 2 ( i ) o 3 ( i ) ] , \boldsymbol{o}^{(i)} = \begin{bmatrix}o_1^{(i)} & o_2^{(i)} & o_3^{(i)}\end{bmatrix},

预测为狗、猫或鸡的概率分布为

y ^ ( i ) = [ y ^ 1 ( i ) y ^ 2 ( i ) y ^ 3 ( i ) ] . \boldsymbol{\hat{y}}^{(i)} = \begin{bmatrix}\hat{y}_1^{(i)} & \hat{y}_2^{(i)} & \hat{y}_3^{(i)}\end{bmatrix}.

softmax回归对样本 i i 分类的矢量计算表达式为

o ( i ) = x ( i ) W + b , y ^ ( i ) = softmax ( o ( i ) ) . \begin{aligned} \boldsymbol{o}^{(i)} &= \boldsymbol{x}^{(i)} \boldsymbol{W} + \boldsymbol{b},\\ \boldsymbol{\hat{y}}^{(i)} &= \text{softmax}(\boldsymbol{o}^{(i)}). \end{aligned}

  • 小批量矢量计算表达式
    为了进一步提升计算效率,我们通常对小批量数据做矢量计算。广义上讲,给定一个小批量样本,其批量大小为 n n ,输入个数(特征数)为 d d ,输出个数(类别数)为 q q 。设批量特征为 X R n × d \boldsymbol{X} \in \mathbb{R}^{n \times d} 。假设softmax回归的权重和偏差参数分别为 W R d × q \boldsymbol{W} \in \mathbb{R}^{d \times q} b R 1 × q \boldsymbol{b} \in \mathbb{R}^{1 \times q} 。softmax回归的矢量计算表达式为

O = X W + b , Y ^ = softmax ( O ) , \begin{aligned} \boldsymbol{O} &= \boldsymbol{X} \boldsymbol{W} + \boldsymbol{b},\\ \boldsymbol{\hat{Y}} &= \text{softmax}(\boldsymbol{O}), \end{aligned}

其中的加法运算使用了广播机制, O , Y ^ R n × q \boldsymbol{O}, \boldsymbol{\hat{Y}} \in \mathbb{R}^{n \times q} 且这两个矩阵的第 i i 行分别为样本 i i 的输出 o ( i ) \boldsymbol{o}^{(i)} 和概率分布 y ^ ( i ) \boldsymbol{\hat{y}}^{(i)}

交叉熵损失函数

对于样本 i i ,我们构造向量 y ( i ) R q \boldsymbol{y}^{(i)}\in \mathbb{R}^{q} ,使其第 y ( i ) y^{(i)} (样本 i i 类别的离散数值)个元素为1,其余为0。这样我们的训练目标可以设为使预测概率分布 y ^ ( i ) \boldsymbol{\hat y}^{(i)} 尽可能接近真实的标签概率分布 y ( i ) \boldsymbol{y}^{(i)}

  • 平方损失估计

L o s s = y ^ ( i ) y ( i ) 2 / 2 \begin{aligned}Loss = |\boldsymbol{\hat y}^{(i)}-\boldsymbol{y}^{(i)}|^2/2\end{aligned}

然而,想要预测分类结果正确,我们其实并不需要预测概率完全等于标签概率。例如,在图像分类的例子里,如果 y ( i ) = 3 y^{(i)}=3 ,那么我们只需要 y ^ 3 ( i ) \hat{y}^{(i)}_3 比其他两个预测值 y ^ 1 ( i ) \hat{y}^{(i)}_1 y ^ 2 ( i ) \hat{y}^{(i)}_2 大就行了。即使 y ^ 3 ( i ) \hat{y}^{(i)}_3 值为0.6,不管其他两个预测值为多少,类别预测均正确。而平方损失则过于严格,例如 y ^ 1 ( i ) = y ^ 2 ( i ) = 0.2 \hat y^{(i)}_1=\hat y^{(i)}_2=0.2 y ^ 1 ( i ) = 0 , y ^ 2 ( i ) = 0.4 \hat y^{(i)}_1=0, \hat y^{(i)}_2=0.4 的损失要小很多,虽然两者都有同样正确的分类预测结果。

改善上述问题的一个方法是使用更适合衡量两个概率分布差异的测量函数。其中,交叉熵(cross entropy)是一个常用的衡量方法:

H ( y ( i ) , y ^ ( i ) ) = j = 1 q y j ( i ) log y ^ j ( i ) , H\left(\boldsymbol y^{(i)}, \boldsymbol {\hat y}^{(i)}\right ) = -\sum_{j=1}^q y_j^{(i)} \log \hat y_j^{(i)},

其中带下标的 y j ( i ) y_j^{(i)} 是向量 y ( i ) \boldsymbol y^{(i)} 中非0即1的元素,需要注意将它与样本 i i 类别的离散数值,即不带下标的 y ( i ) y^{(i)} 区分。在上式中,我们知道向量 y ( i ) \boldsymbol y^{(i)} 中只有第 y ( i ) y^{(i)} 个元素 y ( i ) y ( i ) y^{(i)}{y^{(i)}} 为1,其余全为0,于是 H ( y ( i ) , y ^ ( i ) ) = log y ^ y ( i ) ( i ) H(\boldsymbol y^{(i)}, \boldsymbol {\hat y}^{(i)}) = -\log \hat y_{y^{(i)}}^{(i)} 。也就是说,交叉熵只关心对正确类别的预测概率,因为只要其值足够大,就可以确保分类结果正确。当然,遇到一个样本有多个标签时,例如图像里含有不止一个物体时,我们并不能做这一步简化。但即便对于这种情况,交叉熵同样只关心对图像中出现的物体类别的预测概率。

假设训练数据集的样本数为 n n ,交叉熵损失函数定义为
( Θ ) = 1 n i = 1 n H ( y ( i ) , y ^ ( i ) ) , \ell(\boldsymbol{\Theta}) = \frac{1}{n} \sum_{i=1}^n H\left(\boldsymbol y^{(i)}, \boldsymbol {\hat y}^{(i)}\right ),

其中 Θ \boldsymbol{\Theta} 代表模型参数。同样地,如果每个样本只有一个标签,那么交叉熵损失可以简写成 ( Θ ) = ( 1 / n ) i = 1 n log y ^ y ( i ) ( i ) \ell(\boldsymbol{\Theta}) = -(1/n) \sum_{i=1}^n \log \hat y_{y^{(i)}}^{(i)} 。从另一个角度来看,我们知道最小化 ( Θ ) \ell(\boldsymbol{\Theta}) 等价于最大化 exp ( n ( Θ ) ) = i = 1 n y ^ y ( i ) ( i ) \exp(-n\ell(\boldsymbol{\Theta}))=\prod_{i=1}^n \hat y_{y^{(i)}}^{(i)} ,即最小化交叉熵损失函数等价于最大化训练数据集所有标签类别的联合预测概率。

模型训练和预测

在训练好softmax回归模型后,给定任一样本特征,就可以预测每个输出类别的概率。通常,我们把预测概率最大的类别作为输出类别。如果它与真实类别(标签)一致,说明这次预测是正确的。在3.6节的实验中,我们将使用准确率(accuracy)来评价模型的表现。它等于正确预测数量与总预测数量之比。
softmax函数性质:
softmax函数的常数不变性:softmax(x)=softmax(x+c)。

3、多层感知机

  1. 多层感知机的基本知识
  2. 使用多层感知机图像分类的从零开始的实现
  3. 使用pytorch的简洁实现

多层感知机的基本知识

深度学习主要关注多层模型。在这里,我们将以多层感知机(multilayer perceptron,MLP)为例,介绍多层神经网络的概念。

隐藏层

下图展示了一个多层感知机的神经网络图,它含有一个隐藏层,该层中有5个隐藏单元。

Image Name

表达公式

具体来说,给定一个小批量样本 X R n × d \boldsymbol{X} \in \mathbb{R}^{n \times d} ,其批量大小为 n n ,输入个数为 d d 。假设多层感知机只有一个隐藏层,其中隐藏单元个数为 h h 。记隐藏层的输出(也称为隐藏层变量或隐藏变量)为 H \boldsymbol{H} ,有 H R n × h \boldsymbol{H} \in \mathbb{R}^{n \times h} 。因为隐藏层和输出层均是全连接层,可以设隐藏层的权重参数和偏差参数分别为 W h R d × h \boldsymbol{W}_h \in \mathbb{R}^{d \times h} b h R 1 × h \boldsymbol{b}_h \in \mathbb{R}^{1 \times h} ,输出层的权重和偏差参数分别为 W o R h × q \boldsymbol{W}_o \in \mathbb{R}^{h \times q} b o R 1 × q \boldsymbol{b}_o \in \mathbb{R}^{1 \times q}

我们先来看一种含单隐藏层的多层感知机的设计。其输出 O R n × q \boldsymbol{O} \in \mathbb{R}^{n \times q} 的计算为

H = X W h + b h , O = H W o + b o , \begin{aligned} \boldsymbol{H} &= \boldsymbol{X} \boldsymbol{W}_h + \boldsymbol{b}_h,\\ \boldsymbol{O} &= \boldsymbol{H} \boldsymbol{W}_o + \boldsymbol{b}_o, \end{aligned}

也就是将隐藏层的输出直接作为输出层的输入。如果将以上两个式子联立起来,可以得到

O = ( X W h + b h ) W o + b o = X W h W o + b h W o + b o . \boldsymbol{O} = (\boldsymbol{X} \boldsymbol{W}_h + \boldsymbol{b}_h)\boldsymbol{W}_o + \boldsymbol{b}_o = \boldsymbol{X} \boldsymbol{W}_h\boldsymbol{W}_o + \boldsymbol{b}_h \boldsymbol{W}_o + \boldsymbol{b}_o.

从联立后的式子可以看出,虽然神经网络引入了隐藏层,却依然等价于一个单层神经网络:其中输出层权重参数为 W h W o \boldsymbol{W}_h\boldsymbol{W}_o ,偏差参数为 b h W o + b o \boldsymbol{b}_h \boldsymbol{W}_o + \boldsymbol{b}_o 。不难发现,即便再添加更多的隐藏层,以上设计依然只能与仅含输出层的单层神经网络等价。

激活函数

上述问题的根源在于全连接层只是对数据做仿射变换(affine transformation),而多个仿射变换的叠加仍然是一个仿射变换。解决问题的一个方法是引入非线性变换,例如对隐藏变量使用按元素运算的非线性函数进行变换,然后再作为下一个全连接层的输入。这个非线性函数被称为激活函数(activation function)。

下面我们介绍几个常用的激活函数:

ReLU函数

ReLU(rectified linear unit)函数提供了一个很简单的非线性变换。给定元素 x x ,该函数定义为

ReLU ( x ) = max ( x , 0 ) . \text{ReLU}(x) = \max(x, 0).

可以看出,ReLU函数只保留正数元素,并将负数元素清零。为了直观地观察这一非线性变换,我们先定义一个绘图函数xyplot。
代码(验证各激活函数用):
%matplotlib inline
import torch
import numpy as np
import matplotlib.pyplot as plt
import sys
sys.path.append("/home/kesci/input")
import d2lzh1981 as d2l
print(torch.version)

def xyplot(x_vals, y_vals, name):
# d2l.set_figsize(figsize=(5, 2.5))
plt.plot(x_vals.detach().numpy(), y_vals.detach().numpy())
plt.xlabel(‘x’)
plt.ylabel(name + ‘(x)’)

x = torch.arange(-8.0, 8.0, 0.1, requires_grad=True)
y = x.relu()
xyplot(x, y, ‘relu’)
Relu函数在区间(-8,8)上的图形
y.sum().backward()
xyplot(x, x.grad, ‘grad of relu’)
在这里插入图片描述

Sigmoid函数

sigmoid函数可以将元素的值变换到0和1之间:

sigmoid ( x ) = 1 1 + exp ( x ) . \text{sigmoid}(x) = \frac{1}{1 + \exp(-x)}.

x.grad.zero_()
y.sum().backward()
xyplot(x, x.grad, ‘grad of sigmoid’)
在这里插入图片描述

tanh函数

tanh(双曲正切)函数可以将元素的值变换到-1和1之间:

tanh ( x ) = 1 exp ( 2 x ) 1 + exp ( 2 x ) . \text{tanh}(x) = \frac{1 - \exp(-2x)}{1 + \exp(-2x)}.

我们接着绘制tanh函数。当输入接近0时,tanh函数接近线性变换。虽然该函数的形状和sigmoid函数的形状很像,但tanh函数在坐标系的原点上对称。

y = x.tanh()
xyplot(x, y, ‘tanh’)
在这里插入图片描述
x.grad.zero_()
y.sum().backward()
xyplot(x, x.grad, ‘grad of tanh’)
在这里插入图片描述

关于激活函数的选择

ReLu函数是一个通用的激活函数,目前在大多数情况下使用。但是,ReLU函数只能在隐藏层中使用。

用于分类器时,sigmoid函数及其组合通常效果更好。由于梯度消失问题,有时要避免使用sigmoid和tanh函数。

在神经网络层数较多的时候,最好使用ReLu函数,ReLu函数比较简单计算量少,而sigmoid和tanh函数计算量大很多。

在选择激活函数的时候可以先选用ReLu函数如果效果不理想可以尝试其他激活函数。

多层感知机

多层感知机就是含有至少一个隐藏层的由全连接层组成的神经网络,且每个隐藏层的输出通过激活函数进行变换。多层感知机的层数和各隐藏层中隐藏单元个数都是超参数。以单隐藏层为例并沿用本节之前定义的符号,多层感知机按以下方式计算输出:
H = ϕ ( X W h + b h ) , O = H W o + b o , \begin{aligned} \boldsymbol{H} &= \phi(\boldsymbol{X} \boldsymbol{W}_h + \boldsymbol{b}_h),\\ \boldsymbol{O} &= \boldsymbol{H} \boldsymbol{W}_o + \boldsymbol{b}_o, \end{aligned}
其中 ϕ \phi 表示激活函数。

4、文本预处理

文本是一类序列数据,一篇文章可以看作是字符或单词的序列,本节将介绍文本数据的常见预处理步骤,预处理通常包括四个步骤:

  1. 读入文本
  2. 分词
  3. 建立字典,将每个词映射到一个唯一的索引(index)
  4. 将文本从词的序列转换为索引的序列,方便输入模型

用现有工具进行分词

我们前面介绍的分词方式非常简单,它至少有以下几个缺点:

  1. 标点符号通常可以提供语义信息,但是我们的方法直接将其丢弃了
  2. 类似“shouldn’t", "doesn’t"这样的词会被错误地处理
  3. 类似"Mr.", "Dr."这样的词会被错误地处理

我们可以通过引入更复杂的规则来解决这些问题,但是事实上,有一些现有的工具可以很好地进行分词,我们在这里简单介绍其中的两个:spaCyNLTK

下面是一个简单的例子:
text = “Mr. Chen doesn’t agree with my suggestion.”
import spacy
nlp = spacy.load(‘en_core_web_sm’)
doc = nlp(text)
print([token.text for token in doc])

NLTK:
from nltk.tokenize import word_tokenize
from nltk import data
data.path.append(’/home/kesci/input/nltk_data3784/nltk_data’)
print(word_tokenize(text))

5、语言模型

一段自然语言文本可以看作是一个离散时间序列,给定一个长度为 T T 的词的序列 w 1 , w 2 , , w T w_1, w_2, \ldots, w_T ,语言模型的目标就是评估该序列是否合理,即计算该序列的概率:

P ( w 1 , w 2 , , w T ) . P(w_1, w_2, \ldots, w_T).

本节我们介绍基于统计的语言模型,主要是 n n 元语法( n n -gram)。在后续内容中,我们将会介绍基于神经网络的语言模型。

语言模型

假设序列 w 1 , w 2 , , w T w_1, w_2, \ldots, w_T 中的每个词是依次生成的,我们有

$$

\begin{align*}
P(w_1, w_2, \ldots, w_T)
&= \prod_{t=1}^T P(w_t \mid w_1, \ldots, w_{t-1})\
&= P(w_1)P(w_2 \mid w_1) \cdots P(w_T \mid w_1w_2\cdots w_{T-1})
\end{align*}

$$

例如,一段含有4个词的文本序列的概率

P ( w 1 , w 2 , w 3 , w 4 ) = P ( w 1 ) P ( w 2 w 1 ) P ( w 3 w 1 , w 2 ) P ( w 4 w 1 , w 2 , w 3 ) . P(w_1, w_2, w_3, w_4) = P(w_1) P(w_2 \mid w_1) P(w_3 \mid w_1, w_2) P(w_4 \mid w_1, w_2, w_3).

语言模型的参数就是词的概率以及给定前几个词情况下的条件概率。设训练数据集为一个大型文本语料库,如维基百科的所有条目,词的概率可以通过该词在训练数据集中的相对词频来计算,例如, w 1 w_1 的概率可以计算为:

$$

\hat P(w_1) = \frac{n(w_1)}{n}

$$

其中 n ( w 1 ) n(w_1) 为语料库中以 w 1 w_1 作为第一个词的文本的数量, n n 为语料库中文本的总数量。

类似的,给定 w 1 w_1 情况下, w 2 w_2 的条件概率可以计算为:

$$

\hat P(w_2 \mid w_1) = \frac{n(w_1, w_2)}{n(w_1)}

$$

其中 n ( w 1 , w 2 ) n(w_1, w_2) 为语料库中以 w 1 w_1 作为第一个词, w 2 w_2 作为第二个词的文本的数量。

n元语法

序列长度增加,计算和存储多个词共同出现的概率的复杂度会呈指数级增加。 n n 元语法通过马尔可夫假设简化模型,马尔科夫假设是指一个词的出现只与前面 n n 个词相关,即 n n 阶马尔可夫链(Markov chain of order n n ),如果 n = 1 n=1 ,那么有 P ( w 3 w 1 , w 2 ) = P ( w 3 w 2 ) P(w_3 \mid w_1, w_2) = P(w_3 \mid w_2) 。基于 n 1 n-1 阶马尔可夫链,我们可以将语言模型改写为

P ( w 1 , w 2 , , w T ) = t = 1 T P ( w t w t ( n 1 ) , , w t 1 ) . P(w_1, w_2, \ldots, w_T) = \prod_{t=1}^T P(w_t \mid w_{t-(n-1)}, \ldots, w_{t-1}) .

以上也叫 n n 元语法( n n -grams),它是基于 n 1 n - 1 阶马尔可夫链的概率语言模型。例如,当 n = 2 n=2 时,含有4个词的文本序列的概率就可以改写为:

$$

\begin{align*}
P(w_1, w_2, w_3, w_4)
&= P(w_1) P(w_2 \mid w_1) P(w_3 \mid w_1, w_2) P(w_4 \mid w_1, w_2, w_3)\
&= P(w_1) P(w_2 \mid w_1) P(w_3 \mid w_2) P(w_4 \mid w_3)
\end{align*}

$$

n n 分别为1、2和3时,我们将其分别称作一元语法(unigram)、二元语法(bigram)和三元语法(trigram)。例如,长度为4的序列 w 1 , w 2 , w 3 , w 4 w_1, w_2, w_3, w_4 在一元语法、二元语法和三元语法中的概率分别为

$$

\begin{aligned}
P(w_1, w_2, w_3, w_4) &= P(w_1) P(w_2) P(w_3) P(w_4) ,\
P(w_1, w_2, w_3, w_4) &= P(w_1) P(w_2 \mid w_1) P(w_3 \mid w_2) P(w_4 \mid w_3) ,\
P(w_1, w_2, w_3, w_4) &= P(w_1) P(w_2 \mid w_1) P(w_3 \mid w_1, w_2) P(w_4 \mid w_2, w_3) .
\end{aligned}

$$

n n 较小时, n n 元语法往往并不准确。例如,在一元语法中,由三个词组成的句子“你走先”和“你先走”的概率是一样的。然而,当 n n 较大时, n n 元语法需要计算并存储大量的词频和多词相邻频率。
n n 元语法可能有哪些缺陷?

  1. 参数空间过大 * 对3元模型 P(w1)P(w2|w1)P(w3|w1,w2) v+v2+v3 参数规模与n呈指数关系,不管计算开销和存储开销都很大*
  2. 数据稀疏 齐夫定律(单词的词频与单词词频的排名呈反比,大部分单词的词频可能非常小,计算出来的参数大部分都是零,稀疏问题非常严重。

6、循环神经网络

本节介绍循环神经网络,下图展示了如何基于循环神经网络实现语言模型。我们的目的是基于当前的输入与过去的输入序列,预测序列的下一个字符。循环神经网络引入一个隐藏变量 H H ,用 H t H_{t} 表示 H H 在时间步 t t 的值。 H t H_{t} 的计算基于 X t X_{t} H t 1 H_{t-1} ,可以认为 H t H_{t} 记录了到当前字符为止的序列信息,利用 H t H_{t} 对序列的下一个字符进行预测。
Image Name

循环神经网络的构造

我们先看循环神经网络的具体构造。假设 X t R n × d \boldsymbol{X}_t \in \mathbb{R}^{n \times d} 是时间步 t t 的小批量输入, H t R n × h \boldsymbol{H}_t \in \mathbb{R}^{n \times h} 是该时间步的隐藏变量,则:

H t = ϕ ( X t W x h + H t 1 W h h + b h ) . \boldsymbol{H}_t = \phi(\boldsymbol{X}_t \boldsymbol{W}_{xh} + \boldsymbol{H}_{t-1} \boldsymbol{W}_{hh} + \boldsymbol{b}_h).

其中, W x h R d × h \boldsymbol{W}_{xh} \in \mathbb{R}^{d \times h} W h h R h × h \boldsymbol{W}_{hh} \in \mathbb{R}^{h \times h} b h R 1 × h \boldsymbol{b}_{h} \in \mathbb{R}^{1 \times h} ϕ \phi 函数是非线性激活函数。由于引入了 H t 1 W h h \boldsymbol{H}_{t-1} \boldsymbol{W}_{hh} H t H_{t} 能够捕捉截至当前时间步的序列的历史信息,就像是神经网络当前时间步的状态或记忆一样。由于 H t H_{t} 的计算基于 H t 1 H_{t-1} ,上式的计算是循环的,使用循环计算的网络即循环神经网络(recurrent neural network)。

在时间步 t t ,输出层的输出为:

O t = H t W h q + b q . \boldsymbol{O}_t = \boldsymbol{H}_t \boldsymbol{W}_{hq} + \boldsymbol{b}_q.

其中 W h q R h × q \boldsymbol{W}_{hq} \in \mathbb{R}^{h \times q} b q R 1 × q \boldsymbol{b}_q \in \mathbb{R}^{1 \times q}

困惑度

我们通常使用困惑度(perplexity)来评价语言模型的好坏。回忆一下“softmax回归”一节中交叉熵损失函数的定义。困惑度是对交叉熵损失函数做指数运算后得到的值。特别地,

  • 最佳情况下,模型总是把标签类别的概率预测为1,此时困惑度为1;
  • 最坏情况下,模型总是把标签类别的概率预测为0,此时困惑度为正无穷;
  • 基线情况下,模型总是预测所有类别的概率都相同,此时困惑度为类别个数。

显然,任何一个有效模型的困惑度必须小于类别个数。在本例中,困惑度必须小于词典大小vocab_size

发布了1 篇原创文章 · 获赞 0 · 访问量 69

猜你喜欢

转载自blog.csdn.net/water19111213/article/details/104311075
今日推荐