keras:顺序API,函数式API ,子类API ,保存和还原模型,使用回调函数,tensorboard可视化

使用keras顺序API 构建回归NN

from sklearn.datasets import fetch_california_housing   # 加州住房数据集
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler   # 使用sklearn 的函数加载更加简单(没有缺失值啥的)

housing = fetch_california_housing() # 参数 data_home 指定路径

X_train_full, X_test, y_train_full, y_test = train_test_split(housing.data, housing.target, random_state=42)
X_train, X_valid, y_train, y_valid = train_test_split(X_train_full, y_train_full, random_state=42)
# 分割,然后比例缩放
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_valid = scaler.transform(X_valid)
X_test = scaler.transform(X_test)

model = keras.models.Sequential([
    keras.layers.Dense(30, activation="relu", input_shape=X_train.shape[1:]),
    keras.layers.Dense(1)    # 与分类相比,回归就是输出只有一个单元的,其他都是一样的
])
model.compile(loss="mean_squared_error", optimizer=keras.optimizers.SGD(lr=1e-3))
history = model.fit(X_train, y_train, epochs=20, validation_data=(X_valid, y_valid))
mse_test = model.evaluate(X_test, y_test)
X_new = X_test[:3]
y_pred = model.predict(X_new)

plt.plot(pd.DataFrame(history.history))
plt.grid(True)
plt.gca().set_ylim(0, 1)
plt.show()

在这里插入图片描述

使用函数式API 构建复杂模型

虽然顺序API 十分普遍,但构建具有复杂结构的比如多个输入输出的神经网络也是很常见。比如残差网络之类的。将部分或所有输入直接连接到后层,让神经网络可以学习深度模式和简单规则。

在这里插入图片描述

# 构建一个这样的神经网络
input_ = keras.layers.Input(shape=X_train.shape[1:])
hidden1 = keras.layers.Dense(30, activation="relu")(input_)  # 指定输入数据
hidden2 = keras.layers.Dense(30, activation="relu")(hidden1)  
concat = keras.layers.concatenate([input_, hidden2])  # 合并
output = keras.layers.Dense(1)(concat)  
model = keras.models.Model(inputs=[input_], outputs=[output])

'''
首先创建一个input 对象,这是模型的输入类型,包括shape, dtype ,一个模型可以有多个输入
创建全连接层,想调用函数一样传入输入,这里只是构建这个结构,没有实际处理数据
建立连接层,合并输入
然后建立单个神经元的输出层,最后建立一个 keras.models.Model 指定输入和输出
'''


model.summary()
>>>
Model: "functional_1"
__________________________________________________________________________________________________
Layer (type)                    Output Shape         Param #     Connected to                     
==================================================================================================
input_1 (InputLayer)            [(None, 8)]          0                                            
__________________________________________________________________________________________________
dense (Dense)                   (None, 30)           270         input_1[0][0]                    
__________________________________________________________________________________________________
dense_1 (Dense)                 (None, 30)           930         dense[0][0]                      
__________________________________________________________________________________________________
concatenate (Concatenate)       (None, 38)           0           input_1[0][0]                    
                                                                 dense_1[0][0]                    
__________________________________________________________________________________________________
dense_2 (Dense)                 (None, 1)            39          concatenate[0][0]                
==================================================================================================
Total params: 1,239
Trainable params: 1,239
Non-trainable params: 0
__________________________________________________________________________________________________

model.compile(loss="mean_squared_error", optimizer=keras.optimizers.SGD(lr=1e-3))
history = model.fit(X_train, y_train, epochs=20,
                    validation_data=(X_valid, y_valid))
mse_test = model.evaluate(X_test, y_test)
y_pred = model.predict(X_new)

在这里插入图片描述

input_A = keras.layers.Input(shape=[5], name="wide_input")  # 多输入,宽路径送 5 个特征
input_B = keras.layers.Input(shape=[6], name="deep_input")  # 神路径送 6 个特征
hidden1 = keras.layers.Dense(30, activation="relu")(input_B)   # 对于复杂的模型可以进行命名
hidden2 = keras.layers.Dense(30, activation="relu")(hidden1)
concat = keras.layers.concatenate([input_A, hidden2])
output = keras.layers.Dense(1, name="output")(concat)
model = keras.models.Model(inputs=[input_A, input_B], outputs=[output])

# 然后进行编译,训练,测试
model.compile(loss="mse", optimizer=keras.optimizers.SGD(lr=1e-3))

X_train_A, X_train_B = X_train[:, :5], X_train[:, 2:]
X_valid_A, X_valid_B = X_valid[:, :5], X_valid[:, 2:]
X_test_A, X_test_B = X_test[:, :5], X_test[:, 2:]
X_new_A, X_new_B = X_test_A[:3], X_test_B[:3]

history = model.fit((X_train_A, X_train_B), y_train, epochs=20,  # fit 中需要传入一对矩阵 A B 
                    validation_data=((X_valid_A, X_valid_B), y_valid))
mse_test = model.evaluate((X_test_A, X_test_B), y_test)
y_pred = model.predict((X_new_A, X_new_B))   # 这些 predict evaluate 一样传入 A B 矩阵

还可以有多输出,比如定位加分类,同一数据多个独立任务(表情加人脸检测),再神经网络结构中添加一个辅助输出,确保网络的主要部分可以自己学习而不用依赖网络的其他部分。

在这里插入图片描述

input_A = keras.layers.Input(shape=[5], name="wide_input")
input_B = keras.layers.Input(shape=[6], name="deep_input")
hidden1 = keras.layers.Dense(30, activation="relu")(input_B)
hidden2 = keras.layers.Dense(30, activation="relu")(hidden1)
concat = keras.layers.concatenate([input_A, hidden2])
output = keras.layers.Dense(1, name="main_output")(concat)
aux_output = keras.layers.Dense(1, name="aux_output")(hidden2)  
model = keras.models.Model(inputs=[input_A, input_B]# 添加额外输出只需要链接适当的层,然后添加到模型的输出列表就行
outputs=[output, aux_output])      

# 每个输出都需要自己的损失函数,如果传入单个就认为都是用相同的损失。默认下将多输出的损失简单累加得到最终结构
# 但是可以加个权重,更关心主要输出。
model.compile(loss=["mse", "mse"], loss_weights=[0.9, 0.1], optimizer=keras.optimizers.SGD(lr=1e-3))      
# 训练模型的时候需要给每个输出提供标签,这里主要和辅助输出结果相同所有就用相同的标签了。[y_train,y_train]
history = model.fit([X_train_A, X_train_B], [y_train, y_train], epochs=20,
                    validation_data=([X_valid_A, X_valid_B], [y_valid, y_valid]))

total_loss, main_loss, aux_loss = model.evaluate(
    [X_test_A, X_test_B], [y_test, y_test])   # 
y_pred_main, y_pred_aux = model.predict([X_new_A, X_new_B])
>>>
5160/5160 [==============================] - 0s 19us/sample - loss: 0.4656 - main_output_loss: 0.4165 - aux_output_loss: 0.9111   # 返回总损失和所有单个损失,predict() 也返回每个输出的预测值

使用子类API 构建动态模型

之前的顺序和函数式API 都是声明性的:声明层和链接方式,然后体哦那个数据进行训练。这样可以轻松的保存,克隆,共享模型,可以显示分析结构,便于数据通过前的检测错误,整个模型是一个静态图。但是对于一些模型的循环,变化的形状,条件分支和其他动态行为,子类API 就更加合适。

就是构建 Model的子类,构建函数中创建层,然后再cell() 方法中执行计算。

class WideAndDeepModel(keras.models.Model):
    def __init__(self,units=30, activation='relu',**kwargs):
        super().__init__(**kwargs)
        self.hidden1 = keras.layers.Dense(units,activation=activation)
        self.hidden2 = keras.layers.Dense(units,activation=activation)
        self.main_output = keras.layers.Dense(1)
        self.aux_output = keras.layers.Dense(1)
        
    def call(self,inputs):   
        input_A , input_B = inputs  # 不需要再创建输入了 Input,只需要call() 方法传入输入参数
        hidden1 = self.hidden1(input_B)  # 将层的构建和链接方式区分开来,这样可以再call() 方法中执行更多的操作
        hidden2 = self.hidden2(hidden1)  # 更加的灵活,但是模型的结构隐藏再call()方法中,无法保存克隆
        concat = keras.layers.concatenate([input_A,hidden2]) # 调用summary() 方法时只会得到一个图层列表,没有链接方式了
        main_output = self.main_output(concat)  # 而且无法提前自动检查类型和形状,容易出错
        aux_output = sellf.aux_output(hiddne2)   # 除非真的需要这种灵活性还是建议使用顺序API 。
        return main_output,aux_output
model = WideAndDeepModel(30,activation='relu')  # 


model.compile(loss="mse", loss_weights=[0.9, 0.1], optimizer=keras.optimizers.SGD(lr=1e-3))
history = model.fit((X_train_A, X_train_B), (y_train, y_train), epochs=10,
                    validation_data=((X_valid_A, X_valid_B), (y_valid, y_valid)))
total_loss, main_loss, aux_loss = model.evaluate((X_test_A, X_test_B), (y_test, y_test))
y_pred_main, y_pred_aux = model.predict((X_new_A, X_new_B))

保存和还原模型

使用顺序 或者 函数API 保存训练好的 keras 模型很简单:

model = keras.models.Sequential([
    keras.layers.Dense(30, activation="relu", input_shape=[8]),
    keras.layers.Dense(30, activation="relu"),
    keras.layers.Dense(1)
])    
model.compile(loss="mse", optimizer=keras.optimizers.SGD(lr=1e-3))
history = model.fit(X_train, y_train, epochs=10, validation_data=(X_valid, y_valid))
mse_test = model.evaluate(X_test, y_test)

model.save("my_keras_model.h5")  # keras 使用HDF5 格式保存模型结构(包括每一层的超参数)
# 和每一层的所有模型参数(权重,偏置)。还可以保存优化器
model = keras.models.load_model("my_keras_model.h5")      # 加载模型
model.predict(X_new)   


# 但对于子类化的没用,需要 save_weight() load_weight() 来保存参数,其他的结构就要自己来了。。
model.save_weights("my_keras_weights.ckpt") 
model.load_weights("my_keras_weights.ckpt")

使用回调函数

就是再训练中保存中间模型。fit 方法接收一个 callbacks 参数,指定再训练开始和结束时,每个世代开始和结束时需要调用的对象列表。比如ModelCheckpoint 回调可以定期的保存模型的检查点(默认每个世代结束时)

model.compile(loss="mse", optimizer=keras.optimizers.SGD(lr=1e-3))
checkpoint_cb = keras.callbacks.ModelCheckpoint("my_keras_model.h5", save_best_only=True)  
 # 如果再训练期间使用验证集,设置 save_best_only 只有验证集达到更高的性能时才会保存模型,也是一种提前停止
history = model.fit(X_train, y_train, epochs=10,  
                    validation_data=(X_valid, y_valid),
                    callbacks=[checkpoint_cb])  # 指定检查点
model = keras.models.load_model("my_keras_model.h5")  # rollback to best model
mse_test = model.evaluate(X_test, y_test)

# 实现提前停止的另一种方法就是使用 EarlyStopping 回调。再多个世代验证集上没有进展,就中断训练,回滚到最佳模型,可以将这两个结合来保存模型。
model.compile(loss="mse", optimizer=keras.optimizers.SGD(lr=1e-3))
early_stopping_cb = keras.callbacks.EarlyStopping(patience=10,  # 指定的世代,一般大点比较好
                                                  restore_best_weights=True)
history = model.fit(X_train, y_train, epochs=100,
                    validation_data=(X_valid, y_valid),
                    callbacks=[checkpoint_cb, early_stopping_cb])
mse_test = model.evaluate(X_test, y_test)

也可以自己编写回调,这里显示是训练过程中验证损失与训练损失之间的比率(例如,检查过拟合)

class PrintValTrainRatioCallback(keras.callbacks.Callback):
    def on_epoch_end(self, epoch, logs):
        print("\nval/train: {:.2f}".format(logs["val_loss"] / logs["loss"]))
'''
on_train_begin()	on_train_end()	on_epoch_begin().....on_batch_begin()	on_batch_end()
on_test_begin()	ontest_end()	on_test_batch_begin()  再评估(evaluate())和预测(predict())中使用
on_predict_begin()  on_predict_end()  
'''
val_train_ratio_cb = PrintValTrainRatioCallback()
history = model.fit(X_train, y_train, epochs=1,
                    validation_data=(X_valid, y_valid),
                    callbacks=[val_train_ratio_cb])
>>>
Train on 11610 samples, validate on 3870 samples
10912/11610 [===========================>..] - ETA: 0s - loss: 0.3231
val/train: 1.16   # 这就是了。。。
11610/11610 [==============================] - 0s 30us/sample - loss: 0.3256 - val_loss: 0.3785

使用 TensorBoard 进行可视化

一种交互式可视化工具,可以再训练期间查看学习曲线,比较多次运行的学习曲线,可视化计算图,分析训练统计数据,查看由模型生成的图像,多维数据投影到3D ,自动聚类并进行可视化等等。

使用时需要将数据输出到名为事件的二进制日志文件中,每个二进制数据记录称为摘要,tensorboard 服务器监视日志目录,自动获取并更新可视化效果,来可视化实时数据。

root_logdir = os.path.join(os.curdir, "my_logs")  # 指向不同目录不会混淆
def get_run_logdir():
    import time
    run_id = time.strftime("run_%Y_%m_%d-%H_%M_%S") # 返回以可读字符串表示的当地时间,
    return os.path.join(root_logdir, run_id)

run_logdir = get_run_logdir()     # 定义根目录,和一个更加时间生成子目录的函数
run_logdir
>>> '.\\my_logs\\run_2021_02_28-20_57_45'  

model = keras.models.Sequential([
    keras.layers.Dense(30, activation="relu", input_shape=[8]),
    keras.layers.Dense(30, activation="relu"),
    keras.layers.Dense(1)
])    
model.compile(loss="mse", optimizer=keras.optimizers.SGD(lr=1e-3))

tensorboard_cb = keras.callbacks.TensorBoard(run_logdir)  # 回调函数
history = model.fit(X_train, y_train, epochs=30,
                    validation_data=(X_valid, y_valid),
                    callbacks=[checkpoint_cb, tensorboard_cb])

运行时,TensorBoard() 回调会自动建立日志目录,再训练期间将创建事件文件写入摘要,每次运行都有一个目录,包含一个用于训练日志的子目录和一个用于验证日志的子目录,训练日志包含了概要分析跟踪,可以准确显示模型再所有设备上花费各模型上的时间,用于查找性能瓶颈。

%tensorboard --logdir=./my_logs --port=6006

然后启动服务器打开出现的网址就行了。

在这里插入图片描述
还可以使用 create_file_writer() 创建一个 SmmaryWriter ,用来记录,然后使用tensorboard 可视化

test_logdir = get_run_logdir()
writer = tf.summary.create_file_writer(test_logdir)
with writer.as_default():
	 for step in range(1, 1000 + 1):    # 还是感觉tensorboardX 好用啊
		 tf.summary.scalar("my_scalar", np.sin(step / 10), step=step)  # 
		 data = (np.random.randn(100) + 2) * step / 100 # some random data
		 tf.summary.histogram("my_hist", data, buckets=50, step=step)
		 images = np.random.rand(2, 32, 32, 3) # random 32×32 RGB images
		 tf.summary.image("my_images", images * step / 1000, step=step)
		 texts = ["The step is " + str(step), "Its square is " + str(step**2)]
		 tf.summary.text("my_text", texts, step=step)
		 sine_wave = tf.math.sin(tf.range(12000) / 48000 * 2 * np.pi * step)
		 audio = tf.reshape(tf.cast(sine_wave, tf.float32), [1, -1, 1])
		 tf.summary.audio("my_audio", audio, sample_rate=48000, step=step)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值