keras快速上手 ——学习笔记(三)文字情感分析建模

文字情感分析建模

1、词嵌入技术

为了克服文字长短不均和将词与词之间的联系纳入模型中的困难,人们使用了一种技术——词嵌入。简单说来,就是给每个词赋一个向量,向量代表空间里的点,含义接近的词,其向量也接近,这样对于词的操作就可以转化为对于向量的操作了,在深度学习中,这被叫作张量(tensor)。
用张量表示词的好处在于:
第一,可以克服文字长短不均的问题,因为如果每个词已经有对应的词向量,那么对于长度为N的文本,只要选取对应的N个词所代表的向量并按文本中词的先后顺序排在一起,就是输入张量了,其中每个词向量的维度都是一样的。
第二,词本身无法形成特征,但是张量就是抽象的量化,它是通过多层神经网络的层层抽象计算出来的。
第三,文本是由词组成的,文本的特征可以由词的张量组合。文本的张量蕴含了多个词之间的组合含义,这可以被认为是文本的特征工程,进而为机器学习文本分类提供基础。

2、多层全连接神经网络训练情感分析

不同于已经训练好的词向量,Keras提供了设计嵌入层(Embedding Layer)的模板。只要在建模的时候加一行Embedding Layer函数的代码就可以。注意,嵌入层一般是需要通过数据学习的,读者也可以借用已经训练好的嵌入层比如Word2Vec中预训练好的词向量直接放入模型,或者把预训练好的词向量作为嵌入层初始值,进行再训练。Embedding函数定义了嵌入层的框架,其一般有3个变量:字典的长度(即文本中有多少词向量)、词向量的维度和每个文本输入的长度。
注意,前文提到过每个文本可长可短,所以可以采用Padding技术取最长的文本长度作为文本的输入长度,而不足长度的都用空格填满,即把空格当成一个特殊字符处理。空格本身一般也会被赋予词向量,这可以通过机器学习训练出来。Keras提供了sequence.pad_sequences函数帮我们做文本的处理和填充工作。
1、导入模块和数据集

from keras.models import Sequential  # 序列模型
from keras.layers import Dense, Flatten  # 连接层和展开层
from keras.layers.embeddings import Embedding  # 嵌入层
from keras.preprocessing import sequence  # 文本处理和填充
import keras
import numpy as np
from keras.datasets import imdb 
(X_train, Y_train), (X_test, Y_test) = imdb.load_data()

2、数据预处理
1)去除异常的数据:过长的文本

m = max(list(map(len, X_train)), list(map(len, X_test)))
print(m)

2)设定文本长度
考虑到文本的平均长度为230个字符,可以设定最多输入的文本长度为400个字符,不足400个字符的文本用空格填充,超过400个字符的文本截取400个字符,Keras默认截取后400个字符。
这里1代表空格,其索引被认为是0

maxword = 400
X_train = sequence.pad_sequences(X_train, maxword)  # 处理训练集数据为最长400
X_test = sequence.pad_sequences(X_test, maxword)  # 处理测试集数据最长为400
vocab_size = np.max([np.max(X_train[i]) for i in range(X_train.shape[0])]) + 1  

3) 裁剪数据集长度
这里因为博主本身电脑性能问题,对数据进行裁剪以方便训练。原数据集训练集和测试集都是25000,这里改为10:1,一个单位为1000

X_train = X_train[:10000]
X_test = X_test[:1000]
Y_train = Y_train[:10000]
Y_test = Y_test[:1000]

3、构建模型
第一层是嵌入层,定义了嵌入层的矩阵为vocab_size×64。每个训练段落为其中的maxword×64矩阵,作为数据的输入,填入输入层
第二层把输入层压平,原来是maxword×64的矩阵,现在变成一维的长度为maxword×64的向量。
第三层开始不断搭建全连接神经网络,使用relu函数。relu是简单的非线性函数:f(x)=max(0,x)。注意到神经网络的本质是把输入进行非线性变换。
最后一层用Sigmoid,预测0,1变量的概率,类似于logistic regression的链接函数,目的是把线性变成非线性,并把目标值控制在0~1。因此这里计算的是最后输出的是0或者1的概率。
再写入损失函数,打印网络结构
损失函数使用的是binary_crossentropy,是sigmoid函数对应的损失函数。详情可以参考这里
优化器使用adam算法,是momentum算法和RMSprop算法的结合,是一种非常实用的算法,强烈推荐看一下

model = Sequential()  # 建立序列模型
model.add(Embedding(vocab_size, 64, input_length=maxword))  # 嵌入层
model.add(Flatten())  # 展开层
model.add(Dense(2000, activation='relu'))
model.add(Dense(500, activation='relu'))          
model.add(Dense(200, activation='relu'))          
model.add(Dense(50, activation='relu'))          
model.add(Dense(1, activation='sigmoid'))          
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
print(model.summary())

运行结果:

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding_1 (Embedding)      (None, 400, 64)           5669568   
_________________________________________________________________
flatten_1 (Flatten)          (None, 25600)             0         
_________________________________________________________________
dense_1 (Dense)              (None, 2000)              51202000  
_________________________________________________________________
dense_2 (Dense)              (None, 500)               1000500   
_________________________________________________________________
dense_3 (Dense)              (None, 200)               100200    
_________________________________________________________________
dense_4 (Dense)              (None, 50)                10050     
_________________________________________________________________
dense_5 (Dense)              (None, 1)                 51        
=================================================================
Total params: 57,982,369
Trainable params: 57,982,369
Non-trainable params: 0

4、训练模型

model.fit(X_train, Y_train, validation_data = (X_test, Y_test), epochs = 20, batch_size = 100, verbose = 1)

运行结果:

Train on 10000 samples, validate on 1000 samples
Epoch 1/20
10000/10000 [==============================] - 146s 15ms/step - loss: 0.6664 - acc: 0.5717 - val_loss: 0.4426 - val_acc: 0.8040
Epoch 2/20
10000/10000 [==============================] - 147s 15ms/step - loss: 0.1846 - acc: 0.9277 - val_loss: 0.4191 - val_acc: 0.8280
Epoch 3/20
10000/10000 [==============================] - 144s 14ms/step - loss: 0.0070 - acc: 0.9982 - val_loss: 0.6981 - val_acc: 0.8280
Epoch 4/20
10000/10000 [==============================] - 143s 14ms/step - loss: 1.6187e-04 - acc: 1.0000 - val_loss: 0.9345 - val_acc: 0.8200
Epoch 5/20
10000/10000 [==============================] - 144s 14ms/step - loss: 1.7912e-05 - acc: 1.0000 - val_loss: 0.9642 - val_acc: 0.8240
Epoch 6/20
10000/10000 [==============================] - 140s 14ms/step - loss: 7.2919e-06 - acc: 1.0000 - val_loss: 1.0008 - val_acc: 0.8240
Epoch 7/20
10000/10000 [==============================] - 142s 14ms/step - loss: 4.3943e-06 - acc: 1.0000 - val_loss: 1.0327 - val_acc: 0.8260
Epoch 8/20
10000/10000 [==============================] - 145s 14ms/step - loss: 2.9747e-06 - acc: 1.0000 - val_loss: 1.0601 - val_acc: 0.8250
Epoch 9/20
10000/10000 [==============================] - 147s 15ms/step - loss: 2.1566e-06 - acc: 1.0000 - val_loss: 1.0837 - val_acc: 0.8260
Epoch 10/20
10000/10000 [==============================] - 147s 15ms/step - loss: 1.3897e-06 - acc: 1.0000 - val_loss: 1.1297 - val_acc: 0.8270
Epoch 11/20
10000/10000 [==============================] - 144s 14ms/step - loss: 8.2748e-07 - acc: 1.0000 - val_loss: 1.1683 - val_acc: 0.8260
Epoch 12/20
10000/10000 [==============================] - 143s 14ms/step - loss: 5.9825e-07 - acc: 1.0000 - val_loss: 1.1999 - val_acc: 0.8260
Epoch 13/20
10000/10000 [==============================] - 145s 15ms/step - loss: 4.6976e-07 - acc: 1.0000 - val_loss: 1.2243 - val_acc: 0.8270
Epoch 14/20
10000/10000 [==============================] - 147s 15ms/step - loss: 3.8899e-07 - acc: 1.0000 - val_loss: 1.2458 - val_acc: 0.8270
Epoch 15/20
10000/10000 [==============================] - 142s 14ms/step - loss: 3.3313e-07 - acc: 1.0000 - val_loss: 1.2653 - val_acc: 0.8280
Epoch 16/20
10000/10000 [==============================] - 143s 14ms/step - loss: 2.9289e-07 - acc: 1.0000 - val_loss: 1.2816 - val_acc: 0.8280
Epoch 17/20
10000/10000 [==============================] - 142s 14ms/step - loss: 2.6254e-07 - acc: 1.0000 - val_loss: 1.2967 - val_acc: 0.8280
Epoch 18/20
10000/10000 [==============================] - 140s 14ms/step - loss: 2.3922e-07 - acc: 1.0000 - val_loss: 1.3102 - val_acc: 0.8280
Epoch 19/20
10000/10000 [==============================] - 142s 14ms/step - loss: 2.2071e-07 - acc: 1.0000 - val_loss: 1.3234 - val_acc: 0.8280
Epoch 20/20
10000/10000 [==============================] - 144s 14ms/step - loss: 2.0597e-07 - acc: 1.0000 - val_loss: 1.3351 - val_acc: 0.8280

准确率只有82.8%,与书上的85有一定差距,从趋势来看加大数据集合迭代次数可以提升准确率

猜你喜欢

转载自blog.csdn.net/m0_38106113/article/details/81475029
今日推荐