深度学习的核心是卷积,卷积的核心是张量(Tensor)
理解 Tensor
Tensor 可以简单理解为是标量、向量、矩阵的高维扩展。你可以把张量看作多维数组,但相较于ndarray,Tensor 包含了grad、requires_grad、grad_fn、device 等属性,是为服务于神经网络而设计的类型,
标量可以看作是零维张量、向量可以看作是一维张量、矩阵可以看作是二维张量。
若把二维张量看作一个平面,三维张量就是多个二维张量平面两两平行摆放。灰度图像是典型的二维张量,RGB图像是典型的三维张量(channel, height, width)。
那怎么理解四维张量、五维张量等高维张量?
在之后的深度学习过程中,我们处理图像时会经常遇到四维张量(batch_size, channel, height, width),表示有 batch_size 个 RGB 图像。更高维的张量无非是在前面添加 batch, 如五维张量(batch', batch, c, h, w)。batch 是高维张量的单位。下面通过简图理解一下高维张量:
将三维张量看成零维张量,那四维张量不就是一维张量,五维张量不就是二维张量了吗!张量的升维其实也是在降维!!!
那么六维张量的简图怎么画,相信大家参照三维张量也不难画出了。
Tensor 的创建方式
直接创建
torch.tensor()
torch.tensor(data, dtype=None, device=None, requires_grad=False)
- data: 数据,像数组的类型都可以,如list、tuple、numpy.ndarray等
- dtype: 数据类型,默认与data的数据类型一致
- device: 所在设备,cuda或cpu
- requires_grad: 是否需要梯度
代码示例:
t = torch.tensor(np.ones((2, 3)), device='cuda')
torch.from_numpy(ndarray)
torch.from_numpy(ndarray)
从 numpy.ndarray 创建 Tensor,两者共享内存。
代码示例:
a = np.array([1, 2, 3])
t = torch.from_numpy(a)
print(id(a)==id(t)) # 同一内存
# 修改其中一个的数据,另一个也会被改动
a[0] = 11
t[1] = 22
print(a, t)
根据数值创建 Tensor
torch.zeros()
创建全0张量
torch.zeros(*size, *, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)
- size(int...): 张量的形状
- out(Tensor): 输出张量,将新建的张量写入 out 张量中
- layout: 内存中布局形式,有strided、sparse_coo 等。当是稀疏矩阵时,设置为 sparse_coo 可以减少内存占用。
t_z = torch.zeros(2, 3)
torch.zeros_like()
torch.zeros_like(input, *, dtype=None, layout=None, device=None, requires_grad=False, memory_format=torch.preserve_format)
根据 input 的形状创建全0张量。
input = torch.empty(2, 3)
t_z_l = torch.zeros_like(input)
全1张量(torch.ones()、torch.ones_like())和自定义数值张量(torch.full()、torch.full_like())的创建方式类似全0张量的创建。
input = torch.empty(2, 3)
# 创建全1张量
t_o = torch.ones(2, 3)
t_o_l = torch.ones_like(input)
# 创建自定义值张量
t_f = torch.full((2, 3), 8)
t_f_l = torch.full(input, 8)
torch.arange()
创建等差的一维张量
torch.arange(start=0, end, step=1, *, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)
- start: 起始值
- end: 结束值,右开区间[start, end),取不到end
- step: 步长,也即公差
t_a = torch.arange(2, 10, 2)
创建均分的一维张量————torch.linspace()
创建对数均分的一维张量————torch.logspace()
torch.eye()
torch.eye(n, m=None, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)
创建单位对角矩阵(二维张量)
t_e = torch.eye(3)
根据概率创建 Tensor
torch.normal()
返回一个从均值和标准差已给定的独立正态分布中抽取的随机数张量。
torch.normal(mean, std, *, generator=None, out=None)
- mean:均值。当为 Tensor 类型时其每一个元素分别作为每个输出随机数对应的正态分布的均值,当为 float 类型时作为全部输出随机数对应的正态分布的均值。
- std: 标准差。类型同理。
根据 mean 和 std 的类型有四种情形:
# 一. mean和std均为Tensor类型
torch.normal(mean=torch.arange(1., 11.), std=torch.arange(1, 0, -0.1))
'''
tensor([ 0.4438, 2.4659, 2.8171, 4.5146, 4.6359, 5.2487, 7.4859, 7.9697,
9.2102, 10.0163])
0.4438从分布N(1,1)采样得到,2.4659从分布N(2,0.9)采样得到,其他随机值以此类推。
'''
# 二. mean:float, std: Tensor
torch.normal(mean=0.5, std=torch.arange(1., 6.))
'''
tensor([ 0.9225, -4.9200, -0.8951, 5.7765, -2.3907])
五个随机值采样分布的均值相同,为0.5,标准差不同。
'''
# 三. mean: Tensor, std: float
torch.normal(mean=torch.arange(1., 6.), std=0.5)
'''
tensor([0.7839, 2.0818, 2.2737, 3.9720, 4.9761])
五个随机值采样分布的均值不同,标准差均为0.5
'''
# 四. mean: float, std: float。此时需要设置size表明返回的Tensor形状。
torch.normal(2, 3, size=(1, 4))
'''
tensor([[ 4.2537, -0.9927, -1.4713, 1.4987]])
随机值都是从N(2,3)采样得到
'''
torch.randn()
torch.randn(*size, *, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False, pin_memory=False)
返回一个随机数填充的张量(随机数从N(0,1)取样),张量形状由size决定。
torch.randn(4)
torch.randn(2, 3)
torch.rand()
torch.rand(*size, *, generator=None, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False, pin_memory=False)
返回一个由区间[0,1)上的均匀分布的随机数填充的张量。
torch.rand(4)
torch.rand(2, 3)
还有 torch.rand_like()、torch.randint()、torch.randperm()、torch.bernoulli()等。
张量操作
张量的操作和ndarray的操作有很多共通之处,就不多赘述了,想了解如何具体使用可以看官方文档,或者在python控制台用 help函数了解。
- 拼接操作——torch.cat()、torch.stack()
- 切分操作——torch.chunk()、torch.split()
- 索引操作——torch.index_select()、torch.mask_select()
- 变换操作——torch.reshape()、torch.transpose()、torch.t()、torch.squeeze()、torch.unsqueeze()
张量的数学运算
张量的数学运算和 ndarray 的数学运算类似。由于在深度学习中经常使用先乘后加的操作,要关注以下三个运算:
- torch.add()
- torch.addcdiv()
- torch.addcmul()
torch.add()
troch.add(input, other, *, alpha=1, out=None)
计算公式: out = input + alpha * other
torch.addcdiv()
torch.addcdiv(input, tensor1, tensor2, *, value=1, out=None)
计算公式:out = input + value * (tensor1 / tensor2)
torch.addcmul()
torch.addcmul(input, tensor1, tensor2, *, value=1, out=None)
计算公式:out = input + value * tensor1 * tensor2