深度学习-02-预备知识

221 阅读9分钟

2.1 数据操作

首先介绍一下n维数组,也称为 张量 (tensor)

跟Numpy中的ndarray类似,但又多了一些重要的功能:

  • tensor支持GPU加速计算,而NumPy仅支持CPU计算
  • tensor支持自动微分

因此,张量类更适合深度学习。

2.1.1 入门

这里先介绍一下基础的使用。如shape、reshape、numel各自所代表的含义

import torch

# 使用 arange 创建一个行向量 x
x = torch.arange(12)
print(x)  # tensor([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])
# shape属性: 访问张量的形状(沿每个轴的长度)
print(x.shape)  # torch.Size([12])

# reshape: 改变形状,不改变元素个数和元素值
X = x.reshape(3, 4)
print(X)
# tensor([[ 0,  1,  2,  3],
#         [ 4,  5,  6,  7],
#         [ 8,  9, 10, 11]])
print(X.shape)  # torch.Size([3, 4])

# 查看张量中的元素个数
print(X.numel())

上述reshape中行和列都指定了,其实也可只指定行,或只指定列,剩余一维自动推算。

x.reshape(3, -1)

同时,也可以生成:全0、全1、或者随机化的tensor

  • torch.zeros(2, 3, 4)
  • torch.ones(2, 3, 4)
  • torch.randn(2, 3, 4)

2.1.2 运算符

这里先介绍 按元素(elementwise)运算。

x = torch.tensor([1.0, 2, 4, 8])
y = torch.tensor([2, 2, 2, 2])
x + y, x - y, x * y, x / y, x ** y  # **运算符是求幂运算
(tensor([ 3.,  4.,  6., 10.]),
 tensor([-1.,  0.,  2.,  6.]),
 tensor([ 2.,  4.,  8., 16.]),
 tensor([0.5000, 1.0000, 2.0000, 4.0000]),
 tensor([ 1.,  4., 16., 64.]))

除了按元素计算外,我们还可以执行线性代数运算,包括向量点积和矩阵乘法。这个后续会介绍到。

我们也可以把多个张量连结(concatenate)在一起, 把它们端对端地叠起来形成一个更大的张量。

我们只需要提供张量列表,并给出沿哪个轴连结。

下面的例子分别演示了当我们沿行(轴-0,形状的第一个元素) 和按列(轴-1,形状的第二个元素)连结两个矩阵时,会发生什么情况。

X = torch.arange(12, dtype=torch.float32).reshape((3,4))
Y = torch.tensor([[2.0, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
torch.cat((X, Y), dim=0), torch.cat((X, Y), dim=1)
(tensor([[ 0.,  1.,  2.,  3.],
         [ 4.,  5.,  6.,  7.],
         [ 8.,  9., 10., 11.],
         [ 2.,  1.,  4.,  3.],
         [ 1.,  2.,  3.,  4.],
         [ 4.,  3.,  2.,  1.]]),
 tensor([[ 0.,  1.,  2.,  3.,  2.,  1.,  4.,  3.],
         [ 4.,  5.,  6.,  7.,  1.,  2.,  3.,  4.],
         [ 8.,  9., 10., 11.,  4.,  3.,  2.,  1.]]))

可以看到,对形状的第一个元素进行cat后,3 + 3 ==> 6 对形状的第二个元素进行cat后,4 + 4 ==> 8

有时,我们想通过逻辑运算符构建二元张量。 以X == Y为例: 对于每个位置,如果XY在该位置相等,则新张量中相应项的值为1。 这意味着逻辑语句X == Y在该位置处为真,否则该位置为0。

tensor([[ 0.,  1.,  2.,  3.],
        [ 4.,  5.,  6.,  7.],
        [ 8.,  9., 10., 11.]])
tensor([[2., 1., 4., 3.],
        [1., 2., 3., 4.],
        [4., 3., 2., 1.]])

tensor([[False,  True, False,  True],
        [False, False, False, False],
        [False, False, False, False]])

还有一种操作,对张量中的所有元素进行求和,会产生一个单元素张量。

print(X.sum())   # tensor(66.)

2.1.3 广播机制

在上面的部分中,我们看到了如何在相同形状的两个张量上执行按元素操作。 在某些情况下,即使形状不同,我们仍然可以通过调用 广播机制(broadcasting mechanism)来执行按元素操作。 这种机制的工作方式如下:

  1. 通过适当复制元素来扩展一个或两个数组,以便在转换之后,两个张量具有相同的形状;
  2. 对生成的数组执行按元素操作。

在大多数情况下,我们将沿着数组中长度为1的轴进行广播,如下例子:

a = torch.arange(3).reshape((3, 1))
b = torch.arange(2).reshape((1, 2))
a, b


(tensor([[0],
         [1],
         [2]]),
 tensor([[0, 1]]))

# 扩展之后,变成
0 0
1 1
2 2

0 1
0 1
0 1

# 最后的结果
tensor([[0, 1],
        [1, 2],
        [2, 3]])

矩阵a将复制列, 矩阵b将复制行,形状相同后,再按元素相加。

2.1.4 索引和切片

2.1.5 节省内存

import torch

X = torch.arange(12, dtype=torch.float32).reshape((3,4))
Y = torch.tensor([[2.0, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])

before = id(X)
X = X + Y   # 没有执行原地更新, X的地址发生改变
X[:] = X + Y  # 执行原地更新, X的地址不变,节省内存开销

print(id(X) == before)

2.1.6 转换为其他Python对象

将深度学习框架定义的张量转换为NumPy张量(ndarray)很容易,反之也同样容易。

A = X.numpy()
B = torch.tensor(A)
type(A), type(B)

2.2 数据预处理

我们经常从预处理原始数据开始, 而不是从那些准备好的张量格式数据开始。

在Python中常用的数据分析工具中,我们通常使用pandas软件包。

我们将简要介绍使用pandas预处理原始数据,并将原始数据转换为张量格式的步骤。

2.2.1 读取数据集

首先创建一个人工数据集,并存储在CSV(逗号分隔值)文件 ../data/house_tiny.csv

CSV: comma separate value

该数据集有四行三列。其中每行描述了房间数量(“NumRooms”)、巷子类型(“Alley”)和房屋价格(“Price”)。

import os
import pandas as pd

# 创建人工数据集
def build_dataset():
    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')

# 加载数据集
def load_dataset():
    data_file = os.path.join('..', 'data', 'house_tiny.csv')
    print(data_file)  # 打印文件路径  ..\data\house_tiny.csv
    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

2.2.2 处理缺失值

注意,“NaN”项代表缺失值。 为了处理缺失的数据,典型的方法包括插值法删除法, 其中插值法用一个替代值弥补缺失值,而删除法则直接忽略缺失值。在这里,我们将考虑插值法。

通过位置索引iloc,我们将data分成inputsoutputs, 其中前者为data的前两列,而后者为data的最后一列。

对于inputs中缺少的数值,我们用同一列的均值替换“NaN”项。

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中的类别值或离散值,我们将“NaN”视为一个类别。

由于“巷子类型”(“Alley”)列只接受两种类型的类别值“Pave”和“NaN”, pandas可以自动将此列转换为两列“Alley_Pave”和“Alley_nan”。

巷子类型为“Pave”的行会将“Alley_Pave”的值设置为1,“Alley_nan”的值设置为0。 缺少巷子类型的行会将“Alley_Pave”和“Alley_nan”分别设置为0和1。

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 转换为张量格式

现在inputsoutputs中的所有条目都是数值类型,它们可以转换为张量格式。 当数据采用张量格式后,可以通过上述引入的那些张量函数来进一步操作。

import torch

X, y = torch.tensor(inputs.values), torch.tensor(outputs.values)
X, y
(tensor([[3., 1., 0.],
         [2., 0., 1.],
         [4., 0., 1.],
         [3., 0., 1.]], dtype=torch.float64),
 tensor([127500, 106000, 178100, 140000]))

2.2.4 小结

  • pandas软件包是Python中常用的数据分析工具中,pandas可以与张量兼容。
  • pandas处理缺失的数据时,我们可根据情况选择用插值法和删除法。

2.3 线性代数

2.3.1 标量

2.3.2 向量

默认列向量是向量的默认方向

2.3.2.1 长度、维度和形状

在数学表示法中,如果我们想说一个向量x由n个实值标量组成, 可以将其表示为xRnR^n

请注意维度(dimension)这个词在不同上下文时往往会有不同的含义,这经常会使人感到困惑。 为了清楚起见,我们在此明确一下: 向量的维度被用来表示向量的长度,即向量或轴的元素数量。 然而,张量的维度用来表示张量具有的轴数。 在这个意义上,张量的某个轴的维数就是这个轴的长度。

2.3.3 矩阵

image.png

A = torch.arange(20).reshape(5, 4)
tensor([[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11],
        [12, 13, 14, 15],
        [16, 17, 18, 19]])

当我们交换矩阵的行和列时,结果称为矩阵的转置(transpose)。

现在在代码中访问矩阵的转置。

A.T
tensor([[ 0,  4,  8, 12, 16],
        [ 1,  5,  9, 13, 17],
        [ 2,  6, 10, 14, 18],
        [ 3,  7, 11, 15, 19]])

作为方阵的一种特殊类型,对称矩阵(symmetric matrix) A = A.T

2.3.6 降维

之前一直有个懵懵懂懂的地方。 现在有点懂了

A = torch.arange(20).reshape(5, -1)
print(A)
print(A.sum(axis=0))  # 合并0维(行)
print(A.sum(axis=1))  # 合并1维(列)
tensor([[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11],
        [12, 13, 14, 15],
        [16, 17, 18, 19]])
tensor([40, 45, 50, 55])
tensor([ 6, 22, 38, 54, 70])

2.3.7 点积

x和y的点积 ==> x T^T y

相同位置的按元素乘积的和

2.3.8 矩阵-向量积

matrix-vector product

image.png

torch.mv(A, x)  # A矩阵  x向量     通过torch.mv() 求矩阵-向量积   matrix-vector product

2.3.9 矩阵-矩阵积

注意, 列向量为默认方向的向量。 所以转置后为 行向量

image.png

torch.mm(A, B)

2.3.10 范数

L2范数: 是向量元素平方和的平方根:

image.png

u = torch.tensor([3.0, -4.0])
torch.norm(u)
tensor(5.)

image.png

torch.abs(u).sum()
tensor(7.)

2.4 微积分

2.4.1 导数和微分

2.4.2 偏导数

image.png

其他变量暂时看做常数

2.4.3 梯度

我们可以连结一个多元函数对其所有变量的偏导数,以得到该函数的梯度(gradient)向量

2.4.4 链式法则

image.png

2.5 自动微分

小结

  • 深度学习框架可以自动计算导数:我们首先将梯度附加到想要对其计算偏导数的变量上,然后记录目标值的计算,执行它的反向传播函数,并访问得到的梯度。

2.6 概率

简单的说,机器学习就是做出预测

2.6.2.1 联合概率

P(A=a, B=b) A=a和B=b同时发生的概率,叫做联合概率

2.6.2.2 条件概率

P(A=a | B=b) 在B=b发生的前提下,A=a发生的概率,叫做条件概率

2.6.2.3 贝叶斯定理

Bayes定理

image.png

2.6.2.4 边际化

image.png

2.6.2.5 独立性

两个随机变量是独立的,则

image.png