我们在上节课快结束的时候提到了窗口分类,Lecture 3这节课更详细的介绍了常用分类的背景、窗口分类、更新词向量以实现分类,交叉熵推导经验等,课程的最后5分钟教授简单的介绍了单层神经网络,这部分笔记放在第五节课中。
分类器我们都比较了解,通常给定一个训数据集,其中包含这样的样本:
{ x i , y i } i = 1 N \{x_i,y_i\}_{i=1}^N {xi,yi}i=1N
假设我们以简单的直观的 2 d 2d 2d 词向量分类为例,使用逻辑回归方法(线性决策边界),通常机器学习分类方法是这样做的:输入 x x x 是固定的,只需要训练逻辑回归的权重 W W W 和 仅仅修改决策边界:
p ( y ∣ x ) = e x p ( W y ⋅ x ) ∑ c = 1 C e x p ( W c ⋅ x ) p(y|x) = \frac{exp(W_{y\cdot}x)}{\sum_{c=1}^Cexp(W_{c\cdot}x)} p(y∣x)=∑c=1Cexp(Wc⋅x)exp(Wy⋅x)
训练集 { x i , y i } i = 1 N \{x_i,y_i\}_{i=1}^N {xi,yi}i=1N 的损失函数为:
J ( θ ) = 1 N ∑ i = 1 N − l o g ( e f y i ∑ c = 1 C e f c ) J(\theta) = \frac{1}{N}\sum_{i=1}^N-log(\frac{e^{fy_i}}{\sum_{c=1}^Ce^{fc}}) J(θ)=N1i=1∑N−log(∑c=1Cefcefyi)
对每个数据对 ( x i , y i ) (x_i,y_i) (xi,yi) : f y = f y ( x ) = W y ⋅ x = ∑ j = 1 d W y j x j fy = f_y(x) = W_{y\cdot}x = \sum_{j=1}^dW_{yj}x_j fy=fy(x)=Wy⋅x=∑j=1dWyjxj
我们将 f f f 写成矩阵的形式: f = W x f = Wx f=Wx 。
实际使用时,训练数据集上损失函数包括所有参数 θ \theta θ 的 正则化Regularization:
J ( θ ) = 1 N ∑ i = 1 N − l o g ( e f y i ∑ c = 1 C e f c ) + λ ∑ k θ k 2 J(\theta) = \frac{1}{N}\sum_{i=1}^N-log(\frac{e^{fy_i}}{\sum_{c=1}^Ce^{fc}}) + \lambda\sum_k{\theta}_k^2 J(θ)=N1i=1∑N−log(∑c=1Cefcefyi)+λk∑θk2
当我们有很多特征或者如后面所见我们有很深的深度模型的时候,正则化能够避免过拟合。
上面讲述了通常所用的分类方法,接下来我们看看使用词向量来分类有什么不同?
上述通用的机器学习方法中参数 θ \theta θ 通常只包含 W W W 的列:
θ = [ W ⋅ 1 ⋮ W ⋅ d ] = W ( : ) ∈ R C d \theta = \begin{bmatrix}W_{\cdot{1}}\\\vdots\\W_{\cdot{d}}\end{bmatrix} = W(:) \in R^{Cd} θ=⎣⎢⎡W⋅1⋮W⋅d⎦⎥⎤=W(:)∈RCd
所以,我们只需要更新决策边界:
∇ θ J ( θ ) = [ ∇ W ⋅ 1 ⋮ ∇ W ⋅ d ] ∈ R C d \nabla_{\theta}J(\theta) = \begin{bmatrix}\nabla{W_{\cdot{1}}}\\\vdots\\\nabla{W_{\cdot{d}}}\end{bmatrix} \in R^{Cd} ∇θJ(θ)=⎣⎢⎡∇W⋅1⋮∇W⋅d⎦⎥⎤∈RCd
然而,在深度学习方法中,我们需要同时学习权重 W W W 和 词向量 x x x :
∇ θ J ( θ ) = [ ∇ W ⋅ 1 ⋮ ∇ W ⋅ d ∇ x a a r d v a r k ⋮ ∇ x z e b r a ] ∈ R C d + V d \nabla_{\theta}J(\theta) = \begin{bmatrix}\nabla{W_{\cdot{1}}}\\\vdots\\\nabla{W_{\cdot{d}}}\\\nabla{x_{aardvark}}\\\vdots\\\nabla{x_{zebra}}\end{bmatrix} \in R^{Cd+Vd} ∇θJ(θ)=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡∇W⋅1⋮∇W⋅d∇xaardvark⋮∇xzebra⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤∈RCd+Vd
从上式可以看到,需要求导的未知参数的范围 C d + V d Cd+Vd Cd+Vd, V V V 表示整个词汇集大小,通常是百万级别,如此大的未知参数会导致两个问题:过拟合和失去泛化能力。
上面的这种算法也就是之前课程我们提到过得根据实际任务对词向量的重训练方法,失去泛化能力是什么意思呢?我们还是用之前的同一个例子,假设我们要训练电影评论的情感分类问题,在训练集中有"TV"和“Telly”两个词表示“电视”,测试集中有“television”这个词,当我们重训练词向量的时候发生了什么呢?一方面训练集中的数据在向量空间发生了移动,而测试集中的"television"没有出现在训练集中,所以不会发生移动,这个时候,“TV”,“Telly”和“television”的相对位置就产生了变化,也就失去了他们之间的相似性。(slides上有个图片可以直接的解释这个问题,感兴趣的同学可以自己查看。)
这就给我们带来了启示:
在讲词向量用于分类之前,我们首先来做一些标记,方面后面公式的理解。
词向量矩阵 L L L 也叫做查找表,通常在word2vec或者GloVe中是一个 d d d 行 ∣ V ∣ |V| ∣V∣ 列大小的矩阵,其中每一列表示对应索引的词的词向量,也就是后面我们所说的词的特征。
从概念上说,我们额可以通过乘以一个one-hot向量 e e e 来得到某个词的向量,即 x = L e ∈ d ∗ V ⋅ V ∗ 1 x=Le \in d*V\cdot{V}* 1 x=Le∈d∗V⋅V∗1
我们很少对单个词进行分类,原因是词的多义性。例如,“to seed”的意思可能是播种,也可能是除种,“Hathaway”可能表示城市,也可能表示女明星,这些意思都取决于词的上下文。
所以,就有了这样一个想法:针对单词结合它的上下文窗口来分类。
举个例子,NER任务中我们将词分成4个类别:人名、地名、机构名、其他。很多根据窗口分类的方法是这样做的,例如,对窗口中的词的向量取平均值作为特征,很明显,这样就失去了位置信息。
通用的方法是怎样的呢?通过中心词赋予一个类标,将它周围的所有词的向量首尾拼接,来训练分类器。
例如:对句子“… museums in Paris are amazing … .”这个句子对“Paris”以大小为2的窗口分类,那么就产生了一个大小为 5 d 5d 5d的列向量:
X w i n d o w = [ x m u s e u m s x i n x P a r i s x a r e x a m a z i n g ] ∈ R 5 d X_{window} = [x_{museums} \ x_{in} \ x_{Paris} \ x_{are} \ x_{amazing}] \in R^{5d} Xwindow=[xmuseums xin xParis xare xamazing]∈R5d
我们来看一个最简单的窗口分类器:softmax
y ^ = p ( y ∣ x ) = e x p ( W y ⋅ x ) ∑ c = 1 C e x p ( W c ⋅ x ) \hat{y} = p(y|x) = \frac{exp(W_{y\cdot}x)}{\sum_{c=1}^Cexp(W_{c\cdot}x)} y^=p(y∣x)=∑c=1Cexp(Wc⋅x)exp(Wy⋅x)
以交叉熵为损失函数:
J ( θ ) = 1 N ∑ i = 1 N − l o g ( e f y i ∑ c = 1 C e f c ) J(\theta) = \frac{1}{N}\sum_{i=1}^N-log(\frac{e^{fy_i}}{\sum_{c=1}^Ce^{fc}}) J(θ)=N1i=1∑N−log(∑c=1Cefcefyi)
问题来了,我们如何更新词向量呢?
简短的回答:和前面一样,对每个参数推导。具体的很长的过程我们在PSet #1中已经实现了细节。
更新首尾连接的词向量,有以下技巧需要注意:
定义变量要仔细,并且时刻注意它们的维度。例如:
f = f ( x ) = W x ∈ R C f= f(x) = Wx \in R^C f=f(x)=Wx∈RC
y ^ t W ∈ R C ∗ 5 d \hat{y} \ \ \ \ t\ \ \ W \in R^{C*5d} y^ t W∈RC∗5d
链式法则很重要!不要忘记哪些变量使用了其他变量。
对softmax推导,首先计算 c = y c=y c=y时 f c fc fc的导数,然后计算 c ≠ y c\neq{y} c̸=y时 f c fc fc的导数。
当对 f f f中的每个元素求导后,不要忘记查看,是否能够创造一个包含所有部分导数的梯度,例如:
∂ ∂ f − l o g s o f t m a x ( f y ) = [ y ^ 1 ⋮ y ^ y − 1 ⋮ y ^ C ] = y ^ − t \frac{\partial}{\partial{f}}-log\ softmax(fy) = \begin{bmatrix}\hat{y}_1\\\vdots\\\hat{y}_y-1\\\vdots\\{\hat{y}}_C\end{bmatrix} = \hat{y} - t ∂f∂−log softmax(fy)=⎣⎢⎢⎢⎢⎢⎢⎡y^1⋮y^y−1⋮y^C⎦⎥⎥⎥⎥⎥⎥⎤=y^−t
为了节省后续麻烦,推导结果尽量用向量操作,并且及时定义新的单索引的向量,例如:
∂ ∂ f − l o g s o f t m a x ( f y ) = [ y ^ − t ] = δ \frac{\partial}{\partial{f}}-log\ softmax(fy) = [\hat{y} - t] = \delta ∂f∂−log softmax(fy)=[y^−t]=δ
当使用链式法则的时候,首先使用显示的求和来部分求导,例如对 x i x_i xi 或者 W i j W_{ij} Wij求导:
∑ c = 1 C − ∂ o g s o f t m a x ( f y ( x ) ) ∂ f c ∂ f c ( x ) ∂ x = ∑ c = 1 C δ c W c \sum_{c=1}^C-\frac{\partial{og\ softmax(fy(x)) }}{\partial{fc}}\frac{\partial{fc(x)}}{\partial{x}} = \sum_{c=1}^C{\delta_c}W_c c=1∑C−∂fc∂og softmax(fy(x))∂x∂fc(x)=c=1∑CδcWc
复杂函数的简化,注意变量的维度,尽量用矩阵来简化,例如:
∂ ∂ x − l o g p ( y ∣ x ) = ∑ c = 1 C δ c W c ⋅ = W T δ \frac{\partial}{\partial{x}}-logp(y|x) = \sum_{c=1}^C\delta_cW_{c\cdot} = W^T\delta ∂x∂−logp(y∣x)=c=1∑CδcWc⋅=WTδ
如果不清楚就把求和展开写。
第7点中公式最终的维度是多少?回答:5d。 x x x是5d大小,那么对 x x x求导数也是5d大小。
一个重要的提示:
在实现softmax的时候有两个代价很高的操作,分别是矩阵相乘和指数计算。我们在代码实现过程中,应该尽量避免使用for循环计算矩阵的相乘和其他数学计算。你可以用以下代码计算一下for循环的代价:
from numpy import random
N = 500
d = 300
C = 5
W = rando.rand(C,d)
wordvectors_list = [random.rand(d, 1) for i in range(N)]
wordvectors_one_matrix = random.rand(d, N)
%timeit [W.dot(wordvectors_list[i]) for i in range(N)]
%timeit W.dot(wordvectors_one_matrix)
softmax(=逻辑回归)并不是足够强大的算法。softmax只是在原始空间内给定一个线性的决策边界,对于少量的数据集可能会得到很好的结果,但是对于大量数据,它的作用是有限的!
接下来我们会介绍神经网络,它会更复杂,但是也会产生非线性的决策边界。