跳转至

NLP 作者识别 — 文本如何一步步变成会猜作者的分类器

这一章讲什么: 两个人想说同一件事,从来不会用同一种说法—— 有人爱长句绕弯,有人短促直给;有人嗜用冷门词,有人只信熟词。 这些不自觉的习惯,是每个人留在文字里的指纹。这一章教计算机认这些指纹: 从一段文字出发,经过清洗、提特征、向量化、训练分类器四步, 让它对一段新文字猜出「这是谁写的」。 读完你会第一次看清:文本是怎么一步步变成数字的——这是所有语言 AI 的公共底座。

1. 这一章讲什么

这是全书第一个「智能」项目:前面做的都是「按规则执行的程序」, 这一章的产物是「从例子里学出规律的程序」——也就是机器学习(machine learning, 让程序从数据里找规律,而不是人把规则写死)。

项目本身小而完整:作者识别(authorship identification)。 它的学名叫 stylometry(风格计量学)——把写作风格当成可测量的事物2。 这个手艺并不新:历史学家用它鉴定有争议的信件,文学学者用它考证匿名作品, 网络安全用它识别假账号和代写内容3。新的部分是:用 Python 加一个 AI 助手, 初学者也能按研究者的路子做一遍——分词(把句子切成词)、量频率、比句法(词怎么搭成句子)、训模型4。

这一章在全书链条里的位置:第八章的数据报告、第九章的 Web 应用都会复用这里的 「管道思维」(数据进、步骤化加工、结果出);而对「编程语言与系统研究」, 这一章是最贴近「语言如何被计算」的一章。

2. 顶层全景:四段流水线

  原始文本(脏:标点、大小写、停用词、数字)
      │  ① 预处理:分词 → 去停用词 → 词干化/词形还原
      ▼
  干净的词列表(但机器还是不会读)
      │  ② 特征提取:词频、n-gram、句长 —— 把风格变成数
      ▼
  特征字典(每个作者一组习惯数字)
      │  ③ 向量化:TF-IDF 把文本变成稀疏数值矩阵
      ▼
  数值矩阵 X + 标签 y
      │  ④ 分类:LinearSVC 在矩阵上学出「每个作者的领地」
      ▼
  predict_author(新文本) → 猜一个作者

  图说:每一步都不可逆地丢掉一些信息、留下一些信号;流水线的手艺全在「丢什么、留什么」。

3. 核心原理

3.1 什么特征能暴露一个作者

动手之前先想:到底什么在人与人之间稳定地不同?书里列了五类可测量的习惯5:

特征 说明 例子
词汇选择 有人爱平实词,有人伸手就是罕见词 「delightful」vs「nice」
句长与节奏 短。促。有力。——或者从句套从句的长河 平均句字数
标点癖 有人宠逗号,有人拿破折号当签名 每千句的破折号数
惯用搭配 有人偏爱某些短语与句式 「in fact」「as a result」
词频分布 一个人在全文里用词的比例,惊人地稳定 功能词(of/and/the)的频率

这些可测量的元素就叫 features(特征)——模型用来比较作者的全部原料6。 书里还点出一个反直觉的事实:即使话题变了,一个作者的某些习惯仍然稳定—— 这正是作者识别能成立的根基(提问模板:「哪三种写作特质通常不随话题改变?」)7。

3.2 预处理:清洗的艺术在于「别洗太狠」

为什么不能拿原文就喂模型? 原文是脏的:标点、大小写、停用词(the/and/is, 这类极高频、几乎不携带内容信息的词)、数字、emoji、换行。把脏文本喂给模型, 书里有个传神的比喻:像一个读者被页面上每一滴墨渍分心8。 所以 NLP 工作流永远以预处理管道(preprocessing pipeline)开场。

但清洗有个反向的坑,书里用两句对照演示:

作者 A:"I really enjoyed the afternoon. Everything felt calm." 作者 B:"The afternoon was delightful; a rare moment of calm."

如果粗暴地「去停用词 + 转小写 + 去标点」,这两句会洗成几乎同一袋词 (enjoy/afternoon/calm 与 afternoon/delightful/calm 相去不远)—— 风格线索被洗掉了。好的预处理在「去噪声」与「保风格」之间找平衡9。

安装与第一句话。 三个库的安装命令书里给了:

pip install nltk
pip install spacy
python -m spacy download en_core_web_sm

分工:NLTK 管分词与停用词;spaCy 管词形还原与更高级的解析; en_core_web_sm 是 spaCy 的英文小模型(sm = small)10。

让 AI 生成第一段管道。 提示(可直接照抄):「用 NLTK 和 spaCy 生成一个简单的文本预处理管道: 小写化、去标点、去停用词、词形还原、返回干净的 token(切分后的最小词单位,§3.2 详解)列表;函数模块化可复用。」 生成的核心代码:

import nltk, spacy, string
from nltk.corpus import stopwords

nltk.download("punkt"); nltk.download("stopwords")
nlp = spacy.load("en_core_web_sm")
stop_words = set(stopwords.words("english"))

def preprocess_text(text):
    doc = nlp(text.lower())          # 先转小写,再整句交给 spaCy
    tokens = []
    for token in doc:
        if not token.is_stop and not token.is_punct and not token.is_space:
            tokens.append(token.lemma_)   # lemma_ = 词形还原后的词典形
    return tokens

sample = "The Afternoon was Delightful; a rare moment of calm."
print(preprocess_text(sample))
# → ['afternoon', 'delightful', 'rare', 'moment', 'calm']

输出确认四件事全发生了:标点没了、停用词(the/was/a/of)没了、词形还原了、大小写统一了11。 新词一次性讲掉:token(词元)是 NLP 里「文本切分后的最小单位」,本章一律指切出来的词; 词形还原(lemmatization)是把变形词还原成词典形:was → be、delightful → delightful; 与之对照的是词干化(§3.3)。

两个必踩的坑(书里预先标出)12:

  1. LookupError: Resource stopwords not found → 忘了 nltk.download("stopwords");
  2. OSError: [E050] Cannot find model en_core_web_sm → 忘了下载 spaCy 模型。

以及两条心法:风格模型对大小写敏感——除非你有意清洗;去停用词别过头—— 对风格识别,功能词本身就是强信号(上面 A/B 例句的命门),这是「别洗太狠」的技术理由。

3.3 三个基础件:分词、词干化、停用词

这一节把管道拆开,一件一件看。

分词(tokenization):把文本切成词。 最基础的积木。NLTK 版:

from nltk.tokenize import word_tokenize
tokens = word_tokenize("ChatGPT helps you write better code, faster.")
# → ['ChatGPT', 'helps', 'you', 'write', 'better', 'code', ',', 'faster', '.']

注意标点变成了独立的 token——正常,后面会滤掉13。spaCy 版输出几乎一样, 但它的分词器在边界情形上更稳(缩写、连字符、emoji)14。 向 AI 要对照演示的提示:「给我分别用 NLTK 和 spaCy 分词同一句子的代码, 含安装步骤和各自的预期输出。」15

词干化(stemming):机械砍词尾。 把 running/runs/ran 压成同一个粗略形式(常是 run)。 NLTK 的 PorterStemmer:

from nltk.stem import PorterStemmer
ps = PorterStemmer()
words = ["running", "runs", "ran", "easily", "fairly"]
print([ps.stem(w) for w in words])
# → ['run', 'run', 'ran', 'easili', 'fairli']

看输出最后两个:「easily」变「easili」——词干化是机械操作,不讲语言学, 它只按规则砍后缀,砍完不一定是个真词16。优点是快;代价是糙。

词形还原(lemmatization):查词典的还原。 spaCy 不做词干化,只做词形还原: 结合词性判断,给真词典形(running → run、was → be、quickly → quickly 不变)。 同一个句子的 spaCy 输出:['cat', 'run', 'quickly', 'garden']—— 比词干版干净、可读,更适合风格计量17。

组合管道。 让 AI 写「小写 → 分词 → 去停用词 → 词干化」一条龙: 对 "The cats were running quickly across the garden." 输出 ['cat', 'run', 'quickli', 'across', 'garden', '.']——糙但够用18。 一个有意思的对照:NLTK 链路留了个孤立的句号(分词时拆出来的标点没被滤净), 而 spaCy 版 ['cat', 'run', 'quickly', 'garden'] 更干净——两个库的「顺手程度」差异常在这一级显现。

3.4 特征提取:词频、n-gram、句长

清洗完的文本仍然是「词」,模型要的是「数」。特征提取(feature extraction) 就是把文本变成可比较的数值信号:频率、分布、倾向、习惯19。书里的话: 如果风格计量是侦探工作,特征就是留下的指纹20。

特征一:词频。 最简单的也最强大——作者有无意识的偏爱:某些动词、转折词、形容词, 手一滑就用。工具是标准库 collections.Counter:

from collections import Counter
tokens = ["cat", "run", "garden", "cat", "run", "run"]
freq = Counter(tokens)
# → Counter({'run': 3, 'cat': 2, 'garden': 1})

这个结构「很容易在之后转成机器学习的特征」21。

特征二:n-gram(连续 n 个词的片段)。 单个词告诉你作者谈什么; n-gram 告诉你他怎么把想法串起来22:

名称 粒度 例子
unigram 一个词 the、cat
bigram 连续两个词 in fact、at once
trigram 连续三个词 as a result

NLTK 的 ngrams(tokens, n) 直接切:

from nltk.util import ngrams
tokens = ["the", "cat", "sat", "on", "the", "mat"]
bigrams = list(ngrams(tokens, 2))
# → [('the','cat'), ('cat','sat'), ('sat','on'), ('on','the'), ('the','mat')]

注意 bigram 是滑动窗口:相邻两两一对,共 5 对。有人重度依赖某些固定搭配, bigram/trigram 正是抓这个的23。

特征三:句长。 节奏与流动性的强标记。先分句再计数:

from nltk.tokenize import sent_tokenize, word_tokenize
sentences = sent_tokenize(text)
lengths = [len(word_tokenize(s)) for s in sentences]
# 对 "This is the first sentence. Here is another one, slightly longer. And a third."
# → [6, 7, 3]

三个句子的词数 6、7、3——一组直接可当数值特征的数24。

合成一个 extract_features(text)。 提示:「写 extract_features(text):分词、去停用词、 spaCy 词形还原、提词频、提 bigram、算句长,全部装进一个字典返回,给示例输出。」 产物是本书流水线前半段的定型:

{
  'frequencies': Counter({'cat': 2, 'sit': 1, 'mat': 1, 'look': 1, 'outside': 1}),
  'bigrams': [('cat','sit'), ('sit','mat'), ('cat','look'), ('look','outside')],
  'sentence_lengths': [6, 5]
}

到这里,你的程序已经像真实的 NLP 管道了25。

3.5 向量化:TF-IDF 把文本压成矩阵

分类器只吃数字矩阵,不吃词。 所以我们要把每个文本样本变成一行数。 scikit-learn(本书的机器学习库,下称 sklearn)自带两个文本向量化器:

工具 干什么
CountVectorizer 数每个词出现几次
TfidfVectorizer 词的重要性加权(常见词权重降、有区分度的词权重升)

TF-IDF(term frequency–inverse document frequency,词频-逆文档频率) 的思想一句话:一个词在当前文本里越常见、在全体文本里越罕见,它越有区分度26。 对作者识别它通常效果好;书里建议之后再把它与自定义特征(句长、标点癖)混合27。

from sklearn.feature_extraction.text import TfidfVectorizer

vectorizer = TfidfVectorizer(ngram_range=(1, 2), min_df=2)
X = vectorizer.fit_transform(df["text"])     # df 是 3.6 的数据表
y = df["author"]
print("Feature matrix shape:", X.shape)
# → Feature matrix shape: (12, 1950)

输出 (12, 1950) 读法:12 个文档(文本样本),每个被表示成 1950 维的特征向量—— ngram_range=(1,2) 表示 unigram 和 bigram 都要;min_df=2 表示至少出现在 2 个文档里的 词才进词表(只出现一次的词多半是噪声,砍掉维度、防过拟合——模型只背下训练数据、对新数据不灵)28。 「模型现在有数字可学了」——这一步就是分水岭29。

3.6 数据集:让 AI 帮你搭装载器

模型要例子。最小可用数据集:两三个作者、每人几段。书里的目录约定:

data/
├── austen_1.txt
├── austen_2.txt
├── twain_1.txt
├── twain_2.txt
└── poe_1.txt

作者标签藏在文件名里(austen_1.txt → austen)——一个零成本的标注约定。 让 AI 写装载器:「读文件夹里所有 .txt,按文件名前缀的前缀当作者标签,存成 带 text 和 author 两列的 DataFrame。」产物:

import os, pandas as pd

def load_dataset(folder):
    records = []
    for fname in os.listdir(folder):
        if fname.endswith(".txt"):
            author = fname.split("_")[0]          # austen_1.txt → austen
            with open(os.path.join(folder, fname), "r", encoding="utf-8") as f:
                text = f.read()
            records.append({"author": author, "text": text})
    return pd.DataFrame(records)

df = load_dataset("data")

预览表长这样:author 列 austen/austen/twain/twain/poe,text 列各段开头30。 「这个结构就够开训了」——书里说得轻描淡写,但这句是本章的士气点。

3.7 训练与预测:LinearSVC 与 train-test split

选模型。 sklearn 里对文本好用的算法有三个:逻辑回归(Logistic Regression)、 线性 SVM(Linear SVM)、多项式朴素贝叶斯(Multinomial Naive Bayes)。 书里的选择:从 LinearSVC 开始——文本任务上出色,管道简单可靠31。

训练的标准姿势:train-test split(训练/测试集拆分)——把数据切成两份, 一份学、一份考,考的那份模型从没见过。 这是机器学习的头号纪律: 不拆就评,等于允许作弊32。

from sklearn.model_selection import train_test_split
from sklearn.svm import LinearSVC
from sklearn.metrics import accuracy_score

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.25, random_state=42)

model = LinearSVC()
model.fit(X_train, y_train)
preds = model.predict(X_test)
print("Accuracy:", accuracy_score(y_test, preds))
# → Accuracy: 0.75

test_size=0.25 是四分之一当考题;random_state=42 固定随机种子,让每次切分可复现 (42 是程序员圈的传统随机数,无深意)33。 准确率 0.75 怎么读:测试集里四分之三猜对了——书里的态度很健康: 具体数字随作者差异度和样本量浮动,重要的是你现在有一条可训可测的完整管道34。

对新文本预测,封装成函数:

def predict_author(text):
    vec = vectorizer.transform([text])     # 注意是 transform 不是 fit_transform
    return model.predict(vec)[0]

sample = "It was a dreary night when I first beheld the accomplishment of my toils."
print("Predicted author:", predict_author(sample))
# → Predicted author: poe

transform vs fit_transform 是本节的技术要点:训练时用 fit_transform (学词表 + 转换),预测时只能用 transform(用已学到的词表转换)—— 如果预测时重新 fit,词表就变了,模型看到的维度对不上35。

3.8 章末 Key Terms 与作业(原书收尾)

本章 Key Terms 六个,照收36:

术语 含义
NLP(Natural Language Processing) 帮计算机理解人类语言的技术
Vectorization(向量化) 把文本变成机器能学的数字
Feature Extraction(特征提取) 从文本里取出重要特征(如词频)
Training Set(训练集) 模型从中学习的数据
Classifier(分类器) 预测类别的模型(比如预测作者名)
Accuracy Score(准确率) 模型表现的度量

六个词正好排成流水线的顺序:提取特征 → 向量化 → 训练集 → 分类器 → 准确率。 背这张表不如把 §3.2–§3.7 的代码按这个顺序跑一遍——跑完六个词全是老朋友。

章末作业(Task to Complete)的完整规格37:自建一个小型作者数据集—— 至少两位作者、每人至少 6 段文本(可以自己写);然后完成 拆分训练/测试、向量化、训练分类器、测准确率、对至少两段新文本打印预测。 书里的提醒很坦白:真实世界的 NLP 需要更多数据,但这个小项目模拟了真实的行业工作流38。 换到中文场景,把 jieba 替换进 §3.3 的管道、词干化/词形还原两步直接删掉,其余原样可用 (补充,不在书里,来自通用知识)——流水线的骨架是语言无关的,换的只是一把切词的刀。

3.9 一个更真的对照:NLTK 链路与 spaCy 链路的输出差异

§3.3 末尾提了 NLTK 输出留了个孤立句号,这里把两条链路在同一句上的完整输出并排—— 这个对照是本章最直观的「工具性格」教材:

sample = "The cats were running quickly across the garden."
# NLTK 链路:小写 → word_tokenize → 去停用词/标点 → PorterStemmer
['cat', 'run', 'quickli', 'across', 'garden', '.']        # ← 句号留下了
# spaCy 链路:doc → 过滤 is_stop/is_punct → lemma_
['cat', 'run', 'quickly', 'garden']                        # 干净,无标点

三个差异一次看清:词干化砍出「quickli」(机械)对词形还原给出「quickly」(词典形); NLTK 手工拼装管道容易漏滤标点(句号是单独的 token,没被 string.punctuation 拦下), spaCy 的 is_punct 属性一刀切干净39。 书里没有安排这个并排对照,但两段代码都在书里——把它们摆在一起, 「风格任务偏爱 spaCy」这个结论就从指令变成了可验证的观察。

3.10 特征组合的策略:书里那句「pair it with your custom features」的落地

§3.5 引了书里的话:TF-IDF 之外,把自定义特征(句长、标点癖)pair 上40。 具体怎么落地?书里没有写代码,这一步的实现路径(补充,不在书里,来自通用知识)是:

  1. 用 sklearn 的 FeatureUnion 或手工拼矩阵:TF-IDF 给出稀疏矩阵(绝大部分格子是 0 的矩阵), 自定义特征(每篇的句长均值、标点频率)拼成密集小矩阵,左右合并;
  2. 量纲差异要处理:TF-IDF 值在 0–1 之间,句长是 5–50 的整数—— 拼之前用 StandardScaler 把自定义特征标准化,否则分类器会被大数值特征带偏;
  3. 收益预期管理:作者少、样本小时,TF-IDF 单用通常已够;自定义特征的价值 在「作者风格差异不在用词上」时显现(比如两人词汇相近但句法迥异)。

这条落地路径解释了为什么书停在「pair it with」——完整实现需要 sklearn 的 中级 API(FeatureUnion、ColumnTransformer),超出入门书的边界。知道路存在、 知道从哪起步,比此刻就跑通它更要紧;等你处理第二个 NLP 项目时自然会回来补这段。

3.11 章末的最后建议:跑完一遍之后的三个追问

作业跑通(accuracy 出了数字、新文本猜了作者)之后,别停在「完成」。书里散布的标准 可以收拢成三个追问,把一个练习变成一次研究:41

  1. 「换一个特征试试会怎样?」 把 ngram_range=(1,2) 换成 (1,1) 或 (1,3), 把 min_df=2 调成 1 或 3——每次只动一个参数,记录 accuracy 的变化。 这是「控制变量」的第一次实操;
  2. 「它错在哪篇上?」 打印 y_test != preds 的那些样本,读读它们—— 是样本太短?是两位作者那段恰好风格接近?错误案例比正确案例教得多;
  3. 「换两个更像的作者呢?」 简·奥斯汀对马克·吐温风格悬殊,准确率高不算本事; 换成两位同代同风格的作家(或同一个人的两种文体),accuracy 掉多少, 才是这个模型的真实成色。

三个追问做完,你就把「照书实现」升级成了「理解一个机器学习实验」—— 这正是本章对全书的意义:不是教你 NLP,是教你用最小的完整样本, 走一遍「数据 → 特征 → 模型 → 评估 → 复盘」的科研循环。

3.12 章节的暗线:三个「不要把清洗做绝」的回声

回看全章,「别洗太狠」以三种形式反复出现,书里没把它们连成线,我们补上:

  §3.2  两作者例句:粗暴去停用词会把风格信号洗掉(概念层)
  §3.3  共同 pitfalls:Removing Too Much——over-cleaning removes style cues(规则层)
  §3.2  预处理心法:好预处理平衡「去噪声」与「保风格」(方法层)

三层是同一条原则:清洗的强度是任务函数,不是越大越好。42 做情感分析(判断一段文字是夸还是骂)时停用词确实多为噪声;做作者识别时它们恰是信号—— 同一个预处理管道,换任务就要换强度。这也是为什么书里的 pipeline 把每步(去停用词/词干化)都写成独立函数:换任务时,只调参数与开关, 不拆管道。这是第八章「每站可换」在 NLP 上的预演。

3.13 特征与标签的对照总表(全章四段流水线收拢)

把全章的四段收进一张总表——每段做什么、产什么、交给谁:

段 做什么 产出 交给谁
预处理 小写/分词/去停用词/词形还原 干净的 token 列表 特征提取
特征提取 词频/n-gram/句长 特征字典 向量化
向量化 TF-IDF 加权 + 矩阵化 稀疏矩阵 X + 标签 y 分类器
分类 LinearSVC 学分界 训练好的模型 predict_author

四段各一个动词(洗、提、压、分),各一个产出,各一个下游—— 与第八章报告管道的五段同构。机器学习入门最稳的路,就是把流水线看成 「数据管道的一种」:没什么神秘,只是中间几站做的是统计而不是算术。

3.14 章节的暗线:文本变数的三个「为什么」

全章其实只回答了一个总问题——「文字怎么变成数」——但它在三个地方各给了一个 「为什么」,层次不同:

  • 为什么词频能认人?(§3.1)因为用词分布是作者不自觉的稳定习惯43;
  • 为什么必须清洗?(§3.2)因为噪声会盖过信号,而信号本身是微弱的风格差异44;
  • 为什么要训练/测试拆分?(§3.7)因为不拆的评估是作弊,分数没有含义45。

三个「为什么」由浅到深:第一个是统计学事实,第二个是信号处理直觉, 第三个是实验方法纪律。机器学习的入门,一半在调库,一半在这三个为什么上。 调库任何人五分钟能学会;三个为什么想透,才算跨过门槛。

3.15 给「研究者读者」的一个注脚

本书库的定位是「编程语言与系统研究」——对这类读者,本章有一个额外的读法: 作者识别是「风格计量学(stylometry)」的最小可行实验,而风格计量是计算语言学里 少数「工程门槛低、研究历史长」的课题:从 19 世纪 Mendenhall 的词频曲线, 到 1964 年 Mosteller–Wallace 对《联邦党人文集》的统计鉴定, 再到今天的深度学习(多层神经网络那一支机器学习方法)作者识别46。本书的流水线(预处理 → 词频/n-gram → 线性分类器) 正是这个谱系的入门配置。想往深做:把 LinearSVC 换成 logistic 回归看系数 (哪些词对区分贡献最大),或把特征换成标点/句法结构——工具全在本章的库里。

3.16 九条「Prompt to ChatGPT」:本章的真正教材

通读原书时容易漏掉一个事实:本章的正文几乎全部由「提示 + 期望输出」两段交替构成—— 从预处理到向量化,书一共给出了九段可直接照抄的提示(每段上面都有一个小标题「Prompt to ChatGPT」)。 换句话说,这章的教材不是散文,是提示库1。

把四段流水线的代表性提示各引一句,注意它们的共同骨架——每段都点名输入、库、 以及「Show the expected output(给期望输出)」:

流水线段 原书提示的要件(转述) 原文位置
预处理 用 NLTK + spaCy 生成管道;小写化、去标点、去停用词、词形还原 text/19-ch07-7-creating-an-authorship-identification-program.txt:218(搜「Prompt to ChatGPT」)
词频 用 collections.Counter 数词频,附一段短文本的示例输出 text/19-ch07-7-creating-an-authorship-identification-program.txt:802(搜「collections.Counter」)
句长 用 sent_tokenize 分句再算句长,给期望输出 text/19-ch07-7-creating-an-authorship-identification-program.txt:933(搜「sent_tokenize」)
向量化 TfidfVectorizer,unigram + bigram,报出结果的形状 text/19-ch07-7-creating-an-authorship-identification-program.txt:1281(搜「TfidfVectorizer」)

「给期望输出」这三个字是全书最被低估的提示技巧:它逼 AI 自证理解—— 如果示例输出不对,你还没跑代码就发现了错;它还顺手给了你一个测试样例, 第四章「先写失败测试」的种子在这里就埋下了。同一个技巧在第五章也出现了 (爬虫提示要求「print 第一页结果给我看」),只是第七章用得最系统。

4. 作者的判断与证据

判断(我们的,不是书里的): 本章是全书最像「科研入门」的一章—— 它把一个机器学习实验拆到初学者能亲手走通的最小粒度:六段文本 × 四位特征 × 一个线性模型。 这个粒度的选择很见功力:再小(比如只数词频)就没有「训模型」这一环, 体验不到训练/测试分离;再大(换深度模型)就掉进调参与算力的坑,看不见原理。 如果错,会错在: 这个最小粒度以英文文本为前提——中文的第一步 (分词就得换工具链)比书里呈现的陡,中文读者要预期多花一两个晚上在分词上; 另外,6 段样本的准确率不具任何外推价值,拿它当「我做了个能用的作者识别」会误事。

有依据的判断:

  • 「清洗与保风格要平衡」——用 A/B 例句演示,论证清晰9;
  • 「TF-IDF 适合作者识别,之后再混自定义特征」——机器学习文献支持的方向(补充,不在书里:词频类特征正是风格计量的经典做法,大多数量化作者识别研究以功能词频率为基线);
  • 「train-test split 后再报准确率」——标准纪律32。

经验之谈:

  • 「LinearSVC 起步」——稳妥选择,但朴素贝叶斯在小数据上常常一样好甚至更好; 「先 SVM」是偏好不是定律31;
  • min_df=2 的参数——经验值,样本多了可以调高。

推测:

  • 「词频分布在一个人写作中惊人稳定」——本章的事实基础,书里没有给文献; 这是风格计量学 19 世纪以来的经验定律(Mendenhall 特征),方向可信(补充,不在书里,来自通用知识)。

5. 边界与局限

  • 样本量极小:章末作业要求每作者仅 6 段——在这个规模上,0.75 的准确率更多是「管道通了」 而非「模型可信」;文本分类要稳,每类几十上百段才谈得上;
  • 类别泄漏的坑:同一段长文拆成多个 .txt 喂进去,训练与测试会共享同一篇的片段, 准确率虚高——书里没提 train_test_split 的 stratify 与按文档分组拆分的问题(我们的提醒);
  • 只字未提交叉验证与混淆矩阵:评估只有 accuracy 一个数,看不到「谁被错认成谁」 (混淆矩阵就是「实际是谁 × 猜成是谁」的对照表);
  • 英文中心:punkt、en_core_web_sm 全是英文资源;中文要走 jieba/pkuseg 等另一套,书里未涉及;
  • 过拟合未命名:min_df 的作用是防过拟合,但书里没给这个词与判断方法(我们的补充: 训练准确率远高于测试准确率,就是过拟合的典型症状)。

6. 可带走的

本章主线例子,一行写完: 每人几段文本 → 预处理管道(小写→分词→去停用词→词形还原) → extract_features(词频 + bigram + 句长) → TfidfVectorizer(ngram 1-2, min_df=2) → train_test_split → LinearSVC 训练 → accuracy 0.75 → predict_author 新文本猜作者。

  1. 风格可测量:词汇、句长、标点、搭配、词频分布——五类指纹;
  2. 清洗别过头:功能词本身就是风格信号,粗暴清洗会把两个作者洗成一个人;
  3. 词干化机械、词形还原查词典;风格任务偏爱后者;
  4. n-gram 看「怎么串」:unigram 谈话题,bigram/trigram 看搭配习惯;
  5. TF-IDF:越特有越加权;输出 (文档数, 特征数) 的稀疏矩阵(绝大部分位置是 0 的矩阵,只存非 0 值);
  6. train-test split 是头号纪律:学过的考不算数;
  7. 预测用 transform 不用 fit_transform——词表只能学一次;
  8. 准确率随样本量浮动;小数据上的数字报喜不报忧;
  9. 完整管道 > 花哨模型:本章的每个库只做一件事,串起来就是一个行业工作流的微缩版;
  10. 章末作业照做一遍(每作者 6 段、拆分、向量化、训练、测准、预测两条新文本)—— 亲手跑通比读十遍都牢36。

7. 原文地图

主题 原书章 原文位置
风格计量学定义与应用 Creating an Authorship Identification Program text/19-ch07-7-creating-an-authorship-identification-program.txt:74(搜「measure」) · text/19-ch07-7-creating-an-authorship-identification-program.txt:71(搜「disputed letters」)
五类风格特征 同上 text/19-ch07-7-creating-an-authorship-identification-program.txt:128(搜「Vocabulary Choices」)
脏文本的读者比喻 同上 text/19-ch07-7-creating-an-authorship-identification-program.txt:161(搜「smudge」)
别洗太狠的 A/B 例句 同上 text/19-ch07-7-creating-an-authorship-identification-program.txt:176(搜「really enjoyed」)
安装三库与分工 同上 text/19-ch07-7-creating-an-authorship-identification-program.txt:200(搜「pip install nltk」)
预处理管道代码 同上 text/19-ch07-7-creating-an-authorship-identification-program.txt:284(搜「preprocess_text」)
两个必踩的坑 同上 text/19-ch07-7-creating-an-authorship-identification-program.txt:358(搜「LookupError」)
分词两库对照 同上 text/19-ch07-7-creating-an-authorship-identification-program.txt:449(搜「word_tokenize」)
词干化输出 同上 text/19-ch07-7-creating-an-authorship-identification-program.txt:559(搜「PorterStemmer」)
词形还原对照 同上 text/19-ch07-7-creating-an-authorship-identification-program.txt:745(搜「does not stem」)
Counter 词频 同上 text/19-ch07-7-creating-an-authorship-identification-program.txt:817(搜「Counter」)
n-gram 三级 同上 text/19-ch07-7-creating-an-authorship-identification-program.txt:848(搜「Unigrams」)
句长提取 同上 text/19-ch07-7-creating-an-authorship-identification-program.txt:933(搜「splits text into sentences」)
extract_features 合成 同上 text/19-ch07-7-creating-an-authorship-identification-program.txt:1051(搜「extract_features」)
数据集装载器 同上 text/19-ch07-7-creating-an-authorship-identification-program.txt:1198(搜「load_dataset」)
TF-IDF 与 (12, 1950) 同上 text/19-ch07-7-creating-an-authorship-identification-program.txt:1302(搜「TfidfVectorizer」)
三算法与 LinearSVC 同上 text/19-ch07-7-creating-an-authorship-identification-program.txt:1341(搜「Logistic Regression」)
训练与 0.75 同上 text/19-ch07-7-creating-an-authorship-identification-program.txt:1366(搜「train_test_split」)
predict_author 同上 text/19-ch07-7-creating-an-authorship-identification-program.txt:1426(搜「predict_author」)
章末作业六段 同上 text/19-ch07-7-creating-an-authorship-identification-program.txt:1512(搜「6 text samples」)
Key Terms 六词 同上 text/19-ch07-7-creating-an-authorship-identification-program.txt:1483(搜「Natural Language Processing」)
小项目模拟行业工作流 同上 text/19-ch07-7-creating-an-authorship-identification-program.txt:1475(搜「real industry workflow」)

  1. 原书本章的九段提示小标题均作「Prompt to ChatGPT」,分别位于 text/19-ch07-7-creating-an-authorship-identification-program.txt:218、:416、:538、:640、:799、:864、:930、:1002、:1168、:1278。 ↩

  2. 出处:「Creating an Authorship Identification Program」第 74 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:74,搜「measure」)。原文:「Stylometry is simply the practice of treating writing style as something we can measure.」 ↩

  3. 出处:「Creating an Authorship Identification Program」第 71 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:71,搜「disputed letters」)。 ↩

  4. 出处:「Creating an Authorship Identification Program」第 77 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:77,搜「no longer limited」)。 ↩

  5. 出处:「Creating an Authorship Identification Program」第 17–21 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:128,搜「Vocabulary Choices」)。 ↩

  6. 出处:「Creating an Authorship Identification Program」第 152 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:152,搜「features」)。 ↩

  7. 出处:「Creating an Authorship Identification Program」第 118 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:118,搜「stable」)。 ↩

  8. 出处:「Creating an Authorship Identification Program」第 161 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:161,搜「smudge」)。原文:「If you feed unprocessed text into an NLP model, it behaves like a reader distracted by every smudge on the page.」 ↩

  9. 出处:「Creating an Authorship Identification Program」第 176 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:176,搜「really enjoyed」)。 ↩↩

  10. 出处:「Creating an Authorship Identification Program」第 200 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:200,搜「pip install nltk」)。 ↩

  11. 出处:「Creating an Authorship Identification Program」第 284 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:284,搜「preprocess_text」)与第 324 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:324,搜「afternoon」)。 ↩

  12. 出处:「Creating an Authorship Identification Program」第 36–37 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:358,搜「LookupError」)。 ↩

  13. 出处:「Creating an Authorship Identification Program」第 449 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:449,搜「word_tokenize」)。 ↩

  14. 出处:「Creating an Authorship Identification Program」第 509 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:509,搜「edge cases」)。 ↩

  15. 出处:「Creating an Authorship Identification Program」第 419 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:419,搜「tokenizes a sentence」)。 ↩

  16. 出处:「Creating an Authorship Identification Program」第 577 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:577,搜「easili」)。原文:「This is a reminder that stemming is mechanical, not linguistic.」 ↩

  17. 出处:「Creating an Authorship Identification Program」第 745 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:745,搜「does not stem」)与第 772 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:772,搜「better suited for stylometry」)。 ↩

  18. 出处:「Creating an Authorship Identification Program」第 697 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:697,搜「clean_text」)。 ↩

  19. 出处:「Creating an Authorship Identification Program」第 781 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:781,搜「numerical signals」)。 ↩

  20. 出处:「Creating an Authorship Identification Program」第 781 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:781,搜「fingerprints」)。 ↩

  21. 出处:「Creating an Authorship Identification Program」第 817 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:817,搜「Counter」)。 ↩

  22. 出处:「Creating an Authorship Identification Program」第 844 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:844,搜「string ideas together」)。 ↩

  23. 出处:「Creating an Authorship Identification Program」第 879 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:879,搜「ngrams」)。 ↩

  24. 出处:「Creating an Authorship Identification Program」第 933 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:933,搜「splits text into sentences」)。 ↩

  25. 出处:「Creating an Authorship Identification Program」第 996 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:996,搜「resemble real-world NLP pipelines」)。 ↩

  26. 出处:「Creating an Authorship Identification Program」第 1262 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:1262,搜「CountVectorizer」)。TF-IDF 的「越稀有越加权」是通用知识的标准表述。 ↩

  27. 出处:「Creating an Authorship Identification Program」第 1272 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:1272,搜「Pair it with」)。 ↩

  28. 出处:「Creating an Authorship Identification Program」第 1302 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:1302,搜「TfidfVectorizer」)。 ↩

  29. 出处:「Creating an Authorship Identification Program」第 1331 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:1331,搜「numerical to learn from」)。 ↩

  30. 出处:「Creating an Authorship Identification Program」第 84–86 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:1198,搜「load_dataset」)。 ↩

  31. 出处:「Creating an Authorship Identification Program」第 1341 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:1341,搜「Logistic Regression」)。 ↩↩

  32. 出处:「Creating an Authorship Identification Program」第 1366 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:1366,搜「train_test_split」)。「测试集必须陌生」这条纪律在书中的直接依据是 accuracy 数字的用途——衡量对新文本的表现(第 94 段,text/19-ch07-7-creating-an-authorship-identification-program.txt:1402,搜「vary」)。 ↩↩

  33. 出处:「Creating an Authorship Identification Program」第 1378 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:1378,搜「random_state=42」)。42 的典故来自《银河系漫游指南》(补充,不在书里,来自通用知识)。 ↩

  34. 出处:「Creating an Authorship Identification Program」第 1402 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:1402,搜「vary」)与第 1405 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:1405,搜「trainable and testable」)。 ↩

  35. 出处:「Creating an Authorship Identification Program」第 1426 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:1426,搜「predict_author」)。「预测只能 transform」的依据是向量化器已在训练期 fit(第 92 段);transform-vs-fit_transform 的区别是 sklearn 的通用知识。 ↩

  36. 出处:「Creating an Authorship Identification Program」Task to Complete(text/19-ch07-7-creating-an-authorship-identification-program.txt:1512,搜「6 text samples」)。 ↩↩

  37. 出处:「Creating an Authorship Identification Program」Task to Complete(text/19-ch07-7-creating-an-authorship-identification-program.txt:1505,搜「at least two authors」)。 ↩

  38. 出处:「Creating an Authorship Identification Program」Points to Remember(text/19-ch07-7-creating-an-authorship-identification-program.txt:1475,搜「real industry workflow」)。 ↩

  39. 出处:「Creating an Authorship Identification Program」第 697 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:697,搜「clean_text」)与第 760 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:760,搜「lemmas」)。 ↩

  40. 出处:「Creating an Authorship Identification Program」第 1272 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:1272,搜「Pair it with」)。 ↩

  41. (我们的练习建议;所依据的模型与指标口径见本章 §3.5–§3.7 的原文出处。) ↩

  42. 出处:「Creating an Authorship Identification Program」第 384 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:384,搜「Removing Too Much」)与第 185 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:185,搜「preserving meaningful patterns」)。 ↩

  43. 出处:「Creating an Authorship Identification Program」第 793 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:793,搜「unconscious habits」)。 ↩

  44. 出处:「Creating an Authorship Identification Program」第 161 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:161,搜「smudge」)。 ↩

  45. 出处:「Creating an Authorship Identification Program」第 1402 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:1402,搜「vary」)。 ↩

  46. 出处:「Creating an Authorship Identification Program」第 71 段(text/19-ch07-7-creating-an-authorship-identification-program.txt:71,搜「disputed letters」);Mendenhall 与 Mosteller–Wallace 为背景补充(不在书里,来自通用知识)。 ↩