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

今日通过对深度学习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检索任意计算结果相对于输入的梯度!!!明天继续看第三章复习计网去了。。。。(泪目)

你可能感兴趣的:(学习,深度学习)