Tensor(张量)DefinitionCreate a tensorOperationsBroadcastingAutogradAutograd简介梯度 Parallel ComputingWhy CUDAPC MethodsHow to use CUDA
Tensor(张量)
Definition
- In mathematics, a tensor is an algebraic object that describes a multilinear relationship between sets of algebraic objects related to a vector space.
- 例如标量可以视为0维的张量,矩阵就是二维的
- 多维的张量可以承载更多的数据,例如:
- 3维 = 时间序列
- 4维 = 图像
- 5维 = 视频
- 比如我们考虑一个照片,它可以表示为这样的一个张量
- 再比如说如果我们要训练一个照片的数据集,就可以考虑再加上一个数据量的维度,即
Create a tensor
torch.tensor( data )和torch.Tensor( size )- 第一个函数允许我们从已有的List或者是NumPy数组来创建张量
- 第二个允许我们创建一个基本张量
torch.ones(size)和torch.zeros(size)和torch.eye(size)- 创建单位矩阵或者是零矩阵或者是对角矩阵
torch.arange(s,e,step)和linspace(s, e, steps)torch.arange()函数用于创建一个等差数列的一维张量,其中参数包括起始值、终止值(不包括)、步长linspace(s, e, steps)函数用于在指定的起始值和终止值之间创建一个等间距的一维张量。它的用法如下:
rand(size)和randn(size)rand是[0,1)均匀分布;randn是服从N(0,1)的正态分布
normal(mean, std)- 正态分布(均值为mean,标准差是std)
randperm(n)- 函数用于生成一个随机排列的整数序列。它接受一个参数
n,表示生成的随机排列的整数范围是从 0 到n-1。返回的张量包含了从 0 到n-1的整数的随机排列
Operations
- 加法操作
- 使用原始加号
- 使用函数
add(tensor1,tensor2) - in-place,原值修改
前两个都是返回新的tensor,后一个是在原本基础上修改
- 索引操作
需要注意的是,索引出来的结果与原数据共享内存,修改一个,另一个会跟着修改。如果不想修改,可以考虑使用
copy( )等方法- 维度变换
- 张量的维度变换常见的方法有
torch.view()和torch.reshape(),主要谈第一种方法torch.view() y = x.view(16): 这行代码将原始张量x变形为一个包含16个元素的一维张量y。这相当于将原始张量展平成一个一维张量。view(16)中的参数 16 表示最终张量的大小z = x.view(-1, 8): 这行代码也是对张量x进行形状改变,但是通过1参数来自动计算另一维度的大小。这里1表示该维度的大小由其他维度的大小来决定,即张量的总元素数量不变的情况下,PyTorch 会根据其他维度的大小自动计算该维度的大小。在这个例子中,原始张量x包含了16个元素,所以 PyTorch 会根据需要自动计算出另一维度的大小为2。最终得到的张量z是一个大小为2x8的二维张量- 在 PyTorch 中,
view()方法用于改变张量的形状,但不会对原始张量进行修改,而是返回一个新的张量,其形状已经被改变了 - 很多情况下,我们希望原始张量和变换后的张量互相不影响。为为了使创建的张量和原始张量不共享内存,我们需要使用第二种方法
torch.reshape(), 同样可以改变张量的形状
torch.view() 返回的新tensor与源tensor共享内存(其实是同一个tensor),更改其中的一个,另外一个也会跟着改变。(顾名思义,view()仅仅是改变了对这个张量的观察角度)此函数并不能保证返回的是其拷贝值,所以官方不推荐使用。推荐的方法是我们先用clone()创造一个张量副本然后再使用torch.view()进行函数维度变换 。使用clone()还有一个好处是会被记录在计算图中,即梯度回传到副本时也会传到源 Tensor
- 取值操作 如果我们有一个元素
tensor,我们可以使用.item()来获得这个value,而不获得其他性质:
PyTorch中的 Tensor 支持超过一百种操作,包括转置、索引、切片、数学运算、线性代数、随机数等等,具体使用方法可参考官方文档
Broadcasting
对两个形状不一定的元素进行按元素进行运算的时候,会先将部分元素进行适当复制,再进行运算
Autograd
Autograd简介
autograd是一个面向
torch.tensor类的包梯度方向为方向导数最大的方向,梯度的模为最大的方向导数
- 基本流程
设置属性
.requires_grad 为 True,那么它将会追踪对于该张量的所有操作完成计算后可以通过调用
.backward(),来自动计算所有的梯度。这个张量的所有梯度将会自动累加到.grad属性。如果
y.backward() 中的y是标量,则无须传入参数;否则应当传入y的同型tensor- 阻止跟踪记录
- 要阻止一个张量被跟踪历史,可以调用
.detach()来将它分离并阻止它未来的计算记录被跟踪 - 为了防止跟踪历史记录(和使用内存),可以将代码块包装在
with torch.no_grad():中在评估模型时特别有用,因为模型可能具有requires_grad = True的可训练的参数,但是我们不需要在此过程中对他们进行梯度计算
- 函数与autograd
还有一个类对于
autograd的实现非常重要:FunctionTensor 和 Function 互相连接生成了一个无环图 (acyclic graph),它编码了完整的计算历史。每个张量都有一个.grad_fn属性,该属性引用了创建 Tensor 自身的Function(除非这个张量是用户手动创建的,即这个张量的grad_fn是 None )在PyTorch中,
grad_fn属性是张量的一个属性,它指示了张量是如何计算得到的。对于通过对输入进行数学运算或函数操作而得到的张量,PyTorch会为其分配一个grad_fn属性,用于构建计算图和计算梯度。grad_fn属性是一个PowBackward0对象的引用,它是PyTorch用于构建计算图和计算梯度的一部分创建一个张量并设置
requires_grad=True用来追踪其计算历史- 实例
- 如果需要计算导数,可以在
Tensor上调用.backward()。如果Tensor是一个标量(即它包含一个元素的数据),则不需要为backward()指定任何参数,但是如果它有更多的元素,则需要指定一个gradient参数,该参数是形状匹配的张量。 - 下面我们创建一个tensor来追踪其计算过程
.requires_grad_(...)函数可以随时修改张量的requires_grad参数,该参数默认初始化为Falsegrad_fn:是一个公开的属性,用于指示创建张量的操作或函数。它是一个指向创建当前张量的操作的引用,可以让用户轻松地了解张量是如何被创建的。例如,如果一个张量是通过加法操作创建的,则它的grad_fn属性将指向一个AddBackward0对象,表示它是通过加法操作得到的。grad_fn属性通常用于计算梯度时构建反向传播图。_grad_fn:这是一个内部属性,用于表示张量在反向传播过程中用于计算梯度的函数。与grad_fn不同,_grad_fn是在计算梯度时才会被设置的。它指向的对象通常是一个在反向传播中执行梯度计算的函数,而不是张量本身的创建操作。用户通常不需要直接访问_grad_fn属性。
区分
在 PyTorch 中,
grad_fn 和 _grad_fn 两者都是表示张量的属性,但在内部实现上有所不同。总的来说,
grad_fn 属性用于表示张量的创建过程,而 _grad_fn 属性用于表示张量在反向传播过程中所用的梯度计算函数。通常情况下,我们只需要关注 grad_fn 属性来理解张量是如何被创建的。梯度
我们先补充数学知识
考虑如下的两个张量,我们想要求解,我们可以通过一系列的推到最后得出一个结论:其雅各比行列式就是W的转置,即有
关于上面的解释,,前项取决于out(z)函数的运算本身,求导即为一个与z的同型矩阵,每个位上为0.25(1/4),而z对x的导数即为2的专置,也就是2
数学上,若有向量函数,那么关于的梯度就是一个雅可比矩阵:
而
torch.autograd 这个包就是用来计算一些雅可比矩阵的乘积的。例如,如果是一个标量函数的梯度,那么:,由链式法则,我们可以得到:这意味着梯度是累加的(accumulated)
现在我们来看一个雅可比向量积的例子:
在这种情况下,
y 不再是标量。torch.autograd 不能直接计算完整的雅可比矩阵,但是如果我们只想要雅可比向量积,只需传递一个张量参数给 backward:可以通过将代码块包装在
with torch.no_grad(): 中,来阻止 autograd 跟踪设置了.requires_grad=True的张量的历史记录。我们也可以通过对
tensor.data 进行操作来规避对梯度的影响Parallel Computing
Why CUDA
CUDA是NVIDIA提供的一种GPU并行计算框架。对于GPU本身的编程,使用的是CUDA语言来实现的。但是,在我们使用PyTorch编写深度学习代码时,使用的CUDA又是另一个意思。在PyTorch使用 CUDA表示要开始要求我们的模型或者数据开始使用GPU了。在编写程序中,当我们使用了
.cuda() 时,其功能是让我们的模型或者数据从CPU迁移到GPU上(默认是0号GPU)当中,通过GPU开始计算。tips
- 我们使用GPU时使用的是
.cuda()而不是使用.gpu(),AMD的GPU编程接口采用的是OpenCL,在现阶段PyTorch并不支持。
- 数据在GPU和CPU之间进行传递时会比较耗时,尽量避免数据的切换。
- GPU运算很快,但是在使用简单的操作时,我们应该尽量使用CPU去完成。
- 当我们的服务器上有多个GPU,我们应该指明我们使用的GPU是哪一块,如果我们不设置的话,tensor.cuda()方法会默认将tensor保存到第一块GPU上,等价于tensor.cuda(0),这将有可能导致爆显存。我们可以通过以下两种方式继续设置。
PC Methods
- Network partitioning

不同模型组件在不同的GPU上时,GPU之间的传输就很重要,对于GPU之间的通信是一个考验
- Layer-wise partitioning

这样可以保证在不同组件之间传输的问题,但是在我们需要大量的训练,同步任务加重的情况下,会出现和第一种方式一样的问题。
- Data parallelism

How to use CUDA
- 单卡
- 在PyTorch框架下,CUDA的使用变得非常简单,我们只需要显式的将数据和模型通过
.cuda()方法转移到GPU上就可加速我们的训练
- 多卡
- 单机多卡DP
DataParallel - 将计算任务划分成多个子任务并在多个GPU卡上同时执行这些子任务。主要使用到了
nn.DataParallel函数。 - 除此之外,我们也可以指定GPU进行并行训练,一般有两种方式
nn.DataParallel函数传入device_ids参数,可以指定了使用的GPU编号- 要手动指定对程序可见的GPU设备
- 多机多卡DDP

