ARTICLE DETAIL

资讯详情

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

微博情感分析实战:基于机器学习的短文本情感分类指南

微博情感分析实战:基于机器学习的短文本情感分类指南 简介一套基于机器学习的微博情感分析项目源码面向计算机相关专业正在做课程设计、期末大作业或毕设的学生也适合想了解情感分析落地流程的技术学习者。项目围绕微博文本情感倾向判别展开涵盖数据清洗、特征提取、模型训练与评估等环节并集成中文分词与词表资源整体结构清晰便于对照学习与二次开发。压缩包共104个文件以java源码、txt说明、pdat与map数据文件为主另含wordlist词表、dll动态库和jar依赖等包体约20.34MBtxt适合阅读项目结构pdat/map承担数据与映射java与jar构成核心算法及运行环境。当前已有195人学习下载资源经过严格调试下载即可运行适合具备一定编程基础的读者用于理解机器学习完整流程也可作为微博情感分析功能扩展的起点。1. 微博情感分析项目先搞懂这个任务的“脾气”再动手如果你刚解压了一个名为“基于机器学习的微博情感分析微博源码项目说明.zip”的压缩包里面大概率是一套标准套路CSV语料、清洗脚本、jieba分词、TF-IDF加逻辑回归、还有一份项目说明。这个项目要解决的问题很直接——给一条微博判定正面、负面或中性常见于课程设计、毕业设计或者舆情系统的前置模块。但真正动手复现后你会同意我的结论难点不在“机器学习”四个字而在微博文本本身。它比新闻短、比评论口语化夹杂大量表情、URL、用户和网络新词同一个“给力”在不同博主嘴里可能是夸奖也可能是反讽。这篇笔记会按一个机器学习项目应有的流程把数据清洗、分词、特征工程、模型训练、预测Demo以及最容易被源码忽略的踩坑点捋一遍。新手可以照着操作步骤复现熟手可以重点看后面避坑章节里的参数边界和样本泄露问题。2. 微博文本清洗、分词与标签构造把口语噪音压到最低2.1 微博语料和新闻语料的三点差别短、乱、情绪化新闻语料做情感分析时句子通常有完整主谓宾上下文信息多分词和特征提取都比较“听话”。微博则完全相反单条文本往往只有几十个字有效信息密度极低很多句子省略主语情绪完全靠语气词和表情撑着。这种短文本特性会让TF-IDF的文档频率估计不稳定一个词在训练集里出现三五次就被当作强特征换一批微博就失效。第二点是乱。微博文本里有短链接、话题标签、用户、图片描述前缀还有各种程序员最讨厌的零宽字符。如果不过滤这些噪音会进入词表让特征矩阵稀疏得没法看。但“过滤”不是无脑删话题内容如“#吐槽大会#”“#疫情#”往往自带情绪直接删掉等于把最重要的信号丢掉了。第三点是情绪表达方式口语化。微博用户习惯把情绪藏在“不是我说”“绝了”“yyds”“栓Q”等网络语里传统词典和分词器面对这些词会切得稀碎。情感分析任务里最怕的不是缺模型而是分词后“给力”变成“给/力”“真香”变成“真/香”模型学到的词完全跑偏。所以做这个项目第一步不是选模型而是把数据清洗和分词当成和模型同等重要的事来对待。2.2 清洗脚本去掉URL、用户、话题标签和零宽字符常见做法是把清洗逻辑封装成一个函数训练和预测都要用同一套规则否则线上预测时脏文本直接进模型效果会明显缩水。我一般会在项目里单独建一个preprocess.py里面放下面这段清洗代码import re def clean_weibo_text(raw: str) - str: if not isinstance(raw, str): raw str(raw) # 去掉URL短链接里的utm参数对情感判断毫无意义 text re.sub(rhttps?://\S, , raw) # 话题标签保留“#内容#”里的内容话题词本身可能携带情绪 text re.sub(r#([^#])#, r\1, text) # 用户将用户ID统一替换成USER避免模型学到具体人名 text re.sub(r([^\s]), USER, text) # 零宽字符肉眼看不见却会让分词结果出现空token text re.sub(r[\u200b\u200c\u200d\ufeff], , text) # 连续空白压缩成一个空格 text re.sub(r\s, , text).strip() return text这段代码的要点在于区分“去什么”和“留什么”。URL和HTML实体对情感无语义贡献直接删掉。话题标签里的词是用户主动强调的主题和情绪强相关所以只去掉外层井号内容保留。用户后面的人名对很多任务有用但对情感分析来说模型不该学会“提到XX就一定是负面”因此替换成统一的USER标记比直接删除更稳妥既保留了“这条微博提到了某人”这一结构信息又不会让模型记忆具体ID。零宽字符在爬下来的数据里很常见尤其当数据源做过转码或复制粘贴时。它们不会显示但会让jieba分词结果中出现孤立的空白字符进而在向量化时产生无意义特征。清洗后最好随机打印20条结果看一眼确认没有把“哈哈哈”洗成“哈 哈 哈”这种过度切割。2.3 jieba分词与自定义词典先看分词结果再谈优化模型微博文本分词不能直接依赖jieba默认词典。默认词典以书面语为主对“给力”“绝绝子”“蚌埠住了”这类网络新词识别很差。我一般会在项目里维护一个user_dict.txt每行一个词可以附带词频和词性也可以只写词。示例给力 100 a 绝绝子 50 a 蚌埠住了 50 a 栓Q 30 v对应的分词代码import jieba # 必须在调用tokenize之前加载自定义词典 jieba.load_userdict(user_dict.txt) stopwords set() with open(stopwords.txt, encodingutf-8) as f: for line in f: word line.strip() # 注意否定词和程度副词不能进停用词表 if word and word not in {不, 没, 别, 太, 很, 真, 也, 又}: stopwords.add(word) def tokenize(text: str) - list: words jieba.lcut(text) return [w for w in words if w.strip() and w not in stopwords]这里的参数有个常见误区很多人从网上抄停用词表把“不”“太”“很”也停掉结果“不太好”变成“好”情感完全反转。停用词表只需要去掉“的”“了”“啊”这类纯语法词以及“我”“你”“他”这类人称代词否定词、程度副词都是情感分析里最重要的特征必须保留。另一个坑是jieba.lcut默认开启HMM新词发现它对“给力”这种词有可能继续切错。如果你自定义词典已经覆盖了项目里的主要网络词可以把参数改成jieba.lcut(text, HMMFalse)降低随机性保证复现结果一致。要记住自定义词典的加载时机必须在分词函数被调用之前否则词典对当前进程不生效很多人把load_userdict放在脚本末尾排错半天才发现顺序反了。2.4 标签构造二分类还是三分类怎么避免标注主观性情感分析任务的标签通常有两个来源公开数据集和自建标注。最常见的开源微博情感数据是CSV格式第一列为标签第二列为文本。加载时我会先做一次标签分布检查import pandas as pd df pd.read_csv(data/weibo_senti_100k.csv, names[label, text]) # 常见标签0负向1正向部分版本会多一个2中性 print(df[label].value_counts()) if df[label].max() 2: # 如果项目只想做二分类删掉中性样本 df df[df[label] ! 2].copy() df[label] df[label].astype(int)二分类和三分类的选择直接影响模型阈值和后处理。二分类任务中正负样本通常比较均衡逻辑回归默认阈值0.5就够用三分类任务里中性样本边界非常模糊“今天天气不错但心情一般”到底是正还是负不同人标注会吵架导致噪音很大。如果数据源已经有了三分类标签建议先跑一个baseline看F1再决定是否合并成二分类。如果条件允许自己标注时至少要抽100条做一致性抽检让两个人分别打标计算Cohens Kappa。Kappa大于0.7才说明标注口径稳定否则模型学到的只是标注者的个人偏好。这个步骤在很多项目说明里被省略但嘲讽类微博、反讽类微博如果不靠标注说明单独处理模型在测试集上的分数会虚高实际部署时惨不忍睹。3. 特征表示与基线模型TF-IDF 逻辑回归是性价比最高的起跑线3.1 为什么BOW容易翻车否定词和程度词被拆散直接做词袋模型(Bag of Words)的情感分析通常会遇到一个典型翻车场景句子“我不高兴”和“我高兴”的词频几乎一样但情感完全相反。朴素BOW完全不考虑词序把“不”当成一个独立词模型就不知道“不”修饰的是“高兴”。解决这个问题的直接办法是引入n-gram让“不 高兴”作为一个连续特征出现。在微博场景里n-gram尤其有效因为用户习惯写“不太高兴”“不是很满意”“一点都不好吃”这样的程度否定结构。只用unigram时模型会把“太”和“不”当作两个独立特征无法学到“不 太 高兴”这个组合用ngram_range(1,2)可以覆盖“不 太”“太 高兴”如果数据量够大(1,3)效果还会更好但特征维度会指数膨胀。另一个容易被忽略的问题是TF-IDF对短文本的偏差。TF-IDF的IDF部分依赖文档频率微博每条只有几十字很多网友造的梗只在少数样本里出现IDF会被严重高估导致模型把几个生僻词当强信号。所以参数min_df和max_df必须手动调不能让默认值直接跑。3.2 TF-IDF向量化器的参数设置拿到清洗和分词后的文本下一步就是转成向量。直接用CountVectorizer再拼TF-IDF变换也行但TfidfVectorizer已经把这些封装好了注意它的几个关键参数from sklearn.feature_extraction.text import TfidfVectorizer tfidf TfidfVectorizer( tokenizertokenize, # 使用我们自己的分词函数 ngram_range(1, 2), # unigram bigram max_features50000, # 限制词表大小防止特征爆炸 min_df2, # 至少在2条微博中出现过 max_df0.8, # 出现在80%以上微博的词直接丢弃 sublinear_tfTrue # 用 1log(tf) 代替原始词频 ) X tfidf.fit_transform(df[text_clean])max_features50000很重要微博语料经过n-gram后特征数轻松超过百万不限制的话逻辑回归训练慢且过拟合。min_df2是为了去掉只出现一次的“一次性词汇”这些词往往错字或博主私设梗对泛化无益反而有害。max_df0.8处理的是“哈哈哈”“无语”这类全语料高频词几乎每个样本都有区分度很低IDF还会把它们压到极低权重。sublinear_tfTrue是情感分析里常用的技巧因为微博文本长短不一一个反复出现的表情符号会让原始词频暴涨取对数可以缓解这种偏差。需要注意的是在单独做特征可视化时可以像上面这样fit_transform但最终训练模型时必须把向量化器放进Pipeline否则交叉验证时会造成信息泄漏向量化器在整个数据集上先fit了一遍再切训练验证集验证集的一部分信息已经进到词表里了。3.3 逻辑回归和朴素贝叶斯怎么选先跑哪个情感分析领域最常见的两个基线模型是朴素贝叶斯和逻辑回归。朴素贝叶斯对特征独立性假设很强但在短文本情感任务里它往往表现得不错尤其是语料只有几千条时因为它收敛快、方差小。逻辑回归则需要更多数据来拟合高维稀疏特征但它能做特征加权可以用系数解释模型还能输出概率用于阈值调整。模型优势适用场景朴素贝叶斯参数少、训练快、小样本稳定数据量5000快速验证逻辑回归特征可解释、概率输出、正则可控数据量5000生产基线代码上两个模型都只需几行但参数差异值得注意from sklearn.naive_bayes import MultinomialNB from sklearn.linear_model import LogisticRegression # 朴素贝叶斯alpha是拉普拉斯平滑系数太小会过拟合 nb MultinomialNB(alpha0.1) # 逻辑回归C是正则强度的倒数C越小正则越强 lr LogisticRegression(C1.0, max_iter1000, class_weightbalanced)我一般会先用朴素贝叶斯跑通全流程再换逻辑回归并调C。C的常见搜索范围是[0.1, 1, 10]C1.0是sklearn默认值但微博情感语料里特征很强很多情况下C0.1或C0.5效果更好正则压制掉那些依赖词序生僻特征的过拟合信号。class_weightbalanced是处理正负样本不平衡最省事的手段它会自动给少数类别更高的惩罚权重。逻辑回归还有个好处训练后把权重最大的正特征和负特征打印出来能直观看出模型有没有学到“不行”“垃圾”这类词还是学到了一些没意义的词。这个检查在后续避坑章节里非常有用。3.4 评估视角准确率是情感分析里最骗人的指标很多课程设计要求里只写“准确率达到85%”但准确率在情感分析任务里几乎是个摆设。如果负样本占比90%模型全预测负类也能拿到90%准确率但舆情系统要抓住的是那10%的负面全预测负等于什么都没做。正确评估应该看F1与混淆矩阵。先定好二分类正类是哪一极性。舆情场景通常把“负面”作为正类因为漏报负面比误报正面代价更高。评估代码from sklearn.metrics import classification_report, confusion_matrix y_pred pipe.predict(X_test) print(classification_report(y_test, y_pred, target_names[正面, 负面])) print(confusion_matrix(y_test, y_pred))重点关注负类的召回率真实负样本中有多少被模型识别出来了。如果召回率低于0.8后面就要考虑调整阈值而不是盲目换更贵的模型。F1建议用macro方式它对正负类一视同仁不会因为多数类掩盖少数类的差表现。很多源码的项目说明会写“准确率95%”但你在README里应该补充F1和混淆矩阵这才是说服别人相信模型真的可用的关键。4. 用机器学习跑通微博情感分析从训练到预测的完整代码4.1 项目目录设计源码和项目说明的分工一个能交付的微博情感分析项目不应该只有.py文件和压缩包里的README。通常我会把目录整理成下面这样源码与数据分离模型输出单独存放文件/目录作用data/存放CSV语料第一列label第二列textpreprocess.py清洗与分词函数训练和预测共用user_dict.txt自定义词典stopwords.txt停用词表train.py训练主脚本输出模型文件predict.py加载模型预测单条微博model/保存训练好的pipeline模型README.md项目说明环境、数据、复现、评估很多源码包把清洗函数放在train.py内部predict.py再复制一遍。一旦清洗规则更新只改了一处预测脚本还在用旧规则结果必然翻车。所以无论如何都要把预处理抽成公共模块这是这个项目里最值得投入的工程习惯。4.2 训练脚本从CSV到Pipeline一次成型训练脚本的核心是把清洗、分词、向量化、模型封装成一个Pipeline这样交叉验证、网格搜索、保存模型都只针对这一个对象不存在“训练时洗了数据预测时忘洗”的问题。import re import joblib import pandas as pd import jieba from sklearn.pipeline import Pipeline from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report # ---------- 预处理 ---------- def clean_weibo_text(raw: str) - str: if not isinstance(raw, str): raw str(raw) text re.sub(rhttps?://\S, , raw) text re.sub(r#([^#])#, r\1, text) text re.sub(r([^\s]), USER, text) text re.sub(r[\u200b\u200c\u200d\ufeff], , text) return re.sub(r\s, , text).strip() stopwords set() with open(stopwords.txt, encodingutf-8) as f: for line in f: word line.strip() if word and word not in {不, 没, 别, 太, 很, 真, 也, 又}: stopwords.add(word) jieba.load_userdict(user_dict.txt) def tokenize(text: str) - list: return [w for w in jieba.lcut(text) if w.strip() and w not in stopwords] # ---------- 数据 ---------- df pd.read_csv(data/weibo_senti_100k.csv, names[label, text]) df[text] df[text].fillna().astype(str) # 兼容三分类标签0负 1正 2中二分类任务里删掉中性 if df[label].max() 2: df df[df[label] ! 2].copy() df[text] df[text].apply(clean_weibo_text) # ---------- 模型流水线 ---------- pipe Pipeline([ (tfidf, TfidfVectorizer( tokenizertokenize, ngram_range(1, 2), max_features50000, min_df2, max_df0.8, sublinear_tfTrue )), (clf, LogisticRegression(C1.0, max_iter1000, class_weightbalanced)) ]) # stratify保证切分后正负比例与原始数据一致 X_train, X_test, y_train, y_test train_test_split( df[text], df[label], test_size0.2, random_state42, stratifydf[label] ) pipe.fit(X_train, y_train) y_pred pipe.predict(X_test) print(classification_report(y_test, y_pred)) # 保存整个pipeline预测时不需要再单独加载词表 joblib.dump(pipe, model/weibo_sentiment_pipe.pkl)这段代码里有几个参数需要解释。random_state42固定切分顺序保证复现时结果一致否则你调了一晚上参数第二天重跑结果全变很难判断是代码改了还是随机性造成的。max_iter1000是为了避免默认的100次迭代不收敛产生警告。joblib.dump把向量化器和分类器绑定保存预测脚本只需加载这一个文件。class_weightbalanced并不总是最优。如果你的数据本身正负样本比例接近1:1加这个参数反而会让模型偏向少数类产生更多误报。先用原始比例跑一次看分类报告如果少数类召回率太低再把该参数打开不要无脑加。4.3 预测脚本单条微博如何走完整个Pipeline预测脚本必须复用preprocess.py里的清洗和分词函数不能自己再写一份正则。下面是一个简单的命令行预测脚本import joblib from preprocess import clean_weibo_text, tokenize # 训练脚本中抽出的公共函数 pipe joblib.load(model/weibo_sentiment_pipe.pkl) print(模型类别顺序:, pipe.classes_) while True: text input(请输入微博文本quit退出) if text.strip().lower() quit: break cleaned clean_weibo_text(text) proba pipe.predict_proba([cleaned])[0] label pipe.predict([cleaned])[0] print(清洗后文本:, cleaned) print(预测极性:, 正面 if label 1 else 负面, 概率:, proba)predict_proba输出的两个概率对应pipe.classes_里的顺序。打印classes_是一个好习惯因为如果之前训练时标签是[0,1]你现在判断label1才代表正面但有些数据集的标签定义恰好相反脚本里硬编码label1会导致坑。最稳妥的写法是label_name pipe.classes_[label]而不是用0/1做判断。项目说明里也应该标注清楚标签口径。4.4 项目说明文件里必须写清的四件事压缩包里的“项目说明”质量天差地别。一份能让别人照着跑的README至少要包含四件事。第一环境依赖Python版本、jieba版本、scikit-learn版本、pandas版本直接给一个requirements.txt不要只写“安装依赖”。第二数据格式CSV是逗号分隔还是Tab分隔有没有表头标签是0/1还是0/1/2空值怎么处理。第三复现命令python preprocess.py、python train.py、python predict.py的顺序和前置条件最好直接贴命令块。第四评估结果F1、精确率、召回率、混淆矩阵让读者先知道这个模型真实水平再决定是否使用。项目说明里最容易漏的是“模型输出目录必须存在”。很多人下载源码后跑joblib.dump(pipe, model/weibo_sentiment_pipe.pkl)报错FileNotFoundError才意识到没有model目录。训练脚本里我会先加一行import os os.makedirs(model, exist_okTrue)这一行代码能省下大量初学者的答疑时间。5. 微博情感分析避坑指南现象、原因和解决办法5.1 坑一分词把“给力”切成“给/力”正面情感直接变负面现象输入“这个手机续航真是太给力了”模型预测为负面查看分词结果发现是“真/是/太/给/力/了”模型把“给”当成了动词“力”当成了普通名词没有识别出“给力”这个整体褒义词。原因jieba默认词典没有收录网络新词而训练语料里“给力”出现的次数不够多HMM新词发现又把“给力”拆开了。情感分析里一个词被拆错就可能导致整句极性反转尤其是“给力”“真香”“绝绝子”这类网络流行词。解决维护user_dict.txt把项目涉及的高频网络词都加进去并写清词频和词性。加载后立刻用jieba.lcut验证print(jieba.lcut(这个手机续航真是太给力了)) # 期望输出[这个, 手机, 续航, 真是, 太, 给力, 了]如果还不行就把jieba.lcut(text)改成jieba.lcut(text, HMMFalse)让分词结果更保守依赖自定义词典而不是随机性较强的HMM。这个坑在微博语料里几乎必踩所以第二个样本就值得检查。5.2 坑二表情符号被过度清洗负向情感漏掉一大半现象“你说的都对[微笑]”“我真是服了[泪流满面]”这两条情感强烈的微博模型都预测为正面或中性。看清洗结果才发现[微笑]和[泪流满面]被正则当作普通方括号内容删掉了模型根本没看到情感信号。原因微博的表情符号是文本形式方括号里的文字本身就是情绪表达。很多清洗脚本为了去掉方括号内容会把它们一并删掉导致负面表达里最重要的线索被抹去。解决不要删方括号内容而是把它们映射成统一标记。比如[微笑]映射为POS_EMOJI[泪流满面]映射为NEG_EMOJI再交给分词器。这样模型能学到“带NEG_EMOJI的微博大概率是负面”。实现时可以在清洗函数里加一个映射表EMOJI_MAP { 微笑: POS_EMOJI, 嘻嘻: POS_EMOJI, 泪流满面: NEG_EMOJI, 怒: NEG_EMOJI, 失望: NEG_EMOJI, } def replace_emoji(text: str) - str: for k, v in EMOJI_MAP.items(): text text.replace(f[{k}], f {v} ) return text清洗顺序要先替换表情再去掉仍然存在的方括号内容避免把映射后的标记又误删。5.3 坑三训练集和测试集来自同一条转发链评估分数虚高现象随机切分训练测试集后F1有0.92部署到线上只有0.75。检查发现数据里同一用户发的原创微博和大量转发微博内容高度相似随机切分把这些相似文本分别放进了训练集和测试集模型等于“背答案”。原因微博数据天然具有转发链聚合性——同一个热点事件下几千条微博只是改了几个字。如果按行随机切分这些相似样本会同时出现在两边测试集不能代表真实的新样本分布。解决切分前先按文本内容去重或者按微博ID/用户ID分组。最直接的办法是用simhash或MinHash计算文本相似度相似度高于0.8的只保留一条更简单一点按发布时间排序用前80%时间的数据训练后20%时间的数据测试。这个操作在项目说明里要明确写出来否则别人复现时很可能跳过。5.4 坑四类别不平衡和默认阈值让负面召回率很低现象分类报告显示准确率很高但负类的召回率只有0.4很多负面微博被模型判成正面。把预测概率打印出来发现模型对这些样本的输出概率集中在0.4~0.60.5阈值滑过去就判反了。原因逻辑回归默认阈值是0.5这个阈值只有在正负样本均匀且错分代价相等时才合理。舆情场景要优先抓负面默认阈值显然不适合。解决在验证集上遍历阈值选负类F1最高的点。代码如下import numpy as np from sklearn.metrics import f1_score proba_neg pipe.predict_proba(X_test)[:, 1] # 假设负类在第二列 best_thr 0.5 best_f1 0.0 for thr in np.arange(0.3, 0.7, 0.05): pred_t (proba_neg thr).astype(int) score f1_score(y_test, pred_t, pos_label1) print(fthreshold{thr:.2f}, f1{score:.4f}) if score best_f1: best_f1 score best_thr thr print(best threshold:, best_thr)找到阈值后在预测脚本中不要直接pipe.predict而是用pipe.predict_proba加手动阈值判断极性。这个技巧能让负面召回率在几乎不增加成本的情况下提升5~10个百分点。5.5 坑五项目说明缺环境锁定换机器十分钟后跑不起来现象一样的数据和代码在作者机器上F10.86换一台机器跑先是jieba报错再是sklearn的TfidfVectorizer参数不兼容最后连中文编码都变成乱码。原因项目说明只写了“python train.py”没写Python版本、依赖库版本、文件编码。不同版本的jieba分词结果可能不一致scikit-learn 1.0以后对tokenizer的校验更严格旧代码会报警告或报错。解决在项目根目录放一个锁定版本的requirements.txtjieba0.42.1 numpy1.24.3 pandas1.5.3 scikit-learn1.2.2 joblib1.2.0 flask2.3.2README里明确写用conda create -n weibo_sentiment python3.9创建环境然后pip install -r requirements.txt。数据文件统一用utf-8编码读取如果CSV里出现gbk编码报错在pd.read_csv里加encodingutf-8或encodinggbk再试。换机器能三分钟跑通才是一个项目说明真正合格的标准。6. 进阶优化与验证技巧让模型跳出“能跑”的边界6.1 用Pipeline统一封装网格搜索不再跑偏训练脚本里已经用Pipeline把TF-IDF和逻辑回归串在一起。进阶时可以直接在Pipeline上做网格搜索避免对特征矩阵单独调参时造成信息泄漏from sklearn.model_selection import GridSearchCV params { tfidf__ngram_range: [(1, 2), (1, 3)], tfidf__max_features: [30000, 50000], clf__C: [0.1, 1, 10] } grid GridSearchCV(pipe, params, cv5, scoringf1_macro, n_jobs-1) grid.fit(df[text], df[label]) print(grid.best_params_)这里n_jobs-1会使用所有CPU核心但如果你数据集很大、特征超5万建议先把max_features调小再搜索否则内存会瞬间吃满。网格搜索后保存grid.best_estimator_而不是再次训练一个普通Pipeline。6.2 用Flask暴露一个简单的预测接口跑通命令行后把模型包装成一个HTTP接口能在浏览器里直接演示。一个最小可用的Flask服务只有几行from flask import Flask, request, jsonify import joblib from preprocess import clean_weibo_text app Flask(__name__) pipe joblib.load(model/weibo_sentiment_pipe.pkl) app.route(/sentiment, methods[POST]) def sentiment(): text request.json.get(text, ) cleaned clean_weibo_text(text) proba pipe.predict_proba([cleaned])[0].tolist() label pipe.predict([cleaned])[0] return jsonify({ label: int(label), label_name: pipe.classes_[label], probability: proba }) if __name__ __main__: app.run(host0.0.0.0, port5000)接口返回里不要只给0/1要给label_name和完整概率前端可以直接根据概率调整颜色深浅展示会好看得多。如果要部署到公网Flask前面还要加一层鉴权和限流不过课程设计或内部Demo不需要考虑这些。6.3 三种验证方法交叉验证、错例回看、样本外新数据模型训练完不要只盯着测试集数字。我最常用的验证方法是先用5折交叉验证看均值方差分数波动超过1个百分点说明数据切片不稳定然后把测试集误判样本按“真实负但预测正”“真实正但预测负”分别导出人工逐条回看找到共性错因这比再调一轮参数更有价值最后留一批时间靠后的微博人工标注50条做样本外测试模拟真实预测环境。我自己的习惯是每轮训练前先随机打印20条清洗后的文本和分词结果确认预处理没有破坏情绪表达。这个习惯让我少走了很多弯路——毕竟很多“模型效果差”的问题根源在数据不在模型。希望这篇笔记里的踩坑记录能帮你避开同样的坑。本文还有配套的精品资源点击获取
返回列表