感知机
单层感知机
目标: \(y_i (w^T x_i + b) > 0\),计分类正确
感知机损失函数\(L = - \sum_{i \in misclassified} y_i (w_T x_i+b)\)
求要优化的参数的偏导:
对某一个错分类样本: \(L_i = -y_i(w^T x_i + b)\)
\(w: \frac{\partial L_i}{\partial w} = - y_i x_i\)
\(b: \frac{\partial L_i}{\partial b} = -y_i\)
\[\therefore w = w - \eta \frac{\partial L_i}{\partial w} = w + \eta y_i x_i\]
\[b = b + \eta y_i\]
多层感知机
初始化模型参数
num_imputs, num_outputs, num_hiddens = ...
W1 = nn.Parameter(torch.randn(
num_inputs, num_num_hiddens, requires_grad=True))
b1 = nn.Parameter(torch.zeros(num_hiddnes, requires_grad=True))
W2 = nn.Parameter(torch.randn(
num_hiddens, num_outputs, requires_grad=True) * 0.01)
b2 = nn.Parameter(torch.zeros(num_outputs, requires_grad=True))
- \(X: (batch_size, 784)\),X一般都把
batch_size作为行数 - \(W: (784, hidden)\)
- \(b: (hidden)\)
\(XW + b\)时,\(b会(hidden) \rightarrow (batch_size, hidden)\)
- 参数初始化要是随机数,不能全是0或者全是1
Tip
\(XW\)中,结果的每一列都是X中所有列的线性组合,这个线性组合就是由\(W\)的每一列决定的,因此\(W\)中的每一列就代表一个神经元
回到上面的那个问题,如果每个神经元都初始化一样的值,那就只能学习到一个特征
简化版
RELU激活函数
模型
损失函数
模型选择、欠拟合、过拟合
-
K折交叉验证 在数据不够时
-
欠拟合 under fitting
- 过拟合 over fitting
- 处理过拟合的方法:权重衰减 dropout 后续会讲
权重衰退
通过L2正则化使模型参数不会过大
损失函数里加了一个\(\frac{\lambda}{2} ||w||^2\) ,这样优化的方向就会加上往w缩小的方向优化
\[L(w)=\ell(w,b)+\frac{\lambda}{2}|w|^2\]
\[w_{t + 1} = (1-\eta \lambda) w_t - \eta \frac{\partial l}{\partial w}\]
Note
权重指的就是w,为什么不希望权重大?
- 权重大,模型就敏感,某个特征变化一点,输出就会剧烈变化,权重小输出就会更平滑,防止过拟合
- 权重大小可以理解成模型对某个特征的依赖程度
丢弃法
可能比权重衰退效果好
对神经元进行扰动,使其期望不改变
例如将均值为0点正态分布的偏差作为噪声添加到输入上
还可以以概率\(p\)丢弃每个神经元,即:h这个点有概率为p会变成0,有1-p的概率变成\(\frac{h}{1-p}\)这样不会影响数据的期望
其实验结果显示效果类似正则,所以现在大家会把他当成正则使用
def dropout_layer(X, dropout):
assert 0 <= dropout <= 1
if dropout == 1: # p设置为1时就全保留
return torch.zeros_like(X)
if dropout == 0: # 全丢弃
return X
mask = (torch.rand(X.shape) > dropout).float() # 随机出的数比p大就保留
return mask * X / (1.0 - dropout) # 使用乘法而不是fou循环索引会快一点
模型中的使用方法
class Net(nn.Module):
def __init__(self, num_inputs, num_outputs, num_hiddens1, num_hiddens2,
is_training = True):
super(Net, self).__init__()
self.num_inputs = num_inputs
self.training = is_training
self.lin1 = nn.Linear(num_inputs, num_hiddens1)
self.lin2 = nn.Linear(num_hiddens1, num_hiddens2)
self.lin3 = nn.Linear(num_hiddens2, num_outputs)
self.relu = nn.ReLU()
# 定义了有哪些层,下面forward里就用到了
def forward(self, X):
H1 = self.relu(self.lin1(X.reshape((-1, self.num_inputs))))
# 只有在训练模型时才使用dropout
if self.training == True:
# 在第一个全连接层之后添加一个dropout层
H1 = dropout_layer(H1, dropout1)
H2 = self.relu(self.lin2(H1))
if self.training == True:
# 在第二个全连接层之后添加一个dropout层
H2 = dropout_layer(H2, dropout2)
out = self.lin3(H2)
return out
简洁实现版,使用dropout框架
net = nn.Sequential(nn.Flatten(),
nn.Linear(784, 256),
nn.ReLU(),
# 在第一个全连接层之后添加一个dropout层
nn.Dropout(dropout1),
nn.Linear(256, 256),
nn.ReLU(),
# 在第二个全连接层之后添加一个dropout层
nn.Dropout(dropout2),
nn.Linear(256, 10))
def init_weights(m):
if type(m) == nn.Linear:
nn.init.normal_(m.weight, std=0.01)
Note
- dropout随机置零的点梯度就会变成0
- dropout也是一个超参数
- dropout参数好调一点,比权重衰减
数据稳定性
- 梯度爆炸
- 梯度消失
- 对称性 就是交换同一层里的两个神经元是不会有影响的,也解释了参数初始化为什么不能初始化为相同的值
参数初始化
有点数学这一章,不细究了
- 正态分布初始化
- Xavier初始化
Q&A
- 准确率越训练越差 可能输数据稳定性出了问题