预备知识总结
2.1 数据操作
为了完成各种数据操作,我们需要方法来存储和操作数据。通常需要做两件重要的事:(1)获取数据;(2)将数据读入计算机后对其进行处理。
首先,我们介绍n维数组,也称为张量(tensor)。深度学习框架的张量类(MXNet中为ndarray,PyTorch和TensorFlow中为Tensor)与NumPy的ndarray类似,但有两个重要优势:
- GPU支持加速计算
- 支持自动微分
2.1.1 入门
首先,我们从MXNet导入必要模块:
from mxnet import np, npx
npx.set_np()
张量表示一个由数值组成的数组,可能有多个维度:
- 一个轴的张量对应数学上的向量(vector)
- 两个轴的张量对应数学上的矩阵(matrix)
- 两个轴以上的张量没有特殊的数学名称
创建张量
使用arange创建行向量:
x = np.arange(12)
x
array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11.])
通过张量的shape属性可以访问张量的形状:
x.shape
(12,)
要知道张量中元素的总数,可以检查它的大小(size):
x.size
12
改变形状
使用reshape函数可以改变张量的形状,同时保持元素数量和值不变:
X = x.reshape(3, 4)
X
array([[ 0., 1., 2., 3.],[ 4., 5., 6., 7.],[ 8., 9., 10., 11.]])
我们不需要手动指定每个维度,可以使用-1让系统自动计算某个维度的大小:x.reshape(-1, 4)或x.reshape(3, -1)都可以达到相同效果。
创建特殊张量
创建特定形状的全零张量:
np.zeros((2, 3, 4))
创建全一张量:
np.ones((2, 3, 4))
从特定概率分布随机采样创建张量:
np.random.normal(0, 1, size=(3, 4))
直接从Python列表创建张量:
np.array([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
2.1.2 运算符
深度学习中的一个关键操作是按元素运算(elementwise operations),它将标量运算应用到数组的每个元素。
基本数学运算
x = np.array([1, 2, 4, 8])
y = np.array([2, 2, 2, 2])
x + y, x - y, x * y, x / y, x ** y
(array([ 3., 4., 6., 10.]),array([-1., 0., 2., 6.]),array([ 2., 4., 8., 16.]),array([0.5, 1. , 2. , 4. ]),array([ 1., 4., 16., 64.]))
其他按元素运算示例:
np.exp(x)
array([2.7182817e+00, 7.3890562e+00, 5.4598148e+01, 2.9809580e+03])
张量连结
可以将多个张量连结(concatenate)在一起:
X = np.arange(12).reshape(3, 4)
Y = np.array([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
np.concatenate([X, Y], axis=0), np.concatenate([X, Y], axis=1)
逻辑运算
使用逻辑运算符创建布尔张量:
X == Y
array([[False, True, False, True],[False, False, False, False],[False, False, False, False]])
求和运算:
X.sum()
array(66.)
2.1.3 广播机制
**广播机制(broadcasting mechanism)**允许在形状不同的张量间执行按元素操作:
- 通过复制元素扩展数组,使两个张量具有相同形状
- 对生成的数组执行按元素操作
示例:
a = np.arange(3).reshape(3, 1)
b = np.arange(2).reshape(1, 2)
a + b
array([[0., 1.],[1., 2.],[2., 3.]])
2.1.4 索引和切片
张量支持与Python数组类似的索引和切片操作:
X[-1], X[1:3] # 选择最后一行,选择第2和第3行
写入操作:
X[1, 2] = 9 # 修改单个元素
X[0:2, :] = 12 # 修改多个元素
2.1.5 节省内存
某些操作会创建新的内存来存储结果:
before = id(Y)
Y = Y + X
id(Y) == before # False,Y现在指向新的内存位置
原地操作可以节省内存:
Z = np.zeros_like(Y)
Z[:] = X + Y # 原地操作
或者:
X += Y # 等价于 X[:] = X + Y
2.1.6 转换为其他Python对象
深度学习框架的张量与NumPy数组的转换:
A = X.asnumpy() # 转为NumPy ndarray
B = np.array(A) # 转回框架张量
将大小为1的张量转换为Python标量:
a = np.array([3.5])
a.item(), float(a), int(a) # 3.5, 3.5, 3
2.2 数据预处理
为了用深度学习解决现实问题,我们通常需要从原始数据开始,而不是从准备好的张量开始。Python中常用pandas进行数据预处理。
2.2.1 读取数据集
创建一个简单的CSV数据集:
import osos.makedirs(os.path.join('..', 'data'), exist_ok=True)
data_file = os.path.join('..', 'data', 'house_tiny.csv')
with open(data_file, 'w') as f:f.write('NumRooms,Alley,Price\n')f.write('NA,Pave,127500\n')f.write('2,NA,106000\n')f.write('4,NA,178100\n')f.write('NA,NA,140000\n')
使用pandas读取CSV文件:
import pandas as pddata = pd.read_csv(data_file)
print(data)
NumRooms Alley Price
0 NaN Pave 127500
1 2.0 NaN 106000
2 4.0 NaN 178100
3 NaN NaN 140000
2.2.2 处理缺失值
处理缺失数据的常用方法包括插值法和删除法。这里使用插值法:
inputs, outputs = data.iloc[:, 0:2], data.iloc[:, 2]
inputs = inputs.fillna(inputs.mean())
print(inputs)
NumRooms Alley
0 3.0 Pave
1 2.0 NaN
2 4.0 NaN
3 3.0 NaN
对于类别值,我们使用独热编码(one-hot encoding)处理:
inputs = pd.get_dummies(inputs, dummy_na=True)
print(inputs)
NumRooms Alley_Pave Alley_nan
0 3.0 1 0
1 2.0 0 1
2 4.0 0 1
3 3.0 0 1
2.2.3 转换为张量格式
将处理好的数据转换为张量格式:
X, y = np.array(inputs.to_numpy(dtype=float)), np.array(outputs.to_numpy(dtype=float))
X, y
2.3 线性代数
线性代数是理解和实现深度学习模型的基础。
2.3.1 标量
标量(scalar)是只包含一个数值的量。例如,温度转换公式 f=1.8c+32f = 1.8c + 32f=1.8c+32 中的每一项都是标量。
在代码中,标量用只有一个元素的张量表示:
x = np.array(3.0)
y = np.array(2.0)x + y, x * y, x / y, x ** y
(array(5.), array(6.), array(1.5), array(9.))
2.3.2 向量
向量(vector)是标量值组成的列表。向量的元素通常与现实世界的某些属性相对应。
通过一维张量表示向量:
x = np.arange(4)
x
array([0., 1., 2., 3.])
可以通过索引访问向量元素:
x[3]
array(3.)
长度、维度和形状
向量的长度通常称为向量的维度(dimension):
len(x) # 4
x.shape # (4,)
2.3.3 矩阵
矩阵(matrix)是二维数组,用二维张量表示:
A = np.arange(20).reshape(5, 4)
A
array([[ 0., 1., 2., 3.],[ 4., 5., 6., 7.],[ 8., 9., 10., 11.],[12., 13., 14., 15.],[16., 17., 18., 19.]])
矩阵的转置(transpose)是行和列交换的结果:
A.T
array([[ 0., 4., 8., 12., 16.],[ 1., 5., 9., 13., 17.],[ 2., 6., 10., 14., 18.],[ 3., 7., 11., 15., 19.]])
对称矩阵(symmetric matrix)等于其转置:
B = np.array([[1, 2, 3], [2, 0, 4], [3, 4, 5]])
B == B.T
array([[ True, True, True],[ True, True, True],[ True, True, True]])
2.3.4 张量
张量(tensor)是描述具有任意数量轴的n维数组的通用方法:
X = np.arange(24).reshape(2, 3, 4)
X
array([[[ 0., 1., 2., 3.],[ 4., 5., 6., 7.],[ 8., 9., 10., 11.]],[[12., 13., 14., 15.],[16., 17., 18., 19.],[20., 21., 22., 23.]]])
2.3.5 张量算法的基本性质
张量上的按元素操作保持形状不变:
A = np.arange(20).reshape(5, 4)
B = A.copy()
A + B
两个矩阵的按元素乘法称为Hadamard积(Hadamard product):
A * B
2.3.6 降维
求和运算可以降低张量的维度:
x = np.arange(4)
x.sum() # 6.0
可以指定沿特定轴降维:
A.sum(axis=0) # 沿轴0(行)求和,得到列的总和
A.sum(axis=1) # 沿轴1(列)求和,得到行的总和
求平均值:
A.mean(), A.sum() / A.size
(array(9.5), array(9.5))
非降维求和
使用keepdims=True参数保持轴数不变:
sum_A = A.sum(axis=1, keepdims=True)
A / sum_A # 可以进行广播
累积总和:
A.cumsum(axis=0) # 沿轴0计算累积和
2.3.7 点积
两个向量的点积(dot product)是相同位置元素乘积的和:
y = np.ones(4)
np.dot(x, y) # 等价于np.sum(x * y)
array(6.)
点积在深度学习中有多种应用,如加权平均和计算向量间的余弦相似度。
2.3.8 矩阵-向量积
矩阵-向量积是矩阵的行向量与向量的点积:
np.dot(A, x)
array([ 14., 38., 62., 86., 110.])
2.3.9 矩阵-矩阵乘法
矩阵乘法可以看作是多次矩阵-向量积的组合:
B = np.ones(shape=(4, 3))
np.dot(A, B)
array([[ 6., 6., 6.],[22., 22., 22.],[38., 38., 38.],[54., 54., 54.],[70., 70., 70.]])
2.3.10 范数
范数(norm)是表示向量大小的函数。常用的范数包括:
L2范数
L2范数(欧几里得范数)是向量元素平方和的平方根:
u = np.array([3, -4])
np.linalg.norm(u) # 5.0
L1范数
L1范数是向量元素绝对值之和:
np.abs(u).sum() # 7.0
Frobenius范数
矩阵的Frobenius范数是矩阵元素平方和的平方根:
np.linalg.norm(np.ones((4, 9)))
范数和目标
在深度学习中,范数常用于表达优化目标,如:
- 最大化分配给观测数据的概率
- 最小化预测和真实观测之间的距离
- 最小化相似项目间的距离,最大化不同项目间的距离
