AMS机器学习课程:预测雷暴旋转的基础机器学习 - 超参数试验

目录

本文翻译自 AMS 机器学习 Python 教程,并有部分修改。

Lagerquist, R., and D.J. Gagne II, 2019: “Basic machine learning for predicting thunderstorm rotation: Python tutorial”. https://github.com/djgagne/ams-ml-python-course/blob/master/module_2/ML_Short_Course_Module_2_Basic.ipynb.

本文接上一篇文章

AMS机器学习课程:预测雷暴旋转的基础机器学习 - 正则化

L1 和 L2 正则化的超参数试验

接下来的几个单元格将向您展示如何进行“超参数实验”。 超参数实验的步骤如下。

  1. 选择要尝试的值。 这通常基于对模型如何工作的一些先验知识。 您拥有的专业知识越多,可以尝试的值范围就越窄。在这种情况下,我们尝试

\begin{equation*} \lambda_1 \in \lbrace 10^{-8}, 10^{-7.5}, 10^{-7}, 10^{-6.5}, 10^{-6}, 10^{-5.5}, 10^{-5}, 10^{-4.5}, 10^{-4} \rbrace \end{equation*}

\begin{equation*} \lambda_2 \in \lbrace 10^{-4}, 10^{-3.5}, 10^{-3}, 10^{-2.5}, 10^{-2}, 10^{-1.5}, 10^{-1}, 10^{-0.5}, 10^{0}, 10^{0.5}, 10^{1} \rbrace \end{equation*}

  1. 使用每种超参数组合训练模型。 在这种情况下,lambda_{1} 有9个值,lambda_{2} 有 11 个值,因此有 99 个组合。这称为“网格搜索(grid search)”。 (注意:除了网格搜索,还有其他搜索方法。这些方法特别有用,特别是当组合的数量太大(“组合爆炸”)时,如果您尝试的是几个以上的超参数,通常会发生这种情况。在这种情况下,您可以进行 random search 或 beam search,使用遗传算法来进化超参数,等等。但是在此模块中,我们将坚持使用网格搜索。)

  2. 根据验证数据评估每个模型

  3. 查找在验证数据上表现最佳的模型。 同样,有很多方法可以定义“最佳”。在这里,我们将选择 MAE 技能得分最高的模型。

超参数试验:训练

下一个单元格执行超参数实验的步骤 1 和 2(定义要尝试的值并训练模型)。

下面的代码会使用不同的参数组合训练 ElasticNet 回归模型,并将模型的评估结果保存到数组中。

lambda1_values = np.logspace(-8, -4, num=9)
lambda2_values = np.logspace(-4, 1, num=11)

num_lambda1 = len(lambda1_values)
num_lambda2 = len(lambda2_values)

validation_mae_matrix_s01 = np.full(
    (num_lambda1, num_lambda2),
    np.nan,
)

validation_mse_matrix_s02 = np.full(
    (num_lambda1, num_lambda2),
    np.nan
)

validation_mae_skill_matrix = np.full(
    (num_lambda1, num_lambda2),
    np.nan
)

validation_mse_skill_matrix = np.full(
    (num_lambda1, num_lambda2),
    np.nan
)

mean_training_target_value = np.mean(
    training_target_table[TARGET_NAME].values
)

from tqdm.notebook import tnrange, tqdm_notebook


for i in tqdm_notebook(range(num_lambda1)):
    for j in tqdm_notebook(range(num_lambda2), leave=False):      
        model = setup_linear_regression(
            lambda1=lambda1_values[i],
            lambda2=lambda2_values[j],
        )
        
        _ = train_linear_regression(
            model=model,
            training_predictor_table=training_predictor_table,
            training_target_table=training_target_table
        )

        validation_predictions = model.predict(
            validation_predictor_table.values
        )

        evaluation_dict = evaluate_regression(
            target_values=validation_target_table[TARGET_NAME].values,
            predicted_target_values=validation_predictions,
            mean_training_target_value=mean_training_target_value,
            verbose=False, 
            create_plots=False
        )
        
        validation_mae_matrix_s01[i, j] = evaluation_dict[MAE_KEY]
        validation_mse_matrix_s02[i, j] = evaluation_dict[MSE_KEY]
        validation_mae_skill_matrix[i, j] = evaluation_dict[MAE_SKILL_SCORE_KEY]
        validation_mse_skill_matrix[i, j] = evaluation_dict[MSE_SKILL_SCORE_KEY]

验证

下一个单元格执行超参数实验的步骤 3(在验证数据上评估每个模型)。

使用到的函数

plot_scores_2d 函数绘制二维网格评分图

import matplotlib.colors

def plot_scores_2d(
    score_matrix: np.ndarray, 
    min_colour_value: float, 
    max_colour_value: float, 
    x_tick_labels: np.ndarray,
    y_tick_labels: np.ndarray, 
    colour_map_object=pyplot.cm.plasma
):
    _, axes_object = pyplot.subplots(
        1, 1, 
        figsize=(10, 10)
    )
    
    # 绘制网格图
    pyplot.imshow(
        score_matrix, 
        cmap=colour_map_object, 
        origin='lower',
        vmin=min_colour_value, 
        vmax=max_colour_value
    )

    x_tick_values = np.linspace(
        0, score_matrix.shape[1] - 1, 
        num=score_matrix.shape[1],
        dtype=float
    )
    y_tick_values = np.linspace(
        0, score_matrix.shape[0] - 1, 
        num=score_matrix.shape[0], 
        dtype=float
    )

    pyplot.xticks(x_tick_values, x_tick_labels)
    pyplot.yticks(y_tick_values, y_tick_labels)
    
    # 绘制颜色条
    _add_colour_bar(
        axes_object=axes_object, 
        colour_map_object=colour_map_object,
        values_to_colour=score_matrix, 
        min_colour_value=min_colour_value,
        max_colour_value=max_colour_value
    )

_add_colour_bar 函数向已有的图形中添加颜色条,返回创建的颜色条

def _add_colour_bar(
    axes_object, 
    colour_map_object: matplotlib.pyplot.cm, 
    values_to_colour: np.ndarray, 
    min_colour_value: float,
    max_colour_value: float, 
    colour_norm_object=None,
    orientation_string='vertical',
    extend_min=True, 
    extend_max=True
) -> matplotlib.pyplot.colorbar:
    if colour_norm_object is None:
        # 归一化函数
        colour_norm_object = matplotlib.colors.Normalize(
            vmin=min_colour_value, 
            vmax=max_colour_value, 
            clip=False
        )
    
    # 将标量值映射到 RGBA
    scalar_mappable_object = pyplot.cm.ScalarMappable(
        cmap=colour_map_object, 
        norm=colour_norm_object
    )
    scalar_mappable_object.set_array(values_to_colour)

    if extend_min and extend_max:
        extend_string = 'both'
    elif extend_min:
        extend_string = 'min'
    elif extend_max:
        extend_string = 'max'
    else:
        extend_string = 'neither'

    if orientation_string == 'horizontal':
        padding = 0.075
    else:
        padding = 0.05

    colour_bar_object = pyplot.colorbar(
        ax=axes_object, 
        mappable=scalar_mappable_object,
        orientation=orientation_string, 
        pad=padding, 
        extend=extend_string,
        shrink=0.8
    )

    colour_bar_object.ax.tick_params(labelsize=20)
    return colour_bar_object

验证

MAE

plot_scores_2d(
    score_matrix=validation_mae_matrix_s01,
    min_colour_value=np.percentile(validation_mae_matrix_s01, 1.),
    max_colour_value=np.percentile(validation_mae_matrix_s01, 99.),
    x_tick_labels=np.log10(lambda2_values),
    y_tick_labels=np.log10(lambda1_values)
)

pyplot.xlabel(r'log$_{10}$ of ridge coefficient ($\lambda_2$)')
pyplot.ylabel(r'log$_{10}$ of lasso coefficient ($\lambda_1$)')
pyplot.title(r'Mean absolute error (s$^{-1}$) on validation data')

MSE

plot_scores_2d(
    score_matrix=validation_mse_matrix_s02,
    min_colour_value=np.percentile(validation_mse_matrix_s02, 1.),
    max_colour_value=np.percentile(validation_mse_matrix_s02, 99.),
    x_tick_labels=np.log10(lambda2_values),
    y_tick_labels=np.log10(lambda1_values)
)

pyplot.xlabel(r'log$_{10}$ of ridge coefficient ($\lambda_2$)')
pyplot.ylabel(r'log$_{10}$ of lasso coefficient ($\lambda_1$)')
pyplot.title(r'Mean squared error (s$^{-2}$) on validation data')

MAE 技巧评分

plot_scores_2d(
    score_matrix=validation_mae_skill_matrix,
    min_colour_value=np.percentile(validation_mae_skill_matrix, 1.),
    max_colour_value=np.percentile(validation_mae_skill_matrix, 99.),
    x_tick_labels=np.log10(lambda2_values),
    y_tick_labels=np.log10(lambda1_values)
)

pyplot.xlabel(r'log$_{10}$ of ridge coefficient ($\lambda_2$)')
pyplot.ylabel(r'log$_{10}$ of lasso coefficient ($\lambda_1$)')
pyplot.title(r'MAE skill score on validation data')

MSE 技巧评分

plot_scores_2d(
    score_matrix=validation_mse_skill_matrix,
    min_colour_value=np.percentile(validation_mse_skill_matrix, 1.),
    max_colour_value=np.percentile(validation_mse_skill_matrix, 99.),
    x_tick_labels=np.log10(lambda2_values),
    y_tick_labels=np.log10(lambda1_values)
)

pyplot.xlabel(r'log$_{10}$ of ridge coefficient ($\lambda_2$)')
pyplot.ylabel(r'log$_{10}$ of lasso coefficient ($\lambda_1$)')
pyplot.title(r'MSE skill score on validation data')

选择

下一个单元格执行超参数实验的第 4 步(选择模型)。

best_linear_index = np.argmax(
    np.ravel(validation_mae_skill_matrix)
)
best_linear_index
33

注:

np.ravel 将多维数组展开成一维数组

np.argmax 返回最大值的索引

best_lambda1_index, best_lambda2_index = np.unravel_index(
    best_linear_index,
    (len(lambda1_values), len(lambda2_values))
)
best_lambda1_index, best_lambda2_index
(3, 0)

注:

np.unravel_index 将平面索引转换成坐标系统中的坐标

best_lambda1 = lambda1_values[best_lambda1_index]
best_lambda2 = lambda2_values[best_lambda2_index]
best_validation_maess = np.max(validation_mae_skill_matrix)

print(
    f'Best MAE skill score on validation data = {best_validation_maess:.3f} ...\ncorresponding '
    f'lasso coeff = 10^{np.log10(best_lambda1):.1f}, '
    f'ridge coeff = 10^{np.log10(best_lambda2):.1f}'
)
Best MAE skill score on validation data = 0.316 ...
corresponding lasso coeff = 10^-6.5, ridge coeff = 10^-4.0

构造并训练模型

final_model = setup_linear_regression(
    lambda1=best_lambda1,
    lambda2=best_lambda2,
)

_ = train_linear_regression(
    model=final_model,
    training_predictor_table=training_predictor_table,
    training_target_table=training_target_table,
)

在测试集上的性能

testing_predictions = final_model.predict(
    testing_predictor_table.values
)

mean_training_target_value = np.mean(
    training_target_table[TARGET_NAME].values
)

evaluation_dict = evaluate_regression(
    target_values=testing_target_table[TARGET_NAME].values,
    predicted_target_values=testing_predictions,
    mean_training_target_value=mean_training_target_value,
    dataset_name="testing",
)
Testing MAE (mean absolute error) = 8.257e-04 s^-1
Testing MSE (mean squared error) = 1.413e-06 s^-2
Testing bias (mean signed error) = -8.985e-05 s^-1
Testing MAE skill score (improvement over climatology) = 0.338
Testing MSE skill score (improvement over climatology) = 0.555

练习

基于第一个超参数实验,编写您自己的超参数实验(使用 lambda1 和 lambda2 值的不同集合)。 看看是否可以找到在验证数据上更有效的 lambda1-lambda2 组合。 如果找到在验证数据上更有效的组合,请查看它在测试数据上是否也更好。 如果不是,则 lambda1 和 lambda2在验证数据上过拟合。

参考

https://github.com/djgagne/ams-ml-python-course

AMS 机器学习课程

AMS机器学习课程:数据分析与预处理

AMS机器学习课程:预测雷暴旋转的基础机器学习 - 数据

AMS机器学习课程:预测雷暴旋转的基础机器学习 - 线性回归

AMS机器学习课程:预测雷暴旋转的基础机器学习 - 正则化