动手学深度学习

动手学深度学习

1. 引言

1.2 机器学习中的关键组件

① 可以用来学习的数据(data);

② 如何转换数据的模型(model);

③ 一个目标函数(objective function),用来量化模型的有效性;

④ 调整模型参数以优化目标函数的算法(algorithm)。

目标检测:将目标使用bounding box标识出来。

分割:将图像的边缘标出,抠图。

语义分割:相同的类别不同的个体识别不出来。

实例分割:相同的类别不同的个体可以识别出来。

1.2.1 数据

① 每个数据集由一个个样本(example,sample)组成,遵循独立同分布(independently and identically distributed,i.i.d).

② 样本,又叫数据点(data point)或者数据实例(data instance),通常每个样本由一组称为特征(features,或协变量(covariates))的属性组成

③机器学习根据这些属性预测出“标签(label,或目标(target))”

④维数:特征向量的长度

照片:

样本:单独一张照片
特征:每个像素数值的有序列表,200x200x3,200为长宽的像数值,3为R,G,B三个值
数据集:很多张照片组成
1.2.2 模型

模型:通过大量数据拟合出的函数,根据给定输入,通过相应函数映射出一个输出值。

1.2.3 目标函数

① 目标函数:通过样本的预测结果与真实标记产生的误差反向传播指导网络参数学习和表示学习。

② 目标函数的作用:定义模型的优劣程度。

③ 损失函数(loss function,或cost function):表示预测值与真实值之间的差异,与目标函数本质上相同,只是翻转一下符号。

④ 试图预测数值时,常见的损失函数:平方误差:预测值与实际值之差的平方。

⑤ 试图解决分类问题,常见的目标函数(损失函数):最小化错误率:预测与实际不符的样本比例。

⑥ 有些目标函数(如平方误差)容易优化,有些目标(错误率)由于不可微性或其他复杂性难以直接优化,通常会优化替代目标。

⑦ 损失函数是根据模型参数定义的,并取决于数据集。

⑧ 通过最小化损失来学习模型参数的最佳值。

⑨ 训练数据集:该数据集由一些为训练而手机的样本组成,拟合模型参数。

⑩ 测试数据集:训练数据训练出的模型,通过“新的数据集”来测试性能,评估拟合出的模型

1.2.4 优化算法

① 优化算法:搜索出最佳参数,最小化损失函数。

② 大多流行的优化算法通常基于一种基本方法:梯度下降

③ 在每个步骤中,梯度下降法都会检查每个参数,若仅对该参数进行少量变动,训练集损失会朝哪个方向移动,然后在减少损失的方向上优化参数。

1.3 各种机器学习问题

1.3.1 监督学习

① 监督学习:在“给定输入特征”的情况下预测标签。

② 样本:“特征-标签”对。

③ 监督学习的学习过程(三大步骤)

  1. 从已知大量数据样本中随机选取一个子集,为每个样本获取真实标签。有时,这些样本已有标签;有时,这些样本可能需要被人工标记。这些输入和相应的标签一起构成了训练数据集。
  2. 选择有监督的学习算法,它将训练数据作为输入,并输出一个“已完成学习的模型”。
  3. 将之前没见过的样本特征放到这个“已完成学习的模型”中,使用模型的输出作为相应标签的预测。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nldunSYq-1683168903784)(null)]

1.3.1.1 回归

① 回归问题:根据大量的数据,生成一个模型,使它的预测非常接近实际标签值。

② 回归问题:训练一个回归函数来输出一个值。

③ 常见的损失函数:平方误差。

1.3.1.2 分类

① 分类问题希望模型能够预测样本属于哪个类别(category,正式称为类(class))

② 分类问题:训练一个分类器来输出预测类别。

③ 常见损失函数:交叉熵(crossentropy)。

④ 多项分类:有两个以上的类别。

⑤ 层次分类:错误地分入一个相关分类,也不错误地分入一个遥远的类别。

1.3.1.3 标记问题

多标签分类:学习预测不相互排斥的类别问题。比如一张图片中有猫有狗,可以告诉这张图片中既有猫又有狗。

1.3.1.4 搜索

① 不仅仅希望输出一个类别或一个实值,而且能对返回结果进行排序。

② 解决方案:

  1. 为集合中的每个元素分配相应的相关性分数
  2. 检索评级最高的元素。

③ 百度和谷歌搜索,会将相关性分数最高的放在第一个。

1.3.1.5 推荐系统

淘宝、京东、抖音的推荐系统,会将你感兴趣的推荐给你。

1.3.1.6 序列学习

① 序列学习:输入输出都是可变长度的序列。

② 标记与解析;自动语音识别;文本到语音;机器翻译

1.3.2 无监督学习

① 无监督学习:给出数据集(无标注),让模型“自发”的学习

② 无监督学习回答的问题:

  • 聚类问题:无标签的情况下,给数据进行分类。

给定一组照片由猫、狗、婴儿、山峰,将这组照片分类成猫、狗、婴儿、山峰这几个类别。

  • 主成分分析问题:找到少量的参数来准确地捕捉数据的线性相关属性。

一个球的运动轨迹可以用球的速度、直径和质量来描述。

  • 因果关系和概率图模型问题:描述观察到的许多数据的根本原因

如果我们有关于房价、污染、犯罪、地理位置、教育和工资的人口统计数据,能否简单的根据经验数据发现他们之间的关系。

  • 生成对抗网络:提供一种合成数据的方法,甚至像图像和音频这样复杂的非结构化数据。潜在统计机制是检查真实和虚假数据是否相同的测试。
1.3.3 与环境互动

离线学习:在算法与环境断开后进行的学习。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LZJm4pkW-1683168903981)(null)]

1.3.4 强化学习

① 强化学习:使用机器学习开发与环境交互并采取行动。

② 当环境可被完全观察到时,强化学习问题被称为马尔可夫决策过程。

③ 当状态不依赖于之前的操作时,称该问题为上下文赌博机

④ 当没状态时,只有一组最初位置回报的可用动作时,这个问题就是经典的多臂赌博机。

2. 预备知识

2.1 数据操作

张量:n维数组。

2.1.1 入门
import torch

# 张量:一个由数值组成的数组,该数组可能又多个维度
# 一维张量:对应数学上的向量
# 二维张量:对应数学上的矩阵

# 张量中的每个值被称为元素
# arange():创建张量
x=torch.arange(12)

# 张量的shape属性访问张量的形状
x.shape

# 张量的numel方法:获取张量中元素的总数。
x.numel()

# 改变张量的形状而不改变元素数量和元素值
# 将张量x从形状(12,)的行向量转换为形状为(3,4)的矩阵。
# 可以使用-1来调用此自动计算出维度的功能 X = x.reshape(-1,4);X = x.reshape(3,-1)
X = x.reshape(3,4)

# 创建全0,形状为(2,3,4)的张量
torch.zero((2,3,4))

# 创建全1,形状为(2,3,4)的张量
torch.ones((2,3,4))

# 创建随机采样得到张量中的每个元素的值
# 创建一个形状为(3,4)的张量,其中每个元素都服从均值为0、标准差为1的标准高斯分布(正态分布)中随机采样
torch.randn(3,4)

# 提供包含数值的Python列表,来为所需张量中的每个元素赋予确定值
torch.tensor([[2,1,4,3],[1,2,3,4],[4,3,2,1]])

2.1.2 运算符
import torch

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 # **运算符是求幂运算

# ”按元素“ 
torch.exp(x)

# cat拼接,将X和Y拼接在一起,dim=0:组合成(6,4);dim=1:组合成(3,8)
#由于拼接的两个变量是(3,4),所以最高只能dim=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)

# stack,堆叠,将X和Y堆叠在一起,dim=0:堆叠成(2,3,4),dim=1:堆叠成(3,2,4),dim=2:堆叠成(3,4,2)
#由于堆叠的两个变量是(3,4),所以最高只能dim=2
torch.stack((X,Y),dim=0)
torch.stack((X,Y),dim=1)

# 逻辑判断
X==Y
# 输出结果:tensor([[False,  True, False,  True],
#       		  [False, False, False, False],
#       		  [False, False, False, False]])

# 对张量中的所有元素求和,会产生一个单元素张量
X.sum()

t o r c h . e x p ( x )    :    y = e x i              对每个元素进行操作 torch.exp(x)\;:\;y = e^{x_i}\;\;\;\;\;\;对每个元素进行操作 torch.exp(x):y=exi对每个元素进行操作

2.1.3 广播机制

① 广播机制工作方式:

  1. 通过适当复制元素来扩展一个或两个数组,以便在转换之后,两个张量具有相同的形状;
  2. 对生成的数组执行按元素操作
# 分别创建a为3x3的矩阵,b为1x2的矩阵
a = torch.arange(3),reshape((3,1))
b = torch.arange(2).reshape((1,2))
# a = [[0],             b = [[0,1]]
#      [1],
#      [2]]

# 使a,b相加,由于它们的形状不匹配,所以可以广播为一个更大的3x2的矩阵,矩阵a将复制列,矩阵b将复制行,然后再按元素相加
a + b
# 复制后的 a = [[0,0],       b = [[0,1],       结果为:[[0,1],
#              [1,1],            [0,1],              [1,2],
#              [2,2]]            [0,1]]              [2,3]]
2.1.4 索引和切片
# X = [[1,2,3,4],
#	   [5,6,7,8],
#	   [9,10,11,12],
#	   [13,14,15,16]]

# -1表示最后一个元素的索引,这里是最外层元素内的最后一个索引
X[-1]
# 结果:tensor([13, 14, 15, 16])

# 1:3指第一个元素和第三个元素之间的元素,左闭右开,不包括第三个元素,这里同样指的是最外层元素。
X[1:3]
# 结果:tensor([[ 5,  6,  7,  8],
#        	   [ 9, 10, 11, 12]])


# 通过指定索引来将元素写入矩阵
X[1,2]=9
# 结果:tensor([[ 1,  2,  3,  4],
#            [ 5,  6,  9,  8],
#        	 [ 9, 10, 11, 12],
#        	 [13, 14, 15, 16]])

# 0:2表示第零个元素到第二个元素,:表示第零个元素到第二个元素的所有元素
# 0:2是指最外层元素的索引,:是指第二层元素的索引
X[0:2,:]=20
# 结果:tensor([[20, 20, 20, 20],
#        	   [20, 20, 20, 20],
#        	   [ 9, 10, 11, 12],
#        	   [13, 14, 15, 16]])
2.1.5 节省内存

① 对数据进行一些操作可能会导致为新的结果分配内存

# 查看Y的存储地址,并赋值给before
before = id(Y)
Y = X + Y
id(Y) == before
# 结果:False
# 我们不难发现对Y进行操作后,Y的地址发生了变化,也就是为Y分配新的内存

产生以上情况是不可取的,有两个原因:

  1. 这样会产生很多不必要的内存 ,在机器学习中,可能有数百兆的参数,并且在一秒内多次更新所有参数。通常情况下,我们希望原地执行这些更新。
  2. 若不原地更新,其它引用仍然会指向旧的内存地址,这样某些代码可能无意中引用旧的参数。

② 原地执行操作:

# 创建一个与Y形状相同的变量Z,且全部分配为0
Z = torch.zero_like(Y)
print('id(Z):',id(Z))
# 使用切片法将结果分配给Z
Z[:] = X + Y
print('id(Z):',id(Z))
# 结果中地址并没有改变

③ 后续计算中没有重复使用X,可以使用X[:] = X + Y 或 X += Y来减小开销

before = id(X)
X += Y
id(X) == before
# 结果:True
2.1.6 转换为其他Python对象

① torch张量和numpy数组将共享它们底层内存,就地操作更改一个张量也会同时更改另一个张量。

A = X.numpy()
B = torch.tensor(A)
type(A)
type(B)
# 结果: (numpy.ndarray,toech.Tensor)

② 将大小为1的张量转换为Python标量,可调用item函数或Python的内置函数。

a = torch.tensor([3.5])
a,a.item(),float(a),int(a)
# 结果:tensor([3.5000]),3.5,3.5,3

2.2 数据预处理

2.2.1 读取数据集
import os

# 创建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')    
import pandas as pd

# 使用pandas中的read_csv函数读取csv数据
data = pd.read_csv(data_file)
print(data)
# NumRooms:房间数量;Alley:巷子类型;Price:房屋价格
# 结果:   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"代表缺失值。处理缺失的数据,典型的方法包括:

  1. 插值法:使用代替值弥补缺失值。
  2. 删除法:直接忽略缺失值。
# 插值法
# 通过位置索引iloc,我们将data分成inputs和outputs
#inputs:data的前两列,0:2左闭右开
#outputs:data的最后一列
inputs,outputs = data.iloc[:, 0:2],data.iloc[:, 2]
# mean():求取均值,fillna():填充NaN
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 转换为张量格式
import torch

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

2.3 线性代数

2.3.1 标量

标量:仅包含一个数字,由只有一个元素的张量组成。

数学表示法:普通小写字母表示。

import torch

x = torch.tensor(3.0)
y = torch.tensor(2.0)

x+y, x*y, x/y, x**y

# 结果:(tensor(5.),tensor(6.),tensor(1.5000),tensor(9.))
2.3.2 向量

向量:标量值组成的列表,标量值被称为向量的元素或分量。

数学表示法:粗体小写字母。

注意:向量是竖的。

x = torch.arange(4)
x

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

在数学中向量x 可以写为:
KaTeX parse error: Undefined control sequence: \matrix at position 17: …\pmb{x}=\left[ \̲m̲a̲t̲r̲i̲x̲{x_1\\x_2\\·\\·…

x[3]
# 结果:tensor(3)
2.3.2.1 长度、维度和形状

维度:向量的长度

len(x)
# 结果:4
x.shape
# 结果:torch.Size([4])

明确:

  1. 向量或轴的维度被用来表示向量或轴的长度,即向量或轴的元素数量。
  2. 张量的维度用来表示张量具有的轴数。
2.3.3 矩阵

矩阵:将向量从一阶推广到二阶。

数学表示法:粗体大写字母表示。
KaTeX parse error: Undefined control sequence: \matrix at position 18: …pmb{A}= \left[ \̲m̲a̲t̲r̲i̲x̲{a_{11}&a_{12}&…

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]])

矩阵的转置:交换矩阵的行和列。
KaTeX parse error: Undefined control sequence: \matrix at position 44: …b{A}^T\;=\left[\̲m̲a̲t̲r̲i̲x̲{a_{11}&a_{21}&…

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

对称矩阵: A    =    A T 对称矩阵:\pmb{A}\;=\;\pmb{A}^T 对称矩阵:A=AT

B = torch.tensor([1,2,3],[2,0,4],[3,4,5])
B
# 结果:tensor([[1, 2, 3],
#        	   [2, 0, 4],
#        	   [3, 4, 5]])
B == B.T
# 结果:tensor([[True, True, True],
#        	   [True, True, True],
#        	   [True, True, True]])
2.3.4 张量

张量:描述具有任意数量轴的n维数组的通用方法。

数学表示法:特殊字体的大写字母表示。

图像:以n维数组形式出现,其中3个轴对应于高度、宽度以及通道轴(channel),表示颜色通道(红色、绿色和蓝色)。

X = torch.arange(24).reshape(2,3,4)
X
# 结果:tensor([[[ 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 = torch.arange(20,dtype=torch.float32).reshape(5,4)
B = A.clone()		# 通过分配新内存,将A的一个副本分配给B
A,A+B
# 结果:A= tensor([[ 0.,  1.,  2.,  3.],
#        		  [ 4.,  5.,  6.,  7.],
#        		  [ 8.,  9., 10., 11.],
#        		  [12., 13., 14., 15.],
#        		  [16., 17., 18., 19.]])
#	   A+B= tensor([[ 0.,  2.,  4.,  6.],
#        	  		[ 8., 10., 12., 14.],
#        	  		[16., 18., 20., 22.],
#        	  		[24., 26., 28., 30.],
#        	  		[32., 34., 36., 38.]])

Hadamard积:两个矩阵按元素乘法。
KaTeX parse error: Undefined control sequence: \matrix at position 31: …t\pmb{B}=\left[\̲m̲a̲t̲r̲i̲x̲{a_{11}b_{11}&a…

A * B
# 结果:tensor([[  0.,   1.,   4.,   9.],
#        	   [ 16.,  25.,  36.,  49.],
#        	   [ 64.,  81., 100., 121.],
#        	   [144., 169., 196., 225.],
#        	   [256., 289., 324., 361.]])

将张量乘以或加上一个标量不会改变张良的形状,其中张量的每个元素都将与标量相加或相乘。

a = 2
X = torch.arange(24).reshape(2,3,4)
a + X,(a * X).shape
# 结果:tensor([[[ 2,  3,  4,  5],
#         		[ 6,  7,  8,  9],
#         		[10, 11, 12, 13]],
#
#        	  [[14, 15, 16, 17],
#         	   [18, 19, 20, 21],
#         	   [22, 23, 24, 25]]])
#	   torch.Size([2, 3, 4])
2.3.6 降维

求张量中所有元素的和:

x = torch.arange(4,dtype=torch.float32)
x,x.sum()
# 结果:(tensor([0.,1.,2.,3.]),tensor(6.))
# 可以求出任意形状张量的元素的和。

① 默认情况下,调用求和函数会沿所有的轴降低张量的维度,使它变为一个标量。

② 指定张量沿哪个轴来求和降低维度。

以矩阵为例,为了通过求所有行的元素来降维(轴0),可以在调用函数时指定axis=0,将每一列中的每行的元素相加。

A_sum_axis0 = A.sum(axis=0)
A_sum_axis0,A_sum_axis0.shape
# 结果:(tensor([40.,45.,50.,55.]),torch.Size([4]))

指定axis=1将通过汇总所有的列的元素降维(轴1),将每一行中的每列元素相加。

A_sum_axis1 = A.sum(axis=1)
A_sum_axis1,A_sum_axis1.shape
# 结果:(tensor([6.,22.,38.,54.,70.]),torch.Size([5]))

沿着行和列对矩阵求和,等价于对矩阵所有元素进行求和。

A.sum(axis=[0,1])
# 结果:tensor(190.)

平均值。

A.mean(),A.sum()/A.numel()
# 结果:(tensor(9.5000),tensor(9.5000))

沿指定轴降低张量维度求平均值。

A.mean(axis=0),A.sum(axis=0)/A.shape[0]
# 结果:(tensor([8.,9.,10.,11.]),tensor([8.,9.,10.,11.]))
2.3.6.1 非降维求和
sum_A = A.sum(axis=1, keepdims=True)
sum_A
# 结果:tensor([[ 6.],
#	           [22.],
#      		   [38.],
#        	   [54.],
#        	   [70.]])

由于sum_A在对每行进行求和后仍保持两个轴,可以通过广播将A除以sum_A

A / sum_A
# 结果:tensor([[0.0000, 0.1667, 0.3333, 0.5000],
#        [0.1818, 0.2273, 0.2727, 0.3182],
#        [0.2105, 0.2368, 0.2632, 0.2895],
#        [0.2222, 0.2407, 0.2593, 0.2778],
#        [0.2286, 0.2429, 0.2571, 0.2714]])

若想沿某个轴计算A元素的累积总和, 比如axis=0(按行计算),调用cumsum函数。 此函数不会沿任何轴降低输入张量的维度。注意是依次相加。

A.cumsum(axis=0)
# 结果:tensor([[ 0.,  1.,  2.,  3.],
#        [ 4.,  6.,  8., 10.],
#        [12., 15., 18., 21.],
#        [24., 28., 32., 36.],
#        [40., 45., 50., 55.]])
2.3.7 点积

给定两个向量 x , y ∈ R d , 它们的点积 x T y ( 或 < x , y > ) 是按照相同元素乘积的和: x T y = Σ i = 1 d x i y i 给定两个向量x,y\in{R^d},它们的点积x^Ty(或<x,y>)是按照相同元素乘积的和:\\ x^Ty=\Sigma^{d}_{i=1}x_iy_i 给定两个向量x,yRd,它们的点积xTy(<x,y>)是按照相同元素乘积的和:xTy=Σi=1dxiyi

x = torch.arange(4, dtype = torch.float32)
y = torch.ones(4, dtype = torch.float32)
x, y, torch.dot(x, y)
# 结果:(tensor([0., 1., 2., 3.]), 
# tensor([1., 1., 1., 1.]),
# tensor(6.))

可以通过执行按元素乘法,然后进行求和来表示两个向量的点积:

torch.sum(x * y)
# 结果:tensor(6.)

给定一组由向量 x ∈ R d 表示的值,和一组由 w ∈ R d 表示的权重。 x 中的值根据 权重 w 的加权和,可以表示为点积 x T w 。当权重为非负数且和为 1 (即 Σ i = 1 d w i = 1 )时 点积表示加权平均( w e i g h t e d a v e r a g e ) 给定一组由向量x\in{R^d}表示的值,和一组由w\in{R^d}表示的权重。 x中的值根据\\权重w的加权和,可以表示为点积x^Tw。当权重为非负数且和为1(即\Sigma^{d}_{i=1}wi=1)时\\点积表示加权平均(weighted average) 给定一组由向量xRd表示的值,和一组由wRd表示的权重。x中的值根据权重w的加权和,可以表示为点积xTw。当权重为非负数且和为1(即Σi=1dwi=1)时点积表示加权平均(weightedaverage

2.3.8 矩阵-向量积(matrix-vector product)

将矩阵A用行向量表示:
KaTeX parse error: Undefined control sequence: \matrix at position 16: \pmb{A}=\left[\̲m̲a̲t̲r̲i̲x̲{a_1^T\\a_2^T\\…

KaTeX parse error: Undefined control sequence: \matrix at position 80: …^Tx\\ Ax=\left[\̲m̲a̲t̲r̲i̲x̲{a_1^T\\a_2^T\\…

A.shape,
x.shape,
torch.mv(A,x)
# 结果:(torch.Size([5, 4]), torch.Size([4]), tensor([ 14.,  38.,  62.,  86., 110.]))
2.3.9 矩阵-矩阵乘法(matrix-matrix multiplication)

KaTeX parse error: Undefined control sequence: \matrix at position 67: …pmb{A}= \left[ \̲m̲a̲t̲r̲i̲x̲{a_{11}&a_{12}&…

import torch
A = torch.arange(20, dtype=torch.float32)
B = torch.ones(4,3)
torch.mm(A,B)
# 结果: tensor([[ 6.,  6.,  6.],
#        [22., 22., 22.],
#        [38., 38., 38.],
#        [54., 54., 54.],
#        [70., 70., 70.]])
2.3.10 范数

向量的范数(norm)表示一个向量有多大(考虑的是大小的概念不涉及维度,而是分量的大小)。

性质 1 :如果我们按常数因子 α 缩放向量的所有元素,其范数也会按相同常数因子 的绝对值缩放。 f ( α x ) = ∣ α ∣ f ( x ) 性质1:如果我们按常数因子\alpha缩放向量的所有元素,其范数也会按相同常数因子\\的绝对值缩放。\\f(\alpha{\pmb{x}})=|\alpha|f(\pmb{x}) 性质1:如果我们按常数因子α缩放向量的所有元素,其范数也会按相同常数因子的绝对值缩放。f(αx)=αf(x)

性质 2 : 三角不等式。 f ( x + y ) ≤ ( x ) + f ( y ) 性质2:三角不等式。\\f(\pmb{x+y})\leq(\pmb{x})+f(\pmb{y}) 性质2:三角不等式。f(x+y)(x)+f(y)

性质 3 :范数必须是非负的。 f ( x ) ≥ 0 性质3:范数必须是非负的。\\f(\pmb{x})\geq0 性质3:范数必须是非负的。f(x)0

性质 4 :范数最小为 0 ,当且仅当向量全由 0 组成。 ∀ i , [ x ] i = 0 ⟺ f ( x ) = 0 性质4:范数最小为0,当且仅当向量全由0组成。\\\forall{i},[\pmb{x}]_i=0\Longleftrightarrow{f(\pmb{x})=0} 性质4:范数最小为0,当且仅当向量全由0组成。i,[x]i=0f(x)=0

欧几里得距离是一个 L 2 范数:假设 n 维向量 x 中的元素是 x 1 , . . . , x n , 其 L 2 范数是向量元素平方和的根。 ∣ ∣ x ∣ ∣ 2 = ∣ ∣ x ∣ ∣ = Σ i = 1 n x i 2 欧几里得距离是一个L_2范数:假设n维向量\pmb{x}中的元素是x_1,...,x_n,\\其L_2范数是向量元素平方和的根。\\||\pmb{x}||_2=||\pmb{x}||=\sqrt{\Sigma^n_{i=1}x^2_i} 欧几里得距离是一个L2范数:假设n维向量x中的元素是x1,...xnL2范数是向量元素平方和的根。∣∣x2=∣∣x∣∣=Σi=1nxi2

u = torch.tensor([3,0,-4.0])
torch.norm(u)
# 结果:tensor(5.)

L 1 范数:表示向量元素的绝对值之和,与 L 2 相比 L 1 范数受异常值影响较小。 ∣ ∣ x ∣ ∣ 1 = Σ i = 1 n ∣ x i ∣ L_1范数:表示向量元素的绝对值之和,与L_2相比L_1范数受异常值影响较小。\\||\pmb{x}||_1=\Sigma^n_{i=1}|x_i| L1范数:表示向量元素的绝对值之和,与L2相比L1范数受异常值影响较小。∣∣x1=Σi=1nxi

torch.abs(u).sum()
# 结果:tensor(7.)

L p 范数: ∣ ∣ x ∣ ∣ p = ( Σ i = 1 n ∣ x i ∣ p ) 1 p L_p范数:||\pmb{x}||_p=(\Sigma^n_{i=1}|x_i|^p)^{\frac{1}{p}} Lp范数:∣∣xp=(Σi=1nxip)p1

以上范数说的是向量,矩阵 X ∈ R m × n 的 F r o b e n i u s 范数 ( F r o b e n i u s    n o r m ) 是矩阵元素平方和的平方根。 ∣ ∣ X ∣ ∣ F = Σ i = 1 m Σ j = 1 n ∣ x i j ∣ 2 以上范数说的是向量,矩阵\pmb{X}\in{R^{m\times{n}}}的Frobenius范数(Frobenius\;norm)\\是矩阵元素平方和的平方根。\\||\pmb{X}||_F=\sqrt{\Sigma^m_{i=1}\Sigma^n_{j=1}|x_{ij}|^2} 以上范数说的是向量,矩阵XRm×nFrobenius范数(Frobeniusnorm)是矩阵元素平方和的平方根。∣∣XF=Σi=1mΣj=1nxij2

torch.norm(torch.ones((4,9)))
# 结果:tensor(6.)
2.3.10.1 范数和目标

在深度学习中,试图解决优化问题:最大化分配给观测数据的概率;最小化预测和真实观测之间的距离。

使用向量表示物品,以便最小化相似目标之间的距离,最大化不同项目之间的距离。

目标,深度学习算法最重要的组成部分(除了数据),通常被表达维范数。

2.4. 微积分

  • 优化(optimization):用模型拟合观测数据的过程;
  • 泛化(generalization):数学原理和实践者的智慧,能够指导我们生成出有效性超出用于训练的数据集本身的模型,即使用模型对未参与训练的数据进行预测,得出想要的结果。
2.4.1. 导数和微分

假设有一个函数 f : R → R ,其输入和输出都是标量。如果的导数存在,极限被定义为 : f ′ ( x ) = l i m h → 0 f ( x + h ) − f ( x ) h 如果 f ′ ( a ) 存在,则称 f 在 a 处是可微( d i f f e r e n t i a b l e )的。 假设有一个函数f:R\rightarrow{R},其输入和输出都是标量。如果 的导数存在,极限被定义为:\\ f'(x) = lim_{h\rightarrow{0}}\frac{f(x+h)-f(x)}{h}\\如果f'(a)存在,则称f在a处是可微(differentiable)的。 假设有一个函数f:RR,其输入和输出都是标量。如果的导数存在,极限被定义为:f(x)=limh0hf(x+h)f(x)如果f(a)存在,则称fa处是可微(differentiable)的。

定义 : u = f ( x ) = 3 x 2 − 4 x 定义:u = f(x) = 3x^2-4x 定义:u=f(x)=3x24x

def f(x):
    return 3*x*x-4*x

def numerical_lim(f,x,h):
    return (f(x+h)-f(x))/h

h = 0.1
for i in range(5):
    print(f'h={h:.5f}, numerical limit={numerical_lim(f, 1, h):.5f}')
    h *= 0.1
    
# 结果:h=0.10000, numerical limit=2.30000
#	   h=0.01000, numerical limit=2.03000
#	   h=0.00100, numerical limit=2.00300
#	   h=0.00010, numerical limit=2.00030
#	   h=0.00001, numerical limit=2.00003
# h越接近于0,f'(1)就越接近于2

为了对导数的这种解释进行可视化,将使用matplotlib, 这是一个Python中流行的绘图库,可阅读Matplotlib绘图学习

2.4.2 偏导数

设 y = f ( x 1 , x 2 , . . . , x n ) 是一个具有 n 个变量的函数。 y 关于第 i 个参数 x i 的偏导数为: ∂ y ∂ x i = l i m h → 0 f ( x 1 , . . . , x i − 1 , x i + h , x i + 1 , . . . , x n ) − f ( x 1 , . . . , x i , . . . , x n ) h 设y=f(x_1,x_2,...,x_n)是一个具有n个变量的函数。y关于第i个参数x_i的偏导数为:\\\frac{\partial{y}}{\partial{x_i}}=lim_{h\rightarrow0}\frac{f(x_1,...,x_{i-1},x_i+h,x_{i+1},...,x_n)-f(x_1,...,x_i,...,x_n)}{h} y=f(x1,x2,...,xn)是一个具有n个变量的函数。y关于第i个参数xi的偏导数为:xiy=limh0hf(x1,...,xi1,xi+h,xi+1,...,xn)f(x1,...,xi,...,xn)

2.4.3 梯度

梯度:对多元函数的所有变量求偏导数 设函数 f : R n → R 的输入是一个 n 为向量 x = [ x 1 , x 2 , . . . , x n ] T ,并输出一个标量。 函数 f ( x ) 相对于 x 的梯度是一个包含 n 个偏导数的向量: ∇ x f ( X ) = [ ∂ f ( x ) ∂ x 1 , ∂ f ( x ) ∂ x 2 , . . . , ∂ f ( x ) ∂ x n ] T 其中 ∇ x f ( X ) 没有歧义时被 ∇ f ( X ) 取代。 1. 对于所有 A ∈ R m × n 都有 ∇ x A x = A T 2. 对于所有 A ∈ R n × m 都有 ∇ x x T A = A 3. 对于所有 A ∈ R n × m 都有 ∇ x x T A x = ( A + A T ) x 4. ∇ x ∣ ∣ x ∣ ∣ 2 = ∇ x x T x = 2 x 梯度:对多元函数的所有变量求偏导数\\设函数f:R^n\rightarrow{R}的输入是一个n为向量\pmb{x}=[x_1,x_2,...,x_n]^T,并输出一个标量。\\函数f(x)相对于x的梯度是一个包含n个偏导数的向量:\\\nabla_xf(X)=[\frac{\partial{f(x)}}{\partial{x_1}},\frac{\partial{f(x)}}{\partial{x_2}},...,\frac{\partial{f(x)}}{\partial{x_n}}]^T\\其中\\\nabla_xf(X)没有歧义时被\nabla{f(X)}取代。\\1.对于所有A\in{R^{m\times{n}}}都有\nabla_xAx=A^T\\2.对于所有A\in{R^{n\times{m}}}都有\nabla_x{x^TA}=A\\3.对于所有A\in{R^{n\times{m}}}都有\nabla_x{x^TAx}=(A+A^T)x\\4.\nabla_x||x||^2=\nabla_x{x^Tx}=2x 梯度:对多元函数的所有变量求偏导数设函数f:RnR的输入是一个n为向量x=[x1,x2,...,xn]T,并输出一个标量。函数f(x)相对于x的梯度是一个包含n个偏导数的向量:xf(X)=[x1f(x),x2f(x),...,xnf(x)]T其中xf(X)没有歧义时被f(X)取代。1.对于所有ARm×n都有xAx=AT2.对于所有ARn×m都有xxTA=A3.对于所有ARn×m都有xxTAx=(A+AT)x4.x∣∣x2=xxTx=2x

2.4.4 链式法则

假设函数 y = f ( u ) 和 u = g ( x ) 都是可微的,根据链式法则: d y d x = d y d u d u d x 假设函数y=f(u)和u=g(x)都是可微的,根据链式法则:\\ \frac{dy}{dx}=\frac{dy}{du}\frac{du}{dx} 假设函数y=f(u)u=g(x)都是可微的,根据链式法则:dxdy=dudydxdu

2.5 自动微分

自动微分:深度学习框架通过自动计算导数(系统会自动构建计算图,来跟踪计算哪些数据通过那些操作组合起来产生输出。)

自动微分使系统能够随后反向传播梯度。

反向传播:跟踪计算图,填充关于每个参数的偏导数。

2.5.1 举例

一个标量函数关于向量x的梯度是向量,且与x具有相同的形状。
对函数 y = 2 x T x 关于列向量 x 求导。 对函数y=2x^Tx关于列向量x求导。 对函数y=2xTx关于列向量x求导。

import torch

x = torch.arange(4.0)
print(x)
# 结果:tensor([0., 1., 2., 3.])

x.requires_grad_(True) # 等价于x=torch.arange(4.0,requires_grad=true)
print(x.grad) # 默认值是None,用于存储x的梯度,且不会每次分配内存
# 结果:None

y = 2*torch.dot(x,x)
print(y)
# 结果:tensor(28., grad_fn=<MulBackward0>)

y.backward() # 进行反向传播
print(x.grad)
# 结果:tensor([ 0.,  4.,  8., 12.])

# 默认情况下,Pytorch会积累梯度,所以需要清除
x.grad.zero()
# 函数 y = x1+x2+x3+x4,分别对x1、x2、x3、x4求偏导
y = x.sum()
y.backward()
print(x.grad)
# 结果:tensor([ 1.,  1.,  1.,  1.])
2.5.2 非标量变量的反向传播

y不是标量,向量y关于向量x的导数的最自然解释是一个矩阵。对于高阶和高维的y和x,求导结果可以是一个高阶张量。

x.grad.zero_()
y=x*x   # y=[x1^2,x2^2,x3^2,x4^2]
y.sum().backward()  # u=x1^2+x2^2+x3^2+x4^2,求u分别关于x1,x2,x3,x4的偏导
print(x.grad)
# 结果:tensor([0., 2., 4., 6.])
2.5.3 分离计算

u = x × x z = u × x 对以上 z = u × x 求偏导,我们希望 u 作为常数处理,而不是对 u 中的 x 也求偏导。 u=x\times{x}\\z=u\times{x}\\对以上z=u\times{x}求偏导,我们希望u作为常数处理,而不是对u中的x也求偏导。 u=x×xz=u×x对以上z=u×x求偏导,我们希望u作为常数处理,而不是对u中的x也求偏导。

x.grad.zero_()
y = x * x
u = y.detach()
print(u)
# 结果:tensor([0., 1., 4., 9.])
z = u * x

z.sum().backward()
print(x.grad)
# 结果:tensor([0., 1., 4., 9.])
2.5.4 Python控制流的梯度计算

自动微分的好处:即使构建函数的计算图需要通过Python控制流(如:条件、循环或任意函数调用),我们仍然可以计算得到变量的梯度。即不管怎么样都能得到梯度。

2.6 概率

2.6.1 基本概率论
import torch
from torch.distributions import multinomial
import matplotlib
from matplotlib import pyplot as plt
import numpy as np

# 模仿骰子的6个面的概率
fair_probs = torch.ones([6])/6
print(multinomial.Multinomial(1,fair_probs).sample())
# 结果:tensor([0., 0., 1., 0., 0., 0.])

# 10次实验中,每个面朝上的次数
print(multinomial.Multinomial(10,fair_probs).sample())
# 结果:tensor([3., 1., 0., 3., 1., 2.])

# 进行500组实验,每组实验进行10次
counts = multinomial.Multinomial(10,fair_probs).sample((500,))
print(counts)
# 结果:tensor([[1., 2., 1., 1., 3., 2.],
#         [1., 2., 0., 2., 3., 2.],
#         [2., 0., 3., 2., 1., 2.],
#         ...,
#         [1., 2., 2., 0., 1., 4.],
#         [0., 3., 1., 1., 2., 3.],
#         [1., 2., 2., 1., 2., 2.]])
#         [4., 2., 1., 1., 0., 2.]])
cum_counts = counts.cumsum(dim=0)   # 将每次的数据进行累加起来(每次即每行)
print(cum_counts)
# 结果:tensor([[  1.,   2.,   1.,   1.,   3.,   2.],
#         [  2.,   4.,   1.,   3.,   6.,   4.],
#         [  4.,   4.,   4.,   5.,   7.,   6.],
#         ...,
#         [866., 755., 815., 827., 847., 870.],
#         [866., 758., 816., 828., 849., 873.],
#         [867., 760., 818., 829., 851., 875.]])
x = cum_counts.sum(dim=1,keepdims=True)
estimates = cum_counts / x
# cum_counts.sum(dim=1,keepdims=True):将第一维(第二个[])的数据每行数据相加,并且每行数据相加的和都累加起来
print(estimates)
# 结果:tensor([[0.0000, 0.1000, 0.3000, 0.5000, 0.0000, 0.1000],
#         [0.1500, 0.1500, 0.2500, 0.3000, 0.0500, 0.1000],
#         [0.1667, 0.1333, 0.2333, 0.2667, 0.1000, 0.1000],
#         ...,
#         [0.1731, 0.1691, 0.1665, 0.1757, 0.1550, 0.1606],
#         [0.1729, 0.1693, 0.1669, 0.1754, 0.1549, 0.1605],
#         [0.1728, 0.1694, 0.1668, 0.1756, 0.1548, 0.1606]])

plt.xlim(0, 0.30)
plt.ylim(0, 500)

plt.xticks(np.linspace(0, 500, 6))
plt.yticks(np.linspace(0, 1, 21))
plt.xlabel("Groups of experiments")
plt.ylabel("estimated probability")

for i in range(6):
    plt.plot(x.numpy(),estimates[:,i].numpy(),linestyle='solid')
plt.axhline(y=0.167,color='black',linestyle='dashed')
plt.legend(['D1','D2','D3','D4','D5','D6'])
plt.show()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s2xkZMzv-1683168902160)(typoraJPG\1682353129151.jpg)]

剩余请看概率

3. 线性神经网络

3.1 线性回归

3.1.1 线性回归基本元素

实际的例子: 根据房屋的面积(平方英尺)和房龄(年)来估算房屋价格(美元)。

为了开发一个能预测房价的模型,我们需要收集一个真实的数据集。 这个数据集包括了房屋的销售价格、面积和房龄。 在机器学习的术语中,该数据集称为训练数据集(training data set) 或训练集(training set)。 每行数据(比如一次房屋交易相对应的数据)称为样本(sample), 也可以称为数据点(data point)或数据样本(data instance)。 我们把试图预测的目标(比如预测房屋价格)称为标签(label)或目标(target)。 预测所依据的自变量(面积和房龄)称为特征(feature)或协变量(covariate)。

3.1.1.1 线性模型

线性假设是指目标 ( 房屋价格 ) 可以表示为特征 ( 面积和房龄 ) 的加权和,如下式: p r i c e = w a r e a ⋅ a r e a + w a g e ⋅ a g e + b 上式中的 w a r e a 和 w a g e 为权重,权重决定了每个特征对我们预测值的影响。 b 称为偏置、 偏移量或截距。偏置是指当所有特征取值为 0 时,预测值应该为多少。 即使现实中不会有任何房子的面积是 0 或芳龄正好是 0 ,我们仍需偏置项。 上式输入特征的一个仿射变换( a f f i n e t r a n s f o r m a t i o n )。 仿射变换的特点是通过加权和对特征进行线性变换( l i n e a r t r a n s f o r m a t i o n ), 并通过偏置项来进行平移。 线性假设是指目标(房屋价格)可以表示为特征(面积和房龄)的加权和,如下式:\\ price = w_{area}·area+w_{age}·age+b\\上式中的w_{area}和w_{age}为权重,权重决定了每个特征对我们预测值的影响。b称为偏置、\\偏移量或截距。偏置是指当所有特征取值为0时,预测值应该为多少。\\即使现实中不会有任何房子的面积是0或芳龄正好是0,我们仍需偏置项。\\上式输入特征的一个仿射变换(affine transformation)。\\仿射变换的特点是通过加权和对特征进行线性变换(linear transformation),\\并通过偏置项来进行平移。 线性假设是指目标(房屋价格)可以表示为特征(面积和房龄)的加权和,如下式:price=wareaarea+wageage+b上式中的wareawage为权重,权重决定了每个特征对我们预测值的影响。b称为偏置、偏移量或截距。偏置是指当所有特征取值为0时,预测值应该为多少。即使现实中不会有任何房子的面积是0或芳龄正好是0,我们仍需偏置项。上式输入特征的一个仿射变换(affinetransformation)。仿射变换的特点是通过加权和对特征进行线性变换(lineartransformation),并通过偏置项来进行平移。

输入包含 d 个特征时,预测结果: y ^ ,表示为: y ^ = w 1 x 1 + . . . + w d x d + b 将所有特征放到向量 x ∈ R d 中 , 将所有权重放到向量 w ∈ R d ,上式可表达为 : y ^ = w T x + b 输入包含d个特征时,预测结果:\widehat{y},表示为:\\ \widehat{y}=w_1x_1+...+w_dx_d+b\\ 将所有特征放到向量\pmb{x}\in{R^d}中,将所有权重放到向量\pmb{w}\in{R^d},上式可表达为:\\ \widehat{y}=w^Tx+b 输入包含d个特征时,预测结果:y ,表示为:y =w1x1+...+wdxd+b将所有特征放到向量xRd,将所有权重放到向量wRd,上式可表达为:y =wTx+b

向量 x 对应于单个数据样本的特征。 n 个数据样本整合成一个矩阵用符号 X ∈ R n × d 。 X 的每一行是一个样本每一列是一种特征。 对于特征集合 X , 预测值 y ^ ∈ R n 可以通过矩阵 − 向量乘法 ( m v ) 表示为: y ^ = X w + b 向量\pmb{x}对应于单个数据样本的特征。n个数据样本整合成一个矩阵用符号\pmb{X}\in{R^{n\times{d}}}。\\ \pmb{X}的每一行是一个样本每一列是一种特征。\\ 对于特征集合\pmb{X},预测值\widehat{y}\in{R}^n可以通过矩阵-向量乘法(mv)表示为:\\ \widehat{y}=\pmb{X}w+b 向量x对应于单个数据样本的特征。n个数据样本整合成一个矩阵用符号XRn×dX的每一行是一个样本每一列是一种特征。对于特征集合X,预测值y Rn可以通过矩阵向量乘法(mv)表示为:y =Xw+b

线性回归的目标是找到一组权重向量w和偏置b: 当给定X的同分布中取样的新样本特征时, 这组权重向量和偏置能够使得新样本预测标签的误差尽可能小。

在开始寻找最好的模型参数(model parameters)w和b之前,我们还需要两个东西:

  1. 一种模型质量的度量方式;
  2. 一种能够更新模型以提高模型预测质量的方法。
3.1.1.2 损失函数

损失函数:量化目标的实际值于预测值之间的差距。

作用:衡量模型预测的好坏,模型拟合程度的度量。
最常用的损失函数—平方误差函数。 样本 i 的预测值为 y ^ ( i ) , 相应的真实标签为 y ( i ) ,平方误差可以定义为以下公式: l ( i ) ( w , b ) = 1 2 ( y ^ ( i ) − y ( i ) ) 2 最常用的损失函数—平方误差函数。\\ 样本i的预测值为\widehat{y}^{(i)},相应的真实标签为y^{(i)},平方误差可以定义为以下公式:\\ l^{(i)}(w,b)=\frac{1}{2}(\widehat{y}^{(i)}-y^{(i)})^2 最常用的损失函数平方误差函数。样本i的预测值为y (i),相应的真实标签为y(i),平方误差可以定义为以下公式:l(i)(w,b)=21(y (i)y(i))2
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hcl3mt1d-1683168902161)(typoraJPG\fit-linreg.svg)]
由于平方误差函数中的二次方项,估计值 y ^ ( i ) 和观测值 y ( i ) 之间较大的差异将导致更大的损失。 为了度量模型在整个数据集上的质量,我们需计算在训练集 n 个样本上的损失均值(也等价于求和)。 L ( w , b ) = 1 n Σ i = 1 n l ( i ) ( w , b ) = 1 n Σ i = 1 n 1 2 ( w T x ( i ) + b − y ( i ) ) 2 在训练模型时,我们希望寻找一组参数 ( w ∗ , b ∗ ) , 这组参数能最小化所有训练样本的损失值。 w ∗ , b ∗ = a r g m i n w , b L ( w , b ) 由于平方误差函数中的二次方项,估计值\widehat{y}^{(i)}和观测值y^{(i)}之间较大的差异将导致更大的损失。\\为了度量模型在整个数据集上的质量,我们需计算在训练集n个样本上的损失均值(也等价于求和)。\\ L(w,b)=\frac{1}{n}\Sigma^n_{i=1}l^{(i)}(\pmb{w},b)=\frac{1}{n}\Sigma^n_{i=1}\frac{1}{2}(\pmb{w}^T\pmb{x}^{(i)}+b-y^{(i)})^2\\在训练模型时,我们希望寻找一组参数(\pmb{w}^*,b^*),这组参数能最小化所有训练样本的损失值。\\\pmb{w}^*,b^*=argmin_{\pmb{w},b}L(\pmb{w},b) 由于平方误差函数中的二次方项,估计值y (i)和观测值y(i)之间较大的差异将导致更大的损失。为了度量模型在整个数据集上的质量,我们需计算在训练集n个样本上的损失均值(也等价于求和)。L(w,b)=n1Σi=1nl(i)(w,b)=n1Σi=1n21(wTx(i)+by(i))2在训练模型时,我们希望寻找一组参数(w,b),这组参数能最小化所有训练样本的损失值。w,b=argminw,bL(w,b)

3.1.1.3 解析解

线性回归的解可以用一个公式简单地表达出来,这类解叫做解析解(analytical solution)。
首先,我们将偏置 b 合并到参数 w 中,合并方法是在包含所有参数的矩阵中附加一列。 我们的预测问题是最小化 ∣ ∣ y − X w ∣ ∣ 2 ( 真实值与预测值差的平方 ) 。 这在损失平面上只有一个临界点,这个临界点对应于整个区域的损失极小点。 将损失关于 w 的导数设为 0 ,得到解析解: w ∗ = ( X T X ) − 1 X T y 首先,我们将偏置b合并到参数\pmb{w}中,合并方法是在包含所有参数的矩阵中附加一列。 \\我们的预测问题是最小化||y-\pmb{Xw}||^2(真实值与预测值差的平方)。\\这在损失平面上只有一个临界点,这个临界点对应于整个区域的损失极小点。\\将损失关于\pmb{w}的导数设为0,得到解析解:\\ \pmb{w}^*=(\pmb{X}^T\pmb{X})^{-1}\pmb{X}^Ty 首先,我们将偏置b合并到参数w中,合并方法是在包含所有参数的矩阵中附加一列。我们的预测问题是最小化∣∣yXw2(真实值与预测值差的平方)这在损失平面上只有一个临界点,这个临界点对应于整个区域的损失极小点。将损失关于w的导数设为0,得到解析解:w=(XTX)1XTy

3.1.1.4 随机梯度下降

梯度下降:优化深度学习模型。

原理:通过不断地在损失函数递减的方向上更新参数来降低误差。

梯度下降最简单的用法是计算损失函数(数据集中所有样本的损失均值) 关于模型参数的导数(在这里也可以称为梯度)。 但实际中的执行可能会非常慢:因为在每一次更新参数之前,我们必须遍历整个数据集。 因此,我们通常会在每次需要计算更新的时候随机抽取一小批样本, 这种变体叫做小批量随机梯度下降minibatch stochastic gradient descent)。
步骤: 1. 每次迭代中随机抽取以小批量 β , 它是由固定数量的训练样本组成的。 2. 计算小批量的平均损失关于模型参数的导数(也可以称为梯度)。 3. 将梯度乘以一个预先确定的正数 η ,并从当前参数的值中减掉。 数学公式表示 ( ∂ 表示偏导数 ) : ( w , b ) ← ( w , b ) − η ∣ β ∣ Σ i ∈ β ∂ ( w , b ) l ( i ) ( w , b ) 步骤:\\1.每次迭代中随机抽取以小批量\beta,它是由固定数量的训练样本组成的。 \\2.计算小批量的平均损失关于模型参数的导数(也可以称为梯度)。\\3.将梯度乘以一个预先确定的正数\eta,并从当前参数的值中减掉。\\数学公式表示(\partial表示偏导数):\\(\pmb{w},b)\leftarrow(\pmb{w},b)-\frac{\eta}{|\beta|}\Sigma_{i\in{\pmb{\beta}}}\partial_{(\pmb{w},b)}l^{(i)}(\pmb{w},b) 步骤:1.每次迭代中随机抽取以小批量β,它是由固定数量的训练样本组成的。2.计算小批量的平均损失关于模型参数的导数(也可以称为梯度)。3.将梯度乘以一个预先确定的正数η,并从当前参数的值中减掉。数学公式表示(表示偏导数)(w,b)(w,b)βηΣiβ(w,b)l(i)(w,b)

∣ β ∣ 表示每个小批量的样本数,也成为批量大小 ( B a t c h s i z e ) η 表示学习率 ( L e a r n i n g r a t e ) 以上两个的值通常是手动预先指定,并非通过模型训练得到。 可以调整但不在训练过程中更新的参数称为超参数 ( h y p e r p a r a m e t e r ) 调参 ( h y p e r p a r a m e t e r t u n i n g ) 是选择超参数的过程。 超参数通常是我们根据训练迭代结果来调整的, 而训练迭代结果是在独立的验证数据集( v a l i d a t i o n d a t a s e t )上评估得到的。 |\beta|表示每个小批量的样本数,也成为批量大小(Batch size)\\ \eta表示学习率(Learning rate)\\ 以上两个的值通常是手动预先指定,并非通过模型训练得到。\\ 可以调整但不在训练过程中更新的参数称为超参数(hyperparameter)\\ 调参(hyperparameter tuning)是选择超参数的过程。\\ 超参数通常是我们根据训练迭代结果来调整的,\\而训练迭代结果是在独立的验证数据集(validation dataset)上评估得到的。 β表示每个小批量的样本数,也成为批量大小(Batchsize)η表示学习率(Learningrate)以上两个的值通常是手动预先指定,并非通过模型训练得到。可以调整但不在训练过程中更新的参数称为超参数(hyperparameter)调参(hyperparametertuning)是选择超参数的过程。超参数通常是我们根据训练迭代结果来调整的,而训练迭代结果是在独立的验证数据集(validationdataset)上评估得到的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V2GCyd8w-1683168902161)(typoraJPG\1657609876386.png)]
如上图,对 w 和 b 求偏导, 当在最低点右侧时减去正的,当将到左侧时减去负的, 且减去的量越来越小,直到无限逼近中心点。 记录下模型参数的估计值,表示为 w ^ , b ^ 如上图,对\pmb{w}和b求偏导,\\当在最低点右侧时减去正的,当将到左侧时减去负的,\\且减去的量越来越小,直到无限逼近中心点。\\记录下模型参数的估计值,表示为\widehat{\pmb{w}},\widehat{b} 如上图,对wb求偏导,当在最低点右侧时减去正的,当将到左侧时减去负的,且减去的量越来越小,直到无限逼近中心点。记录下模型参数的估计值,表示为w ,b

3.1.1.5 用模型进行预测

预测值: y ^ = w ^ T x + b ^ 预测值:\widehat{y}=\widehat{\pmb{w}}^T\pmb{x}+\widehat{b} 预测值:y =w Tx+b

3.1.2 矢量化加速

将两个向量中的对应位置上的每个元素进行运算:

  1. 使用for循环分别遍历两个向量中的每个元素,并将响应位置上的元素进行运算。
  2. 依赖对"+“、”-“、”*“、”/"等运算符的调用。
import time
import numpy as np

# 计时器
class Timer:  #@save
    """记录多次运行时间"""
    def __init__(self):
        self.times = []
        self.start()

    def start(self):
        """启动计时器"""
        self.tik = time.time()

    def stop(self):
        """停止计时器并将时间记录在列表中"""
        self.times.append(time.time() - self.tik)
        return self.times[-1]

    def avg(self):
        """返回平均时间"""
        return sum(self.times) / len(self.times)

    def sum(self):
        """返回时间总和"""
        return sum(self.times)

    def cumsum(self):
        """返回累计时间"""
        return np.array(self.times).cumsum().tolist()
import torch
from Timer import Timer

# 创建两个全为1的10000维向量
n = 10000
a = torch.ones([n])
b = torch.ones([n])

# 使用for循环进行+操作
c = torch.zeros([n])
timer = Timer()
for i in range(n):
    c[i] = a[i] + b[i]
print(f'{timer.stop():.5f} sec')
# 结果:0.07852 sec

# 使用重载+直接运算
timer.start()
d = a + b
print(print(f'{timer.stop():.5f} sec'))
# 结果:0.00000 sec
# 这里只保留了小数点后5位,所以显示为0.00000

很明显第二种方法比第一种方法快的多。

3.1.3 正态分布与平方损失

正态分布(normal distribution):又称高斯分布(Gaussian distribution)
若随机变量 x 具有均值 μ 和方差 σ 2 ( 标准差 σ ) , 其中正态分布脉率密度函数如下: p ( x ) = 1 2 π σ 2 e − 1 2 σ 2 ( x − μ ) 2 若随机变量x具有均值\mu和方差\sigma^2(标准差\sigma),其中正态分布脉率密度函数如下:\\ p(x)=\frac{1}{\sqrt{2\pi\sigma^2}}e^{-\frac{1}{2\sigma^2}(x-\mu)^2} 若随机变量x具有均值μ和方差σ2(标准差σ),其中正态分布脉率密度函数如下:p(x)=2πσ2 1e2σ21(xμ)2
Python定义正态分布函数:

def normal(x,mu,sigma):
    p = 1/math.sqrt(2*math.pi*sigma**2)
    return p*np.exp(-0.5/sigma**2*(x-mu)**2)

可视化正态分布:

import math
import numpy as np
from matplotlib import pyplot as plt

x = np.arange(-7,7,0.01)

params = [(0,1),(0,2),(3,1)]

legends = []

for mu,sigma in params:
    plt.plot(x,normal(x,mu,sigma))
    legends.append('mean '+ str(mu) + ',std ' + str(sigma))
plt.title('Normal Distrubution')
plt.xlabel('x')
plt.ylabel('p(x)')
# plt.xlim(-7,7)
# # plt.ylim(0.0,0.4)
# # plt.xticks(np.linspace(-7, 7, 15))
# # plt.yticks(np.linspace(0.0,0.4, 5))
plt.legend(legends)
plt.show()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1GatuNFU-1683168902161)(F:\wangchong\Desktop\学习笔记\typoraJPG\normalDistribution.jpeg)]

均方误差损失函数(简称均方损失)可以用于线性回归的一个原因是: 我们假设了观测中包含噪声,其中噪声服从正态分布。 噪声正态分布如下式:
y = w T x + b + ε 其中噪声 ε ∽ N ( 0 , σ 2 ) y=\pmb{w}^T\pmb{x}+b+\varepsilon\\其中噪声\varepsilon\backsim{N(0,\sigma^2)} y=wTx+b+ε其中噪声εN(0,σ2)

因此,我们现在可以写出通过给定的 x 观测到特定 y 的似然( l i k e l i h o o d ): P ( y ∣ x ) = 1 2 π σ 2 e − 1 2 σ 2 ( y − w T x − b ) 2 ε − μ = y − w T x − b ,因为 μ = 0 ,所以 ε = y − w T x − b 根据极大似然估计法,参数 w 和 b 的最优值是使整个数据集的似然最大的值: P ( y ∣ X ) = Π i = 1 n p ( y ( i ) ∣ x ( i ) ) 因此,我们现在可以写出通过给定的\pmb{x}观测到特定y的似然(likelihood):\\ P(y|\pmb{x})=\frac{1}{\sqrt{2\pi\sigma^2}}e^{-\frac{1}{2\sigma^2}(y-\pmb{w}^T\pmb{x}-b)^2}\\\varepsilon-\mu=y-\pmb{w}^T\pmb{x}-b,因为\mu=0,所以\varepsilon=y-\pmb{w}^T\pmb{x}-b\\根据极大似然估计法,参数\pmb{w}和b的最优值是使整个数据集的似然最大的值:\\P(\pmb{y}|\pmb{X})=\Pi^n_{i=1}p(y^{(i)}|\pmb{x}^{(i)}) 因此,我们现在可以写出通过给定的x观测到特定y的似然(likelihood):P(yx)=2πσ2 1e2σ21(ywTxb)2εμ=ywTxb,因为μ=0,所以ε=ywTxb根据极大似然估计法,参数wb的最优值是使整个数据集的似然最大的值:P(yX)=Πi=1np(y(i)x(i))

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0sZY6caj-1683168902162)(F:\wangchong\Desktop\学习笔记\typoraJPG\snipaste_20230425_162016.jpeg)]

3.1.4 从线性回归到深度网络
3.1.4.1 神经网络图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9JpGwbhU-1683168902163)(F:\wangchong\Desktop\学习笔记\typoraJPG\singleneuron.svg)]

将线性回归模型描述为一个神经网络,注意:上图隐去了权重和偏置值。
输入数 d ( 或称特征维度 ) :输入层中的 x 1 , . . . x d 网络的输出为 o 1 , 输出层中的输出数是 1 。 上图中的网络层数为 1 。 全连接层 ( 稠密层 ) :每个输入与每个输出都相连。 输入数d(或称特征维度):输入层中的x_1,...x_d\\ 网络的输出为o_1,输出层中的输出数是1。\\ 上图中的网络层数为1。\\ 全连接层(稠密层):每个输入与每个输出都相连。 输入数d(或称特征维度):输入层中的x1,...xd网络的输出为o1,输出层中的输出数是1上图中的网络层数为1全连接层(稠密层):每个输入与每个输出都相连。

3.2 线性回归的从零开始实现

  1. 人造生成数据集,模拟真实数据
  2. 将数据打乱随机读取小批量数据
  3. 初始化模型参数w,b
  4. 定义线性回归模型:y = X*w+b
  5. 定义损失函数
  6. 定义优化算法
  7. 训练
import random
import torch
3.2.1 生成数据集

人造数据集:生成带有噪声的线性模型

生成一个包含1000个样本的数据集,以下生成的数据定义为真实值,每个样本包含从标准正态分布中采样的2个特征,合成数据集是一个矩阵:
X ∈ R 1000 × 2 我们使用线性模型参数 w = [ 2 , − 3.4 ] T 、 b = 4.2 和噪声 ε 生成数据集及其标签: y = X w + b + ε 我们使用 ε 服从均值为 0 ,标准差为 0.01 的正态分布。 \pmb{X}\in{R^{1000\times{2}}}\\ 我们使用线性模型参数\pmb{w}=[2,-3.4]^T、b=4.2和噪声\varepsilon生成数据集及其标签:\\ \pmb{y}=\pmb{Xw}+b+\varepsilon\\我们使用\varepsilon服从均值为0,标准差为0.01的正态分布。 XR1000×2我们使用线性模型参数w=[2,3.4]Tb=4.2和噪声ε生成数据集及其标签:y=Xw+b+ε我们使用ε服从均值为0,标准差为0.01的正态分布。

def synthetic_data(w,b,num_examples):
    """生成y=Xw+b+噪声"""
    # X 符合正态分布的矩阵
    X = torch.normal(0,1,(num_examples,len(w))) #num_examples:样本数,len(w):特征数
    y = torch.matmul(X,w)+b
    y += torch.normal(0,0.1,y.shape)
    return X,y.reshape((-1,1))

true_w = torch.tensor([2,-3.4])
true_b = 4.2

features,labels = synthetic_data(true_w,true_b,1000)
print('features:',features[0],"\nlabels:",labels[0])
# 结果:features: tensor([-1.3950,  0.4777]) 
#      labels: tensor([-0.1797])

plt.scatter(features[:,1].detach().numpy(),labels.detach().numpy(),1)
plt.show()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0X5uAd1Q-1683168902163)(F:\wangchong\Desktop\学习笔记\typoraJPG\snipaste_20230425_165958.jpeg)]

3.2.2 读取数据集
def data_iter(batch_size,features,labels):
    num_examples = len(features)
    indices = list(range(num_examples))
    # 这些样本是随机读取的,没有特定的顺序,下面是将indices打乱顺序
    random.shuffle(indices)
    for i in range(0,num_examples,batch_size):
        batch_indices = torch.tensor(indices[i:min(i+batch_size,num_examples)]) # 打乱顺序后的序号
        yield features[batch_indices],labels[batch_indices]

batch_size = 10
for X,y in data_iter(batch_size,features,labels):
    print(X,'\n',y)
    break
# 结果:tensor([[-0.3079, -0.6701],
#         [-0.5426, -0.1432],
#         [ 0.3706,  0.4730],
#         [-0.7258, -0.0523],
#         [ 0.2312,  0.7260],
#         [-0.4103,  0.2407],
#         [ 0.0080, -1.4716],
#         [-1.3461,  0.4402],
#         [ 0.0614,  1.4997],
#         [ 0.1043,  0.5275]]) 
#  tensor([[ 5.9774],
#         [ 3.6094],
#         [ 3.4363],
#         [ 2.9968],
#         [ 2.1160],
#         [ 2.5440],
#         [ 9.3166],
#         [ 0.0801],
#         [-0.8560],
#         [ 2.5673]])
3.2.3 初始化模型参数

通常开始用小批量随机梯度下降优化模型参数之前,需要先有一些参数。

通过从均值0、标准差为0.01的正态分布中采样随机数来初始化权重,并将偏置初始化为0.

w = torch.normal(0,0.01,size=(2,1),require_grad=True)
b = torch.normal(1,require_grad=True)
3.2.4 定义模型
def linreg(X,w,b):
    """线性回归模型"""
    return torch.matul(X,w)+b
3.2.5 定义损失函数
# 定义损失函数
def squared_loss(y_hat,y):
    """均方损失"""
    return (y_hat - y.reshape(y_hat.shape)) **2 /2
3.2.6 定义优化算法
# 定义优化算法
def sgd(params,lr,batch_size):
    """小批量梯度下降"""
    with torch.no_grad():
        for param in params:
            param -= lr* param.grad / batch_size
            param.grad.zero_()
3.2.7 训练
lr = 0.03
# 将相同的数据,迭代3次
num_epochs = 3

for epoch in range(num_epochs):
    for X , y in data_iter(batch_size,features,labels):
        l = squared_loss(linreg(X,w,b),y)
        l.sum().backward()
        sgd([w,b],lr,batch_size)
    with torch.no_grad():
        train_l = squared_loss(linreg(features,w,b),labels)
        print(f'epoch{epoch+1},loss{float(train_l.mean())}')
# 结果:epoch1,loss0.033154480159282684
#      epoch2,loss0.00012394246004987508
#      epoch3,loss4.756678390549496e-05
print(f'w的估计误差: {true_w - w.reshape(true_w.shape)}')
print(f'b的估计误差: {true_b - b}')
# 结果:w的估计误差: tensor([0.0009, 0.0004], grad_fn=<SubBackward0>)
#      b的估计误差: tensor([0.0005], grad_fn=<RsubBackward1>)

3.3 线性回归的简洁实现

3.3.1 生成数据集

与3.2节,生成数据集相同

import numpy as np
import torch
from torch.utils import data
from torch import nn

def synthetic_data(w,b,num_examples):
    """生成y=Xw+b+噪声"""
    # X 符合正态分布的矩阵
    X = torch.normal(0,1,(num_examples,len(w))) #num_examples:样本数,len(w):特征数
    y = torch.matmul(X,w)+b
    y += torch.normal(0,0.01,y.shape)
    return X,y.reshape((-1,1))
# 生成数据集
true_w = torch.tensor([2,-3.4])
true_b = 4.2
features , labels = synthetic_data(true_w,true_b,1000)
3.3.2 读取数据集

is_train表示是否希望数据迭代器对象在每个迭代周期内打乱数据。

# 读取数据集
def load_array(data_arrays,batch_size,is_train=True):
    dataset = data.TensorDataset(*data_arrays)
    return data.DataLoader(dataset,batch_size,shuffle=is_train)

batch_size = 10
data_iter = load_array((features , labels),batch_size)

使用iter构造Python迭代器,并使用next从迭代器中获取第一项。

print(next(iter(data_iter)))
# 结果:[tensor([[ 1.1345, -0.4203],
#         [ 1.5513,  1.0531],
#         [-0.1512, -0.2034],
#         [-0.1561,  0.9577],
#         [ 2.5947, -1.1197],
#         [-1.3840,  0.5783],
#         [-0.4311, -0.2485],
#         [ 1.0449,  0.0387],
#         [-1.0859,  0.1787],
#         [-1.5742,  1.1364]]),
#         tensor([[ 7.9105],
#         [ 3.7076],
#         [ 4.5969],
#         [ 0.6167],
#         [13.2144],
#         [-0.5314],
#         [ 4.1986],
#         [ 6.1676],
#         [ 1.4076],
#         [-2.8074]])]
3.3.3 定义模型

Sequential类将多个层串联在一起。当给定输入数据时,Sequential实例将数据传入第一层,然后将第一层的输出作为第二层的输入,以此类推。

PyTorch中,全连接层在Linear类中定义。将两个参数传递到nn.Linear中。 第一个指定输入特征形状,即2,第二个指定输出特征形状,输出特征形状为单个标量,因此为1。

# 定义模型,nn是神经网络的缩写
net = nn.Sequential(nn.Linear(2,1))
3.3.4 初始化模型参数

通过net[0]选择网络中的第一个图层, 然后使用weight.databias.data方法访问参数。 我们还可以使用替换方法normal_fill_来重写参数值。

# 初始化模型参数
net[0].weight.data.normal_(0,0.01)
net[0].bias.data.fill_(0)
3.3.5 定义损失函数

计算均方误差使用的是MSELoss类。

# 定义损失函数
loss = nn.MSELoss()
3.3.6 定义优化算法

指定优化的参数 (可通过net.parameters()从我们的模型中获得)以及优化算法所需的超参数字典。 小批量随机梯度下降只需要设置lr值,这里设置为0.03。

# 定义优化函数
trainer = torch.optim.SGD(net.parameters(),lr=0.03)
3.3.7 训练
# 训练
num_epochs = 3
for epoch in range(num_epochs):
    for X,y in data_iter:
        l = loss(net(X),y)
        trainer.zero_grad()
        l.backward()
        trainer.step()
    l = loss(net(features),labels)
    print(f'epoch{epoch+1},loss{l:f}')
    # 结果:epoch1,loss0.000300
    # epoch2,loss0.000095
    # epoch3,loss0.000095

3.4 softmax回归

3.4.1 分类问题

图像分类问题:
假设每次输入是一个 2 × 2 的灰度图像。我们可以用一个标量表示每个像素值, 每个图像对应四个特征 x 1 , x 2 , x 3 , x 4 。 此外,假设每个图像属于类别“猫”“鸡”和“狗”中的一个。 假设每次输入是一个2\times{2}的灰度图像。 我们可以用一个标量表示每个像素值,\\每个图像对应四个特征x_1,x_2,x_3,x_4。\\ 此外,假设每个图像属于类别“猫”“鸡”和“狗”中的一个。 假设每次输入是一个2×2的灰度图像。我们可以用一个标量表示每个像素值,每个图像对应四个特征x1,x2,x3,x4此外,假设每个图像属于类别”“中的一个。
独热编码(one-hot encoding):
类别对应的分量设置为 1 ,其他所有分量设置为 0 。 类别: ( 猫,鸡,狗 ) 猫: ( 1 , 0 , 0 ) 鸡: ( 0 , 1 , 0 ) 狗: ( 0 , 0 , 1 ) y ∈ [ ( 1 , 0 , 0 ) , ( 0 , 1 , 0 ) , ( 0 , 0 , 1 ) ] 类别对应的分量设置为1,其他所有分量设置为0。\\ 类别:(猫,鸡,狗)\\ 猫:(1,0,0)\\ 鸡:(0,1,0)\\ 狗:(0,0,1)\\ y\in{[(1,0,0),(0,1,0),(0,0,1)]} 类别对应的分量设置为1,其他所有分量设置为0类别:(猫,鸡,狗)猫:(1,0,0)鸡:(0,1,0)狗:(0,0,1)y[(1,0,0),(0,1,0),(0,0,1)]

3.4.2 网络架构

分类问题:有几个分类对应几个输出。

对于“猫”“鸡”和“狗”的分类中的有4个特征和3个类别,所以需要12个标量来表示权重(带下标的w,注意是标量,合成矩阵为3x4的矩阵),3个标量来表示偏置(带下标的b,注意是标量,合成向量为3x1)。
每个输入计算三个未规范化的预测 ( ‘ l o g i t ‘ ) : o 1 、 o 2 和 o 3 : o 1 = x 1 w 11 + x 2 w 12 + x 3 w 13 + x 4 w 14 + b 1 o 2 = x 1 w 21 + x 2 w 22 + x 3 w 23 + x 4 w 24 + b 2 o 3 = x 1 w 31 + x 2 w 32 + x 3 w 33 + x 4 w 34 + b 3 o = W X T + b W ∈ R 3 × 4 , X ∈ R n × 4 : ( n 为样本的多少 b a t c h − s i z e 的大小 ) , b ∈ R 3 × 1 每个输入计算三个未规范化的预测(`logit`):o_1、o_2和o_3:\\ o_1=x_1w_{11}+x_2w_{12}+x_3w_{13}+x_4w_{14}+b_1\\ o_2=x_1w_{21}+x_2w_{22}+x_3w_{23}+x_4w_{24}+b_2\\ o_3=x_1w_{31}+x_2w_{32}+x_3w_{33}+x_4w_{34}+b_3\\ o = \pmb{W}\pmb{X}^T+\pmb{b}\\\pmb{W}\in{R}^{3\times{4}},\pmb{X}\in{R}^{n\times{4}}:(n为样本的多少batch-size的大小),b\in{R}^{3\times{1}} 每个输入计算三个未规范化的预测(logit):o1o2o3:o1=x1w11+x2w12+x3w13+x4w14+b1o2=x1w21+x2w22+x3w23+x4w24+b2o3=x1w31+x2w32+x3w33+x4w34+b3o=WXT+bWR3×4,XRn×4(n为样本的多少batchsize的大小),bR3×1
与线性回归一样,softmax回归也是一个单层(全连接)神经网络。

神经网络

3.4.3 全连接层的参数开销

对于任何具有 d 个输入和 q 个输出的全连接层,参数开销为 Θ ( d q ) , 但是可以减少到 Θ ( d q n ) , 其中超参数 n 可以由我们自由指定。 对于任何具有d个输入和q个输出的全连接层,参数开销为\Theta(dq),\\但是可以减少到\Theta(\frac{dq}{n}),其中超参数n可以由我们自由指定。 对于任何具有d个输入和q个输出的全连接层,参数开销为Θ(dq),但是可以减少到Θ(ndq),其中超参数n可以由我们自由指定。

3.4.4 softmax运算

为了得到预测结果,我们将设置一个阈值,选择具有最大概率的标签。

在分类器输出0.5的所有样本中,我们希望这些样本是刚好有一半实际上属于预测的类别。 这个属性叫做校准calibration)。

softmax函数能够将未规范化的预测变换为非负数并且总和为1,同时让模型保持 可导的性质。步骤如下:

  1. 对为规范化的预测求幂(保持输出非负);
  2. 求幂之后的结果除以总和(保持输出和为1);

y ^ = s o f t m a x ( o ) ,其中 y ^ j = e o j Σ k e o k 对于所有的 j 总有 0 ≤ y ^ j ≤ 1 , 因此 y ^ 可以视为一个正确的概率分布。 s o f t m a x 运算不会改变未规范化的预测 o 之间的大小次序,只会确定分配给每个类别的概率。 \widehat{\pmb{y}}=softmax(\pmb{o}) ,其中\widehat{y}_j=\frac{e^{o _j}}{\Sigma_ke^{o_k}}\\对于所有的j总有0\leq\widehat{y}_j\leq1,因此\widehat{\pmb{y}}可以视为一个正确的概率分布。\\softmax运算不会改变未规范化的预测\pmb{o}之间的大小次序,只会确定分配给每个类别的概率。 y =softmax(o),其中y j=Σkeokeoj对于所有的j总有0y j1,因此y 可以视为一个正确的概率分布。softmax运算不会改变未规范化的预测o之间的大小次序,只会确定分配给每个类别的概率。

尽管softmax是一个非线性函数,但softmax回归的输出仍然由输入特征的仿射变换决定。 因此,softmax回归是一个线性模型linear model)。

3.4.5 小批量样本矢量化

为了提高计算效率并充分利用GPU(不使用for循环,两个向量)。
假设我们读取了一个批量的样本 X ,其中特征维度(输入数量)为 d ,批量大小为 n 。 此外,假设我们在输出中有 q 个类别。那么小批量样本 : 特征为 X ∈ R n × d , 权重为 W ∈ R d × q , 偏置为 b ∈ R 1 × q 。 s o f t m a x 回归的矢量计算表达式为: O = X W + b Y ^ = s o f t m a x ( O ) 假设我们读取了一个批量的样本\pmb{X},其中特征维度(输入数量)为d,批量大小为n。\\ 此外,假设我们在输出中有q个类别。 那么小批量样本:\\特征为\pmb{X}\in{R^{n\times{d}}},\\ 权重为\pmb{W}\in{R^{d\times{q}}},\\ 偏置为\pmb{b}\in{R^{1\times{q}}}。\\ softmax回归的矢量计算表达式为:\\ \pmb{O}=\pmb{XW}+\pmb{b}\\ \widehat{Y}=softmax(\pmb{O}) 假设我们读取了一个批量的样本X,其中特征维度(输入数量)为d,批量大小为n此外,假设我们在输出中有q个类别。那么小批量样本:特征为XRn×d权重为WRd×q偏置为bR1×qsoftmax回归的矢量计算表达式为:O=XW+bY =softmax(O)
小批量样本的矢量化加快了XW的矩阵-向量乘法。由于X中的每一行代表一个数据样本。

3.4.6 损失函数

最大似然估计。

3.4.6.1 对数似然(不太懂!)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9V1nI8Jt-1683168902164)(F:\wangchong\Desktop\学习笔记\typoraJPG\softmax.jpeg)]

3.4.6.2 softmax及其导数

将 y ^ = s o f t m a x ( o ) ,其中 y ^ j = e o j Σ k e o k    代入    l ( y , y ^ ) = − Σ j = 1 q y i l o g y ^ j l ( y , y ^ ) = − Σ j = 1 q y i l o g e o j Σ k = 1 q e o k = Σ j = 1 q y i l o g Σ k = 1 q e o k − Σ j = 1 q y j o j = l o g Σ k = 1 q e o k − Σ j = 1 q y j o j 考虑相对于任何未规范化的预测 o j 的导数,我们得到: ∂ o j l ( y , y ^ ) = e o j Σ k = 1 q e o k − y i = s o f t m a x ( o ) j − y i 将\widehat{\pmb{y}}=softmax(\pmb{o}) ,其中\widehat{y}_j=\frac{e^{o_j}}{\Sigma_ke^{o_k}}\;代入\;l(y,\widehat{y})=-\Sigma_{j=1}^qy_ilog\widehat{y}_j\\ l(y,\widehat{y})=-\Sigma_{j=1}^qy_ilog\frac{e^{o_j}}{\Sigma_{k=1}^qe^{o_k}}\\ =\Sigma_{j=1}^qy_ilog{\Sigma_{k=1}^qe^{o_k}}-\Sigma_{j=1}^qy_jo_j\\ =log{\Sigma_{k=1}^qe^{o_k}}-\Sigma_{j=1}^qy_jo_j\\ 考虑相对于任何未规范化的预测o_j的导数,我们得到:\\ \partial_{o_j}l(y,\widehat{y})=\frac{e^{o_j}}{\Sigma_{k=1}^qe^{o_k}}-y_i=softmax(o)_j-y_i y =softmax(o),其中y j=Σkeokeoj代入l(y,y )=Σj=1qyilogy jl(y,y )=Σj=1qyilogΣk=1qeokeoj=Σj=1qyilogΣk=1qeokΣj=1qyjoj=logΣk=1qeokΣj=1qyjoj考虑相对于任何未规范化的预测oj的导数,我们得到:ojl(y,y )=Σk=1qeokeojyi=softmax(o)jyi

3.4.7 信息论基础

信息论(information theory)涉及编码、解码、发送以及尽可能简洁地处理信息或数据。

3.4.7.1 熵

信息论的核心思想是量化数据中的信息内容。在信息论中,该数值被称为分布P的熵(entropy)。可以通过以下方程得到:
H [ P ] = Σ j − P ( j ) l o g P ( j ) . 信息论的基本定理之一指出,为了对从分布 p 中随机抽取的数据进行编码, 我们至少需要 H [ P ] " 纳特 ( n a t ) " 对其进行编码。 " 纳特 " 相当于比特 ( b i t ) ,但是对数底为 e 而不是 2. 因此一个纳特是 1 l o g ( 2 ) ≃ 1.44 b i t H[P] = \Sigma_j-P(j)logP(j).\\ \\ 信息论的基本定理之一指出,为了对从分布p中随机抽取的数据进行编码,\\我们至少需要H[P]"纳特(nat)"对其进行编码。\\"纳特"相当于比特(bit),但是对数底为e而不是2.因此一个纳特是\frac{1}{log(2)}\simeq{1.44}bit H[P]=ΣjP(j)logP(j).信息论的基本定理之一指出,为了对从分布p中随机抽取的数据进行编码,我们至少需要H[P]"纳特(nat)"对其进行编码。"纳特"相当于比特(bit),但是对数底为e而不是2.因此一个纳特是log(2)11.44bit

3.4.7.2 信息量

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ctQmpx0B-1683168902164)(F:\wangchong\Desktop\学习笔记\typoraJPG\information.jpeg)]

3.4.7.3 重新审视交叉熵

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AsuFnFh9-1683168902164)(F:\wangchong\Desktop\学习笔记\typoraJPG\snipaste_20230428_165946.jpeg)]

3.4.8 模型预测和估计

在训练softmax回归模型后,给出任何样本特征,我们可以预测每个输出类别的概率。 通常我们使用预测概率最高的类别作为输出类别。 如果预测与实际类别(标签)一致,则预测是正确的。 在接下来的实验中,我们将使用精度(accuracy)来评估模型的性能。 精度等于正确预测数与预测总数之间的比率。

3.4.9 小结
  • softmax运算获取一个向量并将其映射为概率。
  • softmax回归适用于分类问题,它使用了softmax运算中输出类别的概率分布。
  • 交叉熵是一个衡量两个概率分布之间差异的很好的度量,它测量给定模型编码数据所需的比特数。

3.5 图像分类数据集

import torch
import torchvision
from torch.utils import data
from torchvision import transforms
from matplotlib import pyplot as plt
from d2l import torch as d2l
3.5.1 读取数据集
# 读取数据集
# 通过ToTensor实例将图像数据从PIL类型转换成32位浮点数格式
# 并除以225使得所有数值均在0~1之间
trans = transforms.ToTensor()
mnist_train = torchvision.datasets.FashionMNIST(
    root="./data",
    train=True,
    transform=trans,
    download=False)
mnist_test = torchvision.datasets.FashionMNIST(
    root="./data",
    train=False,
    transform=trans,
    download=False)


Fashion-MNIST由10个类别的图像组成,每个类别由训练数据集(train dataset)中的6000张图像和测试数据集(test dataset)中的1000张图像组成。 因此,训练集和测试集分别包含60000和10000张图像。 测试数据集不会用于训练,只用于评估模型性能。

print(len(mnist_train),len(mnist_test))
# 结果:60000 10000

$$
每个输入图像的高度和宽度均为28像素。数据集由灰度图像组成,其通道数为1。\为了简洁起见,本书将高度h像素、宽度w像素图像的形状记为h\times{w}或(h,w)

$$

print(mnist_train[0][0].shape)
# 结果:torch.Size([1, 28, 28])

Fashion-MNIST中包含的10个类别,分别为t-shirt(T恤)、trouser(裤子)、pullover(套衫)、dress(连衣裙)、coat(外套)、sandal(凉鞋)、shirt(衬衫)、sneaker(运动鞋)、bag(包)和ankle boot(短靴)。 以下函数用于在数字标签索引及其文本名称之间进行转换。

def get_fashion_mnist_labels(labels):
    """返回Fashion-MNIST数据集的文本标签"""
    text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat',
                   'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']
    return [text_labels[int(i)] for i in labels]

可视化样本:

def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5): #@save
    # scale:图片与标签的比例
    """绘制图像列表"""
    figsize = (num_cols * scale, num_rows * scale)
    _, axes = plt.subplots(num_rows, num_cols, figsize=figsize)
    axes = axes.flatten()
    for i, (ax, img) in enumerate(zip(axes, imgs)):
        if torch.is_tensor(img):
            # 图片张量
            ax.imshow(img.numpy())
        else:
            # PIL图片
            ax.imshow(img)
        ax.axes.get_xaxis().set_visible(True)
        ax.axes.get_yaxis().set_visible(True)
        if titles:
            ax.set_title(titles[i])
    return axes
# 将60000个训练集,分成每18个一组,以下为第1组:仅作为以下显示使用,并不作为真正的训练
X, y = next(iter(data.DataLoader(mnist_train, batch_size=18)))
# X.reshape(18, 28, 28):18张图片,每张图片像素为28X28,2:两行,9:九列
show_images(X.reshape(18, 28, 28), 2, 9, titles=get_fashion_mnist_labels(y));
plt.show()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xQ1wphFg-1683168902165)(F:\wangchong\Desktop\学习笔记\typoraJPG\1682831384157.png)]

3.5.2 读取小批量

为了使我们在读取训练集和测试集时更容易,使用内置的数据迭代器。在每次迭代中,数据加载器每次都会读取一小批量数据,大小为batch_size。 通过内置数据迭代器,随机打乱了所有样本,从而无偏见地读取小批量。

batch_size = 256
def get_dataloader_workers():   #@save
    """使用4个进程来读取数据"""
    return 0
# 将训练集分为以256个为一组,并放入迭代器中,仅作为以下观察时间使用并不作为训练使用
train_iter = data.DataLoader(mnist_train,batch_size,shuffle=True,num_workers=get_dataloader_workers())

读取数据所需时间:

timer = d2l.Timer()
for X,y in train_iter:
    continue
print(f'{timer.stop():.2f} sec')
# 结果:3.81 sec
3.5.3 整合所有组件

定义load_data_fashion_mnist函数,用于获取和读取Fashion-MNIST数据集。 这个函数返回训练集和验证集的数据迭代器。 此外,这个函数还接受一个可选参数resize,用来将图像大小调整为另一种形状。

# 整合所有组件
def load_data_fashion_mnist(batch_size,resize=None):    #@save
    """下载Fashion-MNIST数据集,然后将其加载到内存中"""
    trans = [transforms.ToTensor()]
    if resize:
        trans.insert(0,transforms.Resize(resize))
    trans = transforms.Compose(trans)
    mnist_train = torchvision.datasets.FashionMNIST(
        root="./data",
        train=True,
        transform=trans,
        download=False)
    mnist_test = torchvision.datasets.FashionMNIST(
        root="./data",
        train=False,
        transform=trans,
        download=False)
    return (data.DataLoader(mnist_train,batch_size,shuffle=True,num_workers=get_dataloader_workers()),
            data.DataLoader(mnist_test, batch_size, shuffle=True, num_workers=get_dataloader_workers()))

下面,我们通过指定resize参数来测试load_data_fashion_mnist函数的图像大小调整功能。

train_iter, test_iter = load_data_fashion_mnist(32,resize=64)
for X,y in train_iter:
    print(X.shape,X.dtype,y.shape,y.dtype)
    break
# 结果:torch.Size([32, 1, 64, 64]) torch.float32 torch.Size([32]) torch.int64

3.6 softmax回归的从零开始实现

读取Fashion-MNIST数据集,并设置数据迭代器的批量大小为256。


3.5.2 读取小批量

为了使我们在读取训练集和测试集时更容易,使用内置的数据迭代器。在每次迭代中,数据加载器每次都会读取一小批量数据,大小为batch_size。 通过内置数据迭代器,随机打乱了所有样本,从而无偏见地读取小批量。

batch_size = 256
def get_dataloader_workers():   #@save
    """使用4个进程来读取数据"""
    return 0
# 将训练集分为以256个为一组,并放入迭代器中,仅作为以下观察时间使用并不作为训练使用
train_iter = data.DataLoader(mnist_train,batch_size,shuffle=True,num_workers=get_dataloader_workers())

读取数据所需时间:

timer = d2l.Timer()
for X,y in train_iter:
    continue
print(f'{timer.stop():.2f} sec')
# 结果:3.81 sec
3.5.3 整合所有组件

定义load_data_fashion_mnist函数,用于获取和读取Fashion-MNIST数据集。 这个函数返回训练集和验证集的数据迭代器。 此外,这个函数还接受一个可选参数resize,用来将图像大小调整为另一种形状。

# 整合所有组件
def load_data_fashion_mnist(batch_size,resize=None):    #@save
    """下载Fashion-MNIST数据集,然后将其加载到内存中"""
    trans = [transforms.ToTensor()]
    if resize:
        trans.insert(0,transforms.Resize(resize))
    trans = transforms.Compose(trans)
    mnist_train = torchvision.datasets.FashionMNIST(
        root="./data",
        train=True,
        transform=trans,
        download=False)
    mnist_test = torchvision.datasets.FashionMNIST(
        root="./data",
        train=False,
        transform=trans,
        download=False)
    return (data.DataLoader(mnist_train,batch_size,shuffle=True,num_workers=get_dataloader_workers()),
            data.DataLoader(mnist_test, batch_size, shuffle=True, num_workers=get_dataloader_workers()))

下面,我们通过指定resize参数来测试load_data_fashion_mnist函数的图像大小调整功能。

train_iter, test_iter = load_data_fashion_mnist(32,resize=64)
for X,y in train_iter:
    print(X.shape,X.dtype,y.shape,y.dtype)
    break
# 结果:torch.Size([32, 1, 64, 64]) torch.float32 torch.Size([32]) torch.int64

3.6 softmax回归的从零开始实现

读取Fashion-MNIST数据集,并设置数据迭代器的批量大小为256。


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值