二分类问题可能是应用最广泛的机器学习问题。今天我们将学习根据电影评论的文字内容将其划分为正面或负面。
一、数据集来源
我们使用的是IMDB数据集,它包含来自互联网电影数据库(IMDB)的50000条严重两极分化的评论。为了避免模型过拟合只记住训练数据,我们将数据集分为用于训练的25000条评论与用于测试的25000条评论,训练集和测试集都包含50%的正面评论和50%的负面评论。
与MNIST数据集一样,IMDB数据集也内置于Keras库。它已经过预处理:评论(单词序列)已经被转换为整数序列,其中每个整数代表字典中的某个单词。
通过以下代码加载数据集并限制每条评论最多取前一万个常用的word,以便于我们进行向量处理。
import tensorflow as tf imdb = tf.keras.datasets.imdb (train_data, train_labels),(test_data, test_labels) = imdb.load_data(num_words=10000) print(train_data[0]) print(train_labels[0])
通过输出可以看到,train_data和test_data是评论记录的集合,每条评论记录又是由众多的单词索引组成的集合。
train_labels和test_labels是针对评论的分类的集合,其中0表示负面评论,1表示正面评论。
[1, 14, 22, 16, 43, 530, 973, 1622, 1385, 65, 458, 4468, 66, 3941, 4, 173, 36, 256, 5, 25, 100, 43, 838, 112, 50, 670, 2, 9, 35, 480, 284, 5, 150, 4, 172, 112, 167, 2, 336, 385, 39, 4, 172, 4536, 1111, 17, 546, 38, 13, 447, 4, 192, 50, 16, 6, 147, 2025, 19, 14, 22, 4, 1920, 4613, 469, 4, 22, 71, 87, 12, 16, 43, 530, 38, 76, 15, 13, 1247, 4, 22, 17, 515, 17, 12, 16, 626, 18, 2, 5, 62, 386, 12, 8, 316, 8, 106, 5, 4, 2223, 5244, 16, 480, 66, 3785, 33, 4, 130, 12, 16, 38, 619, 5, 25, 124, 51, 36, 135, 48, 25, 1415, 33, 6, 22, 12, 215, 28, 77, 52, 5, 14, 407, 16, 82, 2, 8, 4, 107, 117, 5952, 15, 256, 4, 2, 7, 3766, 5, 723, 36, 71, 43, 530, 476, 26, 400, 317, 46, 7, 4, 2, 1029, 13, 104, 88, 4, 381, 15, 297, 98, 32, 2071, 56, 26, 141, 6, 194, 7486, 18, 4, 226, 22, 21, 134, 476, 26, 480, 5, 144, 30, 5535, 18, 51, 36, 28, 224, 92, 25, 104, 4, 226, 65, 16, 38, 1334, 88, 12, 16, 283, 5, 16, 4472, 113, 103, 32, 15, 16, 5345, 19, 178, 32] 1
我们可以通过word与编号的映射关系将评论的内容转化为具体的文本
def get_text(comment_num): """将数字形式的评论转化为文本""" # word_index = tf.keras.datasets.imdb.get_word_index() word_index = imdb.get_word_index() reverse_word_index = dict([(value, key) for (key, value) in word_index.items()]) text = ' '.join([reverse_word_index.get(i - 3, '?') for i in comment_num]) return text comment = get_text(train_data[0]) print(comment)
第一条电影评论的内容
? this film was just brilliant casting location scenery story direction everyone's really suited the part they played and you could just imagine being there robert ? is an amazing actor and now the same being director ?
二、格式化输入数据
由于我们无法直接将整数序列输入神经网络,所以需要将其转换为张量。可以通过以下两种方式进行转化
填充列表,使其具有相同的长度,然后将列表转化为(samples, word_index)的2D形状的整数张量。对列表进行one-hot编码,将其转化为0和1组成的向量。
这里我们采用one-hot进行编码处理
def vectorize_sequences(sequences, diamension = 10000): results = np.zeros((len(sequences), diamension)) for i, sequence in enumerate(sequences): results[i, sequence] = 1 return results x_train = vectorize_sequences(train_data) print(x_train[0]) print(len(x_train[0])) x_test = vectorize_sequences(test_data) print(x_test[0]) print(len(x_test[0]))
转化完成的输入结果
[0. 1. 1. ... 0. 0. 0.]
10000
[0. 1. 1. ... 0. 0. 0.]
将标签进行向量化处理
y_train = np.asarray(train_labels).astype('float32') y_test = np.asarray(test_labels).astype('float32')
三、构建神经网络
针对这里二分类单标签,我们可以直接使用带有relu激活函数的全连接层的简单堆叠。
我们使用了两个具有16个隐藏单元的中间层和具有一个隐藏单元的层。中间层使用的relu激活函数负责将所有的负值归零,最后一层使用sigmoid函数将任意值压缩到[0,1]之间并作为预测结果的概率。
model = models.Sequential() model.add(layers.Dense(16, activation='relu', input_shape=(10000,))) model.add(layers.Dense(16, activation='relu')) model.add(layers.Dense(1, activation='sigmoid'))
这里的Dense层实现了如下的张量计算,传入Dense层的参数16表示隐藏单元的个数,同时也表示这个层输出的数据的维度数量。隐藏单元越多,网络越能够学习到更加复杂的表示,但是网络计算的代价就越高。
output = relu(dot(W, input) + b)
我们使用rmsprop优化器和binary_crossentropy损失函数来配置模型。
model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['accuracy'])
四、训练模型
将训练数据分出一小部分作为校验数据,同时将512个样本作为一批量处理,并进行20轮的训练,同时出入validation_data来监控校验样本上的损失和计算精度。
x_val = x_train[:10000] partial_x_train = x_train[10000:] y_val = y_train[:10000] partial_y_train = y_train[10000:] history = model.fit(partial_x_train, partial_y_train, epochs= 20, batch_size=512, validation_data=(x_val, y_val))
调用fit()返回的history对象包含训练过程的所有数据
history_dict = history.history print(history_dict.keys())
字典中包含4个条目,对应训练过程和校验过程的指标,其中loss是训练过程中损失指标,accuracy是训练过程的准确性指标,而val_loss是校验过程的损失指标,val_accuracy是校验过程的准确性指标。
dict_keys(['loss', 'accuracy', 'val_loss', 'val_accuracy'])
我们使用Matplotlib画出训练损失和校验损失的情况
loss_values = history_dict['loss'] val_loss_values = history_dict['val_loss'] epochs = range(1, len(loss_values) + 1) plt.plot(epochs, loss_values, 'bo', label='Training loss') plt.plot(epochs, val_loss_values, 'b', label='Validation loss') plt.title('Training and validation loss') plt.xlabel('Epochs') plt.ylabel('Loss') plt.legend() plt.show()
从图中可以看到整个训练过程,损失函数值一直在不断的变小,但是校验过程的损失函数值却先变小后变大,在2.5-5之间的某个点达到最小值。
我们使用Matplotlib画出训练精度和校验精度的情况
plt.clf() acc = history_dict['accuracy'] val_acc = history_dict['val_accuracy'] plt.plot(epochs, acc, 'bo', label='Training accuracy') plt.plot(epochs, val_acc, 'b', label='Validation accuracy') plt.title('Training and validation accuracy') plt.xlabel('Epochs') plt.ylabel('Accuracy') plt.legend() plt.show()
从图中可以看到整个训练过程,准确度值一直在不断的升高,但是校验过程的精度数值却在不断的进行波动,在2.5-5之间的某个点达到最大值。
通过对训练和校验指标的分析,可以看到训练的损失每轮都在降低,训练的精度每轮都在提升。但是校验损失和校验精度基本上在第4轮左右达到最佳值。为了防止这种过拟合的情况,我们可以在第四轮完成之后直接停止训练。
history = model.fit(partial_x_train, partial_y_train, epochs= 4, batch_size=512, validation_data=(x_val, y_val)) results = model.evaluate(x_test, y_test) print(results)
重新执行可以看到模型的精度可以达到87%
782/782 [==============================] - 1s 876us/step - loss: 0.3137 - accuracy: 0.8729 [0.3137112557888031, 0.8728799819946289]
五、使用测试数据预测结果
使用训练的模型对test数据集进行预测
result = model.predict(x_test) print(result)
[[0.31683978]
[0.9997941 ]
[0.9842608 ]
...
[0.18170357]
[0.23360077]
六、小结
- 需要对原始数据进行预处理并转化为符合要求的张量。
- 对于二分类问题,最后一层使用sigmoid作为激活函数,并输出0-1的标量来表示结果出现的概率。
- 对于二分类问题的sigmoid标量输出,应该使用binary_crossentropy损失函数。
- 随着训练过程的进行,很容易出现过拟合现象,我们需要时刻监控模型在非训练数据集的表现。
到此这篇关于机器深度学习之电影的二分类情感问题的文章就介绍到这了,更多相关深度学习内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!