今日通过对深度学习tensorflow库中的模型训练过程的细节进行了重新的理解,自己对深度学习底层框架有了更深的认知能力,确实觉得自己需要再细细过一遍深度学习相关的基础内容知识,正所谓语速则不达,加快进度在本月往后推进
更新模型权重时,我们最开始的思路是每次更新一个标量系数(保持其他参数不变),将其增大、减小,分别查看各自的损失值变化等。
但更好的梯度下降算法巧妙通过导数等数学理论简化了这一过程
随机梯度下降SGD后续衍生出了多种变体,例如带动量的Adagard,RMSprop等,这些变体会考虑上一次权重更新,而不仅局限于当前梯度,这些变体也就是在模型中经常用到的优化器
!!!利用动量因子我们可以有效避免局部最小值
利用连续导数相乘实现链式求导法则并求解得到损失函数相对于某个参数的梯度值
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检索任意计算结果相对于输入的梯度!!!明天继续看第三章复习计网去了。。。。(泪目)