深度学习中的线性代数知识
引学
标量(scalar)是一种只有大小,没有方向的量。它可以用一个具体的数值来表示。例如,重量、温度、长度、时间、热量等都是标量。标量与向量、矩阵、张量的关系可以用点线面体的概念来比喻解释:点——标量(scalar),线——向量(vector),面——矩阵(matrix),体——张量(tensor)
标量
import torch
x = torch.tensor(3.0)
y = torch.tensor(2.0)
x+y, x*y, x/y, x**y
向量
x = torch.arange(4)
x
大部分文献认为列向量是向量的默认方向
长度、维度和形状
len(x)
输出4
当用张量表示一个向量(只有一个轴)时,我们也可以通过.shape属性访问向量的长度。形状(shape)是一个元素组,列出了张量沿每个轴的长度(维数)。对于只有一个轴的张量,形状只有一个元素
x.shape
输出torch.Size([4])
注意:向量或轴的维度被用来表示向量或轴的长度,即向量或轴的元素数量。然而,张量的维度用来表示张量具有的轴数。后面有句话会混淆,先在这里死记,就是张量的维度就是轴的数量。
比如下面例题,维度就是3
import torch
x = torch.arange(12).reshape((2, 3, 2))
x
x.shape
而张量某个轴的维数就是这个轴的长度
矩阵
A = torch.arange(20).reshape(5,4)
A
转置跟线代的一样
方阵特殊性质
B = torch.tensor([[1, 2, 3], [2, 0, 4], [3, 4, 5]])
B
B == B.T
张量
张量(Tensor)是一种表示物理量的方式,这个方式就是用基向量与分量组合表示物理量它是一个多维数组,可以看作是标量、向量、矩阵的高维拓展。张量是基于标量和矢量向更高维度的推广,它通过将一系列具有某种共同特征的数进行有序的组合来表示一个更加广义的“数“。
先简单知道一些定义就行,后面到图像处理卷积会再详细介绍。
X = torch.arange(24).reshape(2, 3, 4)
X
张量算法的基本性质
A = torch.arange(20, dtype=torch.float32).reshape(5, 4)
B = A.clone() # 通过分配新内存,将A的一个副本分配给B
A, A+B
关于乘法 我有话说
- np.multiply():无论数组还是矩阵都是元素积(元素对应位置相乘),输出与相乘数组/矩阵大小一致
np.multiply(A, B)
2.np.dot():对于秩为1的数组,执行对应位置相乘再相加;对于秩不唯一的二维数组,执行内积。
np.dot(A,B)
由于两个都是5行四列矩阵 没办法执行矩阵相乘的 所以得改变其中一个的维数
B = B.reshape(4,5)
np.dot(A,B)
**注意:**这里还有一个就是dot函数在实现矩阵乘向量的时候,是将矩阵分成几个向量 分别内积
C = torch.arange(4)
A, C
np.dot(A,C)
还有一个*,不提倡使用,因为用着用着你就迷了
*对数组执行对应位置相乘;对矩阵执行矩阵乘法运算
A*B
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
这个时候你就奇怪了,A不是个矩阵吗?怎么乘起来是对应位置相乘? 真不是! 你可以看看他是tensor,我不知道我有没说错,如果你没有将其转换成mat,你怎么乘都是对应位置相乘。
A = np.mat(A)
B = np.mat(B)
A*B
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
*所以最好不要用。**还有一个@就是矩阵乘法咯!
将张量乘以或加上一个标量不会改变张量的形状,其中张量的每个元素都将与标量相加或相乘。
a = 2
X = torch.arange(24).reshape(2, 3, 4)
a + X, (a + X).shape
降维
x = torch.arange(4, dtype=torch.float32)
x, x.sum()
讲到sum就不得不讲一下,pytorch的backward()方法
Pytorch中的.backward()方法 - 知乎 (zhihu.com)具体可以看看这个链接
小小解释一下
就是我们backward()的工作原理:
在前向传播中,所有对张量的操作都会被追踪并记录在一个有向无环图中。每个节点代表一个操作,每个边代表张量,比如下面这个图
接下来就是调用backward()计算梯度,pytorch会从计算图的末端开始,利用链式求导法则进行梯度计算。(基于反向传播的起点是标量)
然后就算出的梯度会存储在各自张量的.grad属性中。需要注意的是,backward()只会计算并累积那些设置了requires_grad = True的叶子节点张量的梯度。
刚刚讲的是起点是标量的情况,那如果起点不是标量呢,而是向量或者是高维的张量
我们就需要给backward()传入一个和起点形状相同的张量作为参数。这个参数被称为“外部梯度”,用于与雅可比矩阵相乘得到最终的梯度。
一般你会看到求梯度,会是什么l.sum.backward() :意思相通,就是要把l这个向量变成标量在进行梯度下降。
你去细看上面知乎里的链接,你也会发现其实就是,当输出张量为标量是,v_vector大小为1,得到完美的雅可比矩阵J@v.T=J,在不是标量的情况下,加个.sum操作也是为了这样。
其实这就是沐神为什么安排.sum在这降维这里了,.sum也能做到降维的效果。
按照行累加
A = torch.arange(20, dtype=torch.float32).reshape(5, 4)
A_sum_axis0 = A.sum(axis=0)
A_sum_axis0 , A_sum_axis0.shape
按照列累加
指定axis=1将通过汇总所有列的元素降维(轴1)。因此,输入轴1的维数在输出形状中消失。
A_sum_axis1 = A.sum(axis=1)
A_sum_axis1 = A_sum_axis1.shape
A.sum(axis=[0,1]) # 结果和A.sum()相同
计算平均值
A.mean(), A.sum()/A.numel()
沿指定轴降低张量的维度
A.mean(axis=0), A.sum(axis=0)/A.shape[0]
非降维求和
keepdim = True 保持轴不变
sum_A = A.sum(axis=1, keepdims=True)
sum_A
由于sum_A在对每行进行求和后仍保持两个轴,我们可以通过广播将A除以sum_A
A/sum_A
如果我们想沿某个轴计算A元素的累积总和,比如axis=0(按行计算),可以调用cumsum函数。此函数不会沿任何轴降低输入张量的维度。
A.cumsum(axis=0)
dot函数上一篇有讲过,这里就跟着教材过一遍
点积
x = torch.arange(4, dtype=torch.float32)
y = torch.ones(4, dtype=torch.float32)
x, y, torch.dot(x, y)
矩阵-向量积
A.shape, x.shape, torch.mv(A, x)
矩阵-矩阵乘法
B = torch.ones(4, 3)
torch.mm(A, B)
范数
线性代数中最有用的一些运算符是范数(norm)。沐神说的,不是我说的,狗头保命。非正式说,向量的范数是表示一个向量有多大。这里考虑的大小概念不涉及维度,而是分量的大小。(下面全是沐神书里的,看一看吧)
u = torch.tensor([3.0, -4.0])
torch.norm(u)
torch.abs(u).sum()
torch.norm(torch.ones((4, 9)))
最后这个范数,先知道是怎么一回事就行了,后面需要用到时,就会知道它的用处了。