使用scikit-learn进行文本分类

拈花ヽ惹草 提交于 2020-03-23 18:45:59

3 月,跳不动了?>>>

scikit-learn简介

scikit-learn是Python最为流行的一个机器学习库。它具有如下吸引人的特点:

 

  • 简单、高效且异常丰富的数据挖掘/数据分析算法实现;
  • 基于NumPy,SciPy,以及matplotlib,从数据探索性分析,数据可视化到算法实现,整个过程一体化实现;
  • 开源,有非常丰富的学习文档。

尤其是当我们要进行多种算法的效果对比评价,这种一体化实现的优势就更加能够凸显出来了。

 

既然scikit-learn模块如此重要,废话不多说,下面马上开搞!

项目组织及文件加载

 

项目组织

 

工作路径:`D:\my_python_workfile\Thesis\sklearn_exercise`


|--data:用于存放数据


    |--20news-bydate:练习用数据集
                |--20news-bydate-train:训练集
                |--20news-bydate-test:测试集

文件加载

 

假设我们需要加载的数据,组织结构如下:

container_folder/
    category_1_folder/
        file_1.txt file_2.txt ... file_42.txt
    category_2_folder/
        file_43.txt file_44.txt ...

可以使用以下函数进行数据的加载:

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开始的整数索引);
    • target_names:类别标签的具体含义(由子文件夹的名字`category_1_folder`等决定)。

 

下面,即采用这种方式,使用测试数据集[The 20 Newsgroups data set](Home Page for 20 Newsgroups Data Set)进行实例演示。先从网上下载该数据集,再在本地进行数据的加载。

作者:黄耀鹏
链接:https://zhuanlan.zhihu.com/p/20757320
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
 

```python
# 加载库
import os
import sys

##配置utf-8输出环境
#reload(sys)
#sys.setdefaultencoding("utf-8")

# 设置当前工作路径
os.chdir("D:\\my_python_workfile\\Thesis\\sklearn_exercise")

# 加载数据
from sklearn import datasets
twenty_train = datasets.load_files("data/20news-bydate/20news-bydate-train")
twenty_test = datasets.load_files("data/20news-bydate/20news-bydate-test")
```


```python
len(twenty_train.target_names),len(twenty_train.data),len(twenty_train.filenames),len(twenty_test.data)
```

(20, 11314, 11314, 7532)

 

```python
print("\n".join(twenty_train.data[0].split("\n")[:3]))
```

 

From: cubbie@garnet.berkeley.edu ( )

Subject: Re: Cubs behind Marlins? How?

Article-I.D.: agate.1pt592$f9a

 

```python
print(twenty_train.target_names[twenty_train.target[0]])
```

rec.sport.baseball

 

```python
twenty_train.target[:10]
```

array([ 9, 4, 11, 4, 0, 4, 5, 5, 13, 12])

可见,文件已经被成功载入。

当然,作为入门的训练,我们也可以使用`scikit-learn`自带的`toy example`数据集进行测试、玩耍。下面,介绍一下如何加载自带的数据集。

```python
from sklearn.datasets import fetch_20newsgroups

categories = ['alt.atheism', 'soc.religion.christian',
              'comp.graphics', 'sci.med']
twenty_train = fetch_20newsgroups(subset='train',
    categories=categories, shuffle=True, random_state=42)
```

 

文本特征提取

作者:黄耀鹏
链接:https://zhuanlan.zhihu.com/p/20757320
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
 

文本数据属于非结构化的数据,一般要转换成结构化的数据,方能进行实施机器学习算法实现文本分类。

 

常见的做法是将文本转换成『文档-词项矩阵』。矩阵中的元素,可以使用词频,或者TF-IDF值等。

计算词频

```python
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)
X_train_counts.shape
```

(11314, 129783)

使用TF-IDF进行特征提取

```python
from sklearn.feature_extraction.text import TfidfTransformer

tf_transformer = TfidfTransformer(use_idf = False).fit(X_train_counts)
X_train_tf = tf_transformer.transform(X_train_counts)
X_train_tf.shape
```
(11314, 129783)

以上程序使用了两步进行文本的形式化表示:先用`fit()`方法使得模型适用数据;再用`transform()`方法把词频矩阵重新表述成TF-IDF.

如下所示,也可以一步到位进行设置。

```python
tfidf_transformer = TfidfTransformer()
X_train_tfidf = tfidf_transformer.fit_transform(X_train_counts)
X_train_tfidf.shape
```

(11314, 129783)]

 

分类器训练

```python
from sklearn.naive_bayes import MultinomialNB
clf = MultinomialNB().fit(X_train_tfidf,twenty_train.target)
```
```python
# 对新的样本进行预测
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)

predicted = clf.predict(X_new_tfidf)

for doc,category in zip(docs_new,predicted):
    print("%r => %s") %(doc,twenty_train.target_names[category])

```

'God is love' => soc.religion.christian

'OpenGL on the GPU is fast' => comp.graphics

分类效果评价

建立管道

```python
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)
```

测试集分类准确率

```python
import numpy as np
docs_test = twenty_test.data
predicted = text_clf.predict(docs_test)
np.mean(predicted == twenty_test.target)
```

0.81691449814126393

使用朴素贝叶斯分类器,得到的测试集分类准确率为81.7%,效果还不错!

下面,使用线性核支持向量机看看效果如何。

```python
from sklearn.linear_model import SGDClassifier
text_clf_2 = Pipeline([('vect',CountVectorizer(stop_words='english',decode_error='ignore')),
                      ('tfidf',TfidfTransformer()),
                      ('clf',SGDClassifier(loss = 'hinge',penalty = 'l2',
                                          alpha = 1e-3,n_iter = 5, random_state = 42)),
                      ])

_ = text_clf_2.fit(twenty_train.data,twenty_train.target)
predicted = text_clf_2.predict(docs_test)

np.mean(predicted == twenty_test.target)
```
0.82355284121083383

支持向量机的分类准确率有所提升。

`scikit-learn`中提供了更精细化的评价指标,如:各类别的精确度,召回率,F值等。

下面,我们来看看更详细的指标表现如何。

```python
from sklearn import metrics
print(metrics.classification_report(twenty_test.target,predicted,
                                   target_names = twenty_test.target_names))
```

precision recall f1-score support

alt.atheism 0.71 0.71 0.71 319

comp.graphics 0.81 0.69 0.74 389

comp.os.ms-windows.misc 0.72 0.79 0.75 394

comp.sys.ibm.pc.hardware 0.73 0.66 0.69 392

comp.sys.mac.hardware 0.82 0.83 0.82 385

comp.windows.x 0.86 0.77 0.81 395

misc.forsale 0.80 0.87 0.84 390

rec.autos 0.91 0.90 0.90 396

rec.motorcycles 0.93 0.97 0.95 398

rec.sport.baseball 0.88 0.91 0.90 397

rec.sport.hockey 0.87 0.98 0.92 399

sci.crypt 0.85 0.96 0.90 396

sci.electronics 0.80 0.62 0.70 393

sci.med 0.90 0.87 0.88 396

sci.space 0.84 0.96 0.90 394

soc.religion.christian 0.75 0.93 0.83 398

talk.politics.guns 0.70 0.93 0.80 364

talk.politics.mideast 0.92 0.92 0.92 376

talk.politics.misc 0.89 0.56 0.69 310

talk.religion.misc 0.81 0.39 0.53 251

avg / total 0.83 0.82 0.82 7532

测试集的精确度和召回率的表现均不错.

下面看看『混淆矩阵』的结果。

```python
metrics.confusion_matrix(twenty_test.target,predicted)
```

使用网格搜索进行参数优化

我们使用分类器进行文本分类的过程中,有些参数需要预先给定。如前面`TfidfTransformer()`中的`use_idf`;`MultinomialNB()`中的平滑参数`alpha`;`SGClassifier()`中的惩罚系数`alpha`。然而,参数设置为多少,并不能直接拍脑袋决定。因为参数的设置可能会导致结果天差地别。

为了不沦落为一个『调参狗』,我们来看看如何使用暴力的『网格搜索算法』让计算机帮我们进行参数寻优。

```python
from sklearn.grid_search import GridSearchCV
parameters = {
            'vect__ngram_range':[(1,1),(1,2)],
             'tfidf__use_idf':(True,False),
             'clf__alpha':(1e-2,1e-3)
             }
```

如果要穷尽所有参数的组合,那势必要花费很多时间来等待结果。有的『土豪』同学可能会想:我能不能用金钱来换时间?

答案是肯定的。如果你有一台8核的电脑,那就把所有的核都用上吧!

```python
gs_clf = GridSearchCV(text_clf_2,parameters,n_jobs = -1)
```
```python
gs_clf = gs_clf.fit(twenty_train.data,twenty_train.target)
```

设置`n_jobs = -1`,计算机就会帮你自动检测并用上你所有的核进行并行计算。

```python
best_parameters,score,_ = max(gs_clf.grid_scores_,key = lambda x:x[1])
for param_name in sorted(parameters.keys()):
    print("%s: %r" %(param_name,best_parameters[param_name]))
```

clf__alpha: 0.01

tfidf__use_idf: True

vect__ngram_range: (1, 1)

```python
score
```
0.90516174650875025

参考文献


作者:黄耀鹏
链接:https://zhuanlan.zhihu.com/p/20757320
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!