中文文本分类详解及与机器学习算法对比

news2024/9/19 22:25:36

一.文本分类

文本分类旨在对文本集按照一定的分类体系或标准进行自动分类标记,属于一种基于分类体系的自动分类。文本分类最早可以追溯到上世纪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两种方式进行实验。

基于深度学习的文本分类主要有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跳字模型。默认的优化方法负采样,更多参数解释请读者百度。

如果我们存在一个训练集、一个测试集,如果测试集中不存在某个特征词,怎么解决呢?这里我们在获取某个特征词的词向量,并转换为训练矩阵时,使用了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()

四.总结

写道这里,这篇文章就结束了。希望对您有所帮助,同时文章中不足或错误的地方,欢迎读者提出。这些实验都是我在做论文研究或项目评价常见的一些问题,希望读者带着这些问题,结合自己的需求进行深入的思考,更希望大家能学以致用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2123342.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

首届云原生编程挑战赛总决赛冠军比赛攻略_greydog.队

关联比赛: 首届云原生编程挑战赛【复赛】实现一个 Serverless 计算服务调度系统 一、初赛赛道一&#xff08;实现一个分布式统计和过滤的链路追踪&#xff09; 赛题分析 1、数据来源 采集自分布式系统中的多个节点上的调用链数据&#xff0c;每个节点一份数据文件。数据格式…

系统架构师考试学习笔记第四篇——架构设计实践知识(21)安全架构设计理论与实践

本章考点&#xff1a; 第21课时主要学习信息系统中安全架构设计的理论和工作中的实践。根据考试大纲,本课时知识点会涉及案例分析题和论文题(各占25分),而在历年考试中,综合知识选择题目中也有过诸多考查。本课时内容侧重于知识点记忆;,按照以往的出题规律,安全架构设计基础知识…

工具知识 | Linux常用命令

参考 linw7的github《鸟哥的Linux私房菜》 一.文件管理 1.文件查找&#xff1a;find2.文件拷贝&#xff1a;cp3.打包解包&#xff1a;tar 二.文本处理 1.(显示行号)查看文件&#xff1a;nl2.文本查找&#xff1a;grep3.排序&#xff1a;sort4.转换&#xff1a;tr5.切分文本&…

Web 基础——Apache

Event Worker 的升级版、把服务器进程和连接进行分析&#xff0c;基于异步 I/O 模型。 请求过来后进程并不处理请求&#xff0c;而是直接交由其它机制来处理&#xff0c;通过 epoll 机制来通知请求是否完成&#xff1b; 在这个过程中&#xff0c;进程本身一直处于空闲状态&am…

【目标检测数据集】铁轨表面缺损检测数据集4789张VOC+YOLO格式

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;4789 标注数量(xml文件个数)&#xff1a;4789 标注数量(txt文件个数)&#xff1a;4789 标注…

只有IP地址没有域名怎么实现HTTPS访问?

&#x1f510; 实现IP地址HTTPS访问 &#x1f310; 确认公网IP地址 公网IP&#xff1a;确保你拥有一个公网IP地址&#xff0c;或者内网映射公网&#xff0c;这是实现HTTPS访问的前提。 &#x1f4dd; 选择证书颁发机构&#xff08;CA&#xff09; 选择CA&#xff1a;选择一个…

【Qt】Qt音频

Qt 音频 在 Qt 中&#xff0c;⾳频主要是通过 QSound 类来实现。但是需要注意的是 QSound 类只⽀持播放 wav 格式的⾳频⽂件。也就是说如果想要添加⾳频效果&#xff0c;那么⾸先需要将 ⾮wav格式 的⾳频⽂件转换为 wav 格式。 【注意】使⽤ QSound 类时&#xff0c;需要添加模…

【C#Mutex】 initiallyOwned错误引起的缺陷

临界区只能对同一个进程的不同线程同步&#xff0c;互斥量可以跨进程同步。典型应用场景&#xff1a;两个exe会操作同一个注册表项。 错误代码 封装类 public class CMutexHelp : IDisposable {public CMutexHelp(){s_mutex.WaitOne();} private static Mutex s_mutex …

深度学习-目标检测(二)Fast R-CNN

一&#xff1a;Fast R-CNN Fast R-CNN 是一篇由Ross Girshick 在 2015 年发表的论文&#xff0c;题为 “Fast R-CNN”。这篇论文旨在解决目标检测领域中的一些问题&#xff0c;特别是传统目标检测方法中存在的速度和准确性之间的矛盾。 论文摘要&#xff1a;本文提出了一种基于…

关于tomcat如何设置自启动的设置

希望文章能给到你启发和灵感&#xff5e; 如果觉得文章对你有帮助的话&#xff0c;点赞 关注 收藏 支持一下博主吧&#xff5e; 阅读指南 开篇说明一、基础环境说明1.1 硬件环境1.2 软件环境 二、Windows 下的设置服务自启2.1 服务的注册2.2 开启自启 三、MacOS下设置服务自启…

ROS CDK魔法书:建立你的游戏王国(Python篇)

引言 在虚拟游戏的世界里&#xff0c;数字化的乐趣如同流动的音符&#xff0c;谱写着无数玩家的共同回忆。而在这片充满创意与冒险的乐园中&#xff0c;您的使命就是将独特的游戏体验与丰富的技术知识相结合&#xff0c;打造出令人难以忘怀的作品。当面对如何实现这一宏伟蓝图…

【数据结构】4——树和森林

数据结构——4树和森林 笔记 文章目录 数据结构——4树和森林树的存储结构双亲表示法孩子链表孩子兄弟表示法&#xff08;二叉树表示法、二叉链表表示法&#xff09; 树与二叉树转换森林和二叉树转化森林转二叉树二叉树转森林 树和森林的遍历树先根后根层次 森林 树的存储结构…

使用nvm工具实现多个nodejs版本的维护和切换

NodeJS的升级比较快&#xff0c;在开发中要使用最新的版本&#xff0c;必须经常升级&#xff0c;但对于一些老项目可能又要使用低版本的NodeJS&#xff0c;虽然可以在系统中同时安装多个NodeJS的版本&#xff0c;然后通过修改环境变量的方式实现切换&#xff0c;但这种方法太麻…

断点回归模型

断点回归&#xff08;Regression Discontinuity Design, RDD&#xff09;是一种准实验设计方法&#xff0c;用于评估政策或其他干预措施的效果。这种方法利用了一个清晰的阈值或“断点”&#xff0c;在这个阈值上&#xff0c;处理状态&#xff08;例如是否接受某种干预&#xf…

浅谈为什么数据库要用B树

朋友,你有没有遇到过这样的情况?明明数据库里存的东西还不算太多,可一查数据,页面加载慢得像蜗牛?别急,问题可能出在你的数据库索引上。而今天我要跟你聊的,就是在数据库里被广泛应用的B树(B-Trees),它可是提升数据库性能的秘密武器。听起来有点深奥?别担心,我会用…

C程序设计——再说说函数参数的值传递

上一篇的最后&#xff0c;我强调了C语言里&#xff0c;所有函数参数的传递&#xff0c;都是值传递&#xff0c;即形参值改变&#xff0c;不影响实参的值。 指针作为函数参数 我们知道&#xff0c;指针也是C语言的一个类型&#xff0c;所以指针&#xff0c;也可以作为函数参数…

【TPAMI 2024】一种用于混合事件-帧摄像机的异步线性滤波器架构

题目&#xff1a;An Asynchronous Linear Filter Architecture for Hybrid Event-Frame Cameras 一种用于混合事件-帧摄像机的异步线性滤波器架构 作者&#xff1a;Ziwei Wang; Yonhon Ng; Cedric Scheerlinck; Robert Mahony 摘要 事件相机非常适合捕捉高动态范围(HDR)视觉…

运维保障高效化的智慧能源开源了

一、简介 AI视频监控平台, 是一款功能强大且简单易用的实时算法视频监控系统。愿景在最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;减少企业级应用约 95%的开发成本&#xff0c;在强大视频算…

【python因果推断库13】使用 PyMC 模型进行回归断点分析

目录 线性模型、主效应模型和交互作用模型 使用bandwidth 使用基样条 import causalpy as cp %load_ext autoreload %autoreload 2 %config InlineBackend.figure_format retina seed 42 df cp.load_data("rd") 线性模型、主效应模型和交互作用模型 PyMC 采样…

教学辅助微信小程序|基于SSM+vue的高校教学微信小程序系统(源码+数据库+文档)

高校教学 目录 基于SSM教学辅助微信小程序 一、前言 二、系统设计 三、系统功能设计 小程序端 后台功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大厂码农|毕设布道师&#x…