哈工大停用词表:中文NLP文本预处理的利器与实战指南

发布时间:2026/8/2 10:01:19

哈工大停用词表:中文NLP文本预处理的利器与实战指南 1. 停用词表从“噪音”到“信号”的过滤器在自然语言处理和信息检索的日常工作中我们常常会遇到一个看似简单却至关重要的概念停用词。想象一下你正在为一篇科技文章构建一个关键词云或者训练一个情感分析模型。如果不对原始文本进行任何处理像“的”、“了”、“在”、“和”这样的高频虚词会像背景噪音一样充斥在你的词频统计和特征向量中。它们数量庞大却几乎不携带任何区分不同文档主题或情感倾向的有效信息反而会稀释那些真正有意义的实词如“算法”、“性能”、“优化”的权重。这时一个精心设计的停用词表就扮演了文本预处理流水线中第一道“过滤器”的角色它的任务就是精准地剔除这些“噪音”让后续的分析能更聚焦于“信号”。哈工大停用词表正是中文NLP领域里一块响当当的“金字招牌”。它并非一个官方发布的、版本固定的标准而是哈尔滨工业大学社会计算与信息检索研究中心HIT-SCIR在长期的研究与实践中积累、整理并开源出来的一份高质量停用词列表。这份词表之所以被广泛使用和信赖源于其背后扎实的学术研究和工程实践背景。它不仅仅是一个简单的“虚词列表”而是综合考虑了词性、频率、功能等多个维度经过大量真实语料如新闻、百科、论坛文本验证后形成的。对于刚入门的新手来说直接使用哈工大停用词表可以避免自己从零开始构建词表时可能遇到的覆盖不全或过度过滤的问题对于有经验的从业者它也常常作为一个可靠的基准或扩展的起点。那么这份词表具体包含哪些词它又是如何在实际项目中发挥作用的更重要的是我们该如何正确地使用它并规避那些常见的“坑”接下来我将结合多年的项目经验为你深入拆解哈工大停用词表的核心构成、应用场景、实操方法以及那些文档里不会写的注意事项。2. 哈工大停用词表的核心构成与设计逻辑一份停用词表的好坏关键在于其设计的逻辑是否清晰以及是否契合目标场景。哈工大停用词表的内容并非随意堆砌其内部通常可以进行细致的分类理解这些分类有助于我们在使用时做出更灵活的调整。2.1 基础功能词语法结构的“骨架”这是停用词表中最基础、最无争议的部分主要包含那些在句子中主要起语法结构作用而语义内容极弱的词。这类词在任何中文文本中都高频出现过滤它们几乎不会引起争议。助词的、地、得、了、着、过。这些词是中文语法的重要标记但对于理解内容主题帮助甚微。介词在、于、从、自、向、对、对于、关于。它们表示方位、时间、对象等关系但本身不指代具体事物。连词和、与、及、或、而、而且、但是、虽然。它们连接词句表达逻辑关系但通常不是内容的核心。语气词吗、呢、吧、啊、呀。在书面语中尤其是在正式的、信息密集的文本如新闻、论文中过滤掉它们能净化特征。部分副词很、都、就、也、还、却。这些词修饰程度、范围、时间但单独看信息量低。注意对于“很”、“非常”这类程度副词在情感分析任务中需要特别小心。虽然它们单独看是停用词但在“很好”与“好”的对比中“很”起到了强化情感极性的作用。因此在情感分析场景下是否过滤这类词需要根据具体模型设计慎重决定。2.2 高频泛义实词内容上的“干扰项”这部分是哈工大停用词表设计精妙之处它超越了一般的语法层面进入了语义层面。这些词本身是实词有具体含义但在特定领域或广泛语境下它们因为过于常见而失去了区分不同文档的能力。时间、方位名词年、月、日、时、分、秒、今天、现在上、下、左、右、前、后、里、外。在非时间序列分析或地理信息系统中这些词是典型的噪音。泛动词进行、开始、成为、做、有、是、说、表示。这些动词几乎可以搭配任何宾语其本身无法体现动作的具体性和特殊性。泛称代词和人称我们、你们、他们、它、其、此、该。在文档级别的分析中如主题分类指代具体谁通常不重要。数量词和单位一、个、些、种、条、元、公斤。除非任务本身与计量相关否则这些词也是过滤对象。2.3 标点符号与特殊字符严格来说标点符号通常在分词阶段或更早的文本清洗阶段就被处理掉了但一些停用词表也会包含它们以确保彻底。例如 。 、 “ ” ‘ ’ 【 】 《 》 …… —— ·。此外全角/半角空格、制表符、不可见字符等也属于需要清理的“噪音”。2.4 停用词表的设计逻辑平衡的艺术哈工大停用词表的构建体现了一种平衡艺术基于频率但不唯频率单纯剔除最高频的词如“的”是第一步但还需结合语言学知识避免误伤。例如“研究”一词在学术语料中频率可能极高但它是有信息量的实词绝不能放入通用停用词表。兼顾语法与语义如上所述它不仅过滤功能词也谨慎地过滤掉那些“太泛”的实词这是它比许多简单词表更有效的原因。领域普适性优先作为一份“通用”停用词表它倾向于保守主要过滤那些在绝大多数领域都视为噪音的词。这意味着在特定领域如医疗、法律使用时可能需要对其进行“领域化”增删。3. 停用词过滤的完整实操流程与代码解析理解了词表是什么之后我们来看怎么用。停用词过滤通常是文本预处理流水线中的一个标准环节位于分词之后向量化或特征提取之前。下面我将以Python环境为例展示一个从获取词表到完成过滤的完整、健壮的操作流程。3.1 获取与加载停用词表哈工大停用词表有很多变体和版本网络上流传的txt文件其行数可能从几百到一千多不等。一个常见的、较全的版本大约包含1200个词。你可以从GitHub、相关研究机构的开源页面或技术博客中找到它。这里假设我们已经下载了一个名为hit_stopwords.txt的文件每行一个词。def load_stopwords(file_path): 加载停用词表文件。 参数: file_path (str): 停用词表文件的路径。 返回: set: 停用词集合使用集合是为了实现O(1)时间复杂度的查找。 stopwords_set set() try: with open(file_path, r, encodingutf-8) as f: for line in f: word line.strip() # 去除首尾空白字符 if word: # 跳过空行 stopwords_set.add(word) except FileNotFoundError: print(f错误未找到文件 {file_path}) # 在实际项目中这里可以返回一个基础的停用词集合作为fallback return set([的, 了, 在, 是, 我, 有, 和, 就, 不, 人, 都, 一, 一个, 上, 也, 很, 到, 说, 要, 去, 你, 会, 着, 没有, 看, 好, 自己, 这]) except UnicodeDecodeError: print(f错误文件 {file_path} 编码可能不是UTF-8请检查。) return set() print(f成功加载停用词 {len(stopwords_set)} 个。) return stopwords_set # 使用示例 stopwords load_stopwords(hit_stopwords.txt)提示将停用词存储在set集合中而非list列表中是关键的性能优化。集合基于哈希表实现判断一个词是否在集合中的平均时间复杂度是O(1)而列表是O(n)。当处理大量文本时这点差异会累积成显著的性能差距。3.2 结合分词工具进行过滤中文处理必须先分词。这里以最常用的jieba库为例。我们需要在分词后遍历分词结果剔除那些出现在停用词集合中的词。import jieba def remove_stopwords(text, stopwords_set): 对输入文本进行分词并移除停用词。 参数: text (str): 原始中文文本。 stopwords_set (set): 停用词集合。 返回: list: 过滤后的分词列表。 str: 过滤后并用空格连接的字符串适用于某些需要字符串输入的模型。 if not text or not isinstance(text, str): return [], # 使用jieba进行精确模式分词 word_list jieba.lcut(text) # 过滤停用词 filtered_words [word for word in word_list if word not in stopwords_set and word.strip() ! ] # 返回列表和字符串两种形式 filtered_text .join(filtered_words) return filtered_words, filtered_text # 使用示例 sample_text 今天天气非常好我和朋友们都想去公园散步但是突然下起了雨。 filtered_words, filtered_text remove_stopwords(sample_text, stopwords) print(原始文本:, sample_text) print(分词结果:, jieba.lcut(sample_text)) print(过滤后词语列表:, filtered_words) print(过滤后文本字符串:, filtered_text)输出示例原始文本: 今天天气非常好我和朋友们都想去公园散步但是突然下起了雨。 分词结果: [今天, 天气, 非常, 好, , 我, 和, 朋友们, 都, 想, 去, 公园, 散步, , 但是, 突然, 下, 起, 了, 雨, 。] 过滤后词语列表: [天气, 非常, 好, 朋友们, 想, 公园, 散步, 突然, 下, 起, 雨] 过滤后文本字符串: 天气 非常 好 朋友们 想 公园 散步 突然 下 起 雨可以看到“今天”、“我”、“和”、“都”、“了”、“但是”等词被成功过滤。值得注意的是“非常”和“好”被保留了这对于情感分析可能是重要的。3.3 集成到Scikit-learn文本处理流水线中在机器学习项目中我们常使用scikit-learn的CountVectorizer或TfidfVectorizer将文本转化为特征矩阵。这些转换器内置了停用词过滤功能但通常只支持英文。我们可以通过自定义analyzer或预处理函数将中文停用词逻辑集成进去。方法一在自定义分析器analyzer中集成from sklearn.feature_extraction.text import TfidfVectorizer import jieba def chinese_tokenizer(doc, stopwords_set): 自定义分词器包含停用词过滤。 words jieba.lcut(doc) return [w for w in words if w not in stopwords_set and w.strip() ! ] # 假设我们有一个文档列表 corpus [ 自然语言处理是人工智能的重要方向。, 哈工大停用词表在中文文本预处理中很常用。, 机器学习模型需要干净的特征输入。 ] # 加载停用词 stopwords_set load_stopwords(hit_stopwords.txt) # 创建TfidfVectorizer使用自定义分词器 vectorizer TfidfVectorizer(tokenizerlambda doc: chinese_tokenizer(doc, stopwords_set), token_patternNone) # 必须设置token_pattern为None以禁用默认的正则匹配 X vectorizer.fit_transform(corpus) print(特征词列表:, vectorizer.get_feature_names_out()) print(TF-IDF矩阵:\n, X.toarray())方法二先预处理整个语料库这种方法更直观也便于调试。我们先对整个文本数据集进行分词和停用词过滤生成一个“干净”的文本列表每个元素是一个由空格连接的字符串再交给Vectorizer。def preprocess_corpus(corpus, stopwords_set): 预处理整个语料库。 processed_corpus [] for doc in corpus: _, filtered_text remove_stopwords(doc, stopwords_set) processed_corpus.append(filtered_text) return processed_corpus processed_corpus preprocess_corpus(corpus, stopwords_set) print(预处理后的语料:, processed_corpus) # 此时Vectorizer可以使用默认的基于空格的tokenizer vectorizer_simple TfidfVectorizer() # 默认tokenizer会按空格切分 X_simple vectorizer_simple.fit_transform(processed_corpus) print(特征词列表方法二:, vectorizer_simple.get_feature_names_out())方法二更推荐因为它将复杂的中文处理逻辑分词停用词与特征提取逻辑解耦代码更清晰且能利用Vectorizer的一些高级功能如ngram_range而无需重写自定义分析器。4. 高级话题停用词表的动态管理与领域适配直接使用通用的哈工大停用词表并非一劳永逸。在实际项目中尤其是垂直领域我们经常需要对它进行“微调”。这涉及到停用词表的动态管理。4.1 基于当前语料的动态停用词发现有时通用词表无法覆盖当前项目语料中的特殊“噪音词”。例如在一个关于“智能手机”的论坛语料中“手机”、“楼主”、“沙发”、“回复”等词可能频率极高且对区分不同帖子主题帮助不大。我们可以通过统计词频自动发现并添加这类词到停用词表中。from collections import Counter def discover_domain_stopwords(corpus, top_n50, base_stopwordsNone): 从语料中发现高频词作为领域停用词候选。 参数: corpus (list): 预处理后的分词列表的列表。 top_n (int): 考虑的最高频词数量。 base_stopwords (set): 基础停用词集用于排除。 返回: list: 候选领域停用词列表。 all_words [] for doc_words in corpus: # corpus 是已经分词后的列表的列表 all_words.extend(doc_words) word_freq Counter(all_words) most_common word_freq.most_common(top_n) candidate_stopwords [] for word, freq in most_common: # 可以添加规则例如长度小于2的实词或者结合词性 # 这里简单地将高频且不在基础停用词表中的词作为候选 if base_stopwords and word in base_stopwords: continue # 一个简单的启发式规则过滤掉一些高频但可能是实词的词这里需要更复杂的规则例如结合词性标注 # 此处仅为示例 candidate_stopwords.append(word) return candidate_stopwords # 示例假设我们有一个已经分词好的语料库 tokenized_corpus # tokenized_corpus [[天气, 好, 散步], [手机, 性能, 好], ...] # domain_candidates discover_domain_stopwords(tokenized_corpus, top_n30, base_stopwordsstopwords) # print(领域停用词候选:, domain_candidates)发现候选词后必须进行人工审核不能盲目加入。需要判断这些高频词是否真的对下游任务如分类、聚类没有区分度。4.2 创建领域专属停用词表增与删的策略增将动态发现的、经过人工确认的领域噪音词加入停用词表。例如在医疗文本中“患者”、“检查”、“治疗”可能过于普遍可以考虑加入在新闻评论中“网友”、“表示”、“认为”也可能需要过滤。删从通用词表中移除可能在本领域内有重要意义的词。这是更容易被忽略的一步。情感词在情感分析中“好”、“坏”、“喜欢”、“讨厌”等词绝对不能过滤。领域核心词在金融领域“涨”、“跌”、“股票”、“基金”是核心词在法律领域“原告”、“被告”、“条款”、“诉讼”是核心词。即使它们在通用语料中频率不高也绝不能放入停用词表。否定词“不”、“没”、“未”、“勿”等否定词在大多数任务中都非常重要过滤它们会严重改变语义。最佳实践是维护一个项目专用的停用词文件。它的生成方式可以是# my_project_stopwords.txt # 第一部分继承自哈工大基础词表手动删除其中对本领域重要的词 的 地 得 ... # 删除了“不”、“没”等否定词以及“研究”等本领域核心词 # 第二部分新增的领域噪音词 据悉 据了解 报道称 本公司 贵单位 ...4.3 停用词过滤的副作用与边界情况停用词过滤并非没有代价过度过滤或错误过滤会损害模型性能。成语与固定搭配例如“了如指掌”中的“了”如果被过滤剩下的“如指掌”就失去了意义。好在主流分词器如jieba通常能将“了如指掌”作为一个整体词切分出来从而避免被过滤。但仍有边界情况需要关注分词质量。短文本信息损失在处理非常短的文本如微博、搜索查询、商品标题时每一个词都可能携带关键信息。此时停用词过滤需要极其谨慎甚至可以考虑不过滤或者只过滤极少数最高频的虚词如“的”。句法分析任务对于需要理解句子结构如依存句法分析、语义角色标注的任务停用词尤其是介词、连词是重要的句法标记绝对不能过滤。短语挖掘与N-Gram特征当使用Bi-gram或Tri-gram作为特征时停用词可能是连接两个实词的关键。例如“数据的质量”过滤后变成“数据 质量”失去了“的”所表达的所属关系。在这种情况下一种策略是先在包含停用词的文本上生成N-Gram然后再过滤掉那些完全由停用词组成的Gram或者包含过多停用词的Gram。5. 性能优化与工程化考量当处理海量文本如千万级文档时停用词过滤这个环节的性能也需要被仔细考量。5.1 选择高效的数据结构与查找方法如前所述使用set是基础。确保你的停用词集合在内存中避免每次过滤都去读文件。如果停用词表巨大例如融合了多个词表可以考虑使用Bloom Filter布隆过滤器这种概率型数据结构。它可以用较小的内存空间快速判断一个元素“一定不在集合中”或“可能在集合中”。对于停用词过滤这种可以容忍极低概率误判把非停用词判为停用词的场景Bloom Filter 能极大提升性能。Python中有pybloom-live等库可以实现。5.2 向量化过程中的集成过滤在scikit-learn的CountVectorizer或TfidfVectorizer中设置stop_words参数为自定义的列表是最直接的方式。但要注意其内置的过滤发生在tokenization之后对于中文你需要先确保分词正确。更高效的做法是像3.3节的方法二那样预处理阶段一次性完成分词和停用词过滤然后将空格分隔的字符串交给Vectorizer。这样Vectorizer只需要做简单的空格切分速度最快。5.3 并行处理加速对于大规模语料可以使用并行计算框架来加速预处理阶段包括停用词过滤。Python的multiprocessing库或joblib库可以方便地将预处理任务分配到多个CPU核心上。from joblib import Parallel, delayed import jieba def process_single_doc(doc, stopwords_set): words jieba.lcut(doc) return .join([w for w in words if w not in stopwords_set]) # 假设 docs 是一个包含大量文档的列表 n_jobs -1 # 使用所有CPU核心 processed_docs Parallel(n_jobsn_jobs)( delayed(process_single_doc)(doc, stopwords) for doc in large_corpus )6. 效果评估如何验证停用词表是否有效使用了停用词表如何证明它确实提升了模型效果而不是帮了倒忙我们不能只凭感觉需要进行科学的评估。6.1 基于下游任务的评估这是最直接、最可靠的方法。选择一个明确的下游任务如文本分类、聚类并准备验证集/测试集。基准模型在不使用任何停用词过滤的情况下训练一个模型如使用TF-IDF特征和逻辑回归分类器记录其在测试集上的准确率、F1分数等指标。实验模型使用哈工大停用词表进行过滤用同样的流程训练和评估模型。对比分析如果实验模型指标显著优于基准模型说明停用词过滤有效去除了噪音。如果指标持平或略有下降需要检查是否过滤掉了重要特征如领域核心词或否定词。如果指标显著下降很可能停用词表不适合当前领域需要按第4章的方法进行调整。6.2 特征维度与稀疏性分析停用词过滤会直接减少特征空间的维度即词汇表的大小。观察维度变化比较过滤前后Vectorizer.get_feature_names_out()的长度。通常会有显著下降例如从2万个词降到1.5万个。分析特征矩阵过滤后特征矩阵如TF-IDF矩阵的稀疏性可能会发生变化。更少的特征意味着矩阵更小训练和预测速度更快内存占用更少。这是停用词过滤带来的直接工程收益。检查重要特征使用模型如逻辑回归的coef_属性或基于树模型的特征重要性查看过滤后哪些词成为了最重要的特征。确保这些词是符合直觉的、有意义的实词而不是“的”、“了”这样的虚词。6.3 可视化辅助判断词云对比为同一份文档集生成过滤前和过滤后的词云。一个有效的过滤应该能让词云中的主题词如“算法”、“数据”、“用户”更加突出而让“的”、“是”、“在”等词消失。高频词列表对比打印过滤前后最高频的20个词。过滤后列表应该从虚词主导变为实词主导。在我经历的一个电商评论情感分析项目中最初未使用停用词表分类器总是倾向于将含有“很”、“非常”、“特别”等程度副词的评论判为正面但其中混杂了很多像“很差”、“非常糟糕”的负面评论。原因是“很”这个词本身由于频率过高成为了一个强特征干扰了模型。将“很”、“非常”等加入停用词表同时保留“好”、“差”、“棒”、“烂”等情感词后模型学会了更关注情感词本身准确率提升了近3个百分点。这个案例说明停用词过滤有时需要结合任务目标进行精细化的调整而不是简单地套用通用列表。停用词表是NLP工程中的一把利器哈工大停用词表为我们提供了一个优秀的起点。但记住没有放之四海而皆准的“完美”词表。理解其设计原理掌握其使用方法并根据实际数据和任务进行灵活的调整与验证才是用好这把利器的关键。在实际操作中我习惯为每个新项目都建立一个独立的停用词管理脚本方便增删查改和版本控制这比在代码里硬编码一个列表要稳健得多。

相关新闻