
简介这是一套面向Python中文自然语言处理学习者与开发者的实战型教程资源覆盖从环境搭建到深度学习应用的完整知识链路适合具备Python基础、希望系统掌握中文NLP技术的初学者与进阶开发者。资源包共42个文件以txt语料与词典、json模型与分类数据、csv评论与留言数据集为主另含jpg示意图、pkl模型文件、utf8词典及ipynb实战笔记压缩包约53.58MB。内容围绕jieba分词、词性标注、命名实体识别、情感分析与LDA主题建模展开并延伸至TensorFlow、PyTorch下的RNN、LSTM与BERT等模型实践配套金庸小说语料、BosonNLP情感词典、垃圾邮件与新闻分类数据可支撑聊天机器人、自动摘要、问答系统等场景的动手练习。目前已有10699人学习下载适合作为中文NLP入门到实战的参考素材。1. 中文 NLP 到底难在哪从一次分词翻车说起很多人第一次用 Python 做中文自然语言处理都会经历同一个场景拿一段中文丢进英文那套split()里结果整段话被当成一个 token模型什么都学不到。中文没有天然空格分隔这一条就足以让所有英文 NLP 的默认流程失效。中文自然语言处理基础与实战要解决的核心问题就是把「字 → 词 → 句 → 语义」这条链路在 Python 里搭通并且每一步都能解释清楚为什么这么做。这套东西适合谁如果你手上有中文评论、工单、聊天记录、商品标题想做出分类、关键词提取、情感判断、相似度匹配这类功能那这条路径就是绕不开的基本功。它不需要 GPU 集群一台普通笔记本加几个常用库就能跑通全流程。下面我按自己实际做项目的顺序把分词、向量化、关键词、分类、避坑和进阶技巧一层层拆开每一步都给能直接抄的代码和参数说明。2. 中文分词与预处理把句子切成模型能吃的粒度中文 NLP 的第一道工序永远是分词。词是中文里承载语义的最小单位切错了后面全错。这一章先把分词工具选型讲清楚再落到清洗、停用词、词性标注这些预处理动作上。2.1 分词工具怎么选jieba、pkuseg 与 HanLP 的取舍常见的中文分词方案有三类。第一类是基于词典加动态规划代表是 jieba优点是快、零依赖、开箱即用缺点是遇到新词和歧义切分容易翻车。第二类是基于序列标注模型的分词器比如 pkuseg它在特定领域新闻、医药、旅游上准确率更高但需要下载对应领域的模型启动稍慢。第三类是 HanLP 这种全家桶分词只是它的一小部分还带命名实体识别、依存句法适合做完整流水线代价是依赖较重。我一般的选型逻辑是做原型、做小工具、数据量在百万级以下直接用 jieba如果领域词特别多比如医疗、法律先用 jieba 跑一版把错误切分的高频词补进自定义词典往往比换工具更划算。真正需要高精度再上 pkuseg。import jieba # 加载自定义词典每行一个词可带词频和词性 jieba.load_userdict(user_dict.txt) text 这个图像处理Demo的分割效果不太稳定 # 精确模式最常用试图把句子最合理地切开 seg_default jieba.lcut(text) # 全模式把所有可能成词的都切出来适合做召回 seg_full jieba.lcut(text, cut_allTrue) # 搜索引擎模式在精确模式基础上对长词再切适合建索引 seg_search jieba.lcut_for_search(text) print(seg_default) print(seg_full) print(seg_search)这段代码里三个模式的区别是实战中最容易搞混的点。lcut默认走精确模式输出的是最可能的切分cut_allTrue会把「图像」「图像处理」「处理」这类重叠词全吐出来召回高但噪声大lcut_for_search介于两者之间做倒排索引时用它。load_userdict加载的词典格式是「词语 词频 词性」词频可省略但补上词频能让动态规划更倾向切出这个词。2.2 清洗、停用词与词性过滤的完整流水线分词只是开始原始文本里还有 HTML 标签、URL、表情、全半角混排。这些不处理后面统计出来的高频词全是噪声。清洗的原则是能保留语义的尽量保留纯符号和格式噪声一律干掉。import re import jieba.posseg as pseg def clean_text(text): # 去掉 HTML 标签 text re.sub(r[^], , text) # 去掉 URL text re.sub(rhttps?://\S|www\.\S, , text) # 全角转半角 text .join( chr(ord(ch) - 0xFEE0) if 0xFF01 ord(ch) 0xFF5E else ch for ch in text ) # 只保留中文、英文、数字和基本标点 text re.sub(r[^\u4e00-\u9fa5a-zA-Z0-9。、], , text) return text.strip() def load_stopwords(path): with open(path, encodingutf-8) as f: return {line.strip() for line in f if line.strip()} stopwords load_stopwords(stopwords.txt) def tokenize(text, allow_pos(n, v, a)): text clean_text(text) words [] for word, flag in pseg.cut(text): # 过滤停用词、单字、以及不在允许词性里的词 if word in stopwords or len(word) 2: continue if allow_pos and flag not in allow_pos: continue words.append(word) return words print(tokenize(这个图像处理Demo的分割效果不太稳定b准确率/b只有80%))这里的关键参数是allow_pos。中文里名词、动词、形容词n/v/a承载主要语义副词、连词、助词基本是噪声。只保留这三类词性能让后续的关键词和分类特征干净很多。但要注意情感分析里「不」「很」「太」这类词其实有语义如果做情感任务allow_pos里要加上副词d否则「不好」和「好」会被处理成一样。这是很多人第一次做情感分类准确率上不去的隐藏原因。停用词表不要迷信网上的通用版本通用表里往往包含「系统」「问题」这类在你业务里其实是关键词的词。我的习惯是先跑一遍词频把 Top 200 里明显无意义的词手动加进停用词表比直接用现成的效果好。3. 文本向量化从词袋到词向量的三条路线分词之后文本还是一串词模型要的是数字。向量化这一步决定了后面所有任务的上限。这一章讲清楚词袋、TF-IDF、词向量三条路线的适用边界以及中文场景下容易忽略的细节。3.1 词袋与 TF-IDF小数据量下的稳妥选择词袋模型把每篇文档表示成一个「词是否出现」的向量TF-IDF 在此基础上给词加权一个词在当前文档出现越多、在整个语料出现越少权重越高。中文做 TF-IDF 有个坑就是必须先把分词结果用空格拼回去因为 sklearn 默认按空格切词。from sklearn.feature_extraction.text import TfidfVectorizer # 假设 corpus 是已经分好词、用空格连接的文档列表 corpus [ 图像 处理 分割 效果 稳定, 文本 分类 模型 准确率 高, 图像 分类 模型 效果 稳定, ] vectorizer TfidfVectorizer( token_patternr(?u)\b\w\b, # 匹配中文词 max_df0.9, # 出现在 90% 以上文档的词丢掉 min_df2, # 至少出现在 2 篇文档里才保留 ngram_range(1, 2), # 同时考虑单字词和双词组合 ) X vectorizer.fit_transform(corpus) print(vectorizer.get_feature_names_out()) print(X.toarray())max_df和min_df是控制特征规模的两个阀门。max_df0.9过滤掉几乎每篇都有的词等价于自动停用词min_df2过滤掉只出现一次的稀有词降低维度。ngram_range(1,2)让「图像 处理」这种双词组合也成为一个特征对中文特别有用因为很多语义在双词组合里才完整。代价是特征数会膨胀数据量小的时候要谨慎否则容易过拟合。TF-IDF 的优点是快、可解释、不需要训练几千条数据就能出效果。缺点是它完全丢失词序和语义「图像」和「图片」在它眼里是两个毫不相关的维度。所以它适合做基线不适合做需要理解语义的任务。3.2 用预训练词向量处理中文语义相似要解决「图像」和「图片」语义相近的问题就得用词向量。中文词向量常见做法是用大规模语料训练 Word2Vec 或 FastText或者直接加载开源的预训练向量。Word2Vec 的核心假设是「上下文相似的词语义相似」训练出来每个词是一个固定维度的稠密向量。from gensim.models import Word2Vec # sentences 是分词后的列表的列表 sentences [ [图像, 处理, 分割, 效果], [文本, 分类, 模型, 准确率], [图像, 分类, 模型, 效果], ] model Word2Vec( sentences, vector_size100, # 向量维度常用 100~300 window5, # 上下文窗口中文一般 3~5 min_count1, # 词频低于此值的词丢弃 sg1, # 1 表示 Skip-gram0 表示 CBOW epochs20, ) # 查看与「图像」最相近的词 print(model.wv.most_similar(图像, topn3)) # 获取一个词的向量 print(model.wv[图像].shape)sg1用 Skip-gram它对低频词更友好但训练慢sg0用 CBOW快但低频词效果差。window在中文里不宜太大因为中文句子结构紧凑窗口开到 10 会把不相关的词也拉进来。vector_size不是越大越好100 到 300 之间足够再大在小语料上反而学不好。这里必须提醒上面这段代码只是演示 API真正要得到有意义的词向量语料至少要到百万词级别。小语料上训出来的向量基本是玄学相似词结果没法看。实际项目里我一般直接加载开源预训练中文词向量或者用 BERT 这类上下文相关的模型而不是自己从零训。3.3 句向量与文本相似度的落地写法词向量是词的但分类和检索要的是整句或整篇的向量。最简单的做法是把句子里所有词的向量取平均这叫平均词向量。它快但丢失词序遇到「狗咬人」和「人咬狗」会得到一样的结果。更好的做法是用 Sentence-BERT 这类句向量模型但那是后面进阶章的内容。import numpy as np def sentence_vector(sentence, model): vectors [model.wv[w] for w in sentence if w in model.wv] if not vectors: return np.zeros(model.vector_size) return np.mean(vectors, axis0) def cosine_sim(a, b): return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b) 1e-8) s1 sentence_vector([图像, 处理, 效果], model) s2 sentence_vector([图像, 分割, 效果], model) print(cosine_sim(s1, s2))1e-8是防止除零的后悔药向量全零时没有它程序会直接崩。平均词向量适合做快速召回和粗排不适合做精排。如果你的场景对语义精度要求高这一步就该换成句向量模型而不是在平均词向量上反复调参。4. 关键词提取与文本分类两个最常落地的任务向量化之后最常见的两个落地任务是关键词提取和文本分类。前者用于摘要、标签、检索后者用于情感、意图、工单路由。这一章把这两个任务的实现和调参讲透。4.1 TF-IDF 与 TextRank 提取关键词的差异关键词提取有两条主流路线。TF-IDF 路线把每个词在单篇文档的 TF-IDF 值排序取 Top N。TextRank 路线把词建成图用 PageRank 的思想迭代算重要性它更看重词与词之间的共现关系。import jieba.analyse text 图像处理中的分割算法直接影响最终效果分割算法的稳定性是图像处理的核心指标。 # TF-IDF 提取需要 idf 语料这里用内置的 keywords_tfidf jieba.analyse.extract_tags(text, topK5, withWeightTrue) # TextRank 提取不需要外部语料 keywords_textrank jieba.analyse.textrank( text, topK5, withWeightTrue, allowPOS(n, v) ) print(TF-IDF:, keywords_tfidf) print(TextRank:, keywords_textrank)两者的差别在实战里很明显。TF-IDF 依赖 IDF 语料如果你的领域和内置语料差很远权重会失真这时候要么换自己的语料算 IDF要么直接用 TextRank。TextRank 不依赖外部语料但它对长文本更友好短文本上因为共现关系稀疏效果会打折。allowPOS在 TextRank 里建议只留名词和动词把形容词放进来容易提出一堆「稳定」「重要」这种没区分度的词。4.2 用朴素贝叶斯和线性模型做中文文本分类文本分类的基线模型我一般从朴素贝叶斯和线性 SVM 开始。它们训练快、对小数据友好、可解释。流程是分词 → TF-IDF 向量化 → 分类器 → 评估。from sklearn.pipeline import Pipeline from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.naive_bayes import MultinomialNB from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report # texts 是原始中文文本列表labels 是对应标签 texts [图像分割效果很好, 分类模型准确率低, 分割算法稳定, 模型效果很差] labels [1, 0, 1, 0] X_train, X_test, y_train, y_test train_test_split( texts, labels, test_size0.25, random_state42, stratifylabels ) pipe Pipeline([ (tfidf, TfidfVectorizer( tokenizerlambda x: jieba.lcut(x), # 用 jieba 分词 token_patternNone, max_df0.9, min_df1, )), (clf, LogisticRegression(max_iter1000, C1.0)), ]) pipe.fit(X_train, y_train) pred pipe.predict(X_test) print(classification_report(y_test, pred))这里有几个参数值得说。tokenizer直接传 jieba 的lcut这样 TF-IDF 内部就会用中文分词不用手动拼空格。token_patternNone是必须的否则 sklearn 会用它默认的正则去切和自定义 tokenizer 冲突。C是逻辑回归的正则强度越小正则越强过拟合时调小它。stratifylabels保证训练测试集类别比例一致小数据上不加这个评估结果会飘。朴素贝叶斯MultinomialNB适合特征独立假设成立、数据量小的场景训练极快逻辑回归适合需要概率输出、特征有相关性的场景。两者都建议先跑一版看基线再决定要不要上深度学习。4.3 类别不平衡与评估指标的选择中文文本分类里类别不平衡是常态比如工单里 90% 是正常、10% 是异常。这时候准确率accuracy会骗人全预测成正常也有 90%。必须看精确率、召回率和 F1。from sklearn.metrics import f1_score from sklearn.utils.class_weight import compute_class_weight import numpy as np # 自动计算类别权重传给分类器 classes np.unique(y_train) weights compute_class_weight(balanced, classesclasses, yy_train) class_weight dict(zip(classes, weights)) clf LogisticRegression(max_iter1000, class_weightclass_weight)class_weightbalanced会让少数类样本的损失权重变大模型更关注少数类。代价是精确率可能下降因为模型变得更激进。具体调的时候先看业务更怕漏报还是误报怕漏报就提召回怕误报就提精确率然后据此调阈值或权重。这一步没有标准答案必须结合业务。5. 避坑与排查中文 NLP 里最容易翻车的五件事这一章是我踩过的坑里最典型的五个每个都按「现象 → 原因 → 解决」写照着排查能省不少时间。5.1 分词结果和预期不一致现象明明词典里有「图像处理」切出来却是「图像」「处理」。原因jieba 的动态规划会综合词频和词典自定义词如果词频太低可能竞争不过两个高频短词。解决在user_dict.txt里给这个词加一个较高的词频比如「图像处理 100000 n」强制它被切出来。另外确认词典文件编码是 UTF-8编码错了词典会静默失效。5.2 TF-IDF 特征维度爆炸现象几万条文本TF-IDF 出来几十万维训练慢还过拟合。原因min_df设成 1所有只出现一次的词都进了特征。解决把min_df提到 2 或 3max_df设到 0.8 到 0.9再配合max_features限制总维度比如max_features50000。中文里大量低频词是噪声砍掉它们对效果几乎无损。5.3 词向量相似词结果离谱现象most_similar(图像)返回一堆不相关的词。原因训练语料太小或者min_count设太低导致低频词向量没学好。解决语料至少百万词级别min_count提到 5 以上epochs加到 20 以上。如果语料实在不够别自己训直接加载开源预训练向量。5.4 分类模型在测试集上很好上线就崩现象离线 F1 0.9线上效果一塌糊涂。原因训练数据和线上数据的分布不一致比如训练用的是清洗过的文本线上是原始文本里面混着表情、URL、错别字。解决训练时的预处理流程必须和线上完全一致最好把预处理封装成一个函数训练和推理都调它。另外留一部分真实线上数据做验证别只用标注数据。5.5 停用词表把关键信息删了现象情感分类里「不」「没」被当停用词删掉导致「不好」和「好」分不开。原因通用停用词表是为检索设计的不是为情感任务设计的。解决按任务定制停用词表。情感任务保留否定词和程度副词检索任务才删它们。这个坑很隐蔽因为模型不会报错只是效果差很多人调半天参数都想不到是停用词的问题。6. 进阶技巧用句向量模型把语义相似度做扎实前面讲的 TF-IDF 和平均词向量在语义相似度上都有天花板。真正要把「这句话和那句话是不是一个意思」做准得上句向量模型。常见做法是用预训练的中文句向量模型把整句编码成一个固定维度向量再算余弦相似度。它比平均词向量强的地方在于它见过大量句子对知道词序和上下文怎么影响语义。from sentence_transformers import SentenceTransformer import numpy as np # 加载中文句向量模型具体模型名按你本地已有的选 model SentenceTransformer(paraphrase-multilingual-MiniLM-L12-v2) sentences [ 图像分割的效果很稳定, 图片切分的表现很可靠, 今天天气不错, ] embeddings model.encode(sentences, normalize_embeddingsTrue) # 归一化后点积就等于余弦相似度 sim_matrix np.dot(embeddings, embeddings.T) print(sim_matrix)normalize_embeddingsTrue是关键归一化之后点积直接等于余弦相似度省去手动除法也避免除零。句向量模型的选择上多语言模型对中文支持尚可但如果你的场景全是中文优先选在中文语料上微调过的模型。模型大小和速度要权衡MiniLM 这类小模型推理快适合线上大模型精度高但慢适合离线。句向量落地时有个容易忽略的点相似度阈值不能拍脑袋定。正确做法是拿一批标注好的句子对画出相似度分布看正例和负例的分界在哪再定阈值。我见过太多人直接设 0.8结果要么召回太低要么误匹配一堆。这个阈值和你的业务容忍度强相关没有通用值。另一个进阶方向是把句向量接一个轻量分类头做小样本分类。比如你只有几十条标注数据用句向量做特征接一个逻辑回归效果往往比从头训 BERT 好因为句向量已经编码了通用语义。这个思路在标注数据稀缺时特别实用。最后说个我自己的习惯任何中文 NLP 项目我都会先写一个最小的端到端脚本从原始文本一路跑到评估指标哪怕效果很差。这个脚本是后面所有优化的基准线也是排查问题的黑匣子。没有它调参就是盲人摸象。分词、向量化、分类这三步里任何一步换了实现都回到这个脚本上重跑一遍对比而不是凭感觉说「应该会更好」。这个习惯帮我省下的时间比任何调参技巧都多。希望帮到你。本文还有配套的精品资源点击获取