目录

1 模型选择

1.1 基本概念

1.2 K-则交叉验证

2 过拟合与欠拟合

2.1 过拟合

2.2 解决过拟合

2.2.1 获取更多训练数据集

2.2.3 使用较少特征

2.2.3 正则化(Regularization)

3 正则化

3.1 定义

3.2 正则化线性回归 

3.3 正则化逻辑回归

3.4 核心代码

3.5 L2正则化

3.5.1 权重衰退(Weight Decay)

3.6 丢弃法(Dropout)

3.6.1 原理

3.6.2 使用


1 模型选择

1.1 基本概念

训练误差:模型在训练数据上的误差;

泛化误差:模型在新数据上的误差;

验证数据集:用来评估模型好坏的数据集;

测试数据集:只用一次的数据集,其实就是应用于实际的数据。

1.2 K-则交叉验证

应用于没有足够多数据的情况。

原理是将数据分成K份,其中第i份是验证数据集、其他为训练数据集,如此就有了K份验证集,取K个验证集平均误差做为训练误差。K一般取5或10。

2 过拟合与欠拟合

当模型容量低、数据复杂的时候会出现欠拟合,当模型容量高、数据简单的时候会出现过拟合。随着模型容量的提高,训练误差会逐渐降低直至为0,泛化误差会先降低在变大,所以仅当泛化误差取得极小值的时候最优

2.1 过拟合

以房屋价格预测为例,线性回归中,图1对训练数据为欠拟合(underfit),即预测曲线与数据样本偏差较大,这种情况也称高度偏差(high bias);图2模型拟合情况良好,具有良好泛化性(generalization);图3模型虽然能够准确拟合真实样本数据、做到误差为0,但出现波动,与实际场景不符,称为过拟合(overfit),也称高方差(high variance)。

逻辑回归中同理,图1欠拟合,图2泛化性良好,图3过拟合。

2.2 解决过拟合
2.2.1 获取更多训练数据集

通过训练更多的数据集,将过拟合曲线变为泛化性良好的状态。

2.2.3 使用较少特征

使用关键特征进行模型建立,进而进行预测。

2.2.3 正则化(Regularization)

正则化通过鼓励学习算法缩小参数值,而不要求参数直接为0。正则化只改变w大小,修改b对曲线无太大影响。

3 正则化

正则化包括 L1、L2、Dropout、早停(early stopping)等方法。

3.1 定义

正则化后代价函数转变为:J(\vec{w},b)=\frac{1}{2m}[\sum_{i=1}^{m}(f_{\vec{w},b}(\vec{x}^{(i)})-y^{(i)})^{2}+\frac{\lambda }{2m}\sum_{j=1}^{n}w_{j}^{2}+\frac{\lambda }{2m}b^{2}]\frac{\lambda }{2m}b^{2}影响较小可忽略。

\lambda选择:

为0,正则化失效,过拟合;过大,欠拟合。

正则化的思想是平衡拟合,使其适中,满足实际场景;正则化的作用是在每次迭代中使wi变小一点。

3.2 正则化线性回归 

梯度下降:w_{j}=w_{j}-\alpha \frac{\partial J(\vec{w},b)}{\partial w_{j}}=w_{j}-\alpha[\frac{1}{m}\sum_{i=1}^{m}[f_{\vec{w},b}(\vec{x}^{(i)})-y^{(i)})x_{j}^{(i)}]+\frac{\lambda }{m}w_{j}]

                  b=b-\alpha \frac{\partial J(\vec{w},b)}{\partial b}=b-\frac{1}{m}(f_{\vec{w},b}(\vec{x}^{(i)})-y^{(i)})

此处回归模型为f_{\vec{w},b}(\vec{x})=\vec{w}*\vec{x}+b

3.3 正则化逻辑回归

梯度下降:w_{j}=w_{j}-\alpha \frac{\partial J(\vec{w},b)}{\partial w_{j}}=w_{j}-\alpha[\frac{1}{m}\sum_{i=1}^{m}[f_{\vec{w},b}(\vec{x}^{(i)})-y^{(i)})x_{j}^{(i)}]+\frac{\lambda }{m}w_{j}]

                  b=b-\alpha \frac{\partial J(\vec{w},b)}{\partial b}=b-\frac{1}{m}(f_{\vec{w},b}(\vec{x}^{(i)})-y^{(i)})

梯度下降公式与正则化的一致,唯一区别在于f函数,

              

3.4 核心代码

计算带正则化项的线性回归总损失函数J(\vec{w},b)=\frac{1}{2m}[\sum_{i=1}^{m}(f_{\vec{w},b}(\vec{x}^{(i)})-y^{(i)})^{2}+\frac{\lambda }{2m}\sum_{j=1}^{n}w_{j}^{2}]

def compute_cost_linear_reg(X, y, w, b, lambda_ = 1):
    """
    Computes the cost over all examples
    Args:
      X (ndarray (m,n): Data, m examples with n features
      y (ndarray (m,)): target values
      w (ndarray (n,)): model parameters  
      b (scalar)      : model parameter
      lambda_ (scalar): Controls amount of regularization
    Returns:
      total_cost (scalar):  cost 
    """

    m  = X.shape[0]
    n  = len(w)
    cost = 0.
    for i in range(m):
        f_wb_i = np.dot(X[i], w) + b                                   #(n,)(n,)=scalar, see np.dot
        cost = cost + (f_wb_i - y[i])**2                               #scalar             
    cost = cost / (2 * m)                                              #scalar  
 
    reg_cost = 0
    for j in range(n):
        reg_cost += (w[j]**2)                                          #scalar
    reg_cost = (lambda_/(2*m)) * reg_cost                              #scalar
    
    total_cost = cost + reg_cost                                       #scalar
    return total_cost    

计算逻辑回归模型的损失函数J(\vec{w},b)=\frac{1}{m}\sum_{i=1}^{m}[-y^{(i)}log(f_{\vec{w},b}(\vec{x}^{(i)}))-(1-y^{(i)})log(1-f_{\vec{w},b}(\vec{x}^{(i)}))]+\frac{\lambda }{2m}\sum_{j=1}^{n}w_{j}^{2}:

def compute_cost_logistic_reg(X, y, w, b, lambda_ = 1):
    """
    Computes the cost over all examples
    Args:
    Args:
      X (ndarray (m,n): Data, m examples with n features
      y (ndarray (m,)): target values
      w (ndarray (n,)): model parameters  
      b (scalar)      : model parameter
      lambda_ (scalar): Controls amount of regularization
    Returns:
      total_cost (scalar):  cost 
    """

    m,n  = X.shape
    cost = 0.
    for i in range(m):
        z_i = np.dot(X[i], w) + b                                      #(n,)(n,)=scalar, see np.dot
        f_wb_i = sigmoid(z_i)                                          #scalar
        cost +=  -y[i]*np.log(f_wb_i) - (1-y[i])*np.log(1-f_wb_i)      #scalar
             
    cost = cost/m                                                      #scalar

    reg_cost = 0
    for j in range(n):
        reg_cost += (w[j]**2)                                          #scalar
    reg_cost = (lambda_/(2*m)) * reg_cost                              #scalar
    
    total_cost = cost + reg_cost                                       #scalar
    return total_cost                                                  #scalar

计算线性回归代价函数(含正则化)的梯度\frac{\partial }{\partial w_{j}}J(\vec{w},b)=\frac{1}{m}[\sum_{i=1}^{m}(f_{\vec{w},b}(\vec{x}^{(i)})-y^{(i)}x_{j}^{(i)})]+\frac{\lambda }{m}w_{j},

\frac{\partial }{\partial b}J(\vec{w},b)=\frac{1}{m}\sum_{i=1}^{m}(f_{\vec{w},b}(\vec{x}^{(i)})-y^{(i)}):

def compute_gradient_linear_reg(X, y, w, b, lambda_): 
    """
    Computes the gradient for linear regression 
    Args:
      X (ndarray (m,n): Data, m examples with n features
      y (ndarray (m,)): target values
      w (ndarray (n,)): model parameters  
      b (scalar)      : model parameter
      lambda_ (scalar): Controls amount of regularization
      
    Returns:
      dj_dw (ndarray (n,)): The gradient of the cost w.r.t. the parameters w. 
      dj_db (scalar):       The gradient of the cost w.r.t. the parameter b. 
    """
    m,n = X.shape           #(number of examples, number of features)
    dj_dw = np.zeros((n,))
    dj_db = 0.

    for i in range(m):                             
        err = (np.dot(X[i], w) + b) - y[i]                 
        for j in range(n):                         
            dj_dw[j] = dj_dw[j] + err * X[i, j]               
        dj_db = dj_db + err                        
    dj_dw = dj_dw / m                                
    dj_db = dj_db / m   
    
    for j in range(n):
        dj_dw[j] = dj_dw[j] + (lambda_/m) * w[j]

    return dj_db, dj_dw

计算逻辑回归代价函数(含正则化)的梯度

\frac{\partial }{\partial w_{j}}J(\vec{w},b)=\frac{1}{m}\sum_{i=1}^{m}(f_{\vec{w},b}(\vec{x}^{(i)})-y^{(i)}x_{j}^{(i)})+\frac{\lambda }{m}w_{j},

\frac{\partial }{\partial b}J(\vec{w},b)=\frac{1}{m}\sum_{i=1}^{m}(f_{\vec{w},b}(\vec{x}^{(i)})-y^{(i)}):

def compute_gradient_logistic_reg(X, y, w, b, lambda_): 
    """
    Computes the gradient for linear regression 
 
    Args:
      X (ndarray (m,n): Data, m examples with n features
      y (ndarray (m,)): target values
      w (ndarray (n,)): model parameters  
      b (scalar)      : model parameter
      lambda_ (scalar): Controls amount of regularization
    Returns
      dj_dw (ndarray Shape (n,)): The gradient of the cost w.r.t. the parameters w. 
      dj_db (scalar)            : The gradient of the cost w.r.t. the parameter b. 
    """
    m,n = X.shape
    dj_dw = np.zeros((n,))                            #(n,)
    dj_db = 0.0                                       #scalar

    for i in range(m):
        f_wb_i = sigmoid(np.dot(X[i],w) + b)          #(n,)(n,)=scalar
        err_i  = f_wb_i  - y[i]                       #scalar
        for j in range(n):
            dj_dw[j] = dj_dw[j] + err_i * X[i,j]      #scalar
        dj_db = dj_db + err_i
    dj_dw = dj_dw/m                                   #(n,)
    dj_db = dj_db/m                                   #scalar

    for j in range(n):
        dj_dw[j] = dj_dw[j] + (lambda_/m) * w[j]

    return dj_db, dj_dw  
3.5 L2正则化

L2正则化可以通过在损失函数里面加\lambda ||w||^{2}、权重衰退实现,使得模型参数不会过大从而控制模型复杂度。权重衰减更为常见,介绍如下。

3.5.1 权重衰退(Weight Decay)

计算梯度:\frac{\partial }{\partial w}(L(w,b)+\frac{\lambda }{2}||w||^{2})=\frac{\partial }{\partial w}L(w,b)+\lambda w

时间t更新参数:w_{t+1}=w_{t}-\eta \frac{\partial }{\partial w},带入梯度

                         w_{t+1}=(1-\eta \lambda )w_{t}-\eta \frac{\partial }{\partial w_{t}}L(w_{t},b_{t}),通常\lambda \eta < 1

                        (深度学习中叫权重衰退)

代码简洁实现:

def train_concise(wd):
    net = nn.Sequential(nn.Linear(num_inputs, 1))
    for param in net.parameters():
        param.data.normal_()
    loss = nn.MSELoss(reduction='none')
    num_epochs, lr = 100, 0.003
    # 偏置参数没有衰减
    trainer = torch.optim.SGD([
        {"params":net[0].weight,'weight_decay': wd},
        {"params":net[0].bias}], lr=lr)
    animator = d2l.Animator(xlabel='epochs', ylabel='loss', yscale='log',
                            xlim=[5, num_epochs], legend=['train', 'test'])
    for epoch in range(num_epochs):
        for X, y in train_iter:
            trainer.zero_grad()
            l = loss(net(X), y)
            l.mean().backward()
            trainer.step()
        if (epoch + 1) % 5 == 0:
            animator.add(epoch + 1,
                         (d2l.evaluate_loss(net, train_iter, loss),
                          d2l.evaluate_loss(net, test_iter, loss)))
    print('w的L2范数:', net[0].weight.norm().item())
train_concise(0)

train_concise(3)

3.6 丢弃法(Dropout)
3.6.1 原理

Dropout是在层之间加入噪音。

对于x加入噪音得到x^{'},希望期望E不变,即E[x^{'}]=x,即定义x^{'}如下

                                      

3.6.2 使用

使用dropout后隐藏层有些数会置0,用于模型训练。

3.6.3 代码
import torch
from torch import nn
from d2l import torch as d2l


def dropout_layer(X, dropout):
    assert 0 <= dropout <= 1
    # 在本情况中,所有元素都被丢弃
    if dropout == 1:
        return torch.zeros_like(X)
    # 在本情况中,所有元素都被保留
    if dropout == 0:
        return X
    mask = (torch.rand(X.shape) > dropout).float()
    return mask * X / (1.0 - 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)

net.apply(init_weights);

trainer = torch.optim.SGD(net.parameters(), lr=lr)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐