使用Matplotlib创建动图
最近在实验中需要利用来生成对抗网络(generative adversary network)来伪造相应数据,在查阅资料的过程中发现在训练过程中使用动图来表示生成器迭代次数效果图对于实验很有帮助. 例如下图生成正弦函数示意图:

但是,在Jupyter Notebook一个循环当中简单的调用pyplot.plot(x, y)只会在当前单元(cell)下不断产生新的图像,达不到预期效果. 经过一番资料查找,找到了如下的解决方案.
IPython Display
iPython通常和Jupyter Notebook在一块,作为一个通用的科学计算发行包绑定在一块,而iPython内置了许多魔法指令(magic command)和包函数可以用来帮助达到我们的目的.
首先为了能够在Jupyter Notebook单元中产生图像,并且将该图像保存在文档中,需要在当前单元中使用魔法指令:%matplotlib inline. 这样当前matplotlib绘图后端被设置为inline,从而可以直接在当前单元下展示图像. 接着我们想要在同一个单元下反复画图,这样才能产生一种迭代效果.,因此我们还需要从iPython中导入display模块,对当前单元进行调整:
- 首先在循环开始前,需要先清空当前图像(figure)图像中的所有信息,因此我们需要调用
plt.clf(). - 然后我们想要当前单元只产生一个图像,因此需要将其他的产生的图像清空,这时候需要使用
display.clear_output(wait=True). 其中设置选项wait=True是为了等到下一个图像出现时才清除前一个图像,这样可以让图像之前的转化更加平滑. - 最后我们需要展示当前图像,这时候使用
display.disply(plt.gcf())来展示当前图像. 其中display.display(*objs)在所有前端上显示目标,而plt.gcf()表示当前图像.
完整的实例代码如下:
ximport matplotlib.pyplot as pltfrom IPython import displayimport numpy as np
%matplotlib inlineiter = 10for i in range(iter): plt.clf() display.clear_output(wait=True) x = np.linspace(0.24*np.pi, 512) y = np.sin((i+1)/30.*x) plt.plot(x, y) display.display(plt.gcf()) plt.pause(1) # 中途暂停来提供观察时间可以发现该方法展示过程并不是很流畅,可以发现明显的刷新时间,这是因为其原理就是在同一个位置不断进行画图. 除了不流畅的感观以外,也无法将该过程保存为一个动图.
Matplotlib Animation
一个更好的方法是使用matplotlib中提供的animation模块来进行动图制作. matplotliib对于动画提供三种方法:
Animation一个基础的动画类,用来表示一个动画对象.FuncAnimation使用函数来创建一个动画类. 该方法通过调用一个函数func来绘制每一帧图像,综合起来得到一个动画.ArtistAnimation使用预先设定好的Artist类来创建Animation类.
在这里我们使用FuncAnimation来达到我们的目的,其API如下所示:
xxxxxxxxxxFuncAnimation(fig, func, frames=None, init_func=None, fargs=None, save_count=None, *, cache_frame_data=True, **kwargs)在本文中我们主要关注三个参数:fig, func和frames. 其余参数的含义可以参考官方文档. fig表示进行操作(调整大小,画图)的图像目标,func为对每一帧进行画图的函数,FuncAnimation会调用func来为每一帧进行画图,其API如下
xxxxxxxxxxdef func(frame, *fargs) -> iterable_of_artistsfargs对应FuncAnimation中的参数 fargs. frames可以为整数,迭代器、生成器或None,它将被迭代(整数会被转换成range),其每个参数将作为func中的frame被调用来生成每一帧图片.
有了以上知识,我们就可以使用FuncAnimation来改写上述循环来获取迭代动图了. 首先创建一个新的图像对象,获取其操作句柄:
xxxxxxxxxxfig, ax = plt.subplots()ax.set_xlim([0, 60])ax.set_ylim([-1, 1])line, = ax.plot([],[]) # 创建一个空图做为起始目标接着定义更新每一帧的函数func:
xxxxxxxxxxx = np.linspace(0.24*np.pi, 512)def update(frame): y = np.sin((frame+1)/30.*x) line.set_data(x, y) return line,最后使用FuncAnimation创建相应的animation对象:
xxxxxxxxxxanimation = FuncAnimation(fig, update, frames=10)为了在Jupyter Notebook中展示相应的动图,我们首先需要将matplotlib的后端设置成交互模式,%matplotlib notebook然后使用plt.show()进行展示即可. 全部代码如下所示:
xxxxxxxxxximport matplotlib.pyplot as pltfrom matplotlib.animation import FuncAnimationimport numpy as np
%matplotlib notebook
fig, ax = plt.subplots()ax.set_xlim([0, 200])ax.set_ylim([-1, 1])line, = ax.plot([],[])x = np.linspace(0.24*np.pi, 512)
def update(frame): y = np.sin((frame+1)/30.*x) line.set_data(x, y) return line,
ani = FuncAnimation(fig, update, frames=10)plt.show()为了保存该图像,可以直接调用Animation对象中的save方法,在这里我们将其保存为一个gif文件,因此使用PilloWriter进行编码. 具体代码如下:
xxxxxxxxxxfrom matplotlib import animation
filename = r"ani.gif"ani.save(filename, writer=ani.PillowWriter(fps=3))最后结果如下所示

参考资料
https://matplotlib.org/stable/api/animation_api.html
https://holypython.com/how-to-save-matplotlib-animations-the-ultimate-guide/
评论
发表评论