🧠 第六章 自然语言处理
📄 一句话概括

20 句天气语料建的二元模型:真句每字平均对数概率 -3.581,打乱字序的假句 -4.270, 按中点阈值判真假 60 / 60 全对。生成侧随机续写的困惑度 65.9,真句只有 35.9。

📝 本节和前面几节的关系

上一节比了两条技术路线,这一节比两类任务。理解和生成看着是一体两面,其实难点完全不同。

一、理解是收,生成是放

理解类任务:给一段文本,输出一个判断。情感分类、意图识别、真假判别、信息抽取,都属于这一类。 输出空间小,好评估。 生成类任务:给一段上下文,输出下一段文本。翻译、摘要、对话、续写,都属于这一类。 输出空间是整个词表的所有组合。 难度差别在这里:理解只要「判对」,生成要「每一步都判对」,而且一步错,后面全按错的前提往下走。 Demo 用同一个二元模型同时干这两件事,正好能看出差别。

二、判别这一半:真句和乱码分得开吗

做法很朴素:用 20 句真语料统计相邻两字的概率,然后给每句话算一个「每字平均对数概率」。 真句平均 -3.581,把字打乱重排得到的假句平均 -4.270。差 0.689。 取两者中点 -3.926 当阈值,60 条(20 真 + 40 假)全判对。 注意这个 100% 有多容易:假句是把字完全打乱的,相邻关系全毁。 真实的语病句、机器翻译腔,字序大体正常,这个模型就分不出来了。

三、生成这一半:往外续会续出什么

从「今」「明」「下」三个字起步,每次按概率抽下一个字,续 10 个,不许原地打转。 抽出来的三条是:今末天到近风多外温晚晴、明弱厚夜空零三白很午明、下小最量下周零傍今面级。 读一眼就知道:开头两个字像话,往后就散了。因为模型只看前一个字,它不知道整句要说什么。 困惑度也说明了这一点:生成句 65.9,真句 35.9。困惑度越低越像人话,生成的高了近一倍。

四、两类任务的评估方式也不一样

理解类用准确率、精确率、召回率、F1。标准答案唯一,对错清楚。 生成类没法这么算。同一个意思有十种说法,逐字比对会把对的判成错的。 所以用 BLEU、ROUGE 这类「重叠度」,或者干脆用人评。 BLEU 的问题是只数重叠,不看意思。人评的问题是贵、慢、不一致。这是生成任务至今没解决好的评估难题。 现在常见做法是「自动指标筛一遍,人评定稿」。自动指标负责把明显差的挑出去,人负责在剩下的里面排序。

五、动手 Demo:一个二元模型,干判别也干生成

用 20 句天气短句建字符二元模型(加一平滑); 给每句算每字平均对数概率,再拿 40 条打乱字序的假句作对照,取中点当阈值算判别准确率; 然后从三个字起步各续写 10 个字,最后比较真句和生成句的困惑度。

def lp(a, b):                       # log P(b|a)
    return math.log((bi.get(a + b, 0) + 1.0) / (ctx.get(a, 0) + V))

score = sum(lp(t[i], t[i + 1]) for i in range(len(t) - 1)) / (len(t) - 1)

w = np.array([math.exp(lp(cur, c)) for c in cands])
nxt = rr.choice(cands, p=w / w.sum())    # 按概率抽,不取最大

实跑输出:

--------------------------------------------------------------------
理解与生成:判别式判真假,生成式往后续
--------------------------------------------------------------------
  真句平均对数概率         -3.581(每字)
  打乱句平均               -4.270(每字)
  阈值                     -3.926
  判别准确率               60 / 60 = 1.000

  生成侧:从「今」开始,按概率抽下一个字(不许原地打转),续 10 个字。
  生成 1                   今末天到近风多外温晚晴
  生成 2                   明弱厚夜空零三白很午明
  生成 3                   下小最量下周零傍今面级

  生成句平均对数概率       -4.188
  真句对应困惑度           35.9
  生成句对应困惑度         65.9
  困惑度越低越像人话。生成句比真句高一截,因为每续一个字都可能走岔,一旦走岔,后面的条件概率全按错的上文算。
  读一眼生成结果:开头两个字像词,往后就散了。这就是二元模型的上限——它只看前一个字,攒不出整句的意思。

判别那一栏 60 / 60 看着漂亮,但要打个折扣:假句是彻底打乱的,相邻关系全毁,分它们太容易。 三个生成结果里,第一个「今末天到近风多外温晚晴」还算能看出是天气词堆的,后两个就完全散了。 困惑度 35.9 对 65.9,差得不算悬殊,但方向对:生成句确实更不像话。 生成用「按概率抽」而不是「取最大」,是因为取最大会陷入循环——第一版就是这样, 续出来一串「今今今今」。这件事值得单独记一笔。

30 秒实验

  1. 把语料从 20 句扩到 40 句,看判别准确率和生成困惑度各怎么变。
  2. 把生成改成「取概率最大的字」,看会不会原地打转。
  3. 想一想:为什么生成任务的评估比理解任务难?

常见误区

  1. 「判别 100% 说明模型好」——假句太假,任务太容易。
  2. 「生成就是判别反过来」——生成要保证每一步都对,误差会累积。
  3. 「困惑度低就一定通顺」——它只衡量「像不像语料」,不衡量「对不对」。
自测三问(点开对答案) 1. 理解和生成两类任务的核心差别是什么? —— 理解输出一个小判断,生成输出一整个序列,前一步错会带错后面所有步。 2. 为什么生成时不能每次取概率最大的字? —— 会陷入循环,反复吐同一个字。 3. 为什么生成任务不好用准确率评估? —— 同一意思有十种说法,逐字比对会把对的判成错的。

两大类任务认清了。下一节回到最脏最累的一步:语料怎么洗。

相关笔记