李宏毅机器学习第八周深度学习&HW1

摘要

本文内容主要围绕三个部分展开。首先,讨论了运用验证集后仍可能过拟合的原因,这是由于验证集的参数候选范围过大可能导致抽取到效果不好的验证集的概率增大。其次,认识了深度学习的概念以及优点,深度学习的参数候选范围小且范围内参数对应模型损失小。最后,搭建了基于gpu的pytorch环境,调整并训练了HW1中的模型。

Abstract

The content of this article mainly revolves around three parts. First, the reasons why overfitting is still possible after using the validation set are discussed, because the parameter candidate range of the validation set is too large, which may increase the probability of extracting the poorly effective validation set. Secondly, the concept and advantages of deep learning are recognized, and the parameter candidate range of deep learning is small and the loss of the corresponding model of the parameters in the range is small. Finally, a GPU-based pyTorch environment was built, and the model in HW1 was tuned and trained.

一、为什么用了验证集结果还是过拟合

当引入验证集后,运作机制如下

  • 在训练过程中,根据该模型确定一个在训练集中误差最小的 h i ∗ h_i^* hi
    • M o d e l   H i   h i ∗ = a r g min ⁡ h ∈ H i L ( h , D t r a i n ) Model\ H_i\ h_i^*=arg\min_{h\in H_i}{L(h,D_{train})} Model Hi hi=argminhHiL(h,Dtrain)
  • 在验证过程中,计算出 h i ∗ h_i^* hi在验证集上的误差 L ( h i ∗ , D v a l ) L(h_i^*, D_{val}) L(hi,Dval)
  • 最后从所有的 h i ∗ h_i^* hi中选取一个误差最小的应用于测试集中,将测试集作为对所有数据集合的代替
  • 在这里插入图片描述

用验证集去选择模型可以被看作是“在验证集上训练”

由训练确定的多个 h i ∗ h_i^* hi可以看作模型参数的取值范围 H v a l H_{val} Hval,而验证过程所做的操作是 h ∗ = a r g min ⁡ h ∈ H v a l L ( h , D v a l ) h^*=arg\min_{h\in H_{val}}{L(h,D_{val})} h=argminhHvalL(h,Dval)

在这里插入图片描述

在week5笔记中 P ( D t r a i n   i s   b a d ) ≤ ∣ H ∣ ⋅ 2 e x p ( − 2 N ϵ 2 ) P(D_{train}\ is\ bad)\leq|H|\cdot2exp(-2N\epsilon^2) P(Dtrain is bad)H2exp(2Nϵ2)

对于上述情况,则为 P ( D v a l   i s   b a d ) ≤ ∣ H v a l ∣ ⋅ 2 e x p ( − 2 N v a l ϵ 2 ) P(D_{val}\ is\ bad)\leq|H_{val}|\cdot2exp(-2N_{val}\epsilon^2) P(Dval is bad)Hval2exp(2Nvalϵ2)

∣ H v a l ∣ |H_{val}| Hval过大,也会使得抽取出较坏的验证集的可能较大

二、鱼与熊掌可以兼得的深度学习

当参数的选择范围较大时,理想的情况可能较好,但实际选择的情况可能与理想情况差距较大

而当参数的选择范围较小时,理想的情况可能较差,但实际选择的情况可能与理想情况差距较小

能否有这样一种情况?在参数选择范围较小的情况下,其范围各参数在所有数据集合上的表现也较好

在这里插入图片描述

1. review: why hidden layer?

要使用一个network拟合一条曲线。假设拟合效果如下图中绿色曲线部分。拟合的分段线段仅由少数的线段组成,其效果显然是相对粗糙的。若要得到一个拟合效果好的曲线,需要足够多的线段来进行拟合。

在这里插入图片描述

通常,一个分段线段是由一组下图蓝色实线表示的function以及常数组成,通过这种方式生成的分段线段可以拟合任意一条曲线,这样可以使用神经网络来生成任意的曲线。

下图的蓝色实线称作 hard sigmoid,可以由sigmoid function拟合

sigmoid function图像如下图蓝色虚线,公式为 y = c   1 1 + e − e ( b + w x 1 ) = c   s i g m o i d ( b + w x 1 ) y=c\ \frac1{1+e^{-e(b+wx_1)}}=c\ sigmoid(b+wx_1) y=c 1+ee(b+wx1)1=c sigmoid(b+wx1)

由于sigmoid function计算量相对较大,当算量不足以支持时,可以选用ReLU修正线性单元,公式为 y = c   max ⁡ ( 0 , b + w x 1 ) y=c\ \max(0,b+wx_1) y=c max(0,b+wx1)。两段ReLU可以拟合一个hard sigmoid。

在这里插入图片描述

在下图中通过神经网络实现了上述过程,通过神经网络生成了多个sigmoid function以及常数组成的分段线段。

该网络是一个hidden layer的neural network,每个neural可以拟合一条hard sigmoid折线,最后多个neural以及bias偏差拟合出一个分段线段,从而可以使用该网络用于近似任何function。下图中使用sigmoid function来拟合hard sigmoid,也可以使用ReLU进行拟合

以下解释为何使用多个hidden layer构成一个较深的网络,而不是仅使用由足够多neural构成的一层较"胖"的网络。

在这里插入图片描述

2. Deeper is Better?

下图是深度网络以及“宽度”网络在语音辨识上的表现效果,左侧为深度网络,右侧为“宽度”网络

双箭头标识的两组网络的参数量相近,左侧的深度网络的表现效果明显好于右侧。

红色方框标识的两个网络的准确率大致相同,显然左侧的网络参数量较小

综上,使用多个hidden layer构成一个较深的网络,即使用深度网络时效果较好。

以下解释为何采用深度网络进行训练的表现效果好于“宽度”网络

在这里插入图片描述

3. why we need deep?

3.1举例:逻辑电路

奇偶校验,即检验输入序列中,1的数量的奇偶性,奇数输出0,反之输出1

同样的对于d位的输入序列,两层的电路需要 O ( 2 d ) O(2^d) O(2d)个门,而如下图中的多层电路仅需要 O ( d ) O(d) O(d)个门

显然采用深层结构的电路所需的门相比两层结构的电路较少

注:下图的多层电路中的门均为同或门,即当两位相异时输出0,相同时输出1,详见下图左上角

3.2举例:程式

在编程中,鲜有将所有的代码都编写在main函数中,而是编写在多个函数中以便在部分函数中复用其他代码块

以上是在其他领域中深度结构的应用,下面解释机器学习领域中深度结构的应用

在这里插入图片描述

3.3举例:深度学习

以下是一个采用ReLU进行线性拟合的单层神经网络的结构,该结构一层中有两个神经元

在这里插入图片描述

第一个神经元,ReLU公式为 y = max ⁡ ( 0 , − 0.5 + x ) y=\max(0,-0.5+x) y=max(0,0.5+x)。公式如下

y = { 0 , 0 < x ≤ 0.5 x − 0.5 , 0.5 < x < 1 y=\begin{cases}0,\quad 0<x\leq0.5\\x-0.5,\quad 0.5<x<1\end{cases} y={0,0<x0.5x0.5,0.5<x<1

第二个神经元,ReLU公式为 y = max ⁡ ( 0 , 0.5 − x ) y=\max(0,0.5-x) y=max(0,0.5x)。公式如下

y = { 0.5 − x , 0 < x ≤ 0.5 0 , 0.5 < x < 1 y=\begin{cases}0.5-x,\quad 0<x\leq0.5\\0,\quad 0.5<x<1\end{cases} y={0.5x,0<x0.50,0.5<x<1

综上, a 1 = { 0.5 − x , 0 < x ≤ 0.5 x − 0.5 , 0.5 < x < 1 a_1=\begin{cases}0.5-x,\quad 0<x\leq0.5\\x-0.5,\quad 0.5<x<1\end{cases} a1={0.5x,0<x0.5x0.5,0.5<x<1

对于输入为x、输出为 a 1 a_1 a1的网络,其图像如下,即有两个线段组成的折线

在这里插入图片描述

对于两层网络

x ,   0 → 0.25 a 1 ,   1 → 0.5 a 2 ,   1 → 0 x ,   0.25 → 0.5 a 1 ,   0.5 → 0 a 2 ,   0 → 1 x ,   0.5 → 0.75 a 1 ,   0 → 0.5 a 2 ,   1 → 0 x ,   0.75 → 1 a 1 ,   0.5 → 1 a 2 ,   0 → 1 \begin{array}{|c|l|r|}\hline x,\ 0\rightarrow0.25&a_1,\ 1\rightarrow0.5&a_2,\ 1\rightarrow0\\\hline x,\ 0.25\rightarrow0.5&a_1,\ 0.5\rightarrow0&a_2,\ 0\rightarrow1\\\hline x,\ 0.5\rightarrow0.75&a_1,\ 0\rightarrow0.5&a_2,\ 1\rightarrow0\\\hline x,\ 0.75\rightarrow1&a_1,\ 0.5\rightarrow1&a_2,\ 0\rightarrow1\\\hline\end{array} x, 00.25x, 0.250.5x, 0.50.75x, 0.751a1, 10.5a1, 0.50a1, 00.5a1, 0.51a2, 10a2, 01a2, 10a2, 01

两层网络的图像如下图左下角,由四个线段组成的折线

以此类推,三层网络是由 2 3 2^3 23个线段组成的线段

在这里插入图片描述

综上使用每层有两个神经元的k层深度Deep网络拟合类似的 2 k 2^k 2k段的折线段需要2000个神经元

而使用单层的Shallow网络拟合该线段需要 2 k 2^k 2k个神经元

显然,deep网络所使用的参数量远小于shallow网络中的,相应的使得deep网络收敛所需要的资料量也是远小于shallow网络的

在语音以及影像等领域所需要的函数通常是复杂且有规律的,此时采用深度网络的效果要好于shallow网络

在这里插入图片描述

三、李宏毅机器学习HW1预测COVID19

电脑系统windows11,python版本3.9.17

根据视频中的要求,本文主要对模型的结构、特征选取、学习率计划、优化器部分的进行了修改以使模型达到更好的效果

1. some utility functions

def same_seed(seed): 
    '''固定随机数种子以复现实验'''
    # 当使用GPU时,需要固定该项以返回确定的卷积函数
    torch.backends.cudnn.deterministic = True
    # 不在多个卷积算法中选择最优
    torch.backends.cudnn.benchmark = False
    
    np.random.seed(seed)
    torch.manual_seed(seed)
    if torch.cuda.is_available():
        torch.cuda.manual_seed_all(seed)

def train_valid_split(data_set, valid_ratio, seed):
    '''按照valid_ratio的比例,将提供的训练数据划分为训练集和测试集'''
    valid_set_size = int(valid_ratio * len(data_set)) 
    train_set_size = len(data_set) - valid_set_size
    train_set, valid_set = random_split(data_set, [train_set_size, valid_set_size], generator=torch.Generator().manual_seed(seed))
    return np.array(train_set), np.array(valid_set)

def predict(test_loader, model, device):
    '''评估模型'''
    model.eval() # 将模型设置为测试模式
    preds = []
    for x in tqdm(test_loader):
        x = x.to(device)# 将参数复制到device                   
        with torch.no_grad():# 禁用梯度计算     
            pred = model(x)                     
            preds.append(pred.detach().cpu()) # 创建一个pred的新张量并加入preds
    preds = torch.cat(preds, dim=0).numpy()  
    return preds

2. dataset

class COVID19Dataset(Dataset):
    '''
    设置数据集的初始化、取值与长度
    x: Features.
    y: Targets, if none, do prediction.
    '''
    def __init__(self, x, y=None):
        if y is None:
            self.y = y
        else:
            self.y = torch.FloatTensor(y)
        self.x = torch.FloatTensor(x)
        
    def __getitem__(self, idx):
        '''根据关键词idx取出一个数据实例'''
        if self.y is None:
            return self.x[idx]
        else:
            return self.x[idx], self.y[idx]

    def __len__(self):
        return len(self.x)

3. Neural Network Model


class My_Model(nn.Module):
    def __init__(self, input_dim):
        super(My_Model, self).__init__()
        # 可以修改模型,例如16->64,使用更shallow的模型
        self.layers = nn.Sequential(
            nn.Linear(input_dim, 64),
            nn.ReLU(),
            nn.Linear(64, 8),
            nn.ReLU(),
            nn.Linear(8, 1)
        )

    def forward(self, x):
        x = self.layers(x)
        x = x.squeeze(1) # 用于n*1维度的张量降维
        return x

4. feature selection

基于题目特征

题目中描述使用前四天的阳性案例预测第五天

训练数据的构成规律:37个州+16个特征*五天

def select_feat(train_data, valid_data, test_data, select_all=True):
    '''用有效的特征进行回归'''
    y_train, y_valid = train_data[:,-1], valid_data[:,-1]
    raw_x_train, raw_x_valid, raw_x_test = train_data[:,:-1], valid_data[:,:-1], test_data

    if select_all:
        feat_idx = list(range(raw_x_train.shape[1]))
    else:
        feat_idx = [] # 修改本部分,选择前四天特征;修改后需要设置select_all=False
        for i in range(1,5):
            feat_idx.append(16*i+37)
        
    return raw_x_train[:,feat_idx], raw_x_valid[:,feat_idx], raw_x_test[:,feat_idx], y_train, y_valid
调用sklearn库中的SelectKBest

SelectKBest函数用于特征选择。选择函数由参数score_func确定,f_regression为F-回归任务的标签/功能之间的值。k即topK个参数,all则全选

from sklearn.feature_selection import SelectKBest, f_regression

k = config['k'] # 所要选择的特征数量,请在config中进行设置
selector = SelectKBest(score_func=f_regression, k=k)
result = selector.fit(train_data[:, :-1], train_data[:,-1])
idx = np.argsort(result.scores_)[::-1]
feat_idx = list(np.sort(idx[:k]))

5. training loop

def trainer(train_loader, valid_loader, model, config, device):

    criterion = nn.MSELoss(reduction='mean') # 定义损失函数

    # 定义优化算法
    '''
    原文中使用的SGDM算法,现修改为Adam算法
    同时为了更好收敛,将学习率增大至10倍,参数衰减设置为1e-3
    
    关于学习率计划、参数衰减内容详见第五周周报
    '''
    optimizer = torch.optim.Adam(model.parameters(), lr=config['learning_rate']*10,weight_decay=1e-3)
    scheduler = torch.optim.lr_scheduler.CosinAnnealingWarnRestars(optimizer,T_0=2,T_mult=2,eta_min=config['learning_rate'])
    

    # 使用tensorboard显示训练过程
    writer = SummaryWriter("./log")

    if not os.path.isdir('./models'):
        os.mkdir('./models') # 创建存储模型的文件夹

    n_epochs, best_loss, step, early_stop_count = config['n_epochs'], math.inf, 0, 0

    for epoch in range(n_epochs):
        model.train() # 设置模型为训练模式
        loss_record = []

        # tqdm is a package to visualize your training progress.
        train_pbar = tqdm(train_loader, position=0, leave=True)

        for x, y in train_pbar:
            optimizer.zero_grad()               # 梯度置零
            x, y = x.to(device), y.to(device)   # 将参数传至device
            pred = model(x)             
            loss = criterion(pred, y)
            loss.backward()                     # 计算反向传播梯度
            optimizer.step()                    # 更新参数
            step += 1
            loss_record.append(loss.detach().item())
            
            # 将epoch数、loss展示在tqdm上
            train_pbar.set_description(f'Epoch [{epoch+1}/{n_epochs}]')
            train_pbar.set_postfix({'loss': loss.detach().item()})

        mean_train_loss = sum(loss_record)/len(loss_record)
        writer.add_scalar('Loss/train', mean_train_loss, step)

        model.eval() # 设置模型为评估模式
        loss_record = []
        for x, y in valid_loader:
            x, y = x.to(device), y.to(device)
            with torch.no_grad():
                pred = model(x)
                loss = criterion(pred, y)

            loss_record.append(loss.item())
            
        mean_valid_loss = sum(loss_record)/len(loss_record)
        print(f'Epoch [{epoch+1}/{n_epochs}]: Train loss: {mean_train_loss:.4f}, Valid loss: {mean_valid_loss:.4f}')
        writer.add_scalar('Loss/valid', mean_valid_loss, step)

        if mean_valid_loss < best_loss:
            best_loss = mean_valid_loss
            torch.save(model.state_dict(), config['save_path']) # Save your best model
            print('Saving model with loss {:.3f}...'.format(best_loss))
            early_stop_count = 0
        else: 
            early_stop_count += 1

        if early_stop_count >= config['early_stop']:
            print('\nModel is not improving, so we halt the training session.')
            return

tensorboard

原文中为空,为了方便使用tensorboard查询,将日志文件存放目录修改为了log
在终端运行指令:tensorboard --logdir=log

之后http://localhost:6006/查看

CosinAnnealingWarnRestars余弦退火

torch.optim.lr_scheduler.CosinAnnealingWarnRestars(optimizer,T_0=2,T_mult=2,eta_min=config['learning_rate'])

使用余弦退火设置学习率。其中 η m a x \eta_{max} ηmax为初始学习率, T c u r T_{cur} Tcur是子上次重新启动以来的周期数, T i T_i Ti是SGDR中两次热重启之间的epoch数

math—— η t = η m i n + 1 2 ( η m a x − η m i n ) ( 1 + cos ⁡ ( T c u r T i π ) ) \eta_t = \eta_{min} + \frac{1}{2}(\eta_{max} - \eta_{min})\left(1 + \cos\left(\frac{T_{cur}}{T_{i}}\pi\right)\right) ηt=ηmin+21(ηmaxηmin)(1+cos(TiTcurπ))

参数说明:

  • T_0 (int):第一次重新启动的迭代次数
  • T_mult(int,可选):因子在重新启动后增加 : T i T_i Ti,默认值:1
  • eta_min(float,可选):最低学习率: η m i n \eta_{min} ηmin,默认值:0。

学习率计划的大致设置

>>> for epoch in range(100):
>>>     train(...)
>>>     validate(...)
>>>     scheduler.step()

6. 训练结果

未修改模型的训练过程

以下是未修改代码时的训练过程

在这里插入图片描述

修改后模型的训练过程

使用tensorboard展示的训练过程中训练集和验证集的损失率变化过程

在这里插入图片描述

训练集和验证集的损失率

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值