跳转至

感知机

单层感知机

目标: \(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\)中的每一列就代表一个神经元

回到上面的那个问题,如果每个神经元都初始化一样的值,那就只能学习到一个特征

简化版

net = nn.Sequential(
    nn.Flatten(), 
    nn.Linear(784, 256), 
    nn.ReLU(), 
    nn.Linear(256, 10))

def

RELU激活函数

def relu(X):
    a = torch.zero_like(X)
    return torch.max(X, a)  # 逐个元素比较,每个位置取最大的

模型

def net(X):
    X = X.reshape((-1, num_inputs))
    H = relu(X@W1 + b1)
    return (H@W2 + b2)

损失函数

loss = nn.CrossEntropyLoss(reduction='none')

模型选择、欠拟合、过拟合

  • 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

  • 准确率越训练越差 可能输数据稳定性出了问题