PyTorch学习一: 入门

本文基于书籍《深度学习框架PyTorch:入门与实践》的对应代码也是PyTorch入门指南和教程. 并没有完全照抄源章节,而是实现了一些自己的实验之后添加了自己的理解,同时选择性删除了已经在新版pytorch中弃用的variable部分及其他.

Tensor

# from __future__ import print_function 
#该句语句是python2的概念,那么python3对于python2就是future了,
#也就是说,在python2的环境下,超前使用python3的print函数
import torch as t
t.__version__
'1.4.0+cu92'
#只是分配了空间,未初始化
x = t.Tensor
print(x)
x = t.Tensor(5,3)
print(x)
x = t.Tensor([[2,2],[2,2]])
x
<class 'torch.Tensor'>
tensor([[4.2246e-39, 1.0286e-38, 1.0653e-38],
        [1.0194e-38, 8.4490e-39, 1.0469e-38],
        [9.3674e-39, 9.9184e-39, 8.7245e-39],
        [9.2755e-39, 8.9082e-39, 9.9184e-39],
        [8.4490e-39, 9.6429e-39, 1.0653e-38]])





tensor([[2., 2.],
        [2., 2.]])
print(x.size())
x.size()[1],x.size(1) # 查看列的个数, 两种写法等价
#torch.Size 是tuple对象的子类,因此它支持tuple的所有操作,如x.size()[0]
torch.Size([2, 2])
(2, 2)
#加:
#1,
print(x)
x = t.rand(5,3)
print(x)
# 使用[0,1]均匀分布随机初始化二维数组 rand参数是size
y = t.rand(1,3)
print(x+y)   
#2,
print(t.add(x, y))
#3,
result = t.Tensor(5, 3) # 预先分配空间
t.add(x, y, out=result) # 输入到result
result
tensor([[0.6806, 0.1663, 0.7670],
        [0.8551, 0.4156, 0.0987],
        [0.9882, 0.0415, 0.3336],
        [0.7140, 0.8380, 0.1365],
        [0.7197, 0.9418, 0.9951]])
tensor([[0.7685, 0.6577, 0.0177],
        [0.9424, 0.9072, 0.8444],
        [0.3546, 0.7298, 0.4140],
        [0.8316, 0.7530, 0.6691],
        [0.0165, 0.8941, 0.1688]])
tensor([[1.4669, 1.0402, 0.9156],
        [1.6409, 1.2897, 1.7423],
        [1.0531, 1.1124, 1.3119],
        [1.5301, 1.1355, 1.5670],
        [0.7149, 1.2766, 1.0666]])
tensor([[1.4669, 1.0402, 0.9156],
        [1.6409, 1.2897, 1.7423],
        [1.0531, 1.1124, 1.3119],
        [1.5301, 1.1355, 1.5670],
        [0.7149, 1.2766, 1.0666]])





tensor([[1.4669, 1.0402, 0.9156],
        [1.6409, 1.2897, 1.7423],
        [1.0531, 1.1124, 1.3119],
        [1.5301, 1.1355, 1.5670],
        [0.7149, 1.2766, 1.0666]])

注意,函数名后面带下划线_ 的函数会修改Tensor本身。例如,x.add_(y)和x.t_()会改变 x,但x.add(y)和x.t()返回一个新的Tensor, 而x不变。

如不熟悉,初学不推荐使用

# Tensor的(切片)选取操作与Numpy类似
x[:, 1]

Tensor和Numpy的数组之间的
1,互操作非常容易且快速。对于Tensor不支持的操作,可以先转为Numpy数组处理,之后再转回Tensor。
2,对象共享内存,如果其中一个变了,另外一个也会随之改变>>>>所以很快

a  = t.ones(9)  #全为1, 九个值的Tensor
print(a)
b = a.numpy()    #此处未导入numpy包,这是Tensor对象的函数,   .numpy()
b
tensor([1., 1., 1., 1., 1., 1., 1., 1., 1.])

array([1., 1., 1., 1., 1., 1., 1., 1., 1.], dtype=float32)
b = t.from_numpy(b)#变换回Tensor用   t.from_numpy(*)

print(b)
b.add_(1) # 以`_`结尾的函数会修改自身
print(b) # Tensor和Numpy共享内存
tensor([1., 1., 1., 1., 1., 1., 1., 1., 1.])
tensor([2., 2., 2., 2., 2., 2., 2., 2., 2.])

torch.tensor

x = t.tensor([5,3]) # 新建一个包含 5, 3 两个元素的tensor
scalar = t.tensor(3)
scalar
tensor(3)
old_x = x
new_x = old_x.clone()
new_x[0]  = 1111
old_x, new_x
(tensor([5, 3]), tensor([1111,    3]))

需要注意的是,t.tensor()或者tensor.clone()总是会进行数据拷贝,新tensor和原来的数据不再共享内存。所以如果你想共享内存的话,建议使用torch.from_numpy()或者tensor.detach()来新建一个tensor, 二者共享内存。

#尝试失败1
new2_x = old_x.detach()
new2_x = 2222     #小问题:不索引,不成立,被认定为赋值,换成了一个整型的数据
old_x, new2_x   
(tensor([   5, 2222]), 2222)
#尝试失败2
new2_x = t.from_numpy(old_x)   #from_numoy 只能接收numpy
new2_x = 2222
old_x, new2_x
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-127-b7f3851ca223> in <module>
      1 #尝试失败2
----> 2 new2_x = t.from_numpy(old_x)   #from_numoy 只能接收numpy
      3 new2_x = 2222
      4 old_x, new2_x


TypeError: expected np.ndarray (got Tensor)
new2_x = old_x.detach()
new2_x[0] = 2222
old_x, new2_x
(tensor([2222,    3]), tensor([2222,    3]))
x = t.rand(5,3)
y = t.rand(5,3)
#cuda方法将Tensor放在GPU上运算,贼快
# 在不支持CUDA的机器下,下一步还是在CPU上运行
device = t.device("cuda:0" if t.cuda.is_available() else "cpu")
x = x.to(device)
y = y.to(x.device)
z = x+y

此处可能发现GPU运算的速度并未提升太多,这是因为x和y太小且运算也较为简单,而且将数据从内存转移到显存还需要花费额外的开销。GPU的优势需在大规模数据和复杂运算下才能体现出来。

auto grad

深度学习的算法本质上是通过反向传播求导数,而PyTorch的autograd模块则实现了此功能。在Tensor上的所有操作,autograd都能为它们自动提供微分,避免了手动计算导数的复杂过程。
要想使得Tensor使用autograd功能,只需要设置tensor.requries_grad=True.

# 为tensor设置 requires_grad 标识,代表着需要求导数
# pytorch 会自动调用autograd 记录操作
x = t.ones(2, 2, requires_grad=True)

# 上一步等价于
# x = t.ones(2,2)
# x.requires_grad = True

x
tensor([[1., 1.],
        [1., 1.]], requires_grad=True)
y = x.sum()
y
tensor(4., grad_fn=<SumBackward0>)
y.grad_fn      #???????
<SumBackward0 at 0x21817a97f60>
y.backward()   #反向传播,计算梯度
x.grad    #每个值的梯度都为1
tensor([[1., 1.],
        [1., 1.]])

注意:grad在反向传播过程中是累加的(accumulated),这意味着每一次运行反向传播,梯度都会累加之前的梯度,所以反向传播之前需把梯度清零。

y.backward()
print(x.grad)
y.backward()
print(x.grad)
tensor([[2., 2.],
        [2., 2.]])
tensor([[3., 3.],
        [3., 3.]])
# 以下划线结束的函数是inplace操作,会修改自身的值,就像add_
x.grad.data.zero_()
tensor([[0., 0.],
        [0., 0.]])
y.backward()
x.grad
tensor([[1., 1.],
        [1., 1.]])

如果是jupyter notebook,可以上面俩段代码来回试试玩


网络结构

Autograd实现了反向传播功能,但是直接用来写深度学习的代码在很多情况下还是稍显复杂,torch.nn是专门为神经网络设计的模块化接口。nn构建于 Autograd之上,可用来定义和运行神经网络。nn.Module是nn中最重要的类,可把它看成是一个网络的封装,包含网络各层定义以及forward方法,调用forward(input)方法,可返回前向传播的结果。

#Lenet 模型示例
import torch.nn as nn
import torch.nn.functional as F

class Net(nn.Module):                   # 必须基础nn.Module
    def __init__(self):                 # nn.Module子类的函数必须在构造函数中执行父类的构造函数
        super(Net,self).__init__()      # 也可以写成nn.Module.__init__(self) 推荐前一种
                                        # 卷积层参数:   input_channel_size output_channel_size  kernel_size
        #卷积层                         # '1'表示输入图片为单通道, '6'表示输出通道数,'5'表示卷积核为5*5
        self.conv1 = nn.Conv2d(1, 6, 5)
        self.conv2 = nn.Conv2d(6, 16, 5)
        #仿射层/全连接层, y=Wx+b
        self.fc1 = nn.Linear(16*5*5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)
        
    def forward(self, x):
        #卷积>激活>池化
        x = F.max_pool2d(F.relu(self.conv1(x)), (2,2))
        x = F.max_pool2d(F.relu(self.conv2(x)), 2)
        #reshape  自适应('-1')
        x = x.view(x.size()[0], -1)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        s = self.fc3(x)
        return x
    #没写backward,这是继承nn.Modlue,他的子类会autograd,自动实现
    
net = Net()
print(net)
Net(
  (conv1): Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1))
  (conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))
  (fc1): Linear(in_features=400, out_features=120, bias=True)
  (fc2): Linear(in_features=120, out_features=84, bias=True)
  (fc3): Linear(in_features=84, out_features=10, bias=True)
)
#网络的可学习参数通过net.parameters()返回,
#net.named_parameters可同时返回可学习的参数及名称。
params = list(net.parameters())
print(len(params))

for name,parameters in net.named_parameters():
    print(name,':',parameters.size())
#forward函数输入输出都是Tensor
10
conv1.weight : torch.Size([6, 1, 5, 5])
conv1.bias : torch.Size([6])
conv2.weight : torch.Size([16, 6, 5, 5])
conv2.bias : torch.Size([16])
fc1.weight : torch.Size([120, 400])
fc1.bias : torch.Size([120])
fc2.weight : torch.Size([84, 120])
fc2.bias : torch.Size([84])
fc3.weight : torch.Size([10, 84])
fc3.bias : torch.Size([10])
input = t.randn(1,1,32,32)  #randn的n是一般化的意思,
out = net(input)
out.size
<function Tensor.size>
net.zero_grad() # 所有参数的梯度清零
out.backward(t.ones(1,84)) # 反向传播

torch.nn只支持mini-batches,不支持一次只输入一个样本,即一次必须是一个batch。但如果只想输入一个样本,则用 **input.unsqueeze(0)**将batch_size设为1。例如 nn.Conv2d 输入必须是4维的,形如 Samples × nChannels × Height × Width。可将nSample设为1,即1 × nChannels × Height × Width.


损失函数 & 优化器

#
output = net(input)
target = t.arange(0,84).view(1,84).float() 

#创建损失函数和优化器,不过已经有很好用的nn与optim包帮我们建好了
criterion = nn.MSELoss()
import torch.optim as optim
optimizer = optim.SGD(net.parameters(), lr = 0.01)
optimizer.zero_grad   #与net.zero_grad()  效果一样,参数是网络的返回值嘛

#计算损失
loss = criterion(output, target) # loss是个scalar, scalar就算0-dim的Tensor
print(loss)
#反向传播
loss.backward()

#更新参数
optimizer.step()
tensor(2305.7666, grad_fn=<MseLossBackward>)

在反向传播计算完所有参数的梯度后,还需要使用优化方法来更新网络的权重和参数,例如随机梯度下降法(SGD)的更新策略如下:

weight = weight - learning_rate * gradient

手动实现如下:

learning_rate = 0.01
for f in net.parameters():
    f.data.sub_(f.grad.data * learning_rate)# inplace 减法

torch.optim中实现了深度学习中绝大多数的优化方法,例如RMSProp、Adam、SGD等,更便于使用,因此大多数时候并不需要手动写上述代码。

数据加载

深度学习中数据加载及预处理 很烦
PyTorch提供了一些可极大简化和加快数据处理流程的工具
对于常用的数据集,PyTorch也提供了封装好的接口供用户快速调用,这些数据集主要保存在torchvison中

torchvision实现了常用的图像数据加载功能,例如Imagenet、CIFAR10、MNIST等,以及常用的数据转换操作,这极大地方便了数据加载,并且代码具有可重用性。


自查表

Tensor 与 Numpy 区别?
俩者互换的函数,以及归属
加法运算

Tensor创建 随机 1 Tensor矩阵
torch.size是___对象的子类
提醒: 如何查看Tensor列数

inplace()操作有什么不同

反向传播函数:?
反向传播又为什么要清零

cuda的操作简单但也要记住呀,而且多显卡并行DataParallel也要会用,而且看那么多csdn讲cuda加速的,虽然还没有服务器用cuda显卡加速,但我估计会有很多坑等着.

定义网络结构咯,卷积层参数是啥呀,
forward函数和init有什么区别呀
nn.Module()很重要哟
好像也没啥,但就算理论最坚实的部分✌

有很多backward不需要你干,记住Tensor创建时用设成自动求导为 True 就行
损失函数和优化器也不需要,但要记得咋用
数据加载,先找 torchvision 玩玩吧,文件夹导入图片的也不难,反正有DataLoader()

时间: 2020-11-11 torch版本: 1.4.0 参考教程: https://github.com/caoyang13/pytorch-book/tree/2db9c662d745d30951ab882c570d049d413caf45

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值