1. 背景
在文本分类任务中经常使用XGBoost快速建立baseline,在处理文本数据时需要引入TFIDF将文本转换成基于词频的向量才能输入到XGBoost进行分类。这篇博客将简单阐述XGB进行文本分类的实现与部分原理。
2. 实现
代码语言:txt复制import pandas as pd
import xgboost as xgb
import jieba
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn import metrics
from sklearn.model_selection import train_test_split
from matplotlib import pyplot
def word_seg(x):
content = str(x['a']) ' ' str(x['b'])
for i in string.punctuation ''.join([r'N', 't', 'n', '(', ')', ',', '。', ':', '】', '【']):
content = content.replace(i, '')
return ' '.join(jieba.lcut(content))
train_data = pd.read_csv('./data/train.csv')
train_data['content'] = train_data.apply(word_seg, axis=1)
x_train, x_test, y_train, y_test = train_test_split(train_data['content'], train_data['label'], test_size=0.2)
# 将语料转化为词袋向量,根据词袋向量统计TF-IDF
vectorizer = CountVectorizer(max_features=5000)
tf_idf_transformer = TfidfTransformer()
tf_idf = tf_idf_transformer.fit_transform(vectorizer.fit_transform(x_train))
x_train_weight = tf_idf.toarray() # 训练集TF-IDF权重矩阵
tf_idf = tf_idf_transformer.transform(vectorizer.transform(x_test))
x_test_weight = tf_idf.toarray() # 测试集TF-IDF权重矩阵
#基于Scikit-learn接口的分类
# 训练模型
eval_set = [(x_train_weight, y_train), (x_test_weight, y_test)]
model = xgb.XGBClassifier(max_depth=6, learning_rate=0.1, n_estimators=60, objective='binary:logistic')
model.fit(x_train_weight, y_train, eval_set=eval_set, verbose=True)
y_predict = model.predict(x_test_weight)
results = model.evals_result()
results
epochs = len(results['validation_0']['logloss'])
x_axis = range(0, epochs)
# plot log loss
fig, ax = pyplot.subplots()
ax.plot(x_axis, results['validation_0']['logloss'], label='Train')
ax.plot(x_axis, results['validation_1']['logloss'], label='Test')
ax.legend()
pyplot.ylabel('Log Loss')
pyplot.title('XGBoost Log Loss')
pyplot.show()
代码语言:txt复制#利用训练完的模型直接测试
# xgb_model = xgb.Booster(model_file='data/xgb_model') # init model #加载模型
# dtest = xgb.DMatrix('data/test.buffer') #加载数据
# xgb_test(dtest,xgb_model)
# y_predict = model.predict(dtest) # 模型预测
label_all = [0, 1]
confusion_mat = metrics.confusion_matrix(y_test, y_predict)
df = pd.DataFrame(confusion_mat, columns=label_all)
df.index = label_all
print('准确率:n', metrics.accuracy_score(y_test, y_predict))
print('confusion_matrix: n', df)
print('分类报告: n', metrics.classification_report(y_test, y_predict))
print('AUC: %.4f' % metrics.roc_auc_score(y_test, y_predict))
代码语言:txt复制准确率:
0.9730405840669959
confusion_matrix:
0 1
0 48544 0
1 2511 42085
分类报告:
precision recall f1-score support
0 0.95 1.00 0.97 48544
1 1.00 0.94 0.97 44596
accuracy 0.97 93140
macro avg 0.98 0.97 0.97 93140
weighted avg 0.97 0.97 0.97 93140
AUC: 0.9718
3. TfidfVectorizer原理
这里简单介绍下scikit-learn自然语言文本处理的一个开源方法——TfidfVectorizer,该方法分别是由两种方法 CountVectorizer 与 TfidfTransformer 的结合,下面进行说明,说明之前给出三个文档链接(本文基本翻译自官方文档):
(文档在手天下我有,有问题看文档)
方法一:TfidfVectorizer 方法二:CountVectorizer、TfidfTransformer
好了进入正文
TfidfVectorizer 处理文本语言的主要中心思想也就是 TF-IDF (词频-逆文档频率),由于本篇文章的重点是介绍该模块,所以不过多对 TF-IDF 说明,有需要的这里给出之前写的比较详细的文章可以参考——TF-IDF及相关知识
TfidfVectorizer 的使用相当于先调用了 CountVectorizer 方法,然后再调用 TfidfTransformer 方法,所以想了解 TfidfVectorizer 还得从后面两个方法说起。
CountVectorizer:
功能:
将文本文档集合转换为计数的稀疏矩阵。内部的实现方法为调用scipy.sparse.csr_matrix模块。并且,如果在调用CountVectorizer() 时不提供先验词典并且不使用执行某种特征选择的分析器,则特征词的数量将等于通过该方法直接分析数据找到的词汇量。
代码说明:
代码语言:txt复制from sklearn.feature_extraction.text import CountVectorizer
corpus = ['This is the first document.',
'This document is the second document.',
'And this is the third one.',
'Is this the first document?']
vectorizer = CountVectorizer() # ()这里不提供先验词典
# vectorizer.fit(corpus) # 先fit训练传入的文本数据
# X = vectorizer.transform(corpus) # 然后对文本数据进行标记并转换为稀疏计数矩阵
X = vectorizer.fit_transform(corpus) # 可以fit、transform一起使用替代上面的两行
print(vectorizer.get_feature_names()) # 获得模型直接分析数据找到的词汇量(上面单词的集合)
print(X.toarray()) # 直接打印X输出的是每个词的位置
代码语言:txt复制['and', 'document', 'first', 'is', 'one', 'second', 'the', 'third', 'this']
[[0 1 1 1 0 0 1 0 1] # 4行 数据样本
[0 2 0 1 0 1 1 0 1] # 9列 特征单词
[1 0 0 1 1 0 1 1 1]
[0 1 1 1 0 0 1 0 1]]
参数简单说明:
以上为最简单的 CountVectorizer 模块的使用,我们几乎没有使用任何的参数和方法,但依然能达到一个较好的【文本—>词向量稀疏矩阵 】的效果,部分参数如下。
input : string {‘filename’, ‘file’, ‘content’}
encoding : string, ‘utf-8’ by default.
stop_words : string {‘english’}, list, or None (default)
max_df : float in range 0.0, 1.0 or int, default=1.0
min_df : float in range 0.0, 1.0 or int, default=1
max_features : int or None, default=None
TfidfTransformer:
功能:
将计数矩阵(如上图所示)转换为标准化的 tf 或 tf-idf 表示。Tf 表示术语频率,而tf-idf表示术语频率乘以逆文档频率。这是信息检索中常用的术语加权方案,在文档分类中也有很好的用途。用于计算项的 tf-idf 的公式是 tf-idf(d,t)= tf(t)* idf(d,t)。
代码说明:
代码语言:txt复制from sklearn.feature_extraction.text import TfidfTransformer
transform = TfidfTransformer() # 使用TF-IDF(词频、逆文档频率)应用于稀疏矩阵
Y = transform.fit_transform(X) # 使用上面CountVectorizer处理后的 X 数据
print(Y.toarray()) # 输出转换为tf-idf后的 Y 矩阵,同样直接打印 Y 输出每个数据的位置
print(vectorizer.get_feature_names()) # 打印特征名
代码语言:txt复制[[0. 0.46979139 0.58028582 0.38408524 0. 0.
0.38408524 0. 0.38408524]
[0. 0.6876236 0. 0.28108867 0. 0.53864762
0.28108867 0. 0.28108867]
[0.51184851 0. 0. 0.26710379 0.51184851 0.
0.26710379 0.51184851 0.26710379]
[0. 0.46979139 0.58028582 0.38408524 0. 0.
0.38408524 0. 0.38408524]]
['and', 'document', 'first', 'is', 'one', 'second', 'the', 'third', 'this']
参数简单说明:
以上为直接使用 TfidfTransformer 转换 CountVectorizer 处理后的计数矩阵为标准化的 tf-idf 矩阵【词向量稀疏矩阵—>标准化tf-idf】的效果,下面给出其部分参数。
norm : ‘l1’, ‘l2’ or None, optional
Norm used to normalize term vectors. None for no normalization.
use_idf : boolean, default=True
Enable inverse-document-frequency reweighting.
smooth_idf : boolean, default=True
Smooth idf weights by adding one to document frequencies, as if an extra document was seen containing every term in the collection exactly once. Prevents zero divisions.
sublinear_tf : boolean, default=False
Apply sublinear tf scaling, i.e. replace tf with 1 log(tf).
最后可以简单的描述下TfidfVectorizer了
TfidfVectorizer
功能:
前文说过 TfidfVectorizer 相当于两者的结合使用,先后调用 CountVectorizer 和 TfidfTransformer 两种方法(简化了代码,但运算思想还是不变),并且参数的使用基本一致。
代码说明:
代码语言:txt复制from sklearn.feature_extraction.text import TfidfVectorizer
VT = TfidfVectorizer() # 先后调用CountVectorizer和TfidfTransformer两种方法(简化了代码,但运算思想还是不变)
result = VT.fit_transform(corpus)
print(result.toarray())
print(VT.get_feature_names())
代码语言:txt复制[[0. 0.46979139 0.58028582 0.38408524 0. 0.
0.38408524 0. 0.38408524]
[0. 0.6876236 0. 0.28108867 0. 0.53864762
0.28108867 0. 0.28108867]
[0.51184851 0. 0. 0.26710379 0.51184851 0.
0.26710379 0.51184851 0.26710379]
[0. 0.46979139 0.58028582 0.38408524 0. 0.
0.38408524 0. 0.38408524]]
['and', 'document', 'first', 'is', 'one', 'second', 'the', 'third', 'this']
可以看到与上面 CountVectorizer 和 TfidfTransformer 处理后的结果一致,确实为两者的结合使用。
参数及使用方法与 CountVectorizer和TfidfTransformer 一致,这里不再描述。