深度学习框架PyTorch一书的学习-第一/二章
参考https://github.com/chenyuntc/pytorch-book/tree/v1.0
希望大家直接到上面的网址去查看代码,下面是本人的笔记
pytorch的设计遵循tensor-> variable(autograd)-> nn.Module三个由低到高的抽象层次,分别代表高维数组(张量)、自动求导(变量)和神经网络(层/模块)。这三个抽象之间联系紧密,可以同时进行修改和操作
在IPython和Jupyter notebook两个工具中使用了Jupyter notebook
更多安装内容可见:深度学习PyTorch环境安装——mac
1.tensor
Tensor是pytorch中的重要数据结构,可认为是一个高维数组。它可以是一个数(标量)、一维数组(向量)、二维数组(矩阵)或更高维的数组
其简单的用法可见pytorch学习-WHAT IS PYTORCH
1)导入包
from __future__ import print_function
import torch as t
2)仅分配空间,不初始化
#构建5*3矩阵,只是分配了空间,未初始化
x = t.Tensor(,)
x
返回:
tensor([[ 0.0000e+00, 2.0000e+00, -1.0696e+12],
[-1.5849e+29, 2.4158e-12, 1.1625e+33],
[ 8.9605e-01, 1.1632e+33, 5.6003e-02],
[ 7.0374e+22, 5.7453e-44, 0.0000e+00],
[ 0.0000e+00, 2.0000e+00, -9.4044e+11]])
3)初始化
#使用[,]均匀分布随机初始化二维数组
x = t.rand(, )
x
返回:
tensor([[0.0136, 0.1579, 0.7545],
[0.0127, 0.3194, 0.0126],
[0.7604, 0.0289, 0.4808],
[0.3374, 0.8478, 0.4047],
[0.2441, 0.4211, 0.5480]])
4)获得size
print(x.size())
#两种写法,得到二维数组的行列数
#支持该写法的原因是因为torch.Size是tuple对象的子类,因此其支持tuple的所有操作,如索引等
x.size()[], x.size()
返回:
torch.Size([, ])
(, )
5)加法的几种写法
y = t.rand(,)
y
返回:
tensor([[0.6840, 0.5716, 0.3848],
[0.8492, 0.5449, 0.3555],
[0.4052, 0.5689, 0.1948],
[0.2308, 0.6252, 0.8855],
[0.0487, 0.2880, 0.9232]])
1>第一种
x + y
返回:
tensor([[0.6975, 0.7295, 1.1394],
[0.8619, 0.8643, 0.3681],
[1.1656, 0.5978, 0.6756],
[0.5681, 1.4729, 1.2902],
[0.2928, 0.7091, 1.4712]])
2>第二种
t.add(x,y)
结果相同
3》第三种:指定加法结果的输出目标为result
result = t.Tensor(,) #预分配空间存储结果
t.add(x, y, out=result)
result
结果相同
4》第四种:改变y的值
y.add_(x)
y
y变为上面的加法结果
函数名后面带下划线_的函数会修改tensor本身
2.自动微分
深度学习算法的本质是通过反向函数求导数,pytorch的Autograd模块实现了此功能。在Tensor上的所有操作,Autograd都能够为他们自动提供微分,避免手动计算的复杂过程
其中Autograd.Variable是Autograd的核心类。Tensor被封装成Variable后,可以调用它的.backward实现反向传播,自动计算所有梯度

- data : 保存Variable所包含的Tensor
- grad : 保存data对应的梯度,grad也是一个Variable(即也有data\grad\grad_fn三个值),而不是Tensor,它和data的形状一样
- grad_fn : 指向一个Function对象,这个Function用来反向传播计算输入的梯度
1)导入包
from __future__ import print_function
import torch as t
from torch.autograd import Variable
2)新建
#使用Tensor新建一个Variable
x = Variable(t.ones(, ),requires_grad = True)
x
返回:
tensor([[., .],
[., .]], requires_grad=True)
此时查看该值的grad和grad_fn是没有返回值的,因为没有进行任何操作
x.grad_fn
x.grad
3)进行求和操作,查看梯度
y = x.sum()
y
返回:
tensor(., grad_fn=<SumBackward0>)
这时候可查看:
y.grad_fn
返回:
<SumBackward0 at 0x122782978>
可知y是变量Variable x进行sum操作求来的,但是这个时候y.grad是没有返回值的,因为没有使用y进行别的操作
这个时候的x.grad也是没有值的,虽然使用x进行了sum操作,但是还没有对y反向传播来计算梯度
y.backward()#反向传播,计算梯度
然后再查看:
#因为y = x.sum() = (x[][] + x[][] + x[][] + x[][])
#每个值的梯度都为1
x.grad
返回:
tensor([[., .],
[., .]])
但是需要注意的一点就是grad在反向求和的过程中是累加的,所以反向传播前要把梯度清零
比如再运行一遍反向传播:
y.backward()
x.grad
可见返回:
tensor([[., .],
[., .]])
清零操作:
x.grad.data.zero_()#清零
再运行即可:
y.backward()
x.grad
返回:
tensor([[., .],
[., .]])
在这里简单解释一下梯度的计算,如图示:

下面我们使用代码实现一下看看是不是这样:
x = Variable(t.ones(,), requires_grad = True)
y = x +
z = y*y
k = z.mean()
然后进行反向传播,获得x的梯度:
k.backward()
x.grad
返回果然是:
tensor([[., .],
[., .]])
4)Variable和Tensor有近乎一致的接口,在实际中可以无缝切换
x = Variable(t.ones(,))
y = t.cos(x)#传入的是Variable
x_tensor_cos = t.cos(x.data)#传入的是Tensor
print(y)
x_tensor_cos
返回:
tensor([[0.5403, 0.5403, 0.5403, 0.5403, 0.5403],
[0.5403, 0.5403, 0.5403, 0.5403, 0.5403],
[0.5403, 0.5403, 0.5403, 0.5403, 0.5403],
[0.5403, 0.5403, 0.5403, 0.5403, 0.5403]])
tensor([[0.5403, 0.5403, 0.5403, 0.5403, 0.5403],
[0.5403, 0.5403, 0.5403, 0.5403, 0.5403],
[0.5403, 0.5403, 0.5403, 0.5403, 0.5403],
[0.5403, 0.5403, 0.5403, 0.5403, 0.5403]])
可见这样子得到的值并没有什么区别,返回的都是Tensor,而不是Variable
3.神经网络
torch.nn是专门为神经网络设计的模块化接口
nn构建于Autograd之上,可用来定义和运行神经网络。nn.Module是nn中最重要的类,可以把它看作一个网络的封装,包含网络各层定义和forward方法,调用forward(input)可返回前向传播的结果
举例最早的卷积神经网络LeNet(判断图片中的字符),来看看如何使用nn.Module实现
1)定义网络
继承nn.Module,并实现它的forward方法,把网络中具有可学习参数的层放在构造函数__init__中。
如果某一层(如ReLu)不具有可学习的参数,那么可放在构造函数中,也可以不放
import torch.nn as nn
import torch.nn.functional as F class Net(nn.Module):
def __init__(self):
#nn.Module子类的函数必须在构造函数中执行父类的构造函数
#下式等价于nn.Module.__init__(self)
super(Net, self).__init__()
#卷积层1,参数1表示输入图片为单通道,即灰度图像,为1**,6表示输出通道数,即过滤器的通道数,5表示过滤器为5*
#所以下一层得到的值为6**,作为输入
self.conv1 = nn.Conv2d(, , )
#卷积层2
self.conv2 = nn.Conv2d(, , )
#全连接层,y = Wx + b
self.fc1 = nn.Linear(**, )
self.fc2 = nn.Linear(, )
self.fc3 = nn.Linear(, ) #最后的输出是10,是因为判断的数值分成了10类 def forward(self, x):
#先conv1卷积->再relu激活->再max_pool池化
x = F.max_pool2d(F.relu(self.conv1(x)), (, )) #(, )相当于池化将高度和宽度缩减一半,得到结果为6**
x = F.max_pool2d(F.relu(self.conv2(x)), ) #激活后为16**,池化后16**,因为大小是正方形,所以第二个参数可以只有一个值,效果和上面的(,)是相同的 #reshape,将图形扁平化,使其由16**5的三维立体变成1*(**)的二维矩阵
x = x.view(x.size()[], -)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x net = Net()
print(net)
返回:
Net(
(conv1): Conv2d(, , kernel_size=(, ), stride=(, ))
(conv2): Conv2d(, , kernel_size=(, ), stride=(, ))
(fc1): Linear(in_features=, out_features=, bias=True)
(fc2): Linear(in_features=, out_features=, bias=True)
(fc3): Linear(in_features=, out_features=, bias=True)
)
只要在nn.Module的子类中定义了forward函数,backward函数就会被自动实现(利用Autograd)
在forward函数中可以使用任何Variable支持的函数
上面定义的网络的可学习参数可通过net.Parameters()返回,net,named_parameters()可同时返回可学习的参数及名称
params = list(net.parameters())
print(len(params)) #返回10
如果想要查看参数的名字等信息,可见:
for name, parameter in net.named_parameters():
print(name, ':', parameter.size())
返回:
conv1.weight : torch.Size([, , , ])
conv1.bias : torch.Size([])
conv2.weight : torch.Size([, , , ])
conv2.bias : torch.Size([])
fc1.weight : torch.Size([, ])
fc1.bias : torch.Size([])
fc2.weight : torch.Size([, ])
fc2.bias : torch.Size([])
fc3.weight : torch.Size([, ])
fc3.bias : torch.Size([])
forward函数的输入和输出都是Variable,只有Variable才具有自动求导功能,Tensor是没有的。所以在输入时,需要把Tensor封装成Variable
这里nn.Conv2d的输入必须是四维的,即样本数*通道数*高*宽
input = Variable(t.randn(, , , )) #即输入是一张灰度的32*32大小的图片
output = net(input)
output.size()
返回:
torch.Size([, ])
net.zero_grad() #所有参数的梯度清零
output.backward(Variable(t.ones(, ))) #反向传播
这里的backward()中为什么需要传入参数Variable(t.ones(1, 10))呢?没有传入就会报错:
RuntimeError: grad can be implicitly created only for scalar outputs
原因可见:pytorch的backward
这时候就可以查看参数的梯度了:
params = list(net.parameters())
print(params[].grad)
返回:
tensor([[[[ 1.0658e-01, -3.3146e-02, 3.7709e-03, -3.0634e-02, 1.1791e-02],
[ 3.1975e-02, 4.4254e-02, -1.1230e-02, 8.9412e-02, 7.1382e-02],
[-5.6357e-02, 9.1797e-05, 1.8966e-02, -4.4844e-03, 5.7771e-02],
[-1.2467e-03, -6.6853e-02, -6.9389e-02, 1.6258e-02, 7.9231e-02],
[-3.1562e-02, 2.0557e-02, 2.1569e-02, 8.2486e-02, -1.1457e-01]]], [[[-4.3394e-02, -4.5307e-03, 2.7980e-02, 1.8965e-03, 1.0381e-01],
[-2.9320e-02, 2.8249e-02, -3.9046e-02, 1.8078e-02, -4.6308e-02],
[-3.2397e-03, -1.8470e-02, -6.8865e-03, 5.4334e-03, -6.9593e-02],
[-3.7685e-02, -1.2786e-01, -5.6480e-03, -5.7637e-02, -2.2518e-02],
[ 6.1855e-03, 2.5202e-02, 3.0603e-02, 2.8058e-02, -6.9573e-02]]], [[[-9.2159e-02, 8.4124e-02, -7.6889e-02, -4.0251e-03, -2.3461e-02],
[ 1.3258e-01, -4.1542e-02, 1.5377e-02, -2.0014e-02, -1.3080e-02],
[-1.8869e-02, 7.1884e-02, -5.8952e-02, 2.0899e-02, 1.5558e-02],
[ 2.7937e-02, 7.8165e-02, -4.8216e-02, -1.6983e-02, 4.5583e-02],
[-7.2376e-02, 5.4218e-03, 7.5949e-02, -6.5286e-02, 3.4859e-03]]], [[[ 9.8840e-02, -9.1584e-02, 4.7770e-02, 6.2509e-03, 8.5981e-03],
[ 4.2380e-02, 1.8999e-02, 2.4990e-02, 8.1884e-03, -3.5326e-02],
[-1.2791e-02, 9.0136e-02, 8.5160e-06, 2.5163e-02, 7.3440e-03],
[-8.7427e-02, 4.0755e-02, -1.1036e-02, 5.3797e-02, 7.8865e-03],
[-3.1478e-02, 6.5078e-02, 3.5277e-02, -3.8831e-02, -8.0675e-02]]], [[[-7.8461e-03, 5.7134e-02, 5.0355e-02, -1.2885e-02, -1.3467e-02],
[-1.3372e-02, -1.9114e-02, -9.7893e-02, -8.6965e-03, 1.4722e-02],
[ 1.9918e-02, 2.6398e-02, 1.5144e-02, 4.7445e-02, 6.1139e-03],
[-4.0234e-02, -3.6375e-02, 5.7755e-02, -1.3112e-02, -6.8822e-02],
[-2.2237e-02, -3.2318e-02, 1.3830e-03, -1.1355e-02, -2.0060e-02]]], [[[-9.9372e-02, -6.5939e-02, 5.9860e-02, 1.4672e-02, -3.6069e-02],
[ 3.5409e-02, -8.1433e-02, 3.1477e-03, -2.8791e-02, 5.8678e-02],
[ 3.1001e-02, 5.9960e-02, -7.9541e-02, -1.0646e-01, 1.2691e-02],
[ 2.8374e-02, -1.0153e-01, 4.3227e-02, 7.5082e-02, -1.0772e-02],
[-4.9453e-02, -9.9464e-03, -4.2608e-02, -4.2708e-02, 5.5077e-02]]]])
⚠️torch.nn只支持mini_batches,不支持一次只输入一个样本,即一次必须是一个batch
如果只想输入一个样本,则使用input.unsqueeze(0)将batch_size设为1
4.损失函数
nn实现了神经网络中大多数的损失函数,例如nn.MSELoss用来计算均方误差,nn.CrossEntropyLoss用来计算交叉熵损失
output = net(input)
print(output)
target = Variable(t.arange(, ))
print(target)
criterion = nn.MSELoss()
loss = criterion(output, target)
loss
运行时遇到下面的错误:
RuntimeError: Expected object of scalar type Float but got scalar type Long for argument # 'target'
解决办法就是转换target类别为float即可:
output = net(input)
print(output)
target = Variable(t.arange(, )).float() #更改
print(target)
criterion = nn.MSELoss()
loss = criterion(output, target)
loss
返回:
tensor([[-0.0552, -0.0790, 0.0176, -0.1695, -0.1261, -0.0572, 0.0072, -0.1686,
0.0739, -0.0895]], grad_fn=<AddmmBackward>)
tensor([., ., ., ., ., ., ., ., ., .])
tensor(29.0489, grad_fn=<MseLossBackward>)
如果对该loss进行反向传播溯源,即使用grad_fn属性
首先从之前的网络可知其整个计算图为:
input -> conv2d -> relu -> maxpool2d -> conv2d -> relu -> maxpool2d
-> view -> linear -> relu -> linear -> relu -> linear
-> MSELoss
-> loss
当我们使用loss.backward()时,该图会动态生成并自动微分,也会自动计算途中参数(parameter)的导数
net.zero_grad() #把net中所有可学习的参数的梯度清零
print("反向传播前conv1.bias的梯度:")
print(net.conv1.bias.grad)
loss.backward()
print("反向传播后conv1.bias的梯度:")
print(net.conv1.bias.grad)
返回:
反向传播前conv1.bias的梯度:
tensor([., ., ., ., ., .])
反向传播后conv1.bias的梯度:
tensor([-0.0503, 0.0551, 0.0052, 0.0081, 0.0084, 0.0665])
5.优化器
torch.optim中实现了深度学习中绝大多数的优化方法,例如RMSProp,Adam,SGD等
import torch.optim as optim
#新建一个优化器,指定要调整的参数和学习率
optimizer = optim.SGD(net.parameters(), lr = 0.01) #在训练的过程中
#先将梯度清零(和net.zero_grad()效果一样)
optimizer.zero_grad() #计算损失
output = net(input)
loss = criterion(output, target) #反向传播
loss.backward() #更新参数
optimizer.step()
6.数据加载和预处理
torchvision实现了常用的图像数据加载功能,例如Imagenet,CIFAR10,MNIST等,以及常用的数据转换操作,这极大方便了数据加载
小试牛刀:CIFAR-10分类
可见pytorch例子学习——TRAINING A CLASSIFIER
深度学习框架PyTorch一书的学习-第一/二章的更多相关文章
- 深度学习框架PyTorch一书的学习-第五章-常用工具模块
https://github.com/chenyuntc/pytorch-book/blob/v1.0/chapter5-常用工具/chapter5.ipynb 希望大家直接到上面的网址去查看代码,下 ...
- 深度学习框架PyTorch一书的学习-第六章-实战指南
参考:https://github.com/chenyuntc/pytorch-book/tree/v1.0/chapter6-实战指南 希望大家直接到上面的网址去查看代码,下面是本人的笔记 将上面地 ...
- 深度学习框架PyTorch一书的学习-第四章-神经网络工具箱nn
参考https://github.com/chenyuntc/pytorch-book/tree/v1.0 希望大家直接到上面的网址去查看代码,下面是本人的笔记 本章介绍的nn模块是构建与autogr ...
- 深度学习框架PyTorch一书的学习-第三章-Tensor和autograd-2-autograd
参考https://github.com/chenyuntc/pytorch-book/tree/v1.0 希望大家直接到上面的网址去查看代码,下面是本人的笔记 torch.autograd就是为了方 ...
- 深度学习框架PyTorch一书的学习-第三章-Tensor和autograd-1-Tensor
参考https://github.com/chenyuntc/pytorch-book/tree/v1.0 希望大家直接到上面的网址去查看代码,下面是本人的笔记 Tensor Tensor可以是一个数 ...
- 深度学习框架PyTorch一书的学习-第七章-生成对抗网络(GAN)
参考:https://github.com/chenyuntc/pytorch-book/tree/v1.0/chapter7-GAN生成动漫头像 GAN解决了非监督学习中的著名问题:给定一批样本,训 ...
- 《深度学习框架PyTorch:入门与实践》的Loss函数构建代码运行问题
在学习陈云的教程<深度学习框架PyTorch:入门与实践>的损失函数构建时代码如下: 可我运行如下代码: output = net(input) target = Variable(t.a ...
- 《Linux内核设计与实现》 第一二章学习笔记
<Linux内核设计与实现> 第一二章学习笔记 第一章 Linux内核简介 1.1 Unix的历史 Unix的特点 Unix很简洁,所提供的系统调用都有很明确的设计目的. Unix中一切皆 ...
- 神工鬼斧惟肖惟妙,M1 mac系统深度学习框架Pytorch的二次元动漫动画风格迁移滤镜AnimeGANv2+Ffmpeg(图片+视频)快速实践
原文转载自「刘悦的技术博客」https://v3u.cn/a_id_201 前段时间,业界鼎鼎有名的动漫风格转化滤镜库AnimeGAN发布了最新的v2版本,一时间街谈巷议,风头无两.提起二次元,目前国 ...
随机推荐
- 【 js 工具 】如何在Github Pages搭建自己写的页面?
最近发现 github 改版了,已没有像原来的 Launch automatic page generator 这样的按钮等,所以我对我的文章也进行了修正,对于新版来说,步骤更加简单了.欢迎享用. - ...
- MySQL添加新用户、为用户创建数据库、为新用户分配权限
登录MySQL [root@VM_0_2_33_centos /]#mysql -u root -p 添加新用户 允许本地 IP 访问 localhost, 127.0.0.1 mysql>'; ...
- K8S 通过 yaml 文件创建资源
创建 pod cd ~ vi pod-demo.yaml # 内容如下 apiVersion: v1 kind: Pod metadata: name: pod-demo namespace: def ...
- C# 使用System.Data.OleDb;避免oracle中文乱码问题
首先,需要保证oracle客户端服务器的字符集是一样的,并且保证该字符集支持中文.你可以使用plsql查看是否乱码. 代码: using System; using System.Collection ...
- (后端)springboot 在idea中实现热部署(转)
自己用到了iIntelliJ IDEA 这个ide工具,但是和以前的工具写html,css,js直接刷新页面不同,这个需要去热部署,网上搜的解决方法: SpringBoot的web项目,在每一次修改了 ...
- python with open as f写中文乱码
python3和python2的写法不一样具体如下: python3: with open(r'd:\ssss.txt','w',encoding='utf-8') as f: f.write(u'中 ...
- Linux16.04 LTS 环境下将cmake的项目转换成eclipse可导入可调试的工程项目
Linux作为一个开源系统,其中的一个优势就是有效的将各种源码编译得到的库集合在一起,为项目的使用创建了便捷.通常情况下,我们在开发自己的开源项目时,喜欢使用cmake调用各种三方库,如opencv ...
- bootstrap-paginator分页示例 源码 MVC
准备 1.数据:bootstrap包(含分页插件bootstrap-paginator.js) 2.技术方案:ajax动态加载分页.部分视图.BLL取数 代码 模板页 @{ Layout = null ...
- C#-命名空间(十五)
概念 命名空间的设计目的是提供一种让一组名称与其他名称分隔开的方式 在一个命名空间中声明的类的名称与另一个命名空间中声明的相同的类的名称不冲突 命名空间的定义是有一定的规范,避免引起不必要的麻烦 命名 ...
- c strlen和sizeof详解
用双引号定义并且声明的时候明确指定数组大小的话,sizeof就会返回指定的大小,不会自动加1: char str2[10] = "hello c"; printf("st ...