【人工智能笔记】第二十四节 tensorflow 2.0 自定义模型训练流程,采用动态学习速率加快训练过程

本节介绍如何在tenserflow2.0下自定义模型训练流程。首先我们先实现一个正常的训练流程,然后再改变训练流程,加快训练收敛速度。下面分开两部分讲解。

一、正常训练流程

通过重构模型里train_step(self, data)方法,可自定义训练流程。同理重构test_step(self, data)方法,可自定义测试流程。

正常训练流程:

  1. 拆分数据,根据dataset将输入x与label拆分开。
  2. 将x输入model,正向传播,获得结果y_pred。
  3. label与结果计算loss。
  4. 通过loss计算梯度。
  5. 按梯度更新权重。
  6. 计算准确率,按字典形式返回值,显示在训练过程中。

代码如下:

    def train_step(self, data):
        '''
        正常训练流程
        '''
        # 解压缩数据。它取决于你的数据集结构。
        x, y = data
        with tf.GradientTape() as tape:
            y_pred = self(x, training=True)  # 前向传播
            # 计算loss
            # (compiled_loss是在调用`compile()`方法时定义)
            loss = self.compiled_loss(y, y_pred, regularization_losses=self.losses)

        # 获取变量集,计算梯度
        trainable_vars = self.trainable_variables
        gradients = tape.gradient(loss, trainable_vars)
        # 按梯度更新所有权重变量
        self.optimizer.apply_gradients(zip(gradients, trainable_vars))
            
        # 更新指标(包括指标追踪损失)
        self.compiled_metrics.update_state(y, y_pred)
        # 返回一个dict指标名称映射到当前值
        return {m.name: m.result() for m in self.metrics}

二、动态学习速率训练流程

在训练模型时,通常需要较长时间模型才会收敛。要加快收敛速度,可调大学习速率。但速率过大,会导致模型不收敛,太小又会陷入局域极小值。那么能不能我给一个较大的学习速率,更新权重后,先看看loss是否下降,如果没下降再逐步调小学习速率,这样既能加快模型收敛,又可越过局域极小值。(这里要注意,dorpout等随机操作,会影响效果,导致收敛缓慢)

动态学习速率训练流程:

  1. 拆分数据,根据dataset将输入x与label拆分开。
  2. 保存模型初始权重
  3. 将x输入model,正向传播,获得结果y_pred。
  4. label与结果计算loss。
  5. 通过loss计算梯度。
  6. 按梯度更新权重。
  7. 将x输入model,正向传播,获得结果y_pred。
  8. label与结果计算梯度下降后的loss,如发现loss没下降,则还原模型权重,降低学习速率,重复2-8步。直到loss变小,或学习速率小于某个阈值,则跳出循环。
  9. 计算准确率,按字典形式返回值,显示在训练过程中。

完整代码如下:

import tensorflow as tf
import numpy as np
import os
import sys

# 根目录
ROOT_DIR = os.path.abspath("./")

np.random.seed(1)
tf.random.set_seed(1)

class MyModel(tf.keras.Model):
    def __init__(self, min_lr=1e-6, *args, **kwargs):
        super(MyModel, self).__init__(*args, **kwargs)
        self.all_layers = [
            tf.keras.layers.Conv2D(32, (3, 3), padding='same', use_bias=False),
            tf.keras.layers.BatchNormalization(),
            tf.keras.layers.Activation(activation=tf.keras.activations.relu),
            tf.keras.layers.AveragePooling2D((2,2)),
            tf.keras.layers.Conv2D(128, (3, 3), padding='same', use_bias=False),
            tf.keras.layers.BatchNormalization(),
            tf.keras.layers.Activation(activation=tf.keras.activations.relu),
            tf.keras.layers.AveragePooling2D((2,2)),
            tf.keras.layers.Conv2D(10, (7, 7), padding='valid', use_bias=False),
            tf.keras.layers.BatchNormalization(),
            tf.keras.layers.Activation(activation=tf.keras.activations.softmax),
            # tf.keras.layers.Dropout(0.3), # dorp会导致loss浮动,每次loss都不同
            tf.keras.layers.Flatten(),
        ]
        self.min_lr = min_lr
    
    def build(self, input_shape):
        result = super().build(input_shape)
        trainable_vars = self.trainable_variables
        self.bak_trainable_vars = []
        for var in trainable_vars:
            self.bak_trainable_vars.append(tf.Variable(var, trainable=False))
        self.bak_trainable_last_vars = []
        for var in trainable_vars:
            self.bak_trainable_last_vars.append(tf.Variable(var, trainable=False))
        return result

    def call(self, inputs, training):
        x = inputs
        for layer in self.all_layers:
            if isinstance(layer, tf.keras.layers.BatchNormalization):
                x = layer(x, training=training)
            else:
                x = layer(x)
        return x

    def train_step(self, data):
        # 正常训练流程
        # return self.train_step_normal(data)
        # 动态学习速率训练流程
        return self.train_step_fast(data)

    def train_step_fast(self, data):
        '''
        动态学习速率
        '''
        # 解压缩数据。它取决于你的数据集结构。
        x, y = data
        loss = 0.0
        new_loss = 1.0
        # 学习速率从0.1开始,每次乘以0.3,逐渐减少,直到loss小于训练前的loss, 或学习速率小于self.min_lr,停止循环,更新权重
        self.optimizer.learning_rate.assign(0.1)  
        y_pred = self(x, training=True)  # 初始化
        # 定义循环,找到最合适的学习速率
        def loop_fun(loss, new_loss, y_pred, lr):
            with tf.GradientTape() as tape:
                y_pred = self(x, training=True)  # 前向传播
                # 计算loss
                # (compiled_loss是在调用`compile()`方法时定义)
                loss = self.compiled_loss(y, y_pred, regularization_losses=self.losses)

            # 获取变量集,计算梯度
            trainable_vars = self.trainable_variables
            # 记录训练前的权重
            for i in range(len(trainable_vars)):
                self.bak_trainable_vars[i].assign(trainable_vars[i])
            gradients = tape.gradient(loss, trainable_vars)
            # 按梯度更新所有权重变量
            self.optimizer.apply_gradients(zip(gradients, trainable_vars))
            
            # 计算梯度下降后,loss是否下降
            y_pred = self(x, training=True)
            new_loss = self.compiled_loss(y, y_pred, regularization_losses=self.losses)
            self.optimizer.learning_rate.assign(self.optimizer.learning_rate * 0.3)
            # 记录训练后的权重
            for i in range(len(trainable_vars)):
                self.bak_trainable_last_vars[i].assign(trainable_vars[i])
            # 还原训练前的权重
            for i in range(len(trainable_vars)):
                trainable_vars[i].assign(self.bak_trainable_vars[i])
            y_pred = self(x, training=True)  # Forward pass
            old_loss = self.compiled_loss(y, y_pred, regularization_losses=self.losses)
            return (loss, new_loss, y_pred, self.optimizer.learning_rate)
        loss, new_loss, y_pred, _ = tf.while_loop(lambda loss, new_loss, y_pred, lr: tf.math.logical_and(loss<=new_loss,lr>=self.min_lr), loop_fun, (loss, new_loss, y_pred, self.optimizer.learning_rate))

        # 恢复最佳权重
        trainable_vars = self.trainable_variables
        for i in range(len(trainable_vars)):
            trainable_vars[i].assign(self.bak_trainable_last_vars[i])
        # 更新指标(包括指标追踪损失)
        self.compiled_metrics.update_state(y, y_pred)
        # 返回一个dict指标名称映射到当前值
        return {m.name: m.result() for m in self.metrics}

    def train_step_normal(self, data):
        '''
        正常训练流程
        '''
        # 解压缩数据。它取决于你的数据集结构。
        x, y = data
        with tf.GradientTape() as tape:
            y_pred = self(x, training=True)  # 前向传播
            # 计算loss
            # (compiled_loss是在调用`compile()`方法时定义)
            loss = self.compiled_loss(y, y_pred, regularization_losses=self.losses)

        # 获取变量集,计算梯度
        trainable_vars = self.trainable_variables
        gradients = tape.gradient(loss, trainable_vars)
        # 按梯度更新所有权重变量
        self.optimizer.apply_gradients(zip(gradients, trainable_vars))
            
        # 更新指标(包括指标追踪损失)
        self.compiled_metrics.update_state(y, y_pred)
        # 返回一个dict指标名称映射到当前值
        return {m.name: m.result() for m in self.metrics}

def main():
    mnist = tf.keras.datasets.mnist

    (x_train, y_train), (x_test, y_test) = mnist.load_data()
    x_train, x_test = x_train / 255.0, x_test / 255.0
    x_train, x_test = tf.expand_dims(x_train, axis=-1), tf.expand_dims(x_test, axis=-1)
    y_train, y_test = tf.one_hot(y_train, 10), tf.one_hot(y_test, 10)
    print('x_train, y_train', x_train.shape, y_train.shape)
    print('x_train, y_train', type(x_train), type(y_train))
    print('x_test, y_test', x_test.shape, y_test.shape)

    # 卷积实现图片分类
    model = MyModel()

    model.compile(optimizer=tf.keras.optimizers.SGD(),
                  loss=tf.keras.losses.CategoricalCrossentropy(),
                  metrics=['accuracy'],
                  )
    
    x = model(x_train[:1], training=False)
    print('x:', x)

    model.fit(x_train, y_train, epochs=10, validation_data=(x_test, y_test), shuffle=False)
    
    x = model(x_train[:1], training=False)
    print('x:', x)
    trainable_vars = model.trainable_variables
    tmp_trainable_vars = []
    for i in range(len(trainable_vars)):
        tmp_trainable_vars.append(tf.Variable(trainable_vars[i], dtype=trainable_vars[i].dtype))


if __name__ == '__main__':
    main()

本节内容到此结束。更多的赞与收藏是我更新的动力,欢迎收藏,当然也欢迎捐献,哈哈哈。

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,我会尽力回答你的问题。对于您的问题,我理解您想问我如何调用U-NET模型,以下是我给出的回答。 首先,U-NET模型是一个用于图像分割的深度学习模型,它使用卷积神经网络来学图像中不同区域的特征,并将它们分割成不同的类别。在TensorFlow 2.0中,我们可以使用tf.keras API来实现U-NET模型。 以下是一个简单的U-NET模型的实现代码: ```python import tensorflow as tf from tensorflow.keras.layers import * def conv_block(inputs, filters, kernel_size=(3, 3), activation='relu', padding='same'): x = Conv2D(filters, kernel_size, activation=activation, padding=padding)(inputs) x = BatchNormalization()(x) x = Conv2D(filters, kernel_size, activation=activation, padding=padding)(x) x = BatchNormalization()(x) return x def unet(input_shape=(256, 256, 3)): inputs = Input(shape=input_shape) c1 = conv_block(inputs, 64) p1 = MaxPooling2D((2, 2))(c1) c2 = conv_block(p1, 128) p2 = MaxPooling2D((2, 2))(c2) c3 = conv_block(p2, 256) p3 = MaxPooling2D((2, 2))(c3) c4 = conv_block(p3, 512) p4 = MaxPooling2D((2, 2))(c4) c5 = conv_block(p4, 1024) u6 = Conv2DTranspose(512, (2, 2), strides=(2, 2), padding='same')(c5) u6 = concatenate([u6, c4]) c6 = conv_block(u6, 512) u7 = Conv2DTranspose(256, (2, 2), strides=(2, 2), padding='same')(c6) u7 = concatenate([u7, c3]) c7 = conv_block(u7, 256) u8 = Conv2DTranspose(128, (2, 2), strides=(2, 2), padding='same')(c7) u8 = concatenate([u8, c2]) c8 = conv_block(u8, 128) u9 = Conv2DTranspose(64, (2, 2), strides=(2, 2), padding='same')(c8) u9 = concatenate([u9, c1]) c9 = conv_block(u9, 64) outputs = Conv2D(1, (1, 1), activation='sigmoid')(c9) model = tf.keras.Model(inputs=[inputs], outputs=[outputs]) return model ``` 在这个实现中,我们使用了Conv2D、BatchNormalization、MaxPooling2D和Conv2DTranspose等层来构建U-NET模型。我们还定义了一个conv_block函数来简化代码。 使用这个实现代码,我们可以通过以下方式调用U-NET模型: ```python model = unet() ``` 这将返回一个U-NET模型的实例,我们可以使用该实例来进行训练和预测。 希望这个回答对您有所帮助。如果您还有其他问题,请随时问我。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PPHT-H

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值