plot函数多曲线对比图绘制

plot函数多曲线对比图绘制

Python中的matplotlib包堪称二维统计图表神器之一,一句import matplotlib.pyplot as plt 也是科研人员必备的武器。但是如何绘制漂亮的图表、特别是多个二维曲线对比的图表,是新手科研人员苦恼的问题。下面,我会结合深度学习中不同学习率和动量的优化效率可视化比较这个例子,来解释如何使用plot进行绘图。

梯度下降配置

深度学习中学习率的设置一直是一个回避不开的话题,学习率大了难以收敛,学习率小了收敛速度慢。在这里暂时不考虑后期出现各种SGD变种的方法,例如Adaptive学习率。假设我们现在有一个参数x=2,误差曲线是y=4x^2,他的最小值很明显是0,现在采用梯度下降的方法对其进行优化。
先放上优化的结果:
在这里插入图片描述
可以看到经过100次迭代之后,从0.01-0.2的10个学习率都达到了较好的效果

代码理解

1 不带子图(subplot)的绘制

首先看绘制一张图编程的思想。plot函数接受一组x和一组y,注意,必须要转换成ndarray或者列表的形式,如果是tensor类型的,要tensor.detach().numpy()转换成ndarray形式。可以在调试的时候在控制台输x.detach().numpy(),查看一下相关的格式。这里不演示了。
既然我们要画多组曲线,就需要多组x和y。这个x和y的索引,是我们的学习率列表的索引。这样我们就有了基本编程的思想:首先,根据学习率列表长度创建三个二维列表用于记录信息:loss_rec、x_rec、iter_rec。其实最后一个无所谓,都是迭代次数。然后编写循环:关于循环编写有一个心得,就是“超参数”思想。看一看“上一层中哪一个参数是下一层的超参数”。例如不同学习率的比较,“学习率列表”是下一层绘图的超参数,那么就应该先循环学习率,拿到学习率再去计算下面的值。
对于同时要拿出索引号和内容的循环,强烈建议采用enumerate,我现在编写循环基本都是用这个。

# flag = 0
flag = 1
if flag:
    # lr_list = [0.01,0.02,0.03]
    lr_list = np.linspace(0.01,0.2,num=10)
    x_rec = [[] for i in range(len(lr_list))] # 这样才能x_rec[i].append
    loss_rec = [[] for i in range(len(lr_list))]
    iter_rec = [[] for i in range(len(lr_list))]
    MAX_ITER = 100

    # 外层循环是针对每一个学习率的,里面是迭代
    # 就是学习率是下一层循环的超参数——规范:A是B层循环的超参数
    for lr_idx,lr in enumerate(lr_list):
        x = torch.tensor([2.], requires_grad=True) # 必须初始化
        for j in range(MAX_ITER):
            y = func(x)
            y.backward()

            x.data.sub_(lr*x.grad)
            x_rec[lr_idx].append(x.item())

            x.grad.zero_()
            iter_rec[lr_idx].append(j)
            loss_rec[lr_idx].append(y.item()) # 其实y也可以

    for idx,loss_r in enumerate(loss_rec):
        plt.plot(range(len(loss_r)), loss_r, label='lr={}'.format(lr_list[idx]))

    plt.xlabel("Iteration")
    plt.ylabel("Loss")
    plt.grid()
    plt.legend()
    plt.show()

每次选择学习率开始小循环的时候,都要注意,x的初始化参数是2,这个也是小循环的“超参数”。实际上大循环的参数都是小循环的超参数,这一点非常重要。在小循环里我们就拿到了三组不同的loss_rec和x_rec。看后期绘图的需求。本文中第一个例子是绘制单个图像。如果采用subplot的话也可以绘制并列的图像。这样我们就可以循环取出三组曲线对应值,添加plt.plot了,注意要加上label,label标签采用format格式化参数也非常好调整。接下来就是一些配置,xlabel和ylabel标注了坐标轴的含义。legend()和label属性是配合使用的,grid()是网格。加上一个show()就可以显示了。

2 带有子图subplot()的多曲线绘制

上图只绘制了loss和iteration的关系,那么我如果要绘制loss和iteration以及x_rec和iteration之间的两幅图怎么办呢?首先我们介绍subplot()。这个函数接受的参数是图片所在的位置。例如subplot(122)和subplot(121)分别表示:1行,两列的第一个,1行。两列,第二个图像。也就是前两个数字表示图片组成的矩阵的维度,最后一个数字表示坐标。注意,xlabel、ylabel、legend()都不是共享参数的,只有plt.show()写一次就可以了。代码如下:

    for idx,loss_r in enumerate(loss_rec):
        plt.subplot(121).plot(range(len(loss_r)), loss_r, label='lr={:.2f}'.format(lr_list[idx]))
    plt.xlabel("Iteration")
    plt.ylabel("Loss")
    plt.grid()
    plt.legend()

    for idx,x_r in enumerate(x_rec):
        plt.subplot(122).plot(range(len(x_r)), x_r, label='lr={:.2f}'.format(lr_list[idx]))
    plt.xlabel("Iteration")
    plt.ylabel("x_value")
    plt.legend()
    plt.grid()
    plt.show()

由于之前输出的位数太多,必须限制小数点精度,因此采用了{.2f}进行限制,保留两位小数。
在这里插入图片描述

平滑曲线的问题

有时候我们整数的x轴坐标,会导致画出来的图是折线的,比较丑。这时候我们就可以考虑采用平滑的方法进行。但是平滑并非直接作用在曲线上,而是需要我们自己去变换x和y。具体做法如下:
调用from scipy.interpolate import spline。现在会提示不要用spline而采用bspline,但是无所谓,也能达成功能。然后我们加密x坐标,例如可以用np.linspace(start,end,num)。将原始x坐标、原始y坐标和加密x坐标输入到spline函数中构造新曲线,然后我们将加密x坐标和新曲线(这两者的x是对应的)输入到plot中,就可以得到对比。
代码如下:

for mom_idx, mom in enumerate(momentum_list):
    plt.subplot(121).plot(iter_rec[mom_idx],loss_rec[mom_idx],label='lr:{} momentum:{}'.format(0.01,momentum_list[mom_idx]))
plt.legend(loc='upper right')
plt.grid()
plt.xlabel('Itertion')
plt.ylabel('Loss')
plt.title('Unsmoothed')

for mom_idx, mom in enumerate(momentum_list):
    loss_smooth = spline(iter_rec[mom_idx],loss_rec[mom_idx],np.linspace(0,20,num=300))
    plt.subplot(122).plot(np.linspace(0,20,num=300), loss_smooth, label='lr:{} momentum:{}'.format(0.01,momentum_list[mom_idx]))
    # 经过平滑的曲线
plt.legend(loc='upper right')
plt.grid()
plt.xlabel('Itertion')
plt.ylabel('Loss')
plt.title('Smoothed')
plt.show()

在这里插入图片描述
一般来说我们的x轴比较稠密的话,不会出现需要平滑的问题。这里使用了legend()函数中的loc参数来指定位置,相关问题可以查阅博客https://blog.csdn.net/Wannna/article/details/102751689

一般二维图标基本上就是这么绘制的,Python也是绘图神器,做出来的图完全可以刊载到期刊论文上。绘图的基本思想是一致的,后续有更进一步的绘图方法,会进一步补充。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值