机器学习文本分类入门:从数据清洗到中文评论词向量转换

今天的课程主要学习了文本数据的处理方法。和身高、年龄、体重这类数据不同,评论、新闻、小说等文本本身不是数字,计算机不能直接理解一句话表达的意思。

例如:"这款手机外观很好看,运行速度也很快。"

在人类看来,这句话大概率是一条好评。但对机器学习模型来说,它只是一串字符。想让模型判断好评还是差评,首先要把文字转换成数字,这个过程就是文本向量化。

本节课程主要围绕以下内容展开:

  1. 文本数据清洗;
  2. 中文分词和停用词处理;
  3. 词袋模型和 TF-IDF;
  4. 使用 CountVectorizer 转换词向量;
  5. 使用朴素贝叶斯完成评论分类;
  6. 训练集、测试集和数据泄漏;
  7. 通过数据清洗处理结构化数据。

一、为什么要进行文本向量化

机器学习模型的输入通常是一个二维矩阵:

复制代码
行:一条样本
列:一个特征

例如,预测房价时,特征可能是面积、楼层和房间数量:

面积 楼层 房间数
80 10 2
120 18 3

但是评论数据通常是这样的:

评论内容 标签
手机外观漂亮,运行速度很快 好评
电池续航太差,不推荐购买 差评

模型无法直接使用"外观漂亮"这种文字,因此需要先建立一个词语表,再统计每条评论中出现了哪些词。

假设词语表为:

复制代码
外观、漂亮、运行、速度、电池、续航、差

第一条评论可以转换成:

复制代码
[1, 1, 1, 1, 0, 0, 0]

第二条评论可以转换成:

复制代码
[0, 0, 0, 0, 1, 1, 1]

这样,文本就变成了模型可以处理的数字矩阵。


二、数据清洗是文本分类的第一步

原始评论数据通常并不干净,里面可能包含:

空评论;

多余空格;

换行符;

HTML 标签;

表情符号;

特殊字符;

重复评论;

无意义的广告内容;

缺失标签。

如果不进行清洗,模型可能会把这些无关内容当成特征,影响最终分类效果。

首先读取评论数据:

复制代码
import pandas as pd

data = pd.read_csv("苹果手机评论.csv")

print(data.head())
print(data.info())
print(data.isnull().sum())

假设数据中有两列:

复制代码
content:评论内容
label:评论标签

可以删除评论内容或标签为空的数据:

复制代码
data = data.dropna(subset=["content", "label"])

删除重复评论:

复制代码
data = data.drop_duplicates(subset=["content"])

有些数据中,好评和差评可能不是数字,而是中文:

复制代码
好评、差评

机器学习模型通常更适合使用数字标签,因此可以进行转换:

复制代码
label_map = {
    "差评": 0,
    "好评": 1
}

data["label"] = data["label"].map(label_map)

如果标签本来就是 01,则不需要重复转换。


三、中文文本清洗与分词

英文单词之间通常使用空格分隔,例如:

复制代码
this phone is good

但中文没有天然的空格:

复制代码
这款手机运行速度很快

因此,中文文本需要先进行分词。Python 中常用的中文分词库是 jieba

复制代码
import jieba

text = "这款手机运行速度很快"
words = jieba.lcut(text)

print(words)

输出类似:

复制代码
["这款", "手机", "运行", "速度", "很快"]

为了让分词结果更适合模型,还需要删除标点符号和停用词。

停用词是指"的、了、是、我、也"等出现频率很高,但区分能力较弱的词。可以准备一个停用词文件:

复制代码
with open("StopwordsCN.txt", "r", encoding="utf-8") as f:
    stopwords = set(line.strip() for line in f)

编写一个简单的文本清洗函数:

复制代码
import re
import jieba

def clean_text(text):
    text = str(text)

    # 删除网址
    text = re.sub(r"http\S+|www\S+", "", text)

    # 只保留中文、英文、数字
    text = re.sub(r"[^\u4e00-\u9fa5a-zA-Z0-9]", " ", text)

    words = jieba.lcut(text)

    result = []
    for word in words:
        word = word.strip()

        if not word:
            continue

        if word in stopwords:
            continue

        result.append(word)

    return result

处理一条评论:

复制代码
print(clean_text("这款手机真的很好用!!!物流也很快"))

可能得到:

复制代码
["这款", "手机", "真的", "好用", "物流", "很快"]

如果使用 CountVectorizer,通常需要把分词结果重新连接成空格分隔的字符串:

复制代码
data["words"] = data["content"].apply(clean_text)
data["text_cut"] = data["words"].apply(lambda x: " ".join(x))

例如:

复制代码
这款 手机 真的 好用 物流 很快

这样,向量化工具就可以正常识别每个词。


四、词袋模型:统计词语出现次数

词袋模型的思想很简单:不考虑词语在句子中的顺序,只统计词语出现了多少次。

例如有三句话:

复制代码
texts = [
    "手机 外观 漂亮",
    "手机 运行 速度 快",
    "电池 续航 时间 长"
]

使用 CountVectorizer

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

vectorizer = CountVectorizer()

X = vectorizer.fit_transform(texts)

print(vectorizer.get_feature_names_out())
print(X.toarray())

输出的词表可能是:

复制代码
["外观", "手机", "漂亮", "电池", "时间", "续航", "运行", "速度", "长", "快"]

每一行就是一条文本的词频向量。

1. max_features

课程代码中使用了:

复制代码
vectorizer = CountVectorizer(
    max_features=4000,
    lowercase=False,
    ngram_range=(1, 3)
)

max_features=4000 表示最多保留 4000 个特征词。词语数量太多时,会造成矩阵维度过高,训练速度变慢,因此可以限制词表大小。

2. lowercase

lowercase=False 表示不强制把英文转换为小写。中文数据中这个参数影响不大,但如果评论中包含型号、品牌或英文缩写,就可以根据实际需求决定是否统一大小写。

3. ngram_range

复制代码
ngram_range=(1, 3)

表示同时使用:

一个词组成的一元词组;

两个词组成的二元词组;

三个词组成的三元词组。

例如:

复制代码
手机 运行 速度 很快

除了"手机""运行"等单个词,还可以生成:

复制代码
手机 运行
运行 速度
速度 很快
手机 运行 速度

这样可以保留部分词语之间的组合关系。相比只使用单个词,分类效果通常会更好,但特征数量也会增加。


五、TF-IDF:降低常见词的影响

词频只能说明一个词在当前文本中出现了多少次,却不能说明这个词是否有区分度。

例如"手机"可能在所有评论中都出现,它虽然频率很高,但不能很好地区分好评和差评。"续航差""屏幕清晰"等词语可能更有分类价值。

TF-IDF 由两部分组成:

1. TF:词频

表示某个词在当前文本中出现的频率。

复制代码
TF = 某词在当前文本中出现的次数 / 当前文本总词数

2. IDF:逆文档频率

如果一个词在很多文档中都出现,那么它的区分能力较弱,IDF 就会较小。

复制代码
IDF = log(文档总数 / 包含该词的文档数)

TF-IDF 就是:

复制代码
TF-IDF = TF × IDF

使用 TfidfVectorizer

复制代码
from sklearn.feature_extraction.text import TfidfVectorizer

tfidf = TfidfVectorizer(
    max_features=4000,
    ngram_range=(1, 2)
)

X = tfidf.fit_transform(data["text_cut"])

在课程中还通过《红楼梦》文本检索理解 TF-IDF。搜索" 大观园 "时,程序会计算查询词与各个章节之间的权重,权重越高,说明该章节与搜索内容越相关。

TF-IDF 常用于:

文本搜索;

关键词提取;

文档相似度计算;

新闻分类;

评论分类。


六、用多项式朴素贝叶斯识别好评和差评

文本分类中,朴素贝叶斯是一个非常经典的入门模型,尤其适合处理词频或 TF-IDF 特征。

它的核心思想是:根据一条评论中出现的词语,计算它属于不同类别的概率,最后选择概率更高的类别。

例如,评论中出现"漂亮、流畅、满意"等词,可能更接近好评;出现"卡顿、发热、失望"等词,可能更接近差评。

完整代码如下:

复制代码
import pandas as pd
import jieba
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import classification_report

data = pd.read_csv("苹果手机评论.csv")
data = data.dropna(subset=["content", "label"])
data = data.drop_duplicates(subset=["content"])

def cut_text(text):
    words = jieba.lcut(str(text))
    return " ".join(words)

data["text_cut"] = data["content"].apply(cut_text)

x = data["text_cut"]
y = data["label"]

x_train, x_test, y_train, y_test = train_test_split(
    x,
    y,
    test_size=0.25,
    random_state=0,
    stratify=y
)

vectorizer = CountVectorizer(
    max_features=4000,
    lowercase=False,
    ngram_range=(1, 3)
)

x_train_vec = vectorizer.fit_transform(x_train)
x_test_vec = vectorizer.transform(x_test)

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

predicted = classifier.predict(x_test_vec)

print(classification_report(y_test, predicted))

这里最重要的是:

复制代码
vectorizer.fit_transform(x_train)
vectorizer.transform(x_test)

词表必须只使用训练集建立。测试集只能使用训练集已经建立好的词表进行转换。

如果直接对全部数据执行:

复制代码
vectorizer.fit_transform(x)

再划分训练集和测试集,测试集中的词语信息就提前参与了词表建立,这属于数据泄漏,会导致评估结果过于乐观。


七、如何理解分类报告

运行 classification_report 后,通常会看到:

复制代码
              precision    recall  f1-score   support

           0       1.00      0.98      0.99     54068
           1       0.02      0.60      0.03        25

    accuracy                           0.98     54093

很多初学者看到准确率 0.98,会认为模型效果非常好。但从这份报告可以看出,类别 1 只有 25 条样本,F1 值只有 0.03,说明模型几乎不能稳定识别这个类别。

这就是类别不平衡问题。

假设测试集中有 1000 条评论,其中 990 条是好评,10 条是差评。模型即使把所有评论都预测为好评,也能获得 99% 的准确率,但它完全没有识别差评。

因此,评价评论分类模型时不能只看准确率,还要关注:

precision:预测为某类的样本中,有多少是真正的该类;

recall:真实属于某类的样本中,有多少被找出来;

f1-score:精确率和召回率的综合指标;

support:每个类别实际包含的样本数量。

当好评和差评数量差异很大时,可以考虑:

对多数类别进行下采样;

对少数类别进行过采样;

增加少数类别数据;

使用分层抽样;

重点观察少数类别的召回率和 F1 值。


八、结构化数据的数据清洗

课程后半部分还演示了矿物数据的清洗。虽然矿物数据不是文本,但处理机器学习数据时,基本流程是相通的。

首先读取 Excel 文件:

复制代码
import pandas as pd

data = pd.read_excel("矿物数据.xls")
print(data.head())
print(data.shape)

删除不需要的类别,例如课程中删除了类别 E

复制代码
data = data[data["矿物类型"] != "E"]

查看每一列的缺失值:

复制代码
null_num = data.isnull().sum()
print(null_num)

提取特征和标签:

复制代码
X = data.drop(["序号", "矿物类型"], axis=1)
y = data["矿物类型"]

将分类标签转换成数字:

复制代码
label_dict = {
    "A": 0,
    "B": 1,
    "C": 2,
    "D": 3
}

y = y.map(label_dict)

对于特征中的空值,可以使用中位数填充:

复制代码
from sklearn.impute import SimpleImputer

imputer = SimpleImputer(strategy="median")
X = imputer.fit_transform(X)

常见的缺失值处理方式还有:

使用均值填充;

使用中位数填充;

使用众数填充;

使用前一个有效值填充;

使用后一个有效值填充;

使用插值方法填充;

删除缺失比例过高的列或行。

选择哪种方法,需要结合数据分布。比如数据中存在极端值时,中位数通常比均值更稳定。


九、今天课程的完整流程

无论是苹果手机评论,还是矿物分类数据,都可以按照下面的流程处理:

复制代码
读取数据
   ↓
检查缺失值和异常值
   ↓
清洗数据
   ↓
提取特征和标签
   ↓
划分训练集和测试集
   ↓
只用训练集建立特征转换器
   ↓
转换为数字矩阵
   ↓
训练机器学习模型
   ↓
使用测试集评估

对于文本数据,主要增加了中文分词和停用词处理:

复制代码
原始评论
   ↓
去除网址、符号和空白
   ↓
中文分词
   ↓
删除停用词
   ↓
CountVectorizer 或 TF-IDF
   ↓
朴素贝叶斯分类

十、总结

今天学习的重点并不是某一个模型,而是理解"文本如何进入机器学习模型"。

一条评论要经过:

复制代码
文字 → 清洗 → 分词 → 词表 → 数字向量 → 分类模型

CountVectorizer 通过统计词语出现次数建立词频向量,TfidfVectorizer 则会进一步降低常见词的权重。ngram_range=(1, 3) 可以加入二元词组和三元词组,让模型保留更多局部语义。

多项式朴素贝叶斯实现简单、训练速度快,是文本分类的优秀入门模型。但在实际项目中,不能只看准确率,还要检查类别是否平衡,并重点观察少数类别的精确率、召回率和 F1 值。

另外,数据清洗会直接影响模型效果。无论处理评论、新闻,还是矿物检测数据,都应该先检查空值、重复值、异常类别和标签格式,再进入模型训练阶段。只有数据质量可靠,后面的算法结果才有意义。

相关推荐
小宋10211 小时前
Dify 知识库实战:从 PDF 导入到带引用回答,完整搭建企业问答助手
人工智能·ai编程
用户938515635072 小时前
从Vibe Coding到SDD:规范驱动开发如何拯救AI编程失控
人工智能
蓝速科技3 小时前
蓝速科技桌面 AI 双屏翻译机:开放安卓系统商用价值解析
人工智能·科技
博、、3 小时前
社区家政平台开发实战指南:从需求分析到系统部署全流程解析
人工智能·数据挖掘·需求分析
Python私教3 小时前
别急着加 llms.txt:企业官网面向 AI 搜索的工程清单
前端·人工智能·seo
Tokenge3 小时前
AI 前沿日报|2026.08.19:模型安全按下减速键,智能体进入“可控落地”新阶段
人工智能·安全
东方小月3 小时前
从零开发一个 Coding Agent(十三):实现安全的 read 文件读取工具
前端·人工智能·全栈
stuartevil3 小时前
怎么用 AI 创意生图做汽车改装外观效果图?
人工智能·汽车
嗯哼python3 小时前
从 0 到 1 构建 AI 模拟面试系统:RAG + LangGraph 的工程实践
人工智能·面试·职场和发展