赞
踩
pytorch 是深度学习框架和科学计算包
pytorch之所以可以进行科学计算是因为它是一个张量库并且有相关的张量运算
pytorch和numpy有很强的互操作性,原因:
1.张量和数组具有相似性;
2.pytorch的torch.tensor对象是由numpy的ndarray创建的,它们共享内存;
pytorch 张量运算可在GPU上运行;
pytorch的前身是torch,torch是基于lua语言(该语言晦涩难懂且生态系统很小);
pytorch的发起者是facebook的研究员Soumith Chintala
torch: 包含所有torch包和tensor库的顶级包
troch.nn: 是用于建立神经网络的包,它包含类和模块,如layer,weight和forward function
torch.nn.function: 访问损失函数的API
torch.autograd: 支持张量的导数运算,负责优化神经网络
torch.optim: 访问优化器 (SGD,ADAM...)
torch.utils: 包含数据集和数据加载器的实用程序类
torchvision: 访问流行的数据集、计算机视觉中的模型框架及图像转换
所有深度学习框架都有两个特性:张量库和计算导数的包(在pytorch中是torch和torch.autograd)
pytorch is modern, pythonic and thin design
pytorch is a python extension
pytorch的简单性使其具有更长的寿命
pytorch是研究中首选框架的原因:pytorch的计算图是动态的,而其他框架通常是静态的,许多深度学习领域的前沿研究都需要动态图或从动态图中获益
计算图是用于描绘神经网络中张量的函数操作,通常用于计算优化神经网络权重所需的导数
step1 : 下载并安装anaconda(https://www.anaconda.com/distribution/)
step2:进入pytorch网站(https://pytorch.org/), 选择相应的配置,会出现相应的命令行,复制在终端并运行,即可安装;
不需要自己额外安装Cuda,安装pytorch时Cuda会自动安装;
Cudnn是专门用于神经网络的包
- import torch #导入pytorch
- print(torch.__version__) #打印pytorch的版本 我的是2.0.0
-
- torch.cuda.is_available #判断Cuda在我们的系统上是否可用
- #我是Macbook,没有英伟达gpu,不可用;
- print(torch.version.cuda) #看cuda的版本 若有安装会显示出chuda的版本
最适合GPU的运算是可以并行完成的运算
并行计算:一个特定的计算被分解成独立的可以同时进行的很小的计算,得到的计算被重新组合和同步,以形成原始较大的计算结果
一个更大的计算可以被分解的任务的数量取决于硬件上的核数(CPU通常是4或16核,而GPU可以有成千上万个核,故GPU可以大大提升运算速度)
核是指在给定处理器中进行计算的单元
最适合GPU的任务是可以并行完成的任务,所以并行计算是使用GPU来完成的
为什么GPU在深度学习中能够如此广泛的使用?
因为神经网络是易并行的,即很容易就能够将任务分解成一组独立的小任务;神经网络的很多计算都可以很容易地分解成更小的相互独立的计算,这使得GPU在深度学习任务中非常有用;
用于神经网络的包
在安装pytorch时已自带
- import torch
- import numpy as np
- # 定义张量t,默认在CPU上
- t = torch.Tensor([1,2,3]) # tensor([1., 2., 3.])
- # 再将其放在GPU上进行操作
- t.cuda() # 由于Mac没有GPU版的pytorch,我的会报错
张量是神经网络中使用的主要数据结构,网络中的输入、输出和转换均使用张量表示;
n维张量中,n表示在结构中访问特定元素所需要的索引数量(张量是n维数组);
张量被称为一个泛化的原因:我们可以使用张量来表示所有的n维数组;
索引数量 | 计算机科学中的名称 | 数学中的名称 | Tensor表示 |
0 | 数字 | 标量 | 0维张量 |
1 | 数组 | 矢量 | 1维张量 |
2 | 二维数组 | 矩阵 | 2维张量 |
n | N维数组 | N维张量 | n维张量 |
注意:张量的维度跟我们所说的向两空间的向量维度不同,张量的维度并不能告诉我们张量中有多少个分量,如下:二维张量t有9个分量。
- t = torch.Tensor([[1,2,3],[4,5,6],[7,8,9]])
-
- #tensor([[1., 2., 3.],
- # [4., 5., 6.],
- # [7., 8., 9.]])
张量的秩、轴和形状是使用张量时最关注的三个属性;
所有的秩、轴和形状都与索引的概念有本质上的联系;
张量的秩是指张量的维数
一个张量的秩告诉我们需要多少个索引来访问或引用张量结构中包含的特定数据元素
一个张量的轴是一个张量的一个特定维度
对于张量,其最后一个轴的元素均为数字
张量的秩告诉我们一个张量有多少个轴
张量的形状由每个轴的长度决定(知道了张量的形状就可知道每个轴的索引)
- t = torch.tensor([[1,2,3],[4,5,6],[7,8,9]])
- type(t) # 查看张量的类型 torch.Tensor
- t.shape # 查看张量的形状 torch.Size([3, 3])
- rank = len(t.shape) # 张量的秩等于其shape的长度 2
- t.reshape(9,1) # 张量重塑会改变元素的分组,但不会改变元素本身和元素总数
- # tensor([[1],
- # [2],
- # [3],
- # [4],
- # [5],
- # [6],
- # [7],
- # [8],
- # [9]])
CNN输入张量的长度通常为4:[batchsize, color_channel, height, width];通过这4个索引,可以在特定图像的特定颜色通道中导航到特定的像素;
卷积神经网络的样本输入通常是批量的而不是单个的;
张量经过卷积层后的变化:卷积会改变高度、宽度以及颜色通道的数量;通道数与滤波器的数量有关;滤波器的大小会影响到高度和宽度;
经过卷积的通道不再叫彩色通道(已被改变),而叫做特征通道(特征图:输入颜色通道和卷积滤波器所产生的卷积结果)
输出通道 = 特征通道 = 特征映射
pytorch神经网络中必须编写的第一行程序是“数据预处理程序”
数据预处理的最终目标:是将我们要处理的任何数据转换成能够感知神经网络的张量
Data type | dtype | CPU tensor | GPU tensor |
32-bit floating point | torch.float32 | torch.FloatTensor | torch.cuda.FloatTensor |
64-bit floating point | torch.float64 | torch.DoubleTensor | torch.cuda.DoubleTensor |
16-bit floating point | torch.float16 | torch.HalfTensor | torch.cuda.HalfTensor |
8-bit integer(unsigned) | torch.uint8 | torch.BYteTensor | torch.cuda.ByteTensor |
8-bit integer(signed) | torch.int8 | torch.CharTensor | torch.cuda.CharTensor |
16-bit integer(signed) | torch,int16 | torch.ShortTensor | torch.cuda.ShortTensor |
32-bit integer(signed) | torch.int32 | torch.IntTensor | torch.cuda.IntTensor |
64-bit integer(signed) | torch.int64 | torch.LongTensor | torch.cuda.LongTensor |
- t=torch.tensor([[1,2,3],[4,5,6],[7,8,9]])
- print(t.dtype) # 张量中包含的数据类型 torch.int64
- print(t.device) # 数据分配的位置,CPU或GPU
- print(t.layout) # 默认为strided:告诉我们张量数据是如何在内存中布局的,通常不需要改变
注意:张量与张量之间的运算必须是相同数据类型在相同的设备上发生的
- t=np.array([[1,2,3],[4,5,6],[7,8,9]])
- print(torch.Tensor(t)) # 类构造函数
- print(torch.tensor(t)) # 工厂函数
- print(torch.as_tensor(t)) # 工厂函数
- print(torch.from_numpy(t)) # 工厂函数
-
- tensor([[1., 2., 3.],
- [4., 5., 6.],
- [7., 8., 9.]])
- tensor([[1, 2, 3],
- [4, 5, 6],
- [7, 8, 9]], dtype=torch.int32)
- tensor([[1, 2, 3],
- [4, 5, 6],
- [7, 8, 9]], dtype=torch.int32)
- tensor([[1, 2, 3],
- [4, 5, 6],
- [7, 8, 9]], dtype=torch.int32)
工厂函数:接受参数输入并返回特定类型对象的函数;
工厂函数允许更多的动态对象创建;具有更好的文档,并有更多的配置参数,因此通常情况下会更倾向于选择工厂函数;
- # 单位张量的创建(二维)
- torch.eye(3,3)
-
- tensor([[1., 0., 0.],
- [0., 1., 0.],
- [0., 0., 1.]])
-
- # 全零张量的创建(二维)
- torch.zeros(3,3)
-
- tensor([[0., 0., 0.],
- [0., 0., 0.],
- [0., 0., 0.]])
-
- # 全1张量的创建(二维)
- torch.ones(3,3)
-
- tensor([[1., 1., 1.],
- [1., 1., 1.],
- [1., 1., 1.]])
-
- # 随机张量的创建(二维)
- torch.rand(3,3)
-
- tensor([[0.0187, 0.5821, 0.6798],
- [0.9749, 0.1791, 0.4112],
- [0.5518, 0.6551, 0.5451]])
- import torch
- import numpy as np
-
- data=np.array([1,2,3])
- t1=torch.Tensor(data)
- print(t1)
- print(t1.dtype)
-
- t2=torch.tensor(data)
- print(t2)
- print(t2.dtype)
-
- t3=torch.as_tensor(data)
- print(t3)
- print(t3.dtype)
-
- t4=torch.from_numpy(data)
- print(t4)
- print(t4.dtype)
- # 结果
- tensor([1., 2., 3.])
- torch.float32
- tensor([1, 2, 3])
- torch.int64
- tensor([1, 2, 3])
- torch.int64
- tensor([1, 2, 3])
- torch.int64
上述使用类构造函数和工厂函数后生成的数据类型不同,主要原因是:构造函数在构造一个张量时使用全局缺省值,而工厂函数通过输入数据的类型来推断输出数据的类型
- # 查看全局缺省值的数据类型
- d=torch.get_default_dtype #<function torch.get_default_dtype>
-
- # 工厂函数可以显示指定数据类型,类构造函数不能这样操作
- t=torch.tensor(np.array([1,2,3]), dtype=torch.float64)
- # tensor([1., 2., 3.], dtype=torch.float64)
- import torch
- import numpy as np
-
- data=np.array([1,2,3])
- print(data) # [1 2 3]
-
- t1=torch.Tensor(data)
- t2=torch.tensor(data)
- t3=torch.as_tensor(data)
- t4=torch.from_numpy(data)
-
- # 更改数组
- data[0] =0
- data[1] =0
- data[2] =0
- # t1 和 t2 输出的都是更改前的数组
- print(t1) # tensor([1., 2., 3.])
- print(t2) # tensor([1, 2, 3])
- # t3 和 t4 输出的都是更改后的数组
- print(t3) # tensor([0, 0, 0])
- print(t4) # tensor([0, 0, 0])
上述差异是由创建时分配内存的方式造成的:t1和t2的方式是将数组中的元素值直接拷贝到张量中,改变data中的元素值并不会影响到t1和t2中的值;t3和t4的方式是与data数组共享数据(可将t1和t2的方式看作"值传递";t3和t4的方式看作“地址传递”)
共享数据 | 数据拷贝 |
torch.as_tensor() | torch.tensor() |
torch.from_numpy() | torch.Tensor() |
由上可知numpy和tensor是数据共享的,所以他们可以无缝切换
数据共享比数据拷贝更高效,更节省内存空间
数据拷贝方式的最优选择是 torch.tensor() (因为是工厂函数)
内存共享方式的最优选择是 torch.as_tensor() (因为torch.as_tensor可以接受任何python数据结构;而torch.from_numpy只接受numpy数组)
数据拷贝的方式更注重实现;而内存共享的方式更注重代码性能
由于numpy.ndaaray对象分配在CPU上,所以如果使用GPU的话,torch.as_tensor函数必须把数据从CPU上拷到GPU上
as_tensor()对于python内置的数据结构,如列表,是无效的
as_tensor的调用要求熟悉共享特征,以免对底层数据做不必要的更改,而影响到对象
当as_tensor和numpy.ndarray有大量的相互往返的操作时,对性能的提升会有较大的影响
张量的重塑是最重要的张量操作,因为张量的形状能提供给我们一些具体的东西,我们可以用它来塑造和直观的理解张量。
元素的个数与张量的重塑有直接的关系,重塑后每个轴长之积必须与元素个数相同
- import torch
- t=torch.tensor([ [1,1,1,1],
- [2,2,2,2],
- [3,3,3,3] ], dtype=torch.float32)
- print(t.size) # torch.Size([3, 4])
- print(t.shape)
-
- # 得到张量中的元素个数
- # 方法1:
- torch.tensor(t.shape).prod() # 12
- # 方法2:
- t.numel() # number of elements的简写
-
- # 张量的reshape,squeezing和unsqueezing
- print(t.reshape(1,12))
- print(t.reshape(1,12).shape)
-
- print(t.reshape(1,12).squeeze())
- print(t.reshape(1,12).squeeze().shape)
-
- 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])
-
- tensor([1., 1., 1., 1., 2., 2., 2., 2., 3., 3., 3., 3.])
- torch.Size([12])
-
- tensor([[1., 1., 1., 1., 2., 2., 2., 2., 3., 3., 3., 3.]])
- torch.Size([1, 12])
squeezing:可以移除所有长度为1的轴 (减少秩)
unsqueezing: 会增加一个长度为1的轴 (增加秩)
- # 张量的flatten
- t=torch.tensor([[1,1,1,1],
- [2,2,2,2],
- [3,3,3,3]], dtype=torch.float32)
-
- # 方法1:
- def flatten(t):
- t1=t.reshape(1,-1) # -1:根据输入t的总元素数确定该值为多少
- t1=t1.squeeze()
- return t1
- print(flatten(t))
-
- # 方法2:
- t2=t.reshape(1,-1)[0]
- print(t2)
-
- # 方法3:
- t3=t.reshape(-1)
-
- # 方法4:
- t4=t.view(t.numel())
-
- # 方法5:
- t5=t.flatten()
-
- # 结果都是
- tensor([1., 1., 1., 1., 2., 2., 2., 2., 3., 3., 3., 3.])
flatten张量:除去所有的轴,只保留一个,创造一个单轴张量包含原张量所有元素;
flatten操作是从一个卷积层过度到一个全连接层时在神经网络中必须发生的;
flatten操作是一种特殊的reshaping操作,即所有轴被挤压成一个轴
flatten操作的前提:张量至少有两个轴
全连接层接收被flatten的张量来作为输入
多批次张量的flatten:保持batch的长度不变,只flatten图像通道部分
- # t1,t2,t3为三个4x4的图像
- 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]
- ])
- # 使用stack将其合并成一个秩为3的张量
- t=torch.stack((t1,t2,t3)) # torch.Size([3, 4, 4])
-
- # 增加一个彩色通道轴,将其变成CNN期望的形式,以上三张图像均为灰度图像
- t=t.reshape(3,1,4,4)
-
- print(t[0]) # 第一个图像
- print(t[0][0]) # 第一个图像的第一个通道
- print(t[0][0][0]) # 第一个图像的第一个通道中的第一行像素
- print(t[0][0][0][0]) # 第一个图像的第一个通道中的第一行的第一个像素
-
- # 我们期望只将图像张量flatten,而不是将其全部flatten,即 (3,1,4,4)->(3,16)
- print(t.flatten(start_dim=1).shape) # torch.Size([3, 16])
- print(t.flatten(start_dim=1)) # 参数start_dim告诉flatten函数应该从哪个轴开始
- # flatten结果
- 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]])
元素运算是对张量元素的运算,这些元素在张量中对应或具有相同的位置索引
两个张量必须有相同的形状才能执行元素操作
- t1=torch.tensor([
- [1,2],
- [3,4]
- ], dtype=torch.float32)
- t2=torch.tensor([
- [9,8],
- [7,6]
- ], dtype=torch.float32)
-
- t1+t2
- # 结果
- tensor([[10., 10.],
- [10., 10.]])
-
- t1+2
- # 结果
- tensor([[3., 4.],
- [5., 6.]])
-
- t1-2
- # 结果
- tensor([[-1., 0.],
- [ 1., 2.]])
-
- t1*2
- # 结果
- tensor([[2., 4.],
- [6., 8.]])
-
- t1/2
- # 结果
- tensor([[0.5000, 1.0000],
- [1.5000, 2.0000]])
张量的广播:将标量变成与另一个张量相同的形状,张量广播在数据标准化时会常用到
- t1=torch.tensor([
- [1,1],
- [1,1] ])
- t2=torch.tensor([2,4])
-
- t1+t2
- # 广播 np.broadcast_to(t2.numpy,t1.shape)
- # 结果
- tensor([[3, 5],
- [3, 5]])
会返回一个与比较张量形状相同的张量,其值为0或1(0表示False;1表示True)
缩减操作是一个减少张量中包含的元素数量的操作
元素操作允许我们对多个张量进行操作;缩减操作允许我们对单个张量进行操作
- t=torch.tensor([
- [0,1,0],
- [2,0,2],
- [0,3,0]
- ], dtype=torch.float32)
- print(t.sum())
- print(t.numel())
- print(t.sum().numel())
- print(t.sum().numel() <t.numel())
-
- # 结果
- tensor(8.)
- 9
- 1
- True
除了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)
- t.sum(dim=0) # 对列求和 tensor([6., 6., 6., 6.])
- 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() # 输出的是flatten后的索引 tensor(11)
-
- t.max(dim=0) # 输出的是每列的最大值及其索引
- # 结果
- torch.return_types.max(
- values=tensor([4., 3., 3., 5.]),
- indices=tensor([2, 1, 1, 2]))
-
- t.max(dim=1) # 输出的是每行的最大值及其索引
- # 结果
- torch.return_types.max(
- values=tensor([2., 3., 5.]),
- indices=tensor([3, 1, 3]))
- t=torch.tensor([
- [1,2,3],
- [4,5,6],
- [7,8,9]
- ], dtype=torch.float32)
- t.mean() # 求平均值 tensor(5.)
- t.mean().item() # 把标量值张量变为值 5.0
-
- # 返回多个值
- # 方法一
- t.mean(dim=0).tolist() # 返回每一列求平均值的结果 [4.0, 5.0, 6.0]
- # 方法二
- t.mean(dim=0).numpy() # array([4., 5., 6.], dtype=float32)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。