文章目录

  • 1.中文评论情感分析(keras+rnn)
    • 1.1 需要的库
    • 1.2 预训练词向量
    • 1.3 词向量模型
    • 1.4 训练语料 (数据集)
    • 1.5 分词和tokenize
    • 1.6 索引长度标准化
    • 1.7 反向tokenize
    • 1.8 构建embedding matrix
    • 1.9 padding(填充)和truncating(修剪)
    • 1.10 用keras搭建LSTM模型
    • 1.11 结论
    • 1.12 错误分类
  • 2.新浪新闻分类(tensorflow+cnn)
  • 3.搜狐新闻文本分类(word2vec)
    • 3.1 数据的准备
    • 3.2 word2vec模型
    • 3.3 特征工程:
    • 3.4 模型训练,模型评估
      • 标签编码:
      • 逻辑回归模型
      • 保存模型
      • 交叉验证
      • 模型测试
    • 3.5 总结
  • 4.搜狐新闻文本分类(TfidfVectorizer)
  • 5.中文纠错代码解析(pycorrector)
    • 5.1 win10上安装pycorrector
    • 5.2 unbuntu上训练语言模型:
    • 5.3 use kenlm
      • kenlm打分
      • 分词
    • 5.4 (2或3_gram)打分
    • 5.5 numpy矩阵处理
    • 5.6 编辑距离
    • 5.7 pandas use pycorrector

1.中文评论情感分析(keras+rnn)

1.1 需要的库

# 首先加载必用的库,jieba和gensim专门中文
# %matplotlib inline功能是可以内嵌绘图,并且可以省略掉plt.show()这一步
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
import re #正则化用
import jieba # 中文必须用【结巴分词】,因为计算机不会断句
# gensim用来加载预训练word vector
from gensim.models import KeyedVectors
#KeyedVectors实现实体(单词、文档、图片都可以)和向量之间的映射,实体都用string id表示
#有时候运行代码时会有很多warning输出,如提醒新版本之类的,如果不想乱糟糟的输出可以这样
import warnings
warnings.filterwarnings("ignore")

1.2 预训练词向量

词袋cn_model:北京师范大学中文信息处理研究所与中国人民大学 DBIIR 实验室的研究者开源的"chinese-word-vectors" github链接为:https://github.com/Embedding/Chinese-Word-Vectors 。这里我们使用了"chinese-word-vectors"知乎Word + Ngram的词向量,可以从上面github链接下载,作者本人下载好放入网盘和其他语料需自己建立读取路径,链接:https://pan.baidu.com/s/1RCrNNAagOjLqj0BP8cA9EQ 提取码:fgux

# 使用gensim加载预训练中文分词embedding
cn_model = KeyedVectors.load_word2vec_format('chinese_word_vectors/sgns.zhihu.bigram', binary=False)

1.3 词向量模型

在这个词向量模型里,每一个词是一个索引,对应的是一个长度为300的向量,我们今天需要构建的LSTM神经网络模型并不能直接处理汉字文本,需要先进行分次并把词汇转换为词向量,步骤请参考: 0.原始文本:我喜欢文学 1.分词:我,喜欢,文学 2.Tokenize(索引化):[2,345,4564] 3.Embedding(词向量化):用一个300维的词向量,上面的tokens成为一个[3,300]的矩阵 4.RNN:1DCONV,GRU,LSTM等 5.经过激活函数输出分类:如sigmoid输出在0到1间

# 由此可见每一个词都对应一个长度为300的向量
embedding_dim = cn_model['山东大学'].shape[0]  #一词山东大学,shape[0]返回行数
print('词向量的长度为{}'.format(embedding_dim))
cn_model['山东大学']

输出如下

词向量的长度为300
Out[3]:
array([-2.603470e-01, 3.677500e-01, -2.379650e-01, 5.301700e-02,
-3.628220e-01, -3.212010e-01, -1.903330e-01, 1.587220e-01,
.
dtype=float32)

# 计算相似度
cn_model.similarity('橘子', '橙子')

输出

0.66128117

# dot('橘子'/|'橘子'|, '橙子'/|'橙子'| ),余弦相似度
np.dot(cn_model['橘子']/np.linalg.norm(cn_model['橘子']),
cn_model['橙子']/np.linalg.norm(cn_model['橙子']))

输出

0.66128117

# 找出最相近的词,余弦相似度
cn_model.most_similar(positive=['大学'], topn=10)

输出

[(‘高中’, 0.7247823476791382),
(‘本科’, 0.6768535375595093),
(‘研究生’, 0.6244412660598755),
(‘中学’, 0.6088204979896545),
(‘大学本科’, 0.595908522605896),
(‘初中’, 0.5883588790893555),
(‘读研’, 0.5778335332870483),
(‘职高’, 0.5767995119094849),
(‘大学毕业’, 0.5767451524734497),
(‘师范大学’, 0.5708829760551453)]

# 找出不同的词
test_words = '老师 会计师 程序员 律师 医生 老人'
test_words_result = cn_model.doesnt_match(test_words.split())
print('在 '+test_words+' 中:\n不是同一类别的词为: %s' %test_words_result)

输出

在 老师 会计师 程序员 律师 医生 老人 中:
不是同一类别的词为: 老人

cn_model.most_similar(positive=['女人','出轨'], negative=['男人'], topn=1)

输出

[(‘劈腿’, 0.5849199295043945)]

1.4 训练语料 (数据集)

本教程使用了酒店评论语料,训练样本分别被放置在两个文件夹里: 分别的pos和neg,每个文件夹里有2000个txt文件,每个文件内有一段评语,共有4000个训练样本,这样大小的样本数据在NLP中属于非常迷你的

# 获得样本的索引,样本存放于两个文件夹中,
# 分别为 正面评价'pos'文件夹 和 负面评价'neg'文件夹
# 每个文件夹中有2000个txt文件,每个文件中是一例评价,一个对一个
import os #读入读出通道
pos_txts = os.listdir('pos')
neg_txts = os.listdir('neg')
print( '样本总共: '+ str(len(pos_txts) + len(neg_txts)) )

样本总共: 4000

# 现在我们将所有的评价内容放置到一个list里
train_texts_orig = [] # 存储所有评价,每例评价为一条string,原始评论
# 添加完所有样本之后,train_texts_orig为一个含有4000条文本的list
# 其中前2000条文本为正面评价,后2000条为负面评价
#以下为读入.txt文件过程
for i in range(len(pos_txts)):with open('pos/'+pos_txts[i], 'r', errors='ignore') as f:text = f.read().strip()train_texts_orig.append(text)f.close()
for i in range(len(neg_txts)):with open('neg/'+neg_txts[i], 'r', errors='ignore') as f:text = f.read().strip()train_texts_orig.append(text)f.close()
len(train_texts_orig)

4000

# 我们使用tensorflow的keras接口来建模
from keras.models import Sequential
from keras.layers import Dense, GRU, Embedding, LSTM, Bidirectional#Dense全连接
#Bidirectional双向LSTM  callbacks用来调参
from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequences
from keras.optimizers import RMSprop
from keras.optimizers import Adam
from keras.callbacks import EarlyStopping, ModelCheckpoint, TensorBoard, ReduceLROnPlateau

1.5 分词和tokenize

首先我们去掉每个样本的标点符号,然后用jieba分词,jieba分词返回一个生成器,没法直接进行tokenize,所以我们将分词结果转换成一个list,并将它索引化,这样每一例评价的文本变成一段索引数字,对应着预训练词向量模型中的词。

# 进行分词和tokenize
# train_tokens是一个长长的list,其中含有4000个小list,对应每一条评价
train_tokens = []
for text in train_texts_orig:# 去掉标点text = re.sub("[\s+\.\!\/_,$%^*(+\"\']+|[+——!,。?、~@#¥%……&*()]+", "",text)# 结巴分词cut = jieba.cut(text)# 结巴分词的输出结果为一个生成器# 把生成器转换为listcut_list = [ i for i in cut ]for i, word in enumerate(cut_list):try:# 将词转换为索引indexcut_list[i] = cn_model.vocab[word].indexexcept KeyError:# 如果词不在字典中,则输出0cut_list[i] = 0train_tokens.append(cut_list)

1.6 索引长度标准化

因为每段评语的长度是不一样的,我们如果单纯取最长的一个评语,并把其他评填充成同样的长度,这样十分浪费计算资源,所以我们取一个折衷的长度。

# 获得所有tokens的长度
num_tokens = [ len(tokens) for tokens in train_tokens ]
num_tokens = np.array(num_tokens)
# 平均tokens的长度
np.mean(num_tokens)

71.4495

# 最长的评价tokens的长度
np.max(num_tokens)

1540

plt.hist(np.log(num_tokens), bins = 100)#有大有小取对数
plt.xlim((0,20))
plt.ylabel('number of tokens')
plt.xlabel('length of tokens')
plt.title('Distribution of tokens length')
plt.show()

# 取tokens平均值并加上两个tokens的标准差,
# 假设tokens长度的分布为正态分布,则max_tokens这个值可以涵盖95%左右的样本
max_tokens = np.mean(num_tokens) + 2 * np.std(num_tokens)
max_tokens = int(max_tokens)
max_tokens

236

# 取tokens的长度为236时,大约95%的样本被涵盖
# 我们对长度不足的进行padding,超长的进行修剪
np.sum( num_tokens < max_tokens ) / len(num_tokens)

0.9565

1.7 反向tokenize

为了之后来验证 我们定义一个function,用来把索引转换成可阅读的文本,这对于debug很重要。

# 用来将tokens转换为文本
def reverse_tokens(tokens):text = ''for i in tokens:if i != 0:text = text + cn_model.index2word[i]else:text = text + ' 'return text
reverse = reverse_tokens(train_tokens[0])
# 经过tokenize再恢复成文本
# 可见标点符号都没有了
reverse

‘早餐太差无论去多少人那边也不加食品的酒店应该重视一下这个问题了房间本身很好’

# 原始文本
train_texts_orig[0]

‘早餐太差,无论去多少人,那边也不加食品的。酒店应该重视一下这个问题了。\n\n房间本身很好。’

1.8 构建embedding matrix

现在我们来为模型准备embedding matrix(词向量矩阵),根据keras的要求,我们需要准备一个维度为(numwords, embeddingdim)的矩阵【num words代表我们使用的词汇的数量,emdedding dimension在我们现在使用的预训练词向量模型中是300,每一个词汇都用一个长度为300的向量表示】注意我们只选择使用前50k个使用频率最高的词,在这个预训练词向量模型中,一共有260万词汇量,如果全部使用在分类问题上会很浪费计算资源,因为我们的训练样本很小,一共只有4k,如果我们有100k,200k甚至更多的训练样本时,在分类问题上可以考虑减少使用的词汇量。

embedding_dim

300

# 只使用大库前50000个词
num_words = 50000
# 初始化embedding_matrix,之后在keras上进行应用
embedding_matrix = np.zeros((num_words, embedding_dim))
# embedding_matrix为一个 [num_words,embedding_dim] 的矩阵
# 维度为 50000 * 300
for i in range(num_words):embedding_matrix[i,:] = cn_model[cn_model.index2word[i]]
embedding_matrix = embedding_matrix.astype('float32')
# 检查index是否对应,
# 输出300意义为长度为300的embedding向量一一对应
np.sum( cn_model[cn_model.index2word[333]] == embedding_matrix[333] )

300

# embedding_matrix的维度,
# 这个维度为keras的要求,后续会在模型中用到
embedding_matrix.shape

(50000, 300)

1.9 padding(填充)和truncating(修剪)

我们把文本转换为tokens(索引)之后,每一串索引的长度并不相等,所以为了方便模型的训练我们需要把索引的长度标准化,上面我们选择了236这个可以涵盖95%训练样本的长度,接下来我们进行padding和truncating,我们一般采用’pre’的方法,这会在文本索引的前面填充0,因为根据一些研究资料中的实践,如果在文本索引后面填充0的话,会对模型造成一些不良影响。

# 进行padding和truncating, 输入的train_tokens是一个list
# 返回的train_pad是一个numpy array
train_pad = pad_sequences(train_tokens, maxlen=max_tokens,padding='pre', truncating='pre')
# 超出五万个词向量的词用0代替
train_pad[ train_pad>=num_words ] = 0
# 可见padding之后前面的tokens全变成0,文本在最后面
train_pad[33]

array([ 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
290, 3053, 57, 169, 73, 1, 25, 11216, 49,
163, 15985, 0, 0, 30, 8, 0, 1, 228,
223, 40, 35, 653, 0, 5, 1642, 29, 11216,
2751, 500, 98, 30, 3159, 2225, 2146, 371, 6285,
169, 27396, 1, 1191, 5432, 1080, 20055, 57, 562,
1, 22671, 40, 35, 169, 2567, 0, 42665, 7761,
110, 0, 0, 41281, 0, 110, 0, 35891, 110,
0, 28781, 57, 169, 1419, 1, 11670, 0, 19470,
1, 0, 0, 169, 35071, 40, 562, 35, 12398,
657, 4857])

# 准备target向量,前2000样本为1,后2000为0
train_target = np.concatenate( (np.ones(2000),np.zeros(2000)) )
# 进行训练和测试样本的分割
from sklearn.model_selection import train_test_split
train_target.shape
train_pad.shape

(4000, 236)

# 90%的样本用来训练,剩余10%用来测试
#因为前2000个文件夹都是neg一类,所以打乱顺序来训练 random_state
X_train, X_test, y_train, y_test = train_test_split(train_pad,train_target,test_size=0.1,random_state=12)
# 查看训练样本,确认无误
print(reverse_tokens(X_train[35]))
print('class: ',y_train[35])

房间很大还有海景阳台走出酒店就是沙滩非常不错唯一遗憾的就是不能刷 不方便
class: 1.0

1.10 用keras搭建LSTM模型

模型的第一层是Embedding层,只有当我们把tokens索引转换为词向量矩阵之后,才可以用神经网络对文本进行处理。keras提供了Embedding接口,避免了繁琐的稀疏矩阵操作。在Embedding层我们输入的矩阵为:(batchsize, maxtokens),输出矩阵为:(batchsize, maxtokens, embeddingdim)

# 用LSTM对样本进行分类
model = Sequential()
# 模型第一层为embedding,trainable=False因为embedding_matrix下载后已经训练好了
model.add(Embedding(num_words,embedding_dim,weights=[embedding_matrix],input_length=max_tokens,trainable=False))
model.add(Bidirectional(LSTM(units=32, return_sequences=True)))#双向LSTM考虑前后词
model.add(LSTM(units=16, return_sequences=False))#units=16神经元个数

GRU:如果使用GRU,测试样本可以达到87%的准确率,但我测试自己的文本内容时发现,GRU最后一层激活函数的输出都在0.5左右,说明模型的判断不是很明确,信心比较低,而且经过测试发现模型对于否定句的判断有时会失误,我们期望对于负面样本输出接近0,正面样本接近1而不是都徘徊于0.5之间。
BILSTM:测试了LSTM和BiLSTM,发现BiLSTM的表现最好,LSTM的表现略好于GRU,因为BiLSTM对于比较长的句子结构有更好的记忆。Embedding之后第,一层我们用BiLSTM返回sequences,然后第二层16个单元的LSTM不返回sequences,只返回最终结果,最后是一个全链接层,用sigmoid激活函数输出结果

# GRU的代码
# model.add(GRU(units=32, return_sequences=True))
# model.add(GRU(units=16, return_sequences=True))
# model.add(GRU(units=4, return_sequences=False))
#加入全连接层
model.add(Dense(1, activation='sigmoid'))
# 我们使用adam以0.001的learning rate进行优化
optimizer = Adam(lr=1e-3)
model.compile(loss='binary_crossentropy',optimizer=optimizer,  metrics=['accuracy'])
# 我们来看一下模型的结构,一共90k左右可训练的变量,None表示batchsize,一个batch有236词输入
#15000000为50000*300,因为train=false,所以不训练这些参数
#17=16*1+1(bias为一参数)
model.summary()

# 建立一个权重的存储点,verbose=1可以是打印信息更加详细,方面查找问题
path_checkpoint = 'sentiment_checkpoint.keras'
checkpoint = ModelCheckpoint(filepath=path_checkpoint, monitor='val_loss',verbose=1, save_weights_only=True,save_best_only=True)
# 尝试加载已训练模型
try:model.load_weights(path_checkpoint)
except Exception as e:print(e)
# 定义early stoping如果3个epoch内validation loss没有改善则停止训练
earlystopping = EarlyStopping(monitor='val_loss', patience=3, verbose=1)
# 自动降低learning rate
lr_reduction = ReduceLROnPlateau(monitor='val_loss',factor=0.1, min_lr=1e-5, patience=0,verbose=1)
# 定义callback函数
callbacks = [earlystopping, checkpoint,lr_reduction
]
# 开始训练,4000*0.1=400为test,validation_split=0.1为3600*0.1
model.fit(X_train, y_train,validation_split=0.1, epochs=4,batch_size=128,callbacks=callbacks)

1.11 结论

我们定义一个预测函数(将输入文本按模型要求处理再输入),来预测输入的文本的极性,可见模型对于否定句和一些简单的逻辑结构都可以进行准确的判断。

result = model.evaluate(X_test, y_test)
print('Accuracy:{0:.2%}'.format(result[1]))
def predict_sentiment(text):print(text)# 去标点text = re.sub("[\s+\.\!\/_,$%^*(+\"\']+|[+——!,。?、~@#¥%……&*()]+", "",text)# 分词cut = jieba.cut(text)cut_list = [ i for i in cut ]# tokenizefor i, word in enumerate(cut_list):try:cut_list[i] = cn_model.vocab[word].indexexcept KeyError:cut_list[i] = 0# paddingtokens_pad = pad_sequences([cut_list], maxlen=max_tokens,padding='pre', truncating='pre')# 预测result = model.predict(x=tokens_pad)coef = result[0][0]if coef >= 0.5:print('是一例正面评价','output=%.2f'%coef)else:print('是一例负面评价','output=%.2f'%coef)
test_list = ['酒店设施不是新的,服务态度不好','房间很凉爽,空调冷气很足','酒店环境不好,住宿体验很不好','房间隔音不到位' ,'晚上回来发现没有打扫卫生'
]
for text in test_list:predict_sentiment(text)

酒店设施不是新的,服务态度不好
是一例负面评价 output=0.01
房间很凉爽,空调冷气很足
是一例正面评价 output=0.94
酒店环境不好,住宿体验很不好
是一例负面评价 output=0.01
房间隔音不到位
是一例负面评价 output=0.02
晚上回来发现没有打扫卫生
是一例负面评价 output=0.40

1.12 错误分类

y_pred = model.predict(X_test)
y_pred = y_pred.T[0]
y_pred = [1 if p>= 0.5 else 0 for p in y_pred]
y_pred = np.array(y_pred)
y_actual = np.array(y_test)
# 找出错误分类的索引
misclassified = np.where( y_pred != y_actual )[0]
# 输出所有错误分类的索引,在test400条中有48条分错
len(misclassified)
print(len(misclassified))

55

# 我们来找出错误分类的样本看看,misclassified[1]打出第二条分错的
idx=misclassified[1]
print(reverse_tokens(X_test[idx]))
print('预测的分类', y_pred[idx])
print('实际的分类', y_actual[idx])

地理位置还不错到哪里都比较方便但是服务不象是 集团管理的比较差下午睡了 并洗了一个澡本来想让酒店再来打扫一下所以打开了请打扫的服务灯可是到晚上回酒店发现打扫得服务灯被关掉了而房间还是没有打扫过
预测的分类 1
实际的分类 0.0


2.新浪新闻分类(tensorflow+cnn)

数据集采用清华数据集作者本人截取了一部分,数据集百度云下载链接:
链接:https://pan.baidu.com/s/1-Rm9PU7ekU3zx_7gTB9ibw 提取码:3d3o

#以下为获取文件
import os
def getFilePathList(rootDir):filePath_list = []for walk in os.walk(rootDir):part_filePath_list = [os.path.join(walk[0], file) for file in walk[2]]filePath_list.extend(part_filePath_list)return filePath_list
filePath_list = getFilePathList('part_cnews')
len(filePath_list)#一共有1400个.txt文件组成一个list

运行结果:

filePath_list[5]

运行结果:

#所有样本标签的值汇总成一个列表,赋给标签列表label_list
#windows和Linux系统路径字符串的间隔符有区别
label_list = []
for filePath in filePath_list:label = filePath.split('\\')[1]label_list.append(label)
len(label_list)

运行结果:

#标签统计计数
import pandas as pd
pd.value_counts(label_list)

运行结果:

#用pickle库保存label_list
import pickle
with open('label_list.pickle','wb') as file:pickle.dump(label_list,file)
#获取所有样本内容,保存content_list
#pickle.dump可以将python中对象持久化为二进制文件,二进制文件的加载速度非常快。避免内存溢出,每读取一定数量的文件就利用pickle库的dump方法保存
import time
import pickle
import redef getFile(filePath):with open(filePath, encoding='utf8') as file:fileStr = ''.join(file.readlines(1000))return fileStrinterval = 100
n_samples = len(label_list)
startTime = time.time()
directory_name = 'content_list'
if not os.path.isdir(directory_name):os.mkdir(directory_name)
for i in range(0, n_samples, interval):startIndex = iendIndex = i + intervalcontent_list = []print('%06d-%06d start' %(startIndex, endIndex))for filePath in filePath_list[startIndex:endIndex]:fileStr = getFile(filePath)content = re.sub('\s+', ' ', fileStr)content_list.append(content)save_fileName = directory_name + '/%06d-%06d.pickle' %(startIndex, endIndex)with open(save_fileName, 'wb') as file:pickle.dump(content_list, file)used_time = time.time() - startTimeprint('%06d-%06d used time: %.2f seconds' %(startIndex, endIndex, used_time))

运行结果:

#前面为获取数据,拿到原始数据进行处理过程,【content_list文件夹、label_list文件、代码文件】这三者处于相同路径
#加载数据
import time
import pickle
import osdef getFilePathList(rootDir):filePath_list = []for walk in os.walk(rootDir):part_filePath_list = [os.path.join(walk[0], file) for file in walk[2]]filePath_list.extend(part_filePath_list)return filePath_liststartTime = time.time()
contentListPath_list = getFilePathList('content_list')
content_list = []
for filePath in contentListPath_list:with open(filePath, 'rb') as file:part_content_list = pickle.load(file)content_list.extend(part_content_list)
with open('label_list.pickle', 'rb') as file:label_list = pickle.load(file)
used_time = time.time() - startTime
print('used time: %.2f seconds' %used_time)
sample_size = len(content_list)
print('length of content_list,mean sample size: %d' %sample_size)

运行结果:

len(content_list)

运行结果:

#制作词汇表:内容列表content_list中的元素是每篇文章内容,数据类型为字符串。
#对所有文章内容中的字做统计计数,出现次数排名前5000的字赋值给变量vocabulary_list。
from collections import Counter
def getVocabularyList(content_list, vocabulary_size):allContent_str = ''.join(content_list)counter = Counter(allContent_str)vocabulary_list = [k[0] for k in counter.most_common(vocabulary_size)]return ['PAD'] + vocabulary_list
startTime = time.time()
vocabulary_list = getVocabularyList(content_list, 5000)
used_time = time.time() - startTime
print('used time: %.2f seconds' %used_time)

运行结果:

#保存词汇表
import pickle with open('vocabulary_list.pickle', 'wb') as file:pickle.dump(vocabulary_list, file)
#加载词汇表:完成制作词汇表后,将其保存。之后再运行代码则直接加载保存的词汇表,节省了复制作词汇表花费的时间。
import picklewith open('vocabulary_list.pickle', 'rb') as file:vocabulary_list = pickle.load(file)
#数据准备 #在代码块中按Esc键,进入命令模式,代码块左边的竖线会显示蓝色。在命令模式下,点击L键,会显示代码行数
import time
startTime = time.time()#记录本段代码运行开始时间,赋值给变量startTime
from sklearn.model_selection import train_test_split
train_X, test_X, train_y, test_y = train_test_split(content_list, label_list)
train_content_list = train_X
train_label_list = train_y
test_content_list = test_X
test_label_list = test_y
used_time = time.time() - startTime #打印提示信息,表示程序运行至此步花费时间
print('train_test_split used time : %.2f seconds' %used_time)vocabulary_size = 10000  # 词汇表达小
sequence_length = 600  # 序列长度
embedding_size = 64  # 词向量维度
num_filters = 256  # 卷积核数目
filter_size = 5  # 卷积核尺寸
num_fc_units = 128  # 全连接层神经元
dropout_keep_probability = 0.5  # dropout保留比例
learning_rate = 1e-3  # 学习率
batch_size = 64  # 每批训练大小word2id_dict = dict([(b, a) for a, b in enumerate(vocabulary_list)])#使用列表推导式得到词汇及其id对应的列表,并调用dict方法将列表强制转换为字典
content2idList = lambda content : [word2id_dict[word] for word in content if word in word2id_dict]#使用列表推导式和匿名函数定义函数content2idlist,函数作用是将文章中的每个字转换为id
train_idlist_list = [content2idList(content) for content in train_content_list]#使用列表推导式得到的结果是列表的列表,总列表train_idlist_list中的元素是每篇文章中的字对应的id列表
used_time = time.time() - startTime#代码打印提示信息,表示程序运行至此步花费时间
print('content2idList used time : %.2f seconds' %used_time)import numpy as np
num_classes = np.unique(label_list).shape[0]#获取标签的类别数量,例如本文类别数量为14,即变量num_classes的值为14
#以下6行为获得能够用于模型训练的特征矩阵和预测目标值
import tensorflow.contrib.keras as kr
train_X = kr.preprocessing.sequence.pad_sequences(train_idlist_list, sequence_length)#将每个样本统一长度为seq_length,即600上面超参数已设定600
from sklearn.preprocessing import LabelEncoder#导入sklearn.preprocessing库的labelEncoder方法
labelEncoder = LabelEncoder()#实例化LabelEncoder对象
train_y = labelEncoder.fit_transform(train_label_list)#调用LabelEncoder对象的fit_transform方法做标签编码
train_Y = kr.utils.to_categorical(train_y, num_classes)#调用keras.untils库的to_categorical方法将标签编码的结果再做Ont-Hot编码import tensorflow as tf
tf.reset_default_graph()#重置tensorflow图,加强代码的健壮性
X_holder = tf.placeholder(tf.int32, [None, sequence_length])
Y_holder = tf.placeholder(tf.float32, [None, num_classes])
used_time = time.time() - startTime
print('data preparation used time : %.2f seconds' %used_time)

运行结果:

list(word2id_dict.items())[:20]

运行结果:

#搭建神经网络
embedding = tf.get_variable('embedding', [vocabulary_size, embedding_size])#调用tf库的get_variable方法实例化可以更新的模型参数embedding,矩阵形状为vocabulary_size*embedding_size,即10000*64
embedding_inputs = tf.nn.embedding_lookup(embedding,#将输入数据做词嵌入,得到新变量embedding_inputs的形状为batch_size*sequence_length*embedding_size,即64*600*64X_holder)
conv = tf.layers.conv1d(embedding_inputs,  #调用tf.layers.conv1d方法,方法需要3个参数,第1个参数是输入数据,第2个参数是卷积核数量num_filters,第3个参数是卷积核大小filter_sizenum_filters,       #方法结果赋值给变量conv,形状为batch_size*596*num_filters,596是600-5+1的结果filter_size)
max_pooling = tf.reduce_max(conv,  #对变量conv的第1个维度做求最大值操作。方法结果赋值给变量max_pooling,形状为batch_size*num_filters,即64*256[1])
full_connect = tf.layers.dense(max_pooling, #添加全连接层,tf.layers.dense方法结果赋值给变量full_connect,形状为batch_size*num_fc_units,即64*128num_fc_units)
full_connect_dropout = tf.contrib.layers.dropout(full_connect, #代码调用tf.contrib.layers.dropout方法,方法需要2个参数,第1个参数是输入数据,第2个参数是保留比例keep_prob=dropout_keep_probability)
full_connect_activate = tf.nn.relu(full_connect_dropout) #激活函数
softmax_before = tf.layers.dense(full_connect_activate, #添加全连接层,tf.layers.dense方法结果赋值给变量softmax_before,形状为batch_size*num_classes,即64*14num_classes)
predict_Y = tf.nn.softmax(softmax_before)  #tf.nn.softmax方法,方法结果是预测概率值
cross_entropy = tf.nn.softmax_cross_entropy_with_logits_v2(labels=Y_holder,logits=softmax_before)
loss = tf.reduce_mean(cross_entropy)
optimizer = tf.train.AdamOptimizer(learning_rate)
train = optimizer.minimize(loss)
isCorrect = tf.equal(tf.argmax(Y_holder, 1), tf.argmax(predict_Y, 1))
accuracy = tf.reduce_mean(tf.cast(isCorrect, tf.float32))
#参数初始化,对于神经网络模型,重要是其中的参数。开始神经网络模型训练之前,需要做参数初始化
init = tf.global_variables_initializer()
session = tf.Session()
session.run(init)
#模型训练
test_idlist_list = [content2idList(content) for content in test_content_list] #获取测试集中的数据
test_X = kr.preprocessing.sequence.pad_sequences(test_idlist_list, sequence_length)
test_y = labelEncoder.transform(test_label_list)
test_Y = kr.utils.to_categorical(test_y, num_classes)
import random
for i in range(100): #模型迭代训练100次selected_index = random.sample(list(range(len(train_y))), k=batch_size)#从训练集中选取batch_size大小,即64个样本做批量梯度下降batch_X = train_X[selected_index]batch_Y = train_Y[selected_index]session.run(train, {X_holder:batch_X, Y_holder:batch_Y}) #每运行1次,表示模型训练1次step = i + 1 if step % 10 == 0:#每间隔10步打印selected_index = random.sample(list(range(len(test_y))), k=100)#从测试集中随机选取100个样本batch_X = test_X[selected_index]batch_Y = test_Y[selected_index]loss_value, accuracy_value = session.run([loss, accuracy], {X_holder:batch_X, Y_holder:batch_Y})print('step:%d loss:%.4f accuracy:%.4f' %(step, loss_value, accuracy_value))

运行结果:

import warnings
warnings.filterwarnings("ignore")
def predict(input_content):#idList的数据类型必须是列表list,#否则调用kr.preprocessing.sequence.pad_sequences方法会报错idList = [content2idList(input_content)]X = kr.preprocessing.sequence.pad_sequences(idList, sequence_length)Y = session.run(predict_Y, {X_holder:X})y = np.argmax(Y, axis=1)label = labelEncoder.inverse_transform(y)[0]return labelselected_index = random.sample(range(len(test_content_list)), k=1)[0]
selected_sample = test_content_list[selected_index]
true_label = test_label_list[selected_index]
predict_label = predict(selected_sample)print('selected_sample :', selected_sample)
print('true_label :', true_label)
print('predict_label :', predict_label, '\n')
print('predict whatever you want, for example:')
input_content = "足球裁判打人"
print('predict("%s") :' %input_content, predict(input_content))

运行结果:

#混淆矩阵
import numpy as np
import pandas as pd
from sklearn.metrics import confusion_matrixdef predictAll(test_X, batch_size=100):predict_value_list = []for i in range(0, len(test_X), batch_size):selected_X = test_X[i: i + batch_size]predict_value = session.run(predict_Y, {X_holder:selected_X})predict_value_list.extend(predict_value)return np.array(predict_value_list)Y = predictAll(test_X)
y = np.argmax(Y, axis=1)
predict_label_list = labelEncoder.inverse_transform(y)
pd.DataFrame(confusion_matrix(test_label_list, predict_label_list), columns=labelEncoder.classes_,index=labelEncoder.classes_ )

运行结果:

#报告表
import numpy as np
from sklearn.metrics import precision_recall_fscore_supportdef eval_model(y_true, y_pred, labels):# 计算每个分类的Precision, Recall, f1, supportp, r, f1, s = precision_recall_fscore_support(y_true, y_pred)# 计算总体的平均Precision, Recall, f1, supporttot_p = np.average(p, weights=s)tot_r = np.average(r, weights=s)tot_f1 = np.average(f1, weights=s)tot_s = np.sum(s)res1 = pd.DataFrame({u'Label': labels,u'Precision': p,u'Recall': r,u'F1': f1,u'Support': s})res2 = pd.DataFrame({u'Label': ['总体'],u'Precision': [tot_p],u'Recall': [tot_r],u'F1': [tot_f1],u'Support': [tot_s]})res2.index = [999]res = pd.concat([res1, res2])return res[['Label', 'Precision', 'Recall', 'F1', 'Support']]eval_model(test_label_list, predict_label_list, labelEncoder.classes_)

运行结果:


3.搜狐新闻文本分类(word2vec)

在桌面新建基于word2vec文本分类文件夹,文件夹中输入cmd:


新建一个word2vec_test.ipynb:

rename为:word2vec_test

此时文件夹多了以下两个文件:

3.1 数据的准备

训练集共有24000条样本,12个分类,每个分类2000条样本。
测试集共有12000条样本,12个分类,每个分类1000条样本。
链接:https://pan.baidu.com/s/1UbPjMpcp3kqvdd0HMAgMfQ 提取码:b53e
下图红色框里为压缩包解压的文件:

加载训练集到变量train_df中,并打印训练集前5行,代码如下:

#训练集数据共有24000条,测试集数据共有12000条
import pandas as pd
#加载训练集到变量train_df中,并打印训练集前5行,代码如下。
#read_csv方法中有3个参数,第1个参数是加载文本文件的路径,第2个关键字参数sep是分隔符,第3个关键字参数header是文本文件的第1行是否为字段名。
train_df = pd.read_csv('sohu_train.txt', sep='\t', header=None)
train_df.head()

#查看训练集每个分类的名字以及样本数量
for name, group in train_df.groupby(0):print(name,len(group))

#加载测试集并查看每个分类的名字以及样本数量
test_df = pd.read_csv('sohu_test.txt', sep='\t', header=None)
for name, group in test_df.groupby(0):print(name, len(group))

#对训练集的24000条样本循环遍历,使用jieba库的cut方法获得分词列表赋值给变量cutWords
#判断分词是否为停顿词,如果不为停顿词,则添加进变量cutWords中
import jieba
import timetrain_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 += 1if i % 1000 == 0:print('前%d篇文章分词共花费%.2f秒' %(i, time.time()-startTime))cutWords_list.append(cutWords)

运行结果:

#将分词结果保存为本地文件cutWords_list.txt
with open('cutWords_list.txt', 'w') as file: for cutWords in cutWords_list:file.write(' '.join(cutWords) + '\n')

作者提供已经分词完成的文本文件链接:https://pan.baidu.com/s/1oKjLZjSkqE0LfLEvLxkBNw 提取码:oh3u

  #载入分词文件
with open('cutWords_list.txt') as file:cutWords_list = [k.split() for k in file.readlines()]

3.2 word2vec模型

完成此步骤需要先安装gensim库,安装命令:pip install gensim

#调用gensim.models.word2vec库中的LineSentence方法实例化模型对象
from gensim.models import Word2Vecword2vec_model = Word2Vec(cutWords_list, size=100, iter=10, min_count=20)
   #调用模型对象的方法时,一直提示警告信息,避免出现警告信息
import warningswarnings.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('摄影')

运行结果:

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

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

运行结果:

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

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

word2vec_model.save('word2vec_model.w2v')

3.3 特征工程:

#对于每一篇文章,获取文章的每一个分词在word2vec模型的相关性向量
#然后把一篇文章的所有分词在word2vec模型中的相关性向量求和取平均数,即此篇文章在word2vec模型中的相关性向量
#实例化Word2Vec对象时,关键字参数size定义为100,则相关性矩阵都为100维
#定义getVector函数获取每个文章的词向量,传入2个参数,第1个参数是文章分词的结果,第2个参数是word2vec模型对象
#变量vector_list是通过列表推导式得出单篇文章所有分词的词向量,通过np.array方法转成ndarray对象再对每一列求平均值
#第1种方法,用for循环常规计算
import numpy as np
import time def getVector_v1(cutWords, word2vec_model):count = 0article_vector = np.zeros(word2vec_model.layer1_size)for cutWord in cutWords:if cutWord in word2vec_model:article_vector += word2vec_model[cutWord]count += 1return article_vector / countstartTime = time.time()
vector_list = []
i = 0
for cutWords in cutWords_list[:5000]:i += 1if i % 1000 ==0:print('前%d篇文章形成词向量花费%.2f秒' %(i, time.time()-startTime))vector_list.append(getVector_v1(cutWords, word2vec_model))
X = np.array(vector_list)
#第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).valuesreturn cutWord_vectorstartTime = time.time()
vector_list = []
i = 0
for cutWords in cutWords_list[:5000]:i += 1if i % 1000 ==0:print('前%d篇文章形成词向量花费%.2f秒' %(i, time.time()-startTime))vector_list.append(getVector_v2(cutWords, word2vec_model))
X = np.array(vector_list)
#第3种方法,用numpy的mean方法计算
import time
import numpy as npdef 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_vectorstartTime = time.time()
vector_list = []
i = 0
for cutWords in cutWords_list:i += 1if i % 1000 ==0:print('前%d篇文章形成词向量花费%.2f秒' %(i, time.time()-startTime))vector_list.append(getVector_v3(cutWords, word2vec_model))
X = np.array(vector_list)

运行结果:

#第4种方法,用numpy的add、divide方法计算
''''import time
import numpy as npdef getVector_v4(cutWords, word2vec_model):i = 0index2word_set = set(word2vec_model.wv.index2word)article_vector = np.zeros((word2vec_model.layer1_size))for cutWord in cutWords:if cutWord in index2word_set:article_vector = np.add(article_vector, word2vec_model.wv[cutWord])i += 1cutWord_vector = np.divide(article_vector, i)return cutWord_vectorstartTime = time.time()
vector_list = []
i = 0
for cutWords in cutWords_list[:5000]:i += 1if i % 1000 ==0:print('前%d篇文章形成词向量花费%.2f秒' %(i, time.time()-startTime))vector_list.append(getVector_v4(cutWords, word2vec_model))
X = np.array(vector_list)
#因为形成特征矩阵的花费时间较长,为了避免以后重复花费时间,把特征矩阵保存为文件。
#使用ndarray对象的dump方法,需要1个参数,数据类型为字符串,为保存文件的文件名
X.dump('articles_vector.txt')
#加载此文件中的内容赋值给变量X
X = np.load('articles_vector.txt')

3.4 模型训练,模型评估

标签编码:

#标签编码
#调用sklearn.preprocessing库的LabelEncoder方法对文章分类做标签编码
from sklearn.preprocessing import LabelEncoder
import pandas as pdtrain_df = pd.read_csv('sohu_train.txt', sep='\t', header=None)
train_df.columns = ['分类', '文章']
labelEncoder = LabelEncoder()
y = labelEncoder.fit_transform(train_df['分类'])

逻辑回归模型

#调用sklearn.linear_model库的LogisticRegression方法实例化模型对象。
#调用sklearn.model_selection库的train_test_split方法划分训练集和测试集
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_splittrain_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.789375

保存模型

#调用sklearn.externals库中的joblib方法保存模型为logistic.model文件
from sklearn.externals import joblibjoblib.dump(logistic_model, 'logistic.model')
#加载模型
from sklearn.externals import jobliblogistic_model = joblib.load('logistic.model')

交叉验证

#交叉验证的结果更具有说服力。
#调用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_scorecv_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.79104167 0.77375 0.78875 0.77979167 0.78958333]
0.7845833333333333

模型测试

#模型测试
#调用sklearn.externals库的joblib对象的load方法加载模型赋值给变量logistic_model。
#调用pandas库read_csv方法读取测试集数据。
#调用DataFrame对象的groupby方法对每个分类分组,从而每种文章类别的分类准确性。
#调用自定义的getVector方法将文章转换为相关性向量。
#自定义getVectorMatrix方法获得测试集的特征矩阵。
#调用labelEncoder对象的transform方法将预测标签做标签编码,从而获得预测目标值import pandas as pd
import numpy as np
from sklearn.externals import joblib
import jieba def getVectorMatrix(article_series):return np.array([getVector_v3(jieba.cut(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.968
健康 0.814
女人 0.772
娱乐 0.765
房地产 0.879
教育 0.886
文化 0.563
新闻 0.575
旅游 0.82
汽车 0.934
科技 0.817
财经 0.7

3.5 总结

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


4.搜狐新闻文本分类(TfidfVectorizer)

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

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

运行结果:

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

运行结果:

 with open('stopwords.txt', encoding='utf8') as file:stopWord_list = [k.strip() for k in file.readlines()]

运行结果:

 with open('stopwords.txt', encoding='utf8') as file:stopWord_list = [k.strip() for k in file.readlines()]
import jieba
import timetrain_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 += 1if i % 1000 == 0:print('前%d篇文章分词共花费%.2f秒' %(i, time.time()-startTime))cutWords_list.append(cutWords)

with open('cutWords_list.txt', 'w') as file: for cutWords in cutWords_list:file.write(' '.join(cutWords) + '\n')
with open('cutWords_list.txt') as file:cutWords_list = [k.split() for k in file.readlines()]
#特征工程
X = tfidf.fit_transform(train_df[1])
print('词表大小:', len(tfidf.vocabulary_))
print(X.shape)

运行结果:

#调用sklearn.preprocessing库的LabelEncoder方法对文章分类做标签编码。
#最后一行代码查看预测目标的形状。
from sklearn.preprocessing import LabelEncoder
import pandas as pdtrain_df = pd.read_csv('sohu_train.txt', sep='\t', header=None)
labelEncoder = LabelEncoder()
y = labelEncoder.fit_transform(train_df[0])
y.shape

运行结果:

#调用sklearn.linear_model库的LogisticRegression方法实例化模型对象。
#调用sklearn.model_selection库的train_test_split方法划分训练集和测试集。
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_splittrain_X, test_X, train_y, test_y = train_test_split(X, y, test_size=0.2)
logistic_model = LogisticRegression(multi_class='multinomial', solver='lbfgs')
logistic_model.fit(train_X, train_y)
logistic_model.score(test_X, test_y)

运行结果:

#保存模型需要先安装pickle库,安装命令:pip install pickle
#调用pickle库的dump方法保存模型,需要2个参数。
#第1个参数是保存的对象,可以为任意数据类型,因为有3个模型需要保存,所以下面代码第1个参数是字典。
#第2个参数是保存的文件对象,数据类型为_io.BufferedWriter
import picklewith open('tfidf.model', 'wb') as file:save = {'labelEncoder' : labelEncoder,'tfidfVectorizer' : tfidf,'logistic_model' : logistic_model}pickle.dump(save, file)
#调用pickle库的load方法加载保存的模型对象
import picklewith open('tfidf.model', 'rb') as file:tfidf_model = pickle.load(file)tfidfVectorizer = tfidf_model['tfidfVectorizer']labelEncoder = tfidf_model['labelEncoder']logistic_model = tfidf_model['logistic_model']
#调用pandas的read_csv方法加载训练集数据。
#调用TfidfVectorizer对象的transform方法获得特征矩阵。
#调用LabelEncoder对象的transform方法获得预测目标值。
import pandas as pdtrain_df = pd.read_csv('sohu_train.txt', sep='\t', header=None)
X = tfidfVectorizer.transform(train_df[1])
y = labelEncoder.transform(train_df[0])
#调用sklearn.linear_model库的LogisticRegression方法实例化逻辑回归模型对象。
#调用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_scorelogistic_model = LogisticRegression(multi_class='multinomial', solver='lbfgs')
cv_split = ShuffleSplit(n_splits=5, test_size=0.3)
score_ndarray = cross_val_score(logistic_model, X, y, cv=cv_split)
print(score_ndarray)
print(score_ndarray.mean())

运行结果:

#绘制混淆矩阵
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegressionCV
from sklearn.metrics import confusion_matrix
import pandas as pdtrain_X, test_X, train_y, test_y = train_test_split(X, y, test_size=0.2)
logistic_model = LogisticRegressionCV(multi_class='multinomial', solver='lbfgs')
logistic_model.fit(train_X, train_y)
predict_y = logistic_model.predict(test_X)
pd.DataFrame(confusion_matrix(test_y, predict_y), columns=labelEncoder.classes_, index=labelEncoder.classes_)

运行结果:

#绘制precision、recall、f1-score、support报告表
import numpy as np
from sklearn.metrics import precision_recall_fscore_supportdef eval_model(y_true, y_pred, labels):# 计算每个分类的Precision, Recall, f1, supportp, r, f1, s = precision_recall_fscore_support(y_true, y_pred)# 计算总体的平均Precision, Recall, f1, supporttot_p = np.average(p, weights=s)tot_r = np.average(r, weights=s)tot_f1 = np.average(f1, weights=s)tot_s = np.sum(s)res1 = pd.DataFrame({u'Label': labels,u'Precision': p,u'Recall': r,u'F1': f1,u'Support': s})res2 = pd.DataFrame({u'Label': ['总体'],u'Precision': [tot_p],u'Recall': [tot_r],u'F1': [tot_f1],u'Support': [tot_s]})res2.index = [999]res = pd.concat([res1, res2])return res[['Label', 'Precision', 'Recall', 'F1', 'Support']]predict_y = logistic_model.predict(test_X)
eval_model(test_y, predict_y, labelEncoder.classes_)

运行结果:

#模型测试,即对一个全新的测试集进行预测。
#调用pandas库的read_csv方法读取测试集文件。
#调用TfidfVectorizer对象的transform方法获得特征矩阵。
#调用LabelEncoder对象的transform方法获得预测目标值
import pandas as pdtest_df = pd.read_csv('sohu_test.txt', sep='\t', header=None)
test_X = tfidfVectorizer.transform(test_df[1])
test_y = labelEncoder.transform(test_df[0])
predict_y = logistic_model.predict(test_X)
eval_model(test_y, predict_y, labelEncoder.classes_)

运行结果:

5.中文纠错代码解析(pycorrector)

5.1 win10上安装pycorrector

https://github.com/shibing624/pycorrector
1.pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pycorrector出现No module named ‘pypinyin’
2.pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pypinyin出现No module named ‘kenlm’
3.pip install https://github.com/kpu/kenlm/archive/master.zip出现少了Microsoft Visual C++
4.Microsoft Visual C++ 链接:https://pan.baidu.com/s/1toZQAaJXa3xnflhjDMx6lg 提取码:ky7w 。安装完后继续第3步,第1步,再pip install jieba

5.2 unbuntu上训练语言模型:

wget -O - http://kheafield.com/code/kenlm.tar.gz |tar xz
cd kenlm
mkdir -p build
cd build
cmake ..
make -j 4
cmake未安装问题:sudu apt install cmake
boost问题:sudo apt-get install libboost-all-dev
Eigen3的问题:如下图:

build/bin/lmplz -o 3 --verbose_header --text people2014corpus_words.txt --arpa result/people2014corpus_words.arps

build/bin/build_binary ./result/people2014corpus_words.arps ./result/people2014corpus_words.klm

如果提示lmplz不存在,build_binary不存在,则需要设置环境变量:将kenlm文件夹加入路径:gedit .profilesource .profile

5.3 use kenlm

kenlm打分

pycorrector里有一文件包含了很多字,把每个字挨个送进编辑距离产生的空格然后用语言模型打分,困惑度最低的就是正确的。

import kenlm
lm = kenlm.Model('C:/Users/1/Anaconda3/Lib/site-packages/pycorrector/data/kenlm/people_chars_lm.klm')
print(lm.score('银行', bos = True, eos = True)) # begain end

chars = ['中国工商银行','往来账业务']
print(lm.score(' '.join(chars), bos = True, eos = True))

' '.join(chars)  #以空格为分隔符(delimiter)


lm.perplexity('中国工商银行')

分词

分词方法主要基于词典匹配(正向最大匹配法、逆向最大匹配法和双向匹配分词法等)和基于统计(HMM、CRF、和深度学习);主流分词工具库包括中科院计算所NLPIR、哈工大LTP、清华大学THULAC、Hanlp分词器、Python jieba工具库等。更多的分词方法和工具库参考知乎:https://www.zhihu.com/question/19578687

s="我在课堂学习自然语言1000处理"#不能1=
b=jieba.cut(s)
print("/ ".join(b))

我/ 在/ 课堂/ 学习/ 自然语言/ 1000/ 处理

b=jieba.cut(s)
print(b)

<generator object Tokenizer.cut at 0x000001DDD9CFB728>

b=jieba.lcut(s) #l为list
print(b)

[‘我’, ‘在’, ‘课堂’, ‘学习’, ‘自然语言’, ‘1000’, ‘处理’]

b= jieba.cut(s, cut_all=True)
print("Full Mode: " + "/ ".join(b))  # 全模式

Full Mode: 我/ 在/ 课堂/ 学习/ 自然/ 自然语言/ 语言/ 1000/ 处理

jieba.cut 方法接受三个参数:
•需要分词的字符串
•cut_all 参数用来控制是否采用全模式
•HMM 参数用来控制是否使用 HMM 模型

jieba.cut_for_search 方法接受两个参数:用于搜索引擎构建倒排索引的分词,粒度比较细
•需要分词的字符串
•是否使用 HMM 模型。

import jiebaseg_list = jieba.cut("我在课堂学习自然语言1000处理", cut_all=True)
print("Full Mode: " + "/ ".join(seg_list))  # 全模式seg_list = jieba.cut("我在课堂学习自然语言处理", cut_all=False)
print("Default Mode: " + "/ ".join(seg_list))  # 精确模式seg_list = jieba.cut("他毕业于北京航空航天大学,在百度深度学习研究院进行研究")  # 默认是精确模式
print(", ".join(seg_list))seg_list = jieba.cut_for_search("小明硕士毕业于中国科学院计算所,后在斯坦福大学深造")  # 搜索引擎模式
print(", ".join(seg_list))

Full Mode: 我/ 在/ 课堂/ 学习/ 自然/ 自然语言/ 语言/ 1000/ 处理
Default Mode: 我/ 在/ 课堂/ 学习/ 自然语言/ 处理
他, 毕业, 于, 北京航空航天大学, ,, 在, 百度, 深度, 学习, 研究院, 进行, 研究
小明, 硕士, 毕业, 于, 中国, 科学, 学院, 科学院, 中国科学院, 计算, 计算所, ,, 后, 在, 福大, 大学, 斯坦福, 斯坦福大学, 深造

添加用户自定义字典,很多时候我们需要针对自己的场景进行分词,会有一些领域内的专有词汇:
1.可以用jieba.load_userdict(file_name)加载用户字典。
2.少量的词汇可以自己用下面方法手动添加:
2.1add_word(word, freq=None, tag=None)del_word(word) 在程序中动态修改词典
2.2suggest_freq(segment, tune=True) 可调节单个词语的词频,使其能(或不能)被分出来

print('/'.join(jieba.cut('如果放到旧字典中将出错。', HMM=False)))

如果/放到/旧/字典/中将/出错/。

jieba.suggest_freq(('中', '将'), True)
print('/'.join(jieba.cut('如果放到旧字典中将出错。', HMM=False)))

如果/放到/旧/字典/中/将/出错/。

import jieba_fast as jieba
jieba.lcut('浙江萧山农村商业银行对公取款凭条客户联')

from pycorrector.tokenizer import segment as seg
seg('浙江萧山农村商业银行对公取款凭条客户联')

import thulac
thu1 = thulac.thulac()  #默认模式
text = thu1.cut("福州运恒出租车服务有限公司通用机打发票出租汽车专用")
print(text)

5.4 (2或3_gram)打分

import kenlm
lm = kenlm.Model('C:\ProgramData\Anaconda3\Lib\site-packages\pycorrector\data\kenlm/people_chars_lm.klm')
sentence = '中国二商银行'# 2-gram
ngram_avg_scores = []
n = 2
scores = []
for i in range(6 - n + 1):word = sentence[i:i + n]score = lm.score(word, bos=False, eos=False)scores.append(score)
print(scores)
# if not scores:
#     continue
for _ in range( 1):scores.insert(0,scores[0])scores.append(scores[-1])
print(scores)
avg_scores = [sum(scores[i:i + n]) / len(scores[i:i + n]) for i in range(6)]
ngram_avg_scores.append(avg_scores)
print(ngram_avg_scores)

# 3-gram
ngram_avg_scores = []
n = 3
scores = []
for i in range(6 - n + 1):word = sentence[i:i + n]score = lm.score(word, bos=False, eos=False)scores.append(score)
print(scores)
# if not scores:
#     continue
for _ in range( n-1):scores.insert(0,scores[0])scores.append(scores[-1])
print(scores)
avg_scores = [sum(scores[i:i + n]) / len(scores[i:i + n]) for i in range(6)]
ngram_avg_scores.append(avg_scores)
print(ngram_avg_scores)

# 2或3-gram
ngram_avg_scores = []for n in [2,3]:scores = []for i in range(6 - n + 1):word = sentence[i:i + n]score = lm.score(word, bos=False, eos=False)scores.append(score)
#     print(scores)# if not scores:#     continuefor _ in range( n-1):scores.insert(0,scores[0])scores.append(scores[-1])
#     print(scores)avg_scores = [sum(scores[i:i + n]) / len(scores[i:i + n]) for i in range(6)]ngram_avg_scores.append(avg_scores)
print(ngram_avg_scores)

5.5 numpy矩阵处理

import numpy as np
# 取拼接后的ngram平均得分
# sent_scores = list(np.average(np.array(ngram_avg_scores), axis=0))
np.array(ngram_avg_scores)

np.average(np.array(ngram_avg_scores), axis=0)

sent_scores = list(np.average(np.array(ngram_avg_scores), axis=0))
sent_scores

scoress = sent_scores
scoress = np.array(scoress)
scoress

len(scoress.shape)

scores2 = scoress[:, None]
scores2

median = np.median(scores2 , axis = 0)#中位数先排序,奇数取中间,偶数取中间两个求平均。不是np.mean
median

np.sqrt(np.sum((scores2 - median) ** 2 , axis = -1))

#margin_median = np.sqrt(np.sum((scores2 - median) ** 2, axis=-1))
margin_median = np.sqrt(np.sum((scores2 - median) ** 2 , axis = 1))
margin_median

# 平均绝对离差值
med_abs_deviation = np.median(margin_median)
med_abs_deviation

ratio=0.6745
y_score = ratio * margin_median / med_abs_deviation
y_score

# scores = scores.flatten()
# maybe_error_indices = np.where((y_score > threshold) & (scores < median))
scores2 = scores2.flatten()
scores2

print('scores2 :' ,scores2)
print('median :' ,median)
print('y_score :' ,y_score)

np.where(y_score > 1.4)

list(np.where(scores2 < median)[0])

5.6 编辑距离

import re
import os
from collections import Counterdef candidates(word):"""generate possible spelling corrections for word.:param word::return:"""return known([word]) or known(edits1(word)) or known(edits2(word)) or [word]def known(words):"""the subset of 'words' that appear in the dictionary of WORDS:param words::return:"""return set(w for w in words if w in WORDS)def edits1(word):"""all edits that are one edit away from 'word':param word::return:"""letters = 'abcdefghijklmnopqrstuvwxyz'splits = [(word[:i], word[i:]) for i in range(len(word) + 1)]deletes = [L + R[1:] for L, R in splits if R]transposes = [L + R[1] + R[0] + R[2:] for L, R in splits if len(R) > 1]replaces = [L + c + R[1:] for L, R in splits if R for c in letters]inserts = [L + c + R for L, R in splits for c in letters]return set(deletes + transposes + replaces + inserts)def edits2(word):"""all edit that are two edits away from 'word':param word::return:"""return (e2 for e1 in edits1(word) for e2 in edits1(e1))
word = '中国工商银行'
for i in range(len(word) + 1):print(word[:i], word[i:])

edits1('中国工商银行')  #编辑距离算法


sentence  = '##我爱##/中国###//'
sentence.strip('#''/')

from pycorrector.tokenizer import Tokenizer
tokenize = Tokenizer() #类的实例
sentence = '中国是联合国第五大常任理事国'
token = tokenize.tokenize(sentence)
token

5.7 pandas use pycorrector

数据集链接:https://pan.baidu.com/s/1c1EGc_tY4K7rfoS-NbGhMg 提取码:kp4h

import pandas as pd
data = pd.read_csv('data.txt',sep = ' ',header = None)
data

data.info()#1列有(3978-3754)个null值

new_data = data.dropna()#将有NULL的一行如第24行去除,但是序号不变
new_data

new_data.info()#但是序号没变

new_data.index = range(0,3754)
new_data

new_data.columns = ['Right','Wrong']# = 号不要忘记写
new_data

a = new_data['Right'] == new_data['Wrong']
a

new_data_1 = pd.concat([new_data,a],axis=1) #增加一列
new_data_1

new_data_1[0].value_counts()#有2956条要纠错(统计0这列名称为False有2956条)

error_sentences = new_data_1['Wrong']
error_sentences

import pycorrector
corrector = []
for error_sentence in error_sentences:corrected_sent,detail = pycorrector.correct(error_sentence)#不能加单引号'error_sentence'corrector.append(corrected_sent)print(corrected_sent)

corrector

new_data_2 = pd.concat([new_data_1,pd.DataFrame(corrector)],axis=1) #DataFrame不是dateframe,不用单引号
new_data_2.columns = ['Right','Wrong','t/f','correct']
new_data_2

b = new_data_2['Right'] == new_data_2['correct']
new_data_3 = pd.concat([new_data_2,b],axis=1) #增加一列
new_data_3.columns = ['Right','Wrong','t/f','correct','T/F']
new_data_3

new_data_3['T/F'].value_counts()

# 统计将正确纠正错误的个数 和 将错误纠正正确的个数
data_change = new_data_3[new_data_3['t/f'] != new_data_3['T/F']]
data_change

data_change['T/F'].value_counts()

【Python3】文本分类综合(rnn,cnn,word2vec,TfidfVectorizer),中文纠错代码解析(pycorrector)相关推荐

  1. AI练手系列(四)—— cnews中文文本分类(RNN实现)

    数据集介绍 这个数据集是由清华大学根据新浪新闻RSS订阅频道2005-2011年间的历史数据筛选过滤生成的,数据集包含50000个样本的训练集,5000个样本的验证集,10000个样本的测试集,词汇表 ...

  2. [Pytorch系列-61]:循环神经网络 - 中文新闻文本分类详解-3-CNN网络训练与评估代码详解

    作者主页(文火冰糖的硅基工坊):文火冰糖(王文兵)的博客_文火冰糖的硅基工坊_CSDN博客 本文网址:https://blog.csdn.net/HiWangWenBing/article/detai ...

  3. 文本分类Keras RNN实践——应用腾讯和百度中文词向量

    中文词向量 深度学习在NLP领域大展身手,而深度学习处理文本,离不开文本的向量化. 英语独特的语法规则,使得单用空格就能将句子中的单词分割开来,从而取得词向量,这极大简化了英语的NLP预处理过程,工业 ...

  4. 中文文本分类的java包_java实现中文文本分类

    基于libsvm 的中文文本分类原型支持向量机(Support Vector M... 基于SSPP-KELM多标签文本分类算法的实现_电子/电路_工程科技_专业资料.文本数据分类后,根据类标签的个数 ...

  5. [Pytorch系列-60]:循环神经网络 - 中文新闻文本分类详解-2-LSTM网络训练与评估代码详解

    作者主页(文火冰糖的硅基工坊):文火冰糖(王文兵)的博客_文火冰糖的硅基工坊_CSDN博客 本文网址:https://blog.csdn.net/HiWangWenBing/article/detai ...

  6. python文本分类模型_下载 | 最全中文文本分类模型库,上手即用

    原标题:下载 | 最全中文文本分类模型库,上手即用 本文转自『大数据文摘』 如何选择合适的模型上手进行中文文本分类呢? 别慌,福利来了,GitHub上一位名为"huwenxing" ...

  7. 文本分类需要CNN? No!fastText完美解决你的需求(前篇)

    文本分类需要CNN?No!fastText完美解决你的需求(前篇) fastText是个啥?简单一点说,就是一种可以得到和深度学习结果准确率相同,但是速度快出几个世纪的文本分类算法.这个算法类似与CB ...

  8. 文本分类:Keras+RNN vs传统机器学习

    摘要:本文通过Keras实现了一个RNN文本分类学习的案例,并详细介绍了循环神经网络原理知识及与机器学习对比. 本文分享自华为云社区<基于Keras+RNN的文本分类vs基于传统机器学习的文本分 ...

  9. 文本分类需要CNN?No!fastText完美解决你的需求(前篇)

    http://blog.csdn.net/weixin_36604953/article/details/78195462?locationNum=8&fps=1 文本分类需要CNN?No!f ...

最新文章

  1. 如何从头开始构建自己的Linux Dotfiles Manager
  2. Linux终端:speedtest_cli检测你的实时带宽速度
  3. unknown error 1130,unknown error 1045
  4. ikbc键盘自动打字_键盘按斤卖,一斤一百块?IKBC W200机械键盘简晒
  5. Android严苛模式StrictMode使用与取消
  6. MFC90条技巧-带目录
  7. Jquery在线引用地址:
  8. python分布式存储文件_python如何分布式存储文件的方法
  9. React开发(151):外部引入记得导出
  10. MTK调试入门之一-TRACE使用的技巧
  11. Linux Socket poll
  12. GBDT(梯度提升决策树)总结笔记
  13. 为什么要用Redis?
  14. mosquitto查看订阅记录_Mosquitto\Client
  15. TensorFlow 学习(三)—— Variables、Session、初始化
  16. JSONView下载安装
  17. “网易有钱”sketch使用分享
  18. 用Python下载抖音无水印视频
  19. C语言学习日记(yzy):socket(TCP)网络连接
  20. 漫步云中网络 ( by quqi99 )

热门文章

  1. backtrack5实现局域网DNS欺骗
  2. PyTorch开发者福音, OpenVINO整合PyTorch实现推理加速!
  3. 群晖NAS教程(二十二)、利用Docker安装minio
  4. Tushare介绍和入门级实践(1)——使用tushare接口获取沪深300成分股交易日涨跌数据
  5. Python基于Oxford-IIIT Pet Dataset实现宠物识别系统
  6. List的toArray()方法和toArray(T[] a)方法
  7. 微信运动刷步数软件有哪些?微信运动刷步软件推荐[
  8. php青蛙跳井代码,四川招警考试行测答题技巧:青蛙跳井问题全解析
  9. Java之判断回文数
  10. 【Python】浅谈 字节码 + 虚拟机 (Python 解释器)