【深度学习】基于Pytorch的线性模型概念辨析和实现(二)

【深度学习】基于Pytorch的线性模型概念辨析和实现(二) 这把我C 2023-04-25 13:51:39 6035

在这里插入图片描述 @[toc]

1 线性回归的从零开始实现

安装包:

!pip install git+https://github.com/d2l-ai/d2l-zh@release  # installing d2l
%matplotlib inline
import random
import torch
from d2l import torch as d2l

生成数据集 为了简单起见,我们将[根据带有噪声的线性模型构造一个人造数据集。] 我们的任务是使用这个有限样本的数据集来恢复这个模型的参数。 我们将使用低维数据,这样可以很容易地将其可视化。 在这里插入图片描述 你可以将 𝜖 视为捕获特征和标签时的潜在观测误差。在这里我们认为标准假设成立,即 𝜖 服从均值为0的正态分布。 为了简化问题,我们将标准差设为0.01。下面的代码生成合成数据集。

def synthetic_data(w, b, num_examples):  #@save
    """生成 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))
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = synthetic_data(true_w, true_b, 1000)

注意,[features 中的每一行都包含一个二维数据样本,labels 中的每一行都包含一维标签值(一个标量)]。

在这里插入图片描述

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

在这里插入图片描述 读取数据集

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_indices = torch.tensor(indices[i:min(i +
                                                   batch_size, num_examples)])
        yield features[batch_indices], labels[batch_indices]
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)

(定义模型)

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

[定义损失函数]

def squared_loss(y_hat, y):  #@save
    """均方损失。"""
    return (y_hat - y.reshape(y_hat.shape))**2 / 2

(定义优化算法)

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

2 训练

现在我们已经准备好了模型训练所有需要的要素,可以实现主要的[训练过程]部分了。 理解这段代码至关重要,因为在整个深度学习的职业生涯中,你会一遍又一遍地看到几乎相同的训练过程。 在每次迭代中,我们读取一小批量训练样本,并通过我们的模型来获得一组预测。 计算完损失后,我们开始反向传播,存储每个参数的梯度。最后,我们调用优化算法 sgd 来更新模型参数。

概括一下,我们将执行以下循环:

  • 初始化参数
  • 重复,直到完成
    • 计算梯度 $\mathbf{g} \leftarrow \partial{(\mathbf{w},b)} \frac{1}{|\mathcal{B}|} \sum{i \in \mathcal{B}} l(\mathbf{x}^{(i)}, y^{(i)}, \mathbf{w}, b)$
    • 更新参数 $(\mathbf{w}, b) \leftarrow (\mathbf{w}, b) - \eta \mathbf{g}$

在每个迭代周期(epoch)中,我们使用 data_iter 函数遍历整个数据集,并将训练数据集中所有样本都使用一次(假设样本数能够被批量大小整除)。这里的迭代周期个数num_epochs和学习率lr都是超参数,分别设为3和0.03。设置超参数很棘手,需要通过反复试验进行调整。 我们现在忽略这些细节,以后会在 :numref:chap_optimization 中详细介绍。

lr = 0.03
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)  # `X`和`y`的小批量损失
        # 因为`l`形状是(`batch_size`, 1),而不是一个标量。`l`中的所有元素被加到一起,
        # 并以此计算关于[`w`, `b`]的梯度
        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}')

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

3 关于with

with是python中上下文管理器,简单理解,当要进行固定的进入,返回操作时,可以将对应需要的操作,放在with所需要的语句中。比如文件的写入(需要打开关闭文件)等。

以下为一个文件写入使用with的例子。

        with open (filename,'w') as sh:    
            sh.write("#!/bin/bash\n")
            sh.write("#$ -N "+'IC'+altas+str(patientNumber)+altas+'\n')
            sh.write("#$ -o "+pathSh+altas+'log.log\n') 
            sh.write("#$ -e "+pathSh+altas+'err.log\n') 
            sh.write('source ~/.bashrc\n')          
            sh.write('. "/home/kjsun/anaconda3/etc/profile.d/conda.sh"\n')
            sh.write('conda activate python27\n')
            sh.write('echo "to python"\n')
            sh.write('echo "finish"\n')
            sh.close()

with后部分,可以将with后的语句运行,将其返回结果给到as后的变量(sh),之后的代码块对close进行操作。

4 关于with torch.no_grad():

在使用pytorch时,并不是所有的操作都需要进行计算图的生成(计算过程的构建,以便梯度反向传播等操作)。而对于tensor的计算操作,默认是要进行计算图的构建的,在这种情况下,可以使用 with torch.no_grad():,强制之后的内容不进行计算图构建。

以下分别为使用和不使用的情况: 在这一过程中只使用张量和自动微分,不需要定义层或复杂的优化器

5 简介实现

5.1 定义模型

在 PyTorch 中,全连接层在 Linear 类中定义。值得注意的是,我们将两个参数传递到 nn.Linear 中。第一个指定输入特征形状,即 2,第二个指定输出特征形状,输出特征形状为单个标量,因此为 1。

# `nn` 是神经网络的缩写
from torch import nn

net = nn.Sequential(nn.Linear(2, 1))

5.2 初始化模型参数

net[0].weight.data.normal_(0, 0.01)
net[0].bias.data.fill_(0)

5.3 定义损失函数

loss = nn.MSELoss()

5.4 定义优化算法

trainer = torch.optim.SGD(net.parameters(), lr=0.03)

5.5 训练

在每个迭代周期里,我们将完整遍历一次数据集(train_data),不停地从中获取一个小批量的输入和相应的标签。对于每一个小批量,我们会进行以下步骤:

通过调用 net(X) 生成预测并计算损失 l(正向传播)。 通过进行反向传播来计算梯度。 通过调用优化器来更新模型参数。 为了更好的衡量训练效果,我们计算每个迭代周期后的损失,并打印它来监控训练过程。

num_epochs = 3
for epoch in range(num_epochs):
    for X, y in data_iter:
        l = loss(net(X), y)#通过调用 net(X) 生成预测并计算损失 l(正向传播)
        trainer.zero_grad()#梯度清0
        l.backward()#通过进行反向传播来计算梯度。
        trainer.step()#通过调用优化器来更新模型参数。
    l = loss(net(features), labels)
    print(f'epoch {epoch + 1}, loss {l:f}')

在这里插入图片描述 我们可以使用 PyTorch 的高级 API更简洁地实现模型。 在 PyTorch 中,data 模块提供了数据处理工具,nn 模块定义了大量的神经网络层和常见损失函数。 我们可以通过_ 结尾的方法将参数替换,从而初始化参数。

6 Pytorch完成模型常用API

在前一部分,我们自己实现了通过torch的相关方法完成反向传播和参数更新,在pytorch中预设了一些更加灵活简单的对象,让我们来构造模型、定义损失,优化损失等

那么接下来,我们一起来了解一下其中常用的API

6.1 nn.Module

nn.Modul 是torch.nn提供的一个类,是pytorch中我们自定义网络的一个基类,在这个类中定义了很多有用的方法,让我们在继承这个类定义网络的时候非常简单 用前面的y = wx+b的模型举例如下:

from torch import nn
class Lr(nn.Module):
    def __init__(self):
        super(Lr, self).__init__()  #继承父类init的参数
        self.linear = nn.Linear(1, 1) 

    def forward(self, x):
        out = self.linear(x)
        return out

nn.Linear为torch预定义好的线性模型,也被称为全链接层,传入的参数为输入的数量,输出的数量(in_features, out_features),是不算(batch_size的列数) nn.Module定义了call方法,实现的就是调用forward方法,即Lr的实例,能够直接被传入参数调用,实际上调用的是forward方法并传入参数

# 实例化模型
model = Lr()
# 传入数据,计算结果
predict = model(x)

6.2 优化器类

优化器(optimizer),可以理解为torch为我们封装的用来进行更新参数的方法,比如常见的随机梯度下降(stochastic gradient descent,SGD)

优化器类都是由torch.optim提供的,例如

torch.optim.SGD(参数,学习率) torch.optim.Adam(参数,学习率) 注意:

参数可以使用model.parameters()来获取,获取模型中所有requires_grad=True的参数 优化类的使用方法 实例化 所有参数的梯度,将其值置为0 反向传播计算梯度 更新参数值 示例如下:

optimizer = optim.SGD(model.parameters(), lr=1e-3) #1. 实例化
optimizer.zero_grad() #2. 梯度置为0
loss.backward() #3. 计算梯度
optimizer.step()  #4. 更新参数的值

6.3 损失函数

前面的例子是一个回归问题,torch中也预测了很多损失函数

均方误差:nn.MSELoss(),常用于回归问题 交叉熵损失:nn.CrossEntropyLoss(),常用于分类问题 使用方法

model = Lr() #1. 实例化模型
criterion = nn.MSELoss() #2. 实例化损失函数
optimizer = optim.SGD(model.parameters(), lr=1e-3) #3. 实例化优化器类
for i in range(100):
    y_predict = model(x_true) #4. 向前计算预测值
    loss = criterion(y_true,y_predict) #5. 调用损失函数传入真实值和预测值,得到损失结果
    optimizer.zero_grad() #5. 当前循环参数梯度置为0
    loss.backward() #6. 计算梯度
    optimizer.step()  #7. 更新参数的值

6.4 Adam

Adam(Adaptive Moment Estimation)算法是将Momentum算法和RMSProp算法结合起来使用的一种算法,能够达到防止梯度的摆幅多大,同时还能够加开收敛速度

torch中的api为:torch.optim.Adam() 在这里插入图片描述

声明:本文内容由易百纳平台入驻作者撰写,文章观点仅代表作者本人,不代表易百纳立场。如有内容侵权或者其他问题,请联系本站进行删除。
红包 93 收藏 评论 打赏
评论
0个
内容存在敏感词
手气红包
    易百纳技术社区暂无数据
相关专栏
置顶时间设置
结束时间
删除原因
  • 广告/SPAM
  • 恶意灌水
  • 违规内容
  • 文不对题
  • 重复发帖
打赏作者
易百纳技术社区
这把我C
您的支持将鼓励我继续创作!
打赏金额:
¥1易百纳技术社区
¥5易百纳技术社区
¥10易百纳技术社区
¥50易百纳技术社区
¥100易百纳技术社区
支付方式:
微信支付
支付宝支付
易百纳技术社区微信支付
易百纳技术社区
打赏成功!

感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~

举报反馈

举报类型

  • 内容涉黄/赌/毒
  • 内容侵权/抄袭
  • 政治相关
  • 涉嫌广告
  • 侮辱谩骂
  • 其他

详细说明

审核成功

发布时间设置
发布时间:
是否关联周任务-专栏模块

审核失败

失败原因
备注
拼手气红包 红包规则
祝福语
恭喜发财,大吉大利!
红包金额
红包最小金额不能低于5元
红包数量
红包数量范围10~50个
余额支付
当前余额:
可前往问答、专栏板块获取收益 去获取
取 消 确 定

小包子的红包

恭喜发财,大吉大利

已领取20/40,共1.6元 红包规则

    易百纳技术社区