以机器学习为背景,什么样的机器学习小技巧、代码风格比较好?

   不同人编程时的思路不一样,本人想将学习过程中看到的、个人认为比较好的机器学习小技巧、代码写作风格整理一下,便于以后写程序时去用(持续整理)。

  1. 机器学习时会需要调整许多hyper-parameters,有些时候可能我们将这些超参数左定义一个,右定义一个,等程序庞大后,就容易忘记有哪些hyper-parameters了,一个有效的办法是将这些超参数统一整理成一个字典。例如:

    config = {
        'n_epochs': 3000,                # maximum number of epochs
        'batch_size': 270,               # mini-batch size for dataloader
        'optimizer': 'SGD',              # optimization algorithm (optimizer in torch.optim)
        'optim_hparas': {                # hyper-parameters for the optimizer (depends on which optimizer you are using)
            'lr': 0.001,                 # learning rate of SGD
            'momentum': 0.9              # momentum for SGD
        },
        'early_stop': 200,               # early stopping epochs (the number epochs since your model's last improvement)
        'save_path': 'models/model.pth'  # your model will be saved here
    }
    
  2. 许多时候我们选择将loss value打印出来,但我感觉这样并不是很清晰,难以直截了当的看出是否过拟合,这样的话可以加上如下可视化代码:

    def plot_learning_curve(train_loss, valid_loss, title='', train_loss_is_batch_period=True):
        ''' Plot learning curve of your DNN (train & dev loss) '''
        # 验证集通常是一个epoch算一次loss,训练集有时候会一个batch算一次loss,
        # 而一个epoch有n个batch,造成不对等: 需要进行一步“数据对齐”转换
        if train_loss_is_batch_period == True:
            total_steps = len(train_loss)
            x_train = range(total_steps)
            x_valid = x_train[::total_steps // len(valid_loss)]    #双斜杠取整数
            plt.figure(figsize=(6, 4))
            plt.plot(x_train, train_loss, c='red', label='train')
            plt.plot(x_valid, valid_loss, c='cyan', label='valid')
            plt.ylim(0.0, 5.)
            plt.xlabel('Training steps')
            plt.ylabel('loss')
            plt.title('Learning curve of {}'.format(title))
            plt.grid()
            plt.legend('best')
        # 验证集是一个epoch算一次loss,训练集也是一个epoch算一次loss时
        else:
            total_steps = len(train_loss)
            x_train = range(total_steps)
            x_valid = x_train
            plt.figure(figsize=(6, 4))
            plt.plot(x_train, train_loss, c='red', label='train')
            plt.plot(x_valid, valid_loss, c='cyan', label='dev')
            plt.ylim(0.0, 5.)
            plt.xlabel('Training steps')
            plt.ylabel('loss')
            plt.title('Learning curve of {}'.format(title))
            plt.grid()
            plt.legend('best')
    
  3. 我们可以根据训练的表现,将程序提前中止! 为什么需要这样做呢,个人认为:因为你肯定需要设置epoch,如果epoch设置小了,那么这次epoch的时间就白白浪费了(当然,也可以将参数保存起来,这是后话),再重新运行程序重新跑,就需要再次浪费时间了。那好,我把epoch直接设置的非常大不就行了,也不行,因为epoch很大就会占用很长的时间。所以呢,我们可以根据训练的表现,提前设置程序中止(当然,按照李宏毅老师的说法,他们有一次训练模型直到2天后loss value才下降,所以要根据任务要求选择是否采用这一策略)。
       那么具体怎么做呢?参考李宏毅老师2021作业1的做法: 假设训练了一段时间后验证集loss valueL若此后有连续n个epoch的值都大于这个L,就说明程序可能过拟合了(训练到头了)

            # After each epoch, test your model on the validation (development) set.
            dev_mse = dev(dv_set, model, device)
            if dev_mse < min_mse:
                # Save model if your model improved
                min_mse = dev_mse
                print('Saving model (epoch = {:4d}, loss = {:.4f})'
                    .format(epoch + 1, min_mse))
                torch.save(model.state_dict(), config['save_path'])  # Save model to specified path
                early_stop_cnt = 0
            else:
                early_stop_cnt += 1
    
            epoch += 1
            loss_record['dev'].append(dev_mse)
            if early_stop_cnt > config['early_stop']:
                # Stop training if your model stops improving for "config['early_stop']" epochs.
                break    #注意程序最前面有个for循环,碍于空间没有将for循环贴出
    
  4. 我们可以根据神经网络的不同,选择手动初始化特定参数。
    一般有两种方法,第一种是使用先定义初始化模型方法;再使用apply()函数

     # 1. 根据网络层的不同定义不同的初始化方式     
    def weight_init(m):
        if isinstance(m, nn.Linear):
            nn.init.xavier_normal_(m.weight)
            nn.init.constant_(m.bias, 0)
        # 也可以判断是否为conv2d,使用相应的初始化方式 
        elif isinstance(m, nn.Conv2d):
            nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
         # 是否为批归一化层
        elif isinstance(m, nn.BatchNorm2d):
            nn.init.constant_(m.weight, 1)
            nn.init.constant_(m.bias, 0)
    # 2. 初始化网络结构        
    model = Net(in_dim, n_hidden_1, n_hidden_2, out_dim)
    # 3. 将weight_init应用在子模块上
    model.apply(weight_init)
    

    第二种方法:定义在模型中,利用self.modules()来进行循环

    class Net(nn.Module):
        
        
        def __init__(self, in_dim, n_hidden_1, n_hidden_2, out_dim):
            super().__init__()
    
            self.layer = nn.Sequential(
                nn.Linear(in_dim, n_hidden_1), 
                nn.ReLU(True),
                nn.Linear(n_hidden_1, n_hidden_2),
                nn.ReLU(True),
                nn.Linear(n_hidden_2, out_dim)
                
                 )    
           for m in self.modules():
                 if isinstance(m, nn.Conv2d):
                     nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
                 elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)):
                     nn.init.constant_(m.weight, 1)
                     nn.init.constant_(m.bias, 0)
        def forward(self, x):
            x = self.layer1(x)
            x = self.layer2(x)
            x = self.layer3(x)
            return x
    

详细的可参考这两篇文章:文章1 文章2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值