AI-02a5a8.神经网络-与学习相关的技巧-超参数的验证

发布于:2025-05-24 ⋅ 阅读:(16) ⋅ 点赞:(0)

介绍

神经网络中,除了权重和偏置等参数,超参数(hyper-parameter)也经常出现。这里所说的超参数是指,比如各层的神经元数量、batch大小、参数更新时的学习率或权值衰减等。

如果这些超参数没有设置合适的值,模型的性能就会很差。虽然超参数的取值非常重要,但是在决定超参数的过程中一般会伴随很多的试错。

验证数据

复习一下,之前将数据划分为训练数据和测试数据,训练数据用于学习,测试数据用于评估泛化能力。由此,就可以评估是否只过度拟合了训练数据(是否发生了过拟合),以及泛化能力如何等。

现在我们要对超参数设置各种各样的值进行验证。
但是,不能使用测试数据评估超参数的性能。
如果使用测试数据调整超参数,超参数的值会对测试数据发生过拟合。换句话说,用测试数据确认超参数的值的“好坏”,就会导致超参数的值被调整为只拟合测试数据。这样的话,可能就会得到不能拟合其他数据、泛化能力低的模型。

因此,调整超参数时,必须使用超参数专用的确认数据。用于调整超参数的数据,一般称为验证数据(validation data)。我们使用这个验证数据来评估超参数的好坏。

训练数据用于参数(权重和偏置)的学习,
验证数据用于超参数的性能评估。
为了确认泛化能力,要在最后使用(比较理想的是只用一次)测试数据。

根据不同的数据集,有的会事先分成训练数据、验证数据、测试数据三部分,有的只分成训练数据和测试数据两部分,有的则不进行分割。在这种情况下,用户需要自行进行分割。

对 MINIST 分割 20% 的数据作为验证数据。

(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)

# 为了实现高速化,减少训练数据
x_train = x_train[:500]
t_train = t_train[:500]
  
# 分割验证数据
validation_rate = 0.20
validation_num = int(x_train.shape[0] * validation_rate)

# 打乱训练数据,数据集的数据可能存在偏向(比如,数据从“0”到“10”按顺序排列等)。
x_train, t_train = shuffle_dataset(x_train, t_train)

x_val = x_train[:validation_num]
t_val = t_train[:validation_num]
x_train = x_train[validation_num:]
t_train = t_train[validation_num:]

超参数的优化

进行超参数的最优化时,逐渐缩小超参数的“好值”的存在范围非常重要。所谓逐渐缩小范围,是指一开始先大致设定一个范围,从这个范围中随机选出一个超参数(采样),用这个采样到的值进行识别精度的评估;然后,多次重复该操作,观察识别精度的结果,根据这个结果缩小超参数的“好值”的范围。通过重复这一操作,就可以逐渐确定超参数的合适范围。

有报告显示,在进行神经网络的超参数的最优化时,与网格搜索等有规律的搜索相比,随机采样的搜索方式效果更好。这是因为在多个超参数中,各个超参数对最终的识别精度的影响程度不同。

超参数的范围只要“大致地指定”就可以了。所谓“大致地指定”,是指像 0.001 ( 10 − 3 ) 0.001(10^{-3}) 0.001(103) 1000 ( 10 3 ) 1000(10^3) 1000(103)这样,以“10的阶乘”的尺度指定范围(也表述为“用对数尺度(log scale)指定”)。在超参数的最优化中,要注意的是深度学习需要很长时间(比如,几天或几周)。因此,在超参数的搜索中,需要尽早放弃那些不符合逻辑的超参数。于是,在超参数的最优化中,减少学习的epoch,缩短一次评估所需的时间是一个不错的办法。

以上就是超参数的最优化的内容,简单归纳一下,如下所示。

步骤0:设定超参数的范围
步骤1:从设定的超参数范围中随机采样
步骤2:使用步骤1中采样到的超参数的值进行学习,通过验证数据评估识别精度(但是要将epoch设置得很小)。
步骤3:重复步骤1和步骤2(100次等),根据它们的识别精度的结果,缩小超参数的范围。

反复进行上述操作,不断缩小超参数的范围,在缩小到一定程度时,从该范围中选出一个超参数的值。这就是进行超参数的最优化的一种方法。

这个超参数的最优化方法是实践性的方法。如果需要更精炼的方法,可以使用贝叶斯最优化(Bayesian optimization)。贝叶斯最优化运用以贝叶斯定理为中心的数学理论,能够更加严密、高效地进行最优化。

实现

权值衰减系数的初始范围为 10 − 8 10^{−8} 108 10 − 4 10^{−4} 104 ,学习率的初始范围为 10 − 6 10^{−6} 106 10 − 2 10^{−2} 102 ,超参数的随机采样的代码:

weight_decay = 10 ** np.random.uniform(-8, -4)
lr = 10 ** np.random.uniform(-6, -2)

在这里插入图片描述

按识别精度从高到低的顺序排列了验证数据的学习的变化。从图中可知,直到“Best-5”左右,学习进行得都很顺利。因此,我们来观察一下“Best-5”之前的超参数的值(学习率和权值衰减系数),结果如下所示。

Best-1(val acc:0.78) | lr:0.008613737851291131, weight decay:1.6230648956426255e-06
Best-2(val acc:0.76) | lr:0.009380490707420194, weight decay:4.6241431946184976e-07
Best-3(val acc:0.75) | lr:0.009809697440393613, weight decay:2.451147891938336e-05
Best-4(val acc:0.73) | lr:0.008205453614808886, weight decay:9.725627194334811e-05
Best-5(val acc:0.7) | lr:0.006055287804365819, weight decay:1.8120289170540883e-06

从这个结果可以看出,学习率在0.001到0.01、权值衰减系数在 10 − 8 10^{−8} 108 10 − 6 10^{−6} 106 之间时,学习可以顺利进行。像这样,观察可以使学习顺利进行的超参数的范围,从而缩小值的范围。然后,在这个缩小的范围中重复相同的操作。这样就能缩小到合适的超参数的存在范围,然后在某个阶段,选择一个最终的超参数的值。

hyperparameter_optimization.py

def shuffle_dataset(x, t):
    """打乱数据集
    Parameters
    ----------
    x : 训练数据
    t : 监督数据

    Returns
    -------
    x, t : 打乱的训练数据和监督数据
    """
    permutation = np.random.permutation(x.shape[0])
    x = x[permutation,:] if x.ndim == 2 else x[permutation,:,:,:]
    t = t[permutation]
    return x, t
# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # 为了导入父目录的文件而进行的设定
import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from common.multi_layer_net import MultiLayerNet
from common.util import shuffle_dataset
from common.trainer import Trainer

(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)
# 为了实现高速化,减少训练数据
x_train = x_train[:500]
t_train = t_train[:500]

# 分割验证数据
validation_rate = 0.20
validation_num = int(x_train.shape[0] * validation_rate)

# 打乱训练数据,数据集的数据可能存在偏向(比如,数据从“0”到“10”按顺序排列等)。
x_train, t_train = shuffle_dataset(x_train, t_train)
x_val = x_train[:validation_num]
t_val = t_train[:validation_num]
x_train = x_train[validation_num:]
t_train = t_train[validation_num:]

def __train(lr, weight_decay, epocs=50):
    network = MultiLayerNet(input_size=784, hidden_size_list=[100, 100, 100, 100, 100, 100],
                            output_size=10, weight_decay_lambda=weight_decay)
    trainer = Trainer(network, x_train, t_train, x_val, t_val,
                      epochs=epocs, mini_batch_size=100,
                      optimizer='sgd', optimizer_param={'lr': lr}, verbose=False)
    trainer.train()

    return trainer.test_acc_list, trainer.train_acc_list

# 超参数的随机搜索======================================
optimization_trial = 100
results_val = {}
results_train = {}
for _ in range(optimization_trial):
    # 指定搜索的超参数的范围===============
    weight_decay = 10 ** np.random.uniform(-8, -4)
    lr = 10 ** np.random.uniform(-6, -2)
    # ================================================
    val_acc_list, train_acc_list = __train(lr, weight_decay)
    print("val acc:" + str(val_acc_list[-1]) + " | lr:" + str(lr) + ", weight decay:" + str(weight_decay))
    key = "lr:" + str(lr) + ", weight decay:" + str(weight_decay)
    results_val[key] = val_acc_list
    results_train[key] = train_acc_list

# 绘制图形========================================================
print("=========== Hyper-Parameter Optimization Result ===========")
graph_draw_num = 20
col_num = 5
row_num = int(np.ceil(graph_draw_num / col_num))
i = 0
  
for key, val_acc_list in sorted(results_val.items(), key=lambda x:x[1][-1], reverse=True):
    print("Best-" + str(i+1) + "(val acc:" + str(val_acc_list[-1]) + ") | " + key)
    plt.subplot(row_num, col_num, i+1)
    plt.title("Best-" + str(i+1))
    plt.ylim(0.0, 1.0)
    if i % 5: plt.yticks([])
    plt.xticks([])
    x = np.arange(len(val_acc_list))
    plt.plot(x, val_acc_list)
    plt.plot(x, results_train[key], "--")
    i += 1
    if i >= graph_draw_num:
        break

plt.show()

网站公告

今日签到

点亮在社区的每一天
去签到