自然语言处理
📘 NLP 实训技术讲义
—— 从代码到原理,读懂自然语言处理
1. 基础分析 (Basic Analysis)
核心任务:中文分词 & 词性标注
代码对应:jieba.lcut(),jieba.posseg
🧐 它是什么?
计算机不认识“句子”,它只认识“词”。英文单词之间有空格,计算机天然知道哪里是一个词。但中文是一连串的字符(如“南京市长江大桥”),计算机不知道是“南京市/长江/大桥”还是“南京/市长/江大桥”。分词就是把连续的中文序列切分成有意义的词语。
💡 通俗比喻
想象你有一串没有隔开的珍珠项链。分词就是要把它们剪开,变成一颗颗独立的珍珠,这样你才能数清楚有多少颗,或者把红色的挑出来。
⚙️ 技术原理
- 词典匹配:系统里有一个巨大的词典。它会拿着词典去文本里“撞”,能匹配上的就是一个词。
- 最大概率路径:有时候一句话有多种切分法(如“结合/成”vs“结/合成”)。算法会计算哪种切分在整个语言环境中出现的概率最大,就选哪种。
- 停用词过滤:像“的”、“了”、“啊”这种词,虽然常见但没实际意义,分析时通常要扔掉,这叫去噪。
🌍 应用场景
- 搜索引擎(你搜“手机”,它能找到包含“智能手机”的网页)
- 输入法联想
代码示例
# 1. 基础分析.py
import jieba
import jieba.posseg as pseg
text = "南京市长江大桥是一座著名的旅游景点"
print(f"原文:{text}\n")
# 1. 精确模式分词
words = jieba.lcut(text)
print(f"【分词结果】:{' / '.join(words)}")
# 2. 词性标注 (POS Tagging)
words_pos = pseg.cut(text)
print("\n【词性标注】:")
for word, flag in words_pos:
print(f"{word} ({flag})")
# flag 说明:n=名词,v=动词,ns=地名,nt=机构名等
2. 文本相似度 (Text Similarity)
核心任务:计算两段文本像不像
代码对应:TfidfVectorizer,cosine_similarity
🧐 它是什么?
判断两句话的意思是否接近。注意,不是看字是否一样,而是看内容是否一样。“我喜欢这个产品”和“这个产品我很满意”,字不同,但意思很像。
💡 通俗比喻
指纹比对。我们把每篇文章都变成一根“指纹”(向量)。如果两根指纹的纹路方向几乎一致,说明它们很像;如果方向垂直,说明完全没关系。
⚙️ 技术原理
- TF-IDF 向量化:把文字变成数字。
- TF (词频):一个词在这篇文章出现越多,越重要。
- IDF (逆文档频率):一个词在所有文章里都出现(如“的”),它就不重要;如果一个词很稀有(如“量子力学”),它就很能代表这篇文章。
- 余弦相似度:在数学空间里,计算两个向量夹角的余弦值。
- 值为 1:完全相同。
- 值为 0:毫无关系。
- 值为 -1:完全相反。
🌍 应用场景
- 论文查重系统
- 新闻推荐(看了这篇,推荐相似的那篇)
- 抄袭检测
代码示例
# 2. 文本相似度.py
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
import jieba
# 自定义分词函数,确保 TF-IDF 能识别中文词
def chinese_tokenizer(text):
return list(jieba.cut(text))
text1 = "这个手机质量很好,电池耐用"
text2 = "这款手机品质不错,续航能力强"
text3 = "今天天气真晴朗,适合出去玩"
docs = [text1, text2, text3]
# 1. 向量化 (TF-IDF)
# token_pattern=None 允许使用自定义 tokenizer
vectorizer = TfidfVectorizer(tokenizer=chinese_tokenizer, token_pattern=None)
tfidf_matrix = vectorizer.fit_transform(docs)
# 2. 计算余弦相似度
similarity_1_2 = cosine_similarity(tfidf_matrix[0:1], tfidf_matrix[1:2])[0][0]
similarity_1_3 = cosine_similarity(tfidf_matrix[0:1], tfidf_matrix[2:3])[0][0]
print(f"文本 1: {text1}")
print(f"文本 2: {text2}")
print(f"文本 3: {text3}\n")
print(f"【相似度】文本 1 vs 文本 2 (语义相近): {similarity_1_2:.4f}")
print(f"【相似度】文本 1 vs 文本 3 (语义无关): {similarity_1_3:.4f}")
3. 自动摘要 (Auto Summarization)
核心任务:把长文变短文,保留核心意思
代码对应:SnowNLP().summary()
🧐 它是什么?
让机器自动读一篇长文章,然后帮你写出一个简短的摘要。我们这里用的是抽取式摘要,即从原文中直接挑出最重要的几个句子拼在一起。
💡 通俗比喻
划重点。老师复习时不会把书重讲一遍,而是告诉你“第 3 页、第 15 页、第 20 页这几段是考试重点”。自动摘要就是机器在帮你“划重点”。
⚙️ 技术原理
- TextRank 算法:灵感来自谷歌的网页排名(PageRank)。
- 句子投票:把每个句子看作一个节点。如果句子 A 和句子 B 有很多相同的词,它们之间就有一条连线。
- 重要性排序:被很多其他句子“连接”的句子,通常包含了核心信息,得分最高,就被选入摘要。
🌍 应用场景
- 新闻快讯生成
- 法律文档快速审阅
- 会议纪要自动生成
代码示例
# 3. 自动摘要.py
from snownlp import SnowNLP
text = """
今天我去了一家非常不错的餐厅。餐厅的环境非常优雅,装修很有特色。
菜品味道也很棒,特别是招牌红烧肉,入口即化。
但是服务有点慢,等了半个小时才上菜。
总体来说,这是一家值得推荐的餐厅,下次还会带朋友来。
"""
print(f"原文长度:{len(text)} 字\n")
print(f"原文:{text}\n")
# 1. 初始化 SnowNLP
s = SnowNLP(text)
# 2. 生成摘要 (参数 3 表示提取 3 个句子)
summary = s.summary(3)
print("【自动摘要】:")
for i, sentence in enumerate(summary, 1):
print(f"{i}. {sentence}")
4. 多类别分类 (Multi-class Classification)
核心任务:把文本分到具体的类别里(不仅是好/坏)
代码对应:MultinomialNB(朴素贝叶斯)
🧐 它是什么?
情感分析通常只有“好/坏”两类。但实际业务中,我们需要更细的分类。比如用户反馈,是“投诉”?“咨询”?还是“表扬”?这就是多类别分类。
💡 通俗比喻
邮局分拣员。面前有一堆信,分拣员看一眼内容:
- 看到“坏了”、“退货” → 扔进投诉筐。
- 看到“请问”、“怎么” → 扔进咨询筐。
- 看到“谢谢”、“点赞” → 扔进表扬筐。 机器就是在模仿这个分拣员。
⚙️ 技术原理
- 训练 (Training):先给机器看几千封已经分好类的信(训练数据),让它记住“投诉”类的信通常包含哪些词。
- 朴素贝叶斯:一种基于概率的算法。它计算:“出现‘退货’这个词的情况下,这封信属于‘投诉’类的概率是多少?”
- 预测:新来一封信,计算它属于各个类别的概率,选概率最大的那个。
🌍 应用场景
- 客服工单自动路由(投诉转人工,咨询转机器人)
- 垃圾邮件分类(广告、诈骗、正常)
- 新闻分类(体育、财经、娱乐)
代码示例
# 4. 多类别分类.py
from sklearn.naive_bayes import MultinomialNB
from sklearn.pipeline import make_pipeline
from sklearn.feature_extraction.text import TfidfVectorizer
import jieba
# 1. 准备训练数据
train_texts = [
"东西坏了,我要退货", "质量太差,我要投诉", "客服不理人,体验很差", # 投诉
"请问怎么使用", "有保修吗", "什么时候发货", # 咨询
"非常好的产品", "谢谢客服", "五星好评", # 表扬
]
train_labels = [
"投诉", "投诉", "投诉",
"咨询", "咨询", "咨询",
"表扬", "表扬", "表扬"
]
# 2. 构建模型管道 (TF-IDF + 朴素贝叶斯)
def tokenizer(text):
return list(jieba.cut(text))
model = make_pipeline(
TfidfVectorizer(tokenizer=tokenizer, token_pattern=None),
MultinomialNB()
)
# 3. 训练模型
model.fit(train_texts, train_labels)
# 4. 预测新文本
test_texts = ["产品坏了怎么赔", "这个怎么用啊", "太满意了"]
print("【意图识别结果】:")
for text in test_texts:
prediction = model.predict([text])[0]
print(f"文本:'{text}' -> 意图:{prediction}")
5. 情感趋势 (Sentiment Trend)
核心任务:分析长文中情感的变化过程
代码对应:句子分割 +SnowNLP().sentiments循环
🧐 它是什么?
一篇文章的情感不是单一的。比如:“刚开始很满意(正),但是后来发现有问题(负),联系客服后解决了(正)”。情感趋势分析就是把文章切开,看情感是如何波动的。
💡 通俗比喻
心电图。人的心情不是一条直线,而是有起有伏。情感趋势就是给文本画一张“心情心电图”,看它是先扬后抑,还是逐渐好转。
⚙️ 技术原理
- 句子分割:用标点符号(。!?)把文章切成多个短句。
- 逐句打分:对每个短句单独进行情感分析(0~1 分)。
- 连线成图:把每个句子的分数按顺序连起来,形成折线图。
🌍 应用场景
- 深度影评分析(剧情哪里高潮,哪里烂尾)
- 用户旅程体验分析(购买流程中哪里让用户生气了)
- 舆情监控(事件发酵过程中公众情绪变化)
代码示例
# 5. 情感趋势.py
from snownlp import SnowNLP
import matplotlib.pyplot as plt
import re
# 设置 matplotlib 支持中文显示
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False
text = "刚开始很满意。后来发现有问题。联系客服后解决了。现在觉得还不错。"
# 1. 句子分割
sentences = re.split(r'[。!?!?]', text)
sentences = [s.strip() for s in sentences if s.strip()]
# 2. 逐句情感打分
scores = []
for sent in sentences:
s = SnowNLP(sent)
scores.append(s.sentiments)
print("【句子情感得分】:")
for i, (sent, score) in enumerate(zip(sentences, scores)):
status = "正面" if score > 0.5 else "负面"
print(f"{i+1}. [{status}] {score:.2f} - {sent}")
# 3. 绘制趋势图
plt.figure(figsize=(8, 4))
plt.plot(range(1, len(scores)+1), scores, marker='o', linestyle='-', color='b')
plt.axhline(y=0.5, color='r', linestyle='--', label='中性线 (0.5)')
plt.title('文本情感趋势分析')
plt.xlabel('句子顺序')
plt.ylabel('情感得分 (0-1)')
plt.ylim(0, 1)
plt.grid(True, alpha=0.3)
plt.legend()
plt.savefig('sentiment_trend.png') # 保存为图片
print("\n✅ 情感趋势图已保存为 'sentiment_trend.png'")
# plt.show() # 如果在本地运行可取消注释
6. 增强实体识别 (Enhanced NER)
核心任务:找出文本里的“专有名词”
代码对应:jieba.posseg, 词性标签筛选 (nr,ns,nt)
🧐 它是什么?
从一堆文字里,把具体的人名、地名、公司名抠出来。这是构建知识图谱的第一步。
💡 通俗比喻
荧光笔标记。读书时,你用不同颜色的荧光笔:
- 黄色画人名(马云)
- 绿色画地名(杭州)
- 蓝色画公司名(阿里巴巴) 实体识别就是机器在自动帮你画这些荧光笔。
⚙️ 技术原理
- 词性标注 (POS Tagging):先给每个词贴标签。比如“北京”后面标
ns(地名),“腾讯”后面标nt(机构名)。 - 规则提取:代码里写了规则:
if flag.startswith('nr')(如果是人名标签),就把它存下来。 - 上下文依赖:高级的 NER 会看上下文,比如“苹果”在“吃苹果”里是食物,在“苹果公司”里是组织。
🌍 应用场景
- 简历自动解析(提取姓名、电话、学校)
- 合同关键信息提取(甲方、乙方、金额)
- 智能助手(“帮我定一张去北京的票” -> 识别目的地)
代码示例
# 6. 增强实体识别.py
import jieba.posseg as pseg
text = "马云在杭州创立了阿里巴巴集团,后来他又参观了清华大学。"
print(f"原文:{text}\n")
# 1. 词性标注
words = pseg.cut(text)
# 2. 实体分类存储
entities = {
"人名 (nr)": [],
"地名 (ns)": [],
"机构名 (nt)": []
}
for word, flag in words:
if flag.startswith('nr'):
entities["人名 (nr)"].append(word)
elif flag.startswith('ns'):
entities["地名 (ns)"].append(word)
elif flag.startswith('nt'):
entities["机构名 (nt)"].append(word)
# 3. 输出结果
print("【识别到的实体】:")
for type_name, items in entities.items():
if items:
print(f"{type_name}: {', '.join(items)}")
else:
print(f"{type_name}: 无")
7. 词云可视化 (Word Cloud Visualization)
核心任务:把词频变成图形
代码对应:wordcloud.WordCloud,pyecharts
🧐 它是什么?
一种视觉展示方式。出现次数越多的词,在图片里字体越大、越显眼。让人一眼就能看出这段文字在讲什么。
💡 通俗比喻
人群合影。在一个大合影里,谁的声音最大(出现频率最高),谁的头像就放得最大。你一眼看过去,最大的几个头像就是这场合的主角。
⚙️ 技术原理
- 词频统计:数每个词出现了多少次。
- 权重映射:把次数映射为字体大小(比如出现 10 次是 10 号字,100 次是 50 号字)。
- 布局算法:像玩俄罗斯方块一样,把这些大小不一的词挤在一个形状里,尽量不重叠,且美观。
🌍 应用场景
- 政府工作报告解读
- 微博热搜话题展示
- 用户反馈关键词概览
代码示例
# 7. 词云可视化.py
from wordcloud import WordCloud
import matplotlib.pyplot as plt
import os
import jieba
# 1. 准备文本
text = "Python 编程 语言 非常 流行 数据 分析 人工智能 机器学习 深度 学习 网络 开发 软件 工程 代码 程序 算法 结构"
# 2. 中文分词
words = " ".join(jieba.cut(text))
# 3. 自动查找中文字体 (防止乱码)
def get_font_path():
paths = [
"simhei.ttf", # 当前目录
"C:/Windows/Fonts/simhei.ttf", # Windows
"/System/Library/Fonts/STHeiti Light.ttc", # Mac
"/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc" # Linux
]
for p in paths:
if os.path.exists(p):
return p
return None
font_path = get_font_path()
# 4. 生成词云
wc = WordCloud(
font_path=font_path,
width=800,
height=400,
background_color='white',
max_words=50
).generate(words)
# 5. 显示与保存
plt.figure(figsize=(10, 5))
plt.imshow(wc, interpolation='bilinear')
plt.axis('off')
plt.savefig('wordcloud.png')
print("✅ 词云图已保存为 'wordcloud.png'")
# plt.show() # 如果在本地运行可取消注释
🎓 总结与思考
通过这次实训,我们实际上完成了一个微型 NLP 流水线:
- 输入:原始中文文本。
- 预处理:分词、去停用词(基础分析)。
- 特征工程:TF-IDF 向量化(相似度、分类的基础)。
- 模型推理:朴素贝叶斯、TextRank、情感打分。
- 输出:分类结果、摘要、图表(可视化)。
给同学们的思考题:
- 如果我想让模型识别“网络流行语”(如"YYDS"),现在的分词器能识别吗?如果不能,该怎么改代码?(提示:
jieba.add_word) - 目前的相似度算法是基于“词”的。如果两句话意思一样但用词完全不同(如“苹果”vs“水果”),算法还能识别吗?这引出了什么更高级的技术?(提示:词向量 Word2Vec / BERT)
- 为什么我们在 CPU 上能跑得这么快?如果数据量变成 100 万条,现在的代码哪里会崩?(提示:内存、计算复杂度)
希望这份详解能帮助大家不仅会用代码,更能读懂代码背后的智慧!🚀
