ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Twitter情感分析实战:从TF-IDF到LSTM与LoRA微调的完整技术路线

Twitter情感分析实战:从TF-IDF到LSTM与LoRA微调的完整技术路线 简介这是一份面向机器学习与Python数据挖掘学习者的Twitter情感分析实战套餐由20个可运行Python脚本构成覆盖从传统机器学习到深度学习再到LLM微调的完整技术栈。资源共23个文件包括20个源代码、2个CSV训练与验证数据集和1个说明文档压缩包约2.03MB解压后数据规模约10MB脚本覆盖数据预处理、特征工程、模型训练与评估等完整流程。代码手工整理、可直接运行依次演示文本清洗、停用词过滤、词干提取、TF-IDF向量化、词云可视化、情感极性判别并实现逻辑回归、SVM、朴素贝叶斯、决策树、随机森林、XGBoost、LightGBM、CatBoost、LSTM、CNN、BiLSTM等多种模型第19、20个脚本还演示了基于Transformers和Gemma 7B的微调流程可对比不同方法的准确率与F1分数。目前已有99人学习下载适合需要对照完整代码快速理解NLP情感分析建模流程、对比算法效果并开展实验的初中级开发者也可作为毕业设计或竞赛的参考基线。1. 为什么 Twitter 情感分析值得当 AI 实战入口20 份源码、10 MB 数据集和一条完整落地的闭环Twitter 情感分析大概是 NLP 实战里性价比最高的一道题数据是公开的推文文本任务是判断每条推文的情感倾向正面、负面、中性、无关既能把 pandas、正则清洗、特征工程这些基本功练扎实又能从传统机器学习一路跑到 LSTM、TextCNN 和 LLM 微调。这份资源打包了 20 个可运行源码和两个 CSV 数据集训练集加验证集共 10 MB从 XGBoost、随机森林到 BiLSTM、TextCNN再到 Gemma 7B 的 LoRA 微调都覆盖到了。适合两类人想在自己电脑上跑通完整情感分析流程的 Python 工程师以及准备面试、需要横向比较多种模型效果的候选人。文章按我拆项目的习惯从数据读入讲到参数坑照着改就能复现。2. 数据读取与清洗两张 CSV 里藏着全流程的第一个分水岭2.1 数据集结构四列格式、标签分布与读取注意先说数据集本身。twitter_training.csv 是训练集twitter_validation.csv 是验证集两者的列结构一致。公开数据集的常见格式是四列第一列推文 ID第二列实体或话题名比如某个品牌、某个赛事第三列情感标签Positive、Negative、Neutral、Irrelevant第四列才是真正的推文文本。这个四列结构我在多个公开数据集里都见过readme.txt 里一般也会注明如果拿到手发现列名对不上第一件事就是打印前五行把列确认清楚。读取的时候有个小坑推文文本里经常夹着逗号、引号甚至未闭合的引号直接让 pd.read_csv 用默认的 C 引擎解析可能整行错位。常见做法是加 enginepython虽然慢一点但对脏 CSV 的容错高很多。我一般还会先把 text 列统一成 str否则后面正则清洗时遇到 NaN 直接报 TypeError。下面这段是数据读取和标签分布检查的可运行代码import pandas as pd df_train pd.read_csv(twitter_training.csv, encodingutf-8, enginepython) # 原 CSV 可能没有表头这里手动指定列名 df_train.columns [id, entity, sentiment, text] print(df_train.head()) print(df_train[sentiment].value_counts(normalizeTrue)) print(空值数量, df_train.isna().sum().sum()) df_train[text] df_train[text].fillna().astype(str)代码逻辑先用 read_csv 的 enginepython 读入容忍引号错位然后手动指定四列列名避免第一条记录被误当表头value_counts(normalizeTrue) 输出的是每类标签的占比而不是条数方便直接对比训练集和验证集的分布差异最后把 text 列统一成字符串NaN 用空串填充保证后续清洗函数能安全处理。参数说明encoding 默认用 utf-8如果读进来乱码再试 latin-1 或 ISO-8859-1公开的老版本推文数据有不少是这种编码。enginepython 和 c 在列数很多时性能差异明显但这个数据集只有四列性能损失可以忽略。资源里 6-NLP Twitter With ML.py 和 18-twitter sentiment analysis eda random forest.py 的读取段基本都是这个结构区别只在列名映射方式上。标签分布确认完之后再看一眼文本长度分布。Twitter 单条推文有字符上限但数据集中经常混着爬取时拼接的长文本长度方差很大。我习惯加一列 text_len 画个直方图确认截断位置这个值直接决定后面深度学习的 maxlen 参数设多少。2.2 清洗 pipelineURL、、#、停用词与词干化的取舍情感分析的清洗和通用文本分类不完全一样。通用分类可能把 #Apple 当成话题整体保留但在情感任务里# 号和 用户名既不携带情感又会把特征空间撑大通常要处理。资源里 11-twitter sentiment analysis sentiment polarity.py 用了 TextBlob 那套轻量处理5-Twitter Sentiment Analysis Using NLP.py 则做了更重的清洗包括去 HTML 实体和 unicode 符号。我一般把清洗函数拆成五步每步目的明确。URL 直接删链接没有情感倾向 用户名删掉它是交互信息不是情感信息# 话题符删掉但保留话题词比如 #iPhone 变成 iPhone因为话题词本身可能是情感对象非字母字符统一清掉最后转小写加词干化。下面是一版可以直接落地的清洗函数注意停用词表已经做了情感任务的特殊处理import re from nltk.corpus import stopwords from nltk.stem import SnowballStemmer # 情感任务必须保留否定词否则 not good 会被洗成 good negation_words {not, no, never, nor, cannot} stop_words set(stopwords.words(english)) - negation_words stemmer SnowballStemmer(english) def clean_tweet(text): text re.sub(rhttp\S, , str(text)) # 去链接 text re.sub(r\w, , text) # 去 用户名 text re.sub(r#(\w), r\1, text) # 去 # 保留词 text re.sub(r[^a-zA-Z\s], , text) # 去数字和符号 text text.lower() # 统一小写 words text.split() words [w for w in words if w not in stop_words] words [stemmer.stem(w) for w in words] return .join(words) df_train[clean_text] df_train[text].apply(clean_tweet) print(df_train[[text, clean_text]].head())代码逻辑五个正则按顺序处理先删 URL 是因为 URL 里可能带 和 #顺序反了会残留字符影响后续匹配。stop_words 从 nltk 默认英文停用词表里扣掉了 not、no、never、nor、cannot 这几个否定词防止清洗把语义反转。SnowballStemmer 把 playing、played、plays 统一成 play能压特征维度。apply 后生成新列 clean_text原 text 列保留方便后面做 EDA 对比。参数说明这里最要注意的就是停用词表不能照单全收。nltk 自带的 stopwords 包含 not、no 这类否定词如果不排除那听起来是玩笑的 This is not good 会被清成 good情感直接反掉。项目里 17-NLP Assignment 1.py 就踩过这个坑改成自定义停用词表后指标才恢复。词干化也要谨慎PorterStemmer 和 SnowballStemmer 对部分词的处理结果不同后者对现代英语更稳资源里两种都有选一种保持全流程一致就行。清洗后建议顺手统计一下高频词。项目里 18 号脚本用 Counter 做了词频统计这一步的意义在于快速验证清洗方向如果 the、a、is 还占着前排说明清洗没走对如果 good、bad、love 进了前五清洗方向基本正确。2.3 标签编码与数据集划分别在两个 CSV 上各玩各的清洗之后就是标签编码。情感标签是字符串分类器大多要吃整数。常见做法是用 sklearn 的 LabelEncoder 把四个字符串映射成 0 到 3这里有个容易翻车的点必须在训练集上 fit 一次之后验证集只做 transform否则两个集合的编码映射可能不一致模型输出跟着错位。from sklearn.preprocessing import LabelEncoder encoder LabelEncoder() y_train encoder.fit_transform(df_train[sentiment]) # 验证集只用同一映射转换不重新 fit # y_val encoder.transform(df_val[sentiment]) print(编码映射, dict(zip(encoder.classes_, encoder.transform(encoder.classes_))))代码逻辑先在训练集标签上 fit让模型记住字符串到整数的映射之后验证集用同一映射 transform。打印 classes_ 和对应整数确认 Positive 编码成 2 还是 0避免后面读 classification_report 时对错行。参数说明老版本 sklearn 的 LabelEncoder 遇到 NaN 会直接报错所以编码前先处理空值常见做法是 df[sentiment].fillna(Neutral)。标签编码看起来简单但在多分类文本任务里出现频率很高我见过不止一次因为训练和验证编码不一致导致的“模型崩溃”。数据集划分按资源本意来即可两个 CSV 本身就是训练/验证对不再额外切分。如果要调超参可以从训练集里切 10% 当开发集验证集留到最后看真实效果。这样既保证了验证集的纯净又能拿到 early stopping 的观察依据。3. 传统机器学习路线TF-IDF 特征工程与 8 个分类器的横向对比3.1 TF-IDF 参数max_features、ngram_range、min_df 怎么配传统机器学习做情感分类核心是先把文本变成向量。资源里最集中的就是 TF-IDF 路线TfidfVectorizer 把每条推文转成稀疏向量再喂给分类器。TfidfVectorizer 参数看着多真正影响结果的其实只有三个max_features、ngram_range、min_df。max_features 控制特征维度。Twitter 数据集词汇量通常在五万到十万之间如果全量保留特征矩阵会非常大而且低频词基本只出现在一两条推文里对泛化没有贡献。我一般取 3000 到 1000010MB 这个规模取 8000 足够。ngram_range 决定是否保留词序信息单用 (1,1) 会丢掉 not good 这种共现组合设成 (1,2) 能保留两个词的组合对情感分类收益明显代价是特征维度翻倍。min_df 设 3意思是词至少在三条推文里出现才保留和 max_features 形成双保险。from sklearn.feature_extraction.text import TfidfVectorizer vectorizer TfidfVectorizer( max_features8000, ngram_range(1, 2), min_df3, sublinear_tfTrue, stop_wordsenglish ) X_train_tfidf vectorizer.fit_transform(df_train[clean_text]) print(特征矩阵形状, X_train_tfidf.shape)代码逻辑fit_transform 吃进清洗后的文本列输出文档-词项稀疏矩阵。sublinear_tfTrue 把词频换成 1log(TF)压制高频词的爆发影响stop_wordsenglish 是第二道保险兜住前面清洗可能漏掉的停用词ngram_range(1,2) 让特征里同时存在单词和相邻词对。参数说明这三个参数是连动的。ngram_range 越大max_features 越容易被占满min_df 越大能进特征空间的词越少。在 Twitter 这种噪声高的短文本上我建议先固定 ngram_range(1,2) 和 min_df3只调 max_features。验证集分数上不去时优先动这个参数而不是急着换模型。TF-IDF 矩阵出来后看一眼非零元素占比。如果太稀疏很多分类器效果会打折扣如果非零占比过高说明文本太短、特征区分度不够。推文平均长度六十到一百个 token稀疏度在 1% 到 3% 属于正常区间超出这个范围就要回头检查清洗函数。3.2 一个 Pipeline 串起 6 个 sklearn 分类器工程收尾的关键写法资源里 6 号脚本和 18 号脚本把分类器轮着跑了一遍。这里有个工程要点把向量化和分类器串成 Pipeline训练和预测只调一次 fit/predict而且换分类器时不用重新 fit 向量化器避免数据泄露。from sklearn.pipeline import Pipeline from sklearn.linear_model import LogisticRegression from sklearn.svm import SVC from sklearn.naive_bayes import MultinomialNB from sklearn.ensemble import RandomForestClassifier pipelines { lr: Pipeline([(tfidf, vectorizer), (clf, LogisticRegression(max_iter1000, C1.0))]), nb: Pipeline([(tfidf, vectorizer), (clf, MultinomialNB(alpha0.1))]), svm: Pipeline([(tfidf, vectorizer), (clf, SVC(kernellinear, C1.0))]), rf: Pipeline([(tfidf, vectorizer), (clf, RandomForestClassifier(n_estimators200, max_depth20))]), } for name, pipe in pipelines.items(): pipe.fit(df_train[clean_text], y_train) # y_pred pipe.predict(df_val[clean_text]) print(f{name} 完成训练)代码逻辑每个 Pipeline 包含同一个 vectorizer 和不同的分类器训练时 Pipeline 会先 fit 向量化器再训练分类器预测时同理。换分类器只换 clf 环节特征工程部分完全复用。LogisticRegression 用 C1.0 控制正则强度MultinomialNB 的 alpha 用 0.1 而不是默认 1.0短文本高噪声场景下降低平滑系数往往更好。参数说明SVC 用线性核在稀疏高维特征下比 RBF 核快得多效果也更好文本特征本身高维不需要核函数再映射。RandomForest 必须限制 max_depth否则容易过拟合短文本情感分类深度 20 层左右足够。这几个分类器训练速度差异明显逻辑回归和朴素贝叶斯几十秒随机森林几分钟SVC 在一万特征内还算快再多就吃 CPU 了。3.3 XGBoost / LightGBM / CatBoost把文本特征当表格数据打资源里的 4-Simple sentiment analysis with XGBoost.py 专门试了 XGBoost。这套做法的本质是把 TF-IDF 稀疏矩阵当表格数据喂给树模型。XGBoost、LightGBM、CatBoost 都支持稀疏输入对特征缩放不敏感能捕捉特征间的非线性关系。实际使用中XGBoost 的经典参数组合是 n_estimators300、max_depth6、learning_rate0.1配合 subsample 和 colsample_bytree 防过拟合。LightGBM 训练速度更快适合特征维度更大的场景。CatBoost 擅长类别型特征但在纯 TF-IDF 场景里优势不明显所以如果是文本分类我一般不优先选它。import xgboost as xgb from sklearn.metrics import accuracy_score xgb_model xgb.XGBClassifier( n_estimators300, max_depth6, learning_rate0.1, subsample0.8, colsample_bytree0.8, eval_metricmlogloss ) xgb_model.fit(X_train_tfidf, y_train) # y_pred xgb_model.predict(X_val_tfidf) # print(XGB accuracy:, accuracy_score(y_val, y_pred))代码逻辑subsample0.8 和 colsample_bytree0.8 是两列防过拟合的关键参数前者每棵树只用 80% 样本后者每棵树只用 80% 特征对稀疏 TF-IDF 特征尤其有效。eval_metric 设成 mlogloss 适合多分类任务。标签必须是整数所以前面 2.3 节的 LabelEncoder 是前置条件。参数说明n_estimators 不是越大越好300 配 early stopping 实际效果优于 1000。max_depth6 是文本任务的常用起点超过 12 几乎必过拟合。learning_rate 降到 0.05 能再提一点精度但训练时间翻倍小数据集没必要。从这套资源跑下来的经验看在 10MB 这个数据规模上逻辑回归和线性 SVC 的分数通常不输给 XGBoost。树模型的优势要在特征工程更丰富的时候才明显。我的建议是先跑逻辑回归拿 baseline再用 XGBoost 或 LightGBM 验证是否有提升不要一上来就上最重的模型。4. 深度学习路线Tokenizer、BiLSTM 与 TextCNN 的关键参数和取舍4.1 Tokenizer 与 pad_sequences词索引转换的边界条件深度学习路线和 TF-IDF 路线的分水岭在于文本不再被转成稀疏特征向量而是被转成整数索引序列再映射到 Embedding 向量。资源里 1-Twitter Sentiment Analysis using LSTM.py 和 20-Sentiment Analysis with LSTM Model.py 用的都是 Keras 这套流程。第一步是 Tokenizer统计词频建立词到索引的字典。Tokenizer 的关键参数是 num_words、filters、oov_token。num_words 控制词表大小只保留最高频的词oov_token 一定要设否则验证集里没见过的词会被直接丢弃序列长度和语义都会受影响。from tensorflow.keras.preprocessing.text import Tokenizer from tensorflow.keras.preprocessing.sequence import pad_sequences tokenizer Tokenizer(num_words20000, oov_tokenOOV) tokenizer.fit_on_texts(df_train[clean_text]) X_train_seq tokenizer.texts_to_sequences(df_train[clean_text]) X_train_pad pad_sequences(X_train_seq, maxlen120, paddingpost, truncatingpost) print(序列形状:, X_train_pad.shape)代码逻辑fit_on_texts 只在训练集上建字典texts_to_sequences 把每句话变成索引列表。pad_sequences 把长短不一的序列统一成 maxlen120比 120 长的截断短的补 0。paddingpost 在尾部补零truncatingpost 优先截断尾部这样句子开头的信息能保留对情感分类很重要因为情感词汇往往出现在句首。参数说明maxlen 的选择和 TF-IDF 里的 min_df 一样关键。Twitter 文本平均 40 到 80 个 token我一般先画出序列长度分布取 95 分位当 maxlen避免太长浪费算力、太短丢信息。num_words20000 对这个规模的数据偏保守如果发现验证集 OOV 比例高可以提到 30000。提示保存 Keras 模型时tokenizer 必须一并用 pickle 存下来。model.save 只保存网络权重不保存词表映射丢了 tokenizer 就等于丢了解码器预测阶段会彻底卡住。4.2 一个可跑的 BiLSTMEmbedding 维度、Dropout 和优化器怎么配LSTM 在短文本情感分类里是经典主力。资源里 1 号脚本用了单层 LSTM20 号脚本加了 Dropout而带 Bidirectional 的变体会在同样的数据上多 2 到 3 个点。双向的意义在于情感判断往往需要同时看前后文单向 LSTM 只能从左到右积累信息双向等于把倒序也扫了一遍。下面这个模型结构是 LSTM 路线的通用骨架。Embedding 层把 token 索引映射成稠密向量Bidirectional 包裹 LSTM 层后面接 Dense 输出层。多分类用 softmax二分类才用 sigmoid。from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Embedding, Bidirectional, LSTM, Dense, Dropout from tensorflow.keras.optimizers import Adam model Sequential([ Embedding(input_dim20000, output_dim128, input_length120), Bidirectional(LSTM(units64, return_sequencesFalse, dropout0.3)), Dense(64, activationrelu), Dropout(0.3), Dense(4, activationsoftmax) ]) model.compile(optimizerAdam(learning_rate5e-4), losssparse_categorical_crossentropy, metrics[accuracy])代码逻辑Embedding 的 input_dim 必须和 tokenizer.num_words 一致否则加载权重时报维度错误output_dim128 是经验值再大收益不增显存却涨。LSTM 里 units64 是隐层维度dropout0.3 是层内 dropout 防过拟合。Dense(64) 加 Dropout 做分类头最后 Dense(4) 对应四个标签。参数说明sparse_categorical_crossentropy 对应整数标签如果你做了 one-hot 就得换 categorical_crossentropy。有个反直觉的现象Adam 默认学习率 1e-3 对 LSTM 偏大训练曲线会先降后震荡降到 5e-4 后收敛更稳。类别不平衡时还要在 model.fit 里设 class_weight让少数类获得更高加权直接改善 F1。4.3 TextCNN 对比 BiLSTM训练速度与精度的真实差异资源里 13-DL Assignment 02 CNN for Text Classification.py 走的是 TextCNN 路线。TextCNN 用多个不同尺寸的卷积核在序列上滑动本质是提取 n-gram 特征比 LSTM 快很多因为卷积可以并行。滤波器尺寸通常取 3、4、5对应词窗口大小。典型结构是 Embedding → Conv1D GlobalMaxPooling1D → Dense。Conv1D 的 filters128kernel_size3 或同时用 3、4、5 三个尺寸activationrelu然后 global max pooling。max pooling 保留每个卷积核最强烈的信号也就是把一句话里最关键的 n-gram 提取出来。这个设计天然适合短文本因为情感词往往是局部出现的。对比项TextCNNBiLSTM训练速度CPU 可跑快 3 到 5 倍建议 GPU训练慢参数敏感点kernel_size、filtersmaxlen、dropout、学习率长距离依赖弱只看局部窗口强能看整句适用数据规模10MB 以下小数据够用中等数据更能体现优势实践结论TextCNN 在 CPU 上跑 10 个 epoch 比 BiLSTM 快 3 到 5 倍精度低 1 到 2 个点但很稳定。BiLSTM 胜在能建模远距离依赖但推文长度有限远距离依赖其实不多。没有 GPU 就先上 TextCNN追求 F1 上限且有 GPU就上 BiLSTM。资源里 19 号和 12 号脚本走的是前一条路1 号和 20 号走后一条。如果想再提分可以在 Embedding 之上接预训练词向量。资源里 8-Lightning Sentiment Analysis.py 用 gensim 的 KeyedVectors 加载 Word2Vec 向量替换随机初始化。前提是词表对齐预训练字典里的词必须有稳定索引映射缺失的词用随机小值初始化否则加载时全是 KeyError。5. 避坑复盘情感分类项目里反复踩的 5 个坑5.1 停用词把 not 过滤掉情感直接翻转现象清洗后训练集准确率反而低于不清洗验证集上 This is not good 被预测成 Positive。原因nltk 默认 stopwords 包含 not、no、never、nor 这类否定词。清洗函数把它们全删了not good 变成 good语义完全反转。这是情感分类里最隐蔽的翻车点文本看着挺干净指标却莫名其妙掉 5 到 8 个点。解决情感任务前先把否定词从停用词表里排除。我用的写法是 set(stopwords.words(english)) - {not,no,never,nor,cannot}。第 2 章的清洗函数已经这么做了。更深一层是把 not good 拼成 not_good 当一个整体 token需要配合自定义分词器适合对 F1 有更高要求的场景。5.2 训练集和验证集用两套清洗逻辑分数虚高现象训练集做了完整清洗验证集只简单处理验证准确率 98%换到真实数据立刻掉到 70%。原因两个 CSV 分别处理时如果为了快速跑通只清洗了训练集验证集保留原始文本分类器学到的是清洗后的特征分布验证集带着 URL 和符号两边特征空间不一致评估结果等于白测。解决把清洗函数抽到一个 utils.py 里训练集和验证集共用vectorizer 和 tokenizer 只 fit 一边、transform 两边。我现在的习惯是预测前强制校验两边文本长度分布差得多就回去查清洗链路。5.3 GPU 显存溢出batch size 与序列长度没匹配现象训练到第二个 epoch 报 CUDA out of memory有时直接把 Jupyter kernel 挤崩。原因Embedding 输出维度乘以序列长度乘以 batch size就是 LSTM 每一步要占的显存。maxlen120、词向量 128 维、batch64在 4GB 显存上勉强跑换成双向 LSTM 参数量直接翻倍加上验证集前向传播显存瞬间打满。解决优先把 batch size 降到 16 或 32其次把 maxlen 降到 80最后把 Embedding output_dim 降到 100。显存大户是双向层如果还超就砍掉 Bidirectional 换单向。资源里 7-Twitter Sentiment Analysis With LLM on GPU.py 的场景更极端常见做法是开 gradient checkpointing 和混合精度用小 batch 撑住大模型微调LSTM 遇到 OOM 同样按这个顺序排查。5.4 标签分布失衡准确率虚高F1 现原形现象准确率 89%但 classification_report 里 Negative 类的 F1 只有 0.31验证集里 Negative 样本几乎全被预测成 Neutral。原因公开 Twitter 数据集的 Neutral 类占比可能接近 40%模型把样本全预测成 Neutral 就能拿到高准确率。accuracy 在类别不平衡时被多数类主导掩盖少数类的失败。解决评估指标从 accuracy 换成 macro-F1 或 weighted-F1同时要在训练阶段处理类别不平衡。Keras 里用 class_weightsklearn 用 compute_class_weight 算出权重传入模型。XGBoost 可以给每个类别调权重参数多分类时逐类设置。资源里不少脚本的评估段都打印了 classification_report就是为了防止被准确率骗。5.5 词表外 token 与 Embedding 维度对不上现象保存模型后重新加载predict 时报 IndexError或者结果全部变成同一类。原因两个典型来源。一是 Tokenizer 没设 oov_token验证集新词被跳过序列参差不齐后全被 pad 成同一个短序列模型输出自然模板化二是只保存了 model.h5丢了 tokenizer 的 word_index加载后只能用旧索引序列去预测新文本。解决Tokenizer 和模型一起保存pickle 存成 tokenizer.pklEmbedding 的 input_dim 始终等于 len(tokenizer.word_index) 1。用了预训练词向量还要额外验证 vocab 里每个词都能在向量表里查到缺失的用随机小值兜底。从那以后我每次跑完一个模型都会先确认 tokenizer 文件存在再确认 input_dim 和词表长度一致最后才敢删训练时的临时变量。6. 验证与进阶混淆矩阵读法、Gemma 微调与可复用的评估模板6.1 classification_report 和 ConfusionMatrixDisplay 怎么读模型跑完真正告诉你模型能不能用的不是 loss 曲线而是分类报告。我每次只看两件事每个类别的 F1 有没有低于 0.5 的以及混淆集中在哪两个相邻类别上。如果 Neutral 和 Positive 大量互混说明清洗把情感强度词压得太狠如果 Negative 被分到 Neutral 多说明否定词处理没干彻底。from sklearn.metrics import classification_report, confusion_matrix, ConfusionMatrixDisplay print(classification_report(y_val, y_pred, target_names[Irrelevant, Negative, Neutral, Positive])) cm confusion_matrix(y_val, y_pred) disp ConfusionMatrixDisplay(confusion_matrixcm) disp.plot()代码逻辑target_names 的顺序必须和 LabelEncoder 的 classes_ 对齐否则报告行名全是错的。ConfusionMatrixDisplay 把矩阵可视化对角线越大越好偏离对角线越远说明混淆越严重。判断标准也很简单macro-F1 如果只比多数类占比高一点点说明模型没学到真实信号。6.2 进阶Gemma 7B 的 LoRA 微调参数与数据量要求资源里 15-Finetuning Gemma 7B it for Sentiment Analysis.py 是传统路线到 LLM 微调的跳跃。用 LoRA 微调大模型在情感分类上效果好但门槛主要在显存。7B 模型全量微调至少要 24GB用 LoRA 或 QLoRA 可以把单卡需求压到 8 到 10GB。LoRA 的核心参数是 rank 和 alpharank 控制可训练矩阵的秩8 起步16 是上限alpha 是缩放因子一般设成 rank 的两倍。from peft import LoraConfig lora_config LoraConfig( r8, lora_alpha16, target_modules[q_proj, v_proj], lora_dropout0.1 )代码逻辑target_modules 指定对哪些线性层加低秩矩阵只微调 q_proj 和 v_proj 效果稳定显存占用小。lora_dropout0.1 防止低秩适配过拟合。数据量方面10MB 的训练集微调 7B 模型属于偏少的量我实际跑下来 2 到 3 个 epoch 就够每个 epoch 存一次 checkpoint。这个数据规模下LLM 微调的最大收益是天然处理了否定句和反讽。TF-IDF 加分类器在 I love waiting in line 这种反讽句上基本无解而 7B 模型微调后能捕捉到语气信号。从那以后我每次跑情感分析数据集都强制走一遍这个流程确认标签分布和清洗一致性、设好 oov_token、用 macro-F1 当主指标、tokenizer 和模型一起存最后再决定要不要上 LLM。这个顺序帮我挡掉了大多数虚高分数和返工希望帮到你。本文还有配套的精品资源点击获取
返回列表