【PYthon词频统计与文本向量化】苏宁易购评论分析实战

目录

一、引言

在自然语言处理(NLP)入门中,文本分类是一个非常经典的任务。今天,我将带你从最基础的词频统计开始,一步步构建一个能够自动判断评论是"好评"还是"差评"的情感分析模型。

本文会用到 scikit-learnCountVectorizer 做文本向量化,用 jieba 做中文分词,最后用朴素贝叶斯分类器完成训练和预测。整个过程会包含完整的代码和详细的注释。

二、基础知识点CountVectorizer 与词频统计

在开始实战之前,我们先了解一下 CountVectorizer 的核心用法。它能把自然语言文本转换成机器学习模型能处理的数字矩阵,属于"基于统计的文本特征提取"方法。

python 复制代码
from sklearn.feature_extraction.text import CountVectorizer

texts = ["dog cat fish", "dog cat cat", "fish bird", "bird"]
cv = CountVectorizer(max_features=6, ngram_range=(1, 3))

cv_fit = cv.fit_transform(texts)

print(cv_fit)
print(cv.get_feature_names_out())
print(cv_fit.toarray())

1、主要知识点

这段代码演示了 CountVectorizer 最核心的用法,下面逐行拆解:

from sklearn.feature_extraction.text import CountVectorizer:从 scikit-learn 的特征提取模块中导入词频向量化工具。这是文本分类中最常用的特征提取器之一。

texts = ["dog cat fish", "dog cat cat", "fish bird", "bird"]:定义了一个包含 4 条英文文本的列表,每条文本由空格分隔的单词组成。

cv_fit = cv.fit_transform(texts):这是两步合一的操作。fit 阶段根据输入的所有文本,统计出所有符合 ngram_range 的词/词组,并按频率排序筛选出前 6 个,生成词库。transform 把每一条文本转换成对应词库的词频向量,输出一个稀疏矩阵。

print(cv_fit):打印稀疏矩阵,只存储非零元素的位置和数值,节省内存。输出格式为 (行索引, 列索引) 数值

print(cv.get_feature_names_out()):输出词库中的词列表,即筛选出来的前 6 个词/词组。

print(cv_fit.toarray()):将稀疏矩阵转换成普通的二维数组,每一行对应一条文本,每一列对应词库中的一个词,数值是该词在文本中出现的次数。

2、参数讲解

cv = CountVectorizer(max_features=6, ngram_range=(1, 3)):初始化向量化器,这里有两个关键参数需要重点理解:

ngram_range=(1, 3) :控制词/词组的组合长度范围。(1, 3) 表示同时提取单个词(1-gram)、两个词的组合(2-gram)和三个词的组合(3-gram)。例如文本 "dog cat fish" 会提取出:dogcatfishdog catcat fishdog cat fish。这个参数越大,能捕捉的语义信息越丰富,但特征维度也会急剧膨胀。

max_features=6:限制最终保留的特征数量(即词/词组的总数),只保留出现频率最高的前 6 个。这是一个重要的降维手段,可以防止特征过多导致维度灾难和过拟合。在实际项目中,这个值通常设为 1000~10000 之间。

三、实战项目:评论情感分析

1、项目目标

我们手头有两份从苏宁易购网站爬取的关于商品评价的文本文件:差评.txt好评.txt。目标是训练一个模型,能够自动判断任意一条新评论的情感倾向(好评或差评)。

2、数据读取与分词

首先,用 pandas 读取数据,并用 jieba 进行中文分词。

python 复制代码
import pandas as pd
import jieba

cp_content = pd.read_table(r".\差评.txt", encoding='utf-8', header=None)
yzpj_content = pd.read_table(r".\好评.txt", encoding='utf-8', header=None)
# 差评分词
cp_segments = []
contents = cp_content[0].values.tolist()
for content in contents:
    results = jieba.lcut(content)
    if len(results) > 1:
        cp_segments.append(results)
# 好评分词
yzpj_segments = []
contents = yzpj_content[0].values.tolist()
for content in contents:
    results = jieba.lcut(content)
    if len(results) > 1:
        yzpj_segments.append(results)
# 分词结果保存
cp_fc_results = pd.DataFrame({'content': cp_segments})
cp_fc_results.to_excel('cp_fc_results.xlsx', index=False)

yzpj_fc_results = pd.DataFrame({'content': yzpj_segments})
yzpj_fc_results.to_excel('yzpj_fc_results.xlsx', index=False)

jieba.lcut() 返回的是分词列表,if len(results) > 1 用于过滤掉分词后只有一个词(通常是标点或单字)的无效行,避免后续处理出错。pd.read_table()header=None 参数不能漏,否则第一行数据会被误当作列名。

3、去除停用词

停用词(如"的"、"了"、"是"等)对分类任务贡献不大,反而会增加噪声,因此需要去除。

python 复制代码
stopwords = pd.read_csv(r".\StopwordsCN.txt", encoding='utf8', engine='python', index_col=False)

def drop_stopwords(contents, stopwords):
    segments_clean = []
    for content in contents:
        line_clean = []
        for word in content:
            if word in stopwords:
                continue
            line_clean.append(word)
        segments_clean.append(line_clean)
    return segments_clean

contents = cp_fc_results.content.values.tolist()
stopwords_list = stopwords.stopword.values.tolist()
cp_fc_contents_clean_s = drop_stopwords(contents, stopwords_list)

contents = yzpj_fc_results.content.values.tolist()
yzpj_fc_contents_clean_s = drop_stopwords(contents, stopwords_list)

stopwords.stopword.values.tolist() 中的 stopword 是停用词文件中的列名,如果列名不一致(比如叫 wordstopword),需要根据实际文件内容修改,建议先用 print(stopwords.columns) 确认列名。

4、构建分类数据集

将清洗后的文本打上标签:差评为 1,好评为 0,并合并成一个完整的数据集。

python 复制代码
cp_train = pd.DataFrame({'segments_clean': cp_fc_contents_clean_s, 'label': 1})
yzpj_train = pd.DataFrame({'segments_clean': yzpj_fc_contents_clean_s, 'label': 0})
pj_train = pd.concat([cp_train, yzpj_train])
pj_train.to_excel('pj_train.xlsx', index=False)

将预处理(分词、去停用词、打标签)后的中间结果持久化到磁盘,后续可以直接读取该 Excel 文件进行模型训练,避免每次运行程序都重新做分词和清洗(尤其当数据量较大时,能节省大量时间);同时方便人工查看数据是否正确。

5、特征提取与模型训练

这是核心环节。我们将分词后的文本列表拼接成空格分隔的字符串,然后用 CountVectorizer 将其转换为词频向量,最后用朴素贝叶斯分类器进行训练。

python 复制代码
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn import metrics

x_train, x_test, y_train, y_test = train_test_split(
    pj_train['segments_clean'].values, pj_train['label'].values, random_state=0
)

words = []
for line_index in range(len(x_train)):
    words.append(' '.join(x_train[line_index]))

vec = CountVectorizer(max_features=4000, lowercase=False, ngram_range=(1, 3))
vec.fit(words)
x_train_vec = vec.transform(words)

classifier = MultinomialNB(alpha=0.1)
classifier.fit(x_train_vec, y_train)

train_pr = classifier.predict(x_train_vec)
print("训练集评估结果:")
print(metrics.classification_report(y_train, train_pr))

max_features=4000:限制特征数量为 4000,避免维度灾难,数据量大时可适当增大。

ngram_range=(1, 3):同时考虑单个词、双词组合和三词组合,捕捉更多语义信息。对于中文评论,2-gram 和 3-gram 能有效捕捉"不好"、"非常差"等短语级情感。

alpha=0.1:拉普拉斯平滑系数,防止零概率问题。当某个词在训练集中未出现时,平滑系数可以避免概率为 0 导致整个预测失败。值越小,对原始频率的依赖越大;值越大,平滑效果越强。

vec.transform(words)必须分开调用,且fit只能在训练集上执行一次。如果误写成vec.fit_transform(words),虽然语法上没错,但后续测试集和新数据就无法正确向量化了。另外,words列表中的每个元素必须是空格分隔的字符串,不能是原始的分词列表,否则CountVectorizer` 会把整个列表当成一个字符串处理。

6、测试集评估

用同样的方式处理测试集,并评估模型在未见过的数据上的表现。

python 复制代码
test_words = []
for line_index in range(len(x_test)):
    test_words.append(' '.join(x_test[line_index]))

test_pr = classifier.predict(vec.transform(test_words))

print("测试集评估结果:")
print(metrics.classification_report(y_test, test_pr))

测试集向量化时只能用 vec.transform(),绝对不能再次调用 vec.fit()vec.fit_transform()fit 只能调用一次,否则测试集的词库会和训练集不一致,导致预测结果完全错误。

7、模型预测与保存

训练好的模型可以用来预测任意新评论。同时,我们将模型和向量化器保存到磁盘,方便以后直接使用。

python 复制代码
s = '这个玩意真好,我很喜欢'

cut_list = jieba.lcut(s)
cut_str = ' '.join(cut_list)
s_vec = vec.transform([cut_str])
result = classifier.predict(s_vec)

if result[0] == 1:
    print(f"评论【{s}】→预测:好评")
else:
    print(f"评论【{s}】→预测:差评")

import joblib
joblib.dump(classifier, 'nb_model.pkl')
joblib.dump(vec, 'count_vectorizer.pkl')
joblib.dump(stopwords, 'stopwords.pkl')

在预测新评论时,一定要使用训练好的 vec.transform() 进行向量化,而不是 vec.fit_transform()fit 只能在训练集上调用一次,测试集和新数据只能调用 transform,否则会导致词库不一致,预测结果完全错误。vec.transform([cut_str]) 中传入的参数必须是列表形式(即使只有一条评论),因为 CountVectorizer 要求输入是文本列表。joblib.dump 保存的三个 .pkl 文件后续可以直接加载使用,无需重新训练。

代码运行展示

四、进阶:用大模型搭建交互网页

模型训练好了,但每次预测都要跑 Python 代码,不够方便。我们可以借助大模型(如 ChatGPT、Claude 等)帮我们生成一个 HTML 网页,让用户直接在浏览器中输入评论,点击按钮就能得到预测结果。

我们已经有三个关键文件:nb_model.pkl(训练好的朴素贝叶斯模型)、count_vectorizer.pkl(词向量转换器)、stopwords.pkl(停用词表)。大模型可以帮我们生成一个 Flask 后端 + HTML 前端的简易 Web 应用,后端加载模型并暴露 API,前端提供输入框和结果显示区域。确保已安装 Flask:pip install flask,运行 python app.py,在浏览器中访问 http://127.0.0.1:5000,输入评论点击"判断情感"即可看到预测结果。

成果展示

相关推荐
AI人工智能+电脑小能手1 小时前
【大白话说Java面试题 第93题】【Mysql篇】第23题:从查找速度来看,聚集索引和非聚集索引哪个更快?
java·开发语言·数据库·mysql·面试
biter down2 小时前
9:JSONSchema
python
Cheng小攸2 小时前
入侵检测环境部署
开发语言·php
日晨难再2 小时前
C语言&Python&Bash&Tcl:全局变量和局部变量
c语言·python·bash·tcl
麻雀飞吧2 小时前
期货量化主连和具体合约怎么切:天勤 KQ.m 与 KQ.i 用法
python·区块链
先吃饱再说2 小时前
Python List 切片与 LLM Prompt 设计:从数据结构到接口调用
python
我是唐青枫2 小时前
Java MyBatis-Flex 实战指南:从 BaseMapper 到 QueryWrapper 的轻量 ORM 用法
java·开发语言·mybatis
ShyanZh3 小时前
Markitdown 多格式文档智能解析实战指南
开发语言·c#
一只专注api接口开发的技术猿3 小时前
OpenClaw 对接淘宝商品 API,低成本实现全天候选品监控|附可运行 Python 实操代码
大数据·开发语言·数据库·python