深度学习中需要大量对数据进行操作,本文以Pytorch框架为例,对常用的数据操作进行总结。
基本操作深度学习中常使用张量(tensor)这一概念,张量最初起源于力学,用于表示弹性介质中各点应力状态,后来发展为力学和物理学的一个数学工具。它可以满足一切物理定于必须与坐标系的选择无关的特性,是矢量概念的推广。在理解时大致上可以直接将其对应为向量。零阶张量对应标量,一阶张量对应向量,二阶张量对应矩阵。
可以使用arrange创建行向量,张量默认存储在内存中,并采用基于CPU的计算。
x = torch.arrange(12)
可以采用shape来输出张量的形状。
x.shape
调用reshape函数可以改变张量的形状
x.reshape(3,4) #将张量变为3*4的矩阵 x.shape(3,-1) #将张量变为3*4的矩阵,-1表示宽度无需指定,会自动计算
可以用zeros,ones,randn来初始化创建张量,分别表示张量的元素为全0,全1,随机从标准正态分布中取值。也可以通过python列表来为张量元素赋值。
torch.ones(2,3,4) troch.zeros(2,3,4) torch.randn(2,3,4) torch.tensor([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])运算符
张量之间可以进行算术运算,对于任一具有相同形状的张量,可以使用标准算术运算符升级为按元素运算。
x = torch.tensor([1.0, 2, 4, 8]) y = torch.tensor([2, 2, 2, 2]) x + y, x - y, x * y, x / y, x ** y # **运算符是求幂运算
还可以将两个或多个张量连结(concatenate)起来个构成一个更大的张量
X = torch.arange(12, dtype=torch.float32).reshape((3,4)) Y = torch.tensor([[2.0, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]]) torch.cat((X, Y), dim=0), torch.cat((X, Y), dim=1)
此外,还可以对张量进行线性代数运算,包括向量点积和矩阵乘法。
点积
y = torch.ones(4, dtype = torch.float32) x, y, torch.dot(x, y) np.sum(x * y) #也可以通过按元素乘法然后求和的方式来求向量的点积
向量积与点积类似,区别在于(1)调用mv函数;(2)是矩阵乘向量,点积是向量乘向量。
torch.mv(A, x)
矩阵乘法调用mm函数
torch.mm(A, B)范数
向量范数是将向量映射到标量的函数f,可以简单理解为向量的大小。特殊地,欧氏距离为 L 2 L_2 L2范数。 L 1 L_1 L1范数表示向量各元素的绝对值之和
u = torch.tensor([3.0, -4.0]) torch.norm(u) #L2范数 torch.abs(u).sum() #L1范数
矩阵范数的计算同理。
广播机制上节介绍了相同形状的张量如何进行运算,本节介绍形状不同的张量的运算。可以通过广播机制(Broadcasting Mechanism)进行操作,首先通过复制元素使得两个张量具有相同的形状,其次对生成的数组执行按元素操作。
a = torch.arange(3).reshape((3, 1)) b = torch.arange(2).reshape((1, 2))索引与切片
类似于Python语言的特性,张量中的元素可以通过索引访问,也可以通过切片访问,此处略过
节省内存运行某些操作可能会使得程序在内存中为新结果分配内存。例如,用 Y = X + Y,将不再引用Y指向的张量,而是引用新分配内存处的张量。当进行大量数据训练时,需要占用大量内存,此时应尽量避免不必要地分配内存,因此,我们希望原地执行这些更新。
为了达到原地操作的目的,可以使用切片表示法将操作的结果分配给先前分配的数组,例如Y[:] =
Z = torch.zeros_like(Y)
print('id(Z):', id(Z))
Z[:] = X + Y
print('id(Z):', id(Z))
此时两次的Z的地址不变,说明为原地操作。如果后续计算中没有重复使用X,也可以使用X[:] = X + Y或者X += Y来减少内存操作的开销。
转换为其他Python对象Pytorch定义的张量可以与numpy定义的张量进行相互转化。
A = X.numpy() B = torch.tensor(A)自动微分
深度学习可以通过自动计算导数(自动微分)来加快求导。实际上,根据我们设计的模型,系统会构建一个计算图,来跟踪计算是哪些数据通过哪些操作组合起来产生输出。自动微分使系统能够对吼反向传播梯度。以 y = 2 X T X y = 2X^TX y=2XTX关于列向量求导为例
import torch x= torch.arrange(4.0) x.requires_grad_(True) #等效于x = torch.arange(4.0, requires_grad=True) print(x.grad) y = 2 * torch.dot(x, x) y.backward() #调用反向传播函数计算y关于x每个分量的梯度
使用自动微分的一个好处是即使计算图需要通过Python控制流,我们仍然可以计算得到的变量的梯度。
分离计算例如,z是y和x的函数,y是x的函数,如果想计算z关于x的梯度而保持y为常数,可以采用分离y的方法,分离y后,会返回一个新变量,该变量具有与y相同的值,但会丢弃计算图中关于计算y的信息,即梯度不会流经u到x,可以用于计算偏导数。一句话总结即计算偏导数时需要将不求导的变量调用detach函数进行分离。
x.grad.zero_() y = x * x u = y.detach() z = u * x z.sum().backward() x.grad == u
深度学习框架可以自动计算导数:我们首先将梯度附加到想要对其计算偏导数的变量上。然后我们记录目标值的计算,执行它的反向传播函数,并访问得到的梯度。深度学习中需要大量对数据进行操作,本文以Pytorch框架为例,对常用的数据操作进行总结。
基本操作深度学习中常使用张量(tensor)这一概念,张量最初起源于力学,用于表示弹性介质中各点应力状态,后来发展为力学和物理学的一个数学工具。它可以满足一切物理定于必须与坐标系的选择无关的特性,是矢量概念的推广。在理解时大致上可以直接将其对应为向量。零阶张量对应标量,一阶张量对应向量,二阶张量对应矩阵。
可以使用arrange创建行向量,张量默认存储在内存中,并采用基于CPU的计算。
x = torch.arrange(12)
可以采用shape来输出张量的形状。
x.shape
调用reshape函数可以改变张量的形状
x.reshape(3,4) #将张量变为3*4的矩阵 x.shape(3,-1) #将张量变为3*4的矩阵,-1表示宽度无需指定,会自动计算
可以用zeros,ones,randn来初始化创建张量,分别表示张量的元素为全0,全1,随机从标准正态分布中取值。也可以通过python列表来为张量元素赋值。
torch.ones(2,3,4) troch.zeros(2,3,4) torch.randn(2,3,4) torch.tensor([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])运算符
张量之间可以进行算术运算,对于任一具有相同形状的张量,可以使用标准算术运算符升级为按元素运算。
x = torch.tensor([1.0, 2, 4, 8]) y = torch.tensor([2, 2, 2, 2]) x + y, x - y, x * y, x / y, x ** y # **运算符是求幂运算
还可以将两个或多个张量连结(concatenate)起来个构成一个更大的张量
X = torch.arange(12, dtype=torch.float32).reshape((3,4)) Y = torch.tensor([[2.0, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]]) torch.cat((X, Y), dim=0), torch.cat((X, Y), dim=1)
此外,还可以对张量进行线性代数运算,包括向量点积和矩阵乘法。
点积
y = torch.ones(4, dtype = torch.float32) x, y, torch.dot(x, y) np.sum(x * y) #也可以通过按元素乘法然后求和的方式来求向量的点积
向量积与点积类似,区别在于(1)调用mv函数;(2)是矩阵乘向量,点积是向量乘向量。
torch.mv(A, x)
矩阵乘法调用mm函数
torch.mm(A, B)范数
向量范数是将向量映射到标量的函数f,可以简单理解为向量的大小。特殊地,欧氏距离为 L 2 L_2 L2范数。 L 1 L_1 L1范数表示向量各元素的绝对值之和
u = torch.tensor([3.0, -4.0]) torch.norm(u) #L2范数 torch.abs(u).sum() #L1范数
矩阵范数的计算同理。
广播机制上节介绍了相同形状的张量如何进行运算,本节介绍形状不同的张量的运算。可以通过广播机制(Broadcasting Mechanism)进行操作,首先通过复制元素使得两个张量具有相同的形状,其次对生成的数组执行按元素操作。
a = torch.arange(3).reshape((3, 1)) b = torch.arange(2).reshape((1, 2))索引与切片
类似于Python语言的特性,张量中的元素可以通过索引访问,也可以通过切片访问,此处略过
节省内存运行某些操作可能会使得程序在内存中为新结果分配内存。例如,用 Y = X + Y,将不再引用Y指向的张量,而是引用新分配内存处的张量。当进行大量数据训练时,需要占用大量内存,此时应尽量避免不必要地分配内存,因此,我们希望原地执行这些更新。
为了达到原地操作的目的,可以使用切片表示法将操作的结果分配给先前分配的数组,例如Y[:] =
Z = torch.zeros_like(Y)
print('id(Z):', id(Z))
Z[:] = X + Y
print('id(Z):', id(Z))
此时两次的Z的地址不变,说明为原地操作。如果后续计算中没有重复使用X,也可以使用X[:] = X + Y或者X += Y来减少内存操作的开销。
转换为其他Python对象Pytorch定义的张量可以与numpy定义的张量进行相互转化。
A = X.numpy() B = torch.tensor(A)自动微分
深度学习可以通过自动计算导数(自动微分)来加快求导。实际上,根据我们设计的模型,系统会构建一个计算图,来跟踪计算是哪些数据通过哪些操作组合起来产生输出。自动微分使系统能够对吼反向传播梯度。以 y = 2 X T X y = 2X^TX y=2XTX关于列向量求导为例
import torch x= torch.arrange(4.0) x.requires_grad_(True) #等效于x = torch.arange(4.0, requires_grad=True) print(x.grad) y = 2 * torch.dot(x, x) y.backward() #调用反向传播函数计算y关于x每个分量的梯度
使用自动微分的一个好处是即使计算图需要通过Python控制流,我们仍然可以计算得到的变量的梯度。
分离计算例如,z是y和x的函数,y是x的函数,如果想计算z关于x的梯度而保持y为常数,可以采用分离y的方法,分离y后,会返回一个新变量,该变量具有与y相同的值,但会丢弃计算图中关于计算y的信息,即梯度不会流经u到x,可以用于计算偏导数。一句话总结即计算偏导数时需要将不求导的变量调用detach函数进行分离。
x.grad.zero_() y = x * x u = y.detach() z = u * x z.sum().backward() x.grad == u
深度学习框架可以自动计算导数:我们首先将梯度附加到想要对其计算偏导数的变量上。然后我们记录目标值的计算,执行它的反向传播函数,并访问得到的梯度。



