d2l学习笔记 SGD的从0开始和调库实现

  • 线性回归问题定义
  • 数据集
  • 从0开始的实现
    • 生成人工数据集
    • 随机访问样本
    • 线性回归模型
    • 损失函数
    • 优化函数
    • 训练函数
  • 调库实现


\quad 这是我在学习d2l以及使用Pytorch进行机器学习课程学习的过程中整理的笔记以及一些思考,希望能对同样开始学习机器学习的你有所帮助,如有问题还请交流更正。


环境是PyCharm2022.2.1		Python 3.8(conda) 
pip install d2l, torch, torchvision

线性回归问题定义

d2l学习笔记 SGD的从0开始和调库实现_第1张图片
\quad 线性回归的经典模型就是房价预测问题,就比如你的好朋友有一个房子要卖,而你现在知道的是交易市场上发生过的交易中,房子的占地面积以及交易价格。你要做的就是找出一条曲线对数据集进行拟合,然后在定价时给你的好朋友一些建议和指导。


数据集

\quad 线性回归问题是一个监督学习(Supervised learning) 问题。那么什么是监督学习呢?

-监督学习:利用一组已知类别的样本调整分类器的参数,使其达到所要求性能的过程,也称为监督训练或有教师学习。

\quad 通俗的来讲,就是我一直一些输入(房子的占地面积)和真是结果(放假),我需要使我的模型输出结果尽可能地接近真是结果。这样进行的就是监督学习。


从0开始的实现

生成人工数据集

定义synthetic_data(2, b, num_examples)函数,来生成训练需要的人工数据集。函数传入的参数是真实曲线的斜率、截距以及样本数量。

def synthetic_data(w, b, num_examples):
    """生成 y = Xw + b + 噪声"""
    X = torch.normal(0, 1, (num_examples, len(w)))
    y = torch.matmul(X, w) + b
    y += torch.normal(0, 0.01, y.shape)

    return X, y.reshape((-1, 1))

随机访问样本

定义data_iter(batch_size, features, lables)函数,来对样本数据集进行随机访问。函数传入的参数是读取步长,样本数量和标签。这里的random.shuffle()函数的目的是为了打乱编号,来实现随机读取的目的。而随机读取则是为了后面的小批量随机梯度下降,也就是SGD做准备。每次返回一个batch_size大小的数据。

def data_iter(batch_size, features, labels):
    """随机访问样本"""
    num_examples = len(features)
    indices = list(range(num_examples))

    # 随机读取样本,没有特定的顺序
    random.shuffle(indices)

    for i in range(0, num_examples, batch_size):  # batch_size为步长
        batch_indices = torch.tensor(indices[i: min(i + batch_size, num_examples)])
        yield features[batch_indices], labels[batch_indices]

线性回归模型

linereg(X, w, b)函数的功能就是实现线性回归函数的计算,就是一个简单的矩阵计算。返回值为线性回归模型的计算结果。

def linereg(X, w, b):
    """线性回归模型"""
    return torch.matmul(X, w) + b

损失函数

在SGD中我们使用MSELoss,也就是均方损失,计算的是预测值和真实值之间差的平方值,再求和取平均。 1 n ∑ i = 1 n ( y ^ − y ) 2 \frac{1}{n}\sum_{i = 1}^{n}(\hat{y}-y)^{2} n1i=1n(y^y)2

def squared_loss(y_hat, y):
    """均方损失"""
    loss = (y_hat - y.reshape(y_hat.shape)) ** 2 / 2
    return loss        #统一矩阵形状

优化函数

SGD的核心。sgd(params, lr, batch_size)函数用来实现梯度下降。函数传入的参数为**权重参数、学习率和步长。**低度下降的公式为:
W t ⃗ = W t − 1 ⃗ − η α α W t − 1 ⃗ \vec{W_{t}} = \vec{W_{t-1}}-\eta\frac{\alpha}{\alpha \vec{W_{t-1}}} Wt =Wt1 ηαWt1 α

def sgd(params, lr, batch_size):
    """小批量随机梯度下降"""
    with torch.no_grad():
        for param in params:
            param -= lr * param.grad / batch_size
            param.grad.zero_()

训练函数

中规中矩的训练函数

if __name__ == '__main__':
    true_w = torch.tensor(([2, -3.4]))
    true_b = 4.2
    features, labels = synthetic_data(true_w, true_b, 1000)

    d2l.set_figsize()
    d2l.plt.scatter(features[:, 1].detach().numpy(), labels.detach().numpy(), 1)
    # plt.show()

    #############################
    batch_size = 10

    """for X, y in data_iter(batch_size, features, labels):
        print(X, '\n', y)
        break"""
    ############################
    w = torch.normal(0, 0.01, size=(2, 1), requires_grad=True)
    b = torch.zeros(1, requires_grad=True)

    ###########################
    """训练过程"""
    lr = 0.03
    num_epochs = 3
    net = linereg
    loss = squared_loss
    for epoch in range(num_epochs):
        for X, y in data_iter(batch_size, features, labels):
            l = loss(net(X, w, b), y)
            l.sum().backward()
            sgd([w, b], lr, batch_size)
        with torch.no_grad():
            train_l = loss(net(features, w, b), labels)
            print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')

调库实现

整体实现步骤和原理与上述过程是一致的,知识使用了现成的d2l中的函数库来简便实现了,代码如下。

import torch
from torch.utils import data
from d2l import torch as d2l
from torch import nn


def load_array(data_arrays, batch_size, is_train=True):
    """构造Pytorch数据迭代器"""
    dataset = data.TensorDataset(*data_arrays)
    return data.DataLoader(dataset, batch_size, shuffle=is_train)


if __name__ == '__main__':

    true_w = torch.tensor([2, -3.4])
    true_b = 4.2
    features, labels = d2l.synthetic_data(true_w, true_b, 1000)
    """输入输出维度"""
    net = nn.Sequential(nn.Linear(2, 1))
    loss = nn.MSELoss()
    trainer = torch.optim.SGD(net.parameters(), lr=0.03)

    batch_size = 10
    data_iter = load_array((features, labels), batch_size)
    # print(next(iter(data_iter)))
    net[0].weight.data.normal_(0, 0.01)
    net[0].bias.data.fill_(0)

    num_epochs = 3
    for epoch in range(num_epochs):
        for X, y in data_iter:
            l = loss(net(X), y)
            trainer.zero_grad()
            l.backward()
            trainer.step()
        l = loss(net(features), labels)
        print(f'epoch {epoch + 1}, loss {l : f}')
    print(net[0].weight)
    print(net[0].bias)

梯度下降到此就结束啦

你可能感兴趣的:(机器学习,学习,python,机器学习)