cs224n学习笔记 4

1 一般的分类问题

1.1 softmax classifier

训练集的形式是(x_i,y_i)_{i=1}^N,其中x_i是inputs,y_i是labels;在这里,我们做出一些约定,方便后面进行说明:我们认为x_i是一个d维的向量,y_i表示C个类别中的一类

当训练结束,进入预测阶段的时候,我们希望我们的模型能够预测"输入为x,类别为y”这件事的概率,我们可以借助训练得到的参数直接进行计算,计算的式子为p(y|x) = \frac{exp(W_{y.}x)}{\sum_{i=1}^C{exp(W_{c.}x)}},其中参数W是一个C*d的矩阵,该矩阵的第i行是预测输入类别为i时需要用到的参数,我们记W的第i行为W_{i.}

如此一来,上述公式的意义就比较明显了,W_{c.}x可以看做是为输入x的类别是y这件事打了一个分数,而最后得到的概率就是对这个分数进行了一个softmax操作。为了后面方便表示,我们进一步约定:把“输入x的类别是y这件事打分”这个函数记为f_y,即

预测的问题已经说明白了,那么就该考虑如何训练模型得到参数矩阵W。实际上方法也很简单,假设输入x的真实类别就是y,那么我们自然希望p(y|x)的概率尽量大,也就是希望-log(p(y|x))的值尽量小,我们只要使用梯度下降的方法,将-log(p(y|x))作为优化目标进行最小化,最终就可以得到W;当然,我们的训练集当中一共有N笔数据,所以object function的形式就是

J(\theta) = \frac{1}{N}\sum_{i=1}^{N}-log{\frac{exp(f_{y_i})}{\sum_{c=1}^{C}exp(f_c)}}

在实现的过程中,还有一点需要注意:为了提升代码执行的效率以及方便书写,我们不会依次计算f_1,f_2,...,f_c,我们会令f=Wx_i,这样得到的f是一个C*1的列向量,它的每一个元素依次对应了f_1,f_2,...,f_c

当矩阵W比较大,参数比较多的时候,还会涉及到一个老生常谈的话题,就是过拟合的问题。为了解决过拟合的问题,我们往往会在目标函数当中引入一个正则化的项,引入正则化因子之后softmax分类器的object function的形式为

1.2 交叉熵损失(Cross entropy)

在很多模型的训练过程中,我们都希望模型预测得到的向量和输入的标签的one-hot向量二者尽可能相似,但是这个“相似”要如何定义呢?比如对于某一条数据,模型预测得到的概率结果是向量[0.1 0.7 0.2],而这条数据的标签是one-hot向量[0 1 0],我要如何量化他们之间的相似性,来衡量我预测结果的优劣呢?

一个比较好的方法就是使用交叉熵:对于两个C维的向量\vec{p}\vec{q}\vec{p}是一个代表实际标签的one-hot向量,\vec{q}是我们预测得到的概率组成的结果向量,我们称H(\vec{p}, \vec{q}) = -\sum_{c=1}^{C}\vec{p}(c)log(\vec{q}(c))的大小为二者的交叉熵;\vec{p}\vec{q}之间的差异越大,他们的交叉熵也越大,反之则越小,因此我们可以把交叉熵作为我们要最小化的对象

对于1.1中的softmax classifier问题,针对其中的某一条数据(x_i,y_i),我们前面提到过要最小化的对象是-log(p(y_i|x_i))。之前解释的出发点是要让正确预测的概率最大,现在我们可以从交叉熵的角度给出另外一种解释:这条数据的标签向量是一个只在第y_i维是1的one-hot向量\vec{p},模型预测得到的向量\vec{q} = [p(1|x_i),p(2|x_i),...,p(C|x_i)]\vec{p}\vec{q}的交叉熵H(\vec{p},\vec{q})的值就是-log(p(y_i|x_i)),所以最小化-log(p(y_i|x_i))实际上就是最小化\vec{p}\vec{q}之间的交叉熵

1.3 分类对象是词向量

当分类的对象是词向量的时候,我们在训练过程中,更新权重矩阵W的同时,一般还要更新参与训练的词的词向量,由此引发了两个可能产生问题的地方,需要特别注意:

第一,由于全部词向量的个数非常多,这样一来参数的个数就增加了很多,参数数量太多就更容易过拟合,因此需要一定的手段来进行遏制

第二,往往我们用来进行分类的单词的词向量,不是我们自己通过词嵌入算法得到的,而是直接下载别人通过大量的文本训练得到的现成的词向量,如果我们在自己训练的过程中,在已有的基础上对词向量进行了重新训练或者是更新,就可能造成下面的问题:别人在大量文本上训练,因此得到的TV、telly、television三个词的词向量比较接近,而我们自己的参与训练的文本因为比较小,只出现了TV和television这两个词,通过训练之后反而使得telly距离另外两个同义词的距离变得更大了。这显然是我们不希望看到的,对于这个问题的经验就是,如果自己参与训练的文本比较少,就不要轻易对词向量进行更新。

2 Window Classification

对于一般的机器学习问题,第1节的分类方法就是一个非常传统的解决方案,但是针对NLP领域,分类的对象如果是单词的话,第1节的方法往往是不能直接使用的,原因是对于语言来说,常常会出现一词多义的现象,在没有语境的前提下,对于一个单词进行分类会出现很多问题。比如seed这个单词,既可以做“播种种子”的意思,同时也可以做“铲除种子”的意思;再比如我想通过分类判断Paris这个词是否是一个地点,但是Paris作为巴黎来讲的时候就是一个地点,但是作为一个人名来讲的时候就不是一个地点

因此,在训练分类器的时候要把上下文考虑进去,这就是Window Classification的出发点

Window Classification其实也只是在一般的分类问题上进行了一点改变,假设原来原来词向量的长度是d,那么当我想要使用某个词进行训练的时候,我就以这个词为中心,以m为半径,将这2m+1个单词的词向量拼起来,作为一个(2m+1)*d维的向量进行训练,相应的,参数矩阵W的维度变成了R\times{(2m+1)d}。这样一来,Window Classification就化归成了一个一般的分类问题

在1中,给出了一般的分类问题的object function,但是没有计算更新时的梯度,这节给出分类问题的梯度计算

2.1 Window Classification梯度计算

一般分类问题的object function形式为J(\theta)=\sum_{i=1}^{N}-log\frac{exp(W_{y_i.}x)}{\sum_{c=1}^{C}exp(W_{c.}x)},为了方便后面我们使用符号表示,我们再定义下面的几个符号:

f_y(x)=W_{y.}xf_y(x)可以简记为f_y

p(y|x)=softmax(f_y)=\frac{exp(W_{y.}x)}{\sum_{c=1}^{C}exp(W_{c.}x)}

\vec{y}=[p(1|x),p(2|x),...,p(C|x)]代表预测的结果向量

下面,我们将推导训练过程中每一次对词向量更新时使用的梯度

\frac{\partial}{\partial x}-log \ softmax(f_y(x))=\sum_{i=1}^{C}\frac{\partial\ log \ softmax(f_y(x))}{\partial f_c}\frac{\partial f_c(x)}{\partial x}

将求和号右边两部分分别计算

cs224n学习笔记 4_第1张图片

\frac{\partial f_c(x)}{\partial x}=\frac{\partial W_{c.}x}{\partial x} = W_{c.}^T

既然两部分都表示出来了,我们就可以求出原来要求的梯度的值,但是为了能够表示的比较精简,我们引入下面的几个符号:

\vec{\delta} =\frac{\partial -log\ softmax(f_y)}{\partial f}= [\frac{\partial -log\ softmax(f_y)}{\partial f_1}, \frac{\partial -log\ softmax(f_y)}{\partial f_2}, ...,\frac{\partial -log\ softmax(f_y)}{\partial f_C}]^T,

\vec{t}是代表输入的实际类别的one-hot向量,根据上面\frac{\partial-log \ softmax(f_y(x))}{\partial f_c}的结果可知有

\vec{\delta} = \vec{y}-\vec{t}

这么一来,就有\begin{align*} \frac{\partial}{\partial x}-log \ softmax(f_y(x))=\sum_{i=1}^{C}\frac{\partial\ log \ softmax(f_y(x))}{\partial f_c}\frac{\partial f_c(x)}{\partial x} =\sum_{i=1}^{C}\vec{\delta_c}W_{c.}^{T}=W^{T}\vec{\delta} \end{align*},就得到了更新x时使用的梯度,更新W时使用的梯度课上没有给出具体求解过程,但是思路是一样的

2.2 实践中的注意点

在上面求x的梯度的过程中,我们一直试图引入更多的记号,把零散的数值整合成向量,把零散的向量整合成矩阵的形式,一方面是为了方便表示,另一方面是为了提升计算的效率

之所以矩阵运算比单独的元素或向量运算的速度要快,是因为底层有很多黑科技可以加速矩阵乘法的速度,如此一来能减少相当大的时间开销

3 神经网络

在1和2中,解决分类问题所使用的方法其实是logistic regression(虽然和很多地方讲解的logistic regression与上面使用的参数数量、激活函数的形式并不一样),这样产生的一个问题就是,在不进行维度映射的情况下,这样的方法只能作为一个线性分类器发挥作用,拟合能力及其有限;为了能够提升拟合能力,神经网络应运而生

神经网络往往是由很多层构成的,每层又有很多个神经元,从本质上来说,每个神经元都在执行一个logistic regression,前一层神经元的结果传到下一层的神经元上,使得模型能够拟合更为复杂的问题

3.1 单个神经元结构

cs224n学习笔记 4_第2张图片

单个神经元的结构如图,首先确定一点,在表示神经网络的图中,一个神经元本身(图中橘色圆圈)代表的是一个数值大小,这个数值大小一般是一个向量某一维度上的值

右边从神经元中射出的箭头,代表的是这个神经元连向其他的神经元的“出口”,假设神经元本身的值大小是v,那么他作为“出口”连向其他神经元的时候也需要为连向的神经元提供一个值,但这个值的大小并不是v,而是h(v),这里的h是一个激活函数,比如sigmoid函数

左边进入神经元的箭头,代表的是这个神经元的“入口”。这些箭头都是由其他神经元的“出口”连过来的,每一个“出口”提供一个数值x_i,再乘上一个权重w_i,累加起来就是神经元本身的数值v

如此一来,我们就可以说,神经元作为“入口”累加得到的值是v=w_1 \times x_1+w_2 \times x_2+w_3 \times x_3+b\times1=w^Tx+b,而神经元作为“出口”参与其他神经元运算时使用的值为z=f(v),在本节中,我们默认激活函数是sigmoid函数,那么有z=h(v)=\frac{1}{1+e^{-v}}

3.2 相邻两层神经网络之间的关系

cs224n学习笔记 4_第3张图片

如图是一个单层的神经网络结构(单层指只有一个隐藏层),第一层是输入层;第二层称为隐藏层,注意这里a_1,a_2,a_3不是隐藏层神经元本身的值,而是经激活函数之后作为“出口”时的值;第三层是输出层,神经元的值经由激活函数之后,输出一个值,这就是这个模型的输出,我们记为s

根据3.1中的分析,我们知道这个图中满足

\begin{align*} &a1=h(W_{11}\times x_1 + W_{12}\times x_2 + W_{13} \times x_3 + b_1)\\ &a2=h(W_{21}\times x_1 + W_{22}\times x_2 + W_{23} \times x_3 + b_2)\\ &a3=h(W_{31}\times x_1 + W_{32}\times x_2 + W_{33} \times x_3 + b_3)\\ \end{align*}

如果我们以向量和矩阵的角度来认识相邻两层神经网络之间的关系,那么我们令\vec{x}=[x_1,x_2,x_3]^T,令所有权重W_{ij}组成一个矩阵,令所有偏置组成一个列向量\vec{b},令所有输出值组成一个向量\vec{a} = [a_1,a_2,a_3]^T

根据上面的符号,有\vec{a}=h(W\vec{x} + \vec{b}),这就是神经网络结构中相邻两层之间的关系。其中矩阵W是一个n_1 \times n_2维的矩阵,其中n_1是前一层中神经元个数,n_2是后一层中神经元的个数;向量\vec{b}则是n_2 \times 1维的列向量。

3.3 单层神经网络的结构

cs224n学习笔记 4_第4张图片

如图是一个单层的神经网络:

输入\vec{x}是一个20维的列向量,输入层和隐藏层之间的权重矩阵W是一个8 \times 20维的矩阵(说明隐藏层神经元个数是8个),输入层和隐藏层之间的偏置向量是一个8维的列向量

隐藏层神经元的值组成一个8维的列向量\vec{z},值为\vec{z} = W \vec{x} + \vec{b};经过激活函数后,得到一个8维的列向量\vec{a}\vec{a} = f(\vec{z})

在输出层中,输出一个数值s作为“得分”。隐藏层和输出层之间用一个8 \times 1维的参数矩阵U来进行沟通,他们之间的关系是s=U^Ta

但是s并不是我们直接的损失函数,在这个地方为了配合s这个“得分”,我们选择使用max-margin loss作为损失函数,这个损失函数的形式是J(\theta)=max(0, 1 - s + s_c)。式子里符号的含义如下:“得分”s是用label为1的“正样本”计算出的得分,“得分”s_c是用label为0的“负样本”计算出的得分。注意虽然式子里带着一个max函数,但是我们的目标是要让J(\theta)的值最小,J(\theta)小意味着我们希望“正样本”得分尽量高,“负样本”得分尽量低。最后,max函数起到的作用是:当“正样本”得分比“负样本”得分大的值超过1的时候,我们就认为结果足够好了,J(\theta)直接取0,这样就不需要更多的训练,加快了模型收敛。

当然,这个例子只是神经网络的一个形式,事实上很多“组件”都可以进行替换:比如我们可以用Relu函数替代sigmoid作为激活函数;再比如输出层,我们可以对\vec{a}做softmax操作,得到一个8 \times 1的输出层,然后loss funciton改用交叉熵损失,这样一来就可以代替s=U^Ta再对s做max-margin loss

本节后面还对这个神经网络中每个变量进行了求导,但这个内容和下一节的内容有很大重复,所以放在下一节来写

 

 

 

你可能感兴趣的:(cs224d笔记,NLP,自然语言处理,深度学习,人工智能,机器学习)