详解CNN实现中文文本分类过程
摘要:本文主要讲解CNN实现中文文本分类的过程,并与贝叶斯、决策树、逻辑回归、随机森林、KNN、SVM等分类算法进行对比。
本文分享自华为云社区《[Python人工智能] 二十一.Word2Vec+CNN中文文本分类详解及与机器学习算法对比》,作者:eastmount。
一.文本分类
文本分类旨在对文本集按照一定的分类体系或标准进行自动分类标记,属于一种基于分类体系的自动分类。文本分类最早可以追溯到上世纪50年代,那时主要通过专家定义规则来进行文本分类;80年代出现了利用知识工程建立的专家系统;90年代开始借助于机器学习方法,通过人工特征工程和浅层分类模型来进行文本分类。现在多采用词向量以及深度神经网络来进行文本分类。
牛亚峰老师将传统的文本分类流程归纳如下图所示。在传统的文本分类中,基本上大部分机器学习方法都在文本分类领域有所应用。主要包括:
- Naive Bayes
- KNN
- SVM
- 随机森林 \ 决策树
- 集合类方法
- 最大熵
- 神经网络
利用Keras框架进行文本分类的基本流程如下:
- 步骤 1:文本的预处理,分词->去除停用词->统计选择top n的词做为特征词
- 步骤 2:为每个特征词生成ID
- 步骤 3:将文本转化成ID序列,并将左侧补齐
- 步骤 4:训练集shuffle
- 步骤 5:Embedding Layer 将词转化为词向量
- 步骤 6:添加模型,构建神经网络结构
- 步骤 7:训练模型
- 步骤 8:得到准确率、召回率、F1值
注意,如果使用TFIDF而非词向量进行文档表示,则直接分词去停后生成TFIDF矩阵后输入模型。本文将采用词向量、TFIDF两种方式进行实验。
在知乎史老师的“https://zhuanlan.zhihu.com/p/34212945”里总结归类来说,基于深度学习的文本分类主要有5个大类别:
- 词嵌入向量化:word2vec, FastText等
- 卷积神经网络特征提取:TextCNN(卷积神经网络)、Char-CNN等
- 上下文机制:TextRNN(循环神经网络)、BiRNN、BiLSTM、RCNN、TextRCNN(TextRNN+CNN)等
- 记忆存储机制:EntNet, DMN等
- 注意力机制:HAN、TextRNN+Attention等
二.基于随机森林的文本分类
该部分主要围绕常见的文本分类案例进行讲解,由于随机森林效果较好,故主要分享该方法。具体步骤包括:
- 读取CSV中文文本
- 调用Jieba库实现中文分词及数据清洗
- 特征提取采用TF-IDF或Word2Vec词向量表示
- 基于机器学习的分类
- 准确率、召回率、F值计算及评估
1.文本分类
(1).数据集
本文的数据为近期贵州黄果树瀑布的旅游评论文本,来自大众点评网,共有240条数据,其中差评数据114条,好评数据126条,如下图所示:
(2) 随机森林文本分类
本文不再详细叙述代码实现过程,前面很多文章都介绍过,并且源代码有详细的注释供大家参考。
# -*- coding:utf-8 -*-
import csv
import numpy as np
import jieba
import jieba.analyse
from sklearn import feature_extraction
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from sklearn.ensemble import RandomForestClassifier #----------------------------------第一步 读取文件--------------------------------
file = "data.csv" with open(file, "r", encoding="UTF-8") as f:
# 使用csv.DictReader读取文件中的信息
reader = csv.DictReader(f)
labels = []
contents = []
for row in reader:
# 数据元素获取
if row['label'] == '好评':
res = 0
else:
res = 1
labels.append(res)
content = row['content']
seglist = jieba.cut(content,cut_all=False) #精确模式
output = ' '.join(list(seglist)) #空格拼接
#print(output)
contents.append(output) print(labels[:5])
print(contents[:5]) #----------------------------------第二步 数据预处理--------------------------------
# 将文本中的词语转换为词频矩阵 矩阵元素a[i][j] 表示j词在i类文本下的词频
vectorizer = CountVectorizer() # 该类会统计每个词语的tf-idf权值
transformer = TfidfTransformer() #第一个fit_transform是计算tf-idf 第二个fit_transform是将文本转为词频矩阵
tfidf = transformer.fit_transform(vectorizer.fit_transform(contents))
for n in tfidf[:5]:
print(n)
#tfidf = tfidf.astype(np.float32)
print(type(tfidf)) # 获取词袋模型中的所有词语
word = vectorizer.get_feature_names()
for n in word[:5]:
print(n)
print("单词数量:", len(word)) # 将tf-idf矩阵抽取出来,元素w[i][j]表示j词在i类文本中的tf-idf权重
X = tfidf.toarray()
print(X.shape) # 使用 train_test_split 分割 X y 列表
# X_train矩阵的数目对应 y_train列表的数目(一一对应) -->> 用来训练模型
# X_test矩阵的数目对应 (一一对应) -->> 用来测试模型的准确性
X_train, X_test, y_train, y_test = train_test_split(X, labels, test_size=0.3, random_state=1) #----------------------------------第三步 机器学习分类--------------------------------
# 随机森林分类方法模型
# n_estimators:森林中树的数量
clf = RandomForestClassifier(n_estimators=20) # 训练模型
clf.fit(X_train, y_train) # 使用测试值 对 模型的准确度进行计算
print('模型的准确度:{}'.format(clf.score(X_test, y_test)))
print("\n") # 预测结果
pre = clf.predict(X_test)
print('预测结果:', pre[:10])
print(len(pre), len(y_test))
print(classification_report(y_test, pre))
输出结果如下图所示,随机森林的平均准确率为0.86,召回率为0.86,F值也为0.86。
2.算法评价
接着作者尝试自定义准确率(Precision)、召回率(Recall)和F特征值(F-measure),其计算公式如下:
由于本文主要针对2分类问题,其实验评估主要分为0和1两类,完整代码如下:
# -*- coding:utf-8 -*-
import csv
import numpy as np
import jieba
import jieba.analyse
from sklearn import feature_extraction
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from sklearn.ensemble import RandomForestClassifier #----------------------------------第一步 读取文件--------------------------------
file = "data.csv" with open(file, "r", encoding="UTF-8") as f:
# 使用csv.DictReader读取文件中的信息
reader = csv.DictReader(f)
labels = []
contents = []
for row in reader:
# 数据元素获取
if row['label'] == '好评':
res = 0
else:
res = 1
labels.append(res)
content = row['content']
seglist = jieba.cut(content,cut_all=False) #精确模式
output = ' '.join(list(seglist)) #空格拼接
#print(output)
contents.append(output) print(labels[:5])
print(contents[:5]) #----------------------------------第二步 数据预处理--------------------------------
# 将文本中的词语转换为词频矩阵 矩阵元素a[i][j] 表示j词在i类文本下的词频
vectorizer = CountVectorizer() # 该类会统计每个词语的tf-idf权值
transformer = TfidfTransformer() #第一个fit_transform是计算tf-idf 第二个fit_transform是将文本转为词频矩阵
tfidf = transformer.fit_transform(vectorizer.fit_transform(contents))
for n in tfidf[:5]:
print(n)
#tfidf = tfidf.astype(np.float32)
print(type(tfidf)) # 获取词袋模型中的所有词语
word = vectorizer.get_feature_names()
for n in word[:5]:
print(n)
print("单词数量:", len(word)) # 将tf-idf矩阵抽取出来,元素w[i][j]表示j词在i类文本中的tf-idf权重
X = tfidf.toarray()
print(X.shape) # 使用 train_test_split 分割 X y 列表
# X_train矩阵的数目对应 y_train列表的数目(一一对应) -->> 用来训练模型
# X_test矩阵的数目对应 (一一对应) -->> 用来测试模型的准确性
X_train, X_test, y_train, y_test = train_test_split(X, labels, test_size=0.3, random_state=1) #----------------------------------第三步 机器学习分类--------------------------------
# 随机森林分类方法模型
# n_estimators:森林中树的数量
clf = RandomForestClassifier(n_estimators=20) # 训练模型
clf.fit(X_train, y_train) # 使用测试值 对 模型的准确度进行计算
print('模型的准确度:{}'.format(clf.score(X_test, y_test)))
print("\n") # 预测结果
pre = clf.predict(X_test)
print('预测结果:', pre[:10])
print(len(pre), len(y_test))
print(classification_report(y_test, pre)) #----------------------------------第四步 评价结果--------------------------------
def classification_pj(name, y_test, pre):
print("算法评价:", name) # 正确率 Precision = 正确识别的个体总数 / 识别出的个体总数
# 召回率 Recall = 正确识别的个体总数 / 测试集中存在的个体总数
# F值 F-measure = 正确率 * 召回率 * 2 / (正确率 + 召回率) YC_B, YC_G = 0,0 #预测 bad good
ZQ_B, ZQ_G = 0,0 #正确
CZ_B, CZ_G = 0,0 #存在 #0-good 1-bad 同时计算防止类标变化
i = 0
while i<len(pre):
z = int(y_test[i]) #真实
y = int(pre[i]) #预测 if z==0:
CZ_G += 1
else:
CZ_B += 1 if y==0:
YC_G += 1
else:
YC_B += 1 if z==y and z==0 and y==0:
ZQ_G += 1
elif z==y and z==1 and y==1:
ZQ_B += 1
i = i + 1 print(ZQ_B, ZQ_G, YC_B, YC_G, CZ_B, CZ_G)
print("") # 结果输出
P_G = ZQ_G * 1.0 / YC_G
P_B = ZQ_B * 1.0 / YC_B
print("Precision Good 0:", P_G)
print("Precision Bad 1:", P_B) R_G = ZQ_G * 1.0 / CZ_G
R_B = ZQ_B * 1.0 / CZ_B
print("Recall Good 0:", R_G)
print("Recall Bad 1:", R_B) F_G = 2 * P_G * R_G / (P_G + R_G)
F_B = 2 * P_B * R_B / (P_B + R_B)
print("F-measure Good 0:", F_G)
print("F-measure Bad 1:", F_B) # 函数调用
classification_pj("RandomForest", y_test, pre)
输出结果如下图所示,其中好评的准确率、召回率、F值分别为0.9268、0.9268、0.9268,差评的准确率、召回率、F值分别为0.9032、0.9032、0.9032。
3.算法对比
最后作者给出机器学习RF、DTC、SVM、KNN、NB、LR的文本分类结果,这也是写论文中很常见的操作。
# -*- coding:utf-8 -*-
import csv
import numpy as np
import jieba
import jieba.analyse
from sklearn import feature_extraction
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from sklearn.ensemble import RandomForestClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn import svm
from sklearn import neighbors
from sklearn.naive_bayes import MultinomialNB
from sklearn.linear_model import LogisticRegression #----------------------------------第一步 读取文件--------------------------------
file = "data.csv" with open(file, "r", encoding="UTF-8") as f:
# 使用csv.DictReader读取文件中的信息
reader = csv.DictReader(f)
labels = []
contents = []
for row in reader:
# 数据元素获取
if row['label'] == '好评':
res = 0
else:
res = 1
labels.append(res)
content = row['content']
seglist = jieba.cut(content,cut_all=False) #精确模式
output = ' '.join(list(seglist)) #空格拼接
#print(output)
contents.append(output) print(labels[:5])
print(contents[:5]) #----------------------------------第二步 数据预处理--------------------------------
# 将文本中的词语转换为词频矩阵 矩阵元素a[i][j] 表示j词在i类文本下的词频
vectorizer = CountVectorizer() # 该类会统计每个词语的tf-idf权值
transformer = TfidfTransformer() #第一个fit_transform是计算tf-idf 第二个fit_transform是将文本转为词频矩阵
tfidf = transformer.fit_transform(vectorizer.fit_transform(contents))
for n in tfidf[:5]:
print(n)
#tfidf = tfidf.astype(np.float32)
print(type(tfidf)) # 获取词袋模型中的所有词语
word = vectorizer.get_feature_names()
for n in word[:5]:
print(n)
print("单词数量:", len(word)) # 将tf-idf矩阵抽取出来,元素w[i][j]表示j词在i类文本中的tf-idf权重
X = tfidf.toarray()
print(X.shape) # 使用 train_test_split 分割 X y 列表
# X_train矩阵的数目对应 y_train列表的数目(一一对应) -->> 用来训练模型
# X_test矩阵的数目对应 (一一对应) -->> 用来测试模型的准确性
X_train, X_test, y_train, y_test = train_test_split(X, labels, test_size=0.3, random_state=1) #----------------------------------第四步 评价结果--------------------------------
def classification_pj(name, y_test, pre):
print("算法评价:", name) # 正确率 Precision = 正确识别的个体总数 / 识别出的个体总数
# 召回率 Recall = 正确识别的个体总数 / 测试集中存在的个体总数
# F值 F-measure = 正确率 * 召回率 * 2 / (正确率 + 召回率) YC_B, YC_G = 0,0 #预测 bad good
ZQ_B, ZQ_G = 0,0 #正确
CZ_B, CZ_G = 0,0 #存在 #0-good 1-bad 同时计算防止类标变化
i = 0
while i<len(pre):
z = int(y_test[i]) #真实
y = int(pre[i]) #预测 if z==0:
CZ_G += 1
else:
CZ_B += 1 if y==0:
YC_G += 1
else:
YC_B += 1 if z==y and z==0 and y==0:
ZQ_G += 1
elif z==y and z==1 and y==1:
ZQ_B += 1
i = i + 1
print(ZQ_B, ZQ_G, YC_B, YC_G, CZ_B, CZ_G) # 结果输出
P_G = ZQ_G * 1.0 / YC_G
P_B = ZQ_B * 1.0 / YC_B
print("Precision Good 0:{:.4f}".format(P_G))
print("Precision Bad 1:{:.4f}".format(P_B))
print("Avg_precision:{:.4f}".format((P_G+P_B)/2)) R_G = ZQ_G * 1.0 / CZ_G
R_B = ZQ_B * 1.0 / CZ_B
print("Recall Good 0:{:.4f}".format(R_G))
print("Recall Bad 1:{:.4f}".format(R_B))
print("Avg_recall:{:.4f}".format((R_G+R_B)/2)) F_G = 2 * P_G * R_G / (P_G + R_G)
F_B = 2 * P_B * R_B / (P_B + R_B)
print("F-measure Good 0:{:.4f}".format(F_G))
print("F-measure Bad 1:{:.4f}".format(F_B))
print("Avg_fmeasure:{:.4f}".format((F_G+F_B)/2)) #----------------------------------第三步 机器学习分类--------------------------------
# 随机森林分类方法模型
rf = RandomForestClassifier(n_estimators=20)
rf.fit(X_train, y_train)
pre = rf.predict(X_test)
print("随机森林分类")
print(classification_report(y_test, pre))
classification_pj("RandomForest", y_test, pre)
print("\n") # 决策树分类方法模型
dtc = DecisionTreeClassifier()
dtc.fit(X_train, y_train)
pre = dtc.predict(X_test)
print("决策树分类")
print(classification_report(y_test, pre))
classification_pj("DecisionTree", y_test, pre)
print("\n") # SVM分类方法模型
SVM = svm.LinearSVC() #支持向量机分类器LinearSVC
SVM.fit(X_train, y_train)
pre = SVM.predict(X_test)
print("支持向量机分类")
print(classification_report(y_test, pre))
classification_pj("LinearSVC", y_test, pre)
print("\n") # KNN分类方法模型
knn = neighbors.KNeighborsClassifier() #n_neighbors=11
knn.fit(X_train, y_train)
pre = knn.predict(X_test)
print("最近邻分类")
print(classification_report(y_test, pre))
classification_pj("KNeighbors", y_test, pre)
print("\n") # 朴素贝叶斯分类方法模型
nb = MultinomialNB()
nb.fit(X_train, y_train)
pre = nb.predict(X_test)
print("朴素贝叶斯分类")
print(classification_report(y_test, pre))
classification_pj("MultinomialNB", y_test, pre)
print("\n") # 逻辑回归分类方法模型
LR = LogisticRegression(solver='liblinear')
LR.fit(X_train, y_train)
pre = LR.predict(X_test)
print("逻辑回归分类")
print(classification_report(y_test, pre))
classification_pj("LogisticRegression", y_test, pre)
print("\n")
输出结果如下所示,发现贝叶斯算法在文本分类中的效果还是很棒;同时随机森林、逻辑回归、SVM效果都还不错。
完整结果如下:
随机森林分类
precision recall f1-score support 0 0.92 0.88 0.90 41
1 0.85 0.90 0.88 31 accuracy 0.89 72
macro avg 0.89 0.89 0.89 72
weighted avg 0.89 0.89 0.89 72 算法评价: RandomForest
28 36 33 39 31 41
Precision Good 0:0.9231
Precision Bad 1:0.8485
Avg_precision:0.8858
Recall Good 0:0.8780
Recall Bad 1:0.9032
Avg_recall:0.8906
F-measure Good 0:0.9000
F-measure Bad 1:0.8750
Avg_fmeasure:0.8875
决策树分类
precision recall f1-score support 0 0.81 0.73 0.77 41
1 0.69 0.77 0.73 31 accuracy 0.75 72
macro avg 0.75 0.75 0.75 72
weighted avg 0.76 0.75 0.75 72 算法评价: DecisionTree
24 30 35 37 31 41
Precision Good 0:0.8108
Precision Bad 1:0.6857
Avg_precision:0.7483
Recall Good 0:0.7317
Recall Bad 1:0.7742
Avg_recall:0.7530
F-measure Good 0:0.7692
F-measure Bad 1:0.7273
Avg_fmeasure:0.7483
支持向量机分类
最近邻分类
朴素贝叶斯分类
逻辑回归分类
......
三.基于CNN的文本分类
接着我们开始通过CNN实现文本分类,该方法可以应用于很多领域,只要有数据集即可分析。这里仅给出最基础且可用的方法及源码,希望对您有所帮助。
1.数据预处理
上一部分我在写机器学习文本分类时,已经介绍了中文分词等预处理操作,为什么这部分还要介绍呢?因为这里我要增加两个新的操作:
- 去停用词
- 词性标注
这两个操作在文本挖掘过程中非常重要,它一方面能提升我们的分类效果,另一方面能过滤掉无关的特征词,词性标注也能辅助我们进行其他的分析,如情感分析、舆情挖掘等。
该部分代码如下:
# -*- coding:utf-8 -*-
import csv
import numpy as np
import jieba
import jieba.analyse
import jieba.posseg as pseg
from sklearn import feature_extraction
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report #----------------------------------第一步 数据预处理--------------------------------
file = "data.csv" # 获取停用词
def stopwordslist(): #加载停用词表
stopwords = [line.strip() for line in open('stop_words.txt', encoding="UTF-8").readlines()]
return stopwords # 去除停用词
def deleteStop(sentence):
stopwords = stopwordslist()
outstr = ""
for i in sentence:
# print(i)
if i not in stopwords and i!="\n":
outstr += i
return outstr # 中文分词
Mat = []
with open(file, "r", encoding="UTF-8") as f:
# 使用csv.DictReader读取文件中的信息
reader = csv.DictReader(f)
labels = []
contents = []
for row in reader:
# 数据元素获取
if row['label'] == '好评':
res = 0
else:
res = 1
labels.append(res) # 中文分词
content = row['content']
#print(content)
seglist = jieba.cut(content,cut_all=False) #精确模式
#print(seglist) # 去停用词
stc = deleteStop(seglist) #注意此时句子无空格
# 空格拼接
seg_list = jieba.cut(stc,cut_all=False)
output = ' '.join(list(seg_list))
#print(output)
contents.append(output) # 词性标注
res = pseg.cut(stc)
seten = []
for word,flag in res:
if flag not in ['nr','ns','nt','mz','m','f','ul','l','r','t']:
seten.append(word)
Mat.append(seten) print(labels[:5])
print(contents[:5])
print(Mat[:5]) # 文件写入
fileDic = open('wordCut.txt', 'w', encoding="UTF-8")
for i in Mat:
fileDic.write(" ".join(i))
fileDic.write('\n')
fileDic.close()
words = [line.strip().split(" ") for line in open('WordCut.txt',encoding='UTF-8').readlines()]
print(words[:5])
运行结果如下图所示,可以看到原文本被分词,并且过滤掉了“还”、“,”、“常常”等停用词,并且以两种形式呈现,读者可以结合自己的需要进行后续分析。同时,将分词后的文本也写入到wordCut.txt文件中。
- contents:显示已分词且以列表形式存在的句子
- Mat:显示已分词且以列表形式存在的词序列
2.特征提取及Word2Vec词向量转换
(1) 特征词编号
首先,我们先调用Tokenizer和fit_on_texts函数将文本中的每个词编号,词频出现越高其编号越小。如下图所示,“瀑布”、“景区”、“排队”、“水帘洞”等特征词出现较多,注意空格、“评论”、“收起”可以继续过滤掉,在停用词表中添加即可。
#fit_on_texts函数可以将输入的文本每个词编号 编号根据词频(词频越大编号越小)
tokenizer = Tokenizer()
tokenizer.fit_on_texts(Mat)
vocab = tokenizer.word_index #停用词已过滤,获取每个词的编号
print(vocab)
输出结果如下图所示:
(2) Word2Vec词向量训练
获取了特征词编号即将特征矩阵的表头定义好了,接下来我们需要将每一行文本转换成一维词向量,最终构建特征矩阵,用于训练和分类。注意,利用pad_sequences方法将CNN训练的长度统一,更好地进行训练。比如设置为100,如果句子超过100后面的单词会被切掉;如果句子未超过100,则会在句子前面补0,下图展示了补0过程。同时,分类结果[0,1]表示类标是好评0,[1,0]表示类标是差评1。
此时的完整代码如下:
# 使用 train_test_split 分割 X y 列表
X_train, X_test, y_train, y_test = train_test_split(Mat, labels, test_size=0.3, random_state=1)
print(X_train[:5])
print(y_train[:5]) #----------------------------------第三步 词向量构建--------------------------------
# Word2Vec训练
maxLen = 100 #词序列最大长度
num_features = 100 #设置词语向量维度
min_word_count = 3 #保证被考虑词语的最低频度
num_workers = 4 #设置并行化训练使用CPU计算核心数量
context = 4 #设置词语上下文窗口大小 # 设置模型
model = word2vec.Word2Vec(Mat, workers=num_workers, size=num_features,
min_count=min_word_count,window=context)
# 强制单位归一化
model.init_sims(replace=True)
# 输入一个路径保存训练模型 其中./data/model目录事先存在
model.save("CNNw2vModel")
model.wv.save_word2vec_format("CNNVector",binary=False)
print(model)
# 加载模型 如果word2vec已训练好直接用下面语句
w2v_model = word2vec.Word2Vec.load("CNNw2vModel") # 特征编号(不足的前面补0)
trainID = tokenizer.texts_to_sequences(X_train)
print(trainID)
testID = tokenizer.texts_to_sequences(X_test)
print(testID)
# 该方法会让CNN训练的长度统一
trainSeq = pad_sequences(trainID, maxlen=maxLen)
print(trainSeq) # 标签独热编码 转换为one-hot编码
trainCate = to_categorical(y_train, num_classes=2) #二分类问题
print(trainCate)
testCate = to_categorical(y_test, num_classes=2) #二分类问题
print(testCate)
输出结果如下:
[['景色', ' ', '景区', '太', '成熟', '从', '大', '瀑布', '景区', '出发', '景区', '观光车', '足足', '游客', '半小时', '世博会', '路上', '摩肩接踵', '欣赏', '美景', '心情', '观光车', '上车', '处', '标明', '目的地', '入口处', '引导', '走', '冤枉路', '稀里糊涂', '上车', '问', '司机', '到达', '司机', '含糊地', '说', '开出', '景区', '客运站', '七孔', '景区', '开发', '完美', '收起', '评论'],
['淡季', '瀑布', '人', '少', '景美', '机票', '便宜', '值得', '去'],
['瀑布', '体验', '差', '五星', '好评', '全', '是', '刷', '道路', '很窄', '导致', '大面积', '堵塞', '排队', '崩溃', '景区', '指引', '清晰', '排队', '大雨', '遮雨', '设计', '搞', '大人', '小孩', '老人', '淋雨', '景区', '接待', '能力差', '瀑布', '真的', '徒有虚名', '七孔', '收起', '评论'],
['老爸', '分', '瀑布', '瀑布', '瀑布', '游览', '瀑布', '门票', '反正', '超过', ' ', '来到', '熟悉', '告知', '只能', '出', '进入', '口', '回到', '高速', '出口', '直行', '回去', '倒', '指示', '清晰', '隔离', '栏杆', '自驾车', '导进', '停车场', '停车场', '收费', '且', '时间', ' ', '停车场', '经查', '景区', '门票', '单人', '含', '交通', '车费', '交通车', '需', '另付', '从外', '围绕', '路', '花', '不到', '分钟', '车费', '真心', '接受', ' ', '全家人', '不想', '┐', '(', '─', '__', '─', ')', '┌', '利益', '勾结', '剧烈', '涨费', '个金', '瀑布', '好看', '差', '评', ' ', '图片', '未', '开发', '瀑布', '天坑', '瀑布', '壮观', '壮观', '有', '灵秀', '景区', '膨胀', '成', '收起', '评论'],
['全家', '票', '居民', '专享', '优惠', '票']] [1, 0, 1, 1, 1]
Word2Vec(vocab=718, size=100, alpha=0.025) [[ 0 0 0 ... 2481 5 4]
[ 0 0 0 ... 570 52 90]
[ 0 0 0 ... 187 5 4]
...
[ 0 0 0 ... 93 5 4]
[ 0 0 0 ... 30 5 4]
[ 0 0 0 ... 81 18 78]] [[0. 1.]
[1. 0.]
[0. 1.]
[0. 1.]
[0. 1.]
[0. 1.]
[1. 0.]
3.CNN构建
接下来我们开始将构建好的特征矩阵拿去训练,计算不同文本或一维矩阵的相似度,这样会将好评和差评的不同句子按相似度分成两类。这里同样使用Word2Vec实现核心代码如下:
model = word2vec.Word2Vec(
Mat,
workers=num_workers,
size=num_features,
min_count=min_word_count,
window=context
);
训练模型的结果为“Word2Vec(vocab=718, size=100, alpha=0.025)”,这里设置的过滤频度为3,相当于出现频率低于3的被过滤,最终得到718个特征词。num_features值为100,表示是100维的词向量。sg默认为连续词袋模型,也可以设置为1跳字模型。默认的优化方法负采样,更多参数解释请读者百度。
参考作者前文:
[Python人工智能] 九.gensim词向量Word2Vec安装及《庆余年》中文短文本相似度计算
如果我们存在一个训练集、一个测试集,如果测试集中不存在某个特征词,怎么解决呢?这里我们在获取某个特征词的词向量,并转换为训练矩阵时,使用了try-except异常捕获,如果未找到特征词则跳过即可,它会自动补0。
该部分代码如下所示:
#----------------------------------第四步 CNN构建--------------------------------
# 利用训练后的Word2vec自定义Embedding的训练矩阵 每行代表一个词(结合独热编码和矩阵乘法理解)
embedding_matrix = np.zeros((len(vocab)+1, 100)) #从0开始计数 加1对应之前特征词
for word, i in vocab.items():
try:
#提取词向量并放置训练矩阵
embedding_vector = w2v_model[str(word)]
embedding_matrix[i] = embedding_vector
except KeyError: #单词未找到跳过
continue # 训练模型
main_input = Input(shape=(maxLen,), dtype='float64')
# 词嵌入 使用预训练Word2Vec的词向量 自定义权重矩阵 100是输出词向量维度
embedder = Embedding(len(vocab)+1, 100, input_length=maxLen,
weights=[embedding_matrix], trainable=False) #不再训练
# 建立模型
model = Sequential()
model.add(embedder) #构建Embedding层
model.add(Conv1D(256, 3, padding='same', activation='relu')) #卷积层步幅3
model.add(MaxPool1D(maxLen-5, 3, padding='same')) #池化层
model.add(Conv1D(32, 3, padding='same', activation='relu')) #卷积层
model.add(Flatten()) #拉直化
model.add(Dropout(0.3)) #防止过拟合 30%不训练
model.add(Dense(256, activation='relu')) #全连接层
model.add(Dropout(0.2)) #防止过拟合
model.add(Dense(units=2, activation='softmax')) #输出层 # 模型可视化
model.summary() # 激活神经网络
model.compile(optimizer = 'adam', #优化器
loss = 'categorical_crossentropy', #损失
metrics = ['accuracy'] #计算误差或准确率
) #训练(训练数据、训练类标、batch—size每次256条训练、epochs、随机选择、验证集20%)
history = model.fit(trainSeq, trainCate, batch_size=256,
epochs=6, validation_split=0.2)
model.save("TextCNN") #----------------------------------第五步 预测模型--------------------------------
# 预测与评估
mainModel = load_model("TextCNN")
result = mainModel.predict(testSeq) #测试样本
#print(result)
print(np.argmax(result,axis=1))
score = mainModel.evaluate(testSeq,
testCate,
batch_size=32)
print(score)
构建的模型如下:
Model: "sequential_1"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding_2 (Embedding) (None, 100, 100) 290400
_________________________________________________________________
conv1d_1 (Conv1D) (None, 100, 256) 77056
_________________________________________________________________
max_pooling1d_1 (MaxPooling1 (None, 34, 256) 0
_________________________________________________________________
conv1d_2 (Conv1D) (None, 34, 32) 24608
_________________________________________________________________
flatten_1 (Flatten) (None, 1088) 0
_________________________________________________________________
dropout_1 (Dropout) (None, 1088) 0
_________________________________________________________________
dense_1 (Dense) (None, 256) 278784
_________________________________________________________________
dropout_2 (Dropout) (None, 256) 0
_________________________________________________________________
dense_2 (Dense) (None, 2) 514
=================================================================
Total params: 671,362
Trainable params: 380,962
Non-trainable params: 290,400
输出结果如下图所示,该模型的预测结果不是很理想,accuracy值仅为0.625,为什么呢?作者也还在进一步研究深度模型的优化,本文更重要的是提供一种可用的方法,效果不好也请见谅~
4.测试可视化
最后增加可视化代码,绘制图形如下图所示。再次强调,该算法效果确实不理想,误差不是逐渐递减,正确率也不是不断升高。如果读者发现原因或优化方法也恳请您告知,谢谢。
最后附上完整代码:
# -*- coding:utf-8 -*-
import csv
import numpy as np
import jieba
import jieba.analyse
import jieba.posseg as pseg
from sklearn import feature_extraction
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from keras import models
from keras import layers
from keras import Input
from gensim.models import word2vec
from keras.preprocessing.text import Tokenizer
from keras.utils.np_utils import to_categorical
from keras.preprocessing.sequence import pad_sequences
from keras.models import Model
from keras.models import Sequential
from keras.models import load_model
from keras.layers import Flatten, Dense, Dropout, Conv1D, MaxPool1D, Embedding #----------------------------------第一步 数据预处理--------------------------------
file = "data.csv" # 获取停用词
def stopwordslist(): #加载停用词表
stopwords = [line.strip() for line in open('stop_words.txt', encoding="UTF-8").readlines()]
return stopwords # 去除停用词
def deleteStop(sentence):
stopwords = stopwordslist()
outstr = ""
for i in sentence:
# print(i)
if i not in stopwords and i!="\n":
outstr += i
return outstr # 中文分词
Mat = []
with open(file, "r", encoding="UTF-8") as f:
# 使用csv.DictReader读取文件中的信息
reader = csv.DictReader(f)
labels = []
contents = []
for row in reader:
# 数据元素获取
if row['label'] == '好评':
res = 0
else:
res = 1
labels.append(res) # 中文分词
content = row['content']
#print(content)
seglist = jieba.cut(content,cut_all=False) #精确模式
#print(seglist) # 去停用词
stc = deleteStop(seglist) #注意此时句子无空格
# 空格拼接
seg_list = jieba.cut(stc,cut_all=False)
output = ' '.join(list(seg_list))
#print(output)
contents.append(output) # 词性标注
res = pseg.cut(stc)
seten = []
for word,flag in res:
if flag not in ['nr','ns','nt','mz','m','f','ul','l','r','t']:
#print(word,flag)
seten.append(word)
Mat.append(seten) print(labels[:5])
print(contents[:5])
print(Mat[:5]) #----------------------------------第二步 特征编号--------------------------------
# fit_on_texts函数可以将输入的文本每个词编号 编号根据词频(词频越大编号越小)
tokenizer = Tokenizer()
tokenizer.fit_on_texts(Mat)
vocab = tokenizer.word_index #停用词已过滤,获取每个词的编号
print(vocab) # 使用 train_test_split 分割 X y 列表
X_train, X_test, y_train, y_test = train_test_split(Mat, labels, test_size=0.3, random_state=1)
print(X_train[:5])
print(y_train[:5]) #----------------------------------第三步 词向量构建--------------------------------
# Word2Vec训练
maxLen = 100 #词序列最大长度
num_features = 100 #设置词语向量维度
min_word_count = 3 #保证被考虑词语的最低频度
num_workers = 4 #设置并行化训练使用CPU计算核心数量
context = 4 #设置词语上下文窗口大小 # 设置模型
model = word2vec.Word2Vec(Mat, workers=num_workers, size=num_features,
min_count=min_word_count,window=context)
# 强制单位归一化
model.init_sims(replace=True)
# 输入一个路径保存训练模型 其中./data/model目录事先存在
model.save("CNNw2vModel")
model.wv.save_word2vec_format("CNNVector",binary=False)
print(model)
# 加载模型 如果word2vec已训练好直接用下面语句
w2v_model = word2vec.Word2Vec.load("CNNw2vModel") # 特征编号(不足的前面补0)
trainID = tokenizer.texts_to_sequences(X_train)
print(trainID)
testID = tokenizer.texts_to_sequences(X_test)
print(testID)
# 该方法会让CNN训练的长度统一
trainSeq = pad_sequences(trainID, maxlen=maxLen)
print(trainSeq)
testSeq = pad_sequences(testID, maxlen=maxLen)
print(testSeq) # 标签独热编码 转换为one-hot编码
trainCate = to_categorical(y_train, num_classes=2) #二分类问题
print(trainCate)
testCate = to_categorical(y_test, num_classes=2) #二分类问题
print(testCate) #----------------------------------第四步 CNN构建--------------------------------
# 利用训练后的Word2vec自定义Embedding的训练矩阵 每行代表一个词(结合独热编码和矩阵乘法理解)
embedding_matrix = np.zeros((len(vocab)+1, 100)) #从0开始计数 加1对应之前特征词
for word, i in vocab.items():
try:
#提取词向量并放置训练矩阵
embedding_vector = w2v_model[str(word)]
embedding_matrix[i] = embedding_vector
except KeyError: #单词未找到跳过
continue # 训练模型
main_input = Input(shape=(maxLen,), dtype='float64')
# 词嵌入 使用预训练Word2Vec的词向量 自定义权重矩阵 100是输出词向量维度
embedder = Embedding(len(vocab)+1, 100, input_length=maxLen,
weights=[embedding_matrix], trainable=False) #不再训练
# 建立模型
model = Sequential()
model.add(embedder) #构建Embedding层
model.add(Conv1D(256, 3, padding='same', activation='relu')) #卷积层步幅3
model.add(MaxPool1D(maxLen-5, 3, padding='same')) #池化层
model.add(Conv1D(32, 3, padding='same', activation='relu')) #卷积层
model.add(Flatten()) #拉直化
model.add(Dropout(0.3)) #防止过拟合 30%不训练
model.add(Dense(256, activation='relu')) #全连接层
model.add(Dropout(0.2)) #防止过拟合
model.add(Dense(units=2, activation='softmax')) #输出层 # 模型可视化
model.summary() # 激活神经网络
model.compile(optimizer = 'adam', #优化器
loss = 'categorical_crossentropy', #损失
metrics = ['accuracy'] #计算误差或准确率
) #训练(训练数据、训练类标、batch—size每次256条训练、epochs、随机选择、验证集20%)
history = model.fit(trainSeq, trainCate, batch_size=256,
epochs=6, validation_split=0.2)
model.save("TextCNN") #----------------------------------第五步 预测模型--------------------------------
# 预测与评估
mainModel = load_model("TextCNN")
result = mainModel.predict(testSeq) #测试样本
print(result)
print(np.argmax(result,axis=1))
score = mainModel.evaluate(testSeq,
testCate,
batch_size=32)
print(score) #----------------------------------第五步 可视化--------------------------------
import matplotlib.pyplot as plt
plt.plot(history.history['accuracy'])
plt.plot(history.history['val_accuracy'])
plt.title('Model accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.legend(['Train','Valid'], loc='upper left') plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('Model loss')
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.legend(['Train','Valid'], loc='upper left')
plt.show()
四.总结
总之,本文通过Keras实现了一个CNN文本分类学习的案例,并详细介绍了文本分类原理知识及与机器学习对比。
详解CNN实现中文文本分类过程的更多相关文章
- 利用CNN进行中文文本分类(数据集是复旦中文语料)
利用TfidfVectorizer进行中文文本分类(数据集是复旦中文语料) 利用RNN进行中文文本分类(数据集是复旦中文语料) 上一节我们利用了RNN(GRU)对中文文本进行了分类,本节我们将继续使用 ...
- CNN在中文文本分类的应用
深度学习近一段时间以来在图像处理和NLP任务上都取得了不俗的成绩.通常,图像处理的任务是借助CNN来完成的,其特有的卷积.池化结构能够提取图像中各种不同程度的纹理.结构,并最终结合全连接网络实现信息的 ...
- 基于Text-CNN模型的中文文本分类实战 流川枫 发表于AI星球订阅
Text-CNN 1.文本分类 转眼学生生涯就结束了,在家待就业期间正好有一段空闲期,可以对曾经感兴趣的一些知识点进行总结. 本文介绍NLP中文本分类任务中核心流程进行了系统的介绍,文末给出一个基于T ...
- 基于Text-CNN模型的中文文本分类实战
Text-CNN 1.文本分类 转眼学生生涯就结束了,在家待就业期间正好有一段空闲期,可以对曾经感兴趣的一些知识点进行总结. 本文介绍NLP中文本分类任务中核心流程进行了系统的介绍,文末给出一个基于T ...
- 利用RNN进行中文文本分类(数据集是复旦中文语料)
利用TfidfVectorizer进行中文文本分类(数据集是复旦中文语料) 1.训练词向量 数据预处理参考利用TfidfVectorizer进行中文文本分类(数据集是复旦中文语料) ,现在我们有了分词 ...
- 万字总结Keras深度学习中文文本分类
摘要:文章将详细讲解Keras实现经典的深度学习文本分类算法,包括LSTM.BiLSTM.BiLSTM+Attention和CNN.TextCNN. 本文分享自华为云社区<Keras深度学习中文 ...
- Chinese-Text-Classification,用卷积神经网络基于 Tensorflow 实现的中文文本分类。
用卷积神经网络基于 Tensorflow 实现的中文文本分类 项目地址: https://github.com/fendouai/Chinese-Text-Classification 欢迎提问:ht ...
- DDR3内存详解,存储器结构+时序+初始化过程
DDR3内存详解,存储器结构+时序+初始化过程 标签: DDR3存储器博客 2017-06-17 16:10 1943人阅读 评论(1) 收藏 举报 分类: 硬件开发基础(2) 转自:http:/ ...
- 中文文本分类之TextRNN
RNN模型由于具有短期记忆功能,因此天然就比较适合处理自然语言等序列问题,尤其是引入门控机制后,能够解决长期依赖问题,捕获输入样本之间的长距离联系.本文的模型是堆叠两层的LSTM和GRU模型,模型的结 ...
- 中文文本分类之CharCNN
文本分类是自然语言处理中一个非常经典的任务,可用的模型非常多,相关的开源代码也非常多了.这篇博客用一个CNN模型,对新闻文本进行分类. 全部代码有4个模块:1.数据处理模块(命名为:cnews_loa ...
随机推荐
- Python 模块:创建、导入和使用
什么是模块? 将模块视为代码库.模块是一个包含一组函数的文件,您想要在应用程序中包含这些函数. 创建一个模块 要创建一个模块,只需将要包含在其中的代码保存在扩展名为 .py 的文件中: 示例:将以下代 ...
- AlibabaCloudToolkit的简单使用与部署
问题 以往的写好的应用程序放到服务器上部署的方式都是在本地打包成jar包,传到服务器上,在服务器用命令行关闭原版本的应用程序,在启动新版本的应用程序,每次写好一个功能要与前端联调都要经历这些繁琐的步骤 ...
- 高性能渲染——详解Html Canvas的优势与性能
本文由葡萄城技术团队原创并首发.转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 一.什么是Canvas 想必学习前端的同学们对Canvas 都不陌生,它是 ...
- SpringBoot+Redis实现接口级别缓存信息
本文主要讲述如何通过SpringBoot+Redis实现接口级别缓存信息 背景 近期因为一直在处理公司的老项目,恰好碰到产品说页面有一些信息展示慢,简单看了一下页面接口,发现查询的是系统中几张大表(数 ...
- parallel-comparator-200
直接上来就是c代码,这种题还是第一次做,直接写代码逆向回去就行了 但是奈何水平太低写不回去... 分析 需要下面的两个字符串相等才能得到正确的程序,若要相等就只能让result等于0 那就意味着亦或的 ...
- vue-router钩子执行顺序
Vue的路由在执行跳转时,根据源码可知,调用了router中定义的navigate函数 function push(to: RouteLocationRaw) { return pushWithRed ...
- .NET8依赖注入新特性Keyed services
什么是Keyed service Keyed service是指,为一个需要注入的服务定义一个Key Name,并使用使用Key Name检索依赖项注入 (DI) 服务的机制. 使用方法 通过调用 A ...
- [ORB/BEBLID] 利用OpenCV(C++)实现尺度不变性与角度不变性的特征找图算法
本文只发布于利用OpenCV实现尺度不变性与角度不变性的特征找图算法和知乎 一般来说,利用OpenCV实现找图功能,用的比较多的是模板匹配(matchTemplate).笔者比较喜欢里面的NCC算法. ...
- 如何实现一套简单的oauth2授权码类型认证,一些思路,供参考
背景 组内人不少,今年陆陆续续研发了不少系统,一般都会包括一个后台管理系统,现在问题是,每个管理系统都有RBAC那一套用户权限体系,实在是有点浪费人力,于是今年我们搞了个统一管理各个应用系统的RBAC ...
- 神经网络入门篇之深层神经网络:详解前向传播和反向传播(Forward and backward propagation)
深层神经网络(Deep L-layer neural network) 复习下前面的内容: 1.逻辑回归,结构如下图左边.一个隐藏层的神经网络,结构下图右边: 注意,神经网络的层数是这么定义的:从左到 ...