散景:在jupyter实验室重复绘图会增加(浏览器)的内存使用量

时间:2019-12-04 16:49:07

标签: python jupyter-notebook jupyter bokeh jupyter-lab

我正在使用散景 Jupyter Lab 笔记本中绘制许多时间序列(> 100)和许多点(〜20,000) 。 在Jupyter中多次执行单元时,Chrome的内存消耗每次运行会增加400mb以上。在执行了多个单元格后,Chrome往往会崩溃,通常是在累积了几GB RAM使用量时。此外,每次执行后绘图都会变慢。

Jupyter中的“清除[所有]输出” “重新启动内核并清除所有输出...” 也不会释放任何内存。在经典的 Jupyter Notebook 以及 Firefox Edge 中,也会发生此问题。

我的.ipynp的最低版本:

import numpy as np
from bokeh.io import show, output_notebook
from bokeh.plotting import figure
import bokeh
output_notebook() # See e.g.: https://github.com/bokeh/bokeh-notebooks/blob/master/tutorial/01%20-%20Basic%20Plotting.ipynb
# Just create a list of numpy arrays with random-walks as dataset
ts_length = 20000
n_lines = 100
np.random.seed(0)
dataset = [np.cumsum(np.random.randn(ts_length)) + i*100 for i in range(n_lines)]
# Plot exactly the same linechart every time
plot = figure(x_axis_type="linear")
for data in dataset:
    plot.line(x=range(ts_length), y=data)
show(plot)

即使我每次在重新执行上面的(图)单元之前每次都执行以下单元格,这种“内存泄漏”行为仍会继续:

bokeh.io.curdoc().clear()  
bokeh.io.state.State().reset()
bokeh.io.reset_output()

output_notebook() # has to be done again because output was reset


Bokeh中是否有其他可能被我忽略的机制,可以使我清理剧情并释放内存(在浏览器/ js / client中)?

为了避免此问题,我是否必须在Jupyter Notebook中以其他方式绘制(或显示绘制)图?还是这仅仅是Bokeh / Jupyter的错误?


  

系统上已安装的版本(Windows 10):

     
      
  • Python 3.6.6:Anaconda自定义(64位)
  •   
  • 散景:1.4.0
  •   
  • Chrome:78.0.3904.108
  •   
  • jupyter:      
        
    • 核心:4.6.1
    •   
    • 实验室:1.1.4
    •   
    • ipywidgets:7.5.1
    •   
    • labextensions:      
          
      • @ bokeh / jupyter_bokeh:1.1.1版
      •   
      • @ jupyter-widgets / jupyterlab-manager:v1.0。*
      •   
    •   
  •   

1 个答案:

答案 0 :(得分:2)

TLDR;这可能值得making an issue(s) for

内存使用情况

关于不同方面的一些注意事项:

清除/重置功能

首先要注意的是这些:

bokeh.io.curdoc().clear()  
bokeh.io.state.State().reset()
bokeh.io.reset_output()

仅影响 Python进程中的数据结构(例如Jupyter内核)。它们永远不会对浏览器的内存使用或占用空间产生任何影响。

一次性内存占用量

仅基于数据,我希望大约在64MB附近:

20000 * 100 * 2 * 2  * 8 = 64MB

也就是:100行带有20k(x,y)点,这也将转换为(sx,sy)屏幕坐标,所有这些都以float64(8byte)类型的数组表示。但是,Bokeh还会为所有数据构造一个空间索引,以支持诸如悬停工具之类的功能。我希望您正在用此数据炸毁该索引。可能值得将该功能进行配置,以便不需要命中测试的人不必为此付费。讨论此功能的功能请求问题将是适当的。

重复执行

应该有DOM事件触发器,这些触发器将在重新执行笔记本单元时清除。也许这些已经坏了?不幸的是,与一个很小的团队维护三个大型混合Python / JS工具(包括经典Notebook)之间的集成是一个持续的挑战。错误报告问题将是适当的,以便可以对其进行跟踪和调查。

其他选项

您现在能做什么?

更多最佳用法

至少对于您在此处具有相同长度的时间序列的特定情况,以上代码以非常次优的方式构造。您应该尝试将所有内容放在单个ColumnDataSource中:

ts_length = 20000
n_lines = 100
np.random.seed(0)

source = ColumnDataSource(data=dict(x=np.arange(ts_length)))
for i in range(n_lines):
    source.data[f"y{i}"] = np.cumsum(np.random.randn(ts_length)) + i*100

plot = figure()
for i in range(n_lines):
    plot.line(x='x', y=f"y{i}", source=source)
show(plot)

通过将序列文字传递给line,您的代码将导致创建99个不必要的CDS对象(每个line调用一个)。也不会重新使用x数据,从而导致不必要地向BokehJS发送99 * 20k额外点。而且,通过发送普通列表而不是numpy数组,所有这些都将使用效率较低的(在时间和空间上)默认JSON编码进行编码,而不是使用numpy数组的有效二进制编码。

话虽如此,但这并不是在这里引起所有问题,并且可能不是一个解决方案。但我想确保指出这一点。

Datashader

为此,您可以考虑将DataShader与Bokeh结合使用。 Holoviews库还自动将Bokeh和Datashader高度集成。通过在Python端预渲染图像,Datashader实际上是一种带宽压缩工具(除其他外)。

PNG导出

Bokeh倾向于权衡各种交互性。但是,如果您实际上不需要这种交互性,那么您将付出一些额外的费用。如果是这种情况,您可以考虑生成静态PNG,而不是:

from bokeh.io.export import get_screenshot_as_png
p = get_screenshot_as_png(plot)

enter image description here

您将需要安装Exporting Plots中列出的其他可选依赖项,并且如果要进行许多绘图,则可能需要考虑为每个调用显式保存和重用Webdriver。