专项练习 · 11 分钟阅读

Python 朴素贝叶斯练习:计数、先验与平滑

朴素贝叶斯大部分工作就是细致地计数。这些练习构造计数表、转成平滑概率、在对数空间比较类别,并在未参与训练的文本上检查结果。

练习:为每个类别建立计数表

标签不是特征。先按标签拆分训练文档,再在每个组内统计词元。所有文档必须使用相同的分词规则。

docs = [("good movie", "pos"), ("great film", "pos"), ("bad movie", "neg"), ("boring film", "neg")]
counts = {}
for text, label in docs:
    counts.setdefault(label, {})
    for word in text.split():
        counts[label][word] = counts[label].get(word, 0) + 1
print(counts["pos"])
print(counts["neg"])

按标签分组的证据

练习:计算类别先验

先验是某个类别在文档中的占比。当类别极不平衡时,它尤其重要。打印两个先验,并检查它们之和为一。

labels = ["pos", "pos", "neg", "neg"]
for label in ("pos", "neg"):
    print(label, labels.count(label) / len(labels))

每个类别有多常见?

练习:平滑一个没见过的词

不做平滑时,某类别从未出现的词会让整个乘积变成零。给每个计数加一,并在分母加上词表大小。

counts = {"great": 0}
total = 4
vocab_size = 6
probability = (counts.get("great", 0) + 1) / (total + vocab_size)
print(round(probability, 2))

是很小的概率,不是零

练习:比较对数分数

类别分数等于对数先验加上每个词的对数似然。比较两个候选分数,不需要连乘很小的数。分数更大者获胜,同时要规定确定性的平局规则。

import math

pos = math.log(0.5) + math.log(0.2) + math.log(0.2)
neg = math.log(0.5) + math.log(0.1) + math.log(0.1)
print(round(pos, 2), round(neg, 2))
print("pos" if pos > neg else "neg")

先加对数,再比较

练习:预测留出文本

用训练文档建立模型,再给新文档分类。两个例子上的准确率只是快速检查,不能证明生产质量。

import math

train = [("good movie", "pos"), ("great film", "pos"), ("bad movie", "neg"), ("boring film", "neg")]
tests = [("great movie", "pos"), ("boring movie", "neg")]
vocab = sorted({word for text, _ in train for word in text.split()})
counts = {}
priors = {}
for text, label in train:
    priors[label] = priors.get(label, 0) + 1
    counts.setdefault(label, {})
    for word in text.split():
        counts[label][word] = counts[label].get(word, 0) + 1

def predict(text):
    scores = {}
    for label in counts:
        total = sum(counts[label].values())
        score = math.log(priors[label] / len(train))
        for word in text.split():
            score += math.log((counts[label].get(word, 0) + 1) / (total + len(vocab)))
        scores[label] = score
    return max(scores, key=scores.get)

correct = sum(predict(text) == label for text, label in tests)
print(correct / len(tests))

一次快速检查,不是基准测试

你的练习清单

常见问题

朴素贝叶斯需要神经网络吗?

不需要。它是基于计数的概率分类器,也是许多文本任务的强基线。

词不在词表里怎么办?

常见做法是忽略它,或映射到训练时已经存在的未知词元特征。要在评估前定好。

为什么对数分数都是负数?

概率最大为一,所以对数最大为零。比较相对大小即可,不必纠结符号。

练习计数与计分

12 道题,覆盖计数、先验、平滑、对数分数与留出预测。

打开练习

继续阅读: