词袋模型在文本分类中的用法
1.加载数据
20 Newsgroups:数据被组织成 20 个不同的新闻组,每个新闻组对应一个不同的主题。一些新闻组彼此非常密切相关(例如 comp.sys.ibm.pc.hardware
/ comp.sys.mac.hardware
),而其他新闻组则非常不相关(例如 misc.forsale
/ soc.religion.christian
)。以下是 20 个新闻组的列表,按主题划分(或多或少):
数据集:下载地址
# 加载训练集、测试集
from sklearn import datasets
twenty_train = datasets.load_files("20news-bydate/20news-bydate-train")
twenty_test = datasets.load_files("20news-bydate/20news-bydate-test")
sklearn.datasets.load_files(container_path, description=None, categories=None, load_content=True, shuffle=True, encoding=None, decode_error=‘strict’, random_state=0)
container_path
:container_folder 的路径;load_content = True
:是否把文件中的内容加载到内存;encoding = None
:编码方式。当前文本文件的编码方式一般为 utf-8,如果不指明编码方式(encoding=None),那么文件内容将会按照 bytes 处理,而不是 unicode 处理。
返回值:Bunch,Dictionary-like object。主要属性包括:
data
:原始数据;filenames
:每个文件的名字;target
:类别标签(从 0 0 0 开始的整数索引);target_names
:类别标签的具体含义(由子文件夹的名字 category_1_folder 等决定)。
2.文本表示
将文本文件变成数字的特征表示,这里主要是利用词袋模型。
2.1 BOW
使用 CountVectorizer 构建词频向量。CountVectorizer 支持单词或者连续字符的 N-gram 模型的计数,利用 scipy.sparse 矩阵只在内存中保存特征向量中非 0 0 0 元素位置以节省内存。
from sklearn.feature_extraction.text import CountVectorizer
count_vect = CountVectorizer(stop_words="english", decode_error='ignore') # 创建词频转换器
X_train_counts = count_vect.fit_transform(twenty_train.data) # 转换训练集
print(X_train_counts.shape)
(11314, 129782)
2.2 TF-IDF
用 TfidfTransformer 将词频向量转为 TF-IDF 形式。
from sklearn.feature_extraction.text import TfidfTransformer
tfidf_transformer = TfidfTransformer()
X_train_tfidf = tfidf_transformer.fit_transform(X_train_counts)
print(X_train_tfidf.shape)
(11314, 129782)
3.文本分类
3.1 正常流程
离散型朴素贝叶斯 MultinomialNB,朴素贝叶斯模型在多项分布假设下的实现,适用于多分类场景。
from sklearn.naive_bayes import MultinomialNB
clf = MultinomialNB()
clf.fit(X_train_tfidf, twenty_train.target)
docs_new = ['God is love', 'OpenGL on the GPU is fast']
X_new_counts = count_vect.transform(docs_new) # 计算词频
X_new_tfidf = tfidf_transformer.transform(X_new_counts) # 计算 TF-IDF
y_pred = clf.predict(X_new_tfidf)
for doc, category in zip(docs_new, y_pred): # category是数字
print(("%r => %s")%(doc, twenty_train.target_names[category]))
3.2 管道流水
使用管道后,测试集不用一步步重复训练集的预处理,直接管道处理了。
from sklearn.pipeline import Pipeline
text_clf = Pipeline([('vect', CountVectorizer(stop_words="english", decode_error='ignore')),
('tfidf', TfidfTransformer()),
('clf', MultinomialNB())
])
text_clf = text_clf.fit(twenty_train.data, twenty_train.target) # 训练集
score = text_clf.score(twenty_test.data, twenty_test.target) # 测试集
print(score)
0.8169144981412639
SVM,对于大型数据集,使用 LinearSVC 或 SGDClassifier。
from sklearn.linear_model import SGDClassifier
from sklearn import metrics
text_clf_2 = Pipeline([('vect', CountVectorizer(stop_words='english', decode_error='ignore')), # 去停用词
('tfidf', TfidfTransformer()),
('clf', SGDClassifier(loss='hinge', penalty='l2', alpha=1e-3, max_iter=100, random_state=42))
])
text_clf_2.fit(twenty_train.data, twenty_train.target)
# text_clf_2.score(twenty_test.data, twenty_test.target)
y_pred = text_clf_2.predict(twenty_test.data)
acc = metrics.accuracy_score(twenty_test.target, y_pred)
print(acc)
0.8227562400424854
4.结果报告
各类别的精确度,召回率,F值等。
print(metrics.classification_report(twenty_test.target, y_pred, target_names=twenty_test.target_names))
5.超参调优
5.1 网格搜索
CountVectorizer()
中的n-gram
TfidfTransformer()
中的use_idf
SGClassifier()
中的惩罚系数alpha
from sklearn.model_selection import GridSearchCV
parameters = {
'vect__ngram_range': [(1, 1), (1, 2)],
'tfidf__use_idf': (True, False),
'clf__alpha': (1e-2, 1e-3)
}
gs_clf = GridSearchCV(text_clf_2, parameters, n_jobs=-1, cv=5) # text_clf_2: SVC 的 Pipeline
grid_result = gs_clf.fit(twenty_train.data, twenty_train.target)
print("Best: %f \nusing %s" % (grid_result.best_score_, grid_result.best_params_))
Best: 0.906842
using {'clf__alpha': 0.001, 'tfidf__use_idf': True, 'vect__ngram_range': (1, 2)}
5.2 随机搜索
随机搜索进行超参数优化。
from sklearn.model_selection import RandomizedSearchCV
parameters = {
'vect__ngram_range': [(1, 1), (1, 2), (2, 2)],
'tfidf__use_idf': (True, False),
'clf__alpha': (1e-1, 1e-2, 1e-3, 1e-4, 1e-5)
}
rs_clf = RandomizedSearchCV(text_clf_2, parameters, n_jobs=-1, cv=5)
rs_result = rs_clf.fit(twenty_train.data, twenty_train.target)
print("Best: %f \nusing %s" % (rs_result.best_score_, rs_result.best_params_))
Best: 0.927436
using {'vect__ngram_range': (1, 2), 'tfidf__use_idf': True, 'clf__alpha': 1e-05}
已确定 TfidfTransformer 里的参数 use_idf=True
会更好。
5.3 网格搜索-增加超参数
增加参数:
- CountVectorizer 里的
max_df
:按 比例 或 绝对数量 删除 d f超过 max_df 的词。 - CountVectorizer里的
max_features
:选择 tf 最大的 max_features 个特征。 - TfidfTransformer里的
norm
:数据标准化,{‘l1’, ‘l2’} or None, default=’l2’。 - SGDClassifier里的
penalty
:惩罚项。 - SGDClassifier里的
n_iter
:迭代次数。
重要的参数:
- vect__max_df
- vect__ngram_range
- clf__alpha
- clf__penalty
from sklearn.model_selection import GridSearchCV
from sklearn.linear_model import SGDClassifier
from sklearn.pipeline import Pipeline
text_clf_3 = Pipeline([
('vect', CountVectorizer(stop_words="english", decode_error='ignore')),
('tfidf', TfidfTransformer()),
('clf', SGDClassifier()),
])
parameters = {
'vect__max_df': (0.5, 0.75, 1.0),
'vect__max_features': (None, 10000, 50000,60000),
'vect__ngram_range': [(1, 1), (1, 2),(2,2)],
'tfidf__norm': ('l1', 'l2'),
'clf__alpha': (0.0001, 0.00001, 0.000001,0.0000001),
'clf__penalty': ('l2', 'elasticnet'),
'clf__max_iter': (10, 50, 80),
}
gs_clf = GridSearchCV(text_clf_3, parameters, n_jobs = -1, cv=5)
grid_result = gs_clf.fit(twenty_train.data, twenty_train.target)
print("Best: %f \nusing %s" % (grid_result.best_score_, grid_result.best_params_))
计算的时间有点长,最好自己验证一下。
Best: 0.931766
using {'vect__ngram_range': (1, 2),
'clf__alpha': 1e-07,
'vect__max_df': 0.75,
'vect__max_features': None,
'clf__n_iter': 50,
'clf__penalty': 'elasticnet',
'tfidf__norm': 'l1'
}
总结:当文本被向量化后,就可以将其看作是数字,按照常见的机器学习方法进行回归、分类、聚类、降维等任务。