机器学习基础 多层感知机
机器学习基础 多层感知机
文章目录
- 机器学习基础 多层感知机
- 1. 多层感知机
- 1.1 线性模型的失效
- 1.2 感知机
- 1.3 感知机的收敛定理
- 1.4 从线性到非线性
- 1.5 多层感知机的定义和实现
- 参考
1. 多层感知机
1.1 线性模型的失效
在李沐《动手学深度学习》中有这样的描述:
我们想要根据体温预测死亡率。 对体温高于37摄氏度的人来说,温度越高风险越大。 然而,对体温低于37摄氏度的人来说,温度越高风险就越低。 在这种情况下,我们也可以通过一些巧妙的预处理来解决问题。 例如,我们可以使用与37摄氏度的距离作为特征。
有些现实问题确实可以通过线性关系来近似建模,甚至在一些简单的非线性场景中可以通过特征工程,如取对数、取绝对差进一步解决。但在如图像分类等复杂场景中,线性模型就失效了。
比如,在对猫和狗的图像进行分类时,如果我改变图像中的一个像素(比如让它更亮),是否总是会让这张图片更像狗,或者更像猫呢?在线性模型中,假设每个像素对分类结果的影响是独立的、直接的、线性的,可以简单地将所有像素的加权和相加就能做出判断。但显然,在图像分类中行不通,这是因为任何像素的重要性都以复杂的方式取决于该像素的上下文。比如,一小块黑色像素在狗的鼻子上,可能很重要。但一模一样的小块黑色像素在背景上,可能毫无用处。
那么,我们或许可以找到一种更加高级的特征表达,比如“边缘”、“形状”、“局部结构”,分类可能会更简单。然而,这种高级特征的表示我们难以像之前说的特征工程那样设计出来,因为太复杂了。
1.2 感知机
感知机是一种简单的线性模型,给定输入 x \boldsymbol{x} x,权重 w \boldsymbol{w} w 和偏移 b b b,感知机的输出为:
o = σ ( ⟨ w , x ⟩ + b ) , (1) o=\sigma(\lang \boldsymbol{w},\boldsymbol{x}\rang +b),\tag{1} o=σ(⟨w,x⟩+b),(1)
σ ( x ) { 1 , if x > 0 , − 1 , otherwise. (2) \sigma(x) \begin{cases} 1,&\text{if}\quad x>0,\\[4pt] -1,&\text{otherwise.} \end{cases}\tag{2} σ(x){1,−1,ifx>0,otherwise.(2)在这里插入图片描述
感知机的训练是这样的,首先初始化一个样本的权重 w w w 和偏移 b b b 都为0,然后重复以下操作:如果 :
y i [ ⟨ w , x i ⟩ + b ] ≤ 0 , (3) y_i[\lang w,x_i\rang+b]\le 0,\tag{3} yi[⟨w,xi⟩+b]≤0,(3)
那么使用以下方式更新参数:
w ← w + y i x i , b ← b + y i , (4) w\leftarrow w+y_ix_i,\quad b\leftarrow b+y_i,\tag{4} w←w+yixi,b←b+yi,(4)
直到所有类别都分类正确。
感知机等价于批量大小为 1 的梯度下降,因为每次都一个样本进行梯度下降。感知机的损失函数为:
l ( y , x , w ) = max ( 0 , − y ⟨ w , x ⟩ ) . (5) \mathscr{l}(y,\boldsymbol{x},\boldsymbol{w})=\max(0,-y\lang\boldsymbol{w},\boldsymbol{x}\rang).\tag{5} l(y,x,w)=max(0,−y⟨w,x⟩).(5)
如图 2 所示,感知机拟合出来的是一条直线,每次加入一个训练样本,每次都去调整这条直线。那么,如何证明对于一个线性可分的数据集,感知机经过有限次迭代可以得到一个将训练数据集完全正确划分的分离超平面呢?
1.3 感知机的收敛定理
定义如下训练样本:
T = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , ⋯ , ( x N , y N ) } , (6) T=\{(x_1,y_1),(x_2,y_2),\cdots,(x_N,y_N)\},\tag{6} T={(x1,y1),(x2,y2),⋯,(xN,yN)},(6)
每个输入向量 x i ∈ R n x_i\in\mathbb{R}^n xi∈Rn, y i ∈ { − 1 , + 1 } y_i\in\{-1,+1\} yi∈{−1,+1}, i = 1 , 2 , ⋯ , N i=1,2,\cdots,N i=1,2,⋯,N,把偏置 b b b 合并进权重向量,定义:
x ^ i = ( x i 1 ) ∈ R n + 1 . (7) \hat{x}_i= \begin{pmatrix} x_i\\[4pt]1 \end{pmatrix}\in\mathbb{R}^{n+1}.\tag{7} x^i=(xi1)∈Rn+1.(7)
同样,将权重向量 w ∈ R n w\in\mathbb{R}^n w∈Rn 与偏置 b ∈ R b\in\mathbb{R} b∈R 合并为:
w ^ = ( w b ) ∈ R n + 1 . (8) \hat{w}= \begin{pmatrix} w\\[4pt]b \end{pmatrix}\in\mathbb{R}^{n+1}.\tag{8} w^=(wb)∈Rn+1.(8)
分类决策依赖于符号函数:
f ( x ) = sign ( w T x + b ) = sign ( w ^ T x ^ ) . (9) f(x)=\text{sign}(w^Tx+b)=\text{sign}(\hat{w}^T\hat{x}).\tag{9} f(x)=sign(wTx+b)=sign(w^Tx^).(9)
假设数据集线性可分,那么一定存在一个超平面,使得对所有的 i i i 都有:
y i ( w o p t T x i + b o p t ) > 0. (10) y_i(w_{opt}^Tx_i+b_{opt})>0.\tag{10} yi(woptTxi+bopt)>0.(10)
可以把最优解 ( w o p t , b o p t ) (w_{opt},b_{opt}) (wopt,bopt) 规范化,使得 ∥ w ^ o p t ∥ = 1 \lVert \hat{w}_{opt} \rVert=1 ∥w^opt∥=1,定义间隔 γ \gamma γ 为:
γ = min 1 ≤ i ≤ N y i ( w ^ o p t T x ^ i ) > 0 , (11) \gamma = \underset{1\le i\le N}{\min}\ y_i(\hat{w}_{opt}^T\hat{x}_i)>0,\tag{11} γ=1≤i≤Nmin yi(w^optTx^i)>0,(11)
这表示所有样本到超平面的“带符号距离”的最小值,间隔越大,分类越“稳健”。
定义样本半径 R R R 为:
R = max 1 ≤ i ≤ N ∥ x ^ i ∥ , (12) R=\underset{1\le i\le N}{\max}\ \lVert \hat{x}_i \rVert,\tag{12} R=1≤i≤Nmax ∥x^i∥,(12)
可知,样本半径是离原点最远的样本点到原点的距离。
在初始化阶段,令 w ^ 0 = 0 \hat{w}_0=0 w^0=0,在第 k k k 步,如果样本 ( x i , y i ) (x_i,y_i) (xi,yi) 被误分类,则:
y i ( w ^ k − 1 T x ^ i ) ≤ 0. (13) y_i(\hat{w}_{k-1}^T\hat{x}_i)\le 0.\tag{13} yi(w^k−1Tx^i)≤0.(13)
对误分类样本 ( x i , y i ) (x_i,y_i) (xi,yi) 进行更新:
w ^ t = w ^ k − 1 + η y i x ^ i , (14) \hat{w}_t=\hat{w}_{k-1}+\eta y_i\hat{x}_i,\tag{14} w^t=w^k−1+ηyix^i,(14)
其中, η > 0 \eta>0 η>0 是学习率,需注意的是,在每 k k k 轮,感知机都随机取一个样本对参数进行更新,而不是取批量的样本更新。
我们要证明的是,误分类次数 k k k 是有限的,并且满足:
k ≤ ( R γ ) 2 . (15) k\le (\frac{R}{\gamma})^2.\tag{15} k≤(γR)2.(15)
第一步需要证明方向增益下界:
w ^ k ⋅ w ^ o p t ≥ k η γ . (16) \hat{w}_k\cdot \hat{w}_{opt}\ge k\eta \gamma.\tag{16} w^k⋅w^opt≥kηγ.(16)
由于:
w ^ k T w ^ o p t = w ^ k − 1 T w ^ o p t + η y i x ^ i T w ^ o p t , (17) \hat{w}_k^T\hat{w}_{opt}=\hat{w}_{k-1}^T\hat{w}_{opt}+\eta y_i \hat{x}_{i}^T\hat{w}_{opt},\tag{17} w^kTw^opt=w^k−1Tw^opt+ηyix^iTw^opt,(17)
由式 (11) 可知, γ \gamma γ 被定义为所有样本 y i ( w ^ o p t T x ^ i ) y_i(\hat{w}_{opt}^T\hat{x}_i) yi(w^optTx^i) 的最小值,由于数据集线性可分,则必然存在一个超平面使得:
y i ( w ^ o p t T x ^ i ) ≥ γ > 0 , ∀ i , (18) y_i(\hat{w}_{opt}^T\hat{x}_i)\ge \gamma>0,\quad \forall i,\tag{18} yi(w^optTx^i)≥γ>0,∀i,(18)
具体地, w ^ o p t T x ^ i \hat{w}_{opt}^T\hat{x}_i w^optTx^i 的几何意义为样本点 x ^ i \hat{x}_i x^i 到超平面的距离的投影,用来衡量距离超平面有多远。至于为什么这是点到超平面的距离,可以回忆一下高中的距离公式,结合前面的向量合并就可以理解了:
d = ∣ w ⋅ x i + b ∣ ∥ w ∥ . (19) d=\frac{\lvert w\cdot x_i+b\rvert}{\lVert w \rVert}.\tag{19} d=∥w∥∣w⋅xi+b∣.(19)
因此,可以把公式 (17) 化简为:
w ^ k T w ^ o p t ≥ w ^ k − 1 T w ^ o p t + η γ , (20) \hat{w}_k^T\hat{w}_{opt}\ge \hat{w}_{k-1}^T\hat{w}_{opt}+\eta \gamma,\tag{20} w^kTw^opt≥w^k−1Tw^opt+ηγ,(20)
递推 k k k 次后可得:
w ^ k T w ^ o p t ≥ k η γ . (21) \hat{w}_k^T\hat{w}_{opt}\ge k\eta\gamma.\tag{21} w^kTw^opt≥kηγ.(21)
接下来证明范数增长上界:
∥ w ^ k T ∥ 2 ≤ k η 2 R 2 . (22) \lVert\hat{w}_k^T\rVert^2\le k\eta^2R^2.\tag{22} ∥w^kT∥2≤kη2R2.(22)
由公式 (14) 可得:
∥ w ^ k ∥ 2 = ∥ w ^ k − 1 ∥ 2 + 2 η y i w ^ k − 1 T x ^ i + η 2 ∥ x ^ i ∥ 2 , (23) \lVert \hat{w}_k \rVert^2=\lVert \hat{w}_{k-1} \rVert^2+2\eta y_i \hat{w}_{k-1}^T\hat{x}_{i}+\eta^2\lVert \hat{x}_i \rVert^2,\tag{23} ∥w^k∥2=∥w^k−1∥2+2ηyiw^k−1Tx^i+η2∥x^i∥2,(23)
上式中,因为 y i 2 = 1 y_i^2=1 yi2=1,所以没写。由于误分类时有:
y i w ^ k − 1 T x ^ i ≤ 0 , (24) y_i\hat{w}_{k-1}^T\hat{x}_i\le 0,\tag{24} yiw^k−1Tx^i≤0,(24)
结合式 (12) 可将上式 (23) 化简为:
∥ w ^ k ∥ 2 ≤ ∥ w ^ k − 1 ∥ 2 + η 2 ∥ x ^ i ∥ 2 ≤ ∥ w ^ k − 1 ∥ 2 + η 2 R 2 . (25) \lVert \hat{w}_k \rVert^2\le\lVert \hat{w}_{k-1} \rVert^2+\eta^2\lVert \hat{x}_i \rVert^2\le \lVert \hat{w}_{k-1} \rVert^2+\eta^2R^2.\tag{25} ∥w^k∥2≤∥w^k−1∥2+η2∥x^i∥2≤∥w^k−1∥2+η2R2.(25)
同样迭代 k k k 步可得:
∥ w ^ k ∥ 2 ≤ k η 2 R 2 . (26) \lVert \hat{w}_k \rVert^2\le k\eta^2R^2.\tag{26} ∥w^k∥2≤kη2R2.(26)
由柯西 - 施瓦茨不等式得:
w ^ k T w ^ o p t ≤ ∥ w ^ k ∥ ∥ w ^ o p t ∥ , (27) \hat{w}_k^T\hat{w}_{opt}\le \lVert \hat{w}_k \rVert\lVert \hat{w}_{opt} \rVert,\tag{27} w^kTw^opt≤∥w^k∥∥w^opt∥,(27)
因为我们在之前规范化了 ∥ w ^ o p t ∥ = 1 \lVert \hat{w}_{opt} \rVert=1 ∥w^opt∥=1,所以:
w ^ k T w ^ o p t ≤ ∥ w ^ k ∥ . (28) \hat{w}_k^T\hat{w}_{opt}\le\lVert \hat{w}_k \rVert.\tag{28} w^kTw^opt≤∥w^k∥.(28)
结合式 (16) 和式 (22) 可得:
k η γ ≤ w ^ k T w ^ o p t ≤ ∥ w ^ k ∥ ≤ k η R , (29) k\eta\gamma\le \hat{w}_{k}^T\hat{w}_{opt}\le \lVert \hat{w}_k\rVert\le \sqrt{k}\eta R,\tag{29} kηγ≤w^kTw^opt≤∥w^k∥≤kηR,(29)
解上面的不等式得:
k η γ ≤ k η R ⇒ k ≤ ( R γ ) 2 . (30) k\eta\gamma\le\sqrt{k}\eta R\Rightarrow k\le(\frac{R}{\gamma})^2.\tag{30} kηγ≤kηR⇒k≤(γR)2.(30)
1.4 从线性到非线性
对于线性可分的数据集在上个章节进行了充分的讨论,但是对于图 3 这张 XOR 问题,感知机就无法解决了。那么,如何学习 XOR 问题呢?如图 4 所示,学习两个直线:黄线和蓝线,然后取异或得到最后结果。
对于感知机,如何实现这一点呢?答案是加入隐藏层,变成多层感知机。
1.5 多层感知机的定义和实现
假设现在是一个单分类问题,输入为 x ∈ R n \boldsymbol{x}\in \mathbb{R}^n x∈Rn,隐藏层 W 1 ∈ R m × n \boldsymbol{W}_1\in\mathbb{R}^{m\times n} W1∈Rm×n, b 1 ∈ R m \boldsymbol{b}_1\in\mathbb{R}^m b1∈Rm,这里的 m m m 是隐藏层的数量,输出层 w 2 ∈ R m \boldsymbol{w}_2\in\mathbb{R}^m w2∈Rm, b 2 ∈ R b_2\in\mathbb{R} b2∈R,有以下定义:
h = σ ( W 1 x + b 1 ) , o = w 2 T h + b 2 , (31) \begin{align} \boldsymbol{h}&=\sigma(\boldsymbol{W}_1\boldsymbol{x}+\boldsymbol{b}_1),\\[4pt] o&=\boldsymbol{w}_2^T\boldsymbol{h}+b_2, \end{align}\tag{31} ho=σ(W1x+b1),=w2Th+b2,(31)
其中, σ \sigma σ 是按元素的激活函数。
可知,隐藏表示 h ∈ R m \boldsymbol{h}\in\mathbb{R}^{m} h∈Rm, o ∈ R o\in\mathbb{R} o∈R。
多分类也是同理,变换一下维度即可推导出来。代码实现来源于李沐《动手学深度学习》:4.1. 多层感知机。
import torchvision
import torch
from torch import nn
from torchvision import transforms
from torch.utils.data import DataLoadertrans = transforms.ToTensor()
mnist_train = torchvision.datasets.FashionMNIST(root="../data", train=True, transform=trans, download=True)
mnist_test = torchvision.datasets.FashionMNIST(root="../data", train=False, transform=trans, download=True)batch_size = 256
train_iter = DataLoader(mnist_train, batch_size=batch_size, shuffle=True)
test_iter = DataLoader(mnist_test, batch_size=batch_size, shuffle=False)
#%%
num_inputs, num_outputs, hiddens = 784, 10, 256
W1 = nn.Parameter(torch.randn(num_inputs, hiddens, requires_grad=True))
b1 = nn.Parameter(torch.zeros(hiddens, requires_grad=True))
W2 = nn.Parameter(torch.randn(hiddens, num_outputs, requires_grad=True))
b2 = nn.Parameter(torch.zeros(num_outputs, requires_grad=True))params = [W1, b1, W2, b2]
# Q:为什么W1和W2不是初始化为0,而是随机?
# A:因为如果初始化为 0,梯度下降会导致每个神经元的权重更新相同,无法学习到有效的特征。随机初始化可以打破对称性,使得每个神经元学习到不同的特征。
#%%
def relu(X):a = torch.zeros_like(X) # 创建一个与X形状相同的全零张量return torch.max(X, a) # 元素级别的比较,返回每个元素与0的最大值
#%%
def net(X):X = X.reshape((-1, num_inputs)) # 展平输入H = relu(X @ W1 + b1) # 第一层的线性变换和ReLU激活return H @ W2 + b2 # 第二层的线性变换loss = nn.CrossEntropyLoss()
# Q:H = relu(X @ W1 + b1) 中的 @ 符号表示什么?
# A: @ 符号表示矩阵乘法(点积),它是 PyTorch 中用于进行矩阵乘法的运算符。
# Q:不可以用*嘛?
# A: * 符号表示元素级别的乘法,而 @ 符号表示矩阵乘法。在神经网络中,我们通常需要进行矩阵乘法来计算线性变换。
#%%
def accuracy(y_hat, y):if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:y_hat = y_hat.argmax(axis=1)# 确保 y 是 Tensorif not isinstance(y, torch.Tensor):y = torch.tensor(y)cmp = y_hat.type(y.dtype) == yreturn float(cmp.type(torch.float).sum().item())
#%%
class Accumulator:def __init__(self, n):self.data = [0.0] * ndef add(self, *args):for i, arg in enumerate(args):self.data[i] += argdef reset(self):self.data = [0.0] * len(self.data)def __getitem__(self, idx):return self.data[idx]
#%%
def evaluate_accuracy(net, data_iter):if isinstance(net, torch.nn.Module):net.eval() # 设置为评估模式metric = Accumulator(2) # (正确预测数, 总样本数)for X, y in data_iter:with torch.no_grad():y_hat = net(X)acc = accuracy(y_hat, y)metric.add(acc, y.numel()) # y 必须是 Tensorreturn metric[0] / metric[1]
#%%
def train_epoch_ch3(net, train_iter, loss, updater):if isinstance(net, torch.nn.Module):net.train() # 设置为训练模式metric = Accumulator(3) # 累加器,用于计算总损失、正确预测的数量和总样本数for X, y in train_iter:y_hat = net(X) # 前向传播l = loss(y_hat, y).sum() # 计算损失if isinstance(updater, torch.optim.Optimizer):updater.zero_grad() # 清零梯度l.backward() # 反向传播updater.step() # 更新参数else:l.sum().backward() # 手动反向传播updater(X.shape[0]) # 更新参数metric.add(l.item(), accuracy(y_hat, y), y.numel()) # 累加损失、正确预测数量和样本数return metric[0] / metric[2], metric[1] / metric[2] # 返回平均损失和准确率
#%%
def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):for epoch in range(num_epochs):train_metrics = train_epoch_ch3(net, train_iter, loss, updater) # 训练一个epochtest_acc = evaluate_accuracy(net, test_iter) # 在测试集上评估准确率print(f'epoch {epoch + 1}, loss {train_metrics[0]:f}, 'f'train acc {train_metrics[1]:f}, test acc {test_acc:f}')
#%%
num_epochs, lr = 10, 0.1
updater = torch.optim.SGD(params, lr=lr)
train_ch3(net, train_iter, test_iter, loss, num_epochs, updater)
参考
[1] 李沐《动手学机器学习》
[2] 李航《统计学习方法》