资讯动态

中英文停用词表避坑指南:从选型、合并到任务裁剪的工程实践

发布时间:2026/10/15 3:56:27 来源:尧图企业网站定制
简介这份中英文停用词资源面向从事文本分析、自然语言处理与信息检索的开发者及学习者用于解决分词后噪声词干扰关键词提取、情感分析与文本分类的问题。压缩包共11个文件以9个txt停用词表和2个py脚本为主整体约45KBtxt文件分别覆盖中文、英文及中英合并词表py脚本则用于停用词表的合并与整合方便直接接入预处理流程。目前已有1220人学习下载。资源同时收录中文助词、介词、连词与英文冠词、介词等常见停用词可配合jieba、nltk等工具在分词后快速过滤得到更精炼的词汇列表脚本化的合并方式也便于按业务场景定制和定期更新词库适合作为文本预处理阶段的基础参考资料。1. 中英文停用词表为什么你搜到的“史上最全”往往最先翻车做文本挖掘的人几乎都干过同一件事搜一份“史上最全中英文停用词”下载下来直接丢进stopwords.txt然后跑分词、算 TF-IDF、做主题模型。结果往往很玄学——英文那边把not、no、very删了情感分析直接反向中文那边把“不”“没”“无”删了否定句全变肯定句。所谓“最全”很多时候是“最坑”。停用词stop words本质是一类在检索和统计里区分度极低、但在语义里可能极关键的高频词。它的价值不在“多”而在“匹配任务”。这份笔记不给你一份万能表而是讲清楚中英文停用词到底该怎么选、怎么合并、怎么按任务裁剪以及怎么用一份可复现的脚本把它落到你自己的项目里。适合正在做文本分类、关键词抽取、RAG 检索、日志聚类的工程师新手能照着跑熟手能看到边界。2. 中英文停用词的来源、差异与合并策略2.1 英文停用词的三个主流来源与取舍英文停用词表最常被引用的三份来源和定位完全不同混用之前得先知道它们各自为什么存在。第一份是早期信息检索系统内置的通用表特点是短通常 100400 个词只收the、a、of、is这类纯语法词几乎不碰语义词。第二份是搜索引擎类项目公开的表规模更大会把www、http、com、click这类网页噪声词也收进去适合做网页正文抽取但拿来做学术文本分类就会误删。第三份是机器学习库自带的表比如常见 NLP 工具包里的英文停用词集合它按语言维护覆盖面中等好处是版本可控、可编程调用。选型逻辑很简单做检索和倒排索引用第一类短表做网页清洗用第二类做可复现的模型训练优先用第三类因为你能锁定版本。我一般不会直接下载一份“最全”的 txt而是先确定任务再决定用哪一类作为基线。2.2 中文停用词的坑分词器自带表 vs 通用表中文比英文麻烦因为中文没有天然空格停用词过滤通常发生在分词之后。主流中文分词工具大多自带一份停用词表但这份表是为“分词”服务的不是为“你的任务”服务的。一个典型差异分词器自带的表里常常包含“的”“了”“是”这类虚词这没问题但它也可能包含“可以”“因为”“所以”这类在因果抽取任务里非常重要的连接词。你如果无脑套用做事件因果关系抽取时就会把信号词全删掉。另一个坑是中文停用词表普遍存在“单字 vs 词”混装。比如“不”是单字“不是”是词如果分词粒度是词那“不”可能根本不会单独出现你表里留着它没意义反过来如果分词粒度是单字表里全是双字词也匹配不上。所以中文停用词表必须和分词粒度绑定脱离分词器谈中文停用词表是耍流氓。2.3 合并中英文表的可复现脚本下面这段脚本做三件事加载中英文基线表、做归一化小写、去空白、合并去重并输出。它不依赖任何在线下载表内容以集合形式内联方便你替换成自己的来源。# merge_stopwords.py # 合并中英文停用词基线表输出统一格式 # 英文基线只保留纯语法词避免误删语义词 EN_BASE { a, an, the, and, or, but, if, then, than, of, at, by, for, with, about, into, through, is, are, was, were, be, been, being, this, that, these, those, it, its, as, to, from } # 中文基线虚词与高频助词注意与分词粒度对齐此处按词粒度 ZH_BASE { 的, 了, 着, 过, 是, 在, 和, 与, 及, 或, 而, 就, 都, 也, 还, 被, 把, 对, 从, 向, 一个, 一种, 一些, 这个, 那个, 这些, 那些 } def normalize(words): 统一小写并去除首尾空白中文不受影响 return {w.strip().lower() for w in words if w and w.strip()} def merge_stopwords(*tables): 合并多份表并去重 merged set() for t in tables: merged | normalize(t) return merged if __name__ __main__: final merge_stopwords(EN_BASE, ZH_BASE) # 排序后写出方便人工审阅和 diff with open(stopwords_merged.txt, w, encodingutf-8) as f: for w in sorted(final): f.write(w \n) print(fmerged stopwords: {len(final)})逻辑说明normalize负责大小写和空白归一这是英文表合并时最容易出问题的地方——The和the不归一就会重复。merge_stopwords用集合做并集天然去重。参数上EN_BASE和ZH_BASE是你可以直接替换的入口建议把来源不同的表分成不同变量传入而不是先手工拼成一个大文件这样后续做 A/B 对比时能单独开关。提示合并后的表一定要人工过一遍尤其是长度在 24 个字符的中文词误删往往就藏在这一段。3. 按任务裁剪停用词分类、检索、关键词抽取各要一套3.1 为什么“一套表打天下”必然出问题不同任务对同一个词的敏感度完全相反。以not为例在情感分类里它是决定极性的关键否定词删了模型准确率会掉在主题聚类里它几乎不携带主题信息留着只会增加噪声维度。中文的“不”同理。再比如“可以”“需要”“应该”这类情态动词在意图识别里是强特征在词频统计里是高频噪声。所以正确做法不是找一份最全的表而是维护一个“基线表 任务增量表”的两层结构基线表放公认的纯语法词任务增量表按任务增删。3.2 用配置驱动的方式管理多套停用词下面用一个 YAML 配置加 Python 加载器的方式把“基线 任务增量”落下来。这样你换任务时只改配置不动代码。# stopword_loader.py import yaml def load_stopwords(config_path): 从 YAML 加载停用词配置 结构 base: [ ... ] tasks: sentiment: add: [ ... ] remove: [ ... ] with open(config_path, r, encodingutf-8) as f: cfg yaml.safe_load(f) base set(cfg.get(base, [])) return base, cfg.get(tasks, {}) def get_task_stopwords(base, tasks, task_name): 按任务名生成最终停用词集合 task tasks.get(task_name, {}) result set(base) result | set(task.get(add, [])) # 任务专属新增 result - set(task.get(remove, [])) # 从基线中移除如 not return result if __name__ __main__: base, tasks load_stopwords(stopwords.yaml) senti get_task_stopwords(base, tasks, sentiment) print(sentiment stopwords:, len(senti)) print(contains not:, not in senti)对应的stopwords.yaml片段base: - the - a - of - 的 - 了 tasks: sentiment: add: - very - really remove: - not - no - 不 - 没 retrieval: add: - http - www remove: []逻辑说明base是所有任务共享的底线tasks.name.add是任务专属噪声词tasks.name.remove用来把基线里误伤的关键词捞回来。参数上remove的优先级高于add所以即使某个词在 base 里只要在 remove 里就会被剔除。这个结构的好处是情感分析任务里not被保留检索任务里http被加入互不干扰。3.3 裁剪效果怎么验证两个可量化指标裁剪完不能凭感觉至少看两个指标。第一是词表覆盖率在语料上统计被过滤词占总 token 的比例一般英文在 40%50%、中文在 30%40% 比较合理太低说明表太短太高说明误伤严重。第二是任务指标回归分类任务看 F1检索任务看召回率裁剪前后各跑一次掉点超过 2% 就要回查。def coverage(corpus_tokens, stopwords): 计算停用词覆盖率 total sum(len(t) for t in corpus_tokens) hit sum(1 for t in corpus_tokens for w in t if w in stopwords) return hit / total if total else 0.0参数说明corpus_tokens是分词后的二维列表stopwords是当前任务的集合。这个函数只做统计不修改数据方便你在不同配置间快速对比。4. 避坑与排查停用词处理里最常见的 5 个翻车现场4.1 现象英文情感分析准确率骤降原因基线表里包含not、no、never被无差别删除否定句全部变成肯定句。解决在情感任务配置的remove列表里显式保留这些词并单独跑一次回归验证。4.2 现象中文分词后停用词过滤几乎没效果原因停用词表是词粒度但分词器输出的是单字或者反过来。解决先确认分词粒度再对齐词表。用coverage函数看覆盖率如果低于 10%基本就是粒度不匹配。4.3 现象合并多份表后出现大量重复和大小写混装原因不同来源的表没有做归一化The和the、的和的带空格同时存在。解决合并前统一走normalize合并后用sorted输出再 diff 一次。4.4 现象检索任务召回率下降原因把http、www、com这类网页噪声词加进了通用基线导致 URL 相关查询被误伤。解决这类词只放在检索任务的add里不要进 base。4.5 现象换了一版停用词表模型结果无法复现原因表是直接下载的 txt没有版本记录内容随时可能变。解决把停用词表纳入版本管理或者像 3.2 那样用配置内联确保每次实验都能锁定同一份表。5. 把停用词表变成可维护资产版本化与自动化回归停用词表最容易被当成一次性消耗品下载、用完、丢掉。但只要你的项目还在迭代它就应该是一份受版本控制的资产。我现在的习惯是停用词配置和代码放同一个仓库每次改动走 PRCI 里跑一个轻量回归脚本覆盖率和任务指标都打印出来掉点就拦住。具体做法分三步。第一步把stopwords.yaml纳入 git改动必须写清楚“为什么加/删这个词”比如“保留 not情感任务 F1 从 0.81 回到 0.86”。第二步在 CI 里加一个回归任务用固定的小样本语料跑coverage和一次分类评估输出对比表。第三步给停用词表打 tag和模型版本绑定这样半年后回查实验时能精确还原。维护动作频率产出人工审阅新增词每次 PR审阅记录覆盖率回归每次提交覆盖率数值任务指标回归每次提交F1/召回对比版本打 tag每次发版可复现快照一个容易忽略的技巧是不要追求“史上最全”而是追求“每个词都有理由”。我见过太多项目停用词表几千行问其中任何一个词为什么在里面没人答得上来。这种表不是资产是负债。反过来一份两百词、每个词都能说清来源和任务边界的表才是真正能复用的东西。最后说个血泪经验停用词处理没有后悔药删错了词后面所有特征工程都建立在错误前提上。所以每次动表之前先备份、先跑回归、先看覆盖率。这个习惯帮我省过至少三次通宵排查。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑