pytorch搭建模型的五大层次级别解读(由浅入深)

 更新时间:2023年06月15日 08:57:02   作者:LoveMIss-Y  
这篇文章主要介绍了pytorch搭建模型的五大层次级别(由浅入深),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

前言

神经网络的搭建本身是一个较为复杂的过程,但是现在有非常多的、非常人性化的开源框架提供给我们使用,但是即便如此,网络的搭建也是有多种方法可以选择,本文以pytorch为例子加以说明。

神经网络的基本流程可以分为两大步骤,即

网络结构搭建+参数的梯度更新(后者又包括  “前向传播+计算参数的梯度+梯度更新”)

这其实也是后面pytorch搭建神经网络的一个基本思路

1 、原始搭建——使用numpy实现

# -*- coding: utf-8 -*-
import numpy as np
# N是训练的batch size; D_in 是input输入数据的维度;
# H是隐藏层的节点数; D_out 输出的维度,即输出节点数.
N, D_in, H, D_out = 64, 1000, 100, 10
# 创建输入、输出数据
x = np.random.randn(N, D_in)  #(64,1000)
y = np.random.randn(N, D_out) #(64,10)可以看成是一个10分类问题
# 权值初始化
w1 = np.random.randn(D_in, H)  #(1000,100),即输入层到隐藏层的权重
w2 = np.random.randn(H, D_out) #(100,10),即隐藏层到输出层的权重
learning_rate = 1e-6   #学习率
for t in range(500):
    # 第一步:数据的前向传播,计算预测值p_pred
    h = x.dot(w1)
    h_relu = np.maximum(h, 0)
    y_pred = h_relu.dot(w2)
    # 第二步:计算计算预测值p_pred与真实值的误差
    loss = np.square(y_pred - y).sum()
    print(t, loss)
    # 第三步:反向传播误差,更新两个权值矩阵
    grad_y_pred = 2.0 * (y_pred - y)     #注意:这里的导函数也是自己求的,因为这个地方是很简答的函数表达式
    grad_w2 = h_relu.T.dot(grad_y_pred)
    grad_h_relu = grad_y_pred.dot(w2.T)
    grad_h = grad_h_relu.copy()
    grad_h[h < 0] = 0
    grad_w1 = x.T.dot(grad_h)
    # 更新参数
    w1 -= learning_rate * grad_w1
    w2 -= learning_rate * grad_w2

缺点:

(1)没办法搭建复杂的网络结构(网络的结构搭建太底层);

(2)梯度需要自己求导函数,如果函数太复杂,网络太深就很难求了;

(3)没有办法使用GPU加速

2、使用torch的Tensor原始实现

import torch
dtype = torch.float
device = torch.device("cpu")
# device = torch.device("cuda:0") # 这里使用CPU,但实际上可以使用GPU
# N是训练的batch size; D_in 是input输入数据的维度;
# H是隐藏层的节点数; D_out 输出的维度,即输出节点数.
N, D_in, H, D_out = 64, 1000, 100, 10
# 创建输入、输出数据
x = torch.randn(N, D_in, device=device, dtype=dtype)  #(64,1000)
y = torch.randn(N, D_out, device=device, dtype=dtype) #(64,10)可以看成是一个10分类问题
# 权值初始化
w1 = torch.randn(D_in, H, device=device, dtype=dtype) #(1000,100),即输入层到隐藏层的权重
w2 = torch.randn(H, D_out, device=device, dtype=dtype)#(100,10),即隐藏层到输出层的权重
learning_rate = 1e-6
for t in range(500):
    # 第一步:数据的前向传播,计算预测值p_pred
    h = x.mm(w1)
    h_relu = h.clamp(min=0)
    y_pred = h_relu.mm(w2)
    # 第二步:计算计算预测值p_pred与真实值的误差
    loss = (y_pred - y).pow(2).sum().item()
    print(t, loss)
    # 第三步:反向传播误差,更新两个权值矩阵
    grad_y_pred = 2.0 * (y_pred - y)    #注意:这里的导函数也是自己求的,因为这个地方是很简答的函数表达式
    grad_w2 = h_relu.t().mm(grad_y_pred)
    grad_h_relu = grad_y_pred.mm(w2.t())
    grad_h = grad_h_relu.clone()
    grad_h[h < 0] = 0
    grad_w1 = x.t().mm(grad_h)
    # 参数更新(梯度下降法)
    w1 -= learning_rate * grad_w1
    w2 -= learning_rate * grad_w2

缺点:

(1)没办法搭建复杂的网络结构(网络的结构搭建太底层);

(2)梯度需要自己求导函数,如果函数太复杂,网络太深就很难求了;

解决了GPU计算问题、少了一个缺点。

3、torch的自动求导autograd

import torch
dtype = torch.float
device = torch.device("cpu")
# device = torch.device("cuda:0") # 这里使用CPU,但实际上可以使用GPU
# N是训练的batch size; D_in 是input输入数据的维度;
# H是隐藏层的节点数; D_out 输出的维度,即输出节点数.
N, D_in, H, D_out = 64, 1000, 100, 10
# 创建输入、输出数据
x = torch.randn(N, D_in, device=device, dtype=dtype)  #(64,1000)
y = torch.randn(N, D_out, device=device, dtype=dtype) #(64,10)可以看成是一个10分类问题
# 权值初始化
w1 = torch.randn(D_in, H, device=device, dtype=dtype) #(1000,100),即输入层到隐藏层的权重
w2 = torch.randn(H, D_out, device=device, dtype=dtype)#(100,10),即隐藏层到输出层的权重
learning_rate = 1e-6
for t in range(500):
    # 第一步:数据的前向传播,计算预测值p_pred
    y_pred = x.mm(w1).clamp(min=0).mm(w2)
    # 第二步:计算计算预测值p_pred与真实值的误差
    loss = (y_pred - y).pow(2).sum()
    print(t, loss.item())
    # 第三步:反向传播误差,更新两个权值矩阵,这就是关键了,不再需要自己写出导函数,求导是自动完成的
    loss.backward()  #一步到位、自动求导
    # 参数梯度更新
    with torch.no_grad():
        w1 -= learning_rate * w1.grad  #grad属性获取梯度,其实这个地方就是相当于是梯度下降法,优化的过程可以自己定义,因为这里参数很少
        w2 -= learning_rate * w2.grad
        求完一次之后将梯度归零
        w1.grad.zero_()
        w2.grad.zero_()

缺点:

(1)没办法搭建复杂的网络结构(网络的结构搭建太底层);

解决了GPU计算问题、而且梯度导数也不用自己求了,少了两个缺点。那现在就只剩一个问题没解决了,那就是怎么快速搭建更复杂、更深层的网络结构。

(2)上面更新参数w1和w2的过程其实就是一个优化过程,这里是用的就是简单的头下降法,这样做有一个很大的缺点,那就是这里只有两个参数需要优化,所以可以自己写,但是现在的网络有很多的参数需要优化,都自己写的话实在是太麻烦了。于是就有了后面的方法,参见下面

4、使用pytorch.nn模块

可以将它理解为一个较高层次的API封装

# -*- coding: utf-8 -*-
import torch
# N是训练的batch size; D_in 是input输入数据的维度;
# H是隐藏层的节点数; D_out 输出的维度,即输出节点数.
N, D_in, H, D_out = 64, 1000, 100, 10
# 创建输入、输出数据
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)
#模型搭建:这是与前面关键的区别,不再需要自己手动进行矩阵相乘,而是这种一步到位的方法
model = torch.nn.Sequential(
    torch.nn.Linear(D_in, H),
    torch.nn.ReLU(),
    torch.nn.Linear(H, D_out),
)
#定义损失函数
loss_fn = torch.nn.MSELoss(reduction='sum')
learning_rate = 1e-4
for t in range(500):
    # 第一步:数据的前向传播,计算预测值p_pred
    y_pred = model(x)
    # 第二步:计算计算预测值p_pred与真实值的误差
    loss = loss_fn(y_pred, y)
    print(t, loss.item())
    # 在反向传播之前,将模型的梯度归零
    model.zero_grad()
    # 第三步:反向传播误差,更新两个权值矩阵,这就是关键了,不再需要自己写出导函数,求导是自动完成的
    loss.backward()
    #更新参数的梯度
    with torch.no_grad():
        for param in model.parameters():
            param -= learning_rate * param.grad #这其实就是梯度下降法,优化参数,通过循环自动实现,不要再一个一个写了,相较于上面的参数更新方法简单了很多。但是还不够

总结:

到这一步,基本解决了前面的三个致命问题,即解决了GPU计算问题、而且梯度导数也不用自己求了,并且可以搭建复杂的网络,不需要自己进行一个一个的矩阵相乘,少了三个缺点。

使用这里的层次,一些简单的网络类型就没什么问题了,当然我们还可以进一步优化,因为上面我们对loss进行自动求导之后,还需要通过一个循环来对模型的各个参数逐个进行参数的更新,所以下面提供了两个层次方面的优化措施:

5、使用torch.optim

来进一步简化训练过程——进一步省略手动的参数更新,更加一步到位

# -*- coding: utf-8 -*-
import torch
# N是训练的batch size; D_in 是input输入数据的维度;
# H是隐藏层的节点数; D_out 输出的维度,即输出节点数.
N, D_in, H, D_out = 64, 1000, 100, 10
# 创建输入、输出数据
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)
#模型搭建:这是与前面关键的区别,不再需要自己手动进行矩阵相乘,而是这种一步到位的方法
model = torch.nn.Sequential(
    torch.nn.Linear(D_in, H),
    torch.nn.ReLU(),
    torch.nn.Linear(H, D_out),
)
#定义损失函数
loss_fn = torch.nn.MSELoss(reduction='sum')
learning_rate = 1e-4
#构造一个optimizer对象
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
for t in range(500):
    # 第一步:数据的前向传播,计算预测值p_pred
    y_pred = model(x)
    # 第二步:计算计算预测值p_pred与真实值的误差
    loss = loss_fn(y_pred, y)
    print(t, loss.item())
    # 在反向传播之前,将模型的梯度归零,这
    optimizer.zero_grad()
    # 第三步:反向传播误差
    loss.backward()
    # 直接通过梯度一步到位,更新完整个网络的训练参数,一句话优化所有的参数,是不是很牛逼
    optimizer.step()

这是不是更加简单了?

如果是使用过keras这样高层次的API,所以到这里应该发现,他们实在是太像了,现在来总结一下使用pytorch搭建神经网路的一般步骤:

(1)第一步:搭建网络的结构,得到一个model。网络的结构可以是上面这种最简单的序贯模型,当然还可以是多输入-单输出模型、单输入-多输出模型、多输入-多输出模型、跨层连接的模型等,我们好可以自己定义模型,后面再讲。

(2)第二步:定义损失函数。

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

(3)第二步:定义优化方式。构造一个optimizer对象

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

上面是模型以及模型相关的配置三步走,下面是训练的五步走

(1)第一步:计算y_pred;

(2)第二步:根据损失函数计算loss

(3)第三步:梯度归零,

optimizer.zero_grad()

(4)第四步:反向传播误差

loss.backward()

(5)更新参数,使用step()

optimizer.step()

个人观点:

深度学习框架最大的地方在于两个点,正是这两个点大大简化了我们自己的实现思路,当然这两个点不在于一层一层的搭建,个人认为最重要的在于以下两个:

(1)第一,自动求导。设想一下,如果一个如此复杂的“高维度”、“非线性”的函数,需要自己写出求导公式,在进行矩阵运算,这一项就很不现实了。

(2)第二,参数的优化。即所谓的optimizer的作用,它是每一个参数的更新规则,由于模型参数众多,不可能一个一个来更新,而且没一个更新的原理是重复的,深度学习框架正好提供了一步到位的方法,不管是tensorflow还是pytorch。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Python Datetime模块和Calendar模块用法实例分析

    Python Datetime模块和Calendar模块用法实例分析

    这篇文章主要介绍了Python Datetime模块和Calendar模块用法,结合实例形式分析了Python日期时间及日历相关的Datetime模块和Calendar模块原理、用法及操作注意事项,需要的朋友可以参考下
    2019-04-04
  • Python获取本机IP/MAC多网卡方法示例

    Python获取本机IP/MAC多网卡方法示例

    这篇文章主要为大家介绍了Python获取本机IP/MAC多网卡方法示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08
  • python机器学习之神经网络(一)

    python机器学习之神经网络(一)

    这篇文章主要为大家详细介绍了python机器学习之神经网络第一篇,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-12-12
  • Python中用于返回绝对值的abs()方法

    Python中用于返回绝对值的abs()方法

    这篇文章主要介绍了Python中用于返回绝对值的abs()方法,是Python入门中的基础知识,需要的朋友可以参考下
    2015-05-05
  • Python基础教程之装饰器详解

    Python基础教程之装饰器详解

    众所周知,Python装饰器是一种常见的元编程特性,在本教程中,我们将深入探讨Python装饰器的基本概念、语法及其应用,并利用实际例子加深理解,感兴趣的小伙伴快跟随小编一起了解一下吧
    2023-06-06
  • 利用matlab与Excel交互之单元格操作

    利用matlab与Excel交互之单元格操作

    Excel是广泛使用的“电子表格”,Matlab则具有强大的数值计算、统计分析以及图形可视化能力,这篇文章主要给大家介绍了关于利用matlab与Excel交互之单元格操作的相关资料,需要的朋友可以参考下
    2021-08-08
  • python各类经纬度转换的实例代码

    python各类经纬度转换的实例代码

    这篇文章主要介绍了python各类经纬度转换的实例代码,非常不错,具有一定的参考借鉴价值,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-08-08
  • python的多线程原来可以这样解

    python的多线程原来可以这样解

    这篇文章主要为大家介绍了python的多线程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-01-01
  • python发送邮件示例(支持中文邮件标题)

    python发送邮件示例(支持中文邮件标题)

    python发送中文邮件示例,支持中文邮件标题和中文邮件内容。支持多附件。根据用户名推测邮件服务器提供商
    2014-02-02
  • pytorch 加载(.pth)格式的模型实例

    pytorch 加载(.pth)格式的模型实例

    今天小编就为大家分享一篇pytorch 加载(.pth)格式的模型实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-08-08

最新评论