python - 散景:jupyter 实验室中的重复绘图会增加(浏览器)内存使用量
问题描述
我正在使用Bokeh在Jupyter Lab Notebook中绘制许多时间序列 (>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/客户端中)?
我是否必须在 Jupyter Notebook 中以其他方式绘制(或显示情节)以避免此问题?或者这仅仅是 Bokeh/Jupyter 的错误?
我的系统上安装的版本(Windows 10):
- Python 3.6.6:Anaconda 自定义(64 位)
- 散景:1.4.0
- 铬:78.0.3904.108
- 朱皮特:
- 核心:4.6.1
- 实验室:1.1.4
- ipywidgets:7.5.1
- 实验室扩展:
- @bokeh/jupyter_bokeh:v1.1.1
- @jupyter-widgets/jupyterlab-manager:v1.0.*
解决方案
TLDR;这可能值得为.
内存使用情况
只是关于不同方面的一些注释:
清除/重置功能
首先要注意,这些:
bokeh.io.curdoc().clear()
bokeh.io.state.State().reset()
bokeh.io.reset_output()
仅影响Python 进程中的数据结构(例如 Jupyter Kernel)。它们永远不会对浏览器内存使用或占用空间产生任何影响。
一次性内存占用
仅基于数据,我预计大约在 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与 Bokeh 结合使用。Holoviews库还在高级别的自动集成了 Bokeh 和 Datashader 。通过在 Python 端预渲染图像,Datashader 实际上是一种带宽压缩工具(除其他外)。
PNG导出
Bokeh 倾向于权衡提供各种交互性。但是,如果您实际上并不需要这种交互性,那么您需要支付一些额外的费用。如果这是您的情况,您可以考虑生成静态 PNG:
from bokeh.io.export import get_screenshot_as_png
p = get_screenshot_as_png(plot)
您需要安装导出绘图中列出的其他可选依赖项,如果您正在绘制许多绘图,您可能需要考虑为每个调用显式保存和重用 Web 驱动程序。
推荐阅读
- javascript - 如果通过从网站(html 或 javascript)检测它的包名称从 android 查看页面,重定向到特定的 url?
- discord.js - TypeError [INVALID_TYPE]:提供的选项不是对象
- php - Laravel 属于ToMany 关系
- node.js - Typescript Node.js 项目 - 部署到生产工作流程
- powershell - Powershell在斜线后获取字符串
- ios - 如何在 UICollectionView 中为 Button 添加 addTarget?
- view - 如何在不使用 RadioButtonGroup 的情况下在控制器中获取 RadioButton 文本?
- android - Android Studio 下载错误(请求失败,状态码 416)
- c# - 强制函数输入参数不可变?
- linux - GRE 隧道之间的 Centos7 上未转发数据包