torch笔记三 | 线性回归

搭建一个包含一层的全连接层的网络,实现2维数据的回归。y = w1 * x1 + w2 * x2 + b

文章目录

    • 0.基础优化算法
    • 1.从零实现
    • 2.简洁程序实现
    • 3.需要注意的几个问题

0.基础优化算法

梯度下降:挑选一个初始值W0,使用公式 W t = W t − 1 − η ∂ ι W t − 1 W_{t}=W_{t-1}-\eta \frac{\partial \iota }{W_{t-1}} Wt=Wt1ηWt1ι 重复迭代更新W。其中,沿着梯度方向将增加损失函数值;学习率是代表步长的超参数。

小批量随机梯度下降:计算梯度的时候需要对损失函数求导,如果在整个数据集上算梯度太贵了,可以随机采样b个样本来近似损失。b是批量大小。

选择学习率:不能太小,每一次走的步长很小,需要走很多的步骤,每一个步骤都需要计算梯度,而计算梯度是非常贵的;不能太大,可能导致越过最优值,一直在震荡。

选择批量大小:不能太小,每次计算量太小,不适合并行来最大利用计算资源;不能太大,内存消耗增加,浪费计算,例如如果所有样本都是相同的。

小结:梯度下降通过不断沿着反梯度方向更新参数求解,小批量随机梯度下降是深度学习默认的求解算法,两个重要的超参数是批量大小和学习率。

1.从零实现

线性回归是对n维输入的加权,外加偏置,可以看作是单层神经网络。线性回归使用平方损失来衡量预测值和真实值间的差异,有显示解。

# 1.导入模块
import random
import torch

# 2. 制作数据集
def synthetic_data(w, b, num):
    """生成 y = XW + b + 噪声"""
    X = torch.normal(0, 1, (num, len(w)))       # 均值为0,方差为1
    y = torch.matmul(X, w) + b
    y += torch.normal(0, 0.01, y.shape)
    return X, y.reshape((-1, 1))

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

# 3.批量处理
def data_iter(batch_size, features, labels):
    num = len(features)
    indices = list(range(num))
    random.shuffle(indices)                    # 打乱索引
    for i in range(0, num, batch_size):
        batch_indices = torch.tensor(
            indices[i:min(i + batch_size, num)]
        )
        yield features[batch_indices], labels[batch_indices]    # 迭代返回

batch_size = 10

# for X, y in data_iter(batch_size, features, labels):
#     print(X, '\n', y)

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

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

# 5.定义损失函数
def squared_loss(y_hat, y):     # y_hat为预测值
    """均方损失"""
    return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2

# 6.优化算法
def sgd(params, lr, batch_size):
    """小批量随机梯度下降"""
    with torch.no_grad():       # 优化的过程中不需要梯度的计算
        for param in params:
            param -= lr * param.grad / batch_size
            param.grad.zero_()

# 7.训练
lr = 0.05
num_epochs = 3
net = linreg
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形状是(batch_size, 1),而不是一个标量。
        l.sum().backward()
        sgd([w, b], lr, batch_size)     # 使用参数的梯度更新参数
    with torch.no_grad():
        train_l = loss(net(features, w, b), labels)
        print('epoch: %d loss: %.4f' % (epoch+1, train_l.mean().item()))
# 训练完的参数
print(w, b)

2.简洁程序实现

import torch
import torch.utils.data as Data     # 读取数据
import torch.nn as nn
from torch.nn import init           # 初始化模型参数
import numpy as np

# 1.生成数据集
num_features = 2          # 样本的特征数
num_examples = 1000       # 样本个数
true_w = [2, -4.3]
true_b = 4.2
 # 生成样本
features = torch.tensor(np.random.normal(0, 1, (num_examples, num_features)), dtype=torch.float)
# 生成标签
labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b  
# 给标签添加噪声
labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()), dtype=torch.float)    

# 2.读取数据
batch_size = 10
dataset = Data.TensorDataset(features, labels)       # 将训练数据的特征和标签组合
data_iter = Data.DataLoader(dataset, batch_size, shuffle=True)  # 随机读取小批量
# torch.nn仅支持输入一个batch的样本,如果只有单个样本,可使用input.unsqueeze(0)来添加一维。

# 3.定义模型
# 方法1
class LinearNet(nn.Module):
    def __init__(self, n_feature):
        super(LinearNet, self).__init__()
        self.linear = nn.Linear(n_feature, 1)
    # forward 定义前向传播
    def forward(self, x):
        y = self.linear(x)
        return y
model = LinearNet(num_features)

## 方法2
# model = nn.Sequential(
#     nn.Linear(num_features, 1)
# )

## 方法3
# model = nn.Sequential()
# model.add_module('linear', nn.Linear(num_features, 1))

# 查看模型所有的可学习参数
# for param in model.parameters():
#     print(param)

# 4.初始化模型参数

init.normal_(model.linear.weight, mean=0, std=0.01) # 将权重参数每个元素初始化为随机采样于均值为0、标准差为0.01的正态分布
init.constant_(model.linear.bias, val=0)            # 用val值填充

# 5.配置方法
loss = nn.MSELoss()     # 均方误差
optimizer = torch.optim.SGD(model.parameters(), lr=0.03)

# 6.训练
num_epochs = 3
for epoch in range(1, num_epochs + 1):
    for X, y in data_iter:
        output = model(X)               # 前向传播
        l = loss(output, y.view(-1, 1)) # 计算loss
        optimizer.zero_grad()           # 梯度清零,等价于net.zero_grad()
        l.backward()                    # 反向传播
        optimizer.step()                # 调用optim实例的step函数来迭代模型参数
    print('epoch %d, loss: %f' % (epoch, l.item()))

# 7.查看模型参数
dense = model.linear
print(true_w, dense.weight)
print(true_b, dense.bias)

3.需要注意的几个问题

(1)工具包

torch.utils.data模块提供了有关数据处理的工具,
torch.nn模块定义了大量神经网络的层,
torch.nn.init模块定义了各种初始化方法,
torch.optim模块提供了很多常用的优化算法。

(2)访问网络参数

# 网络的定义方式不同,访问网络参数的方法也不一样。尤其在参数初始化时注意。

# class定义的方法时
model.linear.weight
# Sequential定义时,
model[0].weight

(3)学习率扩展

# 可以为不同子网络设置不同的学习率
optimizer =optim.SGD([
                # 如果对某个参数不指定学习率,就使用最外层的默认学习率
                {'params': net.subnet1.parameters()}, # lr=0.03
                {'params': net.subnet2.parameters(), 'lr': 0.01}
            ], lr=0.03)
 
# 不想让学习率固定成一个常数,调整学习率
for param_group in optimizer.param_groups:
    param_group['lr'] *= 0.1 # 学习率为之前的0.1倍

你可能感兴趣的:(torch笔记,深度学习)