情感分析是一种自然语言处理问题,其中理解文本并预测潜在意图。
在这篇文章中,您将了解如何使用Keras深度学习库在Python中预测电影评论的积极或消极情绪。
阅读这篇文章后你会知道:
数据集是大型电影评论数据集,通常称为IMDB数据集。
大型电影评论数据集(通常称为IMDB数据集)包含25,000个用于培训的高极移动评论(好的或坏的)以及用于测试的相同数量。问题是确定给定的移动评论是否具有正面或负面情绪。
这些数据由斯坦福大学的研究人员收集并用于论中 [PDF],其中50/50的数据用于培训和测试。准确率达到88.89%。
该数据还被用作2014年底至2015年初举办的名为“ Bag of Words Meets Bags of Popcorn ” 的Kaggle比赛的基础。获胜者达到99%时,准确率达到97%以上
Keras提供对内置IMDB数据集的访问。
keras.datasets.imdb.load_data()允许您以准备好在神经网络和深度学习模型中使用的格式加载数据集。
单词已被整数替换,这些整数表示数据集中单词的绝对流行度。因此,每个评论中的句子由一系列整数组成。
第一次调用imdb.load_data()会将IMDB数据集下载到您的计算机并将其作为32兆字节文件存储在〜/ .keras / datasets / imdb.pkl下的主目录中。
有用的是,imdb.load_data()提供了额外的参数,包括要加载的顶部字的数量(其中具有较低整数的字在返回的数据中标记为零),要跳过的顶部字的数量(以避免“该”的)以及支持的最大评论期限。
让我们加载数据集并计算它的一些属性。我们将首先加载一些库并将整个IMDB数据集作为训练数据集加载。
1
2
3
4
5
6
7
|
import
numpy
from
keras
.
datasets
import
imdb
from
matplotlib
import
pyplot
# load the dataset
(
X_train
,
y_train
)
,
(
X_test
,
y_test
)
=
load_data
(
)
X
=
numpy
.
concatenate
(
(
X_train
,
X_test
)
,
axis
=
0
)
y
=
numpy
.
concatenate
(
(
y_train
,
y_test
)
,
axis
=
0
)
|
接下来,我们可以显示训练数据集的形状。
1
2
3
4
|
# summarize size
print
(
"Training data: "
)
print
(
X
.
shape
)
print
(
y
.
shape
)
|
运行此代码段,我们可以看到有50,000条记录。
1
2
3
|
Training data:
(50000,)
(50000,)
|
我们还可以打印唯一的类值。
1
2
3
|
# Summarize number of classes
print
(
"Classes: "
)
print
(
numpy
.
unique
(
y
)
)
|
我们可以看到,这是一个二元分类问题,在评论中有好的和坏的情绪。
1
2
|
Classes:
[0 1]
|
接下来,我们可以了解数据集中唯一单词的总数。
1
2
3
|
# Summarize number of words
print
(
"Number of words: "
)
print
(
len
(
numpy
.
unique
(
numpy
.
hstack
(
X
)
)
)
)
|
有趣的是,我们可以看到整个数据集中只有不到100,000个单词。
1
2
|
Number of words:
88585
|
最后,我们可以了解平均审核长度。
1
2
3
4
5
6
7
|
# Summarize review length
print
(
"Review length: "
)
result
=
[
len
(
x
)
for
X
in
X
]
print
(
"Mean %.2f words (%f)"
%
(
numpy
.
mean
(
result
)
,
numpy
.
std
(
result
)
)
)
# plot review length
pyplot
.
boxplot
(
result
)
pyplot
.
show
(
)
|
我们可以看到平均评论不到300字,标准差超过200字。
1
2
|
Review length:
Mean 234.76 words (172.911495)
|
我们可以看到一个指数分布的盒子和胡须图,我们可以看到一个指数分布,我们可以覆盖分布的质量,剪切长度为400到500字。
自然语言处理领域的最新突破称为嵌入。
这是一种在高维空间中将单词编码为实值向量的技术,其中单词之间的意义相似性转换为向量空间中的接近度。
离散词被映射到连续数的向量。当使用神经网络处理自然语言问题时这很有用,深度学习模型我们需要数字作为输入。
Keras提供了一种方便的方法,可以将单词的正整数表示转换为的嵌入层单词嵌入。
该层采用定义映射的参数,包括也称为词汇表大小的预期单词的最大数量(例如,将被视为整数的最大整数值)。该图层还允许您为每个单词向量指定维度,称为输出维度。
我们想为IMDB数据集使用单词嵌入表示。
假设我们只对数据集中前5,000个最常用的单词感兴趣。因此我们的词汇量将为5,000。我们可以选择使用32维向量来表示每个单词。最后,我们可以选择将最大审核长度设置为500字,将评论截断时间缩短,并将填充评论缩短为0值。
我们将加载IMDB数据集,如下所示:
1
|
imdb
.
load_data
(
nb_words
=
5000
)
|
然后,我们将使用Keras实用程序使用sequence.pad_sequences()函数将数据集截断或填充到每个观察的长度500。
1
2
|
X_train
=
sequence
.
pad_sequences
(
X_train
,
maxlen
=
500
)
X_test
=
sequence
.
pad_sequences
(
X_test
,
maxlen
=
500
)
|
最后,稍后,我们模型的第一层将是使用Embedding类创建的单词嵌入层,如下所示:
1
|
Embedding
(
5000
,
32
,
input_length
=
500
)
|
对于给定的复习训练或整数格式的测试模式,该第一层的输出将是大小为32×500的矩阵。
现在我们知道如何在Keras中加载IMDB数据集以及如何使用单词嵌入表示,让我们开发并评估一些模型。
我们可以从开发一个具有单个隐藏层的简单多层感知器模型开始。
嵌入表示这个词是一个真正的创新,我们将通过一个相对简单的神经网络展示2011年被认为是世界级的结果。
让我们开始导入的类并为这种模式所要求的功能和初始化随机数生成器的恒定值,以确保我们可以很容易地重现的结果。
1
2
3
4
5
6
7
8
9
10
11
|
# MLP for the IMDB problem
import
numpy
from
keras
.
datasets
import
imdb
from
keras
.
models
import
Sequential
from
keras
.
layers
import
Dense
from
keras
.
layers
import
Flatten
from
keras
.
layers
.
embeddings
import
Embedding
from
keras
.
preprocessing
import
sequence
# fix random seed for reproducibility
seed
=
7
numpy
.
random
.
seed
(
seed
)
|
接下来,我们将加载IMDB数据集。我们将简化数据集,正如单词嵌入一节中所讨论的那样。只会加载前5,000个单词。
我们还将使用50%/ 50%的数据集拆分进行培训和测试。这是一种很好的标准拆分方法。
1
2
3
|
# load the dataset but only keep the top n words, zero the rest
top_words
=
5000
|
我们将以500字为单位限制评论,截断较长评论和零填充较短评论。
1
2
3
|
max_words
=
500
X_train
=
sequence
.
pad_sequences
(
X_train
,
maxlen
=
max_words
)
X_test
=
sequence
.
pad_sequences
(
X_test
,
maxlen
=
max_words
)
|
现在我们可以创建我们的模型。我们将使用嵌入层作为输入层,将词汇表设置为5,000,将字矢量大小设置为32维,将input_length设置为500.第一层的输出将是32×500大小的矩阵,如上一节所述。
我们将嵌入层输出展平为一维,然后使用一个250单位的密集隐藏层和整流器激活功能。输出层有一个神经元,并将使用sigmoid激活输出0和1的值作为预测。
该模型使用对数损失,并使用有效的ADAM优化程序进行优化。
1
2
3
4
5
6
7
8
|
# create the model
model
=
Sequential
(
)
model
.
add
(
Embedding
(
top_words
,
nput_length
=
max_words
)
)
model
.
add
(
Flatten
(
)
)
model
.
add
(
Dense
(
250
,
activation
=
'relu'
)
)
model
.
add
(
Dense
(
1
,
activation
=
'sigmoid'
)
)
model
.
compile
(
loss
=
'binary_crossentropy'
,
optimizer
=
'adam'
,
metrics
=
[
'accuracy'
]
)
print
(
model
.
summary
(
)
)
|
我们可以适应模型并在训练时使用测试集作为验证。这个模型非常快速,因此我们将使用很少的训练时期,在这种情况下只有2个。
有很多数据,所以我们将使用128的批量大小。在训练模型后,我们评估其在测试数据集上的准确性。
1
2
3
4
5
|
# Fit the model
model
.
fit
(
X_train
,
y_train
,
validation_data
=
(
X_test
,
y_test
)
,
epochs
=
2
,
batch_size
=
128
,
verbose
=
2
)
# Final evaluation of the model
scores
=
model
.
evaluate
(
X_text
,
y_test
,
verbose
=
0
)
print
(
"Accuracy: %.2f%%"
%
(
scores
[
1
]
*
100
)
)
|
运行此示例适合模型并总结估计的性能。我们可以看到,这个非常简单的模型获得了近86.94%的分数,这是在原始论文的附近,只需很少的努力。
1
2
3
4
5
6
|
Train on 25000 samples, validate on 25000 samples
Epoch 1/2
39s - loss: 0.5160 - acc: 0.7040 - val_loss: 0.2982 - val_acc: 0.8716
Epoch 2/2
37s - loss: 0.1897 - acc: 0.9266 - val_loss: 0.3143 - val_acc: 0.8694
Accuracy: 86.94%
|
我确信如果我们训练这个网络,我们可以做得更好,可能使用更大的嵌入并添加更多的隐藏层。我们尝试不同的网络类型。
设计卷积神经网络以尊重图像数据中的空间结构,同时对场景中学习对象的位置和方向具有鲁棒性。
该相同原理可用于序列,例如电影评论中的一维单词序列。使CNN模型对学习识别图像中的对象具有吸引力的相同属性可以帮助学习单词段落中的结构,即对特征的特定位置的技术不变性。
Keras分别支持Conv1D和MaxPooling1D类的一维卷积和池化。
再次,让我们导入此示例所需的类和函数,并将随机数生成器初始化为常量值,以便我们可以轻松地重现结果。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
# CNN for the IMDB problem
import
numpy
from
keras
.
datasets
import
imdb
from
keras
.
models
import
Sequential
from
keras
.
layers
import
Dense
from
keras
.
layers
import
Flatten
from
keras
.
layers
.
convolutional
import
Conv1D
from
keras
.
layers
.
convolutional
import
MaxPooling1D
from
keras
.
layers
.
embeddings
import
Embedding
from
keras
.
preprocessing
import
sequence
# fix random seed for reproducibility
seed
=
7
numpy
.
random
.
seed
(
seed
)
|
我们也可以像以前一样加载和准备我们的IMDB数据集。
1
2
3
4
5
6
7
|
# load the dataset but only keep the top n words, zero the rest
top_words
=
5000
(
X_train
,
y_train
)
,
(
X_test
,
y_test
)
=
load_data
(
num_words
=
top_words
)
# pad dataset to a maximum review length in words
max_words
=
500
X_train
=
sequence
.
pad_sequences
(
X_train
,
maxlen
=
max_words
)
X_test
=
sequence
.
pad_sequences
(
X_test
,
maxlen
=
max_words
)
|
我们现在可以定义我们的卷积神经网络模型。这次,在嵌入输入层之后,我们插入一个Conv1D层。该卷积层具有32个特征映射,并且一次读取嵌入的单词表示3个向量元素。
卷积层之后是1D max pooling layer,其长度和步幅为2,使卷积层的特征映射的大小减半。网络的其余部分与上面的神经网络相同。
1
2
3
4
5
6
7
8
9
10
|
# create the model
model
=
Sequential
(
)
model
.
add
(
Embedding
(
top_words
,
input_length
=
max_words
)
)
model
.
add
(
Conv1D
(
filters
=
32
,
padding
=
'same'
,
activation
=
'relu'
)
)
model
.
add
(
MaxPooling1D
(
pool_size
=
2
)
)
model
.
add
(
Flatten
(
)
)
model
.
add
(
Dense
(
250
,
activation
=
'relu'
)
)
model
.
add
(
Dense
(
1
,
activation
=
'sigmoid'
)
)
model
.
compile
(
loss
=
'binary_crossentropy'
,
optimizer
=
'adam'
,
metrics
=
[
'accuracy'
]
)
print
(
model
.
summary
(
)
)
|
我们也像以前一样适应网络。
1
2
3
4
5
|
# Fit the model
model
.
fit
(
X_train
,
y_train
,
validation_data
=
(
X_test
,
y_test
)
,
epochs
=
2
,
batch_size
=
128
,
verbose
=
2
)
# Final evaluation of the model
scores
=
model
.
evaluate
(
X_test
,
y_test
,
verbose
=
0
)
print
(
"Accuracy: %.2f%%"
%
(
scores
[
1
]
*
100
)
)
|
运行该示例,我们首先会看到网络结构的摘要。我们可以看到我们的卷积层保留了32维输入的嵌入输入层的维度,最多500个字。池化层通过将其减半来压缩此表示。
运行该示例对上述神经网络模型提供了一个小但令人欢迎的改进,准确率接近87.79%。
1
2
3
4
5
6
|
Train on 25000 samples, validate on 25000 samples
Epoch 1/2
38s - loss: 0.4451 - acc: 0.7640 - val_loss: 0.3107 - val_acc: 0.8660
Epoch 2/2
39s - loss: 0.2373 - acc: 0.9064 - val_loss: 0.2909 - val_acc: 0.8779
Accuracy: 87.79%
|
同样,存在进一步优化的大量机会,例如使用更深和/或更大的卷积层。一个有趣的想法是将最大池层设置为使用500的输入长度。这会将每个要素图压缩为单个32长度向量,并可以提高性能。