【NLP 实战】蒙古语情感分析:从 CNN 架构设计到模型训练的全流程解析(内附项目源码及模型成果)

发布于:2025-06-30 ⋅ 阅读:(25) ⋅ 点赞:(0)

文本情感分析


目录

文本情感分析

一、语料预处理

1.数据清洗与去噪    

2.文本分词处理

3.文本向量化和序列填充

4.数据划分

二、卷积神经网络模型

1.卷积层

2.池化层

3.全连接层

三、模型训练

1.实验环境搭建

2.实验数据

3.参数设置

4.实验结果及分析

四、源码及模型

1.项目源码

2.模型成果


一、语料预处理

        本研究的语料全部来自于本人校蒙古语智能信息化团队采集的语料库。研究中构建的25,000条蒙古语言语数据集中的情感标签分别为:“忧愁”、“厌烦”、“欣赏”、“生气”、“惊吓”、“害怕”和“高兴”,是一个有监督的七类别情感分类任务。

注:若需拿到该项目蒙古语所有的文本数据集Mongolian_Datasets,请联系博主!

1.数据清洗与去噪    

        数据清洗与去噪作为数据预处理的核心环节,二者相互关联,共同服务于数据质量提升,为后续数据分析与挖掘的准确性和可靠性提供保障。拿到本研究的蒙古语语料后进行相应的数据清洗:

  1. 首先通过电子表格工具去人为去重文本中重复的言语数据,从而减少一定程度上数据量和计算资源的浪费。以及可避免模型在训练的过程中对于相似数据的过拟合风险。
  2. 其次要纠正错误数据,这需要通过语言检查工具去检查语料中的一些明显错误,如拼写和语法错误等。
  3. 最后再进行缺失性的处理,对于一句话,语料缺失情况严重,直接将其进行删除。而对于语料缺失不严重的情况,研究人员可借助机器翻译工具对缺失语境的语料进行情景补全。这样可以保证语料数据的丰满性和模型后期的准确性。

        而对于数据的去噪,将根据正则表达式等方法去去除一些语料中可能存在的标点及特殊字符,从而防止这些因素干扰我们后续模型的训练,提高模型的训练效率和准确性。

2.文本分词处理

        文本分词处理是自然语言研究领域一个基础且关键的步骤。是把连续文本拆分成有意义的词语或符号单元,按规则和语义分成独立词语或标记的过程。本研究项目的蒙古语语料文本分词处理通过 Keras 的 Tokenizer 类实现。要初始化Tokenizer对象,设置参数,比如过滤字符和是否转小写。本研究中采用按字符分词粒度的方式。

3.文本向量化和序列填充

        文本向量化和序列填充是自然语言处理预处理文本数据的关键步骤,能为后续文本分类、情感分析等任务提供基础数据表示和输入格式。本研究项目的文本向量化用keras的Tokenizer类,为每个分词后的词分配唯一索引,把文本转为索引序列。具体操作如下:

  1. 首先通过tokenizer.fit_on_texts(texts)对输入的文本进行训练,统计文本中每个词的出现频率,并为每个词分配一个唯一的索引。
  2. 其次通过tokenizer.texts_to_sequences(texts)将文本转换为对应的索引序列,每个词都被替换为其对应的索引。

        序列填充用keras的pad_sequence功能函数,按指定的最大长度对索引序列填充和截断,确保所有序列长度一致。具体操作如下:

  1. 首先如果没有传入maxlen,则计算所有文本索引序列的最大长度作为maxlen。
  2. 其次pad_sequences函数将texts_vector填充到指定的长度maxlen。padding='post'表示在序列的后面补齐,truncating='post'表示当序列长度超出maxlen时,从序列的后面截断,补齐的值为 0.0,数据类型为int32。

4.数据划分

        数据划分是把原始语料数据集按一定的规则和目的划分为不同子集,为了更好利用数据进行模型开发和评估。在机器学习、数据分析等领域有重要的理论概念和应用。

        在该研究中,会将80%数据集划分为训练集、20%划分为测试确保模型在训练中学习规律,在测试中评估真实性能。最终将会通过评估函数以损失函数曲线和准确率曲线图的形式可视化模型训练的结果。

二、卷积神经网络模型

        卷积神经网络是一种深度学习算法,广泛的应用于图像识别、语音识别及自然语言处理等前沿领域。它通过输入层、卷积层、池化层、全连接层及输出层等组件层,自动提取数据的特征,具有高效的特征提取和强大的泛化能力。如研究[8]中所述,卷积神经网络能够通过多个处理层次,将包含不同情感色彩的文本当作一维图像,通过捕捉临近词之间的关联实现更加精准的分类与预测。在该研究中,基于卷积神经网络的蒙古语情感分析的原理图如下所示。

基于CNN的文本情感分析原理图

        在后续论述中,将会对卷积层、池化层及全连接层展开详细阐述,其三者又可被称为隐藏层,类似于一种不可见的黑盒模型。文本情感分析的过程原理如下图所示。而对于输入层与输出层,在此先作简要介绍:

  1. 输入层主要承担语料预处理工作,将文本特征矩阵作为卷积神经网络语言模型的输入数据,具体过程在上述模块中已经进行了阐述。该层目的是将自然语言形式的语料转换为适宜神经网络处理的格式,为后续网络层的特征提取打下基础。
  2. 输出层则负责输出文本分析的最终成果 —— 情感标签。历经网络各层对输入语料的层层特征提取与信息处理后,输出层基于所学习到的特征模式利用Softmax分类器将输出的数值转化为一个可能的值。从而判定并输出文本对应的情感类别,实现情感分析任务的预期目标。
文本情感分析过程原理图
文本情感分析过程原理图

1.卷积层

        在卷积层中要对输入层输入的语料数据进行文本特征提取,卷积核在文本序列上滑动,每次卷积操作可捕捉到一个局部的文本片段的特征,例如捕捉到一些具有特定语义或语法结构的短语模式。

        假设卷积层输入文本网格图像为I,卷积核为K,输出特征图为O。则卷积运算的公式为:

在公式中,(i,j)是输出特征图O中的坐标,(m,n)是卷积核K中的坐标,而M和N分别是卷积核的高度和宽度。该公式表示的意义是,通过将卷积核在输入网格文本图像上滑动,在每个位置将卷积核与对应区域的文本图像像素进行点积求和,最终从而得出相应的特征值。  

        值得注意的是,在研究[9]中提出了一种分解卷积操作,该方法是将二维卷积核(如3×3)分解为1×3和3×1的一维卷积核级联。由于文本的一维性,因此该种方法在文本特征提取中更具优势,为后续构建模型的卷积层提供了一种更优的思路;在研究[10]中提出了单层卷积设计,该种简化卷积层在中等规模的数据集上可兼顾分类效果和运行效率,为文本分析提供新思路;在研究[11]中将传统卷积层与BERT、Dropout结合,有效的平衡了局部特征提取与全局语义的理解。

2.池化层

        为了进一步提取关键特征并减少数据的维度,通常在池化层将会通过激活函数进行特征提取和最大池化。通过卷积层的操作得到了文本线性组合的特征图,接着通过激活函数进行非线性变换。激活函数可以突出某些特征,抑制其他特征,从而提取出文本中的关键特征作为池化窗口下的值。ReLU激活函数公式为:

        而最大池化将会选中池化窗口中的最大值作为该窗口的代表值。通过这些操作,能够保留文本中最重要的特征,同时降低模型对输入数据的微小变化的敏感性从而提高鲁棒性。假设输入特征图为X,输出特征图为Y,池化窗口为k×k。则最大池化公式为:

该段公式的含义为,在每个大小为k×k的池化窗口内,选取其中的最大值作为输出特征图相应位置的值。
        上述为在传统架构下池化层的基础理论,通过阅读学术文献的启发,可以在此基础上进行一定的创新拓展。例如在研究[2]中,引入多池化操作(M-max),根据卷积层输出特征图的长度C(len),从而来动态决定池化窗口的大小M=C(len)/2。通过综合考虑多种因素,以获取每个特征图中多个重要特征;在研究[12]中,通过图卷积操作代替传统池化操作,有效的整合了语料数据集之间的语义关联和全局结构信息,科学性地提出一种新的特征处理范式;在研究[13]中,通过全局平均池化层实现了局部与全局特征地高效融合,从而兼顾了特征完整性和计算效率地池化策略;在研究[14]中,将最大池化与多尺度卷积核、注意力机制协同设计,构建了层次化的文本情感特征提取框架。

3.全连接层

        全连接层位于神经网络的末端,它将会对前面卷积层及其他层提取到的特征进行整合,并映射到最终的输出空间以实现最终的分类或回归任务。从而完成情感分析的任务。

        假设输入向量x=(x1,x2,...,xn),权重矩阵W=(Wij),偏执向量b=(b1,b2,...,bm),输出向量为y=(y1,y2,...,ym),其中F()是激活函数,则全连接层公式为:

在研究[15]中提出融合多源特征与时序信息,实现了对文本情感地精准建模;在研究[16]中通过GAP代替全连接层,构建了轻量化。抗过拟合的情感分类框架,为文本情感分析领域提供了新的技术思路;在研究[17]中通过全连接层与多头注意力的协同,构建了多层次特征到情感类别的高效映射;在研究[18]中通过全连接层与CNN、LSTM的深度整合,实现了在文本情感的多维度特征融合与高效分类,其设计在多种场景下有显著优势。

三、模型训练

1.实验环境搭建

        本研究的模型训练工作是在 Windows 11 操作系统下开展的。选用 PyCharm 作为开发工具,以Python 3.7作为运行环境,并将Miniconda作为解析器进行环境管理和相应软件包配置。在此过程中,配置了 TensorFlow、Keras、NumPy及Pandas等一系列项目所需的软件包,以满足数据处理和模型训练等需求。

# 深度学习环境软件包

absl-py==0.15.0
aiohttp==3.8.4
aiosignal==1.3.1
aliyun-python-sdk-core==2.14.0
aliyun-python-sdk-kms==2.16.2
annotated-types==0.5.0
anyio==3.7.1
argon2-cffi==23.1.0
argon2-cffi-bindings==21.2.0
astor==0.8.1
astunparse==1.6.3
async-timeout==4.0.3
asynctest==0.13.0
attrs==23.1.0
backcall==0.2.0
beautifulsoup4==4.12.2
bert4keras==0.11.5
bleach==6.0.0
bottle==0.12.25
bottle-websocket==0.2.9
cached-property==1.5.2
cachetools==4.2.4
certifi @ file:///C:/b/abs_85o_6fm0se/croot/certifi_1671487778835/work/certifi
cffi==1.15.1
charset-normalizer==3.3.2
click==8.1.7
colorama==0.4.6
comm==0.1.4
crcmod==1.7
cryptography==41.0.7
cycler==0.11.0
Cython==0.29.28
debugpy==1.7.0
decorator==5.1.1
defusedxml==0.7.1
dill==0.3.7
dm-tree==0.1.8
Eel==0.16.0
entrypoints==0.4
etils==0.9.0
exceptiongroup==1.1.3
fastapi==0.103.2
fastjsonschema==2.19.0
fasttext==0.9.1
Flask==1.1.1
fonttools==4.38.0
frozenlist==1.3.3
future==0.18.3
gast==0.3.3
gensim==4.2.0
gevent==22.10.2
gevent-websocket==0.10.1
gitdb==4.0.10
GitPython==3.1.36
google-auth==1.35.0
google-auth-oauthlib==0.4.6
google-pasta==0.2.0
googleapis-common-protos==1.62.0
graphviz==0.20.1
greenlet==2.0.2
grpcio==1.32.0
h11==0.14.0
h5py==2.10.0
httptools==0.6.0
idna==3.4
imageio==2.31.2
imbalanced-learn==0.6.0
importlib-metadata==6.7.0
importlib-resources==5.12.0
install==1.3.5
interchange==2021.0.4
ipykernel==6.16.2
ipython==7.34.0
ipython-genutils==0.2.0
ipywidgets==8.1.1
itsdangerous==2.0.1
jedi==0.19.1
jieba==0.42.1
Jinja2==3.0.2
jmespath==0.10.0
joblib==1.1.0
jsonschema==4.17.3
jupyter==1.0.0
jupyter-console==6.6.3
jupyter-server==1.24.0
jupyter_client==7.4.9
jupyter_core==4.12.0
jupyterlab-pygments==0.2.2
jupyterlab-widgets==3.0.9
Keras==2.3.1
Keras-Applications==1.0.8
keras-embed-sim==0.9.0
keras-layer-normalization==0.15.0
keras-metrics==1.1.0
keras-multi-head==0.28.0
keras-pos-embd==0.12.0
keras-position-wise-feed-forward==0.7.0
Keras-Preprocessing==1.1.2
keras-self-attention==0.50.0
keras-transformer==0.39.0
kiwisolver==1.3.1
lightgbm==4.3.0
loguru==0.7.2
Markdown==3.3.6
MarkupSafe==2.1.2
matplotlib==3.3.3
matplotlib-inline==0.1.6
mistune==3.0.2
monotonic==1.6
multidict==6.0.4
nbclassic==1.0.0
nbclient==0.7.4
nbconvert==7.6.0
nbformat==5.8.0
nest-asyncio==1.5.8
networkx==2.6.3
notebook==6.5.6
notebook_shim==0.2.3
numpy==1.19.5
oauthlib==3.2.2
openxlab==0.0.9
opt-einsum==3.3.0
oss2==2.17.0
packaging==23.1
pandas==1.3.5
pandocfilters==1.5.0
pansi==2020.7.3
parso==0.8.3
pickleshare==0.7.5
pigar==2.0.6
Pillow==8.4.0
pkgutil_resolve_name==1.3.10
playwright==1.35.0
prettytable==3.7.0
prometheus-client==0.17.1
promise==2.3
prompt-toolkit==3.0.41
protobuf==3.20.3
psutil==5.9.5
py-cpuinfo==9.0.0
py2neo==2021.2.4
pyahocorasick @ file:///D:/bld/pyahocorasick_1650016220991/work
pyarrow==9.0.0
pyasn1==0.5.1
pyasn1-modules==0.3.0
pybind11==2.12.0
pycparser==2.21
pycryptodome==3.19.0
pydantic==2.4.2
pydantic_core==2.10.1
pydot==1.4.2
pydot-ng==2.0.0
pyecharts==2.0.5
pyee==9.0.4
Pygments==2.17.2
pyparsing==3.0.7
pyrsistent==0.19.3
pystray==0.19.5
python-dateutil==2.8.2
python-dotenv==0.21.1
pytz==2023.3.post1
PyWavelets==1.3.0
pywin32==306
pywinpty==2.0.10
PyYAML==6.0
pyzmq==24.0.1
qtconsole==5.4.4
QtPy==2.4.1
requests==2.28.2
requests-oauthlib==1.3.1
rsa==4.9
schedule==1.2.1
scikit-image==0.19.3
scikit-learn==0.24.0
scipy==1.4.1
seaborn==0.12.2
Send2Trash==1.8.2
simplejson==3.19.2
six==1.16.0
smart-open==7.0.4
sniffio==1.3.0
soupsieve==2.4.1
starlette==0.27.0
tensorboard==2.2.2
tensorboard-plugin-wit==1.8.1
tensorflow-datasets==2.1.0
tensorflow-gpu==2.2.0
tensorflow-gpu-estimator==2.2.0
tensorflow-metadata==1.12.0
termcolor==1.1.0
terminado==0.17.1
thop==0.1.1.post2209072238
threadpoolctl==3.0.0
tifffile==2021.11.2
tinycss2==1.2.1
toml==0.10.2
tornado==6.2
tqdm==4.65.0
traitlets==5.9.0
typing_extensions==4.7.1
ujson==5.7.0
ultralytics==8.0.151
urllib3==1.26.18
uvicorn==0.22.0
watchfiles==0.20.0
wcwidth==0.2.12
webencodings==0.5.1
websocket-client==1.6.1
websockets==11.0.3
whichcraft==0.6.1
widgetsnbextension==4.0.9
win32-setctime==1.1.0
wincertstore==0.2
wrapt==1.16.0
xgboost==1.5.2
xlrd==1.2.0
yarl==1.8.2
zipp==3.6.0
zope.event==5.0
zope.interface==6.1

        在Windows系统下完成模型的构建和训练后,将进一步在云服务器上进行系统环境的适配与搭建,把训练好的模型迁移至云服务器,实现模型的部署工作。为后续软件系统的实际应用奠定工程基础。

2.实验数据

        本研究语料为七种情感类别,分别为厌烦、害怕、忧愁、惊吓、欣赏、生气和高兴,并用标签0到6依次标注。数据处理时,80%的数据集作为训练集用于模型训练;剩下的20%作为测试集评估模型性能。部分情感类别的样本量不充足,本研究通过构建更高质量的模型,最大程度上解决数据不平衡问题。部分标签数据量不足可能让模型分析结果不佳,但这会作为后续研究优化改进的重点方向。

实验数据集组成
情感标签 数据集(条) 训练集(条) 测试集(条)
0 10000 8000 2000
1 2000 1600 400
2 2000 1600 400
3 1000 800 200
4 10000 8000 2000
5 400 320 80
6 300 240 60

3.参数设置

        在本研究的模型训练中,需要设置如下几个关键参数:卷积层参数(卷积核大小、卷积核数量、步长及填充);池化层参数(激活函数、池化类型、池化窗口大小及池化步长);全连接层及输出层参数(神经元数量及激活函数);训练过程相关参数(训练轮数、优化器、学习率及损失函数);正则化相关参数(Dropout率):

  1. 分别运用大小为3×3、4×4和5×5的卷积核。不同大小的卷积核能够从输入的数据里面提取出不同尺度的特征。而对于卷积核的数量每种大小的卷积核会使用64个。因此最终共有192个卷积核用于特征提取。步长默认为1,不进行填充。
  2. 使用relu激活函数,即修正线性单元。它可以将小于0的值置为0,大于0的值保持不变,有助于引入非线性特征。池化技术使用全局最大池化,该种池化技术将会对整个输入的每个通道取最大的值,最终输出一个一维向量,每个通道对应一个值。且因为是全局最大池化技术,因此不存在传统意义上的窗口大小和步长的概念。
  3. 全连接层的神经元数量设置为256,意味着该层包含256个神经元。这些神经元会接收前面几层处理后的特征作为输入,进行后续的线性变换。输出层神经元数量的值代表着分类任务中类别的数量,该模型为七分类,因此最终的num_classes就为7。且最终使用softmax激活函数将输出层的每个神经元的输出转换为概率值,常用于多分类任务中输出每个类别的概率从而得到分析出的情感标识。
  4. 训练过程中的训练轮数Epochs设置为30批次,轮数过少无法提取文本的特征,而轮数过多将会使模型逐渐过度拟合,因此合理设定训练轮次是提升模型准确率的关键因素。使用Adam优化器这种自适应学习率的优化算法,通过该优化器创建学习率为0.001的优化实例。在模型编译时,将该优化器传入相应的方法,同时指定了适用于多分类问题的损失函数,用于衡量模型预测与真实标签概率分布的差异。
  5. 对于丢弃率dropout设置为0.5,通过丢弃率我们可以避免模型出现过拟合的情况,使模型更加泛化。

4.实验结果及分析

        从实验结果分类报告表看,模型在不同情感类别上的分类性能存在差异。大部分情感标签,模型有较好的分类能力。但样本量较小的情感类别,模型分类效果有待提升。特别是召回率不高的标签,要优化模型,来提高模型整体性能和稳定性,增强对各类情感的准确分类能力。

实验结果分类报告
情感标签 精确率 召回率 F1值
0

0.93

0.95 0.94
1

0.93

0.87 0.90
2 0.86 0.91 0.88
3 0.82 0.84 0.83
4 0.95 0.94 0.94
5 0.87 0.70 0.77
6 0.83 0.77 0.80

分析实验结果最终报告表可得知:

  1. 模型的准确率较高,该模型的准确率达93%,说明该模型在进行文本情感分析上具有较高的预测正确性,能够分析大部分样本的情感类别。
  2. 精确率表现良好,该模型精确率达88%,意味着该模型预测为正例的样本中,有88%是正例,说明了模型在判断时具有较高的可靠性,能够有效的避免过度预测的问题。
  3. 召回率有待提升,该模型召回率为85%,说明该模型对正例的捕捉能力还有提升的空间,可能需要进一步优化模型,以降低误判和漏判的情况。
  4. 综合性能良好,F1值为87%,是精确率和召回率的调和平均数,反映了该模型在精确率和召回率之间取得了较好的平衡。即使没有达到非常理想的状态,但该模型在综合性能及应用方面已经有较好体现。
实验结果最终报告
模型 准确率 精确率 召回率 F1值
CNN 0.93 0.88 0.85 0.87

基于上述的实验数据,也绘制出了训练集与测试机损失函数曲线和准确率曲线图来进行相应的可视化模型效果分析。具体如下图所示。

准确率曲线(左)和损失函数曲线图(右)

四、源码及模型

1.项目源码

# 导入模块部分(按功能分组)
import json, re, os, time, random, jieba, pickle, shutil, datetime
from tqdm import tqdm
from collections import defaultdict, Counter

import numpy as np
np.random.seed(7)
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline

# 中文显示配置
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

import sys
if sys.platform.startswith('win'):
    plt.rcParams['font.family'] = ['sans-serif']  # windows系统
else:
    plt.rcParams["font.family"] = 'Arial Unicode MS'  # 苹果系统


from keras.engine.training import Model

# 文本预处理模块
from keras.preprocessing.text import Tokenizer

# 评估函数模块
from sklearn.metrics import accuracy_score, f1_score, precision_score, recall_score, classification_report
from keras.utils import to_categorical

# 网络层模块
from keras.layers import Dense, Dropout, Embedding, Input, Concatenate
from keras.layers import Conv1D
from keras.layers import GlobalMaxPool1D
from keras.preprocessing.sequence import pad_sequences  # 序列填充

# 优化器模块
from keras.optimizers import Adam

# 回调函数模块
from keras.callbacks import CSVLogger

# 工具模块
from keras.utils import to_categorical, plot_model, print_summary

import warnings
warnings.filterwarnings("ignore")

# GPU配置
import os
os.environ["CUDA_VISIBLE_DEVICES"] = "0"
from PIL import ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True

# tf2 GPU检测
import tensorflow as tf
gpus = tf.config.list_physical_devices(device_type='GPU')
print("Num GPUs Available: ", len(gpus))

# 全局参数
epochs = 30       # 批次
embedding_size = 128  # 嵌入维度
drop = 0.5        # 丢弃率
sample_size = 1000  # 一共取多少数据集
num_classes = 7    # 分类个数

# 模型目录初始化
import shutil
shutil.rmtree("model", ignore_errors=True)  # 删除test文件夹下所有的文件、文件夹
if not os.path.exists("model"):
    os.mkdir("model")


# -------------------- 文本处理函数 --------------------
def clean_str(string):
    """
    对数据集中的字符串做清洗.
    """
    string = str(string)
    # 去除标点符号
    string = re.sub("[\s+\.\!\/_,$%^*(+\"\';:“”.]+|[+——!,。??、~@#¥%……&*()]+", "", string)
    # 去除网页
    string = re.sub(r"<[^>].*?>|&.*?;", "", string)
    # 去除数字和英文
    string = re.sub("[a-zA-Z0-9]", "", string)
    # 去除非中文字符
    string = re.sub("[^\u4e00-\u9fff]", "", string)
    
    return string.strip().lower()


def get_texts_vector(texts=None, maxlen=None):
    """
    texts: 分好词的文本
    maxlen: 序列的最大长度 默认句子的最大长度 可以自己指定,
    返回texts的 索引填充后的向量 和 分词器 tokenizer(可以获得词汇表)
    
    # 使用
    # 填充序列  分词器  句子长度(预测需要用到)
    # sequences_pad,tokenizer,maxlen = get_texts_vector(texts)
    # word2num,num2word = tokenizer.word_index,tokenizer.index_word
    # 词汇表长度
    # word_num = len(num2word)
    """
    tokenizer = Tokenizer(
        num_words=None,  # 低于该数值 删除
        filters='!"#$%&()*+,-./:;<=>?@[\\]^_`{|}~\t\n',  # 过滤字符
        lower=True,      # 是否小写
        split='',        # 分隔符
        char_level=True, # true 切分字 false 切分词
        oov_token='<UNK/>',  # 未登陆词设置
    ) 

    # 训练分词器
    # texts 可以是[[word,word],,,] 也可以是["word word", "word word" ] 但是分好词
    tokenizer.fit_on_texts(texts)
    word2num = tokenizer.word_index
    num2word = tokenizer.index_word
    
    # 文本的索引向量化 ,可以结合pad_sequences向量化
    texts_vector = tokenizer.texts_to_sequences(texts)
    
    # 如果不传入最大长度,默认句子的长度的最大值
    if not maxlen:
        maxlen = max([len(_) for _ in texts_vector])

    # 序列填充
    sequences_pad = pad_sequences(
        texts_vector,  # 二维列表 [[ num1,num2],,]
        maxlen=maxlen,  # 序列长度 
        padding='post', # 长度低于maxlen时  pre 序列前补齐  post序列后补齐
        truncating='post', # 长度超出maxlen时 pre 序列前截断 post 序列后截断
        value=0.0,      # 补齐的值
        dtype='int32', 
    )
    return sequences_pad, tokenizer, maxlen


# -------------------- 模型构建函数 --------------------
def build_cnn_model():
    # 输入层
    inputs = Input(shape=(maxlen,))
    
    # 嵌入层
    embedding_layer = Embedding(
        word_num,         # 单词维度
        embedding_size,   # embedding_size
        input_length=maxlen  # 文本向量长度
    )(inputs)
    
    # 定义多个卷积核的大小
    filter_sizes = [3, 4, 5]
    num_filters = 64
    
    # 存储所有池化后的特征
    pooled_outputs = []
    
    # 对每个卷积核应用Conv1D和GlobalMaxPooling1D,并存储结果
    for filter_size in filter_sizes:
        conv_layer = Conv1D(filters=num_filters, kernel_size=filter_size, activation='relu')(embedding_layer)
        pooled_layer = GlobalMaxPool1D()(conv_layer)
        pooled_layer = Dropout(drop)(pooled_layer)
        pooled_outputs.append(pooled_layer)

    # 拼接所有池化后的特征
    concatenated = Concatenate()(pooled_outputs)

    # 全连接层
    dense_layer = Dense(units=256, activation='relu')(concatenated)
    dense_layer = Dropout(drop)(dense_layer)

    # 输出层
    output_layer = Dense(units=num_classes, activation='softmax')(dense_layer)
    
    # 构建模型
    model = Model(inputs=inputs, outputs=output_layer)
    return model


# -------------------- 评估与可视化函数 --------------------
# 评估函数
from sklearn.metrics import accuracy_score, f1_score, precision_score, recall_score, classification_report, confusion_matrix

def evaluate_fun(true_y, pred_y):
    """
    评估函数指标
    """
    acc = accuracy_score(true_y, pred_y)
    f1 = f1_score(true_y, pred_y, average="macro")
    pre = precision_score(true_y, pred_y, average="macro")
    recall = recall_score(true_y, pred_y, average="macro")
    return {
        'acc': acc,
        'f1': f1,
        'pre': pre,
        'recall': recall,
    }


def plot_train(history, model_name):
    """
    绘制模型训练过程
    """
    print(history.history.keys())
    
    # 绘制训练Loss曲线
    plt.figure(1, figsize=(12, 6), dpi=100)
    plt.plot(history.history['loss'], label='训练集')
    plt.plot(history.history['val_loss'], label='测试集')
    plt.title(f'{model_name} loss 曲线')
    plt.legend()
    plt.show()

    # 绘制训练acc曲线
    plt.figure(1, figsize=(12, 6), dpi=100)
    plt.plot(history.history['acc'], label='训练集')
    plt.plot(history.history['val_acc'], label='测试集')
    plt.title(f'{model_name} 准确率 曲线')
    plt.legend()
    plt.show()


def plot_confusion_matrix(true_y, pred_y, labels=[0, 1, 2]):
    """
    绘制混淆矩阵 
    依次是真实值  预测值  标签列表(可以为中文 也可以为数字)
    """
    from sklearn.metrics import confusion_matrix
  
    conf_matrix = confusion_matrix(true_y, pred_y, sample_weight=None)

    plt.rcParams['figure.dpi'] = 200 # 分辨率
    plt.imshow(conf_matrix, cmap=plt.cm.Greens)  # 可以改变颜色
    indices = range(conf_matrix.shape[0])
    
    plt.xticks(indices, labels)
    plt.yticks(indices, labels)
    plt.colorbar()
    plt.xlabel('真实值')
    plt.ylabel('预测值')
    
    # 显示数据
    for first_index in range(conf_matrix.shape[0]):  # trues
        for second_index in range(conf_matrix.shape[1]):  # preds
            plt.text(first_index, second_index, conf_matrix[first_index, second_index])
    plt.title(f" 混淆矩阵")
    plt.show()


# -------------------- 数据加载与处理 --------------------
# 从数据集加载数据  不会改变数据集类型
# 最终的特征为 label text
def get_df_from_txt(data_dir):
    """
    从csv获取文本数据
    并且类别转化为数字
    data = get_df_csv('../data/weibo_senti_100k.csv',balance=1000)
    """
    data_dir = "data/Mongolian_Datasets"

    final_df = pd.DataFrame()
    for filename in os.listdir(data_dir):
        label = filename[:-4]  # 情感类别
        # 不是数据集 不要
        file_path = data_dir + '/' + filename
        if not file_path.endswith('.txt'):
            continue
            
        texts = []
        labels = []
        tmp_df = pd.DataFrame()
        
        # 遍历该类别数据集
        with open(file_path, 'r', encoding="utf8") as f:
            for line in f.readlines():
                text = line.strip().split(";")[1:]
                text = ";".join(text)
                texts.append(text)
                labels.append(label)
            tmp_df['text'] = texts
            tmp_df['label'] = labels
        print(f"{label} 数据集长度为:{len(tmp_df)}")
        
        # 合并数据集
        final_df = pd.concat([final_df, tmp_df])
    
    final_df = final_df.rename(columns={"review": 'text'})
    final_df.index = range(len(final_df))
    
    return final_df


# 加载数据
data_dir = "data/Mongolian_Datasets"
data = get_df_from_txt(data_dir)
data.head()

# 标签统一规定都是字符串 
print(data['label'].dtype)
# 这一步知识把数字转化为 中文
data.head()

data.shape

# -------------------- 文本向量化处理 --------------------
# 保存训练的所有信息
allInfo_path = 'model/train_info.pkl'

if os.path.exists(allInfo_path):
    X_train, Y_train, index2label, tokenizer, maxlen = pickle.load(open(allInfo_path, 'rb'))
    word2num, num2word = tokenizer.word_index, tokenizer.index_word
    word_num = len(num2word) + 1  # 必须加1 
else:
    # 数字对应的标签
    from sklearn.preprocessing import LabelEncoder
    le = LabelEncoder()
    
    new_labels = le.fit_transform(data["label"])
    data["label"] = new_labels
    
    index2label = {i: j for i, j in enumerate(le.classes_)}

    # 分词后的文本
    texts = data["text"]
    # 填充序列  分词器  句子长度(预测需要用到)
    sequences_pad, tokenizer, maxlen = get_texts_vector(texts)
    word2num, num2word = tokenizer.word_index, tokenizer.index_word
    
    # 词汇表长度
    word_num = len(num2word) + 1  # 必须加1 
    X_train, Y_train = sequences_pad, data['label']
    pickle.dump([X_train, Y_train, index2label, tokenizer, maxlen], open(allInfo_path, 'wb'))
    
print("文本处理词向量成功!")

# 保存 类别信息  tokenizer 句子的最大长度 词汇表  预测时需要
pickle.dump([index2label, tokenizer, maxlen, word2num], open('model/final_info.pkl', 'wb'))

X_train.shape

# -------------------- 数据集划分 --------------------
from keras.utils import to_categorical
# 转化为类别 为独热编码
y_binary = to_categorical(Y_train, num_classes=num_classes)

from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(X_train, y_binary, test_size=0.2, shuffle=True)
print("x_train 的形状为:", x_train.shape)
print("y_train 的形状为:", y_train.shape)
print("x_test 的形状为:", x_test.shape)
print("y_test 的形状为:", y_test.shape)

# -------------------- 模型训练 --------------------
# 构建模型
model = build_cnn_model()

# 训练
begin_time = time.time()

# 训练轮结果保存到csv文件
csvlogger = CSVLogger(
    filename='logs/training_log.csv',  # 保存文件
    separator=',',    # 分隔符 默认为 , 
    append=False      # true 追加  false 覆盖
)

callbacks_list = [csvlogger, ]

# 优化器
optimizer = Adam(learning_rate=0.001)
model.compile(optimizer=optimizer,
              loss='categorical_crossentropy',  # binary_crossentropy categorical_crossentropy
              metrics=['acc'] 
             )

# 开始训练
history = model.fit(x_train, y_train,
                batch_size=64,
                epochs=epochs,
#                 validation_split=0.2, # 如果没有validation_data 自动从训练集划分测试集
               validation_data=[x_test, y_test],
             callbacks=callbacks_list
    )

# 保存模型
model.save('model/model.h5')  # 创建 HDF5 文件 'my_model.h5'
model.save_weights('model/model_weights.h5')

# -------------------- 结果评估与可视化 --------------------
# 结果可视化
plot_train(history, 'cnn')

# 模型评估
# 输出评价指标
print("f1 recall precision acc 指标:")
print(evaluate_fun(true_y, pred_y))

print("分类报告:")
true_y_ch = [index2label[i] for i in true_y]
pred_y_ch = [index2label[i] for i in pred_y]
print(classification_report(true_y_ch, pred_y_ch))

# -------------------- 文本预测函数 --------------------
import json, re, os, time, random, jieba, pickle, shutil, datetime
from keras.preprocessing.sequence import pad_sequences  # 序列填充
from keras.models import Sequential, Model, save_model, load_model


def predict_text_label(text):
    """
    预测一个文本的类别
    """
    # 加载信息
    num2label, tokenizer, maxlen, word2num = pickle.load(open('model/final_info.pkl', 'rb'))
    # 加载模型
    model = load_model('model/model.h5')

    # 分词
    texts = [text, ]
    text_vector = tokenizer.texts_to_sequences(texts)
    sequences_pad = pad_sequences(
            text_vector,  # 二维列表 [[ num1,num2],,]
            maxlen=maxlen,  # 序列长度 
            padding='post', # 长度低于maxlen时  pre 序列前补齐  post序列后补齐
            truncating='post', # 长度超出maxlen时 pre 序列前截断 post 序列后截断
            value=0.0,      # 补齐的值
            dtype='int32', 
        )
#     label = model.predict_classes(sequences_pad)[0][0]
    label = model.predict(sequences_pad).argmax(axis=1)[0]
    print(label)
#     print(label)
    labelch = num2label[label]
    print(f"预测的文本:{text}")
    print("---"*45)
    print(f"数字标签:{label} , 中文标签:{labelch}")
    print("\n")


# 测试预测函数
text = "ᠬᠤᠳᠠᠯᠳᠤᠭᠰᠠᠨ ᠤ᠋ ᠳᠠᠷᠠᠭᠠᠬᠢ ᠦᠢᠯᠡᠴᠢᠯᠡᠭᠡ ᠬᠤᠳᠠᠯᠳᠤᠨ ᠠᠪᠬᠤ ᠠ᠋ᠴᠠ ᠡᠮᠦᠨ᠎ᠡ ᠬᠤᠳᠠᠯᠳᠤᠨ ᠠᠪᠬᠤ ᠣᠪᠣᠷ ᠨᠢ ᠪᠦᠷ ᠠᠳᠠᠯᠢ ᠦᠭᠡᠢ ᠪᠣᠯᠵᠠᠢ ᠂ ᠰᠡᠭᠦᠯᠡᠷ ᠪᠦᠷ ᠬᠤᠳᠠᠯᠳᠤᠨ ᠠᠪᠬᠤ ᠦᠭᠡᠢ ᠪᠣᠯᠤᠨ᠎ᠠ  "
predict_text_label(text)

2.模型成果

注:模型成果在文章顶部绑定的资源中可见!

        若有任何问题请联系博主!