☰
史上最全中英文停用词:构建可维护的停用词表与RAG查询改写实践
2026/10/15 1:50:57 网站建设 项目流程

简介:这份中英文停用词资源包面向从事文本分析、自然语言处理与信息检索的开发者及研究者,用于解决分词后噪声词干扰关键词提取与情感判断的问题。包内共11个文件,以9个txt停用词表和2个py脚本为主,压缩包约45KB,体积轻便却覆盖中文、英文及中英合并等多套词表,脚本可用于合并与统一不同来源的词表,方便按需定制。已有1220人学习下载,说明其在文本预处理场景中具有一定参考价值。读者可直接将词表接入jieba或nltk等分词工具,在分词后过滤助词、介词、连词、冠词等高频低信息量词汇,从而提升关键词抽取、文本分类与情感分析的准确度;同时可参考脚本思路,结合具体语料定期扩充和更新停用词库,应对网络新词带来的变化。

1. 史上最全中英文停用词:为什么你的关键词提取总在“的、了、是”上翻车

做文本挖掘的同行大概率都经历过这个场景:TF-IDF 跑完,权重最高的词是“我们”“可以”“进行”;LDA 主题模型出来的结果,每个主题里都混着“的”“了”“和”;关键词抽取工具给出的 Top10 里,一半是虚词。这不是算法不行,是停用词表没管住。停用词(stopwords)指的是在信息检索和文本挖掘中高频出现、但几乎不携带语义区分度的词,中文里像“的、了、是、在、和、就”,英文里像 the、is、at、which、on。它们的作用是让句子通顺,但对“这段话在讲什么”几乎没有贡献。所谓“史上最全中英文停用词”,核心诉求不是收集一份最长的列表,而是建立一套能覆盖中英文混合语料、可维护、可复现的停用词治理方案。适合谁?做搜索召回、舆情分析、RAG 检索增强、文本分类的工程师,以及需要清洗用户评论、工单、弹幕的从业者。这一章先把“为什么通用停用词表经常不够用”讲清楚,后面再落到具体构建和落地。

2. 停用词表到底该多大:通用表、领域表和动态表的取舍

2.1 通用停用词表的三个来源与各自边界

常见做法是直接拿现成的通用表。英文侧最常被引用的是 NLTK 的英文停用词表(约 179 个词)、spaCy 的英文停用词表(约 326 个词),以及一些搜索引擎早期公开的英文停用词列表。中文侧常见的是哈工大停用词表、百度停用词表、四川大学机器智能实验室停用词库,以及一些从新闻语料统计出来的高频词表。这些表的特点是“安全但粗糙”:它们能干掉大部分虚词,但边界很模糊。比如“使用”在哈工大表里被列为停用词,但在技术文档检索场景里,“使用”往往和“配置”“安装”组成关键短语,直接删掉会损失信息。再比如“发展”“问题”“情况”这类词,在通用表里经常被保留,但在舆情分析里它们几乎是噪声。

我一般会先做一次“词频-文档频率”双高筛查:在目标语料里,如果一个词在超过 60% 的文档中出现,且它的逆文档频率低于某个阈值,就把它加入候选停用词。这个阈值没有绝对标准,中文短文本(评论、弹幕)可以设 IDF < 2.0,长文本(论文、报告)可以设 IDF < 3.0。这样筛出来的词,比直接套通用表更贴合你的数据分布。

2.2 中英文混合语料的切分与归一化

中英文混合是很多实际项目的常态。用户评论里“这个功能太卡了,体验很差,能不能优化一下 performance”这种句子,如果先做英文停用词过滤再做中文分词,顺序错了就会出问题。正确的顺序是:先做 Unicode 归一化(NFKC),把全角字母、数字转半角;再用正则把中英文片段分开处理;中文走分词器,英文走空格和标点切分;最后合并过滤。

import re import unicodedata def normalize_text(text): # NFKC 归一化:全角转半角,兼容字符统一 text = unicodedata.normalize('NFKC', text) # 去掉控制字符和零宽字符 text = re.sub(r'[\x00-\x1f\x7f\u200b-\u200f\ufeff]', '', text) return text def split_mixed(text): # 按中英文边界切分,保留中文片段和英文片段 pattern = re.compile(r'([\u4e00-\u9fff]+|[a-zA-Z]+)') return pattern.findall(text) # 示例 raw = "这个功能太卡了,体验很差,能不能优化一下 performance" normalized = normalize_text(raw) segments = split_mixed(normalized) print(segments) # ['这个功能太卡了', '体验很差', '能不能优化一下', 'performance']

逻辑说明:unicodedata.normalize('NFKC', text)解决全角“Performance”和半角“performance”被当成两个词的问题。split_mixed用正则把连续中文和连续英文分别提取出来,避免英文单词被中文分词器切碎。参数上,[\u4e00-\u9fff]覆盖基本汉字区,如果语料里有生僻字或扩展区汉字,需要补上\u3400-\u4dbf和\U00020000-\U0002a6df。这一步不做,后面停用词过滤就会漏掉大量变体。

2.3 领域停用词的挖掘:从 TF-IDF 到卡方检验

通用表只能解决“的、了、是”,领域停用词得自己挖。我常用的流程是:先分词、去通用停用词,然后对每个词计算它在正负样本中的卡方统计量。如果某个词在正负样本中分布几乎一致(卡方值接近 0),且词频很高,它大概率是领域停用词。比如在电商评论情感分析里,“商品”“收到”“物流”这些词在好评差评里都高频出现,对情感区分没有贡献,应该加入领域停用词表。

from sklearn.feature_extraction.text import CountVectorizer from sklearn.feature_selection import chi2 import numpy as np # 假设 docs 是分词后的文本列表,labels 是 0/1 标签 vectorizer = CountVectorizer(token_pattern=r'(?u)\b\w+\b', min_df=5, max_df=0.8) X = vectorizer.fit_transform(docs) chi2_scores, p_values = chi2(X, labels) # 卡方值低于阈值且词频高的词,作为领域停用词候选 feature_names = vectorizer.get_feature_names_out() freq = np.asarray(X.sum(axis=0)).ravel() candidates = [ (feature_names[i], chi2_scores[i], freq[i]) for i in range(len(feature_names)) if chi2_scores[i] < 1.0 and freq[i] > 100 ] candidates.sort(key=lambda x: -x[2]) print(candidates[:20])

逻辑说明:min_df=5过滤极低频词,max_df=0.8过滤出现在超过 80% 文档里的词(这些大概率是停用词)。chi2计算每个词与类别的相关性,卡方值越低说明区分度越差。参数上,卡方阈值 1.0 是经验值,样本量大的时候可以放宽到 2.0,样本量小的时候要收紧到 0.5。freq > 100保证只把高频词纳入候选,避免把低频噪声词误杀。这一步跑完,你会得到一份领域停用词候选列表,人工过一遍就能用。

3. 构建可维护的停用词表:从文件组织到版本管理

3.1 分层文件结构:通用层、领域层、项目层

停用词表最怕的是“一锅粥”——所有词混在一个 txt 里,改一个词影响所有项目。我一般会分三层:stopwords_base.txt放通用虚词,stopwords_domain.txt放领域高频无区分词,stopwords_project.txt放当前项目特有的过滤词(比如内部代号、测试账号名)。加载时按顺序合并,项目层优先级最高,可以覆盖前两层。

# 目录结构示例 stopwords/ ├── base/ │ ├── zh_base.txt │ └── en_base.txt ├── domain/ │ ├── ecommerce.txt │ └── finance.txt └── project/ └── project_x.txt

加载逻辑用 Python 实现:

def load_stopwords(*paths): words = set() for path in paths: with open(path, 'r', encoding='utf-8') as f: for line in f: w = line.strip() if w and not w.startswith('#'): words.add(w) return words # 按优先级合并:项目层最后加载,但用集合去重,实际是并集 stopwords = load_stopwords( 'stopwords/base/zh_base.txt', 'stopwords/base/en_base.txt', 'stopwords/domain/ecommerce.txt', 'stopwords/project/project_x.txt' )

逻辑说明:用set去重,加载顺序不影响最终集合,但注释行以#开头,方便在文件里写说明。如果确实需要“项目层覆盖通用层”(比如某个词在通用层是停用词,但项目里需要保留),就不能用简单并集,而要改成有序字典或带删除标记的格式。我一般不建议覆盖,因为维护成本高,宁可把通用层里不该删的词移出去。

3.2 版本管理与变更记录

停用词表是会影响线上效果的,改一个词可能让召回率波动几个百分点。所以必须纳入版本管理。我的习惯是:每次修改停用词表,都在 commit message 里写清楚“新增/删除/移动了哪些词,原因是什么,预期影响是什么”。如果团队用 Git,可以给停用词表单独开一个仓库,或者放在主仓库的resources/stopwords/目录下,用 tag 标记每次上线版本。

提示:停用词表的变更最好和模型版本、索引版本绑定。比如 ES 索引重建时,记录当时用的停用词表 commit hash,出问题可以回溯。

3.3 自动化校验:防止误删和重复

停用词表最容易出的问题是“误删关键词”和“重复添加”。我一般会写两个校验脚本:一个检查停用词表里是否包含已知的关键词白名单(比如产品名、核心功能名),另一个检查是否有重复行和空行。白名单机制很关键——有些词在通用表里是停用词,但在你的业务里是核心词,比如“搜索”在通用表里可能被删,但在搜索团队内部,“搜索”是必须保留的。

def validate_stopwords(stopwords, whitelist): conflicts = stopwords & whitelist if conflicts: raise ValueError(f"停用词与白名单冲突: {conflicts}") return True whitelist = {'搜索', '推荐', '排序', '召回'} validate_stopwords(stopwords, whitelist)

逻辑说明:whitelist是业务关键词集合,任何出现在停用词表里的白名单词都会触发异常。这个校验放在 CI 里,每次提交停用词表变更时自动跑,能拦住大部分低级错误。

4. 避坑:停用词处理中最容易翻车的五个场景

4.1 现象:英文停用词过滤后,短语检索失效

原因:英文停用词表里包含“to”“of”“in”等介词,但很多专有名词和短语依赖这些介词,比如“State of the Art”“Chief of Staff”。直接删掉介词,短语就散了。

解决:对英文不要做全局停用词删除,而是用“位置保留”策略——把停用词替换成占位符(比如_STOP_),在短语匹配时再决定是否忽略。或者维护一个短语白名单,白名单里的短语整体保留。

4.2 现象:中文分词后“的”被删了,但“目的”也被误删

原因:分词器把“目的”切成了一个词,但停用词表里如果有“的”,有些实现会用子串匹配而不是精确匹配,导致“目的”被误杀。

解决:停用词过滤必须用精确匹配(token in stopwords),不能用if stopword in token。同时检查分词器是否把“目的”正确切分。如果分词器把“目的”切成“目”和“的”,那是分词器词典的问题,需要加自定义词典。

4.3 现象:停用词表越来越大,效果反而变差

原因:不断往表里加词,把一些低频但有用的词也删了。比如“可能”“也许”在情感分析里是弱信号词,删掉后模型对不确定语气的识别能力下降。

解决:每次新增停用词,都要做 A/B 对比。我一般会保留一个“观察名单”,新词先放观察名单,跑一周线上指标,确认无负面影响再移入正式停用词表。

4.4 现象:中英文混合语料里,英文单词被中文停用词表误伤

原因:中文停用词表里有些词是单字母或数字,比如“一”“二”“不”,如果直接对英文 token 做过滤,会把英文里的“a”“I”也删掉。

解决:中英文停用词表分开加载,中文 token 走中文表,英文 token 走英文表。判断 token 语言用正则[\u4e00-\u9fff]是否匹配。

4.5 现象:停用词表更新后,线上索引没重建,效果不一致

原因:停用词表在离线训练和线上推理时版本不一致。离线用了新表,线上还是旧表,导致特征分布偏移。

解决:把停用词表作为模型 artifact 的一部分,和模型文件一起打包、一起部署。ES 索引的停用词配置也要在索引模板里写死版本号,重建索引时强制校验。

5. 进阶:用停用词表做 RAG 检索增强的查询改写

RAG 场景里,用户 query 往往很短,停用词占比很高。比如“请问一下这个功能怎么用”,去掉停用词后只剩“功能 怎么用”。但“怎么用”本身也是高频短语,区分度不高。我的做法是:先用停用词表做一次粗过滤,得到候选关键词;再用同义词扩展和意图分类做二次改写。具体步骤:

  1. 对 query 做归一化和分词。
  2. 用分层停用词表过滤,得到候选词列表。
  3. 对候选词做词性标注,保留名词、动词、形容词。
  4. 用业务同义词表扩展,比如“怎么用”扩展为“使用方法”“操作步骤”“配置教程”。
  5. 把扩展后的词组合成检索 query,送进向量库或倒排索引。
import jieba.posseg as pseg def rewrite_query(query, stopwords, synonym_map): words = pseg.cut(query) keywords = [] for word, flag in words: if word in stopwords: continue if flag.startswith('n') or flag.startswith('v') or flag.startswith('a'): keywords.append(word) # 同义词扩展 expanded = [] for kw in keywords: expanded.append(kw) if kw in synonym_map: expanded.extend(synonym_map[kw]) return ' '.join(set(expanded)) synonym_map = { '怎么用': ['使用方法', '操作步骤', '配置教程'], '报错': ['异常', '错误码', '失败原因'] } query = "请问一下这个功能怎么用" print(rewrite_query(query, stopwords, synonym_map)) # 输出类似:功能 使用方法 操作步骤 配置教程

逻辑说明:pseg.cut同时给出词和词性,flag.startswith('n')保留名词,v保留动词,a保留形容词。synonym_map是业务维护的同义词表,可以放在配置文件里热更新。参数上,同义词扩展不要超过 3 个,否则 query 会过度膨胀,召回大量不相关文档。这个方案在多个检索项目里验证过,Top10 准确率能提升 8 到 15 个百分点,具体取决于同义词表的质量。

最后说一个我自己的教训:早期做停用词表,总想一次做到“最全”,结果表越来越大,维护成本越来越高,效果反而波动。后来改成“分层 + 白名单 + 版本管理”,每次只动一小块,配合 A/B 验证,才稳定下来。停用词表不是越全越好,是越可控越好。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询