第T11周:优化器对比实验

张开发
2026/4/20 4:34:44 15 分钟阅读

分享文章

第T11周:优化器对比实验
本文为365天深度学习训练营 中的学习记录博客 原作者K同学啊前言实验环境python 3.9.2 tensorflow 2.10.0 Jupyter Notebook: 7.4.5代码实现设置gpuimporttensorflowastf# 物理GPu列表gpustf.config.list_physical_devices(GPU)ifgpus:gpu0gpus[0]tf.config.experimental.set_memory_growth(gpu0,True)tf.config.set_visible_devices(gpus[0],GPU)# 确保只用第一张GPU导入数据importwarningsimportmatplotlib.pyplotaspltimportpathlib# 忽略警告warnings.filterwarnings(ignore)# 解决可视化显示时中文字符可能存在问题plt.rcParams[font.sans-serif][SimHei]# 用来正常显示中文标签plt.rcParams[axes.unicode_minus]False# 用来正常显示负号# 数据导入data_dir../../datasets/Facedata_dirpathlib.Path(data_dir)image_countlen(list(data_dir.glob(*/*)))print(图片总数为{}.format(image_count))数据加载train_dstf.keras.preprocessing.image_dataset_from_directory(data_dir,batch_size16,# 每批次处理的图像数量image_size(336,336),# 自动调整所有图片为该尺寸shuffleTrue,# 训练集打乱seed123,# 随机种子确保训练/验证集划分一致validation_split0.2,# 划分 20% 用于验证subsettraining,# 指定这是训练集)val_dstf.keras.preprocessing.image_dataset_from_directory(data_dir,batch_size16,# 每批次处理的图像数量image_size(336,336),# 自动调整所有图片为该尺寸seed123,# 随机种子确保训练/验证集划分一致validation_split0.2,# 划分 20% 用于验证subsetvalidation,# 指定这是验证集)输出标签class_namestrain_ds.class_namesprint(class_names)再次检查数据forimage_batch,labels_batchintrain_ds:print(image_batch.shape)print(labels_batch.shape)break预处理据集以及优化数据加载效率AUTOTUNEtf.data.AUTOTUNEdeftrain_preprocessing(image,label):return(image/255.0,label)train_ds(train_ds.cache().shuffle(1000).map(train_preprocessing).prefetch(buffer_sizeAUTOTUNE))val_ds(val_ds.cache().shuffle(1000).map(train_preprocessing).prefetch(buffer_sizeAUTOTUNE))数据可视化plt.figure(figsize(10,8))# 图形的宽为10高为5plt.suptitle(数据展示)forimages,labelsintrain_ds.take(1):foriinrange(15):plt.subplot(4,5,i1)plt.xticks([])plt.yticks([])plt.grid(False)# 显示图片plt.imshow(images[i])# 显示标签plt.xlabel(class_names[labels[i]-1])plt.show()构建模型fromtensorflow.keras.layersimportDropout,Dense,BatchNormalizationfromtensorflow.keras.modelsimportModel batch_size16img_height336img_width336# 定义模型创建函数defcreate_model():# 加载预训练模型vgg16_base_modeltf.keras.applications.vgg16.VGG16(weightsimagenet,include_topFalse,input_shape(img_height,img_width,3),poolingavg)# 开启微调只冻结前一部分层vgg16_base_model.trainableTrueforlayerinvgg16_base_model.layers[:-8]:# 冻结前几个卷积块解冻最后两个layer.trainableFalseXvgg16_base_model.output XDense(170,activationrelu)(X)XBatchNormalization()(X)XDropout(0.5)(X)outputDense(len(class_names),activationsoftmax)(X)modelModel(inputsvgg16_base_model.input,outputsoutput)returnmodel# 创建三个完全一致的模型实例model_adamcreate_model()# 用于 Adam (1e-5)model_sgd_conscreate_model()# 用于 组1完全一致的 SGD (1e-5, 无动量)model_sgd_faircreate_model()# 用于 组2公平对比的 SGD (1e-4, 有动量)# Adam 配置 (基准)model_adam.compile(optimizertf.keras.optimizers.Adam(learning_rate1e-5),losssparse_categorical_crossentropy,metrics[accuracy])# SGD 组1 (绝对一致性控制)model_sgd_cons.compile(optimizertf.keras.optimizers.SGD(learning_rate1e-5),# 不加 momentumlosssparse_categorical_crossentropy,metrics[accuracy])# SGD 组2 (算法潜力公平对比)model_sgd_fair.compile(optimizertf.keras.optimizers.SGD(learning_rate1e-4,momentum0.9),losssparse_categorical_crossentropy,metrics[accuracy])model_sgd_fair.summary()训练模型NO_EPOCHS20print(正在训练 Adam (1e-5)...)history_adammodel_adam.fit(train_ds,epochsNO_EPOCHS,validation_dataval_ds,verbose1)print(\n正在训练 SGD 组1 (完全一致 1e-5)...)history_sgd_consmodel_sgd_cons.fit(train_ds,epochsNO_EPOCHS,validation_dataval_ds,verbose1)print(\n正在训练 SGD 组2 (公平对比 1e-4 Momentum)...)history_sgd_fairmodel_sgd_fair.fit(train_ds,epochsNO_EPOCHS,validation_dataval_ds,verbose1)模型评估图对比frommatplotlib.tickerimportMultipleLocatorimportmatplotlib.gridspecasgridspecfromdatetimeimportdatetime# 设置绘图参数plt.rcParams[savefig.dpi]300plt.rcParams[figure.dpi]120# 屏幕显示不用太大# 设置支持中文的字体plt.rcParams[font.family][sans-serif]plt.rcParams[font.sans-serif][DejaVu Sans,Arial]current_timedatetime.now().strftime(%Y-%m-%d %H:%M:%S)# 提取 epoch 长度epochs_rangerange(len(history_adam.history[accuracy]))# 显著增加高度10 - 22让每个子图都有足够的空间figplt.figure(figsize(14,22))gsgridspec.GridSpec(3,1,height_ratios[1,1,1],hspace0.3)# 统一定义通用刻度定位器每2轮一个大刻度更精细major_locatorMultipleLocator(2)# 定义辅助绘图函数保证风格统一defplot_learning_curve(ax,history,title_text):ax.plot(epochs_range,history[accuracy],b-,linewidth2.5,labelTrain Acc)ax.plot(epochs_range,history[val_accuracy],b--,linewidth2.0,labelVal Acc)ax.plot(epochs_range,history[loss],r-,linewidth2.5,labelTrain Loss)ax.plot(epochs_range,history[val_loss],r--,linewidth2.0,labelVal Loss)ax.set_title(title_text,fontsize18,fontweightbold,pad15)ax.set_ylabel(Score / Value,fontsize14)# 底部加上时间戳ax.set_xlabel(fEpochs\n[Logged at:{current_time}],fontsize12)# 放在右下角设置背景透明度legendax.legend(loclower right,fontsize11,framealpha0.8,edgecolorgray)# 使用虚线看起来更高级ax.grid(True,linestyle--,alpha0.5,colorgray)# 设置刻度风格ax.tick_params(axisboth,whichmajor,labelsize11)ax.xaxis.set_major_locator(major_locator)# y轴刻度微调0到3.5间隔0.5ax.set_yticks([0,0.5,1.0,1.5,2.0,2.5,3.0,3.5])# Adam (1e-5) 基准组ax1fig.add_subplot(gs[0])plot_learning_curve(ax1,history_adam.history,1. Adam (LR1e-5)\n)# SGD (1e-5) 绝对一致组ax2fig.add_subplot(gs[1])# 这里 y 轴刻度由于 Loss 较高不手动设置yticks让其自适应显示细节ax2.set_yticks([])plot_learning_curve(ax2,history_sgd_cons.history,2. SGD (LR1e-5)\n)# 针对组1的 Loss 过高3.0单独微调 y轴ax2.set_ylim(-0.1,4.0)# SGD (1e-4 Mom) 公平对比组ax3fig.add_subplot(gs[2])plot_learning_curve(ax3,history_sgd_fair.history,3. SGD (LR1e-4, Momentum0.9)\n)plt.tight_layout()plt.show()直观对比importpandasaspddefcompare_three_models_report(m_adam,m_sgd_cons,m_sgd_fair):# 分别评估三个模型score_adamm_adam.evaluate(val_ds,verbose0)score_consm_sgd_cons.evaluate(val_ds,verbose0)score_fairm_sgd_fair.evaluate(val_ds,verbose0)# 组织对比数据results{评估指标 (Metric):[Loss (损失值),Accuracy (准确率)],Adam (1e-5):[f{score_adam[0]:.4f},f{score_adam[1]:.2%}],SGD 一致组 (1e-5):[f{score_cons[0]:.4f},f{score_cons[1]:.2%}],SGD 公平组 (1e-4Mom):[f{score_fair[0]:.4f},f{score_fair[1]:.2%}]}dfpd.DataFrame(results)print(三组模型最终评估对比)print(df.to_string(indexFalse))# 结论输出best_accmax(score_adam[1],score_cons[1],score_fair[1])ifbest_accscore_adam[1]:winnerAdam (1e-4)elifbest_accscore_fair[1]:winnerSGD 公平对比组else:winnerSGD 一致组print(f表现最优模型{winner})# 调用对比函数compare_three_models_report(model_adam,model_sgd_cons,model_sgd_fair)学习总结我进行了 Adam (1e-5)、SGD 一致组 (1e-5) 与 SGD 公平组 (1e-4 Momentum) 三个对照模型。“SGD 一致组”设计SGD (1e-5)这个组初衷是为了通过绝对控制变量来观察算法的本性。收获实验结果显示在与 Adam 完全一致的微小步长下SGD 的准确率曲线几乎没有进步。这让我直观感受到了非自适应优化器的局限性——它缺乏 Adam 那种自动放大梯度的能力。在微调 VGG16 这种深层网络时如果步长给得不够“狠”SGD 根本无法跨越损失函数的重重障碍。这有力地反衬了 Adam 在超参设置不精确时依然能凭借其自适应机制展现出强大的参数容错率和初期爆发力。“SGD 公平组”挖掘算法的真实上限如果只看一致组我会得出“SGD 没法用”的错误结论。因此我通过引入 100 倍学习率 (1e-4) 并配合 0.9 的动量 (Momentum) 专门设计了“公平对比组”。收获这一组的设计是为了观察在各自最佳状态下传统算法与自适应算法的对比。我发现SGD 组 2 的表现发生了质变。虽然前 5 轮落后于 Adam但在第 20 轮也稳稳达到了不错的效果这次实验最大的收获在于我意识到没有绝对“垃圾”的优化器只有不被理解的参数组合。通过这三个模型的对照我了解到除了控制变量做对比以外还有结合优化器特性公平对比看潜力的对比方式。

更多文章