1.标量(0D 张量)
仅包含一个数字的张量叫作
标量
(
scalar
,也叫标量张量、零维张量、
0D
张量)。在
Numpy
中,一个
float32
或
float64
的数字就是一个标量张量(或标量数组)。
2.向量
数字组成的数组叫作
向量
(
vector
)或一维张量(
1D
张量)。一维张量只有一个轴。下面是
一个
Numpy
向量。
x=np.array([12,3,6,14,7])
3.矩阵(2D 张量)
向量组成的数组叫作
矩阵
(
matrix
)或二维张量(
2D
张量)。矩阵有
2
个轴(通常叫作
行
和
列
)。你可以将矩阵直观地理解为数字组成的矩形网格。下面是一个
Numpy
矩阵。
x = np.array([[5, 78, 2, 34, 0],
[6, 79, 3, 35, 1],
[7, 80, 4, 36, 2]])
4.3D 张量与更高维张量
将多个矩阵组合成一个新的数组,可以得到一个
3D
张量,你可以将其直观地理解为数字
组成的立方体。下面是一个
Numpy
的
3D
张量
x = np.array([[[5, 78, 2, 34, 0],
[6, 79, 3, 35, 1],
[7, 80, 4, 36, 2]],
[[5, 78, 2, 34, 0],
[6, 79, 3, 35, 1],
[7, 80, 4, 36, 2]],
[[5, 78, 2, 34, 0],
[6, 79, 3, 35, 1],
[7, 80, 4, 36, 2]]])
将多个
3D
张量组合成一个数组,可以创建一个
4D
张量,以此类推。深度学习处理的一般
是
0D
到
4D
的张量,但处理视频数据时可能会遇到
5D
张量。
5.关键属性
张量是由以下三个关键属性来定义的。
轴的个数(阶):
例如,
3D
张量有
3
个轴,矩阵有
2
个轴。这在
Numpy
等
Python
库中
也叫张量的
ndim
。
形状:
这是一个整数元组,表示张量沿每个轴的维度大小(元素个数)。例如,前面矩
阵示例的形状为
(3, 5)
,
3D
张量示例的形状为
(3, 3, 5)
。向量的形状只包含一个
元素,比如
(5,)
,而标量的形状为空,即
()
。
数据类型:
(在
Python
库中通常叫作
dtype
)。这是张量中所包含数据的类型,例如,张
量的类型可以是
float32
、
uint8
、
float64
等。在极少数情况下,你可能会遇到字符
(
char
)张量。注意,
Numpy
(以及大多数其他库)中不存在字符串张量,因为张量存
储在预先分配的连续内存段中,而字符串的长度是可变的,无法用这种方式存储。
6.张量运算
6.1逐元素运算
relu
运算和加法都是
逐元素
(
element-wise
)的运算,即该运算独立地应用于张量中的每
个元素,也就是说,这些运算非常适合大规模并行实现。下列代码是对逐元素
relu
运算的简单实现。
def naive_relu(x):
assert len(x.shape) == 2
x = x.copy()
for i in range(x.shape[0]):
for j in range(x.shape[1]):
x[i, j] = max(x[i, j], 0)
return x
对于加法采用同样的实现方法。
def naive_add(x, y):
assert len(x.shape) == 2
assert x.shape == y.shape
x = x.copy()
for i in range(x.shape[0]):
for j in range(x.shape[1]):
x[i, j] += y[i, j]
return x
6.2广播
广播包含 以下两步。
(1)
向较小的张量添加轴(叫作
广播轴
),使其
ndim
与较大的张量相同。
(2)
将较小的张量沿着新轴重复,使其形状与较大的张量相同。
来看一个具体的例子。假设
X
的形状是
(32, 10)
,
y
的形状是
(10,)
。首先,我们给
y
添加空的第一个轴,这样
y
的形状变为
(1, 10)
。然后,我们将
y
沿着新轴重复
32
次,这样
得到的张量
Y
的形状为
(32, 10)
,并且
Y[i, :] == y for i in range(0, 32)
。现在,
我们可以将
X
和
Y
相加,因为它们的形状相同。
def naive_add_matrix_and_vector(x, y):
assert len(x.shape) == 2
assert len(y.shape) == 1
assert x.shape[1] == y.shape[0]
x = x.copy()
for i in range(x.shape[0]):
for j in range(x.shape[1]):
x[i, j] += y[j]
return x
6.3张量点积
点积运算,也叫
张量积
(
tensor product
,不要与逐元素的乘积弄混),是最常见也最有用的
张量运算。与逐元素的运算不同,它将输入张量的元素合并在一起。
从数学的角度来看,点积运算做了什么?我们首先看一下两个向量
x
和
y
的点积。其计算
过程如下:
def naive_vector_dot(x, y):
assert len(x.shape) == 1
assert len(y.shape) == 1
assert x.shape[0] == y.shape[0]
z = 0
for i in range(x.shape[0]):
z += x[i] * y[i]
return z
注意,两个向量之间的点积是一个标量,而且只有元素个数相同的向量之间才能做点积。
6.4张量变形
张量变形是指改变张量的行和列,以得到想要的形状。变形后的张量的元素总个数与初始
张量相同。简单的例子可以帮助我们理解张量变形。
import numpy as np
x = np.array([[ 0,1],
[ 2,3],
[ 4,5]])
print(x.shape)
x = x.reshape((6, 1))
print(x)
6.5张量运算的导数(梯度)
梯度
(
gradient
)是张量运算的导数。它是导数这一概念向多元函数导数的推广。多元函数
是以张量作为输入的函数。
随机梯度下降
给定一个可微函数,理论上可以用解析法找到它的最小值:函数的最小值是导数为 0 的点, 因此你只需找到所有导数为 0 的点,然后计算函数在其中哪个点具有最小值。
将这一方法应用于神经网络,就是用解析法求出最小损失函数对应的所有权重值。
由于处理的是一个可微函数,你可以计算出它的梯度,从而有效地实
现第四步。沿着梯度的反方向更新权重,损失每次都会变小一点。
(1)
抽取训练样本
x
和对应目标
y
组成的数据批量。
(2)
在
x
上运行网络,得到预测值
y_pred
。
(3)
计算网络在这批数据上的损失,用于衡量
y_pred
和
y
之间的距离。
(4)
计算损失相对于网络参数的梯度[一次
反向传播
(
backward pass
)]。
(5)
将参数沿着梯度的反方向移动一点,比如
W -= step * gradient
,从而使这批数据
上的损失减小一点。
刚刚描述的方法叫作
小批量随机梯度下降
(
mini-batch stochastic gradient descent
,
又称为小批量
SGD
)。
6.6链式求导:反向传播算法
在前面的算法中,我们假设函数是可微的,因此可以明确计算其导数。在实践中,神经网
络函数包含许多连接在一起的张量运算,每个运算都有简单的、已知的导数。例如,下面这个
网络
f
包含
3
个张量运算
a
、
b
和
c
,还有
3
个权重矩阵
W1
、
W2
和
W3
。
f(W1, W2, W3) = a(W1, b(W2, c(W3)))
根据微积分的知识,这种函数链可以利用下面这个恒等式进行求导,它称为
链式法则
(
chain
rule
):
(f(g(x)))' = f'(g(x)) * g'(x)
。将链式法则应用于神经网络梯度值的计算,得
到的算法叫作
反向传播
(
backpropagation
,有时也叫
反式微分
,
reverse-mode differentiation
)。反
向传播从最终损失值开始,从最顶层反向作用至最底层,利用链式法则计算每个参数对损失值
的贡献大小。