卷积神经网络学习:使用pytorch反向传播

1.pytorch自动求导机制

在Pytorch中,Tensor是其最重要的数据类型。

每个Tensor都有一个requires_grad参数,代表这个Tensor是否会被跟踪自动微分。

这里我们先介绍一个简单的前向计算的例子:

import torch
# requires_grad代表此Tensor会被跟踪自动微分
x = torch.tensor([[1,2],[3,4]],dtype=torch.double,requires_grad=True)
y = x + 2
z = y * y * 3
out = z.mean()

上述计算的输入量为二维矩阵 x ,输出为标量 out ,对于这个公式,如何求解out对输入量 x 的导数呢?

在 Pytorh 中只需要如下代码,无需进行任何手工数学推导:

out.backward()
# 这里就是out对x的导数,out必须是标量,会自动求得y关于x的导数
print(x.grad)
tensor([[4.5000, 6.0000],
        [7.5000, 9.0000]], dtype=torch.float64)

需要注意的是,上述例子中 out 为标量。

如果 out 为一个向量或者Tensor,在反向传播时需要传入一个系数矩阵,backward 方法会对 out 加权求和成一个标量,再进行反向传播。

在实际应用中,我们的 loss_function 一般也是标量。

#雅各比向量,y不是标量
x = torch.tensor([1, 2, 3], dtype=torch.double, requires_grad=True)

y = x * 2
while y.data.norm() < 1000:
    y = y * 2


print(y)

#这里y不是标量,所以求导是针对y中每个元素做一次求导,并且需要传入一个系数
v = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
y.backward(v)
print(x.grad)

 2.利用Pytorch进行反向传播和梯度下降

这里我们介绍一下如何使用Pytoch定义一个numpy定义的两层神经网络模型并对数据进行拟合。在Pytorch中只需要一行代码即可完成反向传播。

import torch
#numpy 两层神经网络
# N为训练数据个数,D_in为输入层大小,H为隐藏层大小,D_out为输出层大小
# 我们定义一个输入层为1000维向量,隐藏层为100个神经元,输出层层10个神经元的神经网络
N, D_in, H, D_out = 64, 1000, 100, 10

# 随机创建训练数据
x = torch.randn(N,D_in)
y = torch.randn(N,D_out)

w1 = torch.randn(D_in,H,requires_grad = True)
w2 = torch.randn(H,D_out,requires_grad = True)

lr = 1e-6
for it in range(500):
    # 前向传播
    y_pred = x.mm(w1).mm(w2)
    
    # loss
    loss = (y_pred - y).pow(2).sum() # 计算损失
    print("\r epoch={} loss={}".format(it,loss), end=' ')
    
    # 反向传播
    loss.backward()
    
    # 如果计算需要求导,那就无法赋值,所以这里必须使用no_grad
    with torch.no_grad():
        w1 -= lr * w1.grad
        w2 -= lr * w2.grad
        # 清空导数,否者下次反向传播求得的导数会累加到这里
        w1.grad.zero_()
        w2.grad.zero_()

上面这种写法还是非常接近于 Numpy 的写法,我们只是将 Numpy 矩阵运算换成了 Pytoch 的 Tensor 运算,并且利用了 Pytorch 的自动求导机制来求解梯度。

实际在 Pytorch 中,有更加优雅的写法,我们可以使用 optimizer 来进行梯度下降,使用它内置的 MSELoss 来实现均方差损失函数:

 

N, D_in, H, D_out = 64, 1000, 100, 10

# 随机创建训练数据(在Pytorch中,一般把batch放在第一个维度,这一点与上面的不同)
x = torch.randn(N,D_in)
y = torch.randn(N,D_out)

model = torch.nn.Sequential(
    torch.nn.Linear(D_in,H),
    torch.nn.Linear(H, D_out),
)

# 定义学习率
lr = 1e-4

loss_fn = torch.nn.MSELoss(reduction='sum')

# 梯度下降策略
optimizer = torch.optim.Adam(model.parameters(),lr=lr)
for it in range(500):
    y_pred = model(x) # 前向传播
    
    loss = loss_fn(y_pred,y)
    print("\r epoch={} loss={}".format(it,loss), end=' ')
    
    # 注意这一步不能少
    optimizer.zero_grad()
    loss.backward()
    
    optimizer.step()

在实际应用中,我们可以将前向传播过程封装成一个类,下面介绍两种以对象形式实现前向传播,并使用自动求导 optimizer 来实现模型的梯度下降的方法:

方式1.使用 Sequential

N, D_in, H, D_out = 64, 1000, 100, 10

# 随机创建训练数据
x = torch.randn(N,D_in)
y = torch.randn(N,D_out)

# 方式1,使用Sequential(序列)函数来构建我们的model
model = torch.nn.Sequential(
    torch.nn.Linear(D_in,H),
    torch.nn.Linear(H, D_out),
)

#torch.nn.init.normal_(model[0].weight)

lr = 1e-4

loss_fn = torch.nn.MSELoss(reduction='sum')

# 梯度下降策略
optimizer = torch.optim.Adam(model.parameters(),lr=lr)
for it in range(500):
    y_pred = model(x) #model.forward()
    
    loss = loss_fn(y_pred,y)
    print("\r epoch={} loss={}".format(it,loss), end=' ')
    
    optimizer.zero_grad()
    loss.backward()
    
    optimizer.step()

方式2.继承 torch.nn.Module

N, D_in, H, D_out = 64, 1000, 100, 10

# 随机创建训练数据
x = torch.randn(N,D_in)
y = torch.randn(N,D_out)

# 方式2,实现自己的类,继承torch.nn.Module
class TwoLayerNet(torch.nn.Module):
    def __init__(self, D_in, H, D_out):
        super(TwoLayerNet,self).__init__()
        self.linear1 = torch.nn.Linear(D_in, H, bias=False)
        self.linear2 = torch.nn.Linear(H, D_out, bias=False)
    
    def forward(self, X):
        y_pred = self.linear2(self.linear1(X))
        return y_pred
    
model = TwoLayerNet(D_in, H, D_out)

lr = 1e-4

loss_fn = torch.nn.MSELoss(reduction='sum')
optimizer = torch.optim.Adam(model.parameters(),lr=lr)
for it in range(500):
    y_pred = model(x) #model.forward()
    
    loss = loss_fn(y_pred,y)
    print("\r epoch={} loss={}".format(it,loss), end=' ')
    
    optimizer.zero_grad()
    loss.backward()
    
    optimizer.step()

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值