学习记录
基于Tensorflow的教程
一 电影评论文本分类
① tf.keras.layers.Embedding
1 embedding的input_dim和output_dim、input_length参数
Emdedding方法的参数解释:
-
batch_size不需要多说,所有设计它的计算都从“加速模型参数更新”的角度思考。
-
input_dim:它的值代表一个界限,一个输入矩阵[batch_size, input_length]中的值不能超越的界限。也就是说该输入矩阵中的数字都是处于(0, input_dim)之间的。另外,其在自然语言建模中时常代表vocabulary size。
-
output_dim:该参数的理解要从Embedding方法的作用角度理解:Turns positive integers (indexes) into dense vectors of fixed size——将正整数转换为固定size大小的稠密向量,其中,该固定size即是output_dim的值;稠密向量的概念是相对于one-hot编码的非1即0的稀疏向量形式,稠密向量中各个位置的值是可训练的参数。因此说,output_dim是Embedding价值的关键。
-
input_length:视情况,本人未深究。
Embdding方法于自然语言模型中的现实意义: -
inputs层只需要一个一维的数组即可,其维度大小代表input_length,即每段评论希望用多少维度的向量来表示,其中input_length即是该向量的维度,同时它也可称为每段平均的特征数目即num_Features = num_words。
-
Vocab_size 即 input_dim 表示语料库中单词的总数,**输入向量(batch_size,input_length)的一个整数,在经过Embdding处理成为一个output_dim维的Embdding vector向量过程中,限定该Embdding vector中新元素的取值范围处于(0,input_dim)input_dim
② GlobalAveragePooling1D方法的解释
- 简单地说,将一个一维向量中的元素做了平均处理得到一个标量。见下图:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | input_shape = (2, 3, 4) x = tf.random.normal(input_shape) y = tf.keras.layers.GlobalAveragePooling1D()(x) print(x) print(y) print(y.shape) ########## tf.Tensor( [[[ 0.4384202 -0.3591519 1.5297682 -0.16539608] [ 1.5761777 0.52934337 1.2045766 0.365361 ] [ 0.8673663 -1.8493764 0.9721958 0.21659349]] [[-0.22293754 -0.3404366 0.5198015 0.78210837] [ 0.508692 1.7574197 1.1817068 -0.16306582] [-1.5862045 -1.8136072 -0.7985062 -1.397476 ]]], shape=(2, 3, 4), dtype=float32) tf.Tensor( [[ 0.96065474 -0.5597283 1.2355136 0.1388528 ] [-0.43348336 -0.13220803 0.30100068 -0.25947782]], shape=(2, 4), dtype=float32) (2, 4) |
二 使用 Keras 和 Tensorflow Hub 对电影评论进行
① 记录设备运行的包版本与TF执行模型与设备
1 2 3 4 5 6 7 8 9 | print("Version: ", tf.__version__) print("Eager mode: ", tf.executing_eagerly()) print("Hub version: ", hub.__version__) print("GPU is", "available" if tf.config.experimental.list_physical_devices("GPU") else "NOT AVAILABLE") ###===========#### Version: 2.3.0 Eager mode: True Hub version: 0.9.0 GPU is available |
② IMDB 数据集的数据分析
- 通过load函数将数据集分成训练集、验证集、测试集三种。note: 每个样本是一个字符串包含一句话
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | train_data, validation_data, test_data = tfds.load( name="imdb_reviews", split=('train[:60%]', 'train[60%:]', 'test'), as_supervised=True) train_examples_batch train_labels_batch ##=============== <tf.Tensor: shape=(10,), dtype=string, numpy= array([b"This was an absolutely terrible movie. Don't be lured in by Christopher Walken", b'I have been known to fall asleep during films, but this is usually due', b'Mann photographs the Alberta Rocky Mountains in a superb fashion, and', b'This is the kind of film for a snowy Sunday afternoon when the rest of', b'As others have mentioned, all the women that go nude in this film are', b"This is a film which should be seen by anybody interested in, ", b'Okay, you have:<br /><br />Penelope Keith as Miss Herringbone-Tweed ', b'The film is based on a genuine 1950s novel.<br /><br />Journalist ', b'I really love the sexy action and sci-fi films of the sixties ', b'Sure, this one isn\'t really a blockbuster, nor '], dtype=object)> <tf.Tensor: shape=(10,), dtype=int64, numpy=array([0, 0, 0, 1, 1, 1, 0, 0, 0, 0])> |
- 使用迁移学习进行字符串的分割,最终将一个句子构造成“嵌入向量”.
一个shape的转换过程大致为:
—> a string
—> 非固定单词个数的单词向量[num_words_nofixed, ]
—> 通过padding转化为固定单词个数的单词向量[num_words, ])
—> 嵌入向量转换(num_words, embedding_dimension)(同EmbddingAPI中的output_dim),本例子中为20。
迁移学习代码如下:
Note: 经过迁移学习后,shape变为(num_words, embedding_dimension)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | embedding = "https://tfhub.dev/google/tf2-preview/gnews-swivel-20dim/1 " hub_layer = hub.KerasLayer(embedding, input_shape=[], dtype=tf.string, trainable=True) hub_layer(train_examples_batch[:3]) #####=============################## <tf.Tensor: shape=(3, 20), dtype=float32, numpy= array([[ 1.765786 , -3.882232 , 3.9134233 , -1.5557289 , -3.3362343 , -1.7357955 , -1.9954445 , 1.2989551 , 5.081598 , -1.1041286 , -2.0503852 , -0.72675157, -0.65675956, 0.24436149, -3.7208383 , 2.0954835 , 2.2969332 , -2.0689783 , -2.9489717 , -1.1315987 ], [ 1.8804485 , -2.5852382 , 3.4066997 , 1.0982676 , -4.056685 , -4.891284 , -2.785554 , 1.3874227 , 3.8476458 , -0.9256538 , -1.896706 , 1.2113281 , 0.11474707, 0.76209456, -4.8791065 , 2.906149 , 4.7087674 , -2.3652055 , -3.5015898 , -1.6390051 ], [ 0.71152234, -0.6353217 , 1.7385626 , -1.1168286 , -0.5451594 , -1.1808156 , 0.09504455, 1.4653089 , 0.66059524, 0.79308075, -2.2268345 , 0.07446612, -1.4075904 , -0.70645386, -1.907037 , 1.4419787 , 1.9551861 , -0.42660055, -2.8022065 , 0.43727064]], dtype=float32)> |
③ 完整模型与损失函数、优化器选择
1 2 3 4 5 6 7 8 9 | embedding = "https://tfhub.dev/google/tf2-preview/gnews-swivel-20dim/1 " hub_layer = hub.KerasLayer(embedding, input_shape=[], dtype=tf.string, trainable=True) model = tf.keras.Sequential() model.add(hub_layer) model.add(tf.keras.layers.Dense(16, activation='relu')) model.add(tf.keras.layers.Dense(1)) model.summary() |
- 二分类问题且模型的输出为概率值(经过
sigmoid 的单一单元层),所以使用binary_crossentropy 损失函数。 model.fit 函数中的metrics **是在每次epoch后进行计算并记录的,训练集与测试集都是如此。**而model.evaluate 的metrics 则是仅计算一次的。原因就在于epoch的次数是否仅为1.
三 预测燃油效率
① 数据预处理
- 特征如果代表的是类别,应该将其转换为哑编码形式。
1 2 3 4 5 | origin = dataset.pop('Origin') dataset['USA'] = (origin == 1)*1.0 dataset['Europe'] = (origin == 2)*1.0 dataset['Japan'] = (origin == 3)*1.0 dataset.tail() |
2. 观察seaborn下的特征间相关性图
对角线以外的图像表示不同特征之间的相关性分布散点图;对角线中的图像则显示单特征的“变量分布”
- Pandas数据集分离出“标签”的方式
1 2 | train_labels = train_dataset.pop('MPG') test_labels = test_dataset.pop('MPG') |
- 数据规范化的逻辑观点
使用不同的尺度和范围对特征归一化是好的实践。尽管模型可能 在没有特征归一化的情况下收敛,但它会使得模型训练更加复杂,并会造成生成的模型依赖输入所使用的单位选择。
由数据规范化引出的“数据预处理”的顶级处理原则:训练集、验证集、测试集三者的预处理必须一致,从根本上说是使得三者的统计分布一致。比如哑编码处理时,应该是集合在一起进行哑编码处理!
② 模型的构建过程
- 构建模型时,将模型整合到一个函数中。
为了与inputs的数据在shapa方面的衔接,一般会在第一层中点明input_shape。
1 2 3 4 5 6 7 8 9 10 11 12 13 | def build_model(): model = keras.Sequential([ layers.Dense(64, activation='relu', input_shape=[len(train_dataset.keys())]), layers.Dense(64, activation='relu'), layers.Dense(1) ]) optimizer = tf.keras.optimizers.RMSprop(0.001) model.compile(loss='mse', optimizer=optimizer, metrics=['mae', 'mse']) return model |
- 自定义callback函数的例子配合早停法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | # 通过为每个完成的时期打印一个点来显示训练进度 class PrintDot(keras.callbacks.Callback): def on_epoch_end(self, epoch, logs): if epoch % 100 == 0: print('') print('.', end='') EPOCHS = 1000 history = model.fit( normed_train_data, train_labels, epochs=EPOCHS, validation_split = 0.2, verbose=0, callbacks=[PrintDot()]) ############################### .................................................................................................... .................................................................................................... .................................................................................................... .................................................................................................... .................................................................................................... .................................................................................................... .................................................................................................... .................................................................................................... .................................................................................................... |
配合早停法后
1 2 3 4 5 6 7 8 9 10 11 12 | model = build_model() # patience 值用来检查改进 epochs 的数量 early_stop = keras.callbacks.EarlyStopping(monitor='val_loss', patience=10) history = model.fit(normed_train_data, train_labels, epochs=EPOCHS, validation_split = 0.2, verbose=0, callbacks=[early_stop, PrintDot()]) plot_history(history) # 在本文的下一节有具体代码 ################################ .................................................................................................... ........................... |
- history我们都知道是
model.fit 的返回结果。一般是直接将history.history的结果扔给plt进行展示,但是如果用pandas进行数据的结构化展示,从可视化的角度来看,不乏便利性质。
1 2 3 | hist = pd.DataFrame(history.history) hist['epoch'] = history.epoch hist.tail() |
- Pandas处理后的history.history能用于Tensorboard以外的Plot图像绘制,这样便于论文写作。
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 | def plot_history(history): hist = pd.DataFrame(history.history) hist['epoch'] = history.epoch plt.figure() plt.xlabel('Epoch') plt.ylabel('Mean Abs Error [MPG]') plt.plot(hist['epoch'], hist['mae'], label='Train Error') plt.plot(hist['epoch'], hist['val_mae'], label = 'Val Error') plt.ylim([0,5]) plt.legend() plt.figure() plt.xlabel('Epoch') plt.ylabel('Mean Square Error [$MPG^2$]') plt.plot(hist['epoch'], hist['mse'], label='Train Error') plt.plot(hist['epoch'], hist['val_mse'], label = 'Val Error') plt.ylim([0,20]) plt.legend() plt.show() plot_history(history) |
- 测试集的
metrics 的价值分析。
除了直白的告诉你,loss,mae, mse的值。
1 2 3 | loss, mae, mse = model.evaluate(normed_test_data, test_labels, verbose=2) print("Testing set Mean Abs Error: {:5.2f} MPG".format(mae)) |
还可以做真实值与预测值的相关性分析,以直观观察模型在测试集中的拟合效果。
1 2 3 4 5 6 7 8 9 10 | test_predictions = model.predict(normed_test_data).flatten() plt.scatter(test_labels, test_predictions) plt.xlabel('True Values [MPG]') plt.ylabel('Predictions [MPG]') plt.axis('equal') plt.axis('square') plt.xlim([0,plt.xlim()[1]]) plt.ylim([0,plt.ylim()[1]]) _ = plt.plot([-100, 100], [-100, 100]) |
然而,最为人称绝的是:绘制误差的直方图分布以辨识误差的产生是否是因为样本数量很小而导致的。
1 2 3 4 | error = test_predictions - test_labels plt.hist(error, bins = 25) plt.xlabel("Prediction Error [MPG]") _ = plt.ylabel("Count") |
四 保存和恢复模型
保存的好处在于:
- 模型可以从任意中断中恢复,并避免耗费比较长的时间在训练上;
- 可以共享您的模型,而其他人可以通过您的模型来重新创建工作;
- 共享数据有助于其他人了解模型的工作原理,并使用新数据自行尝试
① checkpoints的形式
tf.keras.callbacks.ModelCheckpoint 允许在训练的过程中和结束时回调保存的模型参数,而不含网络结构。
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 | # 1 model定义 def create_model(): model = tf.keras.models.Sequential model.compile return model # 2 创建一个基本的模型实例 model = create_model() # 3 CheckPoints的回调用法 # 3.1 基本配置 checkpoint_path = "training_1/cp.ckpt" checkpoint_dir = os.path.dirname(checkpoint_path) # 3.2 创建一个保存模型权重的回调方法 cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_path, save_weights_only=True, verbose=1) # 3.3 使用新的回调训练模型 model.fit(train_images, train_labels, epochs=10, validation_data=(test_images,test_labels), callbacks=[cp_callback]) # 通过回调训练 ####################### 训练完成后 ########################### # 1 创建一个模型结构(以调用checkpoint中的参数) model = create_model() # 2 从checkpoint中加载模型权重参数(且是最新的) latest = tf.train.latest_checkpoint(checkpoint_dir) model.load_weights(latest) # 3 基于checkpoint中的权重参数 进行评估模型 loss,acc = model.evaluate(test_images, test_labels, verbose=2) # 4 基于checkpoint 中的权重参数 重新开始训练 model.fit(train_images, train_labels, epochs=10, validation_data=(test_images,test_labels), callbacks=[cp_callback]) # 通过回调训练 |
② checkpoint回调选项和checkpoint文件介绍
ModelCheckpoint 的几个选项的说明.
1 2 3 4 5 6 7 8 9 10 | # 1 为checkpoint提供唯一名称: 在文件名中包含 epoch (使用 `str.format`) checkpoint_path = "training_2/cp-{epoch:04d}.ckpt" checkpoint_dir = os.path.dirname(checkpoint_path) # 2 调整checkpoint的频率 :创建一个回调,每 5 个 epochs 保存模型的权重 cp_callback = tf.keras.callbacks.ModelCheckpoint( filepath=checkpoint_path, verbose=1, save_weights_only=True, period=5) |
- checkpoint的几个默认行为:
1 2 | # 1 checkpoint文件下 的 1+ 5*2 个文件。 # 其中 1 表示checkpoint基本信息文件;2 表示cp-0000.ckpt.data-00000-of-00001和cp-0000.ckpt.index两个文件; 5表示,默认的 tensorflow 格式仅保存最近的5个 checkpoint。 |
③ ModelCheckpoint 训练中自动保存与Model.save_weights 的手动保存
1 2 3 4 5 6 | # 1 Model.save_weights 手动保存:具体的应用时候不太清楚,可能需要在更加灵活的场景中使用吧。 # save_weights 的 链接:https://tensorflow.google.cn/api_docs/python/tf/keras/Sequential?hl=en#save_weights model.save_weights('./checkpoints/my_checkpoint') model = create_model() model.load_weights('./checkpoints/my_checkpoint') |
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 | # 2 --- ModelCheckpoint 于训练中自动保存。 # 1 model定义 def create_model(): model = tf.keras.models.Sequential model.compile return model # 2 创建一个基本的模型实例 model = create_model() # 3 CheckPoints的回调用法 # 3.1 基本配置 checkpoint_path = "training_1/cp.ckpt" checkpoint_dir = os.path.dirname(checkpoint_path) # 3.2 创建一个保存模型权重的回调方法 cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_path, save_weights_only=True, verbose=1) # 3.3 使用新的回调训练模型 model.fit(train_images, train_labels, epochs=10, validation_data=(test_images,test_labels), callbacks=[cp_callback]) # 通过回调训练 ####################### 训练完成后 ########################### # 1 创建一个模型结构(以调用checkpoint中的参数) model = create_model() # 2 从checkpoint中加载模型权重参数(且是最新的) latest = tf.train.latest_checkpoint(checkpoint_dir) model.load_weights(latest) # 3 基于checkpoint中的权重参数 进行评估模型 loss,acc = model.evaluate(test_images, test_labels, verbose=2) # 4 基于checkpoint 中的权重参数 重新开始训练 model.fit(train_images, train_labels, epochs=10, validation_data=(test_images,test_labels), callbacks=[cp_callback]) # 通过回调训练 |
④ SavedModel 格式–保存模型的参数与网络结构
Note:TensorFlow 的 SavedModel 格式是 TF2.x. 中的默认文件格式。
保存整个模型的好处:可以在 TensorFlow.js中
- 保存命令与加载命令:
model.save('saved_model/my_model') 和tf.keras.models.load_model('saved_model/my_model') 实现。
1 2 3 4 5 6 7 8 | model = create_model() model.fit(train_images, train_labels, epochs=5) # 1 模型的保存 model.save('saved_model/my_model') # 2 模型(网络与参数)的加载 new_model = tf.keras.models.load_model('saved_model/my_model') # 3 模型的使用 loss, acc = new_model.evaluate(test_images, test_labels, verbose=2) |
⑤ HDF5 格式–保存模型的参数与网络结构
Keras使用 HDF5 标准提供了一种基本的保存格式
- 保存和加载命令:
model.save('my_model.h5') 和tf.keras.models.load_model('my_model.h5') ,也就是说与SavedModel 格式的方法一致。
⑥ 模型的保存和加载的总结
Keras 通过检查网络结构来保存模型。这项技术可以保存一切:
- 权重值
- 模型的架构
- 模型的训练配置(您传递给编译的内容)
- 优化器及其状态(如果有的话)(这使您可以在中断的地方重新开始训练)