简介:文本情感分析是自然语言处理中的经典任务,这份基于gensim的Word2Vec与支持向量机(SVM)的情感分类项目,适合需要从零搭建情感分析流程的开发者、学生或研究者。压缩包共9个文件,约64.21MB,包含1个可直接运行的Python主脚本、已训练好的Word2Vec模型与SVM模型,以及训练/测试向量及标签npy数据、正负情感标注Excel语料。项目完整覆盖中文语料清洗、Word2Vec词向量训练、样本向量平均化、SVM分类器训练与评估等关键环节,也展示了核函数和参数调优对分类效果的影响。全部模型和数据均已配套,避免重复构造训练样本,下载后即可运行复现实验。目前已有1814人学习下载,适合作为课程设计、毕业论文实验或情感分析入门实践的参考模板。
1. 把 Word2Vec 和 SVM 组合起来做中文情感分析:这份代码包为什么值得跑一遍
情感分析在中文场景里是个很经典但始终让人头疼的任务,尤其是评论数据量大、表达方式杂的时候,光靠词典匹配早就撑不住了。最近拆了一个基于 gensim Word2Vec + SVM 的文本情感分析项目,代码和数据都是完整的,拿到手就能直接跑通。它的核心思路并不复杂:先用 Word2Vec 把分词后的中文文本转成稠密向量,再把这些向量作为 SVM 分类器的输入,用正负情感样本做监督学习,最后对测试文本预测情感倾向。这个组合的妙处在于,Word2Vec 能捕捉词语的上下文语义,SVM 又很适合小样本、高维特征的分类任务,两者配合在中文评论情感分类上效果相当稳定。对于正在做文本分类、情感分析入门,或者想在项目里尝试词向量 + 传统机器学习方案的人来说,这份资源值得完整跑一遍,它能让你从数据预处理一路看到模型评估,中间踩过的坑几乎都有现成解法。
2. 数据预处理与 Word2Vec 词向量训练:从 Excel 评论到能喂给模型的向量
2.1 数据形态分析与读取方式
这个项目的语料存放在 pos.xls 和 neg.xls 两个 Excel 文件里,分别对应正面和负面情感的中文评论。用 pandas 读取 Excel 文件是最直接的方式,不需要额外安装 xlrd 之外的依赖,代码包里也已经包含了读取逻辑。读取之后要做的是把两部分的文本合并,同时构造标签列:正面评论标为 1,负面评论标为 0,这样后续训练 SVM 时才能有监督信号。
import pandas as pd # 读取正负样本 pos_df = pd.read_excel('data/pos.xls', header=None) neg_df = pd.read_excel('data/neg.xls', header=None) # 添加标签并合并 pos_df['label'] = 1 neg_df['label'] = 0 all_data = pd.concat([pos_df, neg_df], ignore_index=True) all_data.columns = ['comment', 'label'] print(all_data.shape)这里要说明一下,Excel 文件里如果第一行就是评论数据,读取时用 header=None 可以避免把首行误当作列名。合并之后的数据规模通常在几百到几千条,这个量级正好是 SVM 最舒服的训练区间,既不会因为样本太少欠拟合,也不会因为数据量太大导致训练时间过长。
2.2 中文分词与停用词过滤
Word2Vec 的训练单位是词而不是字,所以中文文本必须先分词。jieba 是目前最成熟的中文分词工具,这里没有绕开它去做逐字切分的原因很简单:逐字切分会丢失词组语义,比如“难吃”“好吃”这种双字情感词,拆成单字后向量无法区分方向性。常见做法是先用 jieba.cut 做精确模式分词,再配合停用词表过滤掉“的”“了”“而且”这类无实际情感含义的词。
import jieba stopwords = set() with open('data/stopwords.txt', 'r', encoding='utf-8') as f: for line in f: stopwords.add(line.strip()) def tokenize(text): words = jieba.lcut(str(text).strip()) # 过滤停用词、空字符串和纯数字 words = [w for w in words if w not in stopwords and w.strip() and not w.isdigit()] return words all_data['tokens'] = all_data['comment'].apply(tokenize)这段代码返回的是一个由词列表组成的列,每条评论对应一个分词后的词数组。停用词表的路径和格式需要根据实际文件调整,如果代码包里没有现成的停用词表,可以用网上公开的中文停用词表,大概几百个词就够了。分词质量直接决定 Word2Vec 训练的效果,所以这里宁可多花点时间做过滤,也不要急着进入下一步。
2.3 gensim Word2Vec 模型训练与关键参数
词向量是整个情感分析流程的基石,gensim 在这里扮演的角色是把分词后的语料转换成低维稠密向量。训练时最关键的参数是 vector_size、window 和 min_count,它们分别控制词向量的维度、上下文窗口大小和最低词频阈值。对于几千条评论的语料,vector_size 设置在 100 到 200 之间比较合适,太小了语义信息不足,太大了训练慢而且容易过拟合。
from gensim.models import Word2Vec # 准备训练语料 sentences = all_data['tokens'].tolist() # 训练 Word2Vec w2v_model = Word2Vec( sentences=sentences, vector_size=128, window=5, min_count=2, sg=0, epochs=10 ) # 保存模型 w2v_model.save('w2v_model')这里的 sg=0 表示使用 CBOW 模型,sg=1 则是 Skip-gram。中文评论数据量不大,CBOW 训练速度更快,而且在语义相似度上表现足够稳定;Skip-gram 对低频词更友好,如果你后续要处理的是长文本或稀疏词汇,可以换成 sg=1 试试。min_count=2 意味着出现次数少于 2 的词直接丢弃,这对控制词表规模很有用。训练结束后用 w2v_model.wv['好吃'] 就能查看某个词对应的 128 维向量,这是验证模型是否训练成功最直观的方式。
2.4 句子向量化:平均词向量构造固定维度特征
Word2Vec 训练完成后,每条评论的长度依然不一致,但 SVM 要求输入的是固定维度的向量,所以必须把变长的词序列转换成定长向量。最常见的做法是取该条评论所有词向量的平均值,做维度上的逐位平均,最终得到一条 128 维的向量作为这条评论的特征表示。虽然简单,但这个基线方法在情感分类任务上的表现往往不输给复杂的文本 CNN 模型。
import numpy as np def sentence_vector(tokens, model, vector_size=128): vec = np.zeros(vector_size) count = 0 for word in tokens: if word in model.wv: vec += model.wv[word] count += 1 if count > 0: vec /= count return vec all_data['vec'] = all_data['tokens'].apply(lambda x: sentence_vector(x, w2v_model)) X = np.array(all_data['vec'].tolist()) y = all_data['label'].values需要注意,这里做了未登录词过滤:如果某个词不在 Word2Vec 词表里就直接跳过,避免把全零向量混进平均计算。因为 min_count=2 已经丢弃了低频词,所以在做句子向量化时再过滤一次是必要的。最终得到的 X 形状是 (样本数, 128),y 形状是 (样本数,),这就完全满足 SVM 的输入要求了。
3. SVM 分类器训练与模型评估:词向量之后,SVM 如何完成情感判定
3.1 为什么情感分类选择 SVM 而不是朴素贝叶斯或逻辑回归
在词向量特征已经生成的前提下,可以选的分类器其实不少。朴素贝叶斯假设特征独立,但词向量各个维度之间存在语义关联,这个假设很难成立;逻辑回归在线性边界上表现不错,但词向量的高维特征空间里往往存在非线性分布。SVM 擅长在样本量不大、特征维度较高的情况下找到最大间隔超平面,配合核函数还能处理非线性边界。在这个项目里,数据量是几百到几千条,特征维度是 128 维,这正是 SVM 最典型的适用场景。
SVM 的另一个优势是对过拟合的鲁棒性。情感分类任务里,如果正负样本分布不完全均衡,SVM 通过 C 参数调节容错程度,可以避免模型把噪声样本也当成支持向量。相比之下,逻辑回归对异常值更敏感,容易在边界区域产生误判。
3.2 数据集划分与 SVM 模型训练
训练 SVM 之前要做训练集和测试集的划分。代码包里给出了 y_train.npy、y_test.npy、train_vecs.npy、test_vecs.npy 这几个文件,说明作者在生成词向量之后已经做好了切分。实际操作时可以用 train_test_split 按 7:3 或者 8:2 的比例划分,同时设置 random_state 保证结果可复现。
from sklearn.model_selection import train_test_split from sklearn.svm import SVC X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y ) # 训练 SVM,先用线性核 svm_model = SVC(kernel='linear', C=1.0, random_state=42) svm_model.fit(X_train, y_train) # 保存模型和测试向量 np.save('train_vecs.npy', X_train) np.save('test_vecs.npy', X_test) np.save('y_train.npy', y_train) np.save('y_test.npy', y_test)这里用 stratify=y 做分层抽样,保证训练集和测试机里正负样本比例一致。SVC 默认使用的是 RBF 核,但这里先显式指定 kernel='linear' 是为了跑一个基准结果,方便后续对比其他核函数的表现。C=1.0 是默认值,它控制误分类惩罚力度,C 越大模型越注意正确分类每个训练样本,但也更容易过拟合。
3.3 预测效果与评估指标解读
SVM 模型训练完成后,下一步是在测试集上做预测,并用准确率、精确率、召回率和 F1 值来评估效果。情感分析场景里只看准确率是不够的,因为如果测试集里 80% 都是负样本,模型全预测成负样本也能拿到 80% 的准确率,但实际毫无意义。
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score y_pred = svm_model.predict(X_test) print('Accuracy:', accuracy_score(y_test, y_pred)) print('Precision:', precision_score(y_test, y_pred)) print('Recall:', recall_score(y_test, y_pred)) print('F1 Score:', f1_score(y_test, y_pred))从项目自带的 svm_data 目录来看,作者在训练完成后把 SVM 模型、Word2Vec 模型和中间向量都保存了下来,这样下次做新样本预测时不需要重新训练。加载模型后,对新评论的处理流程是:分词 → 过滤停用词 → 平均词向量 → svm_model.predict,这四步走完就能得到 1 或 0 的情感预测结果。
3.4 核函数对比:线性核与 RBF 核到底该选哪个
在这个项目中,SVM 的核函数选择是个很实际的调参点。线性核计算速度快、可解释性好,在词向量特征维度不高且样本量不大时往往已经够用。RBF 核可以把数据映射到更高维空间,能够处理更复杂的非线性边界,但需要调整 gamma 参数,gamma 太大会导致过拟合,边界区域紧贴着训练样本,泛化能力反而下降。
# RBF 核对比 svm_rbf = SVC(kernel='rbf', C=1.0, gamma='scale', random_state=42) svm_rbf.fit(X_train, y_train) y_pred_rbf = svm_rbf.predict(X_test) print('RBF Accuracy:', accuracy_score(y_test, y_pred_rbf))我一般会两个核都跑一遍,对比 F1 值再决定保留哪个模型。gamma='scale' 是 sklearn 默认值,它根据特征数量自动计算 gamma,比手动指定一个固定值更稳妥。从实际经验看,中文评论情感分析用线性核和 RBF 核的差距通常不大,但 RBF 需要多调一组参数,如果没有明显的精度提升,保留线性核模型更划算。
4. 避坑指南:中文情感分析项目的五个常见踩坑点
4.1 编码问题导致读取 Excel 报错或乱码
现象:用 pandas 读取 pos.xls 或 neg.xls 时,中文评论变成乱码,或者直接抛出 UnicodeDecodeError。
原因:Excel 文件内部编码不是常见的 UTF-8,尤其是旧版本的 .xls 文件,可能使用 GBK 或 GB2312 编码。
解决:读取时用 pandas 的 read_excel 并指定 engine='xlrd',如果仍然乱码,可以先用 LibreOffice 或 pandas 将文件另存为 CSV(UTF-8 编码),再通过 read_csv 读取。项目里的 chinese-sentiment-analysis.py 如果直接运行没问题,说明代码已经绕过了这个坑,但如果是自己替换数据文件,这一步很容易翻车。
4.2 Word2Vec 词表中没找到目标词导致全零向量
现象:某个评论分词后,每个词都不在 w2v_model.wv 里,sentence_vector 返回全零向量。SVM 预测时把这个全零向量输入进去,得到的结果偏向训练集中样本多的那一类,完全没有区分度。
原因:min_count 设置过高,把大量低频词直接丢掉了,或者测试集的词和训练集分布差异太大,OOV(Out-of-Vocabulary)比例过高。
解决:降低 min_count 到 1,或者增加训练语料。如果不想重新训练 Word2Vec,可以在 sentence_vector 里对全零向量的情况做回退处理,比如直接返回训练集中所有句子向量的均值,避免全零特征进入 SVM。这个坑在短文本场景里尤其常见,因为短文本本身词就少,再过滤掉一些低频词,剩下可能就一两个词。
4.3 向量维度不一致导致训练报错
现象:运行 chinese-sentiment-analysis.py 时,sklearn 抛出 ValueError: X.shape[1] should be equal to 128,或者类似的维度不匹配错误。
原因:有些评论分词后长度为 0,sentence_vector 返回的是 np.zeros(128),这在维度上没问题。真正的问题往往出在修改了 vector_size 之后没有重新生成向量文件,train_vecs.npy 里存的还是旧的 128 维向量,而新模型期望的是 200 维。
解决:修改 vector_size 后,必须重新执行整个向量化流程,不能直接复用旧的 .npy 文件。我一般会在代码里加一段校验逻辑,打印 X.shape[1],确保和 svm_model.n_features_in_ 一致,再开始训练或预测。这一点不算难,但很容易被忽略。
4.4 SVM 对特征尺度敏感导致准确率异常
现象:换了一批数据后,SVM 准确率从 85% 掉到 60%,但 Word2Vec 训练看起来没毛病。
原因:SVM 对特征尺度敏感,如果某些维度上的值波动范围特别大,会主导距离计算。平均词向量之后,不同维度的数值范围通常比较接近,但如果文本长度差异很大,长文本的向量值可能被平均到很小的范围,短文本的向量值波动更大。
解决:在训练 SVM 前加 StandardScaler 做标准化,把每个维度的均值归零、方差归一到 1。这一步对 RBF 核尤其重要,因为 RBF 核依赖样本间的欧氏距离。这个项目没有显式做标准化,可能是因为默认数据下线性核的表现已经够好,但你改数据后最好补上这一步。
4.5 标签不平衡导致正样本预测效果差
现象:准确率整体不错,但把模型应用到实际评论上时,正样本的召回率很低,很多正面评价被预测成负面。
原因:pos.xls 和 neg.xls 的数据量如果差距较大,训练出的 SVM 会偏向样本更多的类别。比如 neg 有 3000 条、pos 只有 800 条,模型在高置信度区域几乎全部预测为 0。
解决:在 train_test_split 时用 stratify=y 做分层抽样,保证训练集里正负样本比例一致。如果整体数据本身就不平衡,可以考虑在 SVC 里设置 class_weight='balanced',让 SVM 自动调整不同类别的惩罚权重。这个参数只需要一行代码,但效果立竿见影。
5. 进阶验证与模型保全:如何系统判断这份资源的可用边界并迁移到自己的数据上
这份资源的价值不在于你直接跑通它,而在于你能把它拆开、替换成自己的数据,然后验证这个技术路线在你场景里是否成立。我拿到手后做的第一件事,就是不看 chinese-sentiment-analysis.py,先自己按照预处理的顺序重新生成一遍向量文件,再把生成的向量和项目自带的 train_vecs.npy、test_vecs.npy 做对比。如果数值完全一致,说明代码路径理解了;如果出现偏差,偏差通常来自分词结果不一致,这个时候去检查 jieba 版本和停用词表就好。
在这个基础上,建议你做一个 A/B 验证:用项目自带的数据跑一次准确率,再替换成你自己的评论数据跑一次,对比两次的 F1 值。如果第二个结果远低于第一个,问题大概率不在 SVM,而是出在 Word2Vec 的训练语料上——词向量没有充分捕捉到你领域里的词汇用法。比如电商评论里常见“客服态度好”,在通用语料里“客服”和“态度”的关联度可能不高,但在你的数据集里它们高度相关。解药很简单:用你自己的全量语料重新训练 Word2Vec,不要使用别人训练好的词向量模型,领域相关性差的词向量对 SVM 来说是噪声而不是特征。
模型保全方面,我习惯把训练好的 Word2Vec、SVM 和标准化器一起打包保存,这样后续做实时预测时不需要重新训练。Word2Vec 用 gensim 的 save 方法保存后,加载时用 Word2Vec.load 即可;SVM 用 joblib.dump 保存,加载时用 joblib.load。预测新评论的完整链路如下:
import joblib from gensim.models import Word2Vec w2v_model = Word2Vec.load('w2v_model') svm_model = joblib.load('svm_model.pkl') def predict_sentiment(text): tokens = tokenize(text) vec = sentence_vector(tokens, w2v_model) return svm_model.predict(vec.reshape(1, -1))[0] print(predict_sentiment('这家店的服务态度非常好,上菜速度也快')) print(predict_sentiment('等了一个小时还没上菜,环境也特别差'))如果预测结果和直觉不符,不要急着调 SVM 参数,先检查 sentence_vector 里有没有把停用词过滤干净,再看 Word2Vec 词表里是否包含了核心情感词。大部分翻车场景都出在特征工程环节,而不是分类器环节。从那以后我每次换数据集做情感分析,都会强制走一遍这套流程:先验证词向量质量,再做句子向量化,最后才动 SVM 参数,顺序反了只会浪费时间在错误的方向上调参。希望这些拆包和踩坑记录能帮你在自己的文本分类任务上少走弯路。
本文还有配套的精品资源,点击获取