跳至主要內容

自然语言处理

周子力大约 12 分钟教学文档Python基础

📘 NLP 实训技术讲义

—— 从代码到原理,读懂自然语言处理


1. 基础分析 (Basic Analysis)

核心任务:中文分词 & 词性标注
代码对应jieba.lcut(), jieba.posseg

🧐 它是什么?

计算机不认识“句子”,它只认识“词”。英文单词之间有空格,计算机天然知道哪里是一个词。但中文是一连串的字符(如“南京市长江大桥”),计算机不知道是“南京市/长江/大桥”还是“南京/市长/江大桥”。分词就是把连续的中文序列切分成有意义的词语。

💡 通俗比喻

想象你有一串没有隔开的珍珠项链。分词就是要把它们剪开,变成一颗颗独立的珍珠,这样你才能数清楚有多少颗,或者把红色的挑出来。

⚙️ 技术原理

  1. 词典匹配:系统里有一个巨大的词典。它会拿着词典去文本里“撞”,能匹配上的就是一个词。
  2. 最大概率路径:有时候一句话有多种切分法(如“结合/成”vs“结/合成”)。算法会计算哪种切分在整个语言环境中出现的概率最大,就选哪种。
  3. 停用词过滤:像“的”、“了”、“啊”这种词,虽然常见但没实际意义,分析时通常要扔掉,这叫去噪。

🌍 应用场景

  • 搜索引擎(你搜“手机”,它能找到包含“智能手机”的网页)
  • 输入法联想

代码示例

# 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

🧐 它是什么?

判断两句话的意思是否接近。注意,不是看字是否一样,而是看内容是否一样。“我喜欢这个产品”和“这个产品我很满意”,字不同,但意思很像。

💡 通俗比喻

指纹比对。我们把每篇文章都变成一根“指纹”(向量)。如果两根指纹的纹路方向几乎一致,说明它们很像;如果方向垂直,说明完全没关系。

⚙️ 技术原理

  1. TF-IDF 向量化:把文字变成数字。
    • TF (词频):一个词在这篇文章出现越多,越重要。
    • IDF (逆文档频率):一个词在所有文章里都出现(如“的”),它就不重要;如果一个词很稀有(如“量子力学”),它就很能代表这篇文章。
  2. 余弦相似度:在数学空间里,计算两个向量夹角的余弦值。
    • 值为 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 页这几段是考试重点”。自动摘要就是机器在帮你“划重点”。

⚙️ 技术原理

  1. TextRank 算法:灵感来自谷歌的网页排名(PageRank)。
  2. 句子投票:把每个句子看作一个节点。如果句子 A 和句子 B 有很多相同的词,它们之间就有一条连线。
  3. 重要性排序:被很多其他句子“连接”的句子,通常包含了核心信息,得分最高,就被选入摘要。

🌍 应用场景

  • 新闻快讯生成
  • 法律文档快速审阅
  • 会议纪要自动生成

代码示例

# 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 (朴素贝叶斯)

🧐 它是什么?

情感分析通常只有“好/坏”两类。但实际业务中,我们需要更细的分类。比如用户反馈,是“投诉”?“咨询”?还是“表扬”?这就是多类别分类。

💡 通俗比喻

邮局分拣员。面前有一堆信,分拣员看一眼内容:

  • 看到“坏了”、“退货” → 扔进投诉筐。
  • 看到“请问”、“怎么” → 扔进咨询筐。
  • 看到“谢谢”、“点赞” → 扔进表扬筐。 机器就是在模仿这个分拣员。

⚙️ 技术原理

  1. 训练 (Training):先给机器看几千封已经分好类的信(训练数据),让它记住“投诉”类的信通常包含哪些词。
  2. 朴素贝叶斯:一种基于概率的算法。它计算:“出现‘退货’这个词的情况下,这封信属于‘投诉’类的概率是多少?”
  3. 预测:新来一封信,计算它属于各个类别的概率,选概率最大的那个。

🌍 应用场景

  • 客服工单自动路由(投诉转人工,咨询转机器人)
  • 垃圾邮件分类(广告、诈骗、正常)
  • 新闻分类(体育、财经、娱乐)

代码示例

# 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 循环

🧐 它是什么?

一篇文章的情感不是单一的。比如:“刚开始很满意(正),但是后来发现有问题(负),联系客服后解决了(正)”。情感趋势分析就是把文章切开,看情感是如何波动的。

💡 通俗比喻

心电图。人的心情不是一条直线,而是有起有伏。情感趋势就是给文本画一张“心情心电图”,看它是先扬后抑,还是逐渐好转。

⚙️ 技术原理

  1. 句子分割:用标点符号(。!?)把文章切成多个短句。
  2. 逐句打分:对每个短句单独进行情感分析(0~1 分)。
  3. 连线成图:把每个句子的分数按顺序连起来,形成折线图。

🌍 应用场景

  • 深度影评分析(剧情哪里高潮,哪里烂尾)
  • 用户旅程体验分析(购买流程中哪里让用户生气了)
  • 舆情监控(事件发酵过程中公众情绪变化)

代码示例

# 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)

🧐 它是什么?

从一堆文字里,把具体的人名、地名、公司名抠出来。这是构建知识图谱的第一步。

💡 通俗比喻

荧光笔标记。读书时,你用不同颜色的荧光笔:

  • 黄色画人名(马云)
  • 绿色画地名(杭州)
  • 蓝色画公司名(阿里巴巴) 实体识别就是机器在自动帮你画这些荧光笔。

⚙️ 技术原理

  1. 词性标注 (POS Tagging):先给每个词贴标签。比如“北京”后面标 ns (地名),“腾讯”后面标 nt (机构名)。
  2. 规则提取:代码里写了规则:if flag.startswith('nr') (如果是人名标签),就把它存下来。
  3. 上下文依赖:高级的 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

🧐 它是什么?

一种视觉展示方式。出现次数越多的词,在图片里字体越大、越显眼。让人一眼就能看出这段文字在讲什么。

💡 通俗比喻

人群合影。在一个大合影里,谁的声音最大(出现频率最高),谁的头像就放得最大。你一眼看过去,最大的几个头像就是这场合的主角。

⚙️ 技术原理

  1. 词频统计:数每个词出现了多少次。
  2. 权重映射:把次数映射为字体大小(比如出现 10 次是 10 号字,100 次是 50 号字)。
  3. 布局算法:像玩俄罗斯方块一样,把这些大小不一的词挤在一个形状里,尽量不重叠,且美观。

🌍 应用场景

  • 政府工作报告解读
  • 微博热搜话题展示
  • 用户反馈关键词概览

代码示例

# 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 流水线

  1. 输入:原始中文文本。
  2. 预处理:分词、去停用词(基础分析)。
  3. 特征工程:TF-IDF 向量化(相似度、分类的基础)。
  4. 模型推理:朴素贝叶斯、TextRank、情感打分。
  5. 输出:分类结果、摘要、图表(可视化)。

给同学们的思考题:

  1. 如果我想让模型识别“网络流行语”(如"YYDS"),现在的分词器能识别吗?如果不能,该怎么改代码?(提示:jieba.add_word
  2. 目前的相似度算法是基于“词”的。如果两句话意思一样但用词完全不同(如“苹果”vs“水果”),算法还能识别吗?这引出了什么更高级的技术?(提示:词向量 Word2Vec / BERT)
  3. 为什么我们在 CPU 上能跑得这么快?如果数据量变成 100 万条,现在的代码哪里会崩?(提示:内存、计算复杂度)

希望这份详解能帮助大家不仅会用代码,更能读懂代码背后的智慧!🚀

上次编辑于:
贡献者: zilizhou