不同人编程时的思路不一样,本人想将学习过程中看到的、个人认为比较好的机器学习小技巧、代码写作风格整理一下,便于以后写程序时去用(持续整理)。
-
机器学习时会需要调整许多
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 }
-
许多时候我们选择将
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')
-
我们可以根据训练的表现,将程序提前中止! 为什么需要这样做呢,个人认为:因为你肯定需要设置epoch,如果epoch设置小了,那么这次epoch的时间就白白浪费了(当然,也可以将参数保存起来,这是后话),再重新运行程序重新跑,就需要再次浪费时间了。那好,我把epoch直接设置的非常大不就行了,也不行,因为epoch很大就会占用很长的时间。所以呢,我们可以根据训练的表现,提前设置程序中止(当然,按照李宏毅老师的说法,他们有一次训练模型直到2天后loss value才下降,所以要根据任务要求选择是否采用这一策略)。
那么具体怎么做呢?参考李宏毅老师2021作业1的做法: 假设训练了一段时间后,验证集的loss value
是L
,若此后有连续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循环贴出
-
我们可以根据神经网络的不同,选择手动初始化特定参数。
一般有两种方法,第一种是使用先定义初始化模型方法;再使用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