08 线性回归 + 基础优化算法【动手学深度学习v2】

线性回归

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
输出层不当成一层 输入层和权重层放一起

在这里插入图片描述
在这里插入图片描述
x和y是列向量
在这里插入图片描述
因为loss=1/2(y-y_hat)^2
又因为y_hat的平均值=1/n(xi*w+b)=1/n(Xw+b)
在这里插入图片描述
唯一一个有显示解的模型
在这里插入图片描述

基础优化方法

不但更新w
负梯度:下降最快的方向 即图中负梯度的方向
n:学习率 表示我沿着这个方向每次走多远
类似下山的时候每次沿着最陡的地方走一大步 这样很快就可以到山低
在这里插入图片描述
学习率太小需要走很多步 计算很多次梯度
学习率太大会震荡 错过 并不是真正的在下降在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
从零开始实现整个方法,包括数据流水线、模型。损失函数和小批量随机梯度下降优化器。

 
import random
import torch
import matplotlib.pyplot as plt
# from d2l import torch as d2l

def sy_data(w,b,num_examples):
    # 生成y=Xw+b+噪音 X中n行有w列
    X = torch.normal(0, 1, (num_examples, len(w)))
    y = torch.matmul(X, w)+b
    # Xw的内积+b
    y += torch.normal(0, 0.01, y.shape)
    # 再加一个均值为0 方差为0.01的噪音
    print(X,y,y.reshape((-1, 1)))
    # 一个形状尺寸可以为-1。在这种情况下,该值是根据数组的长度和其余维来推断的。 1表示1列,是列向量
    return X, y.reshape((-1, 1))


true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = sy_data(true_w, true_b, 1000)
print('features:', features[0], '\nlabel:', labels[0])

# a=plt.scatter(features[:, 1].detach().numpy(),
#             labels.detach().numpy(), 1);
# print(a)
'''
features: tensor([1.1566, 0.7321]) 
label: tensor([4.0169])
'''


# 接受批量大小 特征矩阵和标签向量作为输入 生成batch_size的小批量
def data_iter(batch_size, features, labels):
    num_examples = len(features)
    indices = list(range(num_examples))# 生成数字0-999
    # 这些样本随机读取 没有特定顺序
    random.shuffle(indices)
    print(random.shuffle(indices))
    for i in range(0, num_examples, batch_size):
        batch_indices = torch.tensor(
            indices[i:min(i + batch_size,num_examples)]
        )
        # 间隔batch_size取数 赋予batch_indicse
        '''
        例如[248, 62, 699, 776, 517, 85, 90, 352, 545, 841]
        tensor([248,  62, 699, 776, 517,  85,  90, 352, 545, 841])
        '''
        print(indices[i:min(i + batch_size,num_examples)])
        print(batch_indices)
    yield features[batch_indices],labels[batch_indices]


batch_size = 10

for X, y in data_iter(batch_size, features, labels):
    print(X, y)
    break

'''
选取样本
X 10*2 因为batchsize=10 w=2
y 10*1
tensor([[-0.6701,  1.0900],
        [ 0.9038, -0.1050],
        [ 0.0928,  1.0312],
        [ 0.2287, -1.0536],
        [-0.5450, -0.8574],
        [-1.0709, -0.6346],
        [-0.4716, -0.9125],
        [ 1.1762,  1.4156],
        [-0.3838,  0.2382],
        [-1.1287, -0.0346]]) 
tensor([[-0.8472],
        [ 6.3714],
        [ 0.8639],
        [ 8.2421],
        [ 6.0209],
        [ 4.2200],
        [ 6.3587],
        [ 1.7371],
        [ 2.6191],
        [ 2.0615]])


'''

#  定义 初始化模型参数
w = torch.normal(0, 0.01, size=(2, 1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
print(w, b)


# 定义模型
def line(X, w, b):
    return torch.matmul(X, w) + b


# 定义损失函数
def squared_loss(y_hat, y):
    # 均方损失
    return (y_hat - y.reshape(y_hat.shape))**2/2# 这里不做均值


# 定义优化算法
'''
小批量随机梯度下降
'''


def sgd(params, lr, batch_size):
    # 更新时不要梯度 params是参数列表 [w,b]
    with torch.no_grad():
        for p in params:
            p -= lr*p.grad/batch_size# 因为在损失函数中没有求均值 所以这里求均值
            # 手动设置梯度为0 这样下一次就不会累积上一次的梯度
            p.grad.zero_()

# 训练过程
lr = 0.03
num_epochs = 3 #整个数据扫三遍
net = line
loss = squared_loss

for epoch in range(num_epochs):
    # 每次处理批量大小的X和y
    for X,y in data_iter(batch_size, features, labels):
        l = loss(net(X, w, b),y) 
        #将X,w,b放进net中做预测,然后预测y与真实y做损失
        
        # 损失就是长为一个批量大小的向量(‘batch_size’,1)
        l.sum().backward()  # 求和之后算梯度
        
        sgd([w, b], lr, batch_size)   # 对w,b进行更新
    # 评价进度,不需要计算梯度
    with torch.no_grad():
        train_l = loss(net(features, w, b), labels)
        print(f'epoch{epoch+1}, loss {float(train_l.mean()):f}')




# 比较真实参数和通过训练学到的参数来评估训练的成功程度
print(f'w的估计误差:{true_w - w.reshape(true_w.shape)}')
print(f'b的估计误差:{true_b - b}')

'''
可以通过增大lr和epoch 使得loss变小
lr=0.03 epoch=3
epoch3, loss 14.084221
w的估计误差:tensor([ 1.7688, -3.2452], grad_fn=<SubBackward0>)
b的估计误差:tensor([3.7988], grad_fn=<RsubBackward1>)

lr=0.5 epoch=10
epoch10, loss 0.000207
w的估计误差:tensor([-0.0082, -0.0007], grad_fn=<SubBackward0>)
b的估计误差:tensor([0.0150], grad_fn=<RsubBackward1>)
'''

使用深度学习框架来简洁地实现线性回归模型生成数据集

import numpy as np
import torch
from torch.utils import data
from d2l import torch as d2l

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


# 调用框架中现有的API来读取数据
def load_array(data_arrays, batch_size, is_train=True):
    """构造一个pytorch数据迭代器"""
    dataset = data.TensorDataset(*data_arrays)  # 将X和 y传进去
    # 得到数据集之后调用DataLoader
    # shuffle:是不是需要随机打乱顺序
    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)))  # 得到x和y

# 使用框架的预定义好的层
from torch import nn

net = nn.Sequential(nn.Linear(2, 1))  # 输入和输出的维度

# 初始化模型参数
# net[0]访问linear,.weight访问w,.data真实值,normal_使用正态分布替换掉data值
print(net[0].weight.data.normal_(0, 0.01))
# bias偏差设置为0
print(net[0].bias.data.fill_(0))

# 计算均方误差使用的是MSELoss类(L2范数)
loss = nn.MSELoss()

# 实例化SGD实例
# net.parameters():所有的参数
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本身带有模型参数
        trainer.zero_grad()  # 梯度清零
        l.backward()
        trainer.step()  # 模型更新
    l = loss(net(features), labels)
    print(f'epoch{epoch + 1}, loss {l:f}')

问题答疑:

  • 关于神经网络 我们是通过误差反馈修改参数

  • batchsize越小收敛越小 一定的噪音使得网络不会偏 泛化性更好

  • 针对batchsize大小的数据集进行网络训练的时候,网络中每个参数更新时的减去的梯度是batchsize中每个样本对应参数梯度求和后取得的平均值

  • 随机梯度下降的‘随机’指的是在样本中随机采集batchsize大小个的元素

  • 统计模型和优化模型 我关心的是收敛到哪 不是收敛快不快

  • 使用yield而不是return 是因为每次生成一个batchsize接着生成就好,还有就是因为习惯

  • 如果样本大小不是批量数的整数倍,那需要随机剔除多余的样本
    吗?答:如果是1000=个样本 batchsize=60 (1)就取剩下的40个(2)丢掉剩下的40个(3)从下一个epoch再补20个

  • 判断收敛?1用验证数据集看其梯度有没有增加 2看两个epoch直接目标函数已经变化不大了就表示收敛了

  • 本质上我们为什么要用SGD(随机梯度下降),是因为大部分的实际loss太复杂,推导不出导数为0的解么?只能逐个batch去逼近? 答:是的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值