这段时间在学习TensorFlow,这些都是一些官网上的例子,在这里和大家分享记录一下。
出自:https://www.tensorflow.org/tutorials/keras/basic_text_classification
此教程使用评论文本将电影评论分类为正面或负面。这是二元或两类分类的一个例子,这是一种重要且广泛适用的机器学习问题。
我们将使用包含来自Internet电影数据库的50,000条电影评论文本的IMDB数据集。这些分为25,000条培训评论和25,000条评审评论。训练集和测试集包含相同数量的正面和负面评论。
教程使用tf.keras,一个高级API,用于在TensorFlow中构建和训练模型。有关使用更高级的文本分类教程tf.keras,请参阅MLCC文本分类指南。
IMDB数据集与TensorFlow一起打包。它已经被预处理,使得评论(单词序列)已经被转换为整数序列,其中每个整数表示字典中的特定单词。
以下代码将IMDB数据集下载到您的计算机(如果您已经下载了它,则会自动使用缓存副本):
imdb = keras.datasets.imdb
(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)
该参数num_words=10000保留了训练数据中最常出现的10,000个单词。丢弃罕见的单词以保持数据的大小可管理。
我们花一点时间来了解数据的格式。数据集经过预处理:每个示例都是一个整数数组,表示电影评论的单词。每个标签都是0或1的整数值,其中0表示负面评论,1表示正面评论。
print("Training entries: {}, labels: {}".format(len(train_data), len(train_labels)))
输出:
Training entries: 25000, labels: :25000
评论文本已转换为整数(在机器学习处理文本中,一般都会根据语料建立一个字库,用词语所在序号来替换掉语料中的文本
),其中每个整数表示字典中的特定单词。以下是第一篇评论的内容:
print(train_data[0])
输出:
[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,11920,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,10,10,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]
电影评论的长度可能不同。以下代码显示了第一次和第二次评论中的字数。由于对神经网络的输入必须是相同的长度,我们稍后需要解决此问题。
len(train_data[0]), len(train_data[1])
输出:
(218,189)
了解如何将整数转换回文本可能很有用。在这里,我们将创建一个辅助函数来查询包含整数到字符串映射的字典对象:
# A dictionary mapping words to an integer index
word_index = imdb.get_word_index()
# The first indices are reserved
word_index = {k:(v+3) for k,v in word_index.items()}
word_index[""] = 0
word_index[""] = 1
word_index[""] = 2 # unknown
word_index[""] = 3
reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])
def decode_review(text):
return ' '.join([reverse_word_index.get(i, '?') for i in text])
现在我们可以使用该decode_review函数显示第一次审核的文本:
decode_review(train_data[0])
输出:
" 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 father came from the same scottish island as myself so i loved the fact
there was a real connection with this film the witty remarks throughout the film were great it was just brilliant so much that i bought the film as soon as it was released for and would recommend it to everyone to watch and the fly fishing was amazing really cried at the end it was
so sad and you know what they say if you cry at a film it must have been good and this definitely was also to the two little boy's that played the of norman and paul they were just brilliant
children are often left out of the list i think because the stars that play them all grown up are such a big profile for the whole film but these children are amazing and should be praised for what they have done don't you think the whole story was so lovely because it was true and was
someone's life after all that was shared with us all"
reviews—the arrays of integers 必须在输入神经网络之前转换为张量。这种转换可以通过以下两种方式完成:
对数组进行单热编码,将其转换为0和1的向量。例如,序列[3,5]将成为10,000维向量,除了索引3和5(其为1)之外全部为零。然后,将其作为我们网络中的第一层 - 一个可以处理浮点矢量数据的Dense层。然而,这种方法是存储器密集型的,需要num_words * num_reviews大小矩阵。
或者,我们可以填充数组,使它们都具有相同的长度,然后创建一个整数张量的形状max_length * num_reviews。我们可以使用能够处理这种形状的嵌入层作为我们网络中的第一层。
在本教程中,我们将使用第二种方法。
由于电影评论的长度必须相同,我们将使用pad_sequences
函数来标准化长度:
train_data = keras.preprocessing.sequence.pad_sequences(train_data,
value=word_index[""],
padding='post',
maxlen=256)
test_data = keras.preprocessing.sequence.pad_sequences(test_data,
value=word_index[""],
padding='post',
maxlen=256)
这里介绍一下pad_sequences函数
tf.keras.preprocessing.sequence.pad_sequences(
sequences,
maxlen=None,
dtype=‘int32’,
padding=‘pre’,
truncating=‘pre’,
value=0.0)
此函数将 num_samples序列列表(整数列表)转换为2D Numpy形状数组(num_samples, num_timesteps)。 num_timesteps是maxlen提供的参数,或者是最长序列的长度。
短于的序列在末尾num_timesteps 填充value。
序列长于num_timesteps截短的序列,以便它们符合所需的长度。填充或截断发生的位置分别由参数padding和truncating。
预填充是默认值。
参数:
1、sequences:列表列表,其中每个元素都是一个序列。
2、maxlen:Int,所有序列的最大长度。
3、dtype:输出序列的类型。
4、padding:String,‘pre’或’post’:在每个序列之前或之后填充。
5、truncating:String,‘pre’或’post’:从序列maxlen的开头或结尾处大于序列的值中删除值 。
6、value:浮点数,填充值。
返回:
x:Numpy数组形状 (len(sequences), maxlen)
我们现在看一下示例的长度:
len(train_data[0]), len(train_data[1])
输出:
(256,256)
并检查(已填充)审查:
[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 8 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 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 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?让我们为这个问题建立一个模型:
# input shape is the vocabulary count used for the movie reviews (10,000 words)
vocab_size = 10000
model = keras.Sequential()
model.add(keras.layers.Embedding(vocab_size, 16))
model.add(keras.layers.GlobalAveragePooling1D())
model.add(keras.layers.Dense(16, activation=tf.nn.relu))
model.add(keras.layers.Dense(1, activation=tf.nn.sigmoid))
model.summary()
输出:
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding_1 (Embedding) (None, None, 16) 160000
_________________________________________________________________
global_average_pooling1d_1 ( (None, 16) 0
_________________________________________________________________
dense_2 (Dense) (None, 16) 272
_________________________________________________________________
dense_3 (Dense) (None, 1) 17
=================================================================
Total params: 160,289
Trainable params: 160,289
Non-trainable params: 0
_________________________________________________________________
这4个图层按顺序堆叠以构建分类器:
这里说一下第一层的keras.layers.embeddings.Embedding
参数
输入shape
输出shape
例子
model = Sequential()
model.add(Embedding(1000, 64, input_length=10))
# the model will take as input an integer matrix of size (batch, input_length).
# the largest integer (i.e. word index) in the input should be no larger than 999 (vocabulary size).
# now model.output_shape == (None, 10, 64), where None is the batch dimension.
input_array = np.random.randint(1000, size=(32, 10))
model.compile('rmsprop', 'mse')
output_array = model.predict(input_array)
assert output_array.shape == (32, 10, 64)
上述模型在输入和输出之间有两个中间或“隐藏”层。输出的数量(单位,节点或神经元)是图层的表示空间的维度。换句话说,在学习内部表示时允许网络的自由度。
如果模型具有更多隐藏单元(更高维度的表示空间)或更多层,则网络可以学习更复杂的表示。但是,它使网络的计算成本更高,并且可能导致学习不需要的patterns—patterns,这些patterns可以提高训练数据的性能,但不会提高测试数据的性能。这称为过度拟合,我们稍后会进行探讨。
模型需要一个损失函数和一个用于训练的优化器。由于这是二元分类问题和模型输出概率(具有S形激活的单个单元层),我们将使用binary_crossentropy
损失函数。
这不是损失函数的唯一选择,例如,您可以选择mean_squared_error
。但是,通常,binary_crossentropy
处理概率更好 - 它测量概率分布之间的“距离”,或者在我们的情况下,测量地面实况分布和预测之间的“距离”。
后来,当我们探索回归问题(比如预测房子的价格)时,我们将看到如何使用另一种称为均方误差的损失函数。
现在,配置模型以使用优化器和损失函数:
model.compile(optimizer=tf.train.AdamOptimizer(),
loss='binary_crossentropy',
metrics=['accuracy'])
在训练时,我们想要检查模型在以前没有见过的数据上的准确性。通过从原始训练数据中分离10,000个示例来创建验证集。(为什么不立即使用测试集?我们的目标是仅使用训练数据开发和调整我们的模型,然后仅使用测试数据来评估我们的准确性)。
x_val = train_data[:10000]
partial_x_train = train_data[10000:]
y_val = train_labels[:10000]
partial_y_train = train_labels[10000:]
其实我们也可以直接跳过这一步,直接在训练方法中用validation_split=0.2,
进行切分
以512个样本的小批量训练模型40个时期。这是对张量x_train和y_train张量中的所有样本的40次迭代。在培训期间,监控模型在验证集中的10,000个样本的损失和准确性:
history = model.fit(partial_x_train,
partial_y_train,
epochs=40,
batch_size=512,
validation_split=0.2,
verbose=1)
输出:
Train on 12000 samples, validate on 3000 samples
Epoch 1/40
12000/12000 [==============================] - 3s 279us/step - loss: 0.7570 - acc: 0.4969 - val_loss: 0.6953 - val_acc: 0.4947
Epoch 2/40
12000/12000 [==============================] - 1s 121us/step - loss: 0.6910 - acc: 0.5222 - val_loss: 0.6908 - val_acc: 0.5183
Epoch 3/40
12000/12000 [==============================] - 1s 123us/step - loss: 0.6892 - acc: 0.5382 - val_loss: 0.6878 - val_acc: 0.5800
让我们看看模型的表现。将返回两个值。损失率(代表我们的错误的数字,更低的值更好)和准确性。
results = model.evaluate(test_data, test_labels)
print(results)
输出:
25000/25000 [==============================] - 1s 31us/step
[0.32530811003684995, 0.8664]
这种相当天真的方法可以达到约87%的准确度。采用更先进的方法,模型应该接近95%。
model.fit()
返回一个History
包含字典的对象,其中包含训练期间发生的所有事情:
history_dict = history.history
# history_dict.keys()
# dict_keys(['val_acc','val_loss','acc','loss'])
有四个条目:在培训和验证期间,每个条目对应一个受监控的指标。我们可以使用这些来绘制训练和验证损失以进行比较,以及培训和验证准确性:
import matplotlib.pyplot as plt
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(acc) + 1)
# "bo" is for "blue dot"
plt.plot(epochs, loss, 'bo', label='Training loss')
# b is for "solid blue line"
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()
第二个准确性图表:
plt.clf() # clear figure
acc_values = history_dict['acc']
val_acc_values = history_dict['val_acc']
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()
plt.show()
在这2张图中,点表示训练损失和准确度,实线表示验证损失和准确度。
请注意,训练损失随着每个时期而减少,并且训练准确度随着每个时期而增加。这在使用梯度下降优化时是预期的 - 它应该在每次迭代时最小化期望的数量。
这不是验证损失和准确性的情况 - 它们似乎在大约二十个时代之后达到峰值。这是过度拟合的一个例子:模型在训练数据上的表现比在以前从未见过的数据上表现得更好。在此之后,模型过度优化并学习特定于训练数据的表示,这些表示不会推广到测试数据
。
对于这种特殊情况,我们可以通过在二十个左右的时期之后停止训练来防止过度拟合。
附上完整代码:
# -*- coding: utf-8 -*-
"""
Created on Tue Sep 18 09:42:55 2018
@author: chenyang
"""
# TensorFlow and tf.keras
import tensorflow as tf
from tensorflow import keras
# Helper libraries
import numpy as np
import matplotlib.pyplot as plt
print(tf.__version__)
def decode_review(text):
return ' '.join([reverse_word_index.get(i, '?') for i in text])
if __name__ == '__main__':
# tf中封装了IMDB数据集
imdb = keras.datasets.imdb
#加载数据
(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)
# print("Training entries: {}, labels: {}".format(len(train_data), len(train_labels)))
# 把整数转换回单词
# A dictionary mapping words to an integer index
word_index = imdb.get_word_index()
# The first indices are reserved
word_index = {k:(v+3) for k,v in word_index.items()}
word_index[""] = 0
word_index[""] = 1
word_index[""] = 2 # unknown
word_index[""] = 3
reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])
# 数据处理
# 可以填充数组,使它们都具有相同的长度,然后创建一个整数张量的形状max_length * num_reviews
train_data = keras.preprocessing.sequence.pad_sequences(train_data,
value=word_index[""],
padding='post',
maxlen=256)
test_data = keras.preprocessing.sequence.pad_sequences(test_data,
value=word_index[""],
padding='post',
maxlen=256)
# 建立模型
vocab_size = 10000
model = keras.Sequential()
# 嵌入层
model.add(keras.layers.Embedding(vocab_size, 16))
# 池化层
model.add(keras.layers.GlobalAveragePooling1D())
model.add(keras.layers.Dense(16, activation=tf.nn.relu))
model.add(keras.layers.Dense(1, activation=tf.nn.sigmoid))
model.summary()
# 配置costfunction 和优化器、度量标准
model.compile(optimizer=tf.train.AdamOptimizer(),
loss='binary_crossentropy',
metrics=['accuracy'])
# 创建验证集
x_val = train_data[:10000]
partial_x_train = train_data[10000:]
y_val = train_labels[:10000]
partial_y_train = train_labels[10000:]
history = model.fit(partial_x_train,
partial_y_train,
epochs=40,
batch_size=512,
validation_split=0.2,
verbose=1)
# 模型评估
results = model.evaluate(test_data, test_labels)
print(results)
# 创建一段时间内准确性和损失的图表
# dict_keys(['val_acc','val_loss','acc','loss'])
history_dict = history.history
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(acc) + 1)
# "bo" is for "blue dot"
plt.plot(epochs, loss, 'bo', label='Training loss')
# b is for "solid blue line"
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()
# 第二张图表
plt.clf() # clear figure
acc_values = history_dict['acc']
val_acc_values = history_dict['val_acc']
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()
plt.show()