🎯 学习目标
- 独立完成"评论情感分析"全流程:清洗→分词→特征→训练→评估→保存
- 掌握新闻文本分类在真实数据集上的工程组织方式
- 用 TF-IDF + 余弦相似度实现一个可运行的简易搜索引擎
- 用 NER + 关系模式做合同/新闻信息抽取并输出结构化 JSON
- 了解模型持久化、FastAPI 部署与大模型 API 的调用方式
1项目一:电商评论情感分析(完整可运行)
把第 2、3、5、6 章的知识整合成一个单文件可运行的项目。真实使用时把 DATA 部分替换为 pandas 读取的评论 CSV 即可。
# ===== sentiment_project.py =====
import re, jieba, joblib
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.svm import LinearSVC
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.pipeline import Pipeline
from sklearn.metrics import classification_report, confusion_matrix
# ---------- 1. 数据(真实项目换成 pd.read_csv("comments.csv"))----------
DATA = [
("手机拍照清晰,续航超长,非常满意的一次购物", 1),
("物流超快第二天就到,包装严实,客服热情", 1),
("性价比很高,运行流畅,推荐大家购买", 1),
("屏幕色彩惊艳,音质也很棒,喜欢", 1),
("用了两周依然很流畅,系统简洁好用", 1),
("发货迅速,赠品丰富,五星好评", 1),
("收到货很惊喜,做工精致,超出预期", 1),
("续航能力强,重度使用一天无压力", 1),
("物流太慢了,一周才收到,体验很差", 0),
("手机频繁卡顿死机,电池也不耐用,后悔", 0),
("客服态度恶劣,退货推三阻四,差评", 0),
("屏幕有坏点,包装破损,申请售后中", 0),
("发热严重,玩游戏掉帧,完全不值这个价", 0),
("异味很大,用了三天就坏了,千万别买", 0),
("广告特别多,系统臃肿,没法用", 0),
("漏光严重,颜色失真,已经退货", 0),
]
df = pd.DataFrame(DATA, columns=["text", "label"])
# ---------- 2. 清洗 + 分词 ----------
def clean(text):
text = re.sub(r"https?://\S+", " ", text)
text = re.sub(r"[0-9a-zA-Z.+-]+@[0-9a-zA-Z.-]+", " ", text)
text = re.sub(r"[^一-龥a-zA-Z,。!?、\s]", " ", text)
return re.sub(r"\s+", " ", text).strip()
def tokenize(text):
# 情感分析保留否定词,停用词表要谨慎!
return " ".join(w for w in jieba.lcut(clean(text)) if w.strip())
df["x"] = df["text"].map(tokenize)
# ---------- 3. 划分数据集(分层抽样保持正负比例)----------
X_tr, X_te, y_tr, y_te = train_test_split(
df["x"], df["label"], test_size=0.25,
random_state=42, stratify=df["label"])
# ---------- 4. TF-IDF + 分类器流水线 ----------
model = Pipeline([
("tfidf", TfidfVectorizer(ngram_range=(1, 2),
min_df=1, max_df=0.9,
token_pattern=r"(?u)\b\w+\b",
sublinear_tf=True)),
("clf", LinearSVC(class_weight="balanced")),
])
model.fit(X_tr, y_tr)
# ---------- 5. 评估 ----------
pred = model.predict(X_te)
print(classification_report(y_te, pred,
target_names=["负面", "正面"], digits=3, zero_division=0))
print("混淆矩阵:\n", confusion_matrix(y_te, pred))
# ---------- 6. 模型解释:看哪些词最影响判断 ----------
tfidf = model.named_steps["tfidf"]
clf = model.named_steps["clf"]
words = tfidf.get_feature_names_out()
weight = clf.coef_[0]
top_pos = sorted(zip(weight, words), reverse=True)[:8]
top_neg = sorted(zip(weight, words))[:8]
print("最正面词:", [w for _, w in top_pos])
print("最负面词:", [w for _, w in top_neg])
# ---------- 7. 保存 / 加载 / 上线预测 ----------
joblib.dump(model, "sentiment_model.pkl")
loaded = joblib.load("sentiment_model.pkl")
def predict(text):
x = tokenize(text)
tag = "正面" if loaded.predict([x])[0] == 1 else "负面"
return tag
for t in ["电池很耐用,拍照也很清楚", "卡顿发热,客服还不理人"]:
print(t, "→", predict(t))
错误分析是提升模型最有效的一步:把测试集中预测错的样本逐条看——是分词错(领域词未加入词典)、否定没处理好("不是不好")、反讽("真是好样的,三天就坏了"),还是训练集缺这类表达。针对性补数据远好于盲目换算法。反讽等复杂语义最终需要 BERT/大模型。
2项目二:新闻文本分类
数据集推荐 THUCNews(14 个类别:体育、财经、房产、教育……)。目录通常按类别组织:
THUCNews/
├── 体育/*.txt
├── 财经/*.txt
├── 房产/*.txt
└── ...(共14类)
# ===== news_classify.py =====
from pathlib import Path
import jieba, joblib
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.svm import LinearSVC
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
def load_thucnews(root, per_class=2000):
"""读取按类别分文件夹存放的语料,每类最多取 per_class 篇。"""
texts, labels = [], []
for cat_dir in Path(root).iterdir():
if not cat_dir.is_dir():
continue
for i, f in enumerate(cat_dir.glob("*.txt")):
if i >= per_class:
break
texts.append(f.read_text(encoding="utf-8", errors="ignore"))
labels.append(cat_dir.name)
return texts, labels
# X, y = load_thucnews("THUCNews")
# X = [" ".join(jieba.lcut(t)) for t in X]
# Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.2,
# random_state=42, stratify=y)
# news_model = Pipeline([
# ("tfidf", TfidfVectorizer(max_features=30000, ngram_range=(1, 2),
# min_df=3, max_df=0.9,
# token_pattern=r"(?u)\b\w+\b")),
# ("clf", LinearSVC()),
# ]).fit(Xtr, ytr)
# print(classification_report(yte, news_model.predict(Xte), digits=3))
# joblib.dump(news_model, "news_model.pkl")
#
# 经验:清洗+分词+TF-IDF(1,2gram)+LinearSVC 在 THUCNews 上
# 即可达到 90%+ 准确率,是非常强的基线。
# 无中文数据集时,可用 sklearn 内置英文 20 类新闻集体验流程:
from sklearn.datasets import fetch_20newsgroups
train = fetch_20newsgroups(subset="train", remove=("headers", "footers"))
test = fetch_20newsgroups(subset="test", remove=("headers", "footers"))
en_model = Pipeline([
("tfidf", TfidfVectorizer(ngram_range=(1, 2), stop_words="english")),
("clf", LinearSVC()),
]).fit(train.data, train.target)
print("20News 准确率:", en_model.score(test.data, test.target))
3项目三:简易语义搜索引擎
实现"输入查询 → 返回最相关文档 TopK"的核心:文档向量化 + 倒排检索 + 余弦排序。真实搜索引擎(Elasticsearch)内核思想一致。
# ===== mini_search.py =====
import jieba
import numpy as np
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
DOCS = [
"Python 是人工智能与自然语言处理最常用的编程语言",
"jieba 是流行的中文分词工具,支持自定义词典和词性标注",
"TF-IDF 通过词频和逆文档频率衡量词语对文档的重要程度",
"朴素贝叶斯和支持向量机常用于文本分类任务",
"word2vec 可以把词语训练成低维稠密的语义向量",
"情感分析用于判断电商评论是正面还是负面评价",
"BERT 是基于 Transformer 的预训练语言模型",
]
class MiniSearchEngine:
def __init__(self, docs):
self.docs = docs
cut = [" ".join(jieba.lcut(d)) for d in docs]
self.tv = TfidfVectorizer(ngram_range=(1, 2),
token_pattern=r"(?u)\b\w+\b")
self.matrix = self.tv.fit_transform(cut) # 文档库一次性建索引
def search(self, query, top_k=3):
q = " ".join(jieba.lcut(query))
qv = self.tv.transform([q]) # 查询向量化
sims = cosine_similarity(qv, self.matrix)[0]
order = np.argsort(sims)[::-1][:top_k] # 相似度降序
return [(i + 1, round(float(sims[i]), 3), self.docs[i])
for i in order if sims[i] > 0]
engine = MiniSearchEngine(DOCS)
for rank, score, text in engine.search("怎么判断评论的好坏", top_k=3):
print(f"#{rank} score={score} {text}")
# 语义相关的"情感分析…正面还是负面评价"会排在前列
升级路径:① 文档多了以后用稀疏矩阵 + 倒排索引只对含查询词的文档算相似度;② 关键词命中(BM25,Elasticsearch 默认算法)与语义向量(bge 句向量)混合召回;③ 用 FAISS/Milvus 做百万级向量近邻搜索;④ 加前端页面即一个完整问答/文档检索系统。
4项目四:合同/新闻信息抽取
目标:从非结构化文本输出结构化 JSON(实体 + 关系),可直接入库或对接知识图谱。
# ===== information_extract.py =====
import re, json
import spacy
nlp = spacy.load("zh_core_web_sm")
text = ("2026年3月15日,阿里巴巴(中国)有限公司与华为技术有限公司"
"在深圳签署战略合作协议,金额为5亿元人民币,"
"双方将在云计算领域开展合作,签约人为李明。")
doc = nlp(text)
record = {"entities": [], "money": [], "dates": [], "relations": []}
# ① 实体
for ent in doc.ents:
record["entities"].append({"text": ent.text, "type": ent.label_})
# ② 正则补充:金额、日期、协议编号
record["money"] = re.findall(r"\d+(?:\.\d+)?(?:亿|万)?元(?:人民币)?", text)
record["dates"] = re.findall(r"\d{4}年\d{1,2}月\d{1,2}日", text)
# ③ 关系模式抽取("X 与 Y 签署…协议")
orgs = [e.text for e in doc.ents if e.label_ == "ORG"]
m = re.search(r"(.*?)与(.*?)签署(.*?)协议", text)
if m and len(orgs) >= 2:
record["relations"].append(
{"head": orgs[0], "relation": "签约合作", "tail": orgs[1]})
print(json.dumps(record, ensure_ascii=False, indent=2))
# {
# "entities": [{"text": "阿里巴巴(中国)有限公司", "type": "ORG"}, ...],
# "money": ["5亿元人民币"],
# "dates": ["2026年3月15日"],
# "relations": [{"head": "阿里巴巴...", "relation": "签约合作",
# "tail": "华为技术有限公司"}]
# }
工程现实:正则+小模型在固定格式合同上效果好;开放文本的关系抽取建议用大模型做零样本/少样本抽取(在 prompt 中直接要求输出 JSON),再用规则做格式校验,是当前性价比最高的方案。
5模型部署:从 notebook 到 API
训练好的模型用 joblib 持久化,再用 FastAPI 包成 HTTP 接口,前端/业务系统即可调用:
# ===== serve.py 运行:uvicorn serve:app --reload =====
import joblib, jieba
from fastapi import FastAPI
from pydantic import BaseModel
model = joblib.load("sentiment_model.pkl") # 启动时加载一次
app = FastAPI(title="情感分析接口")
class Review(BaseModel):
text: str
@app.post("/predict")
def predict(review: Review):
x = " ".join(jieba.lcut(review.text))
tag = "正面" if model.predict([x])[0] == 1 else "负面"
return {"text": review.text, "sentiment": tag}
# 测试(另开终端):
# curl -X POST http://127.0.0.1:8000/predict \
# -H "Content-Type: application/json" \
# -d '{"text":"物流很快,非常满意"}'
大模型 API 调用(情感/摘要/抽取通用范式)
# pip install openai (兼容 OpenAI 接口的任意国内大模型均可)
from openai import OpenAI
client = OpenAI(
api_key="你的API_KEY",
base_url="https://你的服务商地址/v1")
def llm_sentiment(text):
resp = client.chat.completions.create(
model="模型名",
messages=[
{"role": "system",
"content": "你是情感分析器,只输出 JSON:{\"label\":\"正面/负面/中性\","
"\"reason\":\"一句话理由\"}"},
{"role": "user", "content": text},
],
temperature=0)
return resp.choices[0].message.content
# print(llm_sentiment("价格贵了点,但是拍照效果确实惊艳"))
# 工程技巧:用大模型批量【预标注】数据 → 人工校对 → 训练小模型本地部署(省钱+快+数据私有)
6NLP 工程师进阶路线图
| 阶段 | 补足能力 | 推荐内容 |
|---|---|---|
| ① 地基(你在这里) | Python + 传统 NLP 全流程 | 本教程 + 复现 4 个项目 |
| ② 数学与 ML | 线代/概率统计、sklearn 系统学习 | 吴恩达 ML、周志华《机器学习》选读 |
| ③ 深度学习 | PyTorch、RNN/CNN/注意力、Transformer | 李沐《动手学深度学习》、Annotated Transformer |
| ④ 预训练模型 | BERT 微调文本分类/NER、LoRA 微调 | HuggingFace 官方教程、bge/chatGLM/Qwen |
| ⑤ 大模型应用 | Prompt、RAG 检索增强、Agent、评测 | LangChain/LlamaIndex、向量数据库 |
| ⑥ 工程化 | 数据标注、蒸馏、量化推理、监控迭代 | ONNX/vLLM、AB 测试、标注平台 |
7全教程知识地图回顾
Python 语法篇(14章) NLP 应用篇(8章)
基础:类型/运算/流程/字符串 → 清洗(正则/归一化/停用词)
容器/函数/面向对象 → jieba 分词 / 词性 / NER
异常/文件/高级特性/正则/库 → 词频 → TF-IDF → 关键词 → 词云
↓
文本向量化(词袋/TF-IDF/word2vec/BERT)
↓
分类与情感(贝叶斯/LR/SVM + 评估指标)
↓
综合项目:情感分析 · 新闻分类 · 搜索引擎 · 信息抽取 · API 部署
8收官练习
📌 结语
你已经走完从 Python 零基础到 NLP 项目实战的完整路径。接下来最重要的只有一件事:动手做自己的项目——爬取你感兴趣的评论、分析你工作中的文本、把模型部署给朋友试用。在真实数据中遇到的每一个脏问题,都是进阶最快的台阶。祝编码愉快!