梅飞飞飞的假期学习日记day3

本文介绍了深度学习中模型训练的细节,特别是tensorflow库中的Dense和Sequential类的实现,以及批量生成器的作用。文章强调了梯度下降算法和其变体如SGD、Adagrad、RMSprop在优化过程中的重要性,并展示了如何使用GradientTape进行自动微分以更新模型权重。最后,文章通过一个简单的例子演示了训练过程和评估模型性能的方法。
摘要由CSDN通过智能技术生成

今日通过对深度学习tensorflow库中的模型训练过程的细节进行了重新的理解,自己对深度学习底层框架有了更深的认知能力,确实觉得自己需要再细细过一遍深度学习相关的基础内容知识,正所谓语速则不达,加快进度在本月往后推进

更新模型权重时,我们最开始的思路是每次更新一个标量系数(保持其他参数不变),将其增大、减小,分别查看各自的损失值变化等。

但更好的梯度下降算法巧妙通过导数等数学理论简化了这一过程

随机梯度下降SGD后续衍生出了多种变体,例如带动量的Adagard,RMSprop等,这些变体会考虑上一次权重更新,而不仅局限于当前梯度,这些变体也就是在模型中经常用到的优化器

!!!利用动量因子我们可以有效避免局部最小值

利用连续导数相乘实现链式求导法则并求解得到损失函数相对于某个参数的梯度值

通过自实现简单的Dense,Sequential类来加深对于神经网络层的理解

1.Dense

import tensorflow as tf

class NaiveDense;
    def __init__(self, input_size, output_size, activation):
        self.activation = activation
        
        w_shape = (input_size, output_size)    //初始化一个w矩阵
        w_initial_value = tf.random.uniform(w_shape, minval=0, maxval=1e-1)
        self.W = tf.Variable(w_initial_value)

        b_shape = (outputsize,)            //一个形状为outputsize的0向量
        b_initial_value = tf.zeros(b_shape)
        self.b = tf.Variable(b_initial_value)

    def __call__(self, inputs):  //前向传播
        return self.activation(tf.matmul(inputs, self.W) + self.b)

    def weights(self):          //获取权重方法
        return [self.W, self.b]

2.Sequential

class NaiveSequential:
    def __init__(self, layers):
        self.layers = layers

    def __call__(self, inputs):
        x = inputs
        for layer in self.layers:
            x = layer(x)
        return x

    def weights(self):
        weights = []
        for layer in self.layers:
            weights += layer.weights
        return weights

利用Sequential类我们将之前的层给链接起来

model = NaiveSequential([
    NaiveDense(input_size = 28*28, output_size=512, activation=tf.nn.relu),
    NaiveDense(input_size = 512, output_size=10, activation=tf.nn.softmax)
])
assert len(model.weights) == 4

参数的组数限制在四组参数以内

3.批量生成器

用来对数据集进行小批量迭代

import math

class BatchGenerator:
    def __init__(self, images, labels, batch_size=128):
        assert len(images) == len(labels)
        self.index = 0
        self.images = images
        self.labels = labels
        self.batch_size = batch_size
        self.num_batches = math.ceil(len(images) / batch_size)

    def next(self):
        images = self.images[self.index : self.index = self.batch_size]
        labels = self.labels[self.index : self.index = self.batch_size]
        self.index += self.batch_size
        return images, labels

num_batches为每一次处理的数据集尺度

利用next进行迭代

完成训练步骤

在此处我们需要实现在一批数据上运行模型后对模型的参数更新,有以下几个方面需要我们做到:

(1)计算模型对图像批量的预测值

(2)根据实际标签,计算预测值对应的损失值

(3)计算损失相对于模型权重的梯度大小

(4)将权重沿着梯度的反方向移动一小步(略微更新)

def one_training_step(model, images_batch, labels_batch):
    with tf.GradientTape() as tape:
        predictions = model(images_batch)
        per_sample_losses = tf.keras.losses.sparse_categorical_crossentropy(
            labels_batch, predictions)
        average_loss = tf.reduce_mean(per_sample_losses)
    gradients = tape.gradient(average_loss, model.weights)
    update_weights(gradients, model.weights)
    return average_loss

此处用到了API,GradientTape,主要用来获取某个变量相对于某个变量的梯度

update_weights方法

learning_rate = 1e-3

def update_weights(gradients, weights):
    for g,w in zip(gradients, weights):  
        w.assign_sub(g * learning_rate) //assign_sub相当于“-=”

由此实现了权重更新的简单函数

但在实际操作时我们一般都不用手动实现权重更新,直接调用优化器即可

from tensorflow.keras import optimizers

optimizer = optimizers.SGD(learning_rate=1e-3)

def update_weights(gradients, weights):
    optimizer.apply_gradients(zip(gradients,weights))

利用zip将gradients和weights组成一个类似二维数组的元组,并调用optimizer的方法进行优化

进行训练并完成一此完整的训练循环

fit函数

def fit(model, images, labels, epochs, batch_size=128):
    for epoch_counter in range(epochs):
        print(f"Epoch {epoch_counter}")
        batch_generator = BatchGenerator(images, labels)
        for batch_counter in range(batch_generator.num_batcher):
            images_batch, labels_batch = batch_generator.next()
            loss = one_training_step(model, images_batch, labels_batch)
            if batch_counter % 100 == 0:
                print(f"loss at batch {batch_counter}: {loss:.2f}")

试运行

from tensorflow.keras.datasets import mnist
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

train_images = train_images.reshape((60000, 28 * 28))
train_images = train_images.astype("float32") / 255
test_images = test_images.reshape((10000, 28 * 28))
test_images = test_images.astype("float32") / 255

fit(model, train_images, train_labels, epochs=10, batch_size=128)

评估结果

predictions = model(test_images)
predictions = predictions.numpy()
predicted_labels = np.argmax(predictions, axis=1)
matches = predicted_labels == test_labels
print(f"accuracy: {matches.mean():.2f}")

此处利用numpy将tensorflow格式的张量转换,便于利用numpy库中的函数对结果进行进一步操作

Numpy中的张量是可以随时更改数值的,而Tensorflow中的张量是一种类似常量的存在,一旦赋值则不可再被修改,所以我们一般要调用tf.variable来创建供模型参数迭代的tensorflow“变量”

此后便可以对tensorflow变量使用assign赋值(相关的计算操作在使用时查阅即可)

GradientTape

tensorflow相较于Numpy的明显优势,就是可以利用该API检索任意计算结果相对于输入的梯度!!!明天继续看第三章复习计网去了。。。。(泪目)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值