python朴素贝叶斯调参_中文新闻分类,逻辑回归大战朴素贝叶斯

1 问题背景

我们有一堆新闻文本数据,包含“新闻类别”、“新闻内容”两列,希望建立一个模型,来预测新闻内容属于哪个类别。

这是一个文本分类问题,朴素贝叶斯和逻辑回归可以说是文本分类最常用的Baseline模型,今天小树带大家来看看,朴素贝叶斯和逻辑回归效果如何以及它们两个究竟谁更厉害。

操作环境:jupyter notebook + python3

预备知识:中文分词、tf-idf、朴素贝叶斯模型、逻辑回归模型

2 加载相关包

import numpy as np

import pandas as pd

import random

from sklearn.svm import SVC

from sklearn import preprocessing, decomposition, model_selection, metrics, pipeline

from sklearn.model_selection import GridSearchCV

from sklearn.feature_extraction.text import TfidfVectorizer

from sklearn.decomposition import TruncatedSVD

from sklearn.linear_model import LogisticRegression

from sklearn.naive_bayes import MultinomialNB

3 加载数据并进行数据预处理

(1)加载数据

# load data

data = [[i.split("\t")[0],i.split("\t")[1]] for i in open(u"data/cnews.train.txt","r", encoding="utf-8")]

# 打乱数据

random.shuffle(data)

data[1]

output

(2)分词,标签转换,求tf-idf

# 对新闻内容进行分词

import jieba

jieba.enable_parallel() #并行分词开启

terms = [" ".join([j for j in jieba.cut(i[1])])

for i in data[:1000]]

#接下来用scikit-learn中的LabelEncoder将文本标签(Text Label)转化为数字(Integer)

lbl_enc = preprocessing.LabelEncoder()

y = lbl_enc.fit_transform([i[0] for i in data[:1000]])

from sklearn.feature_extraction.text import TfidfVectorizer

# 初始化TFidf对象,去停用词,加2元语言模型

tfv = TfidfVectorizer(min_df=3, max_features=None, strip_accents='unicode', analyzer='word',token_pattern=r'\w{1,}', ngram_range=(1, 2), use_idf=1,smooth_idf=1,sublinear_tf=1, stop_words = 'english')

tfv.fit(terms)

X_all = tfv.transform(terms)

(3)观察label分布,划分数据集

len_train = 600

X_train = X_all[:len_train]

y_train = y[:len_train]

X_test = X_all[len_train:]

y_test = y[len_train:]

print('X_train shape: ', X.shape)

print('y_train shape: ', y_train.shape)

print('X_test shape: ', X_test.shape)

print('y_test shape: ', y_test.shape)

# 观察label的分布: 共有10个类别,类别均衡

label_data = pd.DataFrame({'label':y})

print('\nDistribution of all label:\n', label_data.label.value_counts())

output

从上面的数据可以得到两点结论:

我们的特征有29812个,而训练样本只有600个,特征维度远远大于训练样本数量,因此特征空间是非常稀疏的。

从label的分布来看,各个类别是平衡的。

4 定义评估标准

这是一个典型的多分类问题,采用常用的multi-class log loss进行评估。

def multiclass_logloss(actual, predicted, eps=1e-15):

"""

对数损失度量(Logarithmic Loss Metric)的多分类版本。

:param actual: 包含actual target classes的数组

:param predicted: 分类预测结果矩阵, 每个类别都有一个概率

"""

# Convert 'actual' to a binary array if it's not already:

if len(actual.shape) == 1:

actual2 = np.zeros((actual.shape[0], predicted.shape[1]))

for i, val in enumerate(actual):

actual2[i, val] = 1

actual = actual2

clip = np.clip(predicted, eps, 1 - eps)

rows = actual.shape[0]

vsota = np.sum(actual * np.log(clip))

return -1.0 / rows * vsota

好了,万事俱备,开始激动人心的建模时刻!

5 朴素贝叶斯 vs 逻辑回归

step 1 建立一个朴素贝叶斯的baseline模型

clf_nb = MultinomialNB() # 多项式朴素贝叶斯

clf_nb.fit(X_train, y_train)

predictions = clf_nb.predict_proba(X_test)

print ("logloss: %0.3f " % multiclass_logloss(y_test, predictions))

logloss: 1.111

朴素贝叶斯在测试集上的logloss为1.111, 效果不太好,没关系,我们把这个作为baseline。

step 2 建立一个逻辑回归模型来对比看看

clf_lr = LogisticRegression(C=1.0,solver='lbfgs',multi_class='multinomial')

clf_lr.fit(X_train, y_train)

predictions = clf_lr.predict_proba(X_test)

print ("logloss: %0.3f " % multiclass_logloss(y_test, predictions))

logloss: 1.223

看起来似乎逻辑回归比朴素贝叶斯效果还差喔。

step 3 我们来调参吧

# 创建评分函数

mll_scorer = metrics.make_scorer(multiclass_logloss, greater_is_better=False, needs_proba=True)

(1)朴素贝叶斯调参

调参指南:朴素贝叶斯调参一般调alpha,平滑参数,其值越小,越容易过拟合,值越大,容易造成欠拟合。

下面建立一个pipeline来调参。

nb_model = MultinomialNB()

# 创建pipeline

clf = pipeline.Pipeline([('nb', nb_model)])

# 搜索参数设置

param_grid = {'nb__alpha': [0.001, 0.01, 0.1, 1, 10, 100]}

# 网格搜索模型(Grid Search Model)初始化

model = GridSearchCV(estimator=clf, param_grid=param_grid, scoring=mll_scorer,

verbose=10, n_jobs=-1, iid=True, refit=True, cv=6)

# fit网格搜索模型

model.fit(X_train, y_train)

print("Best score: %0.3f" % model.best_score_)

print("Best parameters set:")

best_parameters = model.best_estimator_.get_params()

for param_name in sorted(param_grid.keys()):

print("\t%s: %r" % (param_name, best_parameters[param_name]))

Best parameters set: nb__alpha: 0.1

我们发现alpha = 0.1时,模型效果最好,用这个参数重新跑跑看

# 使用最优参数建模

clf_nb = MultinomialNB(alpha=0.1)

clf_nb.fit(X_train, y_train)

predictions = clf_nb.predict_proba(X_test)

print ("logloss: %0.3f " % multiclass_logloss(y_test, predictions))

logloss: 0.243

朴素贝叶斯调参后logloss 从1.111 直接降到了0.243,惊不惊喜,意不意外!只是简单的调参就收获了如此大的性能提升,而且0.243这个表现说明朴素贝叶斯的效果还是杠杠的哟。

下面来看看逻辑回归调参之后能否反超朴素贝叶斯!

(2)逻辑回归调参

调参指南:逻辑回归调参常调正则项C以及penalty,由于这里我们使用了lgfgs solver,它只支持L2 penalty,因此我们只调C。注意,C是正则化系数的倒数,其值越大,正则项的权重越小,模型越容易过拟合。

lr_model = LogisticRegression(solver='lbfgs',multi_class='multinomial')

# 创建pipeline

clf = pipeline.Pipeline([('lr', lr_model)])

# 搜索参数设置

param_grid = {'lr__C': [0.1, 1.0, 10]}

# 网格搜索模型(Grid Search Model)初始化

model = GridSearchCV(estimator=clf, param_grid=param_grid, scoring=mll_scorer,

verbose=10, n_jobs=-1, iid=True, refit=True, cv=6)

# fit网格搜索模型

model.fit(X_train, y_train)

print("Best score: %0.3f" % model.best_score_)

print("Best parameters set:")

best_parameters = model.best_estimator_.get_params()

for param_name in sorted(param_grid.keys()):

print("\t%s: %r" % (param_name, best_parameters[param_name]))

Best parameters set: lr__C: 10

用这个最优参数重新训练模型:

clf_lr = LogisticRegression(C=10, solver='lbfgs',multi_class='multinomial')

clf_lr.fit(X_train, y_train)

predictions = clf_lr.predict_proba(X_test)

print ("logloss: %0.3f " % multiclass_logloss(y_test, predictions))

logloss: 0.546

调参后逻辑回归的表现也变好了很多,但是跟朴素贝叶斯比,还是相去甚远,看起来好像还是朴素贝叶斯更厉害嘛!逻辑回归还有翻盘的机会吗?

我们来观察一下模型的结果:

print('train accuracy: ',clf_lr.score(X_train, y_train))

print('test accuracy: ',clf_lr.score(X_test, y_test))

train accuracy: 1.0

test accuracy: 0.9675

在测试集上准确率很高啊!奇怪了,我们打印一个预测的结果来看看:

predictions[0]

array([0.02830099, 0.03794662, 0.03657158, 0.02690717, 0.02604819,

0.04728005, 0.02332729, 0.73275343, 0.02299653, 0.01786817])

测试集的第一个数据真实label是7,而模型预测为7的概率为0.73,由于我们使用交叉熵损失作为评估,所以看起来模型的表现不太好。尽管特征很多,样本量很小,我们还想让模型拟合得更好!

那怎么办?我有一个大胆的想法...

我们重新调逻辑回归的正则项参数C,加大加大,人有多大胆,地有多大产!!!说白了,我就是不要正则化了!

lr_model = LogisticRegression(solver='lbfgs',multi_class='multinomial')

# 创建pipeline

clf = pipeline.Pipeline([('lr', lr_model)])

# 搜索参数设置

param_grid = {'lr__C': [1000,10000,100000,1000000, 10000000, 100000000]}

# 网格搜索模型(Grid Search Model)初始化

model = GridSearchCV(estimator=clf, param_grid=param_grid, scoring=mll_scorer,

verbose=10, n_jobs=-1, iid=True, refit=True, cv=6)

# fit网格搜索模型

model.fit(X_train, y_train)

print("Best score: %0.3f" % model.best_score_)

print("Best parameters set:")

best_parameters = model.best_estimator_.get_params()

for param_name in sorted(param_grid.keys()):

print("\t%s: %r" % (param_name, best_parameters[param_name]))

Best parameters set: lr__C: 1000000

这路子有点野...看看它行不行先!

clf_lr = LogisticRegression(C=1000000, solver='lbfgs',multi_class='multinomial')

clf_lr.fit(X_train, y_train)

predictions = clf_lr.predict_proba(X_test)

print ("logloss: %0.3f " % multiclass_logloss(y_test, predictions))

logloss: 0.125

内心诚惶诚恐,但数据显示很牛逼!好了,贝叶斯你跪下叫爸爸吧。。。

。。。。。。

贝叶斯表示这路子太野我不服,能不能来点正常的套路?!

那降维打击了解一下?

#使用SVD(奇异值分解)进行降维,components设为180,对于SVM来说,SVD的components的合适调整区间一般为120~200

svd = decomposition.TruncatedSVD(n_components=180)

svd.fit(X_train)

xtrain_svd = svd.transform(X_train)

xtest_svd = svd.transform(X_test)

#对从SVD获得的数据进行缩放

scl = preprocessing.StandardScaler()

scl.fit(xtrain_svd)

xtrain_svd_scl = scl.transform(xtrain_svd)

xtest_svd_scl = scl.transform(xtest_svd)

# 看看降维后LR的效果

clf_lr = LogisticRegression(C=100, solver='lbfgs',multi_class='multinomial')

clf_lr.fit(xtrain_svd_scl, y_train)

predictions = clf_lr.predict_proba(xtest_svd_scl)

print ("logloss: %0.3f " % multiclass_logloss(y_test, predictions))

logloss: 0.144

贝叶斯,卒。至此,逻辑回归大获全胜!

参考资料:

你可能感兴趣的:(python朴素贝叶斯调参)