
简介这份资源面向NLP初学者与希望对比不同建模路线的开发者以情感分析为完整案例从无标签语料出发逐步演示打标签、预处理到建模的全流程。内容同时覆盖传统机器学习与深度学习两条技术路线用SVM构造超平面并调优核函数与正则参数用LSTM及双向结构捕捉上下文依赖并对比两者在特征工程、训练速度与资源消耗上的差异。压缩包共24个文件包含14个txt词典与语料、4个py脚本、2个md说明文档、2张png架构图及gitignore、license等配置整体约30.7MB目录按data、dict、img等模块组织便于按步骤复现。已有126人学习下载。读者可据此掌握情感标注、TF-IDF与词袋特征、词嵌入、模型训练与评估的完整链路并获得可直接运行的脚本与词典资源为后续NLP项目打下实践基础。1. 情感分析选型之争SVM 和 LSTM 到底该用哪个电商评论里一句“东西还行就是物流太慢”到底是好评还是差评这就是情感分析要解决的问题。NLP 自然语言处理里情感分析是最经典的落地任务之一而建模路线上一直有两派一派用传统机器学习模型 SVM一派用深度学习模型 LSTM。很多刚入门机器学习的人会纠结是不是深度学习一定吊打传统方法答案没那么简单。SVM 在几千到几万条标注数据、特征工程做得扎实时训练快、可解释、调参少往往能拿到不输深度模型的 F1LSTM 在数据量上万、句子长、上下文依赖强时才有明显优势。这篇笔记就按“数据准备 → SVM 基线 → LSTM 建模 → 对比与排错”的路径把两条路线都跑通让你拿到一套能直接复现的代码和判断标准知道自己的场景该选哪个、参数怎么设、坑在哪。2. 数据准备与特征工程把中文评论变成模型能吃的输入2.1 数据集结构与标签定义情感分析常见的数据格式是一行一条样本包含文本和标签两列。标签一般用 0/1 表示负面/正面也有用 -1/0/1 表示负/中/正的。我一般会先统一成二分类把中性样本要么归入正面要么单独剔除避免三分类让评估变复杂。数据量方面SVM 在 300010000 条时就能跑出稳定结果LSTM 建议至少 10000 条以上否则容易过拟合。拿到数据后第一件事是看类别分布。如果正面 9000 条、负面 1000 条直接训练会让模型偏向多数类准确率虚高但召回崩掉。常见做法是欠采样多数类或对少数类做数据增强比如同义词替换、随机插入。下面这段代码做的是加载数据、看分布、划分训练测试集。import pandas as pd from sklearn.model_selection import train_test_split # 假设数据文件是两列text, label df pd.read_csv(sentiment_data.csv, encodingutf-8) print(类别分布\n, df[label].value_counts()) # 统一标签为 0/1 df df[df[label].isin([0, 1])].dropna(subset[text]) # 分层划分保证训练测试集类别比例一致 X_train, X_test, y_train, y_test train_test_split( df[text].values, df[label].values, test_size0.2, random_state42, stratifydf[label] ) print(训练集, len(X_train), 测试集, len(X_test))逻辑说明stratify参数是关键它让训练集和测试集的类别比例与原始数据一致避免随机划分导致测试集里某一类几乎消失。random_state固定后结果可复现调参对比时不会因为数据划分不同而误判。参数上test_size0.2是常规选择数据量小于 5000 时可以调到 0.3 让评估更稳。2.2 中文分词与停用词处理中文不像英文有空格分隔必须先分词。常用工具是 jieba它对评论类短文本效果够用。分词后要去掉停用词但注意情感分析里“不”“没”“别”这类否定词绝对不能删删了会把“不好”变成“好”这是血泪经验。停用词表建议用哈工大或百度的通用表再手动把情感词从表里剔除。import jieba # 加载停用词但保留否定词 stopwords set(open(stopwords.txt, encodingutf-8).read().split()) keep_words {不, 没, 别, 无, 非, 莫, 未} stopwords stopwords - keep_words def cut_text(text): words jieba.lcut(str(text)) return .join([w for w in words if w not in stopwords and len(w) 1]) X_train_cut [cut_text(t) for t in X_train] X_test_cut [cut_text(t) for t in X_test]逻辑说明len(w) 1过滤掉单字减少噪声但“不”这类单字否定词因为已经在keep_words里需要单独保留。实际写的时候可以改成if w in keep_words or (w not in stopwords and len(w) 1)。停用词表路径按自己环境改没有的话先用 jieba 自带的高频词表替代。2.3 特征表示TF-IDF 与词向量的选择SVM 吃的是数值特征最常用的是 TF-IDF。它把每个词的重要性用“词频 × 逆文档频率”算出来能压低“的”“了”这种到处出现的词权重。参数上max_features控制词表大小评论数据一般 500020000 够用ngram_range设成 (1,2) 能捕捉“不 好”这种二元组合对情感分析提升明显。LSTM 则需要词向量。有两种做法一是用预训练词向量如腾讯词向量、Word2Vec 自己训二是让模型从随机初始化开始学。数据量小于 2 万条时预训练词向量能明显加快收敛数据量大时随机初始化也能学到不错的表示。下面用 Keras 的 Tokenizer 做序列化。from sklearn.feature_extraction.text import TfidfVectorizer from tensorflow.keras.preprocessing.text import Tokenizer from tensorflow.keras.preprocessing.sequence import pad_sequences # SVM 用的 TF-IDF 特征 tfidf TfidfVectorizer(max_features10000, ngram_range(1, 2), min_df2) X_train_tfidf tfidf.fit_transform(X_train_cut) X_test_tfidf tfidf.transform(X_test_cut) # LSTM 用的序列特征 tokenizer Tokenizer(num_words10000, oov_tokenOOV) tokenizer.fit_on_texts(X_train_cut) seq_train tokenizer.texts_to_sequences(X_train_cut) seq_test tokenizer.texts_to_sequences(X_test_cut) # 统一长度短补长截 maxlen 100 X_train_seq pad_sequences(seq_train, maxlenmaxlen, paddingpost, truncatingpost) X_test_seq pad_sequences(seq_test, maxlenmaxlen, paddingpost, truncatingpost)逻辑说明min_df2过滤掉只出现一次的词降低噪声这对机器学习的噪声数据问题很有效。oov_token处理测试集里没见过的词避免索引越界。maxlen100是按评论平均长度定的可以先统计一下分位数取 95% 分位对应的长度太长会拖慢 LSTM 训练。3. SVM 基线建模小数据量下的性价比之王3.1 为什么先跑 SVM 而不是直接上 LSTM很多人一上来就搭 LSTM结果调了两天参数还不如 SVM 跑十分钟。SVM 的优势在于第一它在小样本上泛化能力好靠的是最大间隔原理不容易过拟合第二训练是凸优化结果稳定不像神经网络受初始化影响大第三可解释性强能看哪些词对分类贡献大。所以我的习惯永远是先跑一个 SVM 基线拿到 F1 之后再决定要不要上深度模型。如果 SVM 已经到 0.88LSTM 只到 0.89那多出来的算力成本就不值。3.2 线性核与 RBF 核的参数怎么选SVM 常用两种核线性核和 RBF 核。文本分类里特征维度高TF-IDF 动辄上万维线性核往往就够了而且训练快。RBF 核适合特征维度低、样本非线性可分的情况但参数多、调参慢。我一般先用线性核跑一版如果欠拟合再换 RBF。关键参数是 C它控制对误分类的惩罚。C 大模型偏向把所有训练样本分对容易过拟合C 小容忍更多误分类泛化可能更好。下面用网格搜索找最佳 C。from sklearn.svm import LinearSVC from sklearn.model_selection import GridSearchCV from sklearn.metrics import classification_report # 线性 SVM用 GridSearchCV 调 C param_grid {C: [0.1, 0.5, 1.0, 2.0, 5.0]} svm LinearSVC(max_iter5000, dualTrue) grid GridSearchCV(svm, param_grid, cv5, scoringf1, n_jobs-1) grid.fit(X_train_tfidf, y_train) print(最佳 C, grid.best_params_) best_svm grid.best_estimator_ y_pred_svm best_svm.predict(X_test_tfidf) print(classification_report(y_test, y_pred_svm, digits4))逻辑说明dualTrue在样本数小于特征数时更快文本场景通常满足。scoringf1而不是准确率是因为类别不平衡时准确率会骗人。cv5是常规选择数据量小于 2000 时用 3 折。跑完看classification_report重点看少数类的 recall如果低于 0.7 就要回去处理类别不平衡。3.3 用系数权重看哪些词在驱动判断线性 SVM 训练完每个特征有一个权重系数。权重为正表示这个词偏向正面为负偏向负面。把权重排序就能看出模型学到了什么这也是排查噪声的好办法。如果发现“快递”“客服”这种中性词权重很高说明数据里它们和标签有虚假相关需要检查标注质量。import numpy as np feature_names tfidf.get_feature_names_out() coef best_svm.coef_[0] # 取权重最高的正面词和最低的负面词 top_pos np.argsort(coef)[-20:] top_neg np.argsort(coef)[:20] print(正面词, [feature_names[i] for i in top_pos]) print(负面词, [feature_names[i] for i in top_neg])逻辑说明coef_是线性核的权重向量RBF 核没有这个直接解释。看结果时注意如果出现“不 错”这种二元组在正面词里说明 ngram 起作用了。如果出现明显无关的词优先怀疑标注噪声而不是急着换模型。4. LSTM 建模什么时候深度模型才真正划算4.1 LSTM 处理情感分析的原理与适用边界LSTM 是循环神经网络的一种靠门控机制输入门、遗忘门、输出门决定哪些信息保留、哪些丢弃。情感分析里一句话的情感往往由几个关键词决定但上下文会改变极性比如“本来以为不好结果很惊喜”。LSTM 能记住前面的“不好”再和后面的“惊喜”结合这是它比 TF-IDF SVM 强的地方。但 LSTM 不是万能的。数据量小于 1 万条时它很容易过拟合验证集 loss 先降后升。句子平均长度小于 20 字时LSTM 的长依赖优势发挥不出来反而不如 SVM 快。所以我的判断标准是数据量过万、句子偏长、有明确上下文依赖才值得上 LSTM。4.2 用 Keras 搭一个可用的 LSTM 模型下面是一个基础版 LSTM包含 Embedding 层、LSTM 层、Dropout 和全连接输出。Embedding 维度一般取 100300LSTM 单元数取 64128层数 12 层足够再深容易过拟合。from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Embedding, LSTM, Dense, Dropout, SpatialDropout1D from tensorflow.keras.callbacks import EarlyStopping vocab_size 10000 embed_dim 128 model Sequential([ Embedding(vocab_size, embed_dim, input_lengthmaxlen), SpatialDropout1D(0.3), # 按词维度 dropout比普通 dropout 更适合 embedding LSTM(64, dropout0.2, recurrent_dropout0.2), Dense(32, activationrelu), Dropout(0.3), Dense(1, activationsigmoid) # 二分类输出 ]) model.compile(lossbinary_crossentropy, optimizeradam, metrics[accuracy]) model.summary() early_stop EarlyStopping(monitorval_loss, patience3, restore_best_weightsTrue) history model.fit( X_train_seq, y_train, validation_split0.2, epochs15, batch_size64, callbacks[early_stop] )逻辑说明SpatialDropout1D随机丢弃整个词向量比普通 Dropout 更适合文本能防止模型依赖某几个词。recurrent_dropout在 LSTM 内部做 dropout但会拖慢训练GPU 上可以开CPU 上建议设 0。EarlyStopping的patience3表示验证 loss 连续 3 轮不降就停restore_best_weights保证拿回最优轮次的权重这是防止过拟合的后悔药。4.3 训练过程监控与过拟合判断训练时重点看两条曲线训练 loss 和验证 loss。如果训练 loss 一直降、验证 loss 先降后升说明过拟合要加 dropout 或减 LSTM 单元数。如果两条都降但验证 loss 始终比训练高很多说明数据分布不一致或正则化太强。如果两条都不降说明学习率太大或模型容量不够。import matplotlib.pyplot as plt plt.plot(history.history[loss], labeltrain_loss) plt.plot(history.history[val_loss], labelval_loss) plt.legend() plt.savefig(loss_curve.png) # 测试集评估 loss, acc model.evaluate(X_test_seq, y_test) print(测试集准确率, acc)逻辑说明validation_split0.2从训练集里再切 20% 做验证不碰测试集。测试集只在最后评估一次避免调参时偷看测试集导致结果虚高。如果验证 loss 波动大把 batch_size 调小到 32或者用学习率衰减。5. 避坑与排查SVM 和 LSTM 实战中最容易翻车的地方5.1 分词把否定词删了导致极性反转现象模型把“不好用”预测成正面检查发现“不”被停用词表过滤了。原因通用停用词表包含大量单字否定词直接套用会破坏情感极性。解决手动维护一个否定词白名单分词后强制保留或者用 jieba 的自定义词典把“不好”“不错”当成整体词。5.2 TF-IDF 的 min_df 设太大丢关键特征现象SVM 在测试集上 F1 只有 0.6看特征发现很多情感词没进词表。原因min_df设成 5 或 10把只在少数样本里出现但极性很强的词过滤了。解决min_df设 2 或 1配合max_features控制维度。如果维度爆炸先用卡方检验选 top-k 特征。5.3 LSTM 的 padding 方式影响长句判断现象长评论预测准确率明显低于短评论。原因paddingpre和post会影响 LSTM 最后时刻读到的内容post补零会让模型在句尾读到一堆无意义填充。解决短句用post长句为主时用pre或者用Masking层让模型忽略填充位。5.4 类别不平衡时准确率虚高现象模型准确率 0.92但负面评论召回只有 0.3。原因负面样本只占 8%模型全猜正面也能拿高准确率。解决训练时设class_weight或者用 focal loss评估只看 F1 和少数类召回。SVM 里用class_weightbalancedLSTM 里用compute_class_weight算权重传进 fit。5.5 随机种子不固定导致结果不可复现现象同样的代码跑两次F1 差 3 个点。原因数据划分、权重初始化、dropout 都带随机性。解决固定random_state、np.random.seed、tf.random.set_seed并在 SVM 的GridSearchCV里也设random_state。对比模型时至少跑 3 次取平均单次结果不可信。6. 两条路线的对比与选型决策表跑完两套模型后怎么选不是拍脑袋而是看数据量、延迟要求、可解释性需求三个维度。下面这张表是我自己在项目里用的判断标准数值是经验值具体场景要微调。维度SVM TF-IDFLSTM数据量下限3000 条10000 条训练时间1 万条13 分钟1030 分钟GPU推理延迟毫秒级十毫秒级可解释性高可看词权重低黑匣子调参成本低主要调 C高学习率/层数/单元数长句上下文弱强类别不平衡处理class_weightclass_weight / focal loss如果数据量在 5000 条以下、要求快速上线、需要解释给业务方听选 SVM。如果数据过万、句子长、有 GPU、追求最后几个点的提升选 LSTM。还有一种常见做法是两者融合用 SVM 的输出概率作为 LSTM 的额外特征或者用 LSTM 的 embedding 喂给 SVM在部分数据集上能再涨 12 个点。验证模型是否值得上线不能只看测试集 F1。我一般会做三件事第一拿最近一周的新数据做时间外验证看分布漂移大不大第二构造对抗样本比如加错别字、换同义词看模型鲁棒性第三看错误样本里有没有系统性偏差比如某个品类全错那可能是标注问题而不是模型问题。最后说个我自己的习惯每次跑完新模型先把错误样本按置信度排序看模型最自信但答错的那批。这批样本里往往藏着标注错误或数据泄漏修完再训一版提升比调参明显得多。希望帮到你。本文还有配套的精品资源点击获取