20 行原始语料:去标签后还是 20 行,去空行剩 16,归一化去重剩 13, 去掉含 URL 的行剩 11; 切出 12 句、164 个单字,去停用字后 155,最后词表 98 项。
前面四节都在讲「怎么表示、怎么算」,这一节讲最不体面但最影响结果的一步: 把脏语料洗成能用的样子。
一、六个步骤,Demo 拆成九行看
书里说的六步是:清洗、归一化、分句、分词、去停用词、建词表。 Demo 把「清洗」拆成了三行(去标签、去空行、去 URL),所以表里有九行,从 0 到 8。 顺序不是随便定的:先做便宜的(正则、strip),再做贵的(分词);先去重再分句,能省一半分词开销。 这个顺序在小语料上看不出差别,语料上百万行时能差出几个小时。
二、每一步砍掉多少
20 行进,16 行过空行关(4 行是空的或只有全角空格); 归一化去重后剩 13 行(3 行完全重复); 去掉含 URL 的 2 行剩 11 行。 从 20 到 11,砍掉 45%。这个比例在爬来的网页语料里算少的,真实场景经常留下不到两成。 归一化那一步用的是 NFKC:把全角字母变半角、全角空格变半角。 不做这一步,「ABC」和「abc」会被当成两个不同的词。
三、分词这一步,我用了单字切
沙箱里没有 jieba,也没有别的分词库,所以我按单字切。11 行切出 164 个字。 单字切的问题很明显:没有词边界,「路灯」和「路」「灯」是一回事,下游任务的天花板当场就被压低了。 这一步我没有取巧假装用了分词库。表里写的是「按单字切(无分词库)」,读的人一眼能看出限制在哪。 去停用字用的是 10 个字的表(的、了、在、是、也、有、和、与、该、约),去掉 9 个,剩 155。 词表去重后 98 项。
四、预处理最容易被低估
做 NLP 的人有个共识:数据决定上限,模型决定离上限多近。而预处理是唯一能改上限的环节。 三个常见的坑: 1. 顺序错。先分词再去重,等于把同一句话分了两遍,白烧一半算力。 2. 归一化过头。把大小写全抹平,人名地名就分不出来了。 3. 停用词表抄别人的。别人为搜索场景做的表,用在情感分析上会把「不」「没」删掉,情感直接反了。 第三条最要命。停用词表必须按任务定,不能通用。
五、动手 Demo:语料预处理:从 20 行脏数据到 98 项词表
造 20 行带噪声的语料(HTML 标签、空行、全角字母、重复行、含 URL 的行); 依次做去标签、去空行、NFKC 归一化去重、去 URL 行、分句、单字切分、去停用字、建词表, 每一步都打印剩余规模。
l1 = [re.sub(r'<[^>]+>', u'', s) for s in raw] # 去标签
l3 = [unicodedata.normalize('NFKC', s) for s in l2] # 全半角归一
l4 = [s for s in l3 if u'http' not in s] # 去 URL 行
for s in l4:
for p in re.split(u'[。!?;]', s): # 分句
if p.strip():
sents.append(p.strip())
实跑输出:
--------------------------------------------------------------------
语料预处理六步:每一步砍掉多少
--------------------------------------------------------------------
步骤 做了什么 剩余规模
-------------------------------------------------
0 原始 逐行读入 20
1 去标签 正则剥掉尖括号 20
2 去空行 strip 后非空 16
3 归一化去重 NFKC + 完全相同去重 13
4 去 URL 行 含 http 的整行丢弃 11
5 分句 按句末标点切 12
6 分词 这里按单字切(无分词库) 164
7 去停用词 10 个停用字 155
8 建词表 去重 98
从 20 行到 11 行 砍掉 9 行
从 164 个词到 98 个词表项 压缩 1.7 倍
词表 一、三、下、不、业、主、二、亮、人、仍、体、余、全、内、准、分、制、前…
注意第 6 步:这里按单字切,因为沙箱没有分词库。单字切会丢掉词边界,下游任务的天花板就被压低了。
「0 原始」和「1 去标签」都是 20 行——去标签不减少行数,只减字符数。 表里只看行数会以为这步没用,其实它把尖括号里的东西清掉了。 「2 去空行」从 20 掉到 16,砍掉的是两行真空行和一行只有全角空格的行。 全角空格用 strip 能去掉,这是 NFKC 之前就能做的。 「5 分句」从 11 行变成 12 句——有一行里有两个句号,被切开成两句。 行数和句数不是一回事,这一点在算语料规模时经常搞混。 「6 分词」那一行的 164 是字数不是词数,因为这里按单字切。换成真分词库,这个数会掉到六七十。
30 秒实验
- 把停用字表里的「不」加进去,看词表少几项、句子意思变了多少。
- 把 NFKC 归一化那一步删掉,看去重后还剩几行。
- 想一想:为什么情感分析的停用词表绝不能删「不」和「没」?
常见误区
- 「预处理是杂活,随便洗洗」——它决定整个任务的上限。
- 「停用词表可以通用」——删错一个「不」,情感分析直接反。
- 「行数就是句子数」——一行里可能有好几句,切完会变多。
自测三问(点开对答案)
1. 为什么先去重再分句? —— 重复句只分一次,省掉一半分词开销;语料越大省得越多。 2. NFKC 归一化解决什么问题? —— 全角字母、全角空格和半角混用,不归一化会被当成不同的词。 3. 情感分析的停用词表为什么要特别小心? —— 「不」「没」被删掉,否定语气丢失,情感判断会整个反过来。语料洗完了,这一章的基础部分到此结束。下一节开始看五类具体场景,第一个是最常用的情感分析。
- ☐ (本节完,下一节 6.2.1 情感分析:理解文本中的情感倾向)