Seaborn单图可视化:映射点尺寸为loss对比三优化器训练耗时表现
问题根因
- 多次调用
sns.relplot生成独立画布的本质原因:relplot是seaborn的figure级绘图接口,每次调用都会新建独立的Figure和Axes对象,即便绘图参数一致,也会生成独立的子图区域,无法自动合并到同一张画布 - 原生
plt.scatter无法实现loss尺寸映射的本质原因:逐次调用scatter时没有把对应分组的loss值和点尺寸参数做绑定,传入的s是固定数值,自然没法随loss值动态调整点的大小
可复现实现步骤
第一步:重构为长表格式
不管用哪个绘图接口,首先要把原来的宽格式实验记录(6组独立序列)转成tidy data结构,每一行对应单条观测,包含输入规模、所属优化器、训练耗时、最终loss四个字段,这是seaborn自动做视觉映射的前提。
import pandas as pd import numpy as np import seaborn as sns import matplotlib.pyplot as plt # 实际使用时替换为你自己的实验采集数据 input_sizes = np.arange(1000, 10001, 1000) # 步长1000,从1000到10001的输入规模 time_sgd = np.random.uniform(0.5, 2, 10) time_rmsprop = np.random.uniform(0.4, 1.8, 10) time_adam = np.random.uniform(0.6, 2.2, 10) loss_sgd = np.random.uniform(0.2, 0.8, 10) loss_rmsprop = np.random.uniform(0.1, 0.5, 10) loss_adam = np.random.uniform(0.05, 0.3, 10) # 转换为seaborn绘图要求的长表结构 records = [] for idx, size in enumerate(input_sizes): records.append({ "input_size": size, "optimizer": "SGD", "train_time": time_sgd[idx], "final_loss": loss_sgd[idx] }) records.append({ "input_size": size, "optimizer": "RMSProp", "train_time": time_rmsprop[idx], "final_loss": loss_rmsprop[idx] }) records.append({ "input_size": size, "optimizer": "Adam", "train_time": time_adam[idx], "final_loss": loss_adam[idx] }) df = pd.DataFrame(records)
第二步:单图完成全维度映射
这里要避开一个常见误区:不需要为每个优化器单独调用一次绘图接口。seaborn的分类视觉映射是在同一坐标系内完成分组绘制的,只要把分类字段传给hue参数,就能自动在同一个坐标轴内画出不同类别的散点,不会生成独立画布。
sns.set_style("whitegrid") g = sns.relplot( data=df, x="input_size", y="train_time", hue="optimizer", # 按优化器类别自动分配区分色 size="final_loss", # 按最终loss值自动调整散点大小 sizes=(40, 220), # 约束散点尺寸范围,避免点过密或过大 alpha=0.65, # 加透明度减少重叠遮挡 height=6, aspect=1.2 ) # 优化图表标注可读性 g.set_axis_labels("输入数据集样本规模", "10轮训练总耗时(秒)") g.fig.suptitle("不同优化器训练效率与最终损失对比", y=1.03) plt.show()
生成的单图会同时满足两个核心需求:
- 三个优化器的散点全部在同一坐标轴内,通过颜色区分系列,自动生成对应图例
- 每个散点的大小直接对应自身的最终loss值,seaborn会自动生成尺寸对照图例,不需要手动调整点大小
原生matplotlib等效实现
如果不想依赖seaborn的自动映射逻辑,用原生plt.scatter也可以实现同样效果,核心是把每个分组的loss值归一化到合理的尺寸范围,作为s参数传入:
fig, ax = plt.subplots(figsize=(8, 6)) # 统一取全量数据的loss极值做尺寸归一化基准 loss_min, loss_max = df["final_loss"].min(), df["final_loss"].max() size_range_min, size_range_max = 40, 220 optimizer_config = { "SGD": "#1f77b4", "RMSProp": "#ff7f0e", "Adam": "#2ca02c" } for opt_name, color in optimizer_config.items(): opt_subset = df[df["optimizer"] == opt_name] # 把loss值线性映射到指定的尺寸区间 scaled_point_size = ((opt_subset["final_loss"] - loss_min) / (loss_max - loss_min)) * (size_range_max - size_range_min) + size_range_min ax.scatter( opt_subset["input_size"], opt_subset["train_time"], s=scaled_point_size, c=color, label=opt_name, alpha=0.65 ) ax.set_xlabel("输入数据集样本规模") ax.set_ylabel("10轮训练总耗时(秒)") ax.legend(title="优化器类型") plt.show()
实现时注意两个细节:
- 尺寸归一化一定要用全量实验数据的loss极值做基准,不能每个优化器单独算归一化值,否则相同loss对应的点大小不一致,会破坏对比的公平性
- 如果同一输入规模下三个优化器的训练耗时差异极小,点完全重叠,可以给每个优化器的x值加±20的微小偏移,让三个点在x方向错开位置,避免遮挡
内容的提问来源于stack exchange,提问作者Nitin1901
相关产品推荐
相关产品推荐

