最近准备认真学习NLP,所以找了一个比较好的NLP课程即斯坦福大学的CS224N,总共有三次作业,我已经完成了第一次作业,把做的过程和方法写成博客,一个目的是分享,一个目的是作为自己的笔记。
作业是使用Python2.7写的,不过我现在基本都在用Python3.5以上了,但是不用担心,只需要改很少的一部分即可。作业形式是给出大体的框架,然后要求我们实现其中的关键函数功能,这种也是比较常见的作业形式,就像Andrew Ng(吴恩达)的Deep Learning课程作业一样。第一次作业分为四个问题,也可以称为四个部分。第一个部分实现softmax即可,第二部分需要实现一个梯度检验、sigmoid以及其导数和一个简单的神经网络的正向和反向算法,第三部分需要实现word2vec的skip-gram和cbow算法,第四部分是基于之前的word embedding结果进行句子情感分类。
首先,我们要实现softmax,我们需要知道softmax是什么?
softmax是将一系列值转化为0-1之间的概率值,并且他们的和为1,这个应该是比较通俗的解释,比较好理解,我们来看一下softmax如何进行转化:
p ( a i ) = e a i ∑ i = 0 k e a i p(a_{i})=\frac{e^{a_{i}}}{\sum_{i=0}^{k}e^{a_{i}}} p(ai)=∑i=0keaieai
看起来还是很简单,我们来讨论一下为什么使用指数函数:
我们可以看到函数传进来的是一个句子,然后在它提示的地方写关键代码(YOUR CODE HERE AND END YOUR CODE)即可,写完之后记得将raise NotImplementedError注释掉。
这个使用python的numpy非常容易:
比如当shape<=1时,即数据只有一行时:
x = np.exp(x)/np.sum(np.exp(x))
当shape>1时,即数据有多行,分别对某一行求:
x = np.exp(x)/np.sum(np.exp(x),axis=1,keepdims=True)
np.exp(x)函数就是对矩阵x的某一个元素求指数,np.sum(x)就是将矩阵x的所有元素求和并返回。
而axis=1(或者0)就是按照行求和,还是按照列求和,而keepdims如果为False就返回(shape,)的向量,而为True时返回(shape,1)的向量,这个是很重要的,如果不注意就会在矩阵乘法或者取值的时候出错。
这样做之后我们会以为自己已经完成了,但是这里有个小问题,也是第一次作业第一部分唯一的问题吧(我觉得),我们发现出现的结果里面有nan,nan在python里面的意思是not a number,出现这个结果的原因就是数太大了,求指数之后更大了,超过这个数据类型可以承载的,就相当正无穷的,所以使用softmax的时候就需要使用一个小trick(小技巧),就是先做一下减去最大值的运算:
只有一行:x = x-np.max(x)
多行:x = x - np.max(x,axis=1,keepdims=True)
下面我们证明一下这样做的合理性:
假设我们只有两个数a,b:
p ( a ) = e a e a + e b , p ( b ) = e b e a + e b p(a)=\frac{e^{a}}{e^{a}+e^{b}},\quad \quad p(b)=\frac{e^{b}}{e^{a}+e^{b}} p(a)=ea+ebea,p(b)=ea+ebeb
我们对分子分母除以最大值(不妨设为a)的指数之后:
p ( a ) = e a − a e a − a + e b − a , p ( b ) = e b − a e a − a + e b − a p(a)=\frac{e^{a-a}}{e^{a-a}+e^{b-a}},\quad \quad p(b)=\frac{e^{b-a}}{e^{a-a}+e^{b-a}} p(a)=ea−a+eb−aea−a,p(b)=ea−a+eb−aeb−a
这就相当于对于每个数减去最大值(a)。
所以对于每个元素减去最大值之后不会影响结果,并且不会出现nan,但是可能因为有点值太大,会出现某个值的概率为1,但是这是合理的,因为概率为1是合理的,并且是可以运算的。
欢迎评论交流,也欢迎关注,会将CS224N的所有作业写成博客的。