【Pytorch基础1】从tensor开始到实现一个简单的网络

前言:
复现论文时发现对pytorch不够熟练,故重新学习并记录

主要根据Pytorch中文手册系统的学习一遍pytorch知识

1. 张量tensor

( 1 )tensor与numpy中的ndarray相似,但是tensor可以使用GPU计算。

import torch
#创建一个5行3列的未初始化矩阵
x = torch.empty(5,3)
#创建一个5行三列的随机矩阵
x = torch.rand(5,3) 
#创建一个0填充的矩阵 数据类型为long
x = torch.zeros(5,3,dtype=torch.long) 
#通过现有矩阵创建一个矩阵
x = torch.tensor([5.5,3])
#根据现有的张量创建张量,新的张量的属性和现有的一样
#创建一个和现有x的属性一样的5行3列的张量
x = x.new_ones(5,3)
#如果重新设置dtype则张量的属性会发生变化
x = torch.randn_like(x,dtype = torch.float)

Note:
size方法和numpy的shape属性返回的相同

x.size()
# 输出torch.Size([5,3])

( 2 )加法操作

y = torch.rand(5,3)
#第一种方法
x+y
# 第二种方法
torch.add(x,y,out = result)
# 将x加给y,会改变y的值
y.add_(x)

Note:
任何以’'结尾的操作的结果都会替换掉原变量。如x.copy(y)
( 3 )索引操作
和numpy的索引操作一样

x[:,1]

( 4 )改变维度
torch.view和np.reshape相似

#创建一个4行4列的矩阵
x = torch.randn(4,4)
#变成1维
y = x.view(16)
#view的第一个元素为-1时从其他的维度推断该维度应该为多少
#这里是2行8列
z = x.view(-1,8)

Note:view的第一个元素为-1时从其他的维度推断该维度应该为多少

( 5 )numpy转换
tensor和numpy数组之间的转换非常方便,同时tensor和numpy数组共享内存,修改其中一个也会引起另一个的变化
5.1 torch tensor转换为numpy数组

a = torch.ones(5,5)
b = a.numpy()

5.2 numpy数组转换为torch tensor
使用from_numpy自动转化

a = no.ones(5,5)
b = torch.from_numpy(a)

( 6 )将tensor送入gpu中
6.1 tensor可以直接在gpu中创建

if torch.cuda.is_available():
	device = torch.device('cuda')
	y = torch.ones_like(x, device = device)

6.2 使用.to方法也可以将tensor送入gpu中

x = x.to(device)

2. autograd:自动求导

autograd包为张量上的所有操作提供了自动求导
先看代码

import torch
x = torch.ones(2,2,requires_grad=True)

设置requires_grad=True去追踪所有对于x这个张量的操作
同时通过 a . requires_grad_(True)的操作可以改变张量的requires_grad的属性

y = x+2
y.grad_fn

y被求出来后gran_fn就会被自动记录
接下来继续对y操作

z = y*y*3
out = y.mean()
out.backward()

一般的情况下使用lose.backward()这里的loss是一个标量,如果此时的loss不是一个标量,那么计算会复杂一些。可以参考PyTorch中的backward
这里举例子的out.backward()等价与out.backward(torch.tensor(1))
现在就完成了反向传播计算梯度,那么此时的out对x的梯度为d(out)/d(x)代码则非常简约

x.grad

Note:
.backward()方法自动计算所有的梯度,这个张量的梯度会自动累积到**.grad**属性
在评估模型的时候,一般就不需要再进行梯度的计算了,但是此时的requires_grad=True,那我我们将代码块就包装在with torch.no_grad()中进行评估。代码如下所示,此时是不会进行梯度的计算的

x = torch.randn(2,2,requires_grad = True)
with torch.no_grad():
	x**2

3. Neural Networks

( 1 )使用torch.nn来构建神经网络
一个nn.Module包含各个层和一个forward(input)方法,该方法返回output。
在pytorch框架中,如果需要自定义一个net,1.先继承(nn.Moudle),2.再构建组件( __init __),3.最后组装(forward)。这里的网络注释来源于构建简单CNN

import torch
import torch.nn as nn
import torch.nn.functional as F

class Net(nn.Module):
    #定义Net的初始化函数,这个函数定义了该神经网络的基本结构
    def __init__(self):
        super(Net, self).__init__() #复制并使用Net的父类的初始化方法,即先运行nn.Module的初始化函数
        self.conv1 = nn.Conv2d(1, 6, 5) # 定义conv1函数的是图像卷积函数:输入为图像(1个通道,即灰度图),输出为 6张特征图, 卷积核为5x5正方形
        self.conv2 = nn.Conv2d(6, 16, 5)# 定义conv2函数的是图像卷积函数:输入为6张特征图,输出为16张特征图, 卷积核为5x5正方形
        self.fc1   = nn.Linear(16*5*5, 120) # 定义fc1(fullconnect)全连接函数1为线性函数:y = Wx + b,并将16*5*5个节点连接到120个节点上。
        self.fc2   = nn.Linear(120, 84)#定义fc2(fullconnect)全连接函数2为线性函数:y = Wx + b,并将120个节点连接到84个节点上。
        self.fc3   = nn.Linear(84, 10)#定义fc3(fullconnect)全连接函数3为线性函数:y = Wx + b,并将84个节点连接到10个节点上。

    #定义该神经网络的向前传播函数,该函数必须定义,一旦定义成功,向后传播函数也会自动生成(autograd)
    def forward(self, x):
        x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2)) #输入x经过卷积conv1之后,经过激活函数ReLU,使用2x2的窗口进行最大池化Max pooling,然后更新到x。
        x = F.max_pool2d(F.relu(self.conv2(x)), 2) #输入x经过卷积conv2之后,经过激活函数ReLU,使用2x2的窗口进行最大池化Max pooling,然后更新到x。
        x = x.view(-1, self.num_flat_features(x)) #view函数将张量x变形成一维的向量形式,总特征数并不改变,为接下来的全连接作准备。
        x = F.relu(self.fc1(x)) #输入x经过全连接1,再经过ReLU激活函数,然后更新x
        x = F.relu(self.fc2(x)) #输入x经过全连接2,再经过ReLU激活函数,然后更新x
        x = self.fc3(x) #输入x经过全连接3,然后更新x
        return x

Note:
nn.Module详解
我们在定义自已的网络的时候,需要继承nn.Module类,并重新实现构造函数__init__和构造函数forward这两个方法。但有一些注意技巧:

(1)一般把网络中具有**可学习参数的层(如全连接层、卷积层等)**放在构造函数__init__()中,当然我也可以吧不具有参数的层也放在里面;

(2)一般把不具有可学习参数的层(如ReLU、dropout、BatchNormanation层)可放在构造函数forward中,也可不放在构造函数中,如果不放在构造函数__init__里面,则在forward方法里面可以使用nn.functional来代替

(3)forward方法是必须要重写的,它是实现模型的功能,实现各个层之间的连接关系的核心。
(4)上面的是将所有的层都放在了构造函数__init__里面,但是只是定义了一系列的层,各个层之间到底是什么连接关系并没有,而是在forward里面实现所有层的连接关系
总结:所有放在构造函数__init__里面的层的都是这个模型的“固有属性”
( 2 )nn.ModuleList和nn.Sequential
PyTorch 中的 ModuleList 和 Sequential: 区别和使用场景
ModuleList 就是一个储存各种模块的 list,这些模块之间没有联系,没有实现 forward 功能,但相比于普通的 Python list,ModuleList 可以把添加到其中的模块和参数自动注册到网络上。而Sequential 内的模块需要按照顺序排列,要保证相邻层的输入输出大小相匹配,内部 forward 功能已经实现,可以使代码更加整洁
( 3 )选择损失函数
一个损失函数接受一对(output, target)作为输入,计算一个值来估计网络的输出和目标的差距
nn包内有很多的损失函数,要根据不同的场景选择不同的损失函数。
19个损失函数汇总

net = Net()
input = torch.randn(1,1,32,32)
output = net(input)
target = torch.randn(10)
target = target.view(1,-1)
criterion = nn.MSELoss()
loss = criterion(output, target)

( 4 )历史梯度清零和反向传播计算新的梯度
因为前文所讲.grad属性会累加,所以要清零

net.zero_grad()
...中间算呀算output等
loss.backward()

( 5 )更新权重
想使用不同的权重更新规则时,比如SGD,Nesterov-SGD,Adam等,torch.optim实现了所有的这些规则

import torch.optim as optim
optimizer = optim.SGD(net.parameters(), lr=0.01)

上面这段代码定义了用什么优化器,放在模型定义之后,训练之前。

optimizer.zero_grad()
output = net(input)
criterion = nn.MSELoss()
loss = criterion(output, target)
loss.backward()
optimizer.step()

主要回顾了一下tensor的概念,pytorch的自动求导机制,以及如何使用pytorch定义和训练一个简单网络结构。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值