pytorch学习_Autograd & Optimization

确定神经网络中的参数需要两个步骤:

  1. Forward Propagation:正向传播,就是神经网络利用自己“猜测”最好的参数,根据input data得到output(包括各个node的值)。
  2. Backward Propagation: 反向传播,神经网络通过正向传播的output得到的error适应自己的参数。更新权重参数的过程是在BP阶段完成的。

模型中的参数根据损失函数的梯度来确定。为了计算这些梯度,Pytorch有一些内置的引擎,叫torch.autograd。(模型中具体如何通过BP来确定参数,可以去看一哈3Blue1Brown的视频)

另外:pytorch官网上有两个关于aotograd的tutorial,个人建议看第二个。
https://pytorch.org/tutorials/beginner/blitz/autograd_tutorial.html#sphx-glr-beginner-blitz-autograd-tutorial-py

梯度计算:

一个最简单的一层网络模型。

import torch

x = torch.ones(5)  # input tensor
y = torch.zeros(3)  # expected output
w = torch.randn(5, 3, requires_grad=True)
b = torch.randn(3, requires_grad=True)
z = torch.matmul(x, w)+b
loss = torch.nn.functional.binary_cross_entropy_with_logits(z, y)

将上述代码转化成计算图如下:
简单一层网络的计算图
其中w和b是网络中的待求参数,需要计算每个w和b的损失函数的梯度来确定他们的取值。为了完成这些,我们需要设置requires_grad属性。(原文中是property,不是attribute,查了以下是有一点区别的)。只有当该属性为True时,梯度才能被计算。

其实我们对tensors构建计算图的方法是一个类的对象,这个对象知道如何进行直接的正向计算也能计算反向传播中的导数。

当然,为了寻找神经网络中最合适的参数,我们需要计算每一个w和b对应的损失函数的导数。为了计算这些导数,我们调用 loss.backward(),再从w.gradb.grad中获得这些导数值。

loss.backward()
print(w.grad)		//这个就是y对w的导数。
print(b.grad)

结果为:
tensor([[0.2183, 0.0097, 0.1539],
        [0.2183, 0.0097, 0.1539],
        [0.2183, 0.0097, 0.1539],
        [0.2183, 0.0097, 0.1539],
        [0.2183, 0.0097, 0.1539]])
tensor([0.2183, 0.0097, 0.1539])

注:只有当requires_grad的属性为Trure时,grad的属性才能被访问。而在网格中所有节点中,只有叶子节点的该属性可以被设为True。
为了保证性能,我们只能再使用给定网格中使用backward才能进行梯度计算。如果我们要对同一个网格的backward做更多操作,可以通过retain_graph=True对backward进行调用。

取消Gradient Tracking

默认情况下,tensor设置中的requires_grad=True,这样可以追溯之前的计算历史,支持梯度计算。但是在一些情况下我们需要忽略一些历史信息。例如,我们有一个已经训练好的模型,只需要它来计算input data然后产生一个输出就可以了。用torch.no_grad()方法可以实现:

z = torch.matmul(x, w)+b
print(z.requires_grad)

with torch.no_grad():
    z = torch.matmul(x, w)+b
print(z.requires_grad)

输出为:
True
False

还有一种方法,是detach()方法。

z = torch.matmul(x, w)+b
z_det = z.detach()
print(z_det.requires_grad)

一些情况下需要disable gradient tracking:

  1. 使一些参数为“冻结参数”,这经常在微调预训练网络中被使用。
  2. 加速计算,在只有前馈计算的情况下。

优化模型参数:

import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets
from torchvision.transforms import ToTensor, Lambda

training_data = datasets.FashionMNIST(
    root="data",
    train=True,
    download=True,
    transform=ToTensor()
)


test_data = datasets.FashionMNIST(
    root="data",
    train=False,
    download=True,
    transform=ToTensor()
)

train_dataloader = DataLoader(training_data, batch_size=64)
test_dataloader = DataLoader(test_data, batch_size=64)

class NeuralNetwork(nn.Module):
    def __init__(self):
        super(NeuralNetwork, self).__init__()
        self.flatten = nn.Flatten()
        self.linear_relu_stack = nn.Sequential(
            nn.Linear(28*28, 512),
            nn.ReLU(),
            nn.Linear(512, 512),
            nn.ReLU(),
            nn.Linear(512, 10),
            nn.ReLU()
        )

    def forward(self, x):
        x = self.flatten(x)
        logits = self.linear_relu_stack(x)
        return logits

model = NeuralNetwork()

重要参数:

超参数:是人为设置的,控制模型优化过程的参数。不同的超参数影响着模型训练和收敛速度。有以下超参数:
· Number of Epochs - 整个数据集迭代的次数。
· Batch Size - 在参数更新前,数据样本传递给网络的次数。
· Learning Rate - 被一batch和ephoch的“更新力度”。

其中,epochs主要包含以下两个主要部分:
Train Loop 和 Validation/Test Loop。 从名字中就可以看出端倪,前者是训练循环,后者是验证循环。

损失函数:常用的损失函数有 nn.MSELoss(拟合任务), nn.NLLLoss(分类任务),nn.CrossEntropyLoss(结合了nn.LogSoftmas和nn.NLLLoss)

优化器:优化算法定义了模型参数的计算过程。所有优化方法都被封装在一个 optimizer 对象里面。这里,我们使用SGD optimizer,

在整个training loop中,优化发生在三个步骤中。

  1. 调用 optimizer.zero_grad()去重新设置模型参数的梯度。梯度默认设置是增加的,为了防止重复计算,我们在每个迭代开始时将初值设为0。
  2. 反向传播,预测loss时调用 loss.backwards()。
  3. 一旦我们拥有了梯度,我们就会调用optimizer.step(),通过反向计算中的梯度来更新新梯度。

这些重要参数的设置代码:

learning_rate = 1e-3
batch_size = 64
epochs = 5
loss_fn = nn.CrossEntropyLoss()

optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)

部署:

先放一个完整代码:

import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets
from torchvision.transforms import ToTensor, Lambda


class NeuralNetwork(nn.Module):
    def __init__(self):
        super(NeuralNetwork, self).__init__()
        self.flatten = nn.Flatten()
        self.linear_relu_stack = nn.Sequential(
            nn.Linear(28*28, 512),
            nn.ReLU(),
            nn.Linear(512, 512),
            nn.ReLU(),
            nn.Linear(512, 10),
            nn.ReLU()
        )

    def forward(self, x):
        x = self.flatten(x)
        logits = self.linear_relu_stack(x)
        return logits


def train_loop(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)
    for batch, (X, y) in enumerate(dataloader):
        # Compute prediction and loss
        pred = model(X)
        loss = loss_fn(pred, y)

        # Backpropagation
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        if batch % 100 == 0:
            loss, current = loss.item(), batch * len(X)
            print(f"loss: {loss:>7f}  [{current:>5d}/{size:>5d}]")


def test_loop(dataloader, model, loss_fn):
    size = len(dataloader.dataset)
    num_batches = len(dataloader)
    test_loss, correct = 0, 0

    with torch.no_grad():
        for X, y in dataloader:
            pred = model(X)
            test_loss += loss_fn(pred, y).item()
            correct += (pred.argmax(1) == y).type(torch.float).sum().item()

    test_loss /= num_batches
    correct /= size
    print(f"Test Error: \n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \n")


training_data = datasets.FashionMNIST(
    root="data",
    train=True,
    download=True,
    transform=ToTensor()
)

test_data = datasets.FashionMNIST(
    root="data",
    train=False,
    download=True,
    transform=ToTensor()
)

train_dataloader = DataLoader(training_data, batch_size=64)
test_dataloader = DataLoader(test_data, batch_size=64)

model = NeuralNetwork()

learning_rate = 1e-3
batch_size = 64
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
epochs = 10
for t in range(epochs):
    print(f"Epoch {t+1}\n-------------------------------")
    train_loop(train_dataloader, model, loss_fn, optimizer)
    test_loop(test_dataloader, model, loss_fn)
print("Done!")

具体解释:

  1. 首先来看一下只有一个训练步骤的模型。举个例子:我们将一个已经训练好了的resnet18作为我们的模型。创造一个random tensor,代表一个三通道的图像,并且它的长和宽都是64。(36464)同样,labels也给随机掉。
import torch, torchvision
model = torchvision.models.resnet18(pretrained=True)
data = torch.rand(1, 3, 64, 64)
labels = torch.rand(1, 1000)
  1. 用前馈 forward pass来计算结果。
prediction = model(data) # forward pass
  1. 定义损失函数 loss,利用反向传播 backpropagation来将error传播到整个网络。反向传播在我们对 loss 调用.backward() 的时候就被使用,自动计算梯度并且将求得的结果存在模型参数的.grad属性中。
loss = (prediction - labels).sum()
loss.backward() # backward pass
  1. 选用SGD优化方法,learning rate设置为0.01,momentum为0.9
    (momentum:动量,来源于牛顿定律,基本思想是为了找到最优加入“惯性”的影响,当误差曲面中存在平坦区域,SGD就可以更快的学习。)
optim = torch.optim.SGD(model.parameters(), lr=1e-2, momentum=0.9)
  1. 调用 .step()来启动gradient descent,optimizer就会对之前存在.grad里的参数来进行调整。
optim.step() #gradient descent
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值