感知机
-
给定输入x,权重w,和偏移b,感知机输出 o = σ ( < w , x > + b ) o = \sigma(<w,x> + b) o=σ(<w,x>+b)
-
σ
(
x
)
=
{
1
,
if x>0
0
,
otherwise
\sigma(x) =\begin{cases}1, & \text{if x>0}\\ 0, & \text{otherwise}\end{cases}
σ(x)={1,0,if x>0otherwise
-
-
σ
(
x
)
=
{
1
,
if x>0
0
,
otherwise
\sigma(x) =\begin{cases}1, & \text{if x>0}\\ 0, & \text{otherwise}\end{cases}
σ(x)={1,0,if x>0otherwise
-
可以看做是二分类问题: 0,1,输出离散的类
- 线性回归输出实数
- softmax回归输出概率
训练感知机
- 训练过程
- 初始化w =0,b=0
- 循环 if y i ( < w , x > + b ) ≤ 0 y_i(<w,x> + b ) ≤ 0 yi(<w,x>+b)≤0
- 注 :≤0相当于感知机的预测样本错了,
(
<
w
,
x
>
+
b
)
<
0
(<w,x> + b )<0
(<w,x>+b)<0为负类,因此
y
i
y_i
yi也应为负,正号同理, y与$(<w,x> + b )要同号
- w ← w + y i x i , b ← b + y i w \leftarrow w+y_ix_i,b \leftarrow b + y_i w←w+yixi,b←b+yi
- 注: w = w + y i x i w=w+y_ix_i w=w+yixi 标号乘样本 做权重更新, $b = b+y_i $ ,b加上标号,
- 直到所有分类正确
- 等价于使用批量大小为1的梯度下降,并使用如下损失函数:
l
(
y
,
x
,
w
)
=
m
a
x
(
0
,
−
y
<
w
,
x
>
)
l(y,x,w) = max(0, -y<w,x>)
l(y,x,w)=max(0,−y<w,x>)
- 损失函数求导,w导数为 y i x i y_ix_i yixi,b导数为 y i y_i yi
- max()对应上面if的语句 分类对了,就 = 0不更新,分类错了就会存在梯度,因此损失函数会进行更新
收敛定理
- 假设数据在半径 r 内
- 余量ρ分类两类 y ( x T w + b ) ≥ ρ y(x^Tw+b)≥ρ y(xTw+b)≥ρ 对于 ∣ ∣ w ∣ ∣ 2 + b 2 ≤ 1 ||w||^2+b^2≤1 ∣∣w∣∣2+b2≤1
- 感知机保证在 r 2 + 1 ρ 2 \large \frac{r^2+1}{ρ^2} ρ2r2+1 步后收敛
XOR 异或 问题
- 感知机不能拟合XOR函数,它只能产生线性分割面
-
对于感知机
-
感知机是二分类模型
-
求解算法等价于使用批量大小为1的梯度下降
-
不能拟合XOR函数
多层感知机
在网络中加入隐藏层
单隐藏层-单分类
-
输入 x ∈ R n x∈R^n x∈Rn
-
隐藏层 W 1 ∈ R m × n , b 1 ∈ R m W_1 ∈ R^{m×n},b_1∈R^m W1∈Rm×n,b1∈Rm
-
隐藏层 W 2 ∈ R m , b 2 ∈ R W_2 ∈ R^m,b_2∈R W2∈Rm,b2∈R
-
h = σ ( W 1 x + b 1 ) h =\sigma(W_1x+b_1) h=σ(W1x+b1) , σ \sigma σ 是按元素的激活函数
-
o = w 2 T h + b 2 o = w^T_2 h +b_2 o=w2Th+b2
-
为什么需要非线性的激活函数
- 假设 h = W 1 x + b 1 h =W_1x+b_1 h=W1x+b1,那么 o = w 2 T h + b 2 = w 2 T W 1 x + w 2 T b 1 + b 2 o = w^T_2 h +b_2 = w_2^TW_1x+w_2^Tb_1+b_2 o=w2Th+b2=w2TW1x+w2Tb1+b2 仍然是线性的 ,相当于单层感知机
sigmodi激活函数
- 对于一个定义域在R中的输入, sigmoid函数将输入变换为区间(0, 1)上的输出。 σ ( x ) = { 1 , if x>0 0 , otherwise \sigma(x) =\begin{cases}1, & \text{if x>0}\\ 0, & \text{otherwise}\end{cases} σ(x)={1,0,if x>0otherwise
- 因此,sigmoid通常称为挤压函数(squashing function): 它将范围(-inf, inf)中的任意输入压缩到区间(0, 1)中的某个值:
- s i g m o i d ( x ) = 1 1 + e x p ( − x ) sigmoid(x) = \frac{1}{1+exp(-x)} sigmoid(x)=1+exp(−x)1
- 结果如图:
Tanh激活函数
- 将输入投影到(-1,1)
- t a n h ( x ) = 1 − e x p ( − 2 x ) 1 + e x p ( − 2 x ) tanh(x) = \frac{1-exp(-2x)}{1+exp(-2x)} tanh(x)=1+exp(−2x)1−exp(−2x)
ReLU激活函数:rectified linear unit 修正线性单元
- ReLU(x) = max(x,0)
多类分类:在softmax回归中加了一层隐藏层
-
输入 x ∈ R n x∈R^n x∈Rn
-
隐藏层 W 1 ∈ R m × n , b 1 ∈ R m W_1 ∈ R^{m×n},b_1∈R^m W1∈Rm×n,b1∈Rm
-
隐藏层 W 2 ∈ R m × k , b 2 ∈ R k W_2 ∈ R^{m×k},b_2∈R^k W2∈Rm×k,b2∈Rk
-
h = σ ( W 1 x + b 1 ) h =\sigma(W_1x+b_1) h=σ(W1x+b1) , σ \sigma σ 是按元素的激活函数
-
o = w 2 T h + b 2 o = w^T_2 h +b_2 o=w2Th+b2
-
y = softmax(o)
-
可以有多隐藏层: h 1 = σ ( W 1 x + b 1 ) h_1 =\sigma(W_1x+b_1) h1=σ(W1x+b1) h 2 = σ ( W 2 h 1 + b 2 ) h_2 =\sigma(W_2h_1+b_2) h2=σ(W2h1+b2) h 3 = σ ( W 3 h 2 + b 3 ) h_3 =\sigma(W_3h_2+b_3) h3=σ(W3h2+b3) o = W 4 h 3 + b 4 o =W_4h_3+b_4 o=W4h3+b4
- 超参数:隐藏层数 每层隐藏层的大小
-
示意图如下:
注:
-
层数越多 模型越复杂,第一层隐藏层设得稍微大一点,取决于输入的复杂度有多少,
-
假设数据比较复杂,
- 使用单隐藏层,将隐藏层设的大一些,假设输入维度是128,隐藏层可设64.128.256,
- 单隐藏层当其设的过大时,容易过拟合,且不好训练
- 使用多隐藏层,设的相对于单隐藏层较小,且越深是越来越小
-
多隐藏层,假设第一层设的相对输入较小,那么可能会损失很多信息,需要注意
知识总结
- 多层感知机在输出层和输入层之间增加一个或多个全连接隐藏层,并通过激活函数转换隐藏层的输出。
- 常用的激活函数包括ReLU函数、sigmoid函数和tanh函数
- 使用Softmax来处理多类分类
- 超参数为隐藏层数 和 每层隐藏层的大小
- 激活函数的本质 是引入非线性性
代码实现
导入工具包
import torch
from torch import nn
from d2l import torch as d2l
模型
-
与softmax回归的实现相比, 唯一的区别是我们添加了2个全连接层(之前我们只添加了1个全连接层)
-
第一层是隐藏层,它包含256个隐藏单元,并使用了ReLU激活函数。 第二层是输出层。
-
net = nn.Sequential(nn.Flatten(), # Flatten成2维 nn.Linear(784, 256), nn.ReLU(), nn.Linear(256, 10)) def init_weights(m): if type(m) == nn.Linear: nn.init.normal_(m.weight, std=0.01) net.apply(init_weights);
训练过程
-
batch_size, lr, num_epochs = 256, 0.1, 10 loss = nn.CrossEntropyLoss(reduction='none') trainer = torch.optim.SGD(net.parameters(), lr=lr) train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size) d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)
- 运行结果如图:
- 运行结果如图:
-
updater
是更新模型参数的常用函数,它接受批量大小作为参数,可以是sgd
函数,也可以是框架的内置优化函数 -
def train_epoch_ch3(net, train_iter, loss, updater): #@save """训练模型一个迭代周期(定义见第3章)""" # 将模型设置为训练模式 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) if isinstance(updater, torch.optim.Optimizer): # 使用PyTorch内置的优化器和损失函数 updater.zero_grad() l.mean().backward() updater.step() else: # 使用定制的优化器和损失函数 l.sum().backward() updater(X.shape[0]) metric.add(float(l.sum()), accuracy(y_hat, y), y.numel()) # 将样本数,正确的样本数等放进累加器 # 返回训练损失和训练精度 return metric[0] / metric[2], metric[1] / metric[2]
-
一个在动画中绘制数据的实用程序类
Animator
-
class Animator: #@save """在动画中绘制数据""" def __init__(self, xlabel=None, ylabel=None, legend=None, xlim=None, ylim=None, xscale='linear', yscale='linear', fmts=('-', 'm--', 'g-.', 'r:'), nrows=1, ncols=1, figsize=(3.5, 2.5)): # 增量地绘制多条线 if legend is None: legend = [] d2l.use_svg_display() self.fig, self.axes = d2l.plt.subplots(nrows, ncols, figsize=figsize) if nrows * ncols == 1: self.axes = [self.axes, ] # 使用lambda函数捕获参数 self.config_axes = lambda: d2l.set_axes( self.axes[0], xlabel, ylabel, xlim, ylim, xscale, yscale, legend) self.X, self.Y, self.fmts = None, None, fmts def add(self, x, y): # 向图表中添加多个数据点 if not hasattr(y, "__len__"): y = [y] n = len(y) if not hasattr(x, "__len__"): x = [x] * n if not self.X: self.X = [[] for _ in range(n)] if not self.Y: self.Y = [[] for _ in range(n)] for i, (a, b) in enumerate(zip(x, y)): if a is not None and b is not None: self.X[i].append(a) self.Y[i].append(b) self.axes[0].cla() for x, y, fmt in zip(self.X, self.Y, self.fmts): self.axes[0].plot(x, y, fmt) self.config_axes() display.display(self.fig) display.clear_output(wait=True)
-