[书籍原文]太大了传不上 ¶
预备知识、数学基础 ¶
深度学习能够解决的典型问题和tensor的基本运算 ¶
分类问题(如图片识别猫狗),回归预测问题(如预测房价),语音识别、语音生成问题,聚类问题(没有指定标签,让机器自动总结不同数据之间得模式来聚类)等。 深度学习存储和操作数据的主要接口是张量(n维数组)。它提供了各种功能,包括基本数学运算、广播、索引、切片、内存节省和转换其他Python对象,一些笔记如下
import torch
x = torch.arange(12)
x = x.reshape(3,4)
y = torch.ones(2,3,4)
y
tensor([[[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]],
[[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]]])
print(id(y))
2162619729384
y = x+y
print(id(y))#这时候发现y的id变化了,因此不能直接这么操作,可以直接更改y里的值,这样就不会占用新的内存
Z = torch.zeros_like(y)
print('id(Z):', id(Z))
Z[:] = x + y
print('id(Z):', id(Z))#这时候发现两次Z的内存地址没变,更节约内存
2162628254040
id(Z): 2162628254920
id(Z): 2162628254920
x = torch.zeros(2,3)
y = torch.ones(2,3)
x > y
tensor([[False, False, False],
[False, False, False]])
import os#对于数据预处理,我们先人工定义一个数据集
os.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')
import pandas as pd
data = 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
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
inputs = pd.get_dummies(inputs, dummy_na=True)#dummy_na就是来检测NAN的
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
X = torch.tensor(inputs.to_numpy(dtype=float))
y = torch.tensor(outputs.to_numpy(dtype=float))
X, y #这时候就转换为tensor数据类型了
(tensor([[3., 1., 0.],
[2., 0., 1.],
[4., 0., 1.],
[3., 0., 1.]], dtype=torch.float64),
tensor([127500., 106000., 178100., 140000.], dtype=torch.float64))
线性代数部分👇 ¶
向量:严格来说,仅包含一个数值被称为标量。向量可以被视为标量值组成的列表。这些标量值被称为向量的元素(element)或分量(component)。当向 量表示数据集中的样本时,它们的值具有一定的现实意义。在数学表示法中,向量通常记为粗体、小写的符号(例如,x、y和z)) 维度(dimension)这个词在不同上下文时往往会有不同的含义,这经常会使人感到困惑。为了清楚起见,我们在此明确一下:向量或轴的维度被用来表示向量或轴的长度,即向量或轴的元素数量。然而,张量的维度用来表示张量具有的轴数。在这个意义上,张量的某个轴的维数就是这个轴的长度。
矩阵:正如向量将标量从零阶推广到一阶,矩阵将向量从一阶推广到二阶。矩阵,我们通常用粗体、大写字母来表示(例如,X、Y和Z),在代码中表示为具有两个轴的张量。数学表示法使用A ∈ Rm×n 来表示矩阵A,其由m行和n列的实值标量组成。我们可以将任意矩阵A ∈ Rm×n视为一个表格,其中每个元素aij属于第i行第j列
A = torch.arange(20).reshape(5, 4)
A
tensor([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15],
[16, 17, 18, 19]])
A.T #当我们交换矩阵的行和列时,结果称为矩阵的转置(transpose)。通常用a⊤来表示矩阵的转置,如果B = A⊤,则对于任意i和j,都有bij = aji。
tensor([[ 0, 4, 8, 12, 16],
[ 1, 5, 9, 13, 17],
[ 2, 6, 10, 14, 18],
[ 3, 7, 11, 15, 19]])
张量:就像向量是标量的推广,矩阵是向量的推广一样,我们可以构建具有更多轴的数据结构。张量(本小节中的“张量”指代数对象)是描述具有任意数量轴的n维数组的通用方法。例如,向量是一阶张量,矩阵是二阶张量。张量用特殊字体的大写字母表示(例如,X、Y和Z),它们的索引机制(例如xijk和[X]1,2i−1,3)与矩阵类似。
A = torch.arange(20, dtype=torch.float32).reshape(2,5, 2)
B = A.clone() # 通过分配新内存,将A的一个副本分配给B
A, A + B#可以看出张量的运算是这样的
(tensor([[[ 0., 1.],
[ 2., 3.],
[ 4., 5.],
[ 6., 7.],
[ 8., 9.]],
[[10., 11.],
[12., 13.],
[14., 15.],
[16., 17.],
[18., 19.]]]),
tensor([[[ 0., 2.],
[ 4., 6.],
[ 8., 10.],
[12., 14.],
[16., 18.]],
[[20., 22.],
[24., 26.],
[28., 30.],
[32., 34.],
[36., 38.]]]))
具体而言,两个矩阵的按元素乘法称为Hadamard积(Hadamard product)(数学符号⊙)。
A*B
tensor([[[ 0., 1.],
[ 4., 9.],
[ 16., 25.],
[ 36., 49.],
[ 64., 81.]],
[[100., 121.],
[144., 169.],
[196., 225.],
[256., 289.],
[324., 361.]]])
默认情况下,调用求和函数会沿所有的轴降低张量的维度,使它变为一个标量。我们还可以指定张量沿哪一个轴来通过求和降低维度。以矩阵为例,为了通过求和所有行的元素来降维(轴0),可以在调用函数时指定axis=0。由于输入矩阵沿0轴降维以生成输出向量,因此输入轴0的维数在输出形状中消失。
A_sum_axis0 = A.sum(axis=0)#在这里,0轴是channel,因此就是0+10、1+11这样
A_sum_axis0, A_sum_axis0.shape
(tensor([[10., 12.],
[14., 16.],
[18., 20.],
[22., 24.],
[26., 28.]]),
torch.Size([5, 2]))
A_sum_axis1 = A.sum(axis=1)
A_sum_axis1, A_sum_axis1.shape
(tensor([[20., 25.],
[70., 75.]]),
torch.Size([2, 2]))
沿着行和列对矩阵求和,等价于对矩阵的所有元素进行求和。
A.sum(axis=[0, 1])
tensor([ 90., 100.])
A.mean(axis=0), A.sum(axis=0) / A.shape[0]
(tensor([[ 5., 6.],
[ 7., 8.],
[ 9., 10.],
[11., 12.],
[13., 14.]]),
tensor([[ 5., 6.],
[ 7., 8.],
[ 9., 10.],
[11., 12.],
[13., 14.]]))