基于jieba、gensim.word2vec、LogisticRegression的文档分类

建议读者安装anaconda,这个集成开发环境自带了很多包。 到2018年8月30日仍为最新版本的anaconda下载链接: https://pan.baidu.com/s/1pbzVbr1ZJ-iQqJzy1wKs0A 密码: g6ex 官网下载地址:https://repo.anaconda.com/archive/Anaconda3-5.2.0-Windows-x86_64.exe 下面代码的开发环境为jupyter notebook,使用在jupyter notebook中的截图表示运行结果。

0.打开jupyter

在桌面新建文件夹命名为基于word2vec的文档分类,如下图所示:

image.png

打开基于word2vec的文档分类文件夹,在按住Shift键的情况下,点击鼠标右键,出现如下图所示。 选择在此处打开PowerShell窗口,之后会在此路径下打开PowerShell。

image.png

在PowerShell中输入命令并运行:jupyter notebook

image.png

PowerShell运行命令后,会自动打开网页,点击如下图所示网页中的按钮:

image.png

代码文件重命名为word2vecTest,重命名按钮位置如下图所示:

image.png

1.数据准备

训练集共有24000条样本,12个分类,每个分类2000条样本。 测试集共有12000条样本,12个分类,每个分类1000条样本。 数据集下载链接: https://pan.baidu.com/s/1PY3u-WtfBdZQ8FsKgWo_KA 密码: hq5v 下载完成后,将压缩文件包放到基于word2vec的文档分类文件夹中,并将其解压到当前文件夹,如下图所示:

image.png

加载训练集到变量train_df中,并打印训练集前5行,代码如下。 read_csv方法中有3个参数,第1个参数是加载文本文件的路径,第2个关键字参数sep是分隔符,第3个关键字参数header是文本文件的第1行是否为字段名。

import pandas as pd

train_df = pd.read_csv('sohu_train.txt', sep='\t', header=None)
train_df.head()

上面一段代码的运行结果如下图所示:

image.png

查看训练集每个分类的名字以及样本数量,代码如下:

for name, group in train_df.groupby(0):
    print(name,len(group))

上面一段代码的运行结果如下图所示:

image.png

加载测试集并查看每个分类的名字以及样本数量,代码如下:

test_df = pd.read_csv('sohu_test.txt', sep='\t', header=None)
for name, group in test_df.groupby(0):
    print(name, len(group))

上面一段代码的运行结果如下图所示:

image.png

2.分词

需要安装jieba库,cmd中安装命令:pip install jieba 对训练集的24000条样本循环遍历,使用jieba库的cut方法获得分词列表赋值给变量cutWords。 判断分词是否为停顿词,如果不为停顿词,则添加进变量cutWords中。 代码如下:

iimport jieba
import time

train_df.columns = ['分类', '文章']
stopword_list = [k.strip() for k in open('stopwords.txt', encoding='utf8').readlines() if k.strip() != '']
cutWords_list = []
i = 0
startTime = time.time()
for article in train_df['文章']:
    cutWords = [k for k in jieba.cut(article) if k not in stopword_list]
    i += 1
    if i % 1000 == 0:
        print('前%d篇文章分词共花费%.2f秒' %(i, time.time()-startTime))
    cutWords_list.append(cutWords)

上面一段代码的运行结果如下:

前1000篇文章分词共花费67.62秒 前2000篇文章分词共花费133.32秒 前3000篇文章分词共花费272.28秒 前4000篇文章分词共花费405.01秒 前5000篇文章分词共花费529.79秒 前6000篇文章分词共花费660.60秒 前7000篇文章分词共花费696.51秒 前8000篇文章分词共花费732.88秒 前9000篇文章分词共花费788.51秒 前10000篇文章分词共花费841.61秒 前11000篇文章分词共花费903.35秒 前12000篇文章分词共花费970.47秒 前13000篇文章分词共花费1010.61秒 前14000篇文章分词共花费1048.76秒 前15000篇文章分词共花费1100.81秒 前16000篇文章分词共花费1154.80秒 前17000篇文章分词共花费1207.07秒 前18000篇文章分词共花费1256.73秒 前19000篇文章分词共花费1374.76秒 前20000篇文章分词共花费1493.85秒 前21000篇文章分词共花费1523.02秒 前22000篇文章分词共花费1552.69秒 前23000篇文章分词共花费1598.88秒 前24000篇文章分词共花费1644.56秒

从上面的运行结果可以看出,对24000篇文章进行分词共使用1644秒,即27分24秒。 时间充裕的读者可以自己运行试试,将分词结果保存为本地文件cutWords_list.txt,代码如下:

with open('cutWords_list.txt', 'w') as file: 
    for cutWords in cutWords_list:
        file.write(' '.join(cutWords) + '\n')

上面一段代码大概5秒左右运行完成,本文作者提供已经分词完成的文本文件。 读者节省时间可以下载,链接: https://pan.baidu.com/s/1vCBeHNR6DEGSQQDvA7yQOw 密码: j49q 下载文件是单个文本文件压缩的zip文件,文件大小为50M。 压缩的zip文件解压后的文本文件大小为118M。 载入分词文件的代码如下:

with open('cutWords_list.txt') as file:
    cutWords_list = [k.split() for k in file.readlines()]

3.word2vec模型

完成此步骤需要先安装gensim库,安装命令:pip install gensim 调用gensim.models.word2vec库中的LineSentence方法实例化行模型对象,代码如下:

from gensim.models import Word2Vec

word2vec_model = Word2Vec(cutWords_list, size=100, iter=10, min_count=20)

调用模型对象的方法时,一直提示警告信息,避免出现烦人的警告信息,代码如下:

import warnings

warnings.filterwarnings('ignore')

调用Word2Vec模型对象的wv.most_similar方法查看与摄影含义最相近的词。 wv.most_similar方法有2个参数,第1个参数是要搜索的词,第2个关键字参数topn数据类型为正整数,是指需要列出多少个最相关的词汇,默认为10,即列出10个最相关的词汇。 wv.most_similar方法返回值的数据类型为列表,列表中的每个元素的数据类型为元组,元组有2个元素,第1个元素为相关词汇,第2个元素为相关程度,数据类型为浮点型。

word2vec_model.wv.most_similar('摄影')

上面一段代码的运行结果,如下图所示:

image.png

wv.most_similar方法使用positive和negative这2个关键字参数的简单示例。 查看女人+先生-男人的结果,代码如下:

word2vec_model.most_similar(positive=['女人', '先生'], negative=['男人'], topn=1)

上面一段代码的运行结果,如下图所示:

image.png

查看两个词的相关性,如下图所示:

image.png

保存Word2Vec模型为word2vec_model.w2v文件,代码如下:

word2vec_model.save('word2vec_model.w2v')

4.特征工程

对于每一篇文章,获取文章的每一个分词在word2vec模型的相关性向量。 然后把一篇文章的所有分词在word2vec模型中的相关性向量求和取平均数,即此篇文章在word2vec模型中的相关性向量。 实例化Word2Vec对象时,关键字参数size定义为100,则相关性矩阵都为100维。 定义getVector函数获取每个文章的词向量,传入2个参数,第1个参数是文章分词的结果,第2个参数是word2vec模型对象。 变量vector_list是通过列表推导式得出单篇文章所有分词的词向量,通过np.array方法转成ndarray对象再对每一列求平均值。 代码经过作者实验,使用numpy库计算速度最快,读者如果发现运行更快的代码写法可以留言。 每当完成1000篇文章词向量转换的时候,打印花费时间。 最终将24000篇文章的词向量赋值给变量X,即X为特征矩阵。

对比文章转换为相关性向量的3种方法花费时间。 为了节省时间,只对比前5000篇文章转换为相关性向量的花费时间。

4.1 第1种方法,用for循环常规计算

代码如下:

import numpy as np 
import time 

def getVector_v1(cutWords, word2vec_model):
    count = 0
    article_vector = np.zeros(word2vec_model.layer1_size)
    for cutWord in cutWords:
        if cutWord in word2vec_model:
            article_vector += word2vec_model[cutWord]
            count += 1
    return article_vector / count

startTime = time.time()
vector_list = []
i = 0
for cutWords in cutWords_list[:5000]:
    i += 1
    if i % 1000 ==0:
        print('前%d篇文章形成词向量花费%.2f秒' %(i, time.time()-startTime))
    vector_list.append(getVector_v1(cutWords, word2vec_model))
X = np.array(vector_list)

上面一段代码的运行结果如下:

前1000篇文章形成词向量花费21.05秒 前2000篇文章形成词向量花费41.51秒 前3000篇文章形成词向量花费92.37秒 前4000篇文章形成词向量花费140.78秒 前5000篇文章形成词向量花费181.00秒

4.2 第2种方法,用pandas的mean方法计算

代码如下:

import time 
import pandas as pd 

def getVector_v2(cutWords, word2vec_model):
    vector_list = [word2vec_model[k] for k in cutWords if k in word2vec_model]
    vector_df = pd.DataFrame(vector_list)
    cutWord_vector = vector_df.mean(axis=0).values
    return cutWord_vector

startTime = time.time()
vector_list = []
i = 0
for cutWords in cutWords_list[:5000]:
    i += 1
    if i % 1000 ==0:
        print('前%d篇文章形成词向量花费%.2f秒' %(i, time.time()-startTime))
    vector_list.append(getVector_v2(cutWords, word2vec_model))
X = np.array(vector_list)

上面一段代码的运行结果如下:

前1000篇文章形成词向量花费42.44秒 前2000篇文章形成词向量花费83.17秒 前3000篇文章形成词向量花费180.70秒 前4000篇文章形成词向量花费272.86秒 前5000篇文章形成词向量花费349.57秒

4.3 第3种方法,用numpy的mean方法计算

代码如下:

def getVector_v3(cutWords, word2vec_model):
    vector_list = [word2vec_model[k] for k in cutWords if k in word2vec_model]
    cutWord_vector = np.array(vector_list).mean(axis=0)
    return cutWord_vector

startTime = time.time()
vector_list = []
i = 0
for cutWords in cutWords_list:
    i += 1
    if i % 1000 ==0:
        print('前%d篇文章形成词向量花费%.2f秒' %(i, time.time()-startTime))
    vector_list.append(getVector_v3(cutWords, word2vec_model))
X = np.array(vector_list)

上面一段代码的运行时间如下:

前1000篇文章形成词向量花费15.50秒 前2000篇文章形成词向量花费30.63秒 前3000篇文章形成词向量花费68.17秒 前4000篇文章形成词向量花费103.79秒 前5000篇文章形成词向量花费133.46秒 前6000篇文章形成词向量花费164.61秒 前7000篇文章形成词向量花费172.98秒 前8000篇文章形成词向量花费181.54秒 前9000篇文章形成词向量花费195.56秒 前10000篇文章形成词向量花费208.95秒 前11000篇文章形成词向量花费223.78秒 前12000篇文章形成词向量花费240.10秒 前13000篇文章形成词向量花费249.37秒 前14000篇文章形成词向量花费258.24秒 前15000篇文章形成词向量花费270.88秒 前16000篇文章形成词向量花费284.11秒 前17000篇文章形成词向量花费297.26秒 前18000篇文章形成词向量花费309.80秒 前19000篇文章形成词向量花费336.87秒 前20000篇文章形成词向量花费364.37秒 前21000篇文章形成词向量花费370.69秒 前22000篇文章形成词向量花费377.07秒 前23000篇文章形成词向量花费386.88秒 前24000篇文章形成词向量花费396.64秒

因为形成特征矩阵的花费时间较长,为了避免以后重复花费时间,把特征矩阵保存为文件。 使用ndarray对象的dump方法,需要1个参数,数据类型为字符串,为保存文件的文件名,代码如下:

X.dump('articles_vector.txt')

文章向量文件,即特征矩阵文件下载链接: https://pan.baidu.com/s/1tYwZHIdu4GfU3xoMbjYYPQ 密码: y66i 加载此文件中的内容赋值给变量X,代码如下:

X = np.load('articles_vector.txt')

5.模型训练、模型评估

5.1 标签编码

调用sklearn.preprocessing库的LabelEncoder方法对文章分类标签编码

from sklearn.preprocessing import LabelEncoder
import pandas as pd

train_df = pd.read_csv('sohu_train.txt', sep='\t', header=None)
train_df.columns = ['分类', '文章']
labelEncoder = LabelEncoder()
y = labelEncoder.fit_transform(train_df['分类'])

5.2 逻辑回归模型

调用sklearn.linear_model库的LogisticRegression方法实例化模型对象。 调用sklearn.model_selection库的train_test_split方法划分训练集和测试集。

from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split

train_X, test_X, train_y, test_y = train_test_split(X, y, test_size=0.2)
logistic_model = LogisticRegression()
logistic_model.fit(train_X, train_y)
logistic_model.score(test_X, test_y)

上面一段代码的运行结果如下:

0.7825

5.3 保存模型

调用sklearn.externals库中的joblib方法保存模型为logistic.model文件。 模型持久化官方文档示例:http://sklearn.apachecn.org/cn/0.19.0/modules/model_persistence.html

from sklearn.externals import joblib

joblib.dump(logistic_model, 'logistic.model')

模型文件logistic.model下载链接: https://pan.baidu.com/s/1N6oowCjjYPyQ7Z0fg9O4dQ 密码: r2xx 加载模型代码如下:

from sklearn.externals import joblib

logistic_model = joblib.load('logistic.model')

5.4 交叉验证

交叉验证的结果更具有说服力。 调用sklearn.model_selection库的ShuffleSplit方法实例化交叉验证对象。 调用sklearn.model_selection库的cross_val_score方法获得交叉验证每一次的得分。

from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import ShuffleSplit
from sklearn.model_selection import cross_val_score

cv_split = ShuffleSplit(n_splits=5, train_size=0.7, test_size=0.2)
logistic_model = LogisticRegression()
score_ndarray = cross_val_score(logistic_model, X, y, cv=cv_split)
print(score_ndarray)
print(score_ndarray.mean())

上面一段代码的运行结果如下:

[0.78604167 0.78895833 0.78 0.78375 0.785625 ] 0.784875

6.模型测试

调用sklearn.externals库的joblib对象的load方法加载模型赋值给变量logistic_model。 调用pandas库read_csv方法读取测试集数据。 调用DataFrame对象的groupby方法对每个分类分组,从而每种文章类别的分类准确性。 调用自定义的getVector方法将文章转换为相关性向量。 自定义getVectorMatrix方法获得测试集的特征矩阵。 调用StandardScaler对象的transform方法将预测标签做标签编码,从而获得预测目标值。

import pandas as pd
import numpy as np
from sklearn.externals import joblib

def getVectorMatrix(article_series):
    return np.array([getVector_v3(k, word2vec_model) for k in article_series])
    
logistic_model = joblib.load('logistic.model')
test_df = pd.read_csv('sohu_test.txt', sep='\t', header=None)
test_df.columns = ['分类', '文章']
for name, group in test_df.groupby('分类'):
    featureMatrix = getVectorMatrix(group['文章'])
    target = labelEncoder.transform(group['分类'])
    print(name, logistic_model.score(featureMatrix, target))

上面一段代码的运行结果如下:

体育 0.915 健康 0.811 女人 0.391 娱乐 0.061 房地产 0.53 教育 0.865 文化 0.805 新闻 0.036 旅游 0.766 汽车 0.693 科技 0.21 财经 0.077

7.结论

word2vec模型应用的第1个小型项目,训练集数据共有24000条,测试集数据共有12000条。 经过交叉验证,模型平均得分为0.78左右。 测试集的验证效果中,体育、教育、健康、文化、旅游、汽车、娱乐这7个分类得分较高,即容易被正确分类。 女人、娱乐、新闻、科技、财经这5个分类得分较低,即难以被正确分类。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏机器之心

教程 | 如何用百度深度学习框架PaddlePaddle做数据预处理

35560
来自专栏PaddlePaddle

PaddlePaddle发布新版API,简化深度学习编程

PaddlePaddle是百度于2016年9月开源的一款分布式深度学习平台,为百度内部多项产品提供深度学习算法支持。为了使PaddlePaddle更加易用,我们...

36980
来自专栏数据魔术师

运筹学教学|Berders decomposition (二)应用实例及算法实现(附源代码及详细的代码注释)

我们在运筹学教学|Benders decomposition(一)技术介绍篇中已经介绍了Benders Decomposition的基本原理,下面为大家提供具体...

58370
来自专栏大数据智能实战

LargeVis可视化技术学习

大图可视化一直是大数据可视化领域的一个关键技术,当前有各种办法,但是今年出来了一个LargeVis的技术,因此对这个技术进行复现和学习一下。 前面有很多基础理论...

47470
来自专栏机器学习算法原理与实践

tensorflow机器学习模型的跨平台上线

    在用PMML实现机器学习模型的跨平台上线中,我们讨论了使用PMML文件来实现跨平台模型上线的方法,这个方法当然也适用于tensorflow生成的模型,但...

24120
来自专栏企鹅号快讯

Invoke-PSImage利用分析

0x00 前言 最近在github看见一个有趣的项目:Invoke-PSImage,在png文件的像素内插入powershell代码作为payload(不影响原...

27690
来自专栏生信技能树

blast简介及格式解读及练习题

双序列比对可以采用是基于动态规划算法的Needleman-Wunsch(NW)和Smith-Waterman algorithm(SW)算法,虽然精度高,但计...

27930
来自专栏用户2442861的专栏

Caffe学习:Blobs, Layers, and Nets

-注意:网络结构是设备无关的,Blob和Layer=隐藏了模型定义的具体实现细节。定义网络结构后,可以通过Caffe::mode()或者Caffe::set_m...

7900
来自专栏机器学习算法工程师

史上最详细的XGBoost实战(上)

作者:章华燕 编辑:祝鑫泉 零 环境介绍: · Python版本:3.6.2 · 操作系统:Windows · 集成开发环境:PyCharm 一 安装Pyt...

78840
来自专栏机器学习实践二三事

Caffe中均值文件的问题

关于均值文件 (1) 在Caffe中作classification时经常需要使用均值文件,但是caffe自己提供的脚本只能将图像数据转换为 binarypr...

23290

扫码关注云+社区

领取腾讯云代金券