上一篇笔记记录了如何利用字母数值对应函数和规整函数处理文本,使神经网络能够接受输入,进而对文本进行分类的方法。本篇讲述一下如何在文本分类的基础上进行迁移学习、回归模型构建与早期停止函数。
利用TF Hub库的迁移学习
使用的数据集仍旧是TensorFlow官网提供的IMDB数据集,主要使用.keras包和tensorflow_hub(一个用于在一行代码中从TFHub加载预训练模型的库)。TFHub是tensorflow官网提供的一个数据库,里面存储了已经预训练好的模型参数。在训练模型时,可以从TFHub中加载预训练模型的参数信息,再进一步对模型进行训练,进而减少训练时间、提高精度。
import tensorflow as tf
import tensorflow_hub as hub
import tensorflow_datasets as tfds
import os
import matplotlib.pyplot as plt
train_data, validation_data, test_data = tfds.load(
name="imdb_reviews",
split=('train[:60%]', 'train[60%:]', 'test'),
as_supervised=True)
# 在加载数据的同时,指明加载数据包的名字,分割比例
# 训练集的前60%指明为训练数据,后40%指明为验证数据,整个测试集为测试数据
# 为真表示按行将数据处理为特征和标签一一对应的形式,为假表示将数据按照原始的字典形式处理
# train_examples_batch, train_labels_batch = next(iter(train_data.batch(10)))
# print(train_labels_batch,train_examples_batch)
# next 返回迭代器的下一个项目,iter生成迭代器
# list、tuple等都是可迭代对象,我们可以通过iter()函数获取这些可迭代对象的迭代器。
# 对获取到的迭代器不断使用next()函数来获取下⼀条数据。
# 使用next和iter迭代从训练数据中取出10个数据查看,print打印张量形状和具体内容
embedding = "https://hub.tensorflow.google.cn/google/nnlm-en-dim50/2"
# 从tensorflow的指定网站获取预训练文本嵌入向量模型
hub_layer = hub.KerasLayer(embedding, input_shape=[], dtype=tf.string, trainable=True)
# print(hub_layer(train_examples_batch[:10]))
#hub.KerasLayer() 从指定url获取训练好的对象转换为Keras的layer,而不需要运行原始的网络结构构建代码
# 搭建sequential结构
model = tf.keras.Sequential()
# 添加hub_layer层
model.add(hub_layer)
model.add(tf.keras.layers.Dense(16, activation='relu'))
# 添加全连接层
model.add(tf.keras.layers.Dense(16, activation='relu'))
# 全连接 输出层
model.add(tf.keras.layers.Dense(1))
#配置优化器
model.compile(optimizer='adam',
loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
metrics=['accuracy'])
# 使用history存放fit的训练内容
history = model.fit(train_data.shuffle(10000).batch(512),
epochs=10,
validation_data=validation_data.batch(512),
verbose=1)
# 打印网络结构
model.summary()
# evaluate函数评估结果,将测试集数据放入网络中测试
history_dict=history.history
acc=history_dict['accuracy']
val_acc=history_dict['val_accuracy']
loss=history_dict['loss']
val_loss=history_dict['val_loss']
epochs=range(1,len(acc)+1)
# plt.plot(x, y, format_string, **kwargs)
# x:x轴数据,列表或数组,可选
# y:y轴数据,列表或数组
# format_string:控制曲线的格式字符串,可选,由颜色字符、风格字符和标记字符组成
# **kwargs,第二组或更多,(x,y,format_string)
plt.plot(epochs,loss,'bo',label='Training loss')
plt.plot(epochs,val_loss,'b',label='Training val_loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
# 设置图例,又三个参数,可以设置展示的图是否有边框,背景,和图的位置,字体大小等
plt.show()
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()
# 用evaluate()检测训练结果
results=model.evaluate(test_data.batch(512),verbose=2)
# 等于1 输出进度条记录;等于2,直接输出结果
# 此处batch是元组,并不是列表。若设置为列表则报错
for name, value in zip(model.metrics_names, results):
print("%s: %.3f" % (name, value))
# zip() 将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。
# 如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同,利用 * 号操作符,可以将元组解压为列表
回归模型与早期停止函数
在我们训练模型的过程时,有时会因为数据不是特别合适,造成验证损失值越来越大,即训练模型恶化。此时可以采取早期停止训练函数及时停止训练,从而让参数保证模型的泛化能力。前面的笔记中有提及,早期停止训练就是防止过拟合的方法之一。
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
# 在matplotlib基础上补充的绘图模块
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
dataset_path = keras.utils.get_file("auto-mpg.data", "http://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data")
# (文件名,文件加载地址) 从指定文件加载地址获取文件名对应的文件
column_names = ['MPG','Cylinders','Displacement','Horsepower','Weight',
'Acceleration', 'Model Year', 'Origin']
# 列名字
raw_dataset = pd.read_csv(dataset_path,
names=column_names,
na_values = "?",
# 缺失值用?表示
comment='\t',
# 指示不应分析行的其余部分。如果在一行的开头找到该行,则将完全忽略该行
sep=" ",
# 表示分隔符
skipinitialspace=True)
# 在定界符后跳过空格
# 未经处理的数据,利用pandas读取CSV文件(文件路径,列名字,。。。)
dataset = raw_dataset.copy()
# 复制raw_dataset的数据和结构,赋给dataset
print(dataset.tail()) #打印数据
dataset.isna().sum() #检测缺失值并统计
dataset = dataset.dropna() #找到缺失值并删除所在的行和列
origin = dataset.pop('Origin') #将其转换为独热码的形式
# Origin列表示汽车的产地,分别用1,2,3存储。但是产地没有大小之分,所以用独热编码更合适
dataset['USA'] = (origin == 1)*1.0
dataset['Europe'] = (origin == 2)*1.0
dataset['Japan'] = (origin == 3)*1.0
# print(dataset.tail())
train_dataset = dataset.sample(frac=0.8,random_state=0) #随机选取行和列
test_dataset = dataset.drop(train_dataset.index) #剔除带有训练集索引的部分数据
sns.pairplot(train_dataset[["MPG", "Cylinders", "Displacement", "Weight"]], diag_kind="kde")
# sns用来展示两两特征之间的关系
plt.show()
# 必须用plt.show让函数图显示
# 查看总体的数据统计
train_stats = train_dataset.describe()
# 返回数据的波动及变化
train_stats.pop("MPG")
train_stats = train_stats.transpose()
# transpose 调换矩阵行列的索引值,类似于求转置矩阵
print(train_stats)
train_labels = train_dataset.pop('MPG')
test_labels = test_dataset.pop('MPG')
# 数据规范化
def norm(x):
return (x - train_stats['mean']) / train_stats['std']
normed_train_data = norm(train_dataset)
normed_test_data = norm(test_dataset)
# 定义了一个创建神经网络模型的函数
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
#此处第一次建立模型
# model = build_model()
# model.summary()
# 通过为每个完成的时期打印一个点来显示训练进度
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()])
hist = pd.DataFrame(history.history)
# dataframe pandas的表格型数据结构
hist['epoch'] = history.epoch
# print(hist.tail())
'''
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.show()
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)
# 第一次模型结果恶化,采用早期停止函数避免过拟合,使训练效果更好
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)
loss, mae, mse = model.evaluate(normed_test_data, test_labels, verbose=2)
print("Testing set Mean Abs Error: {:5.2f} MPG".format(mae))
test_predictions = model.predict(normed_test_data).flatten()
# faltten将predict结果拉直,形成一维数组
plt.scatter(test_labels, test_predictions)
plt.xlabel('True Values [MPG]')
plt.ylabel('Predictions [MPG]')
plt.axis('equal')
# xy轴刻度等长
plt.axis('square')
# 作图为正方形
plt.xlim([0,plt.xlim()[1]])
plt.ylim([0,plt.ylim()[1]])
_ = plt.plot([-100, 100], [-100, 100])
plt.show()
error = test_predictions - test_labels
plt.hist(error, bins = 25)
# 直方图,参数表示数据来源和直方的柱数
plt.xlabel("Prediction Error [MPG]")
_ = plt.ylabel("Count")
plt.show()