当前位置:   article > 正文

【深度学习从0到1】16.NLP任务项目实战【TextCNN】keras_nlp项目实战

nlp项目实战

TextCNN

【参考:中文情感分析之TextCNN_狮子HH的博客-CSDN博客写得非常不错

TextCNN模型的结构比较简单,由输入表征 --> 卷积层 --> 最大池化 --> 全连接层 --> 输出softmax组成

我们从某电商网站中拿到了50000条评论数据,这些数据中好评和差评数据各占25000条,其中的一条好评数据是“质量好,做工也不错,而且尺码标准。”。拿到这些数据后,我们进行的第一步处理是数据预处理过程,即对这50000条数据中的每一条进行分词、去停用词操作。分词故名思义就是将句子切分成词语,分词后得到“质量 好 , 做工 也 不错 , 而且 尺码 标准”。去停用词是去掉无意义的词语或符号,如‘也’、‘而且’、标点符号等起连接作用的词,去停用词后得到“质量 好 做工 不错 尺码 标准”。经过预处理过程后我们就把原来的中文句子,转化为这样的词语组合,原来的每条句子变成了由多个词组成的词组。

将句子拆成词组之后,需要继续将其转化为方便计算机处理的数字,最直观的想法就是给每个词分一个编号,以编号来代替词语。这个想法是可行的,然而我们也没必要对所有词语进行编号,因为这之间肯定有很多词语只是偶尔出现了一次。所以我们考虑对出现的所有词语进行统计分析,看下各个词语出现的概率,然后取出现概率最高的前n个词语对其进行编号,将词语映射为id。构建了这样一个映射关系后,就可以将各个词组转换为数字列表。“质量 好 做工 不错 尺码 标准”就变成了[15, 20, 210, 3, 337, 241],也就是说“质量”这个词语的编号就是15。这里有个问题,我们在编号过程中丢掉了一些低频词,那这些词要怎么转为数字呢?先不做处理,给他个特殊标记,如OOV。

至此,词组转成了数字列表,但是由于原句子长短不一,转换后的数字列表也长短不齐,由于后续模型的输入需要固定尺寸,因此还需要对数字列表进行补全/截断操作。假设要将数字列表的长度都变为10,补全后得到[15, 20, 210, 3, 337, 241, 0, 0, 0, 0]。

将词语转为数字后,我们就可以来构建TextCNN模型了,模型的第一层是Embedding层,这一层的作用是将词语转换为词向量(word embedding)。我们所拥有的数据量并不大,所以考虑使用预训练好的词向量来进行模型训练。构建Embedding层需要使用之前将词语转数值时的词语和id的映射关系,假设word_index表示word–>id的映射,那么embedding矩阵即为id–>embedding_vector的映射,也就是说embedding矩阵是一个词向量列表,它的第i个元素表示原word_index中id为i的词对应的词向量(embedding矩阵每一行代表一个词语。绕了一圈,做的工作其实就是将单词转为了词向量,只不过因为模型输入需要是数字,所以多做了词语到数值,数值到词向量的转换。如果觉得比较绕,可以简单的将Embedding层理解为将词语转为词向量。(对于之前的标记,由于它不在词向量矩阵中,所以经过该层后以0向量填充,相当于将低频词都映射为0向量了)

假设输入样本长度为10(每个样本即为一个词语),词向量维度为300,经过Embedding层后,输出为10 × 300 的词向量矩阵。至此,我们已经成功地将中文文本转化为规整的m × n矩阵,这样的数据可以作为各种机器学习模型的输入。

在TextCNN模型中,数据经过Embedding层转换后送入卷积层,这里卷积使用一维卷积,这是因为虽然Embedding层输出是一个二维矩阵,但是在词向量维度上的卷积运算是无意义的,因此只进行时间维度上的卷积运算来获取相邻词之间的关系。一维卷积带来的一个问题是,需要设计不同尺寸的滤波器核来捕获不同尺度的信息。直观上看,中文词以2个或4个为一组的比较多,因此考虑使用滤波器核尺寸为2、4和5的(shape:(行数,词向量长度)三种滤波器进行卷积操作,每种滤波器使用128个。10×300的矩阵经过kernal_size为2,4,5的一维卷积操作后分别得到9(10-2+1) × 128(滤波器的个数 = 生成的特征图的个数) 、7 × 128 和6 × 12 。

注意:一维卷积不代表卷积核只有一维,也不代表被卷积的feature也是一维。一维的意思是说卷积的方向是一维的。 上面就是从上往下卷积

数据经过卷积层进行特征映射后,送入时序最大池化层,即一维全局最大池化,该层的主要作用是提取各滤波器输出的主要特征,过滤掉无效数据的影响。经过最大池化处理后,之前处理中的padding补全操作或者无映射关系标记为等处理的数据都会过滤掉。

经过时序最大池化处理后得到的是3组(前面三种滤波器)128维(滤波器个数)的向量,我们将这3组输出合并起来得到384维的向量数据,将其作为全连接层的输入。

全连接层使用一个有两个隐层的神经网络,每层的节点数设置为128,在两层隐层之间加入dropout层用于减轻过拟合。dropout层就是在每次训练过程中按概率随机丢弃一些参数,比如当丢弃率设置为0.5的时候,在每次训练过程中更新隐层权重的时候会以50%的概率对128个节点的权重进行更新,也就是说在每次训练回合中只更新其中的64个节点的参数。dropout层可以有效减轻模型的过拟合程度,使得模型具有较好的泛化性能。

在这里插入图片描述

一维卷积英语电影评论情感分类

导库

from keras.preprocessing import sequence
from keras.models import Sequential
from keras.layers import Dense,Dropout,Embedding
from keras.layers import Conv1D,GlobalMaxPool1D
from keras.datasets import imdb
# from plot_model import plot_model # 这个没用
from keras.utils.vis_utils  import plot_model # 画模型图

import os
# 需要转这个软件 Graphviz
os.environ["PATH"] += os.pathsep + 'C:/Program Files/Graphviz/bin'

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

数据处理

# 最大词汇数量
max_words=10000
# 最长句子设置为400
# 这里句子长度值的是句子词汇数量,句子有100个词则长度为100
max_len=400
# 批次大小
batch_size=32  # 25000 // 32 = 782 一批次有大概782个数据
# 词向量长度
embedding_dims=128
# 训练周期
epochs=3
# 滤波器数量
filter=64
# 卷积核大小 shape(3,词向量的长度) 宽为3的矩形,长为词向量的长度
kernel_size=3

# 载入imdb评论数据集,设置最大词汇数,只保留出现频率最高的前max_words个词
# 出现频率越高,编号越小。词的编号从4开始,也就是频率最大的词编号为4。
# 编号0表示padding,1表示句子的开始(每个句子第一个编号都是1),2表示OOV,3表示预留(所有的数据中都没有3)
# Out-of-vocabulary,简称OOV,表示不在字典中的词
# 数据的标签为0和1。0表示负面情感,1表示正面情感。
(x_train,y_train),(x_test, y_test)=imdb.load_data(
    num_words=max_words)

# 查看测试集第0个句子
print(x_test[0])

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
[1, 591, 202, 14, 31, 6, 717, 10, 10, 2, 2, 5, 4, 360, 7, 4, 177, 5760, 394, 354, 4, 123, 9, 1035, 1035, 1035, 10, 10, 13, 92, 124, 89, 488, 7944, 100, 28, 1668, 14, 31, 23, 27, 7479, 29, 220, 468, 8, 124, 14, 286, 170, 8, 157, 46, 5, 27, 239, 16, 179, 2, 38, 32, 25, 7944, 451, 202, 14, 6, 717]
  • 1

# 获得imdb数据集的字典,字典的键是英语词汇,值是编号
# 注意这个字典的编词汇编号跟数据集中的词汇编号是不对应的
# 数据集中的编号减三才能得到这个字典的编号,(前面写了词的编号从4开始)举个例子:
# 比如在x_train中'a'的编号为6,在word2id中'a'的编号为3
word2id = imdb.get_word_index()

# 把字典的键值对反过来:键是编号,值是英语词汇
# 编号数值范围:0-88587
# value+3把字典中词汇的编号跟x_train和x_test数据中的编号对应起来
id2word = dict([(value+3, key) for (key, value) in word2id.items()])
# 设置预留字符
id2word[3] = '[RESERVE]'
# 设置Out-of-vocabulary字符
id2word[2] = '[OOV]'
# 设置起始字符
id2word[1] = '[START]'
# 设置填充字符
id2word[0] = '[PAD]'

# In[6]:
# 在词典中查询得到原始英语句子,如果编号不在字典用则用'?'替代
decoded_review = ' '.join([id2word.get(i, '?') for i in x_test[0]])
print(decoded_review)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
x_train shape: (25000, 400)
x_test shape: (25000, 400)
[   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    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    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    1  591  202   14
   31    6  717   10   10    2    2    5    4  360    7    4  177 5760
  394  354    4  123    9 1035 1035 1035   10   10   13   92  124   89
  488 7944  100   28 1668   14   31   23   27 7479   29  220  468    8
  124   14  286  170    8  157   46    5   27  239   16  179    2   38
   32   25 7944  451  202   14    6  717]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

构建模型

# 构建模型
model=Sequential()

# Embedding是一个权值矩阵,包含所有词汇的词向量,Embedding的行数等于词汇数max_word,列数等于词向量长度
# Embedding的作用是获得每个词对应的词向量,这里的词向量是没有经过预训练的随机值,会跟随模型一起训练
# max_words词汇数,embedding_dims词向量长度
# 模型训练时数据输入为(batch, maxlen)
model.add(
    Embedding(input_dim=max_words,output_dim=embedding_dims)
)
# 设置一个一维卷积
model.add(
    Conv1D(filters=filter
           ,kernel_size=kernel_size
           ,strides=1
           ,padding='same'  # 卷积后的特征图大小和原始图像大小相同
           ,activation='relu'
           )
)
# 卷积计算后得到的数据为(batch, maxlen, filters)
# GlobalMaxPooling1D-全局最大池化计算每一张特征图的最大值
# 池化后得到(batch, filters)
model.add(GlobalMaxPool1D())
# 加上Dropout
model.add(Dropout(0.5))
# 最后2分类,设置2个神经元
model.add(
    Dense(units=2,activation='softmax')
)
# 模型参数
model.summary()
# 画图
# https://keras.io/zh/utils/#plot_model
plot_model(model=model
           ,to_file='cnn_1d_model.png'
           ,show_shapes=True # 是否显示形状信息
            ,show_layer_names=True # 是否显示图层名称
            ,rankdir="TB" # "TB":垂直图  "LR":水平图
            ,expand_nested=True # 是否将嵌套模型展开为簇。
            ,dpi=96 # 图片每英寸点数。
           ,show_layer_activations=True
)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 embedding (Embedding)       (None, None, 128)         1280000   
                                                                 
 conv1d (Conv1D)             (None, None, 64)          24640     
                                                                 
 global_max_pooling1d (Globa  (None, 64)               0         
 lMaxPooling1D)                                                  
                                                                 
 dropout (Dropout)           (None, 64)                0         
                                                                 
 dense (Dense)               (None, 2)                 130       
                                                                 
=================================================================
Total params: 1,304,770
Trainable params: 1,304,770
Non-trainable params: 0
_________________________________________________________________
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

在这里插入图片描述

# sparse_categorical_crossentropy和categorical_crossentropy都是交叉熵代价函数
# categorical_crossentropy需要把标签变成独热编码one-hot
# sparse_categorical_crossentropy不需要把标签变成独热编码one-hot(不是真的不需要,而且程序中会自动帮你做转换)
# 所以这个程序中的标签没有转独热编码one-hot
model.compile(loss='sparse_categorical_crossentropy',
              optimizer='adam',
              metrics=['accuracy'])
# 训练模型
model.fit(
    x=x_train
    ,y=y_train
    ,batch_size=batch_size
    ,epochs=epochs
    ,validation_data=(x_test,y_test)
)

#%%
# 保存模型
model.save('cnn_1d_model.h5')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
Epoch 1/3
782/782 [==============================] - 45s 57ms/step - loss: 0.4677 - accuracy: 0.7700 - val_loss: 0.3169 - val_accuracy: 0.8680
Epoch 2/3
782/782 [==============================] - 48s 61ms/step - loss: 0.2982 - accuracy: 0.8784 - val_loss: 0.2896 - val_accuracy: 0.8805
Epoch 3/3
782/782 [==============================] - 45s 58ms/step - loss: 0.2178 - accuracy: 0.9146 - val_loss: 0.3051 - val_accuracy: 0.8742
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

预测 待定

二维

在这里插入图片描述
在这里插入图片描述

流程

导入数据
分词
建立词典(过滤一些符号)词:编号 word_index
把前面分的词全部转化为编号
把每段文本转换为序列
??? 还是不是很理解过程
构建训练集
训练

#%%
import jieba
import pandas as pd
import numpy as np
from keras.layers import Dense,Input,Dropout
from keras.layers import Conv2D,GlobalMaxPool2D,Embedding,concatenate
from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequences
from keras.models import Model,load_model
from keras.backend import expand_dims
from keras.layers import Lambda
import keras.backend as K
from sklearn.model_selection import train_test_split
import json

from keras.utils.vis_utils  import plot_model # 画模型图

import os
# 需要转这个软件 Graphviz
os.environ["PATH"] += os.pathsep + 'C:/Program Files/Graphviz/bin'
```pythob
#%%
# 批次大小
batch_size=128
# 训练周期
epochs=3
# 词向量长度
embedding_dims=128
# 滤波器数量
filter=32
# 这个数据前半部分都是正样本,后半部分都是负样本
data=pd.read_csv('weibo_senti_100k.csv')
# 查看数据前5行
data.head()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

在这里插入图片描述

#%%
# 计算正样本数量
poslen = sum(data['label']==1)
# 计算负样本数量
neglen = sum(data['label']==0)
print('正样本数量:', poslen)
print('负样本数量:', neglen)
#%%
# 测试一下结巴分词的使用
print(list(jieba.cut('做父母一定要有刘墉这样的心态,不断地学习,不断地进步')))
#%%
#定义分词函数,对传入的x进行分词
cw = lambda x: list(jieba.cut(x))

# apply传入一个函数,把cw函数应用到data['review']的每一行
# 把分词后的结果保存到data['words']中
data['word']=data['review'].apply(cw)

data.head()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

正样本数量: 59993
负样本数量: 59995
[‘做’, ‘父母’, ‘一定’, ‘要’, ‘有’, ‘刘墉’, ‘这样’, ‘的’, ‘心态’, ‘,’, ‘不断’, ‘地’, ‘学习’, ‘,’, ‘不断’, ‘地’, ‘进步’]
在这里插入图片描述

#%%
# 计算一条数据最多有多少个词汇
max_length=max([len(x) for x in data['word']])
# 打印看到结果为202,最长的句子词汇数不算太多
# 后面就以202作为标准,把所有句子的长度都填充到202的长度
# 比如最长的句子为2000,那么说明有些句子太长了,我们可以设置一个小一点的值作为所有句子的标准长度
# 比如设置1000,那么超过1000的句子只取前面1000个词,不足1000的句子填充到1000的长度
print(max_length) # 202
#%%
# 把data['words']中所有的list都变成字符串格式
# 这里有个空格
texts=[' '.join(x) for x in data['word']]

# 查看一条评论,现在数据变成了字符串格式,并且词与词之间用空格隔开
# 这是为了满足下面数据处理对格式的要求,下面要使用Tokenizer对数据进行处理
print(texts[4]) # 
#%%
# 实例化Tokenizer,设置字典中最大词汇数为30000
# Tokenizer会自动过滤掉一些符号比如:!"#$%&()*+,-./:;<=>?@[\\]^_`{|}~\t\n
tokenizer=Tokenizer(num_words=30000)
# 传入我们的训练数据,建立词典,词的编号根据词频设定,频率越大,编号越小,
tokenizer.fit_on_texts(texts=texts)
# 把词转换为编号,编号大于30000的词会被过滤掉
sequences=tokenizer.texts_to_sequences(texts=texts)
# 把序列设定为max_length的长度,超过max_length的部分舍弃,不到max_length则补0
# padding='pre'在句子前面进行填充,padding='post'在句子后面进行填充
X=pad_sequences(sequences=sequences,maxlen=max_length,padding='pre')

print(X.shape)
print(X[4]) # 梦想 有 多 大 , 舞台 就 有 多 大 ! [ 鼓掌 ]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
(119988, 202)
[   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  581   18   75   77    1
 1946   20   18   75   77   19]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
#%%
# 获取字典
dict_text=tokenizer.word_index
#%%
# 在字典中查询词
print(dict_text['梦想'])
print(dict_text['睡觉'])

# 581
# 352

#%%
# 把token_config保存到json文件中,方便在模型预测阶段使用
file=open('token_config.json','w',encoding='utf-8')
# 把tokenizer变成json数据
token_config=tokenizer.to_json() # tokenizer的一些配置
# 保存json数据
json.dump(obj=token_config,fp=file)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

(119988, 2)
[[0 1]
[0 1]
[0 1]

[1 0]
[1 0]
[1 0]]

#%%
# 定义标签
# 01为正样本,10为负样本
positive_labels=[[0,1] for _ in range(poslen)]
negative_labels=[[1,0] for _ in range(neglen)]
# 合并标签
Y=np.array(positive_labels+negative_labels)

print(Y.shape)
print(Y)
#%%
print(Y[59992]) # 59993个正样本 59995个负样本
print(Y[59993])
print(Y[59994])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
#%%
# 切分数据集
x_train,x_test,y_train,y_test=train_test_split(X,Y,test_size=0.2)

print(x_train.shape)
print(y_train.shape)
print(x_test.shape)
print(y_test.shape)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

(95990, 202)
(95990, 2)
(23998, 202)
(23998, 2)

#%%
# 定义函数式模型

# https://keras.io/zh/getting-started/functional-api-guide/
# 以张量为参数,并且返回一个张量
# 定义模型输入,shape-(batch, max_length=202)
sequences_input=Input(shape=(max_length,))
# Embedding层,30000表示30000个词,每个词对应的向量为128维
# 该层只能用作模型中的第一层 input_dim=词汇表大小 output_dim=词向量的维度
embedding_layer=Embedding(input_dim=30000,output_dim=embedding_dims)
# embedded_sequences的shape-(batch, 202, 128)
embedded_sequences=embedding_layer(sequences_input)

# embedded_sequences的shape变成了(batch, 202, 128, 1)
# -1  指的是最后一个
embedded_sequences=K.expand_dims(x=embedded_sequences,axis=-1)

# 卷积核大小为3,列数必须等于词向量长度
cnn1=Conv2D(filters=filter
            ,kernel_size=(3,embedding_dims)
            ,activation='relu'
            )(embedded_sequences)

cnn1=GlobalMaxPool2D()(cnn1)

# 卷积核大小为4,列数必须等于词向量长度
cnn2=Conv2D(filters=filter
            ,kernel_size=(4,embedding_dims)
            ,activation='relu'
            )(embedded_sequences)

cnn2=GlobalMaxPool2D()(cnn2)

# 卷积核大小为5,列数必须等于词向量长度
cnn3=Conv2D(filters=filter
            ,kernel_size=(5,embedding_dims)
            ,activation='relu'
            )(embedded_sequences)

cnn3=GlobalMaxPool2D()(cnn3)

# 合并
merge=concatenate([cnn1,cnn2,cnn3],axis=-1)
# 全连接层
x=Dense(128,activation='relu')(merge)
# Dropout层
x=Dropout(0.5)(x)
# 输出层
preds=Dense(2,activation='softmax')(x)
# 定义模型
model=Model(sequences_input,preds)
#%%
# 模型参数
model.summary()
# 画图
# https://keras.io/zh/utils/#plot_model
plot_model(model=model
           ,to_file='cnn_2d_model.png'
           ,show_shapes=True # 是否显示形状信息
            ,show_layer_names=True # 是否显示图层名称
            ,rankdir="TB" # "TB":垂直图  "LR":水平图
            ,expand_nested=True # 是否将嵌套模型展开为簇。
            ,dpi=200 # 图片每英寸点数。
           ,show_layer_activations=True
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
Model: "model"
__________________________________________________________________________________________________
 Layer (type)                   Output Shape         Param #     Connected to                     
==================================================================================================
 input_1 (InputLayer)           [(None, 202)]        0           []                               
                                                                                                  
 embedding (Embedding)          (None, 202, 128)     3840000     ['input_1[0][0]']                
                                                                                                  
 tf.expand_dims (TFOpLambda)    (None, 202, 128, 1)  0           ['embedding[0][0]']              
                                                                                                  
 conv2d (Conv2D)                (None, 200, 1, 32)   12320       ['tf.expand_dims[0][0]']         
                                                                                                  
 conv2d_1 (Conv2D)              (None, 199, 1, 32)   16416       ['tf.expand_dims[0][0]']         
                                                                                                  
 conv2d_2 (Conv2D)              (None, 198, 1, 32)   20512       ['tf.expand_dims[0][0]']         
                                                                                                  
 global_max_pooling2d (GlobalMa  (None, 32)          0           ['conv2d[0][0]']                 
 xPooling2D)                                                                                      
                                                                                                  
 global_max_pooling2d_1 (Global  (None, 32)          0           ['conv2d_1[0][0]']               
 MaxPooling2D)                                                                                    
                                                                                                  
 global_max_pooling2d_2 (Global  (None, 32)          0           ['conv2d_2[0][0]']               
 MaxPooling2D)                                                                                    
                                                                                                  
 concatenate (Concatenate)      (None, 96)           0           ['global_max_pooling2d[0][0]',   
                                                                  'global_max_pooling2d_1[0][0]', 
                                                                  'global_max_pooling2d_2[0][0]'] 
                                                                                                  
 dense (Dense)                  (None, 128)          12416       ['concatenate[0][0]']            
                                                                                                  
 dropout (Dropout)              (None, 128)          0           ['dense[0][0]']                  
                                                                                                  
 dense_1 (Dense)                (None, 2)            258         ['dropout[0][0]']                
                                                                                                  
==================================================================================================
Total params: 3,901,922
Trainable params: 3,901,922
Non-trainable params: 0
__________________________________________________________________________________________________
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

在这里插入图片描述

#%%
model.compile(
    loss='categorical_crossentropy'
    ,optimizer='adam'
    ,metrics=['acc']
)

model.fit(x=x_train
          ,y=y_train
          ,batch_size=batch_size
          ,epochs=epochs
          ,validation_data=(x_test,y_test))

model.save('cnn_2d_model.h5')
#%%

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
Epoch 1/3
750/750 [==============================] - 119s 158ms/step - loss: 0.0801 - acc: 0.9677 - val_loss: 0.0448 - val_acc: 0.9813
Epoch 2/3
750/750 [==============================] - 118s 157ms/step - loss: 0.0413 - acc: 0.9819 - val_loss: 0.0511 - val_acc: 0.9812
Epoch 3/3
750/750 [==============================] - 118s 157ms/step - loss: 0.0344 - acc: 0.9828 - val_loss: 0.0668 - val_acc: 0.9797
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

预测

from keras.models import load_model
from keras.preprocessing.text import tokenizer_from_json
from keras.preprocessing.sequence import pad_sequences
import jieba
import numpy as np
import json

json_file=open('token_config.json','r',encoding='utf-8')
token_config=json.load(json_file)
tokenizer=tokenizer_from_json(token_config)

model=load_model('cnn_2d_model.h5')

def predict(text):
    # 对句子分词
    cw=list(jieba.cut(text))# ['今天天气', '真', '好', ',', '我要', '去', '打球']
    # list转字符串,元素之间用' '隔开
    texts=' '.join(cw) # 字符串 '今天天气 真 好 , 我要 去 打球'
    # print([texts])# ['今天天气 真 好 , 我要 去 打球']
    # 把词转换为编号,编号大于30000的词会被过滤掉
    sequences=tokenizer.texts_to_sequences([texts]) # [texts] 是把字符串变成列表
    # model.input_shape为(None, 202),202为训练模型时的序列长度
    # 把序列设定为202的长度,超过202的部分舍弃,不到202则补0
    sequences=pad_sequences(sequences=sequences
                            ,maxlen=model.input_shape[1] # 202
                            ,padding = 'pre')
    # 模型预测 shape(1,2)
    predict_result=model.predict(sequences)
    # 取出predict_result中元素最大值所对应的索引
    result=np.argmax(predict_result)
    if(result==1):
        print('正面情绪')
    else:
        print('负面情绪')

if __name__ == '__main__':
    predict('今天天气真好,我要去打球')
    predict("一大屋子人,结果清早告停水了,我崩溃到现在[抓狂]")
    predict("今天我很高兴")
    # pass
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

正面情绪
负面情绪
正面情绪

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/353330?site
推荐阅读
相关标签
  

闽ICP备14008679号