
简介面向文本分析、信息检索与自然语言处理场景这份“史上最全中英文停用词”提供了覆盖中英文常用停用词的集合可解决分词后噪声词干扰关键词提取、情感分析、机器翻译等任务预处理效率的问题适合高校学生、算法工程师及数据分析初学者使用。包体共11个文件以txt词表为主9个txt另有2个Python脚本用于合并与查看停用词整体压缩后仅45KB轻量易部署可配合jieba或nltk直接加载。目前已有1222人学习下载。资源内包含中文、英文及中英文混合停用词表并可基于脚本灵活合并或扩充自定义词表帮助读者在文本预处理中快速过滤“的、了、the、is”等无实质意义词汇适用于中文分词、英文tokenize、文本分类与情感分析等场景既可作为日常开发的基础词典也可作为定制化停用词库的起点。1. 从检索到训练为什么 | “史上最全中英文停用词”值得认真对待做搜索的、做文本分类的、做情感分析的几乎都会被同一样东西绊倒停用词。“史上最全中英文停用词”这个词条常年出现在技术社区和分享帖的标题里但真正的问题从来不是“哪一份词表最全”而是“拿到一份号称最全的词表之后怎么让它在你自己的语料和任务里不翻车”。我见过不少团队直接下载一份三千行的停用词表灌进分词管道然后发现检索召回率不升反降情感分析的结论反了分类模型的指标也掉了。这篇笔记想把停用词这件事讲透一份中英文停用词表到底该覆盖什么、怎么从源头把它清洗成能直接用的词典、不同任务下应该怎么裁剪、以及那些最容易让你踩坑的细节。适合正在搭搜索服务、做文本预处理、或者被分词结果里的“的了在”烦到的从业者。2. 先把“最全”拆开覆盖度、纯净度、可控性三个硬指标“最全”这个词是有诱惑力的。人容易觉得词表越长越好但一份真正能用的中英文停用词表评判标准不是行数而是三个彼此纠缠的指标覆盖度、纯净度、可控性。这三个指标不掰扯清楚后面所有“直接套用”的行为都是在给自己埋雷。2.1 覆盖度几百、几千还是几万先明确词表的真实成分先给一个基本盘英文经典停用词表大约三百个词中文常用停用词表大约在五百到一千之间。为什么这么少因为停用词的定义是“高频但对语义贡献低”的词。英文里的 the、a、of、is、and 属于这一类中文里的“了、的、在、是、和、与”属于这一类。这些词删掉之后文本的主体结构依然完整。那“史上最全”为什么能攒到几万行因为很多版本把数字、标点、单字符、双字符、甚至常见人名地名品牌名都塞进去了。比如把“123”、“2024”、“http”、“com”、“www”都算成停用词。这类词确实在部分场景属于噪声但它们不是严格意义上的停用词而是“噪声词”。把两者混在一起是覆盖度指标急剧膨胀的根源。我一般会把一份所谓的“全量停用词表”拆成三层第一层是核心高频虚词中英文各几百个这是任何任务都建议过滤的第二层是扩展停用词包含代词变体、介词短语、时间词、数量词这层需要按任务选择要不要启用第三层是噪声词包含标点、数字、URL 片段、无意义符号这一层通常只在检索场景启用在情感分析场景必须慎用。如果你的目标只是做一个通用的预处理词典覆盖度盯住前两层就够了。第三层每往里面加一个词都要问一句这个词在你的真实语料里出现频率高吗被它误伤的合法词有多少2.2 纯净度混入的标点和乱码如何悄悄污染向量空间纯净度是“最全”词表最容易翻车的地方。一份号称几万词的停用词表里面往往混着全角标点、半角标点、空白字符、BOM 头、零宽字符甚至还有从 PDF 里复制出来的人工换行碎片。你把这份词表灌进分词器时很多词表面上看存在实际上匹配不到任何 token。举一个我踩过的例子某个中文词表是从网页直接保存的里面所有逗号和句号都是全角但分词器切出来的标点是半角于是一整批标点停用词全部失效。排查了半天最后发现词表里根本没有“”而是“”这种肉眼几乎不可区分的变体。纯净度的第二层含义是语言纯净中文词表里不应该有英文词英文词表里不应该有中文汉字。别笑我在不少公开词表里都见过这种串味。所以在合并词表这一步按语言分拣、按字符集过滤这两件事比什么都重要。宁可丢掉一部分词也不要让一份带乱码的词表进入生产管道。2.3 可控性一份词典要能按任务切换强度可控性指的是停用词表不能是一坨不可拆分的集合。它应该是一组有层级的子集让你在不同任务里能“开多层还是关多层”。比如做搜索召回时你可能需要最激进的过滤把“的、了、吗、呢”全部丢掉把标点数字也丢掉这能让索引小很多。但如果你做的是情感分析“不、没、无、很、太、最”这些词一旦被划进停用词表整句的情感倾向会直接反转。同一个词表不能两头通吃所以可控性的落地方式就是把词表按类别拆成多个文件运行时按任务组合。常见的做法是拆成 stopwords_core.txt、stopwords_extended.txt、stopwords_noise.txt 三个文件再在配置里指定当前任务加载哪几个。这一层想清楚了“最全”就变成了一种可选的资源而不是一个强制的负担。写到这里我想强调一件事一套“最全”词表如果没有做分层它的使用成本极高因为你永远不知道哪些词被哪个任务误伤了。分层是让“全”变成可用能力的第一步。3. 从源头词表到生产可用合并、去重、清洗三步走当你手里有几份来源不同的停用词表时第一步永远是合并去重但合并本身有讲究。不是把文件拼起来就完事而是要按语言分拣、按字符集过滤、按长度约束最后输出带统计信息的干净词表。这一节我给出一套可直接跑通的 Python 流程。3.1 收集与合并先让同一份词表覆盖多类源头假设你手头有四个文本文件两份中文词表、两份英文词表。第一步是把它们按语言归拢去重然后输出两份合并后的原始词表。# merge_stopwords.py from pathlib import Path sources [ Path(zh_a.txt), Path(zh_b.txt), Path(en_a.txt), Path(en_b.txt), ] zh_words set() en_words set() for src in sources: is_zh src.name.startswith(zh) with open(src, encodingutf-8, errorsignore) as f: # 跳过空行和 # 注释行 lines [line.strip() for line in f if line.strip() and not line.startswith(#)] if is_zh: zh_words.update(lines) else: en_words.update(lines) # 排序后写出保证每次生成的文件内容一致 with open(merged_zh.txt, w, encodingutf-8) as f: f.write(\n.join(sorted(zh_words))) with open(merged_en.txt, w, encodingutf-8) as f: f.write(\n.join(sorted(en_words)))这段代码的逻辑是用文件名前缀判断语言用set天然去重用errorsignore跳过无法解码的坏字节。输出前做了排序这样后续 diff、版本管理都方便。要注意errorsignore是双刃剑它能避免脚本崩溃但也会悄悄丢弃乱码字符所以合并完一定要再看一眼输出文件里有没有可疑的行。3.2 清洗脚本剔除标点、控制长度、限制字符集合并完只是第一步清洗才是让词表可用的关键。中文停用词有一个特点绝大多数是单字或双字三字以上的虚词很少。英文停用词绝大多数是两到五个字母的短词偶尔有带撇号的变体比如 dont、cant。清洗脚本要基于这些规律做约束。# clean_stopwords.py import re # 中文只保留 1~4 个汉字 ZH_RE re.compile(r^[\u4e00-\u9fa5]{1,4}$) # 英文只保留 2~10 个小写字母撇号单独处理 EN_RE re.compile(r^[a-z]{2,10}$) def clean_zh(raw_path: str, out_path: str): kept set() with open(raw_path, encodingutf-8) as f: for line in f: w line.strip() w w.replace(\ufeff, ).replace(\u3000, ) if ZH_RE.match(w) and w not in kept: kept.add(w) with open(out_path, w, encodingutf-8) as f: f.write(\n.join(sorted(kept))) def clean_en(raw_path: str, out_path: str): kept set() with open(raw_path, encodingutf-8) as f: for line in f: w line.strip().lower() w w.replace(\ufeff, ).replace(\u3000, ) if EN_RE.match(w) and w not in kept: # 单字母在英文停用词里只保留 a, i其他都丢掉 if len(w) 1 and w not in (a, i): continue kept.add(w) with open(out_path, w, encodingutf-8) as f: f.write(\n.join(sorted(kept))) clean_zh(merged_zh.txt, stopwords_zh.txt) clean_en(merged_en.txt, stopwords_en.txt)参数怎么调ZH_RE里的{1,4}表示最多四个汉字。如果你发现语料里偶尔出现五字虚词比如“在一定程度上”可以放宽到{1,5}但代价是可能混入五字短语。EN_RE里的[a-z]{2,10}把大写词全部排除这会让词表更干净但代价是文本里首字母大写的 The 会被漏掉。常见的做法是在过滤之前先把语料整体转成小写而不是在词表里去兼容大小写。单字母的a和i是英文里唯一值得保留的单字母停用词其他单字母如u、r通常是网络缩写直接在清洗阶段丢掉更安全。中文侧不需要做这个特殊处理因为中文单字虚词本身就是主体。3.3 统计输出让“最全”变得可度量清洗完成后做一次统计把两份词表的规模、样例输出到一个报告文件里。这不是仪式感而是后续每次增删词时的基准线。# stats_stopwords.py from pathlib import Path for lang in (zh, en): path Path(fstopwords_{lang}.txt) words path.read_text(encodingutf-8).splitlines() sample words[:20] print(f{lang}: total {len(words)}, sample {sample})看输出时重点确认三件事总数是否符合预期、样例里有没有混入非目标语言字符、有没有奇怪的符号残留。如果中文词表里出现“the”或英文词表里出现“的”说明源头文件的标注或分拣出了问题要回头查原始文件而不是在这里硬改。4. 别急着套用三种场景的停用词定制造型词表清洗到能用的程度只是完成了一半。接下来要解决的问题是同一份“最全”基础词表怎么派生出检索、分类、情感分析三个不同任务各自需要的版本。这个环节最考验工程判断力。4.1 检索场景保住实体词和单字查询搜索召回里停用词的作用是缩小索引、提升查询效率。常见的做法是把“的、了、在、是、和、与”这类高频虚词以及标点、数字全部过滤掉。但有个坑单字查询不能被停用词表吞掉。比如用户搜“冰箱”和搜“保鲜冰箱”后者在分词后可能得到“保鲜”“冰箱”两个词。如果“保”或“鲜”在停用词表里查询语义就残缺了。所以检索场景的停用词表必须配一份“强制保留词”白名单把品牌词、产品词、地区词、数字型号放进去。很多搜索系统不是不做这个而是没意识到停用词表的“过滤”优先级高于查询词的手动保留逻辑。我一般会在检索配置里加一个 rule当查询词命中白名单时跳过停用词过滤只有未命中白名单的词才走停用词表。这个顺序一旦反了白名单就形同虚设。4.2 分类场景拉大类别区分度的词要进白名单文本分类任务里停用词的作用是去掉那些所有类别里都高频出现的词。但问题在于某些词汇虽然整体频率高却在某个类别里显著更高这类词的区分度不能被停用词表抹掉。举例来说在做汽车领域评论分类时“汽车”这个词在很多文档里都会出现但它恰恰是识别“汽车类”文档的重要特征。把它加进停用词表等于主动丢弃了判别信息。处理方式是用卡方检验或信息增益算一遍每个词的类别区分度把区分度排名前 5% 的词从停用词表里摘除。即使这些词是“的、是、有”只要在某个类别里分布异常都应该保留。实践中我也见过反过来的做法有人为了提升分类模型的泛化能力故意把类别专属词也过滤掉让模型只看结构特征。这种思路在短文本上偶尔有效但会牺牲长文本的准确率。文本分类的停用词表没有标准答案只有按测试集指标调出来的版本。4.3 情感分析场景否定词与程度副词必须保留情感分析是对停用词最敏感的任务。很多人直接套用通用停用词表结果“不怎么样”“太差了”“没意思”这类句子里的“不”“太”“没”全部被过滤剩下的“怎么样”“差”“意思”根本表达不了真实倾向。这个坑的解法是把否定词、程度副词、转折词单独拎出来形成一份“情感保留词”在过滤阶段优先级高于停用词表。我常用的集合包括不、没、无、未、别、莫、勿、很、太、最、极、更、都、也、还、但、却、虽、仍、依、然。这些词一旦被通用停用词表吞掉模型的输入就失真了。还有一个容易被忽略的细节英文情感分析里的 not、no、never、but、however、too、very 同样要保留。如果你的英文停用词清单一律把 not 当成噪声那“not good”会被处理成“good”方向直接反了。情感分析场景中停用词表的核心目的不是“删”而是“选择性删”。那些对情感极性有贡献的词应该比停用词表拥有更高的优先级。5. 避坑停用词表使用中的五个经典翻车现场这一章把我见过的、自己和同事踩过的坑集中列一遍。每一类都是“现象—原因—解决”的结构建议你把这些条目当成上线前 checklist 来用。5.1 误删否定词情感分析结果直接反转现象某次舆情系统里一条“这家餐厅的服务一点都不好”被识别为正面评价。排查后发现问题出在停用词表里包含了“一点”“都”“不”。原因通用停用词表为了检索效率会把“都”“不”这类高频词列为噪声但情感分析需要它们承载否定语义。解决把否定词和程度副词全部移出停用词表改成白名单机制并写一条单元测试输入包含“不”的典型句子断言情感极性方向正确。每次更新停用词表时自动跑这批回归用例。5.2 领域术语被过滤语义检索召回塌方现象某个商品搜索系统上线新版词表后“全脂牛奶”的召回量掉了一半。查日志发现“全”“脂”“奶”三个词都被停用词表过滤了只剩一个“牛”。原因那份“最全”词表里把大量单字高频词一网打尽而商品名称里恰恰有大量单字词。解决给自己的业务词库建一张白名单把品牌、品类、属性词都放进去。过滤顺序必须是“白名单优先、停用词表次之”白名单里的词一律不过滤。5.3 全角空格和零宽字符导致词表能看不能用现象词表文件里明明写着“的”但程序里token in stopwords永远返回 False。原因文件里混入了零宽空格或者从富文本编辑器复制来的不可见字符肉眼看不出来但in判断是逐字符精确匹配的。解决写清洗脚本时统一处理\ufeff、\u3000以及所有\u200b-\u200d区间的字符另外用十六进制查看器抽查词表的末尾行和中间行确认没有残留不可见字符。5.4 词表越攒越“全”效果却一路下滑现象有人把网上能找到的五六份中英文停用词表全部合并去重得到将近两万词灌进系统后各项指标全面下滑。原因多份词表存在互相冲突的过滤偏好。一份词表把“信息”过滤掉另一份把它保留合并之后“信息”被过滤文档的关键信息被误伤。解决不要追求一次合并所有来源。以一份经过验证的核心词表为基座只从其他来源逐条挑选确有必要的词并记录每个词的来源和添加理由。这样词表的每次增删都可追溯。5.5 没有版本化词表改动无法回滚现象某次测试临时给停用词表加了几十个词跑完忘了撤销直接带上线三天后线上指标开始恶化但没人记得改过什么。原因词表文件没有被纳入版本管理也没有通过配置中心下发导致改动不可追溯。解决把停用词表当作代码一样维护每个版本打 tag发布记录写清改动原因。我在工程里习惯把词表放在独立目录包含stopwords_zh.txt、stopwords_en.txt、whitelist.txt、changelog.md每次改动必须同步更新 changelog。6. 验证你的“最全”停用词表覆盖率、误杀率、业务指标三件套一份停用词表到底值不值得用最终要看三个数字覆盖率、误杀率、业务指标变化。这里给出最轻量的验证方案。覆盖率统计的是停用词在语料中的命中比例。抽样一万条文档分词后统计有多少 token 被停用词过滤覆盖率不是越高越好而是要落在合理区间。中英文混合的通用语料里中文覆盖率通常在 20% 到 35%英文覆盖率在 15% 到 25%。远超这个区间优先怀疑词表混入了过多种类词。误杀率需要人工抽样。随机取 200 个被过滤的 token逐一判断里面有多少是实体词、业务关键词、情感词。误杀率超过 5%就该考虑收窄词表范围或加强白名单。业务指标变化最直接用同一批训练集和模型分别跑“有停用词”“无停用词”“有定制停用词”三个版本的实验对比准确率、召回率或搜索的点击率。这一步能彻底终结“要不要用停用词”的争论。我现在的习惯是每份词表上线前先跑一遍覆盖率脚本做快速筛查再抽查误杀率最后用最小业务实验验证收益。这中间白名单的维护优先级高于停用词表本身的增删。把所有词表改动都落到版本管理里给自己留好后悔药。我见过太多因为一份“最全”词表而翻车的案例希望这套验证流程能帮你少踩几个坑。本文还有配套的精品资源点击获取