提升文本分类准确率:Naive Bayes与SVM仅62%的优化问询
提升大规模文本分类准确率的优化方案(针对内存受限场景)
看起来你在处理汽车故障描述的分类任务,手里有100万行数据集却受限于内存只能分批处理10万行,当前Naive Bayes和SVM的准确率卡在62%左右,已经做了词干提取、停用词过滤,试过单/二元ngram效果差异不大。除了数据清洗,我给你整理了几个针对性的优化方向,结合你的代码场景具体说明:
1. 优化TF-IDF配置 + 特征降噪
你在Naive Bayes的Pipeline里设置了use_idf=False,这其实浪费了TF-IDF的核心能力——故障描述里不同故障类型的关键词权重差异很大,启用IDF能帮模型聚焦更有区分度的词汇。另外,高维特征里的噪音会拖累模型,必须做特征选择:
- 用
SelectKBest或SelectPercentile过滤低信息量特征,既能降内存又能提准确率 - 给你修改后的Pipeline示例:
from sklearn.feature_selection import SelectKBest, chi2 # Naive Bayes 优化版 text_mnb_stemmed = Pipeline([ ('vect', stemmed_count_vect), ('tfidf', TfidfTransformer(use_idf=True)), # 启用IDF,强化关键词权重 ('select', SelectKBest(chi2, k=5000)), # 保留Top5000高区分度特征,可根据内存调整k值 ('mnb', MultinomialNB(fit_prior=False, alpha=0.01)) ]) # SVM 优化版(同理加入特征选择) text_svm_stemmed = Pipeline([ ('vect', stemmed_count_vect), ('tfidf', TfidfTransformer(use_idf=True)), ('select', SelectPercentile(chi2, percentile=20)), # 保留前20%特征 ('clf-svm', SGDClassifier(loss='hinge', penalty='l2', alpha=0.001, n_iter_no_change=5, random_state=60)) ])
注意:新版sklearn里n_iter参数已被n_iter_no_change替代,建议更新参数避免警告
2. 用增量学习利用全部100万行数据
你现在每次只处理10万行,但其实可以用增量学习把全部数据都用上,不用一次性加载:
- SGDClassifier天然支持
partial_fit方法,能分批喂数据训练 - 给你一个增量训练的框架示例:
from sklearn.utils import shuffle # 先获取所有类别标签(避免分批训练时漏类别) all_classes = pd.read_csv('SIMPLE_CMPL.txt', usecols=['COMPID'], encoding="ISO-8859-1")['COMPID'].unique() # 初始化各个组件 stemmed_count_vect = StemmedCountVectorizer(stop_words=my_stop_words, ngram_range=(1,1)) tfidf_transformer = TfidfTransformer(use_idf=True) selector = SelectPercentile(chi2, percentile=20) clf_svm = SGDClassifier(loss='hinge', penalty='l2', alpha=0.001, random_state=60) # 分批加载训练数据 chunk_size = 100000 for idx, chunk in enumerate(pd.read_csv('SIMPLE_CMPL.txt', chunksize=chunk_size, delimiter=',', quoting=3, header=0, encoding="ISO-8859-1", skip_blank_lines=True)): chunk = shuffle(chunk) # 打乱批次数据避免顺序偏差 # 第一次批次用fit_transform,后续用transform X_chunk = stemmed_count_vect.fit_transform(chunk['CDESCR']) if idx == 0 else stemmed_count_vect.transform(chunk['CDESCR']) X_chunk = tfidf_transformer.fit_transform(X_chunk) if idx == 0 else tfidf_transformer.transform(X_chunk) X_chunk = selector.fit_transform(X_chunk, chunk['COMPID']) if idx ==0 else selector.transform(X_chunk) # 增量训练 clf_svm.partial_fit(X_chunk, chunk['COMPID'], classes=all_classes)
用这种方式能用上全部100万数据,训练效果会比只用10万行好很多。
3. 超参数调优(别再手动试参数了)
你当前的alpha、ngram等参数都是手动设置的,建议用随机搜索/网格搜索找到最优组合,同时兼顾内存限制:
- 对SVM重点调
alpha(正则强度)、penalty(l1/l2)、loss(hinge/log) - 对Naive Bayes重点调
alpha(平滑参数) - 随机搜索更适合内存紧张的场景,示例代码:
from sklearn.model_selection import RandomizedSearchCV # 定义参数搜索范围 param_dist = { 'vect__ngram_range': [(1,1), (1,2)], 'tfidf__use_idf': [True, False], 'clf-svm__alpha': [1e-4, 1e-3, 1e-2], 'clf-svm__penalty': ['l1', 'l2'] } # 随机搜索,n_iter控制搜索次数,cv控制交叉验证折数 random_search = RandomizedSearchCV(text_svm_stemmed, param_distributions=param_dist, n_iter=10, cv=3, n_jobs=-1, random_state=60) random_search.fit(train_data['CDESCR'], train_data['COMPID']) print("最优参数组合:", random_search.best_params_) print("最优交叉验证准确率:", random_search.best_score_)
4. 换用更高效的文本表示方法
CountVectorizer+词干提取的方式比较基础,试试这些适合大规模文本的方法:
- HashingVectorizer:不用保存词汇表,直接哈希映射,内存占用极低,完美适配超大规模数据,替换CountVectorizer即可:
from sklearn.feature_extraction.text import HashingVectorizer class StemmedHashingVectorizer(HashingVectorizer): def build_analyzer(self): analyzer = super(StemmedHashingVectorizer, self).build_analyzer() return lambda doc: ([stemmer.stem(w) for w in analyzer(doc)]) # 替换原来的StemmedCountVectorizer,n_features可根据内存调整 stemmed_hash_vect = StemmedHashingVectorizer(stop_words=my_stop_words, ngram_range=(1,2), n_features=2**18)
- 预训练词嵌入:比如用GloVe的预训练向量,把文本转换成固定维度的语义向量,能捕捉词汇间的语义关联,对于故障描述这类有特定领域词汇的文本效果可能更好。可以用
gensim加载预训练模型,再结合分类器训练。
5. 处理类别不平衡问题
检查你的COMPID类别分布,如果某些故障类型的样本数量极少,会拉低整体准确率:
- SGDClassifier支持
class_weight='balanced'参数,自动调整类别权重 - 或者对少数类进行过采样、多数类进行欠采样,但注意不要破坏数据的真实分布
你的原始代码小修正点
- 停用词里重复添加了
'0',可以去掉重复项 - 读取数据时
quoting=True应该用数值(比如quoting=3表示QUOTE_NONE),避免解析错误 - SGDClassifier的
n_iter参数已废弃,改用n_iter_no_change和max_iter
内容的提问来源于stack exchange,提问作者juliano.net




