概率模型——前传

在词义消歧中,我们根据已有的数据 $(x,y)$,希望在句子 $x$ 和词义 $y$ 间构建映射 $y=g(x)$,该映射的目标是希望 $p(y\vert x)$ 尽可能大。而朴素贝叶斯模型先利用 $p(x\vert y)$ 求出了整体 $p(x,y)$,再求出 $p(y\vert x)$,这种模型属于生成模型(generative models)。反之,如果直接求出了 $p(y\vert x)$,则叫做判别模型(discriminative models)。

生成模型的目标虽然也是求 $p(y\vert x)$,但它先通过 $p(x\vert y)$ 以及贝叶斯公式,得到 $p(x,y)=p(y)p(x\vert y)$。随后结合全概率公式,有:

\[p(y|x)=\frac{p(x,y)}{p(x)}=\frac{p(y)p(x|y)}{\sum\limits_{y'}p(y')p(x|y')}\]

我们只想获得使这个值最大的 $y$,而分母这个值是固定的,因此只需要求 $y=g(x)=\arg\max\limits_y p(y)p(x\vert y)=\arg\max\limits_y p(x,y)$ 就好了。

而判别模型直接尝试求 $p(y\vert x)$,通常更加自然,更能获取特征,准确率也更高,但很容易导致过拟合。这方面有非常多的模型,下面我们从最简单的模型开始。

三、线性模型

想象一下我们做一篇阅读理解,碰到一个不认识的词,我们抓着前后文的单词就开始猜。我们综合考虑所有词的搭配,给出一个猜想,对不对就不知道了,为了准确率高点,需要大量的训练(刷题)。我们称前后文中重要的线索叫特征

1. 特征表示与词袋模型

机器学习需要将数据 $x$ 提取出一定的特征,才能推理出结果 $y$。传统的特征是手动提取,后来通常使用 Embedding(嵌入)方法,把句子转换成向量。向量的每个元素是一个特征,比如“这个词的前面是 transfer 时,它的意思一定是 FINANCIAL”。可以看到一个特征是关于句子与标签的函数,多个特征组成了特征向量 $[f_1(x,y),\cdots,f_n(x,y)]$。实际上每个特征表示的都是 $p(y\vert x)$,值越大概率越大。特征向量可以是手动标定的特征值,也可以用出现次数或是 tf-idf 值,反正是特征就行,具体效果看具体场景。

补充一下 tf-idf 值,对于词 $t$ 和文档 $d$,$tf=1+\log_{10}count(t,d)$,其中 $count$ 是词在文档中的出现次数;$idf=\log_{10}(N/DF_t)$,其中 $N$ 是总文档数,$DF_t$ 是出现过词 $t$ 的文档数,tf-idf 值就是上述两个值相乘。

给特征向量举个例子吧,比如我们有一句话:

I cash a check in that bank and transfer 100 dollars to my mom.

我们想判断其中 bank 是不是银行的意思,因此提取出这个句子的一些特征:前一个词是 that,再前一个是 in;后面第二个词是 transfer;不是句子的开头;没有被大写。我们先简单一点,取 $f_i(x,y)$ 的值都是 0 或 1,比如我们认为前一个词是 that,可以认为 bank 是银行的意思,就记为 1;这样把所有的特征都变成数字,并且放在一个向量里,这个句子就变成了一个向量 $[1,1,1,1,0,0,\cdots,1,0,0]$,我们记为 $f(x,y)$,表示句子 $x$ 中 bank 的意思是 $y$ 的特征。像这样只有 0 和 1 的向量叫 One-hot 向量(独热向量)。

还有更弱智的方法是词袋模型,就是把整个文本中的所有单词的次数统计成一个向量,这种方法除了快以外也没啥优势了,因此只能用在文本分类、垃圾邮件过滤等简单的任务上。

向量中那些非常小的值,其实是没有信息的,而且其实向量中有大量这种无意义的值,因此预处理是很必要的。有两个常用的预处理方法,一种是建立停用词列表(stop-word list),处理的是如 the, a, and 这些没啥卵用的词,碰到它们就直接忽略;另一种是词干提取(stemming),这个好理解,不同形态的同义词,比如 playing, played 都变回原形。

2. 线性模型

处理完数据后就可以构建模型了,线性模型的本质是对所有特征加权求和,我们训练的就是每个特征的权重。用数学表示一下,假如特征是 $f(x,y)$,权重是 $\lambda_{f(x,y)}$,则对任意数据对,如果确定了权重,就可以算出分数:

\[score(x,y)=\sum_i \lambda_{f_i(x,y)}f_i(x,y)\]

一个标签的分数在所有标签中的占比就是 $p(y\vert x)$:

\[\begin{align*} p(y|x)&=\frac{score(x,y)}{\sum_{y'}score(x,y')}\\ &\Rightarrow\frac{\exp(\lambda\cdot f(x,y))}{\sum_{y'}\exp(\lambda\cdot f(x,y'))}\\ \log p(y|x;\lambda)&=\boxed{\lambda\cdot f(x,y)-\log\sum_{y'}\exp(\lambda\cdot f(x,y'))}\\ \end{align*}\]

注意我们取分数的指数形式,以保证每个的分数都是正数。此时我们就能用一个线性模型来衡量在句子 $x$ 与权重 $\lambda$ 的条件下,取语义 $y$ 的概率:先用线性模型对特征加权 $\lambda f(x,y)$,再减去归一化的部分。

接着我们考虑如何训练模型,以找到最佳的 $\lambda$。简单的想法是设计 $L(\lambda)=\prod_k p(y_k\vert x_k;\lambda)$,也就是让所有训练集的数据的概率最大。取对数后就变成了求和:

\[LL(\lambda)=\sum_k \lambda\cdot f(x_k,y_k)-\sum_k\log\sum_{y'}\exp(\lambda\cdot f(x_k,y'))\]

这个值越大,说明模型在这个训练集上的效果越好。那假如算出了一个 $LL$ 值,为了更新参数,就需要梯度下降,也就需要求:

\[\begin{align*} \frac{\partial LL(\lambda)}{\partial\lambda_{f_i}}&=\sum_k f_i(x_k,y_k)-\sum_k \frac{\sum_{y'}f_i(x_k,y')\exp(\lambda\cdot f(x_k,y'))}{\sum_{z'}\exp(\lambda\cdot f(x_k,z'))}\\ &=\sum_k f_i(x_k,y_k)-\sum_k \sum_{y'}f_i(x_k,y')\frac{\exp(\lambda\cdot f(x_k,y'))}{\sum_{z'}\exp(\lambda\cdot f(x_k,z'))}\\ &=\sum_k f_i(x_k,y_k)-\sum_k \sum_{y'}f_i(x_k,y')p(y'|x_k;\lambda)\\ \end{align*}\]

我们的目的是让梯度为 0,因此目的就是让上面这个减法的前后两项相等,我们把前面这一项 $\sum_k f_i(x_k,y_k)$ 称作 Empirical Counts(经验计数),后面这一项称作 Expected Counts(期望计数)。怎么理解这两个定义呢,前者其实记录的是 $f_i$ 被激活的次数,也就是说到底有多少个数据 $(x_k,y_k)$ 能使得 $f_i(x_k,y_k)$ 被激活成 1(这里是拿 One-hot 向量举例子,即使不是 1 你也可以看成激活的程度)。后者拿 $f_i$ 乘以 $p$,其实看的是期望值,考虑的是所有候选标签。不理解就背下来吧。

同时为了最大化 $LL(\lambda)$,因此需要沿着梯度的方向上升。具体有两种做法:

  • 完整批量梯度上升:初始化所有 $\lambda_i=0$,对每轮迭代,都先按照上述公式算出梯度,然后找到沿着这个方向走,取到最大 $LL$ 时 $\lambda$ 的值。用数学公式来说,就是找到 $\beta^=\arg\max\limits_\beta LL(\lambda+\beta\Delta)$,然后取 $\lambda$ 为 $\lambda+\beta^\Delta$。
  • 随机梯度上升:由于上述方法每轮迭代都得遍历整套数据集,因此我们每次只用一个样本计算。具体来说,步骤是这样的:
  1. 初始化 $\lambda$;
  2. 每轮都打乱数据集;
  3. 对每个样本 $(x_k,y_k)$,计算梯度:$$\frac{\partial LL_k(\lambda)}{\partial\lambda_i}=f_i(x_k,y_k)-\sum_{y’}f_i(x_k,y’)p(y’ x_k;\lambda)$$ 并基于这个梯度上升固定值 $\alpha$,也即 $\lambda_i\leftarrow\lambda_i+\alpha\cdot\frac{\partial LL_k(\lambda)}{\partial\lambda_i}$;
  4. 多轮迭代即可。

我们也可以把最大化似然 $\arg\max LL(\lambda)$ 改为最小化损失 $\arg\min-LL(\lambda)$,此时目标就变成了:

\[\lambda^*=\arg\min_\lambda\sum_k-\log p(y|k,x|k;\lambda)\]

这也就是常说的交叉熵损失(cross entropy loss)。

最后我们简单介绍一下正则化。正则化处理的问题是:防止某个参数的绝对值过大。举个例子,如果某个特征在训练集反复出现,比如训练集中每次出现 bank 这个词,它的前一个词永远是 in,那么最大似然可能会让这个特征的权重达到非常大。因此我们需要防止权重的长度 $|\lambda|$ 过大。比较常用的是 L2 正则化,也就是控制 $|\lambda|^2=\sum\limits_i\lambda_i^2$ 的大小。具体而言,我们修改似然值:

\[LL(\lambda)=\sum_k \lambda\cdot f(x_k,y_k)-\sum_k\log\sum_{y'}\exp(\lambda\cdot f(x_k,y'))-\frac{\alpha}{2}\|\lambda\|^2\]

就是对似然值减去权重的总长度,如果权重太长了,就狠狠惩罚它。似然值改了,梯度也得改:

\[\frac{\partial LL(\lambda)}{\partial\lambda_{f_i}}=\sum_k f_i(x_k,y_k)-\sum_k \sum_{y'}f_i(x_k,y')p(y'|x_k;\lambda)-\alpha\lambda_i\]

3. 专门用一小节总结线性模型的计算方法

核心的思路就是:不断调整 $\lambda$,让经验计数接近期望计数。具体的运算方法是:

  1. 设计特征函数 $f_i(x,y)$
  2. 给特征函数加权重,得到线性的分数 $\lambda\cdot f(x,y)$
  3. 用 softmax 转成概率:$$p(y x)=\frac{\exp(\lambda\cdot f(x,y))}{\sum\limits_{y’}\exp(\lambda\cdot f(x,y))}$$
  4. 求对数得到似然值:\(LL(\lambda)=\sum_k\lambda\cdot f(x_k,y_k)-\sum_k\log\sum_{y'}\exp(\lambda\cdot f(x_k,y'))\) 这个值可以用来输出,以供参考模型的好坏。
  5. 求梯度并加上正则化:$$\frac{\partial LL(\lambda)}{\partial\lambda_{f_i}}=\sum_k f_i(x_k,y_k)-\sum_k \sum_{y’}f_i(x_k,y’)p(y’ x_k;\lambda)-\alpha\lambda_i$$
  6. 梯度上升:\(\lambda_i\leftarrow\lambda_i+\alpha\frac{\partial LL(\lambda)}{\partial\lambda_i}\)

4. 零零碎碎的神经网络训练知识点

训练集是用于训练的(?),可以用来调整模型参数。验证集在训练集之后使用,可以用来选择超参数。测试集是最终使用的,绝不能放进训练集(?)。

超参数是一些预定义的参数,比如学习率之类的。

K 折交叉验证是把训练数据分为 $K$ 份,每次用 $K-1$ 份训练,一份验证,最后报告平均性能。

Leave a comment