线性回归代码解析

226 阅读3分钟

线性回归模型代码

以下代码来自 d2l.ai/chapter_lin…

只是加上我自己的认知的注释:

import tensorflow as tf
import matplotlib.pyplot as plt

# d2l 的命名 dive into deeplearning
from d2l import tensorflow as d2l

class LinearRegressionScratch(d2l.Module):  #@save
    """The linear regression model implemented from scratch."""
    def __init__(self, num_inputs, lr, sigma=0.01):
        super().__init__()
        self.save_hyperparameters()
        w = tf.random.normal((num_inputs, 1), mean=0, stddev=0.01) # 随机生成num_inputs行,1列满足正态分布的矩阵,mean=0是均值,steddev是标准差
        b = tf.zeros(1) # 生成一个(1,) 张量
        self.w = tf.Variable(w, trainable=True) # w 和 b 标记为可训练变量, 那就在训练的时候会更新它的值 
        self.b = tf.Variable(b, trainable=True)

# forward 方法通常用于定义模型的前向传播过程。在机器学习和深度学习中,前向传播是指将输入数据通过模型的计算过程,得到输出结果的过程
# 线性模型相当于一个最简单的神经元
@d2l.add_to_class(LinearRegressionScratch)  #@save
def forward(self, X):
    return tf.matmul(X, self.w) + self.b

# 定义损失函数
@d2l.add_to_class(LinearRegressionScratch)  #@save
def loss(self, y_hat, y):
    l = (y_hat - y) ** 2 / 2 # y_hat 表示预测值,y 表示真实值
    return tf.reduce_mean(l) # reduce_mean 表示讲损失的张量,降维成标量,然后计算平均值。常见就是整个张量求和取平均


# 定义随机梯度算法用来最小化损失函数
class SGD(d2l.HyperParameters):  #@save
    """Minibatch stochastic gradient descent."""
    def __init__(self, lr):
        self.save_hyperparameters() #  保存一些超参数之类的学习率,批量大小,迭代次数,正则化参数;lr 是学习率表示每次batch调参的幅度

    def apply_gradients(self, grads_and_vars): # 用于优化器中得到应用梯度更新。
        for grad, param in grads_and_vars:
            param.assign_sub(self.lr * grad)

# 给线性模型提供优化算法
@d2l.add_to_class(LinearRegressionScratch)  #@save
def configure_optimizers(self):
    return SGD(self.lr)

@d2l.add_to_class(d2l.Trainer)  #@save
def prepare_batch(self, batch):
    return batch

@d2l.add_to_class(d2l.Trainer)  #@save
def fit_epoch(self):
    # 一个epoch 指将整个数据集训练完,tensorflow 库里面默认就是这段线性的fit_epoch代码

    # 模型进入训练模式
    self.model.training = True
    for batch in self.train_dataloader:
        # 每次使用一个batch 的训练数据
        with tf.GradientTape() as tape:
            # 使用模型的中 training_step, MSE 均方误差如上定义
            loss = self.model.training_step(self.prepare_batch(batch))

        # tape.gradient 损失函数 相对于w, b求导(梯度)
        print(self.model.trainable_variables)
        grads = tape.gradient(loss, self.model.trainable_variables)

        #  设置了梯度裁剪的阈值(gradient_clip_val)则在这一步对梯度进行裁剪。
        #  梯度裁剪是为了防止梯度爆炸的问题,即梯度值过大导致训练不稳定
        if self.gradient_clip_val > 0:
            grads = self.clip_gradients(self.gradient_clip_val, grads)
        self.optim.apply_gradients(zip(grads, self.model.trainable_variables)) # 用更新后的梯度和训练数据优化
        self.train_batch_idx += 1

    if self.val_dataloader is None:
        return
    self.model.training = False
    # 用来定义模型在验证集上的验证过程
    for batch in self.val_dataloader:
        self.model.validation_step(self.prepare_batch(batch))
        self.val_batch_idx += 1
    
    plt.savefig('my_plot.png')



##################################### 主流程 #########################################
# 定义线性模型        
model = LinearRegressionScratch(2, lr=0.03)

# 默认生成 2000 组数据加上噪声的,其中 1000 组数据用来训练,1000 组数据用来验证。
data = d2l.SyntheticRegressionData(w=tf.constant([2, -3.4]), b=4.2)

# 打印出来是个 2000*2 的矩阵
# print(data.X)

# 2000 * 1 矩阵
# print(data.y)

# 创建训练器
trainer = d2l.Trainer(max_epochs=3)

# 开始训练,fit 大致内容
# 1. prepare_data(data)
#    1.1 self.train_dataloader = data.train_dataloader() # 训练数据加载到 self.train_dataloader
#    1.2 self.val_dataloader = data.val_dataloader() # 验证数据加载到 self.val_dataloader
#    加载后 2000 组数据会被分成 32 batch batch大小在SyntheticRegressionData定义
# 2. self.prepare_model(model)
# 3. 训练 max_epochs=3 轮fit_epoch,fit_epoch 需要用户去实现。
trainer.fit(model, data)


# 计算误差
print(f'error in estimating w: {data.w - tf.reshape(model.w, data.w.shape)}')
print(f'error in estimating b: {data.b - model.b}')

小批量梯度下降介绍:

image.png

公式的简单推导

image.png