笔者在总结时跳过了第3,4,5节课。其中第3,4节课分别介绍了神经网络和反向传播,笔者假定读者有着基本的机器学习和神经网络相关的知识,所以掠过了这两章内容。其中第5节课介绍了语法结构,但是因为与笔者目前方向不一致,故略去。
本节为第6节课,介绍了Language Model并且以此为契机引入了RNN模型。
天气预报说今天下雨,所以出门要________
语言模型要做的事情,就是给出一段文字,由模型生成其后的内容。语言模型是NLP领域内一个bench mark的问题。
假设一段文字给出了前 n n n 个词 x ( 1 ) , x ( 2 ) , . . . , x ( n ) x^{(1)},x^{(2)},...,x^{(n)} x(1),x(2),...,x(n),要生成的词为 x ( n + 1 ) x^{(n+1)} x(n+1) 的概率为
P ( x ( n + 1 ) ∣ x ( 1 ) , x ( 2 ) , . . . , x ( n ) ) P(x^{(n+1)}|x^{(1)},x^{(2)},...,x^{(n)}) P(x(n+1)∣x(1),x(2),...,x(n))
如果可以计算出这个分布,就可以得到一个已知上文生成下文的概率分布,从而通过选取概率最大的结果来实现生成操作。改变 n n n 的值,即可改变上文窗口的大小,对模型做出调整。那么可以直接暴力遍历整个语料库,并且计算概率,结果保存在一个巨大的概率矩阵中。
这种做法类似于一个马尔可夫模型,其中每个节点为n个前后相继的词,也就是说其状态空间为 ( ∣ X ∣ ) n (|X|)^{n} (∣X∣)n ,其中 ∣ X ∣ |X| ∣X∣ 为词汇集的大小。然后暴力枚举语料库,并且人工标注每个转移发生的概率,需要注意的是,可能发生的转移只有 { a , b , . . . , c } → { b , . . . , c , d } \{a,b,...,c\}\rightarrow\{b,...,c,d\} {a,b,...,c}→{b,...,c,d}
这种做法有着如下致命缺陷:
所以考虑神经网络版本的n-gram Language Model。
还是一样的模型,只不过这一次我们用神经网络来代替暴力枚举马尔可夫模型中转移边的概率。即输入有序的n个词向量 x ( 1 ) , x ( 2 ) , . . . , x ( n ) x^{(1)},x^{(2)},...,x^{(n)} x(1),x(2),...,x(n) , 输出预期的 x ( n + 1 ) x^{(n+1)} x(n+1) , 构造一个
{ x ( 1 ) , x ( 2 ) , . . . , x ( n ) } → { x ( n + 1 ) } \{x^{(1)},x^{(2)},...,x^{(n)}\}\rightarrow\{x^{(n+1)}\} {x(1),x(2),...,x(n)}→{x(n+1)}
的映射,并通过神经网络来学习这个映射。从而解决了基于概率矩阵的第2,3个问题,结果变成连续值,且只需要存储神经网络的权重即可。但是但是第一个问题仍然存在,且因为窗口固定,无法适应不同长度的文本。此外,每个位置上对于每个输入值带来的影响是独立计算的,但是事实上他们之间往往有着某些关系,该模型也无法刻画这种关系。
因此,引出循环神经网络RNN模型。
RNN和普通的神经网络相比,多了一个模型自带的参数,不妨记为 h,且有着一个time-step的概念,即每一时刻会发生一些不同的操作。假设在 i i i 时刻时,输入参数为词向量 x ( i ) x^{(i)} x(i),当前自带参数为 h ( i ) h^{(i)} h(i),当前时刻的输出值 y ^ ( i + 1 ) \hat{y}^{(i+1)} y^(i+1),则有
y ^ ( i + 1 ) = f ( x ( i ) , h ( i ) , θ ) \hat{y}^{(i+1)} = f(x^{(i)},h^{(i)},\theta) y^(i+1)=f(x(i),h(i),θ)
h ( i + 1 ) = g ( x ( i ) , h ( i ) , θ ) h^{(i+1)} = g(x^{(i)},h^{(i)},\theta) h(i+1)=g(x(i),h(i),θ)
其中, f , g f,g f,g 一般都是简单的线性回归模型,如
y ^ ( i + 1 ) = σ ( W x ⋅ x ( i ) + W h ⋅ h ( i ) + b 1 ) \hat{y}^{(i+1)} = \sigma(W_x\cdot x^{(i)}+W_h\cdot h^{(i)}+b_1) y^(i+1)=σ(Wx⋅x(i)+Wh⋅h(i)+b1)
h ( i + 1 ) = s o f t m a x ( U ⋅ h ( i ) + b 2 ) h^{(i+1)} = softmax(U\cdot h^{(i)}+b_2) h(i+1)=softmax(U⋅h(i)+b2)
(其中 W x , W h , U , b 1 , b 2 W_x,W_h,U,b_1,b_2 Wx,Wh,U,b1,b2 为待训练的参数,对应 θ \theta θ),但也在其拓展形式中也可作为各种奇奇怪怪的函数。在训练时,填入每一时刻的feature x ( t ) x^{(t)} x(t)和其label y ( t + 1 ) y^{(t+1)} y(t+1),则在 t t t 时刻有损失函数
J ( t ) ( θ ) = C E ( t ) ( y ( t + 1 ) , y ^ ( t + 1 ) ) J^{(t)}(\theta)=CE^{(t)}(y^{(t+1)},\hat{y}^{(t+1)}) J(t)(θ)=CE(t)(y(t+1),y^(t+1))
则总损失函数可以表示为所有时刻之损失函数之均值,即
J ( θ ) = 1 n ∑ t = 1 n J ( t ) ( θ ) J(\theta)=\frac{1}{n}\sum_{t=1}^nJ^{(t)}(\theta) J(θ)=n1t=1∑nJ(t)(θ)
有了损失函数后,和对应的feature和label,待优化参数,则可以使用各种优化手段进行优化。
此外,需要补充的是,对于文本的开头和结尾要分别设置两个对应的词向量作为标识符,标点也同样处理,将这些非文本信息都视为独立的词进行处理即可。
考虑Language Model问题,在 t t t时刻时,则有原文本序列 x ( 1 ) , x ( 2 ) , . . . , x ( n ) x^{(1)},x^{(2)},...,x^{(n)} x(1),x(2),...,x(n) 中的 x ( t ) x^{(t)} x(t)对应 t t t时刻的输入值,下一时刻的 x ( t + 1 ) x^{(t+1)} x(t+1) 对应输出值label y ( t + 1 ) y^{(t+1)} y(t+1),其他均与上述模型所述一致。事实上,RNN能做的远不止于Language Model,它适用于一系列序列问题,如果我们将其视为一个编码器,那么对于任意长度的输入值,都可以将其编码为固定长度的信息(即内含的隐藏参数 h h h)。如果需要对文本进行分析,则直接对编码信息进行处理即可,如分类器等等(一般会统筹考虑所有过程中产生的 h h h,综合考量,如取均值或者取最值);如果需要对文本进行转换,如翻译等,则再用另一个RNN训练一个解码器即可。事实上,RNN某种意义上和傅里叶变换很像,将复杂的序列信息转换为定长的一个结构化的信息,可以对其进行各种处理,然后再变回去。
但是RNN仍然有着一些缺陷: