视频地址(B站):
适用于初学者的PyTorch神经网络编程教学
课程官方博客地址:
DEEPLIZARD
个人笔记第二部分:
DeepLizard:Pytorch神经网络编程学习笔记(第二部分)
说明
:
- 笔记为个人学习笔记,如有错误,欢迎指正!
-
本篇笔记整理的内容为课程的第一部分,对应视频为
P1
∼
P
13
P_1\sim P_{13}
P
1
∼
P
13
- DeepLizard课程为全英文授课,B站视频为中文字幕,且每个视频都有配套的博客与课后测试题。
Section 1:PyTorch介绍
对应视频:
P
1
∼
P
4
P_1\sim P_4
P
1
∼
P
4
P1:PyTorch历史
略
P2:PyTorch常用包以及PyTorch的优势
- torch:顶层包
- torch.nn:包括类和模块,比如图层、权重和前向函数。P.S.神经网络是建立在torch.nn的基础上的
-
torch.autograd:负责处理在核心处优化我们的神经网络权重所需的导数计算
- P.S.所有深度学习框架都有两个特性:一个张量库和一个用于计算导数的包。对PyTorch来说,它们是torch和torch.autograd
- torch.nn.functional:功能接口,有关函数
- torch.optim:优化
- torch.utils:包含数据集和数据加载器这样的实用程序类,使数据预处理更加容易
- torchvision:一个单独的包,能让我们访问流行的数据集、计算机视觉的模型架构等
关于计算图
计算图用于绘制神经网络中张量上发生的函数操作,然后这些图用于计算优化神经网络所需的导数。
在PyTorch中,使用动态计算图,也就是说图是在创建操作时动态生成的。深度学习中的许多前沿研究课题都需要用到动态图。
P3:PyTorch的安装
略。
P4:进一步介绍cuda,为什么使用GPU
GPU
并行计算
:通过特定的计算,可被分解为同时执行的独立较小的计算,然后得到结果,之后进行重新组合以形成原始较大计算的结果。
GPU
:适用于处理并行计算,可以使用并行编程和GPU加速我们的计算。神经网络中的许多计算,都可以很容易分解为更小的相互独立的计算,这使得GPU在深度学习中非常有用。e.g.卷积计算
CUDA
Nvidia GPU 是支持并行计算的硬件,而cuda是为开发者提供API的软件层。
使用pytorch驱动cuda:
# 方式一:以这种方式创建的张量对象在默认情况下是在CPU上运行
t = torch.tensor([1,2,3])
# 方式二:在一个张量上调用cuda函数会返回同样的张量,但是是在GPU上
t = t.cuda()
cuda0,0代表索引,pytorch支持多个GPU,如果有多个GPU,你可以把这个张量放在一个特定的GPU上,计算就可以被选择性执行。
GPU VS CPU
- GPU对于特定的专门任务来说,速度更快
- 将数据从CPU转移到GPU上代价太高,因此将计算量较小的任务转移到GPU上反而可能使整体性能下降
GPU计算堆栈
- GPU作为底层硬件
- Cuda是GPU顶端的软件架构
- 最后是cuda顶部的cudnn库(cudnn是cuda的深层神经网络组件)
-
cuda和cudnn顶部是pytorch,它将会与最终支持应用程序的应用程序一起工作
Section 2:引入张量
对应视频:
P
5
∼
P
9
P_5\sim P_9
P
5
∼
P
9
P5:张量介绍
张量的概念
张量的概念是对其他更具体概念的数学推广。
张量的一般概念的具体实例:
- 第一组中的都是计算机科学中常用的术语
- 第二组是数学中常用的术语
访问元素所需的索引
张量是泛化
在机器学习中,张量是一个多维数组或简称n维数组。
说张量是泛化的原因是,我们用张量这个词来表示所有的n值,例如标量是零维张量,矢量是一维张量,矩阵是二维张量,n维数组是一个n维向量。张量这个概念允许我们只用一个n来确定我们正在处理的维度的数量。
但是张量的维数与向量空间中向量的维数不同,张量的维数并未告诉我们张量中有多少个分量。例如,在三维欧几里得空间中的一个三维向量,它有三分量(x,y,z);但是一个三维张量可以有多于三个的分量,例如该dd二维张量就有9个分量:
P6:深入研究张量,并引入张量的三个基本属性(秩、轴、形状)
张量的秩
张量的秩:张量中存在的维数。
一个张量的秩告诉我们需要多少个索引来访问或引用张量数据结构中包含的特定数据元素。例如,我们有一个二维张量,我们就需要两个索引来访问张量的单个元素:
张量的轴
张量的轴:是一个张量的一个特定的维度。
比如说有一个二维张量,意思是这个张量有两个轴。
元素被认为是存在或是沿着一个轴运动,这个运动会受到每个轴长度的限制,每个轴的长度告诉我们沿轴有多少个索引。
例如,有一个名为t的张量,它第一个轴的长度为3,第二个轴的长度为4。也就是说,我们可以沿着第一个轴索引3个位置,沿着第二个轴索引4个位置:
例如之前的dd张量,第一个轴上的每个元素都是一个数组,第二个轴上的每个元素都是数字:
需要注意,对于张量,最后一个轴的元素总是数字,每个轴都包含一个n维数组。
张量的形状
张量的形状是由每个轴的长度决定的,也就是说,给定一个张量的形状,我们也就知道了每个轴的长度。
如上图所示,3 * 3的形状告诉我们这个秩为2的张量的每个轴的长度为3,也就是说每个轴上都有3个索引。
张量的秩等于其形状的长度:
张量形状的重要性:
- shape允许我们从想象一个张量,甚至更高阶更抽象的张量
- shape还对所有有关轴、秩、索引的相关信息进行编码
- 在神经网络编程时,常会进行reshape的操作,当我们的张量在网络中流动时,在网络中不同的点会有特定的形状,因此我们要有能力理解其形状,并能够根据需要对其进行重塑
张量的重塑
如上图所示的例子,形状改变了元素的分组,但并没有改变潜在的元素,数据保持不变。
使用pytorch进行重塑:为每个轴指定我们想要的长度:
需要注意的是,在重塑时,分量值的乘积必须等于张量中元素的总数。
P7:用一个实际的例子来演示张量概念的使用
以CNN为例,把图像输入看做是CNN的张量,注意,张量的形状编码了所有关于张量的轴、秩和索引的相关信息。
CNN输入的形状
CNN输入的形状通常为4,这就说明我们有一个秩为4的张量,它有4个轴:
张量形状中的每个索引代表一个特定的轴,每个索引处的值为我们提供了相应轴的长度:
如果沿着最后一个轴运行,我们会看到一个个数字:
如果沿着其他轴运行,我们看到的元素将是多维数组:
下标含义
对于图像,原始数据以像素的形式出现,像素由一个数字表示,并用高度和宽度来表示两个维度。图像的高度和宽度在最后两个轴上表示,下一个轴表示颜色通道:
有了这三个轴,我们访问数据时就需要三个索引,选中一个颜色通道,选中一个高度,再选中一个宽度就能找到特定的像素值:
第一个轴表示一个批量大小,在神经网络中,我们通常使用批量的样本而不是单一样本,这个轴的长度告诉我们在我们的批中有多少个样本:
例如,给定形状为[3, 1, 28, 28]的张量,我们就可以知道有一批三个图像,每个图像有1个颜色通道,图像的高度和宽度均为28:
输出通道和特征图
假设有一个张量,其中包含来自单个灰度图像的数据,张量的形状为[1, 1, 28, 28]。假设这个图像被传递给我们的CNN并通过第一个卷积层,卷积将改变高度和宽度以及通道数,输出通道的数量根据卷积层中使用的滤波器数量而变化。
假设有三个卷积滤波器。那么卷积前后的变化为:
卷积前
卷积后
使用输出通道,我们不再有颜色通道,而是修改后的通道,我们称之为特征图。这些所谓的特征图是使用输入颜色通道和卷积滤波器发生的卷积的输出。特征图是从卷积创建的输出通道。
P8:探索pytorch张量(神经网络编程)
PyTorch张量介绍
PyTorch 张量是 Python 类的实例,可以使用类构造函数创建对象:
t = torch.Tensor()
张量的属性:
torch.dtype
torch.device
torch.layout
可以查看张量的默认属性值:
- dtpye指定在张量中包含的数据类型,每种类型的数据都有一个CPU和一个GPU版本,要注意张量之间的运算必须与同数据类型的张量发生,但在1.3版本及以上的可以进行混合运算。
- device指定设备,要么是CPU,要么是GPU,这是数据被分配的位置。使用多个设备时,张量之间的张量操作必须与存在于统一设备上的张量发生。
- layout:strided告诉我们张量数据时如何在内存中布局的,是默认值,也没必要改变。
创建张量
-
无数据创建张量
- 使用数据创建张量
torch.Tensor(data)
torch.tensor(data)
torch.as_tensor(data)
torch.from_numpy(data)
P9:将数据转换为PyTorch张量的方法,以及这些方法之间的区别
-
默认数据类型
可以看到除了torch.Tensor(data)的默认数据类型是float32之外,其他几个都是int32。 -
内存的复制与共享
可以看到,torch.as_tensor()和torch.from_numpy()采用的是内存共享,而torch.tensor()和torch.Tensor()采用的是内存复制。 - 在PyTorch中创建张量的最佳选择
torch.tensor()
torch.as_tensor()
Section 3:张量运算
对应视频:
P
10
∼
P
13
P_{10}\sim P_{13}
P
10
∼
P
13
P10:Flatten,Reshape,Squeeze
Reshape
假设有以下张量:
t = torch.tensor([
[1,1,1,1],
[2,2,2,2],
[3,3,3,3]
], dtype=torch.float32)
-
获取张量的形状:
t.size() # torch.Size([3, 4]) t.shape # torch.Size([3, 4])
-
张量的秩:(张量的秩就等于其形状的长度)
len(t.shape) # 2
-
张量中包含的元素数量
torch.tensor(t.shape).prod() # tensor(12) t.numel() # 12
-
张量重塑:用
reshape()
函数可以进行张量重塑,但要注意所有“行”和“列”的乘积要等于元素的个数。比如高维度的重塑:> t.reshape(2,2,3) tensor( [ [ [1., 1., 1.], [1., 2., 2.] ], [ [2., 2., 3.], [3., 3., 3.] ] ])
Squeeze
可以通过squeeze和unsqueeze来改变张量的形状。
- 对张量进行squeeze操作会删除长度为1的轴或维度
- 对张量进行unsqueeze操作会添加一个长度为1的维度
> print(t.reshape([1,12]))
> print(t.reshape([1,12]).shape)
tensor([[1., 1., 1., 1., 2., 2., 2., 2., 3., 3., 3., 3.]])
torch.Size([1, 12])
> print(t.reshape([1,12]).squeeze())
> print(t.reshape([1,12]).squeeze().shape)
tensor([1., 1., 1., 1., 2., 2., 2., 2., 3., 3., 3., 3.])
torch.Size([12]) # 删除长度为1的维度
> print(t.reshape([1,12]).squeeze().unsqueeze(dim=0))
> print(t.reshape([1,12]).squeeze().unsqueeze(dim=0).shape)
tensor([[1., 1., 1., 1., 2., 2., 2., 2., 3., 3., 3., 3.]])
torch.Size([1, 12]) # 添加长度为1的维度
Flatten
flatten操作将调整张量的形状,使其形状等于张量中包含的元素数。flatten张量意味着删除除了一个维度之外的所有维度。
def flatten(t):
t = t.reshape(1, -1) # -1告诉reshape函数,根据一个张量中包含的其他值和元素个数来求出值应该是多少
t = t.squeeze()
return t
示例:
> t = torch.ones(4, 3)
> t
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]])
> flatten(t)
tensor([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.])
连接张量cat()
假设有以下张量:
-
方式一:按axis0进行连接
> torch.cat((t1, t2), dim=0) tensor([[1, 2], [3, 4], [5, 6], [7, 8]])
-
方式二:按axis1进行连接
> torch.cat((t1, t2), dim=1) tensor([[1, 2, 5, 6], [3, 4, 7, 8]])
P11:对一个灰度图像张量的张量flatten操作,如何在一个张量中flatten特定的轴
为一批图像构建张量表示
假设有以下三个张量
t1 = torch.tensor([
[1,1,1,1],
[1,1,1,1],
[1,1,1,1],
[1,1,1,1]
])
t2 = torch.tensor([
[2,2,2,2],
[2,2,2,2],
[2,2,2,2],
[2,2,2,2]
])
t3 = torch.tensor([
[3,3,3,3],
[3,3,3,3],
[3,3,3,3],
[3,3,3,3]
])
将这三个张量合成一个具有三个轴的更大张量:
> t = torch.stack((t1, t2, t3))
> t.shape
torch.Size([3, 4, 4])
添加一个指定长度为1的轴:
> t = t.reshape(3,1,4,4)
> t
直观的看每个轴的元素:
-
t[0]:
-
t[0][0]:
-
t[0][0][0]:
-
t[0][0][0][0]:
展平张量的批处理轴
> t.reshape(1,-1)[0] # Thank you Mick!
tensor([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3])
> t.reshape(-1) # Thank you Aamir!
tensor([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3])
> t.view(t.numel()) # Thank you Ulm!
tensor([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3])
> t.flatten() # Thank you PyTorch!
tensor([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3])
这里,对整个批次进行了展平操作,这将所有图像粉碎为一个轴。
展平张量的C,H,W轴
展平每个图像,同时保持批处理轴。
> t.flatten(start_dim=1).shape
torch.Size([3, 16])
> t.flatten(start_dim=1)
tensor(
[
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]
]
)
注意:参数start_dim告诉flatten函数应该在哪个轴上启动展平操作,这里start_dim=1,也就是说,我们跳过批处理轴,使其保持不变。
示例:RGB图像的拼合与展平
-
构建一个高度和宽度均为2的RGB图像张量:
import torch r = torch.ones(1,2,2) g = torch.ones(1,2,2) + 1 b = torch.ones(1,2,2) + 2 img = torch.cat( (r,g,b) ,dim = 0 ) print(img) # 输出 tensor([[[1., 1.], [1., 1.]], [[2., 2.], [2., 2.]], [[3., 3.], [3., 3.]]])
-
展平图像张量
img.flatten(start_dim=0) # 输出 tensor([1., 1., 1., 1., 2., 2., 2., 2., 3., 3., 3., 3.]) img.flatten(start_dim=1) # 输出 tensor([[1., 1., 1., 1.], [2., 2., 2., 2.], [3., 3., 3., 3.]])
P12:元素的运算
元素操作的定义:元素运算是对张量元素的运算,这些张量元素在张量中对应或有相同的索引位置
广播broadcast,描述了在元素操作期间如何处理不同形状的张量,它允许我们将标量添加到更高维的张量中。
秩不同的张量进行元素操作时,小秩向大秩看齐,也就是较低秩的张量通过广播进行转换,以匹配高阶张量的形状。
P13:张量的缩减操作,argmax函数
张量的缩减操作
- 常见的张量缩减操作
t.sum()
t.prod()
t.mean()
t.std()
- 按轴减少张量
t = torch.tensor([
[1,1,1,1],
[2,2,2,2],
[3,3,3,3]
], dtype=torch.float32)
# dim = 0
t.sum(dim=0)
# 输出
tensor([6., 6., 6., 6.])
# dim = 1
t.sum(dim=1)
# 输出
tensor([ 4., 8., 12.])
argmax函数
argmax函数返回张量内最大值的索引位置。
t = torch.tensor([
[1,0,0,2],
[0,3,3,0],
[4,0,0,5]
], dtype=torch.float32)
t.max()
# 输出:tensor(5.)
t.argmax()
# 输出:tensor(11)
t.flatten()
# 输出:tensor([1., 0., 0., 2., 0., 3., 3., 0., 4., 0., 0., 5.])
使用特定的轴:
t.max(dim=0)
# 输出
(tensor([4., 3., 3., 5.]), tensor([2, 1, 1, 2]))
t.argmax(dim=0)
# 输出
tensor([2, 1, 1, 2])
t.max(dim=1)
# 输出
(tensor([2., 3., 5.]), tensor([3, 1, 3]))
t.argmax(dim=1)
# 输出
tensor([3, 1, 3])
个人笔记第二部分:
DeepLizard:Pytorch神经网络编程学习笔记(第二部分)