From 427467937617dd817d19567289607a39272e1406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Haian=20Huang=28=E6=B7=B1=E5=BA=A6=E7=9C=B8=29?= <1286304229@qq.com> Date: Sun, 24 Apr 2022 19:21:10 +0800 Subject: [PATCH] Refine Visualizer docs (#177) * Refine Visualizer docs * update * update * update featmap * update docs * update visualizer docs --- docs/zh_cn/tutorials/visualization.md | 405 +++++++++++++------------- 1 file changed, 202 insertions(+), 203 deletions(-) diff --git a/docs/zh_cn/tutorials/visualization.md b/docs/zh_cn/tutorials/visualization.md index 80acabcc..4aa7d6ec 100644 --- a/docs/zh_cn/tutorials/visualization.md +++ b/docs/zh_cn/tutorials/visualization.md @@ -2,8 +2,6 @@ ## 概述 -**(1) 总体介ç»** - å¯è§†åŒ–å¯ä»¥ç»™æ·±åº¦å¦ä¹ 的模型è®ç»ƒå’Œæµ‹è¯•è¿‡ç¨‹æ供直观解释。在 OpenMMLab 算法库ä¸ï¼Œæˆ‘们期望å¯è§†åŒ–功能的设计能满足以下需求: - æ供丰富的开箱å³ç”¨å¯è§†åŒ–功能,能够满足大部分计算机视觉å¯è§†åŒ–任务 @@ -11,291 +9,292 @@ - 能够在è®ç»ƒå’Œæµ‹è¯•æµç¨‹çš„ä»»æ„点ä½è¿›è¡Œå¯è§†åŒ– - OpenMMLab å„个算法库具有统一å¯è§†åŒ–接å£ï¼Œåˆ©äºŽç”¨æˆ·ç†è§£å’Œç»´æŠ¤ -基于上述需求,OpenMMLab 2.0 引入了绘制对象 Visualizer 和写端对象 Writer 的概念 +基于上述需求,OpenMMLab 2.0 引入了å¯è§†åŒ–对象 Visualizer å’Œå„个å¯è§†åŒ–å˜å‚¨åŽç«¯ VisBackend 如 `LocalVisBackend`ã€`WandbVisBackend` å’Œ `TensorboardVisBackend` ç‰ã€‚æ¤å¤„çš„å¯è§†åŒ–ä¸ä»…仅包括图片数æ®æ ¼å¼ï¼Œè¿˜åŒ…括é…置内容ã€æ ‡é‡å’Œæ¨¡åž‹å›¾ç‰æ•°æ®çš„å¯è§†åŒ–。 -- **Visualizer è´Ÿè´£å•å¼ 图片的绘制功能** +- 为了方便调用,Visualizer æ供的接å£å®žçŽ°äº†ç»˜åˆ¶å’Œå˜å‚¨çš„功能。å¯è§†åŒ–å˜å‚¨åŽç«¯ VisBackend 作为 Visualizer 的内部属性,会在需è¦çš„时候被 Visualizer 调用,将数æ®å˜åˆ°ä¸åŒçš„åŽç«¯ +- 考虑到绘制åŽä¼šå¸Œæœ›å˜å‚¨åˆ°å¤šä¸ªåŽç«¯ï¼ŒVisualizer å¯ä»¥é…置多个 VisBackend,当用户调用 Visualizer çš„å˜å‚¨æŽ¥å£æ—¶å€™ï¼ŒVisualizer 内部会é历的调用 VisBackend å˜å‚¨æŽ¥å£ - MMEngine æ供了以 Matplotlib 库为绘制åŽç«¯çš„ `Visualizer` 类,其具备如下功能: +两者的 UML 关系图如下 - - æä¾›äº†ä¸€ç³»åˆ—å’Œè§†è§‰ä»»åŠ¡æ— å…³çš„åŸºç¡€æ–¹æ³•ï¼Œä¾‹å¦‚ `draw_bboxes` å’Œ `draw_texts` ç‰ - - å„个基础方法支æŒé“¾å¼è°ƒç”¨ï¼Œæ–¹ä¾¿å åŠ ç»˜åˆ¶æ˜¾ç¤º - - 通过 `draw_featmap` æ供绘制特å¾å›¾åŠŸèƒ½ +<div align="center"> + <img src="https://user-images.githubusercontent.com/17425982/163327736-f7cb3b16-ef07-46bc-982a-3cc7495e6c82.png" > +</div> - å„个下游算法库å¯ä»¥ç»§æ‰¿ `Visualizer` 并在 `draw` 接å£ä¸å®žçŽ°æ‰€éœ€çš„å¯è§†åŒ–功能,例如 MMDetection ä¸çš„ `DetVisualizer` 继承自 `Visualizer` 并在 `draw` 接å£ä¸å®žçŽ°å¯è§†åŒ–检测框ã€å®žä¾‹æŽ©ç å’Œè¯ä¹‰åˆ†å‰²å›¾ç‰åŠŸèƒ½ã€‚Visualizer 类的 UML 关系图如下 +## å¯è§†åŒ–对象 Visualizer - <div align="center"> - <img src="https://user-images.githubusercontent.com/17425982/154475592-7208a34b-f6cb-4171-b0be-9dbb13306862.png" > - </div> +### 接å£è¯´æ˜Ž -- **Writer 负责将å„类数æ®å†™å…¥åˆ°æŒ‡å®šåŽç«¯** +å¯è§†åŒ–对象 Visualizer 对外æ供了所有接å£ã€‚å¯ä»¥å°†å…¶æŽ¥å£åˆ†æˆ 3 大类,如下所示 - 为了统一接å£è°ƒç”¨ï¼ŒMMEngine æ供了统一的抽象类 `BaseWriter`,和一些常用的 Writer 如 `LocalWriter` æ¥æ”¯æŒå°†æ•°æ®å†™å…¥æœ¬åœ°ï¼Œ`TensorboardWriter` æ¥æ”¯æŒå°†æ•°æ®å†™å…¥ Tensorboard,`WandbWriter` æ¥æ”¯æŒå°†æ•°æ®å†™å…¥ Wandb。用户也å¯ä»¥è‡ªå®šä¹‰ Writer æ¥å°†æ•°æ®å†™å…¥è‡ªå®šä¹‰åŽç«¯ã€‚写入的数æ®å¯ä»¥æ˜¯å›¾ç‰‡ï¼Œæ¨¡åž‹ç»“æž„å›¾ï¼Œæ ‡é‡å¦‚æ¨¡åž‹ç²¾åº¦æŒ‡æ ‡ç‰ã€‚ +**(1) 绘制相关接å£** - 考虑到在è®ç»ƒæˆ–者测试过程ä¸å¯èƒ½åŒæ—¶å˜åœ¨å¤šä¸ª Writer 对象,例如åŒæ—¶æƒ³è¿›è¡Œæœ¬åœ°å’Œè¿œç¨‹ç«¯å†™æ•°æ®ï¼Œä¸ºæ¤è®¾è®¡äº† `ComposedWriter` 负责管ç†æ‰€æœ‰è¿è¡Œä¸å®žä¾‹åŒ–çš„ Writer 对象,其会自动管ç†æ‰€æœ‰ Writer 对象,并é历调用所有 Writer 对象的方法。Writer 类的 UML 关系图如下 - <div align="center"> - <img src="https://user-images.githubusercontent.com/17425982/157000633-9f552539-f722-44b1-b253-1abaf4a8eba6.png" > - </div> +- [draw_bboxes](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.Visualizer.draw_bboxes) 绘制å•ä¸ªæˆ–多个边界框 +- [draw_points](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.Visualizer.draw_points) 绘制å•ä¸ªæˆ–多个点 +- [draw_texts](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.Visualizer.draw_texts) 绘制å•ä¸ªæˆ–多个文本框 +- [draw_lines](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.Visualizer.lines) 绘制å•ä¸ªæˆ–多个线段 +- [draw_circles](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.Visualizer.draw_circles) 绘制å•ä¸ªæˆ–多个圆 +- [draw_polygons](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.Visualizer.draw_polygons) 绘制å•ä¸ªæˆ–多个多边形 +- [draw_binary_masks](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.Visualizer.draw_binary_mask) 绘制å•ä¸ªæˆ–多个二值掩ç +- [draw_featmap](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.Visualizer.draw_featmap) 绘制特å¾å›¾ï¼Œé™æ€æ–¹æ³• -**(2) Writer å’Œ Visualizer 关系** +上述接å£é™¤äº† `draw_featmap` 外都å¯ä»¥é“¾å¼è°ƒç”¨ï¼Œå› 为该方法调用åŽå¯èƒ½ä¼šå¯¼è‡´å›¾ç‰‡å°ºå¯¸å‘生改å˜ã€‚为了é¿å…给用户带æ¥å›°æ‰°ï¼Œ `draw_featmap` 被设置为é™æ€æ–¹æ³•ã€‚ -Writer å¯¹è±¡çš„æ ¸å¿ƒåŠŸèƒ½æ˜¯å†™å„类数æ®åˆ°æŒ‡å®šåŽç«¯ä¸ï¼Œä¾‹å¦‚写图片ã€å†™æ¨¡åž‹å›¾ã€å†™è¶…å‚å’Œå†™æ¨¡åž‹ç²¾åº¦æŒ‡æ ‡ç‰ï¼ŒåŽç«¯å¯ä»¥æŒ‡å®šä¸ºæœ¬åœ°å˜å‚¨ã€Wandb å’Œ Tensorboard ç‰ç‰ã€‚在写图片过程ä¸ï¼Œé€šå¸¸å¸Œæœ›èƒ½å¤Ÿå°†é¢„æµ‹ç»“æžœæˆ–è€…æ ‡æ³¨ç»“æžœç»˜åˆ¶åˆ°å›¾ç‰‡ä¸Šï¼Œç„¶åŽå†è¿›è¡Œå†™æ“作,为æ¤åœ¨ Writer 内部维护了 Visualizer 对象,将 Visualizer 作为 Writer 的一个属性。需è¦æ³¨æ„的是: +当用户想先绘制边界框,在æ¤åŸºç¡€ä¸Šç»˜åˆ¶æ–‡æœ¬ï¼Œç»˜åˆ¶çº¿æ®µçš„时候,å¯ä»¥é€šè¿‡é“¾å¼è°ƒç”¨å®žçŽ°ï¼š -- åªæœ‰è°ƒç”¨äº† Writer ä¸çš„ `add_image` 写图片功能时候æ‰å¯èƒ½ä¼šç”¨åˆ° Visualizer 对象,其余接å£å’Œ Visualizer 没有关系 -- 考虑到æŸäº› Writer åŽç«¯æœ¬èº«å°±å…·å¤‡ç»˜åˆ¶åŠŸèƒ½ä¾‹å¦‚ `WandbWriter`,æ¤æ—¶ `WandbWriter` ä¸çš„ Visualizer 属性就是å¯é€‰çš„,如果用户在åˆå§‹åŒ–æ—¶å€™ä¼ å…¥äº† Visualizer 对象,则在 `add_image` 时候会调用 Visualizer 对象,å¦åˆ™ä¼šç›´æŽ¥è°ƒç”¨ Wandb 本身 API 进行图片绘制 -- `LocalWriter` å’Œ `TensorboardWriter` 由于绘制功能å•ä¸€ï¼Œç›®å‰å¼ºåˆ¶ç”± Visualizer 对象绘制,所以这两个 Writer å¿…é¡»ä¼ å…¥ Visualizer 或者å类对象 +```python +visualizer.set_image(image) +visualizer.draw_bboxes(...).draw_texts(...).draw_lines(...) +visualizer.show() # å¯è§†åŒ–绘制结果 +``` -`WandbWriter` 的一个简略的演示代ç 如下 +特å¾å›¾å¯è§†åŒ–是一个常è§çš„功能,用户通过调用 `draw_featmap` å¯è§†åŒ–特å¾å›¾ï¼Œå…¶å‚数定义为: ```python -# 为了方便ç†è§£ï¼Œæ²¡æœ‰ç»§æ‰¿ BaseWriter -class WandbWriter: - def __init__(self, visualizer=None): - self._visualizer = None - if visualizer: - # 示例é…ç½® visualizer=dict(type='DetVisualizer') - self._visualizer = VISUALIZERS.build(visualizer) - - @property - def visualizer(self): - return self._visualizer - - def add_image(self, name, image, gt_sample=None, pred_sample=None, draw_gt=True, draw_pred=True, step=0, **kwargs): - if self._visualize: - self._visualize.draw(image, gt_sample, pred_sample, draw_gt, draw_pred) - # 调用 Writer API 写图片到åŽç«¯ - self.wandb.log({name: self.visualizer.get_image()}, ...) - ... - else: - # 调用 Writer API 汇总并写图片到åŽç«¯ - ... - - def add_scalar(self, name, value, step): - self.wandb.log({name: value}, ...) +@staticmethod +def draw_featmap(featmap: torch.Tensor, # è¾“å…¥æ ¼å¼è¦æ±‚为 CHW + overlaid_image: Optional[np.ndarray] = None, # 如果åŒæ—¶è¾“入了 image æ•°æ®ï¼Œåˆ™ç‰¹å¾å›¾ä¼šå åŠ åˆ° image 上绘制 + channel_reduction: Optional[str] = 'squeeze_mean', # 多个通é“压缩为å•é€šé“çš„ç–ç•¥ + topk: int = 10, # å¯é€‰æ‹©æ¿€æ´»åº¦æœ€é«˜çš„ topk 个特å¾å›¾æ˜¾ç¤º + arrangement: Tuple[int, int] = (5, 2), # 多通é“å±•å¼€ä¸ºå¤šå¼ å›¾æ—¶å€™å¸ƒå±€ + resize_shape:Optional[tuple] = None, # å¯ä»¥æŒ‡å®š resize_shape å‚æ•°æ¥ç¼©æ”¾ç‰¹å¾å›¾ + alpha: float = 0.5) -> np.ndarray: # 图片和特å¾å›¾ç»˜åˆ¶çš„å åŠ æ¯”ä¾‹ ``` +特å¾å›¾å¯è§†åŒ–功能较多,目å‰ä¸æ”¯æŒ Batch 输入,其功能å¯ä»¥å½’纳如下 -## 绘制对象 Visualizer +- 输入的 Tensor 一般是包括多个通é“的,channel_reduction å‚æ•°å¯ä»¥å°†å¤šä¸ªé€šé“压缩为å•é€šé“,然åŽå’Œå›¾ç‰‡è¿›è¡Œå åŠ æ˜¾ç¤º + - `squeeze_mean` 将输入的 C 维度采用 mean 函数压缩为一个通é“,输出维度å˜æˆ (1, H, W) + - `select_max` 从输入的 C 维度ä¸å…ˆåœ¨ç©ºé—´ç»´åº¦ sum,维度å˜æˆ (C, ),然åŽé€‰æ‹©å€¼æœ€å¤§çš„é€šé“ + - `None` 表示ä¸éœ€è¦åŽ‹ç¼©ï¼Œæ¤æ—¶å¯ä»¥é€šè¿‡ topk å‚æ•°å¯é€‰æ‹©æ¿€æ´»åº¦æœ€é«˜çš„ topk 个特å¾å›¾æ˜¾ç¤º -绘制对象 Visualizer è´Ÿè´£å•å¼ 图片的å„类绘制功能,默认绘制åŽç«¯ä¸º Matplotlib。为了统一 OpenMMLab å„个算法库的å¯è§†åŒ–接å£ï¼ŒMMEngine 定义æ供了基础绘制功能的 `Visualizer` 类,下游库å¯ä»¥ç»§æ‰¿ `Visualizer` 并实现 `draw` 接å£æ¥æ»¡è¶³è‡ªå·±çš„绘制需求。 +- 在 channel_reduction å‚数为 None 的情况下,topk å‚数生效,其会按照激活度排åºé€‰æ‹© topk 个通é“,然åŽå’Œå›¾ç‰‡è¿›è¡Œå åŠ æ˜¾ç¤ºï¼Œå¹¶ä¸”æ¤æ—¶ä¼šé€šè¿‡ arrangement å‚数指定显示的布局 + - 如果 topk ä¸æ˜¯ -1,则会按照激活度排åºé€‰æ‹© topk 个通é“显示 + - 如果 topk = -1,æ¤æ—¶é€šé“ C 必须是 1 或者 3 表示输入数æ®æ˜¯å›¾ç‰‡ï¼Œå¦åˆ™æŠ¥é”™æ示用户应该设置 `channel_reduction`æ¥åŽ‹ç¼©é€šé“。 -### Visualizer +- 考虑到输入的特å¾å›¾é€šå¸¸éžå¸¸å°ï¼Œå‡½æ•°æ”¯æŒè¾“å…¥ `resize_shape` å‚数,方便将特å¾å›¾è¿›è¡Œä¸Šé‡‡æ ·åŽè¿›è¡Œå¯è§†åŒ–。 -`Visualizer` æ供了基础而通用的绘制功能,主è¦æŽ¥å£å¦‚下: +**(2) å˜å‚¨ç›¸å…³æŽ¥å£** -**(1) ç»˜åˆ¶æ— å…³çš„åŠŸèƒ½æ€§æŽ¥å£** +- [add_config](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.writer.BaseWriter.add_config) 写é…置到特定å˜å‚¨åŽç«¯ +- [add_graph](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.writer.BaseWriter.add_graph) 写模型图到特定å˜å‚¨åŽç«¯ +- [add_image](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.writer.BaseWriter.add_image) 写图片到特定å˜å‚¨åŽç«¯ +- [add_scalar](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.writer.BaseWriter.add_scalar) å†™æ ‡é‡åˆ°ç‰¹å®šå˜å‚¨åŽç«¯ +- [add_scalars](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.writer.BaseWriter.add_scalars) ä¸€æ¬¡æ€§å†™å¤šä¸ªæ ‡é‡åˆ°ç‰¹å®šå˜å‚¨åŽç«¯ +- [add_datasample](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.writer.BaseWriter.add_datasample) å„个下游库绘制 datasample æ•°æ®çš„æŠ½è±¡æŽ¥å£ + +以 add å‰ç¼€å¼€å¤´çš„接å£è¡¨ç¤ºå˜å‚¨æŽ¥å£ã€‚datasample 是 OpenMMLab 2.0 架构ä¸è®¾è®¡çš„å„个下游库统一的抽象数æ®æŽ¥å£ï¼Œè€Œ `add_datasample` 接å£å¯ä»¥ç›´æŽ¥å¤„ç†è¯¥æ•°æ®æ ¼å¼ï¼Œä¾‹å¦‚å¯è§†åŒ–预测结果ã€å¯è§†åŒ– Dataset 或者 DataLoader 输出ã€å¯è§†åŒ–ä¸é—´é¢„测结果ç‰ç‰éƒ½å¯ä»¥ç›´æŽ¥è°ƒç”¨ä¸‹æ¸¸åº“é‡å†™çš„ `add_datasample` 接å£ã€‚ + +所有下游库都必须è¦ç»§æ‰¿ Visualizer 并实现 `add_datasample` 接å£ã€‚以 MMDetection 为例,应该继承并通过该接å£å®žçŽ°ç›®æ ‡æ£€æµ‹ä¸æ‰€æœ‰é¢„置任务的å¯è§†åŒ–åŠŸèƒ½ï¼Œä¾‹å¦‚ç›®æ ‡æ£€æµ‹ã€å®žä¾‹åˆ†å‰²ã€å…¨æ™¯åˆ†å‰²ä»»åŠ¡ç»“果的绘制和å˜å‚¨ã€‚ + +**(3) 其余功能性接å£** - [set_image](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.Visualizer.set_image) 设置原始图片数æ®ï¼Œé»˜è®¤è¾“å…¥å›¾ç‰‡æ ¼å¼ä¸º RGB - [get_image](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.Visualizer.get_image) 获å–绘制åŽçš„ Numpy æ ¼å¼å›¾ç‰‡æ•°æ®ï¼Œé»˜è®¤è¾“å‡ºæ ¼å¼ä¸º RGB - [show](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.Visualizer.show) å¯è§†åŒ– -- [register_task](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.Visualizer.register_task) 注册绘制函数(其作用在 *自定义 Visualizer* å°èŠ‚æè¿°) - -**(2) 绘制相关接å£** +- [get_backend](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.Visualizer.get_backend) 通过 name 获å–特定å˜å‚¨åŽç«¯ +- [close](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.Visualizer.close) å…³é—所有已ç»æ‰“开的资æºï¼ŒåŒ…括 VisBackend -- [draw](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.Visualizer.draw) ç”¨æˆ·ä½¿ç”¨çš„æŠ½è±¡ç»˜åˆ¶æŽ¥å£ -- [draw_featmap](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.Visualizer.draw_featmap) 绘制特å¾å›¾ -- [draw_bboxes](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.Visualizer.draw_bboxes) 绘制å•ä¸ªæˆ–者多个边界框 -- [draw_texts](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.Visualizer.draw_texts) 绘制å•ä¸ªæˆ–者多个文本框 -- [draw_lines](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.Visualizer.lines) 绘制å•ä¸ªæˆ–者多个线段 -- [draw_circles](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.Visualizer.draw_circles) 绘制å•ä¸ªæˆ–者多个圆 -- [draw_polygons](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.Visualizer.draw_polygons) 绘制å•ä¸ªæˆ–者多个多边形 -- [draw_binary_masks](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.Visualizer.draw_binary_mask) 绘制å•ä¸ªæˆ–者多个二值掩ç +### ä½¿ç”¨æ ·ä¾‹ -用户除了å¯ä»¥å•ç‹¬è°ƒç”¨ `Visualizer` ä¸åŸºç¡€ç»˜åˆ¶æŽ¥å£ï¼ŒåŒæ—¶ä¹Ÿæ供了链å¼è°ƒç”¨åŠŸèƒ½å’Œç‰¹å¾å›¾å¯è§†åŒ–功能。`draw` 函数是抽象接å£ï¼Œå†…部没有任何实现,继承了 Visualizer çš„ç±»å¯ä»¥å®žçŽ°è¯¥æŽ¥å£ï¼Œä»Žè€Œå¯¹å¤–æ供统一的绘制功能,而 `draw_xxx` ç‰ç›®çš„是æä¾›æœ€åŸºç¡€çš„ç»˜åˆ¶åŠŸèƒ½ï¼Œç”¨æˆ·ä¸€èˆ¬æ— éœ€é‡å†™ã€‚ +**(1) 在任æ„ä½ç½®èŽ·å– visualizer** -**(1) 链å¼è°ƒç”¨** +为了确ä¿å¯è§†åŒ–对象 Visualizer 能够在任何地方被调用,设计上将其继承自 `ManagerMixin` 类,转å˜ä¸ºå…¨å±€å”¯ä¸€å¯¹è±¡ï¼Œç”¨æˆ·åˆå§‹åŒ– `Visualizer` 时必须è¦è°ƒç”¨ `visualizer.get_instance()` 方法æ‰èƒ½ä½¿å®žä¾‹å¯¹è±¡å…·å¤‡å…¨å±€å”¯ä¸€æ€§ã€‚一旦实例化完æˆï¼ŒåŽç»å¯ä»¥åœ¨ä»»æ„代ç ä½ç½®é€šè¿‡ `Visualizer.get_current_instance()` æ¥èŽ·å–å¯è§†åŒ–对象。 -例如用户先绘制边界框,在æ¤åŸºç¡€ä¸Šç»˜åˆ¶æ–‡æœ¬ï¼Œç»˜åˆ¶çº¿æ®µï¼Œåˆ™è°ƒç”¨è¿‡ç¨‹ä¸ºï¼š +以 MMDetection 为例,å‡è®¾ `DetLocalVisualizer` 类继承自 `Visualizer`,并实现了 `add_datasample` 接å£ã€‚é…置文件写法为: ```python -visualizer.set_image(image) -visualizer.draw_bboxes(...).draw_texts(...).draw_lines(...) -visualizer.show() # å¯è§†åŒ–绘制结果 +vis_backends = [dict(type='LocalVisBackend')] +visualizer = dict( + type='DetLocalVisualizer', vis_backends=vis_backends, name='visualizer') +``` +```python +# 内部会调用 get_instance() 进行全局唯一实例化 +VISUALIZERS.build(cfg.visualizer) ``` -**(2) å¯è§†åŒ–特å¾å›¾** - -特å¾å›¾å¯è§†åŒ–是一个常è§çš„功能,通过调用 `draw_featmap` å¯ä»¥ç›´æŽ¥å¯è§†åŒ–特å¾å›¾ï¼Œå…¶å‚数定义为: +通过上述代ç 实例化åŽï¼Œå¯ä»¥åœ¨ä»»æ„ä½ç½®è°ƒç”¨ `get_current_instance` 方法æ¥èŽ·å– visualizer ```python -@staticmethod -def draw_featmap(tensor_chw: torch.Tensor, # è¾“å…¥æ ¼å¼è¦æ±‚为 CHW - image: Optional[np.ndarray] = None, # 如果åŒæ—¶è¾“入了 image æ•°æ®ï¼Œåˆ™ç‰¹å¾å›¾ä¼šå åŠ åˆ° image 上绘制 - mode: Optional[str] = 'mean', # 多个通é“压缩为å•é€šé“çš„ç–ç•¥ - topk: int = 10, # å¯é€‰æ‹©æ¿€æ´»åº¦æœ€é«˜çš„ topk 个特å¾å›¾æ˜¾ç¤º - arrangement: Tuple[int, int] = (5, 2), # 多通é“å±•å¼€ä¸ºå¤šå¼ å›¾æ—¶å€™å¸ƒå±€ - alpha: float = 0.3) -> np.ndarray: # 图片和特å¾å›¾ç»˜åˆ¶çš„å åŠ æ¯”ä¾‹ +# ä»»æ„代ç ä½ç½®èŽ·å– visualizer +visualizer = Visualizer.get_current_instance() ``` -特å¾å›¾å¯è§†åŒ–功能较多,目å‰ä¸æ”¯æŒ Batch 输入 +如果用户直接使用了 MMEngine 或者下游库ä¸çš„ Runnerï¼Œåˆ™æ— éœ€è¿›è¡Œé¢å¤–çš„å®žä¾‹åŒ–ï¼Œå› ä¸ºåœ¨ Runner çš„åˆå§‹åŒ–函数ä¸ä¼šè‡ªåŠ¨åˆ›å»ºå…¨å±€å”¯ä¸€çš„ visualizer。 + +**(2) 将数æ®å†™å…¥è‡³ç‰¹å®šåŽç«¯** -- mode ä¸æ˜¯ None,topk æ— æ•ˆï¼Œä¼šå°†å¤šä¸ªé€šé“输出采用 mode 模å¼å‡½æ•°åŽ‹ç¼©ä¸ºå•é€šé“,å˜æˆå•å¼ å›¾ç‰‡æ˜¾ç¤ºï¼Œç›®å‰ mode ä»…æ”¯æŒ Noneã€'mean'ã€'max' å’Œ 'min' å‚数输入 -- mode 是 None,topk 有效,如果 topk ä¸æ˜¯ -1,则会按照激活度排åºé€‰æ‹© topk 个通é“显示,æ¤æ—¶å¯ä»¥é€šè¿‡ arrangement å‚数指定显示的布局 -- mode 是 None,topk 有效,如果 `topk = -1`,æ¤æ—¶é€šé“ C 必须是 1 或者 3 表示输入数æ®æ˜¯å›¾ç‰‡ï¼Œå¯ä»¥ç›´æŽ¥æ˜¾ç¤ºï¼Œå¦åˆ™æŠ¥é”™æ示用户应该设置 mode æ¥åŽ‹ç¼©é€šé“ +在获å–到 visualizer åŽï¼Œå¯ä»¥è°ƒç”¨ `add_xxx` 接å£å°†å„类数æ®å†™å…¥åˆ°ç‰¹å®šåŽç«¯ ```python -featmap=visualizer.draw_featmap(tensor_chw,image) -``` +# 绘制 datasample,并ä¿å˜åˆ°æœ¬åœ°å˜å‚¨åŽç«¯ +visualizer.add_datasample('demo_image', image, gt_sample, pred_sample, step=1) +# 直接本地窗å£æ˜¾ç¤ºï¼Œè€Œæ— 需å˜å‚¨ +visualizer.add_datasample('demo_image', image, gt_sample, pred_sample, show=True) -### 自定义 Visualizer +# 写图片 +visualizer.add_image('demo_image', image, step=1) -自定义的 Visualizer ä¸å¤§éƒ¨åˆ†æƒ…况下åªéœ€è¦å®žçŽ° `get_image` å’Œ `draw` 接å£ã€‚`draw` 是最高层的用户调用接å£ï¼Œ`draw` 接å£è´Ÿè´£æ‰€æœ‰ç»˜åˆ¶åŠŸèƒ½ï¼Œä¾‹å¦‚绘制检测框ã€æ£€æµ‹æŽ©ç mask å’Œ 检测è¯ä¹‰åˆ†å‰²å›¾ç‰ç‰ã€‚ä¾æ®ä»»åŠ¡çš„ä¸åŒï¼Œ`draw` 接å£å®žçŽ°çš„å¤æ‚度也ä¸åŒã€‚ +# 写模型精度值 +visualizer.add_scalar('mAP', 0.9, step=1) +visualizer.add_scalars({'loss': 1.2, 'acc': 0.8}, step=1) -ä»¥ç›®æ ‡æ£€æµ‹å¯è§†åŒ–需求为例,å¯èƒ½éœ€è¦åŒæ—¶ç»˜åˆ¶è¾¹ç•Œæ¡† bboxã€æŽ©ç mask å’Œè¯ä¹‰åˆ†å‰²å›¾ seg_map,如果如æ¤å¤šåŠŸèƒ½å…¨éƒ¨å†™åˆ° `draw` 方法ä¸ä¼šéš¾ä»¥ç†è§£å’Œç»´æŠ¤ã€‚为了解决该问题,`Visualizer` 基于 OpenMMLab 2.0 抽象数æ®æŽ¥å£è§„范支æŒäº† `register_task` 函数。å‡è®¾ MMDetection ä¸éœ€è¦åŒæ—¶ç»˜åˆ¶é¢„测结果ä¸çš„ instances å’Œ sem_seg,å¯ä»¥åœ¨ MMDetection çš„ `DetVisualizer` ä¸å®žçŽ° `draw_instances` å’Œ `draw_sem_seg` 两个方法,用于绘制预测实例和预测è¯ä¹‰åˆ†å‰²å›¾ï¼Œ 我们希望åªè¦è¾“入数æ®ä¸å˜åœ¨ instances 或 sem_seg 时候,对应的两个绘制函数 `draw_instances` å’Œ `draw_sem_seg` 能够自动被调用,而用户ä¸éœ€è¦æ‰‹åŠ¨è°ƒç”¨ã€‚为了实现上述功能,å¯ä»¥é€šè¿‡åœ¨ `draw_instances` å’Œ `draw_sem_seg` ä¸¤ä¸ªå‡½æ•°åŠ ä¸Š `@Visualizer.register_task` 装饰器,æ¤æ—¶ `task_dict` ä¸å°±ä¼šå˜å‚¨å—ç¬¦ä¸²å’Œå‡½æ•°çš„æ˜ å°„å…³ç³»ï¼Œåœ¨è°ƒç”¨ `draw` 方法时候就å¯ä»¥é€šè¿‡ `self.task_dict`获å–到已ç»è¢«æ³¨å†Œçš„函数。一个简略的实现如下所示 +# 写é…置文件 +visualizer.add_config(cfg) -```python -class DetVisualizer(Visualizer): - - def draw(self, image, gt_sample=None, pred_sample=None, draw_gt=True, draw_pred=True): - # 将图片和 matplotlib å¸ƒå±€å…³è” - self.set_image(image) - - if draw_gt: - # self.task_dict 内部å˜å‚¨å¦‚下信æ¯ï¼š - # dict(instances=draw_instance 方法,sem_seg=draw_sem_seg 方法) - for task in self.task_dict: - task_attr = 'gt_' + task - if task_attr in gt_sample: - self.task_dict[task](self, gt_sample[task_attr], 'gt') - if draw_pred: - for task in self.task_dict: - task_attr = 'pred_' + task - if task_attr in pred_sample: - self.task_dict[task](self, pred_sample[task_attr], 'pred') - - # data_type 用于区分当å‰ç»˜åˆ¶çš„å†…å®¹æ˜¯æ ‡æ³¨è¿˜æ˜¯é¢„æµ‹ç»“æžœ - @Visualizer.register_task('instances') - def draw_instance(self, instances, data_type): - ... - - # data_type 用于区分当å‰ç»˜åˆ¶çš„å†…å®¹æ˜¯æ ‡æ³¨è¿˜æ˜¯é¢„æµ‹ç»“æžœ - @Visualizer.register_task('sem_seg') - def draw_sem_seg(self, pixel_data, data_type): - ... +# 写模型图 +visualizer.add_graph(model, data_batch) ``` -注æ„:是å¦ä½¿ç”¨ `register_task` 装饰器函数ä¸æ˜¯å¿…须的,如果用户自定义 Visualizer,并且 `draw` 实现éžå¸¸ç®€å•ï¼Œåˆ™æ— 需考虑 `register_task`。 +**(3) 特å¾å›¾å¯è§†åŒ–** -在使用 Jupyter notebook 或者其他地方ä¸éœ€è¦å†™æ•°æ®åˆ°æŒ‡å®šåŽç«¯çš„情形下,用户å¯ä»¥è‡ªå·±å®žä¾‹åŒ– visualizer。一个简å•çš„例å如下 +通过 `channel_reduction` å‚数压缩或者选择特å¾å›¾ï¼Œå¹¶æ˜¾ç¤ºåˆ°æœ¬åœ°çª—å£ ```python -# 实例化 visualizer -visualizer=dict(type='DetVisualizer') -visualizer = VISUALIZERS.build(visualizer) -visualizer.draw(image, datasample) -visualizer.show() # å¯è§†åŒ–绘制结果 +featmap = ... # CHW shape çš„ tensor + +# 压缩 +feat_img = visualizer.draw_featmap(featmap, channel_reduction='squeeze_mean') +visualizer.show(feat_img) + +# 选择激活度最高的通é“显示 +feat_img = visualizer.draw_featmap(featmap, channel_reduction='select_max') +visualizer.show(feat_img) ``` -## 写端 Writer +å åŠ å›¾ç‰‡æ˜¾ç¤º + +```python +featmap = ... # CHW shape çš„ tensor +img = ... # 如果 featmap å’Œ img 空间尺寸ä¸ä¸€è‡´ï¼Œå†…部会对 featmap 进行æ’值 -Visualizer åªå®žçŽ°äº†å•å¼ 图片的绘制功能,但是在è®ç»ƒæˆ–者测试过程ä¸ï¼Œå¯¹ä¸€äº›å…³é”®æŒ‡æ ‡æˆ–者模型è®ç»ƒè¶…å‚的记录éžå¸¸é‡è¦ï¼Œæ¤åŠŸèƒ½é€šè¿‡å†™ç«¯ Writer 实现。为了统一接å£è°ƒç”¨ï¼ŒMMEngine æ供了统一的抽象类 `BaseWriter`,和一些常用的 Writer 如 `LocalWriter` ã€`TensorboardWriter` å’Œ `WandbWriter` 。 +# 压缩 +feat_img = visualizer.draw_featmap(featmap, img, channel_reduction='squeeze_mean') +visualizer.show(feat_img) -### BaseWriter +# 选择激活度最高的通é“显示 +feat_img = visualizer.draw_featmap(featmap, img, channel_reduction='select_max') +visualizer.show(feat_img) +``` -BaseWriter 定义了对外调用的接å£è§„范,主è¦æŽ¥å£å’Œå±žæ€§å¦‚下: +通过 `topk` å‚数选择指定个数的通é“æ˜¾ç¤ºï¼Œå¹¶æ˜¾ç¤ºåˆ°æœ¬åœ°çª—å£ -- [add_params](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.writer.BaseWriter.add_params) 写超å‚到特定åŽç«¯ï¼Œå¸¸è§çš„è®ç»ƒè¶…å‚如åˆå§‹å¦ä¹ 率 LRã€æƒé‡è¡°å‡ç³»æ•°å’Œæ‰¹å¤§å°ç‰ç‰ -- [add_graph](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.writer.BaseWriter.add_graph) 写模型图到特定åŽç«¯ -- [add_image](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.writer.BaseWriter.add_image) 写图片到特定åŽç«¯ -- [add_scalar](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.writer.BaseWriter.add_scalar) å†™æ ‡é‡åˆ°ç‰¹å®šåŽç«¯ -- [add_scalars](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.writer.BaseWriter.add_scalars) ä¸€æ¬¡æ€§å†™å¤šä¸ªæ ‡é‡åˆ°ç‰¹å®šåŽç«¯ -- [visualizer](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.writer.BaseWriter.visualizer) 绘制对象 -- [experiment](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.writer.BaseWriter.experiment) 写åŽç«¯å¯¹è±¡ï¼Œä¾‹å¦‚ Wandb 对象和 Tensorboard 对象 +```python +featmap= ... # CHW shape çš„ tensor -`BaseWriter` 定义了 5 个常è§çš„写数æ®æŽ¥å£ï¼Œè€ƒè™‘到æŸäº›å†™åŽç«¯åŠŸèƒ½éžå¸¸å¼ºå¤§ï¼Œä¾‹å¦‚ Wandbï¼Œå…¶å…·å¤‡å†™è¡¨æ ¼ï¼Œå†™è§†é¢‘ç‰ç‰åŠŸèƒ½ï¼Œé’ˆå¯¹è¿™ç±»éœ€æ±‚用户å¯ä»¥ç›´æŽ¥èŽ·å– experiment 对象,然åŽè°ƒç”¨å†™åŽç«¯å¯¹è±¡æœ¬èº«çš„ API å³å¯ã€‚ +# topk,并以 2 è¡Œ 5 列模å¼æ˜¾ç¤º +feat_img = visualizer.draw_featmap(featmap, channel_reduction=None, topk=10, arrangement=(2, 5)) +visualizer.show(feat_img) -### LocalWriterã€TensorboardWriter å’Œ WandbWriter +# topk,并以 5 è¡Œ 2 列模å¼æ˜¾ç¤º +feat_img = visualizer.draw_featmap(featmap, channel_reduction=None, topk=10, arrangement=(5, 2)) +visualizer.show(feat_img) +``` -`LocalWriter` æ供了将数æ®å†™å…¥åˆ°æœ¬åœ°ç£ç›˜åŠŸèƒ½ã€‚如果用户需è¦å†™å›¾ç‰‡åˆ°ç¡¬ç›˜ï¼Œåˆ™**å¿…é¡»è¦é€šè¿‡åˆå§‹åŒ–å‚æ•°æä¾› Visualizer对象**。其典型用法为: +通过 `resize_shape` 缩放显示的特å¾å›¾ ```python -# é…置文件 -writer=dict(type='LocalWriter', save_dir='demo_dir', visualizer=dict(type='DetVisualizer')) -# 实例化和调用 -local_writer=WRITERS.build(writer) -# 写模型精度值 -local_writer.add_scalar('mAP', 0.9) -local_writer.add_scalars({'loss': 1.2, 'acc': 0.8}) -# å†™è¶…å‚ -local_writer.add_params(dict(lr=0.1, mode='linear')) -# 写图片 -local_writer.add_image('demo_image', image, datasample) +featmap = ... # CHW shape çš„ tensor + +# 压缩 +feat_img = visualizer.draw_featmap(featmap, channel_reduction='squeeze_mean', resize_shape=(224, 224)) +visualizer.show(feat_img) ``` -如果用户有自定义绘制需求,则å¯ä»¥é€šè¿‡èŽ·å–内部的 visualizer 属性æ¥å®žçŽ°ï¼Œå¦‚下所示 +å˜å‚¨ç‰¹å¾å›¾åˆ°å¯è§†åŒ–åŽç«¯ ```python -# é…置文件 -writer=dict(type='LocalWriter', save_dir='demo_dir', visualizer=dict(type='DetVisualizer')) -# 实例化和调用 -local_writer=WRITERS.build(writer) -# 写图片 -local_writer.visualizer.draw_bboxes(np.array([0, 0, 1, 1])) -local_writer.add_image('img', local_writer.visualizer.get_image()) +featmap = ... # CHW shape çš„ tensor -# 绘制特å¾å›¾å¹¶ä¿å˜åˆ°æœ¬åœ° -featmap_image=local_writer.visualizer.draw_featmap(tensor_chw) -local_writer.add_image('featmap', featmap_image) +# 压缩 +feat_img = visualizer.draw_featmap(featmap, channel_reduction='squeeze_mean', resize_shape=(224, 224)) +# å˜å‚¨ +visualizer.add_image('feat_image', feat_img) ``` -`TensorboardWriter` æ供了将å„类数æ®å†™å…¥åˆ° Tensorboard 功能,其用法和 LocalWriter éžå¸¸ç±»ä¼¼ã€‚ 注æ„如果用户需è¦å†™å›¾ç‰‡åˆ° Tensorboard,则**å¿…é¡»è¦é€šè¿‡åˆå§‹åŒ–å‚æ•°æä¾› Visualizer对象**。 +**(4) 远程窗å£æ˜¾ç¤º** -`WandbWriter` æ供了将å„类数æ®å†™å…¥åˆ° Wandb 功能。考虑到 Wandb 本身具备强大的图片功能,在调用 `WandbWriter` çš„ `add_image` 方法时 Visualizer 对象是å¯é€‰çš„,如果用户指定了 Visualizer 对象,则会调用 Visualizer 对象的绘制方法,å¦åˆ™ç›´æŽ¥è°ƒç”¨ Wandb 自带的图片处ç†åŠŸèƒ½ã€‚ +用户å¯ä»¥æŒ‡å®š Wandb ã€Tensorboard 或者自定义具备远程窗å£æ˜¾ç¤ºçš„åŽç«¯æ¥ä¿å˜æ•°æ®ï¼Œç„¶åŽåœ¨æµè§ˆå™¨ä¸Šæ˜¾ç¤ºã€‚以 Wandb 为例,典型é…置为: -## 组åˆå†™ç«¯ ComposedWriter +```python +vis_backends = [dict(type='WandbVisBackend')] +visualizer = dict( + type='DetWandbVisualizer', vis_backends=vis_backends, name='visualizer') +``` -考虑到在è®ç»ƒæˆ–者测试过程ä¸ï¼Œå¯èƒ½éœ€è¦åŒæ—¶è°ƒç”¨å¤šä¸ª Writer,例如想åŒæ—¶å†™åˆ°æœ¬åœ°å’Œ Wandb 端,为æ¤è®¾è®¡äº†å¯¹å¤–çš„ `ComposedWriter` 类,在è®ç»ƒæˆ–è€…æµ‹è¯•è¿‡ç¨‹ä¸ `ComposedWriter` 会ä¾æ¬¡è°ƒç”¨å„个 Writer 的接å£ï¼Œå…¶æŽ¥å£å’Œ `BaseWriter` 一致,主è¦æŽ¥å£å¦‚下: +使用方法和上é¢å®Œå…¨ä¸€è‡´ã€‚需è¦ç‰¹åˆ«æ³¨æ„的是由于 Wandb 绘制的数æ®æ— 法和 `LocalVisBackend` åŽç«¯å…¼å®¹ï¼Œæ‰€ä»¥å½“ `vis_backends` å˜åœ¨å¤šä¸ªå¯è§†åŒ–å˜å‚¨åŽç«¯æ—¶å€™åªæœ‰ `WandbVisBackend` æ‰æ˜¯æœ‰æ•ˆçš„。 -- [add_params](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.writer.ComposedWriter.add_params) 写超å‚到所有已ç»åŠ 入的åŽç«¯ä¸ï¼Œå¸¸è§çš„è®ç»ƒè¶…å‚如åˆå§‹å¦ä¹ 率 LRã€æƒé‡è¡°å‡ç³»æ•°å’Œæ‰¹å¤§å°ç‰ç‰ -- [add_graph](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.writer.ComposedWriter.add_graph) 写模型图到所有已ç»åŠ 入的åŽç«¯ä¸ -- [add_image](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.writer.ComposedWriter.add_image) 写图片到所有已ç»åŠ 入的åŽç«¯ä¸ -- [add_scalar](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.writer.ComposedWriter.add_scalar) å†™æ ‡é‡åˆ°æ‰€æœ‰å·²ç»åŠ 入的åŽç«¯ä¸ -- [add_scalars](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.writer.ComposedWriter.add_scalars) ä¸€æ¬¡æ€§å†™å¤šä¸ªæ ‡é‡åˆ°æ‰€æœ‰å·²ç»åŠ 入的åŽç«¯ä¸ -- [get_writer](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.writer.ComposedWriter.get_writer) 获å–指定索引的 Writer,任何一个 Writer ä¸åŒ…括了 experiment å’Œ visualizer 属性 -- [get_experiment](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.writer.ComposedWriter.get_experiment) 获å–指定索引的 experiment -- [get_visualizer](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.writer.ComposedWriter.get_visualizer) 获å–指定索引的 visualizer -- [close](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.writer.ComposedWriter.close) 调用所有 Writer çš„ close 方法 +## å¯è§†åŒ–å˜å‚¨åŽç«¯ VisBackend -为了让用户å¯ä»¥åœ¨ä»£ç çš„ä»»æ„ä½ç½®è¿›è¡Œæ•°æ®å¯è§†åŒ–,`ComposedWriter` 类继承至 [全局å¯è®¿é—®åŸºç±» BaseGlobalAccessible](./logging.md/#全局å¯è®¿é—®åŸºç±»baseglobalaccessible)。一旦继承了全局å¯è®¿é—®åŸºç±», 用户就å¯ä»¥é€šè¿‡è°ƒç”¨ `ComposedWriter` 对象的 `get_instance` æ¥èŽ·å–全局对象。其基本用法如下 +在绘制åŽå¯ä»¥å°†ç»˜åˆ¶åŽçš„æ•°æ®å˜å‚¨åˆ°å¤šä¸ªå¯è§†åŒ–å˜å‚¨åŽç«¯ä¸ã€‚为了统一接å£è°ƒç”¨ï¼ŒMMEngine æ供了统一的抽象类 `BaseVisBackend`,和一些常用的 VisBackend 如 `LocalVisBackend`ã€`WandbVisBackend` å’Œ `TensorboardVisBackend`。 -```python -# 创建实例 -writers=[dict(type='LocalWriter', save_dir='temp_dir', visualizer=dict(type='DetVisualizer')), dict(type='WandbWriter')] +### 接å£è¯´æ˜Ž -ComposedWriter.create_instance('composed_writer', writers=writers) -``` +BaseVisBackend 定义了对外调用的接å£è§„范,主è¦æŽ¥å£å’Œå±žæ€§å¦‚下: -一旦创建实例åŽï¼Œå¯ä»¥åœ¨ä»£ç ä»»æ„ä½ç½®èŽ·å– `ComposedWriter` 对象 +- [add_config](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.vis_backend.BaseVisBackend.add_config) 写é…置到特定å˜å‚¨åŽç«¯ +- [add_graph](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.vis_backend.BaseVisBackend.add_graph) 写模型图到特定åŽç«¯ +- [add_image](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.vis_backend.BaseVisBackend.add_image) 写图片到特定åŽç«¯ +- [add_scalar](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.vis_backend.BaseVisBackend.add_scalar) å†™æ ‡é‡åˆ°ç‰¹å®šåŽç«¯ +- [add_scalars](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.vis_backend.BaseVisBackend.add_scalars) ä¸€æ¬¡æ€§å†™å¤šä¸ªæ ‡é‡åˆ°ç‰¹å®šåŽç«¯ +- [close](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.vis_backend.BaseVisBackend.close) å…³é—å·²ç»æ‰“å¼€çš„èµ„æº +- [experiment](https://mmengine.readthedocs.io/zh/latest/api.html#mmengine.visualization.vis_backend.BaseVisBackend.experiment) 写åŽç«¯å¯¹è±¡ï¼Œä¾‹å¦‚ Wandb 对象和 Tensorboard 对象 -```python -composed_writer=ComposedWriter.get_instance('composed_writer') +`BaseVisBackend` 定义了 5 个常è§çš„写数æ®æŽ¥å£ï¼Œè€ƒè™‘到æŸäº›å†™åŽç«¯åŠŸèƒ½éžå¸¸å¼ºå¤§ï¼Œä¾‹å¦‚ Wandbï¼Œå…¶å…·å¤‡å†™è¡¨æ ¼ï¼Œå†™è§†é¢‘ç‰ç‰åŠŸèƒ½ï¼Œé’ˆå¯¹è¿™ç±»éœ€æ±‚用户å¯ä»¥ç›´æŽ¥èŽ·å– experiment 对象,然åŽè°ƒç”¨å†™åŽç«¯å¯¹è±¡æœ¬èº«çš„ API å³å¯ã€‚而 `LocalVisBackend`ã€`WandbVisBackend` å’Œ `TensorboardVisBackend` ç‰éƒ½æ˜¯ç»§æ‰¿è‡ª `BaseVisBackend`ï¼Œå¹¶æ ¹æ®è‡ªèº«ç‰¹æ€§å®žçŽ°äº†å¯¹åº”çš„å˜å‚¨åŠŸèƒ½ã€‚ -# 写模型精度值 -composed_writer.add_scalar('mAP', 0.9) -composed_writer.add_scalars({'loss': 1.2, 'acc': 0.8}) -# å†™è¶…å‚ -composed_writer.add_params(dict(lr=0.1, mode='linear')) -# 写图片 -composed_writer.add_image('demo_image', image, datasample) -# 写模型图 -composed_writer.add_graph(model, input_array) -``` +### 使用案例 -对于一些用户需è¦çš„自定义绘制需求或者上述接å£æ— 法满足的需求,用户å¯ä»¥é€šè¿‡ `get_xxx` 方法获å–具体对象æ¥å®žçŽ°ç‰¹å®šéœ€æ±‚ +ä¸€èˆ¬æƒ…å†µä¸‹ç”¨æˆ·æ— éœ€æ“作 VisBackend 对象,åªæœ‰åœ¨å½“å‰å¯è§†åŒ–å˜å‚¨æ— 法满足需求时候,用户会希望直接æ“作å˜å‚¨åŽç«¯ã€‚以 Wandb 为例,其æ供了éžå¸¸ä¸°å¯Œçš„å˜å‚¨æ ¼å¼ï¼Œä¾‹å¦‚å˜å‚¨è¡¨æ ¼ã€å˜å‚¨æƒé‡ç‰ç‰æŽ¥å£ã€‚为了所有åŽç«¯èƒ½å¤Ÿç»Ÿä¸€æŽ¥å£ï¼Œæˆ‘们并没有æ供这类常用接å£ï¼Œæ¤æ—¶ç”¨æˆ·å¯ä»¥ç›´æŽ¥èŽ·å– Wandb 对象进行自定义å˜å‚¨ã€‚ ```python -composed_writer=ComposedWriter.get_instance('composed_writer') +vis_backends = [dict(type='WandbVisBackend')] +visualizer = dict( + type='DetWandbVisualizer', vis_backends=vis_backends, name='visualizer') +``` -# 绘制特å¾å›¾ï¼ŒèŽ·å– LocalWriter ä¸çš„ visualizer -visualizer=composed_writer.get_visualizer(0) -featmap_image=visualizer.draw_featmap(tensor_chw) -composed_writer.add_image('featmap', featmap_image) +```python +# 内部会调用 get_instance() 进行全局唯一实例化 +VISUALIZERS.build(cfg.visualizer) +# ä»»æ„代ç ä½ç½®èŽ·å– visualizer +visualizer = Visualizer.get_current_instance() # 扩展 add 功能,例如利用 Wandb å¯¹è±¡ç»˜åˆ¶è¡¨æ ¼ -wandb=composed_writer.get_experiment(1) +wandb = visualizer.get_backend('WandbVisBackend').experiment val_table = wandb.Table(data=my_data, columns=column_names) wandb.log({'my_val_table': val_table}) +``` + +一个 visualizer 对象å¯ä»¥æŽ¥å…¥ä»»æ„多个 VisBackend。为了方便用户获å–ä»»æ„çš„ VisBackend,在ä¸æŒ‡å®š name å‚数情况下,å¯ä»¥é€šè¿‡ç±»åèŽ·å– + +```python +vis_backends = [dict(type='LocalVisBackend'), dict(type='WandbVisBackend')] +visualizer = dict( + type='DetLocalVisualizer', vis_backends=vis_backends, name='visualizer') +``` + +```python +# 内部会调用 get_instance() 进行全局唯一实例化 +VISUALIZERS.build(cfg.visualizer) +# ä»»æ„代ç ä½ç½®èŽ·å– visualizer +visualizer = Visualizer.get_current_instance() + +local_vis_backend = visualizer.get_backend('LocalVisBackend') +wandb_vis_backend = visualizer.get_backend('WandbVisBackend') +``` + +当å˜åœ¨å¤šä¸ªåŒåçš„ VisBackend 时候,用户必须指定唯一的 name å‚数,åŽç»å¯ä»¥é€šè¿‡ name å—符串æ¥èŽ·å– + +```python +vis_backends = [dict(type='LocalVisBackend', name='local_vis_backend_1'), dict(type='LocalVisBackend', name='local_vis_backend_2')] +visualizer = dict( + type='DetLocalVisualizer', vis_backends=vis_backends, name='visualizer') +``` + +```python +# 内部会调用 get_instance() 进行全局唯一实例化 +VISUALIZERS.build(cfg.visualizer) +# ä»»æ„代ç ä½ç½®èŽ·å– visualizer +visualizer = Visualizer.get_current_instance() -# é…ç½®ä¸å˜åœ¨å¤šä¸ª Writer,在ä¸æƒ³æ”¹åŠ¨é…置情况下åªä½¿ç”¨ LocalWriter -local_writer=composed_writer.get_writer(0) -local_writer.add_image('demo_image', image, datasample) +local_vis_backend_1 = visualizer.get_backend('local_vis_backend_1') +local_vis_backend_2 = visualizer.get_backend('local_vis_backend_2') ``` -- GitLab