You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

提升文本分类准确率:Naive Bayes与SVM仅62%的优化问询

提升大规模文本分类准确率的优化方案(针对内存受限场景)

看起来你在处理汽车故障描述的分类任务,手里有100万行数据集却受限于内存只能分批处理10万行,当前Naive Bayes和SVM的准确率卡在62%左右,已经做了词干提取、停用词过滤,试过单/二元ngram效果差异不大。除了数据清洗,我给你整理了几个针对性的优化方向,结合你的代码场景具体说明:

1. 优化TF-IDF配置 + 特征降噪

你在Naive Bayes的Pipeline里设置了use_idf=False,这其实浪费了TF-IDF的核心能力——故障描述里不同故障类型的关键词权重差异很大,启用IDF能帮模型聚焦更有区分度的词汇。另外,高维特征里的噪音会拖累模型,必须做特征选择:

  • SelectKBestSelectPercentile过滤低信息量特征,既能降内存又能提准确率
  • 给你修改后的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'参数,自动调整类别权重
  • 或者对少数类进行过采样、多数类进行欠采样,但注意不要破坏数据的真实分布

你的原始代码小修正点

  1. 停用词里重复添加了'0',可以去掉重复项
  2. 读取数据时quoting=True应该用数值(比如quoting=3表示QUOTE_NONE),避免解析错误
  3. SGDClassifier的n_iter参数已废弃,改用n_iter_no_changemax_iter

内容的提问来源于stack exchange,提问作者juliano.net

火山引擎 最新活动