动手学深度学习v2笔记-Day2-线性回归

动手学深度学习v2

Day 2


0x00 线性回归

简单且流行的基本回归模型
对n维输入 x n x_n xn的加权 w n w_n wn再加偏差 b b b

  • 模型举例
    y y y = w 1 x 1 w_1x_1 w1x1 + w 2 x 2 w_2x_2 w2x2 + w 3 x 3 w_3x_3 w3x3 + b b b
    上述线性模型(可拓展到全部线性模型),可以看做单层的神经网络

动手学深度学习v2笔记-Day2-线性回归_第1张图片
输入的维度是d 输出维度为1
上图看出一个输入层和一个输出层,带权重的层只有输入层一层,因此叫单层网络

  • 平方损失函数
    l ( y , y ^ ) = 1 2 ( y − y ^ ) 2 l(y, \hat y) = \frac 1 2(y - \hat y)^2 l(y,y^)=21(yy^)2
    以上求解结果越小区别越小,结果越大区别越大

  • 训练
    利用现有数据制作训练集,通常情况下越多越好
    线性模型有显示解析解,但实际过程基本不会有线性问题出现,不追求显示解

  • 优化方法

  1. 梯度下降
    一个模型没有显示解的时候,随机挑选一个初始值(任意初始值),接下来不断沿着梯度下降的方向更新参数,最终获取到最优解。
    w t = w t − 1 − η ∂ l ∂ w t − 1 w_t = w_{t-1} - \eta \frac {\partial l} {\partial{w_{t-1}}} wt=wt1ηwt1l
    η \eta η : 学习率(超参数:需要人工设置的值)
    ∂ l ∂ w t − 1 \frac {\partial l} {\partial{w_{t-1}}} wt1l : 梯度

动手学深度学习v2笔记-Day2-线性回归_第2张图片
太小计算次数太多复杂度高,太大产生震荡现象而拿不到最优解

  1. 小批量随机梯度下降(默认算法)
    直接使用梯度下降太过于复杂,需要对整个训练集数据操作。
    采样b个样本来近似全部数据,b是另一个重要的超参数-采样批量大小(batch)

  2. 总结
    不断地沿着反梯度方向(梯度方向是增长最快的方向)更新参数
    b b b η \eta η是很重要超参数

0x01 手动实现

1.训练数据准备

这里的函数synthetic_data实现了一个带有噪声的线性模型如下:
y = w x + b + c y = wx + b + c y=wx+b+c
w w w = [ 2 , − 3.4 ] T [2, -3.4]^T [2,3.4]T b = 4.2 b = 4.2 b=4.2

import torch
import random

# 此函数会根据输入的w,b的值成对输出y x 我们用作训练集数据
def synthetic_data(w, b, num_examples):
    x = torch.normal(0, 1, (num_examples, len(w)))
    y = torch.matmul(x, w) + b  # pytorch的乘法
    y += torch.normal(0, 0.01, y.shape) # 加上噪声
    return x, y.reshape((-1, 1))

输入w,b的真值测试一下,生成1000个

true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = synthetic_data(true_w, true_b, 1000)
# 输出看一下
for i in range(1000):
    print("数据  ", features[i], " 标签 ", labels[i])

部分输出如下:

数据   tensor([1.9494, 1.3384])  标签  tensor([3.5615])
数据   tensor([ 1.0765, -0.0897])  标签  tensor([6.6455])
数据   tensor([-0.3591, -0.9325])  标签  tensor([6.6370])
数据   tensor([-1.2803, -0.2001])  标签  tensor([2.3254])
数据   tensor([-1.3962,  0.8183])  标签  tensor([-1.3768])
数据   tensor([ 0.5650, -0.4530])  标签  tensor([6.8746])
数据   tensor([-0.5573, -0.3996])  标签  tensor([4.4393])
...

2.抽取batch


# 这里实现的是,每次从训练集中抽取一个batch的数据进行训练
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)])  # 防止最后一组没有一个batch_size的数据
        '''
        yield 生成器 学识浅薄惭愧 从没用过的python关键字 
        查了下暂时理解成return 每次需要时候迭代一次 比list省内存
        每次需要用到下一个的时候才调用
        '''
        yield features[batch_indices], labels[batch_indices]

以下代码测试输出:

batch_size = 10
for x, y in data_iter(batch_size, features, labels):
    print(x)
    print(y)

部分输出如下:

tensor([[-0.4891, -0.2155],
        [-0.4724,  2.2419],
        [ 0.8081, -0.0244],
        [ 0.1264,  0.8761],
        [ 1.5874,  2.0016],
        [ 1.8350,  0.3115],
        [ 0.2307, -1.1173],
        [-1.0151, -0.4539],
        [ 0.0415, -0.4983],
        [ 0.7697,  0.4390]])
tensor([[ 3.9540],
        [-4.3682],
        [ 5.8896],
        [ 1.4842],
        [ 0.5696],
        [ 6.8205],
        [ 8.4443],
        [ 3.7195],
        [ 5.9762],
        [ 4.2288]])
...

3.模型设置
很容易理解,实现一个线性模型:
y = w x + b y = wx + b y=wx+b

def linreg(x, w, b):
    return torch.matmul(x, w) + b

4.损失函数
定义上述均方根损失函数

def squard_loss(y_hat, y):
    return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2  # 这里的reshape目的是为了统一计算,过程中可能一个行向量一个列向量,也可以在之前的步骤中规定好

5.优化算法
小批量随机梯度下降算法的手动实现

# lr 学习率
# batch_size 批量大小
def sgd(params, lr, batch_size):
    with torch.no_grad():
        for param in params:
            param -= lr * param.grad / batch_size  # 之前没有求均值,在这里求
            param.grad.zero_()  # 防止梯度累加

6.训练验证

# 定义超参数
lr = 0.03  # 学习率
num_epochs = 3  # 数据扫三遍
# 以下是为了多个网络方便编程,感觉像是函数别名
net = linreg
loss = squared_loss

# w b 都需要记录梯度
w = torch.normal(0, 0.01, size=(2, 1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)

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()  # 这里l形状(batch_size, 1) 不是标量,因此计算梯度利用sum()计算
        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-string语法 {}内直接显示为变量 :f是保留6位精度

print(f"w的估计误差:{true_w - w.reshape(true_w.shape)}")
print(f"b的估计误差:{true_b - b}")

结果如下:

epoch 1, loss 0.034460
epoch 2, loss 0.000128
epoch 3, loss 0.000056
w的估计误差:tensor([0.0005, 0.0001], grad_fn=<SubBackward0>)
b的估计误差:tensor([5.6744e-05], grad_fn=<RsubBackward1>)

0x02 pytorch实现

1.准备数据集

import torch
import torch.utils import data

true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = synthetic_data(true_w, true_b, 1000)

2.构造了一个pytorch的数据迭代器

  • TensorDataset() 传入两个参数分别是 样本数据,样本标签 并根据第一个张量(样本数据)的维度返回一一对应的样本
  • Dataloader 随机取batch_size个数据
def load_array(data_arrays, batch_size, is_train=True):
    # 此处*将列表元素分别当做两个参数传入 即features 和 labels
    dataset = data.TensorDataset(*data_arrays)
    return data.DataLoader(dataset, batch_size, shuffle=is_train)

batch_size = 10
data_iter = load_array((features, labels), batch_size)
print(next(iter(data_iter)))

输出如下:

# 此处为了清晰我手动调整了格式
[
tensor([[-0.1956, -0.9790],
        [-0.8501,  1.4546],
        [ 0.0941,  1.3185],
        [ 1.6486, -0.5259],
        [ 0.9986,  0.7224],
        [ 0.0788, -0.0592],
        [-0.7319,  0.0070],
        [-1.3904, -1.5994],
        [ 0.0679,  0.9106],
        [ 0.1502, -1.1432]]), 
tensor([[ 7.1412],  
        [-2.4247],
        [-0.0891],
        [ 9.2879],
        [ 3.7425],
        [ 4.5828],
        [ 2.7132],
        [ 6.8642],
        [ 1.2361],
        [ 8.3878]])
]

3.模型定义

from torch import nn  # 这个类有大量定义好的层

# 此处Squential是将这个线性层放到一个list中,深度神经网络中更方便
net = nn.Squential(nn.Linear(2, 1))  # Linear全连接层、线性层

4.损失函数和优化算法

nn.MSELoss()    # 均方误差损失函数
torch.optim.SGD(网络参数, 学习率)   # 小批量随机梯度下降算法 

5.训练验证

# 初始化参数
net[0].weight.data.normal_(0, 0.01)  # w
net[0].bias.data.fill_(0)            # b

# 均方误差 平方L2范数
loss = nn.MSELoss()
trainer = torch.optim.SGD(net.parameters(), lr=0.03)

# 次数
num_epochs = 3

# 训练
for epoch in range(num_epochs):
    for x,y in data_iter:               # 拿出一个批次数据
        l = loss(net(x), y)             # net已经初始化w、b无需传入 net(x) 是预测值 y是标签
        trainer.zero_grad()             # 梯度清零
        l.backward()                    # 计算梯度 pytorch自动做了sum操作
        trainer.step()                  # 更新模型w b
    l = loss(net(features), labels)     # 计算一下偏差
    print(f"epoch {epoch + 1}, loss {l:f}")

输出如下:

epoch 1, loss 0.000309
epoch 2, loss 0.000104
epoch 3, loss 0.000105

你可能感兴趣的:(动手学深度学习v2笔记,深度学习,学习,机器学习,人工智能,pytorch)