动手学深度学习 学习笔记(一)预备知识
基本概念
机器学习的重要性
- 机器学习允许计算机程序通过经验学习,自动改进性能,而不需要人类详细地编程。
- 机器学习在处理复杂任务(如天气预测、自然语言处理、图像识别等)时展现出了超越传统编程的能力。
机器学习的日常应用
- 机器学习技术已经渗透到日常生活中,如语音识别、地图导航等。
- 通过收集和标记数据集,机器学习算法可以学习识别模式,如识别唤醒词。
机器学习的关键组件
- 可以用来学习的数据(data);
- 如何转换数据的模型(model);
- 一个目标函数(objective function),用来量化模型的有效性;
- 调整模型参数以优化目标函数的算法(algorithm)。
机器学习问题的类型
- 监督学习:预测给定输入特征的标签。
- 无监督学习:在没有标签的情况下发现数据的模式。
- 强化学习:智能体与环境互动并学习最佳策略。
深度学习的发展
- 深度学习是机器学习的一个子集,关注使用多层神经网络进行学习。
- 深度学习的成功得益于大数据集的可用性、计算能力的提升(尤其是GPU的发展)以及算法的进步。
深度学习的特点
- 表示学习:自动找到数据的合适表示。
- 端到端训练:整个系统作为一个整体进行训练和优化。
- 非参数模型:使用复杂的模型来拟合数据,而不是依赖简化的参数化假设。
代码部分
conda安装
https://conda.io/en/latest/miniconda.html
conda create --name d2l python=3.9 -y
conda activate d2l
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
pip install d2l==0.17.6
mkdir d2l-zh && cd d2l-zh
curl https://zh-v2.d2l.ai/d2l-zh-2.0.0.zip -o d2l-zh.zip
unzip d2l-zh.zip && rm d2l-zh.zip
cd pytorch
jupyter notebook
数据操作
张量(tensor)表示一个由数值组成的数组,这个数组可能有多个维度。 具有一个轴的张量对应数学上的向量(vector); 具有两个轴的张量对应数学上的矩阵(matrix); 具有两个轴以上的张量没有特殊的数学名称。
行向量
x = torch.arange(3) # 创建一个行向量 [0, 1, 2]
print(x.shape) # 输出形状:torch.Size([3])
print(x.numel()) # 输出元素个数:3
改变张量形状
print(x.reshape(-1, 1)) # 将行向量转换为列向量 [[0], [1], [2]]
创建全零和全一的张量
print(torch.zeros((1, 2, 3))) # 创建一个形状为 (1, 2, 3) 的全零张量
print(torch.ones((1, 2, 3))) # 创建一个形状为 (1, 2, 3) 的全一张量
随机采样
print(torch.randn(3, 4)) # 创建一个随机张量,形状为 (3, 4)
自定义张量
print(torch.tensor([[1, 2, 3], [1, 2, 3]])) # 创建一个自定义的二维张量
幂运算
print(torch.exp(x)) # 对行向量中的每个元素进行指数运算
创建二维张量
X = torch.arange(12, dtype=torch.float32).reshape((3, 4)) # 创建形状为 (3, 4) 的张量
print(X)
Y = torch.tensor([[2.0, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]]) # 创建另一个张量
print(Y)
连接张量
print(torch.cat((X, Y), dim=0)) # 沿着行方向(dim=0)连接 X 和 Y
print(torch.cat((X, Y), dim=1)) # 沿着列方向(dim=1)连接 X 和 Y
比较张量
print(X == Y) # 比较两个张量,输出布尔数组
求和
print(X.sum()) # 对张量 X 中的所有元素求和
张量索引与切片
print(X[-1]) # 获取最后一行的元素
print(X[1:3]) # 切片获取第 1 到第 2 行(不包括第 3 行)的元素
数据预处理
import os
import pandas as pd
import torch
# 创建一个人工数据集,并存储在CSV
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')
# 从创建的CSV文件中加载原始数据集
data = pd.read_csv(data_file)
print(data)
# 位置索引iloc。其中前者为data的前两列,而后者为data的最后一列
inputs, outputs = data.iloc[:, 0:2], data.iloc[:, 2]
inputs = inputs.fillna(inputs.mean()) # 插值法
print(inputs)
inputs = pd.get_dummies(inputs, dummy_na=True)
print(inputs)
# 转换为张量格式
X = torch.tensor(inputs.to_numpy(dtype=float))
y = torch.tensor(outputs.to_numpy(dtype=float))
print(X, y)
线性代数
矩阵创建与转置
A = torch.arange(20, dtype=torch.float32).reshape(5, 4) # 创建一个 5x4 的矩阵
print(A, A.T) # 输出矩阵 A 和它的转置 A.T
矩阵操作
B = A.clone() # 克隆矩阵 A
print(A * B) # 元素-wise 相乘
标量与矩阵计算
a = 2 # 标量
X = torch.arange(24).reshape(2, 3, 4) # 创建一个 3维的张量
print(X)
print(a + X, a * X) # 标量与矩阵加法与乘法
求和操作
x = torch.arange(4, dtype=torch.float32) # 创建向量 x
print(x)
print(x.sum()) # 向量求和
print(A.sum(axis=0)) # 对每列求和
print(A.sum(axis=1)) # 对每行求和
print(A.sum(axis=1, keepdims=True)) # 保持轴数不变的情况下对每行求和
print(A.cumsum(axis=0)) # 累积总和
向量点积
y = torch.ones(4, dtype=torch.float32) # 创建向量 y
print(x, y, torch.dot(x, y)) # 计算 x 和 y 的点积
向量积
print(torch.mv(A, x)) # 矩阵 A 与向量 x 的矩阵-向量乘法
矩阵-矩阵乘法
B = torch.ones(4, 3) # 创建一个 4x3 的全一矩阵
print(torch.mm(A, B)) # 矩阵 A 与矩阵 B 的矩阵乘法
L1范数与 L2范数
向量的范数(norm)表示一个向量的大小,此处的大小不涉及维度,而是分量的大小。
# L1范数
torch.abs(u).sum()
# L2范数
u = torch.tensor([3.0, -4.0]) # 创建向量 u
print(torch.norm(u)) # 计算向量的 L2 范数
print(torch.norm(torch.ones((4, 9)))) # 计算矩阵的 L2 范数
微积分
关于微分、导数和偏导数的几何解释:https://blog.abyssdawn.com/archives/472.html
- 逼近法(Method of Exhaustion)
- 古希腊人通过内接多边形逼近圆等曲线形状,以计算面积。
- 边数越多,多边形越接近圆,面积计算越精确。
- 逼近法是积分微积分的前身。
- 微积分的两大分支
- 积分微积分:起源于逼近法,用于计算面积和体积。
- 微分微积分:用于解决优化问题,寻找函数的最大值或最小值。
- 深度学习中的优化问题
- 优化(Optimization):调整模型参数以最小化损失函数,使模型拟合训练数据。
- 泛化(Generalization):确保模型在新、未见过的数据上也能表现良好。
- 微分在深度学习中的应用
- 微分知识是理解和实施深度学习优化算法的基础。
- 通过微分,可以计算损失函数关于模型参数的梯度,指导参数更新。
import numpy as np
from d2l.torch import plot
# 函数
def f(x):
return 3 * x ** 2 - 4 * x
# 导数定义
def numerical_lim(f, x, h):
return (f(x + h) - f(x)) / h
# h 无限将近0
h = 0.1
for i in range(5):
print(f'h={h:.5f}, numerical limit={numerical_lim(f, 1, h):.5f}')
h *= 0.1
# 绘制原函数和原函数的切线
x = np.arange(0, 3, 0.1)
plot(x, [f(x), 2 * x - 3], 'x', 'f(x)', legend=['f(x)', 'Tangent line (x=1)'])
自动微分
import torch
x = torch.arange(4.0)
print(x)
x.requires_grad_(True) # 等价于x=torch.arange(4.0,requires_grad=True)
print(x.grad) # 默认值是None
y = 2 * torch.dot(x, x)
print(y) # 28
# 自动计算y关于x每个分量的梯度
y.backward()
print(x.grad) # tensor([ 0., 4., 8., 12.])
# 这篇笔记解释了为什么是这个结果:https://www.cnblogs.com/AncilunKiang/p/17431935.html
# 计算x的另一个函数
x.grad.zero_()
y = x.sum()
y.backward()
print(x.grad)
# 当y不是标量时,向量y关于向量x的导数的最自然解释是一个矩阵。 对于高阶和高维的y和x,求导的结果可以是一个高阶张量
x.grad.zero_()
y = x * x # 相当于 y=x² 对每个求偏导 就是2x
# 等价于y.backward(torch.ones(len(x)))
y.sum().backward()
print(x.grad)
概率
在统计学中,我们把从概率分布中抽取样本的过程称为抽样(sampling)。 笼统来说,可以把分布(distribution)看作对事件的概率分配。 将概率分配给一些离散选择的分布称为多项分布(multinomial distribution)。
import torch
from torch.distributions import multinomial
from d2l import torch as d2l
# 掷骰子的概率
fair_probs = torch.ones([6]) / 6 # 1/6 1/6 1/6 1/6 1/6 1/6
# 多项分布 将概率分配给一些离散选择
# counts = multinomial.Multinomial(1000, fair_probs).sample() # 模拟1000次投掷
# print(counts / 1000) # 相对频率作为估计值
# 进行500组实验,每组抽取10个样本
counts = multinomial.Multinomial(10, fair_probs).sample((500,))
cum_counts = counts.cumsum(dim=0)
estimates = cum_counts / cum_counts.sum(dim=1, keepdims=True)
d2l.set_figsize((6, 4.5))
for i in range(6):
d2l.plt.plot(estimates[:, i].numpy(),
label=("P(die=" + str(i + 1) + ")"))
d2l.plt.axhline(y=0.167, color='black', linestyle='dashed')
d2l.plt.gca().set_xlabel('Groups of experiments')
d2l.plt.gca().set_ylabel('Estimated probability')
d2l.plt.legend()
理论:样本空间(sample space)或结果空间(outcome space):随机实验的所有可能结果构成的集合。 结果(outcome):样本空间或结果空间中的元素。 事件(event):一组给定样本空间的随机结果。
在处理骰子掷出时,我们将集合 $S={1,2,3,4,5,6}$ 称为样本空间(sample space)或结果空间(outcome space), 其中每个元素都是结果(outcome)。 事件(event)是一组给定样本空间的随机结果。 例如,“看到 5 ”( {5} )和“看到奇数”( {1,3,5} )都是掷出骰子的有效事件。 注意,如果一个随机实验的结果在 $A$ 中,则事件 $A$ 已经发生。 也就是说,如果投掷出 3 点,因为 $3∈{1,3,5}$ ,我们可以说,“看到奇数”的事件发生了。
P(X=a):表示x=a 时的概率,X表示随即变量
P(A=a,B=b):联合概率。表示A=a且B=b同时发生的概率
P(B=b|A=a):条件概率。表示B=b的概率,前提是A=a已经发生
贝叶斯定理的公式可以用以下数学符号表示:
$$
P(A|B) = \frac{P(B|A) \cdot P(A)}{P(B)}
$$
其中:
- $ P(A|B) $ 是在事件 B 发生的情况下,事件 A 发生的概率;
- $ P(B|A) $ 是在事件 A 发生的情况下,事件 B 发生的概率;
- $ P(A) $ 是事件 A 的先验概率;
- $ P(B) $ 是事件 B 的边际概率。
边际化公式:$P(B)$的概率等于计算A的所有可能,$P(B) = P(B)P(B,A=1) + P(B)P(B,A=0)$
本文记录学习笔记。