
简介一套围绕word2vec与FastText词向量结合支持向量机SVM的中英文情感分类实战方案面向希望用Python实现情感分类的初学者与研究人员适用于社交媒体分析、客户反馈处理等场景。资源提供完整可运行的Python代码、训练/预测脚本、微博情感数据CSV、停用词表与说明文档覆盖从文本清洗、分词、词向量训练到SVM建模、评估与预测的完整链路。压缩包共19个文件包含5个py脚本、8个txt词表/停用词、3个csv数据集以及效果展示图片、动图和1份md说明文档整体仅23.5MB便于下载与复现。目前已有9889人学习下载是入门文本情感分类的实用素材。通过这套资源读者可以直观理解CBOW与Skip-gram的差异、FastText对未登录词的处理优势以及如何将词向量作为SVM输入特征完成分类同时借助wordCloud脚本和可视化图片直观呈现分析结果。1. 拿到这份 word2vec / FastText SVM 情感分类资源先搞懂它解决什么问题做文本情感分类的同行应该都遇到过这种尴尬拿一个公开情感数据集跑 SVM结果准确率卡在 60% 出头换什么核函数都没用。问题往往不出在 SVM 本身而是喂给它的特征太弱。这份资源把 word2vec 和 FastText 两种词向量方案跟 SVM 分类器串成了一整套可复现的中英文情感分类流程从微博中文语料到英文通用文本都能用。它不仅给你pos_neg.py、train_svm.py、predict.py这些可以直接跑的脚本还配了多份停用词词典和搜狗、百度、腾讯扩展词典以及词云生成和最大句子长度统计的小工具。适合正在做舆情分析、在线评论分类、客服工单打标的工程师也适合刚接触 NLP 想把分类流程完整走一遍的入门者。它不会帮你调出最顶尖的深度学习模型但能把「词向量 线性分类器」这条经典路线的坑提前蹚平。2. 先拆资源包从文件清单到中英文数据预处理流水线2.1 资源包组成不是单一脚本而是一条可替换的数据链拿到sentimentAnalysis_SVM.zip之后解开看到的是下面这批文件我先按用途把它们分成了四组原始数据与中间产物data目录、neg.csv、pos.csv、weibo_senti_100k.csv词典资源Stopword.txt、dict目录下的dict_sougou_utf8.txt、dict_baidu_utf8.txt、dict_tencent_utf8.txt、my_dict.txt、SogouLabDic.txt、dict_pangu.txt核心处理脚本pos_neg.py把原始语料拆成正负样本、train_svm.py训练模型、predict.py对未知文本预测、get_maxlen.py统计句长、wordCloud.py生成词云辅助文件readme.md、requirements.txt、model目录、background.png、background.gif这里值得注意的一点是weibo_senti_100k.csv是中文微博情感语料已经是带标签的格式但它未必直接是pos.csv/neg.csv的结构。pos_neg.py干的事就是把原始宽表拆成两条干净的路径一条是训练用的正负样本文本另一条是后续train_svm.py要读的 CSV。我习惯的做法是拿到资源后先用head -n 5看一眼文件头确认字段分隔符和是否带表头再决定pos_neg.py里的delimiter参数用逗号还是制表符。2.2 中文清洗与分词先处理标点、数字再用 jieba 切开对于中文情感分类直接拿原始句子做词向量训练基本是自找麻烦。标点、数字、语气词都会变成大量低频 token稀释真正有情感倾向的词。这个资源里pos_neg.py的数据预处理部分我把它精简成一个标准函数逻辑如下import re import jieba def clean_chinese_text(text: str, stopwords: set) - str: # 去掉URL和用户——微博语料里这两类噪声特别多 text re.sub(rhttps?://\S, , text) text re.sub(r\w, , text) # 统一小写中文不受影响但对英文部分友好 text text.lower() # 去掉数字和标点保留中英文和基础空白 text re.sub(r[0-9a-zA-Z]|[\W_], , text) # 中文分词返回list words jieba.lcut(text) # 过滤停用词和单字单字通常没有情感判别力 filtered [w for w in words if w not in stopwords and len(w) 1] return .join(filtered)这段逻辑里第一个正则把 URL 和 用户去掉是因为微博语料里http://...和某人跟情感极性基本无关留着只会增加向量空间的维度。[0-9a-zA-Z]和[\W_]的组合容易误伤中文所以我一般把数字、英文单词单独剥离再用空白连起来。jieba.lcut返回的是列表注意别用jieba.cut直接和join混用停用词过滤要放在分词之后才能正确删掉「的」「了」这类高频但无语义的词。这个函数里我没有做词性过滤因为在短文本情感分类场景里保留动词和形容词反而对词向量聚类有帮助。2.3 停用词表和扩展词典为什么一份资源要塞六七个字典Stopword.txt是基础停用词表但资源里还放了搜狗、百度、腾讯三份领域词典外加my_dict.txt和SogouLabDic.txt、dict_pangu.txt。很多人会问停用词表和扩展词典不是矛盾的吗其实分工不同。停用词表用于删除删掉对情感判断没帮助的虚词降低噪声。扩展词典用于分词加固把「好high哦」「这波不亏」这类网络新词以整体形式切出来避免 jieba 把它们切成碎片后丢失情感信息。我一般会这样合并处理def load_all_dicts(dict_dir: str) - set: extra_words set() for fname in [dict_sougou_utf8.txt, dict_baidu_utf8.txt, dict_tencent_utf8.txt, my_dict.txt, SogouLabDic.txt, dict_pangu.txt]: path f{dict_dir}/{fname} with open(path, r, encodingutf-8, errorsignore) as f: for line in f: word line.strip() if word: extra_words.add(word) # 把词直接加进jieba的用户词典提高分词命中率 jieba.add_word(word) return extra_words这段代码里的关键是jieba.add_word(word)它让 jieba 在切分时优先把这串字符保留为一个完整词。比如「我太难了」如果没有词典干预可能会被切成「我 / 太难 / 了」加了「我太难了」后就能整体保留语义更完整。注意errorsignore很有必要那些第三方词典里偶尔混入无效编码不忽略的话脚本会在读到第几行时突然崩掉。文件编码这里也是个常见坑很多公开词典是 GBK 编码这里统一用utf-8加errorsignore是折中方案稳妥的做法是先file命令确认编码再决定用哪个参数。3. 词向量特征提取word2vec 和 FastText 的选型与工程实现3.1 word2vec 训练CBOW 与 Skip-gram 在情感任务里怎么选词向量是整个情感分类管道的特征来源。直接用词频统计的 BoW 特征会丢失「好」和「棒」在语义空间里的相近关系而 word2vec 学习到的向量是把语义相近的词聚到邻近区域。CBOW 用上下文词预测中心词Skip-gram 用中心词预测上下文。在小规模微博语料上我倾向于先用 CBOW 试试因为它平均了上下文信息训练速度更快而 Skip-gram 对低频词更友好但训练更耗时。这里用 Gensim 训练一个简单模型from gensim.models import Word2Vec def train_word2vec(texts, vector_size100, window5, workers4, sg0): model Word2Vec( sentencestexts, # 已经是分词后以空格连接的句子列表 vector_sizevector_size, windowwindow, min_count1, # 微博短文本里低频词不少min_count用1保留更多词 sgsg, # 0表示CBOW1表示Skip-gram workersworkers, epochs10 ) model.save(model/word2vec.model) return modelsentences要求是可迭代的 list每个元素是分词后的 token 列表。vector_size100对于 10 万条微博级语料来说够用做 200 维也不会过拟合window5能兼顾前后文。min_count1会让词汇表很大但情感分类任务有一些表情词和网络缩写出现次数极少却极有判别力所以我保留全部 token。训练好后模型文件里存了词向量矩阵但注意Word2Vec默认会对词向量做归一化SVM 用的就是这份归一化向量。3.2 FastText 引入字符级信息中文未登录词的底气FastText 解决的核心问题是未登录词。word2vec 遇到训练集里没见过的词直接返回 KeyErrorFastText 因为把每个词拆成了字符 n-gram比如「开心」拆成开、心、开心等所以即使遇到新词也能用字符 n-gram 的向量凑出这个词的表示。用 Gensim 训练 FastText 的成本几乎和 word2vec 一样低from gensim.models import FastText def train_fasttext(texts, vector_size100, window5, min_n2, max_n4): model FastText( sentencestexts, vector_sizevector_size, windowwindow, min_count1, sg0, min_nmin_n, max_nmax_n, # n-gram长度范围中文2-4比较合理 epochs10 ) model.save(model/fasttext.model) return modelmin_n2, max_n4这套参数对中文比较合适单个汉字没有太多语义两个字的双字词和三个字的词是中文信息的主要载体四字成语也能覆盖。比英文默认的min_n3, max_n6在中文场景里更稳。训练完成后你可以用model.wv[没见过的词]拿到一个不报错的向量这在预测脚本里是最大的优势——不用对未知词做填充零向量的「后悔药」处理。3.3 从词向量到句子向量加权平均、均值池化还是直接拼接单个词的向量不能直接丢给 SVMSVM 需要一条固定维度的特征向量。最常见做法是把句子中所有词的向量做均值池化。但也有人用 TF-IDF 加权平均。我在这份资源里看到他们把文本转换成「词向量的加权和」所以以下做法是一线工位最常用的import numpy as np def text_to_vector(text_tokens: list, wv_model, vector_size: int 100) - np.ndarray: vectors [] for token in text_tokens: try: vec wv_model[token] except KeyError: # word2vec遇到词表外词直接跳过FastText基本不会到这里 continue vectors.append(vec) if not vectors: return np.zeros(vector_size) # 全平均保留所有维度的scale信息 return np.mean(vectors, axis0)这里对 word2vec 和 FastText 共用同一个函数对于 word2vec遇到缺词就跳过对于 FastText理论上wv_model[token]总能返回一个向量。np.mean(vectors, axis0)是最简单的池化也有人试过 max pooling我个人的经验是在情感分类短文本上均值池化通常比 max 池化稳定因为 max 会把每个维度上最极端的信号挑出来容易放大噪声词的贡献。如果你想微调可以把平均改成加权平均权重用 TF-IDF但我实测在 10 万条微博语料上收益不到 1 个点不值得为此增加依赖。4. SVM 模型训练与评估把词向量变成情感分类器4.1 用 scikit-learn 训练线性 SVM核函数选择与网格搜索词向量特征通常维度是 100 或 200样本数在几万到十几万之间。对这种「维度不算高、样本数偏多」的场景线性 SVM 是最优选。高斯核 RBF 我在这类任务上试过训练慢而且和线性核的准确率差距通常小于 0.5 个百分点但调参成本高得多。所以train_svm.py里的核心逻辑应该是from sklearn.svm import LinearSVC from sklearn.model_selection import GridSearchCV def train_svm(train_vectors, train_labels): param_grid {C: [0.1, 0.5, 1.0, 2.0]} svm LinearSVC( losshinge, # 真正的SVM损失 max_iter5000, # 不设置的话容易收敛警告 random_state42 ) grid GridSearchCV(svm, param_grid, cv5, scoringf1_macro) grid.fit(train_vectors, train_labels) return grid.best_estimator_, grid.best_score_losshinge是 LinearSVC 的正统配置默认的squared_hinge在 sklearn 的 old 接口里非常容易踩坑两者预测边界有细微差别。max_iter5000是在大样本下有必要的默认 1000 次迭代很容易到不了收敛条件然后抛出的警告不会让程序崩溃但模型可能没有达到最小损失。用 5 折交叉验证跑C的搜索我一般会让C在对数尺度上排列从 0.1 到 2.0 就够因为词向量本身做过归一化C1 附近通常是较优区域。4.2 模型评估指标宏观 F1 和类别分布一样重要情感分类数据通常三类正面、负面、中性微博数据集里中性样本往往偏少。只看准确率会被多数类主导所以脚本里要同时输出precision,recall,f1-score并且用macro平均from sklearn.metrics import classification_report, confusion_matrix def evaluate_svm(model, test_vectors, test_labels, label_names): preds model.predict(test_vectors) print(classification_report(test_labels, preds, target_nameslabel_names)) cm confusion_matrix(test_labels, preds) print(Confusion Matrix:\n, cm)classification_report会给出每个类的精确率、召回率和 F1这样你能很快看出是哪一类拖累了整体。混淆矩阵里如果发现「中性」被大量错分成「正面」说明词向量没有捕捉到中性的边界应该回到数据预处理阶段检查是否中性样本本身存在标注噪声。这条进阶验证路径是这套资源相对良心的设计——它不强求一个漂亮的分类准确率而是给你看模型错在哪。4.3 从 pos_neg.py 到 train_svm.py完整流程怎么串起来资源包里的脚本是分步的pos_neg.py负责把weibo_senti_100k.csv处理成pos.csv、neg.csvtrain_svm.py负责从 CSV 读数据、训练、保存模型predict.py负责对单条或批量文本打分。我平时会把这个流程串成一个 makefile 风格的 shell 命令减少中间步骤手滑出错python pos_neg.py --input weibo_senti_100k.csv --output data/ python train_svm.py --pos data/pos.csv --neg data/neg.csv \ --model_dir model/ --w2v_size 100 python predict.py --model model/svm.pkl --text 这个产品性价比很高推荐第一行做数据切分第二行训练词向量和 SVM注意train_svm.py内部要先调用分词和词向量训练再进入 SVM第三行对单条新文本返回positive/negative的标签。这里的--w2v_size参数贯穿到 SVM 的输入维度如果你改大了记得同步text_to_vector里的维度参数否则会撞上矩阵形状不匹配。这一点后面避坑章节还要单独说。5. 避坑与常见问题排查从预测脚本到中文乱码的四条血泪经验5.1 未知词向量为 0F1 从 85% 掉到 70%现象用 word2vec 训练好的 SVM 在测试集上准确率正常但拿一条包含不少新词的真实评论去predict.py模型把明显正面的句子判成了负面。观察 A 后发现这条文本里多个新词都没有向量text_to_vector把这些词直接跳过最终句子向量变成少数几个停用词的均值甚至变成全零向量。原因word2vec 的词表来自训练语料真实世界的用户评论总会出现训练时没见过的用法、缩写、网络新词。全零向量进入线性 SVM 时相当于把样本放到了坐标原点附近而原点离决策边界方向恰恰容易被分到大多数类。解决把text_to_vector里的continue改成兜底逻辑。首选方案是用 FastText 替换 word2vec 做预测如果坚持 word2vec可以保留UNK的随机小向量但更简单的是对全零向量返回一个之前训练集句向量的均值。我后来把这个逻辑写成了函数参数def text_to_vector(text_tokens, wv_model, vector_size, fallbackmean): if fallback mean: # 加载训练集所有句向量的均值作为fallback ...从那以后我再没在预测阶段因为新词翻过车。5.2 jieba 分词把整个句子切成一个超大词现象pos_neg.py处理中文时某些长度较长的句子分词后出现了一个包含十几个字的超长词比如把「这个手机屏幕摔碎了但还能用性价比很高」整体当成一个词。词向量训练时这个超长词出现次数低根本学不到有效向量还会占用词表空间。原因资源里的停用词表把「这个」「但」「还」等词都删掉了jieba 在连续一段去掉了停用词的文本上有可能因为缺少切分锚点而把相邻词合并。另外 jieba 不少版本对长文本连续字母数字串的分词策略激进。解决在清洗阶段不要把所有停用词在分词前去掉。正确顺序是先分词再删停用词。我写的clean_chinese_text里已经遵守了「先jieba.lcut再过滤过滤词」的顺序。另外给 jieba 加载dict_sougou_utf8.txt等词典也可以让切分更细粒度。验证方法是用get_maxlen.py统计pos.csv中最长句子的长度如果出现异常的长词或长句把那条样本单独拉出来看分词结果。5.3 读取 CSV 时因引号不配对导致ParserError现象运行train_svm.py读pos.csv时抛ParserError: expected after line 12, I got ...用文本编辑器打开 CSV 却看不出明显问题。原因pos.csv里的评论文本包含英文引号或半角引号且内容里出现了不配对的情况。csv 模块默认启用QUOTE_MINIMAL在遇到I said wow这类文本时字段会被切断于是后面的列错位同一行出现过少的字段数量。解决读 CSV 时显式关闭无关的引号语法或者用 pandas 指定quotingcsv.QUOTE_NONE如果文本内部还有英文引号最好在写入时就转成全角引号。我通常修改读取的参数import pandas as pd df pd.read_csv(data/pos.csv, encodingutf-8, enginepython, on_bad_linesskip)enginepython会更宽容地处理多行字段on_bad_linesskip会把问题行直接跳过但注意跳过的样本会造成类别分布偏移所以跳过之后要打印日志记录行号。5.4 词向量维度与 SVM 输入维度不一致导致矩阵相乘错误现象训练时使用--w2v_size 50模型保存后修改了配置想用 100 维重新预测。predict 时model.predict(features)报ValueError: X has 50 features, but SVC is expecting 100 features。原因train_svm.py训练时把 SVM 和词向量模型都保存在model目录但predict.py只加载了 SVM 模型文件没有同步加载新词向量模型或者加载了不同名字的旧模型。解决每次训练后把词向量维度写入元信息文件比如vector_size.txt预测前先读取该文件。更稳妥的是在predict.py里同时加载word2vec.model和 SVM 模型从词向量模型中获取vector_size杜绝人工配置不一致。这个坑看似低级但项目调试越到后期越容易触发尤其是在技能迁移到另一个项目后。6. 进阶验证用词云和最长句子长度校准文本处理流水线6.1 get_maxlen.py 与 wordCloud.py 的实战用途get_maxlen.py统计语料中的句子长度分布很多人以为这只是看看数据其实它对模型选择影响很大。如果pos_neg.py按固定截断长度切分句文本get_maxlen能告诉你截断长度是否合理如果句子普遍很短少于 20 个 token词向量窗口可以缩小到 3训练更快。我通常这样运行并观察分布python get_maxlen.py --input data/pos.csv --tokenized输出的第 95 百分位长度就是后续 SVC 输入向量不产生截断损失的参考线。如果发现异常的高值比如超过 200 个词大概率是清洗没干净文本里混入了超长拼写或没有换行的网页源码。wordCloud.py可以生成正面和负面词云它的诊断价值在于词云里如果出现大量中性词例如「东西」「时候」说明停用词表还不完整如果正负词云的高频词高度重叠说明数据标签可能存在噪声或者词向量空间没有把情感方向拉开。比如background.gif/background.png是词云的背景图脚本应该支持替换成自定义图案。6.2 给新语料做一次性体检的脚本套路拿到任何新语料不限于这份微博数据我都会先跑一遍类似下面的体检def inspect_corpus(filepath: str): df pd.read_csv(filepath) print(样本数:, len(df)) print(标签分布:\n, df[label].value_counts()) df[text_len] df[text].map(lambda x: len(x)) print(句子长度分位数:q50, df[text_len].median(), q90, df[text_len].quantile(0.9))这一步能提前发现类别严重不平衡、空文本字段、超长异常样本。接着做一遍清洗和分词随机挑几条检查分词质量。我自己的习惯是第一次练兵用 2000 条样本跑完整流程确认每个脚本都通了再上全量 10 万条。因为词向量如果有一行脏数据混进去训练时不会报错但会把某个词的向量污染SVM 决策边界也随之偏移。6.3 从训练到预测的最短闭环留一份可以反复调用的经验这套资源最大的价值不在单脚本而在于它的文件设计逼着你按「预处理 → 词向量 → SVM → 评估 → 预测」的顺序走完。我把最短闭环浓缩成下面这段可以直接抄的代码块用于验证环境是否全部就绪# 单批推理给定一条文本输出情感标签 def predict_one(text: str, svm_model, wv_model, stopwords: set): cleaned clean_chinese_text(text, stopwords) tokens cleaned.split() vec text_to_vector(tokens, wv_model, wv_model.vector_size) vec vec.reshape(1, -1) label svm_model.predict(vec)[0] return {0: 负面, 1: 正面}[label]这段代码把前面所有函数串成一条线clean_chinese_text负责清洗分词text_to_vector负责获得句向量svm_model.predict负责分类。注意wv_model.vector_size在 Gensim 4.x 中通过属性访问在 3.x 里是wv_model.vector_size也通用但早期版本为model.wv.vector_size。这个闭环跑通后你就能把任何一条输入文本快速放入现有模型看到模型对具体词向量的敏感度。从那以后我每次换新语料都强制先跑一遍get_maxlen 词云 单条预测的体检流程确认数据形态没变再上全量训练。希望这套关键词相连的事前检查能帮你也少走几次弯路。本文还有配套的精品资源点击获取