diff --git a/docs/zh_cn/tutorials/visualization.md b/docs/zh_cn/tutorials/visualization.md new file mode 100644 index 0000000000000000000000000000000000000000..80acabccf07a68e872cdfc0362f00aef9e34f509 --- /dev/null +++ b/docs/zh_cn/tutorials/visualization.md @@ -0,0 +1,301 @@ +# å¯è§†åŒ– (Visualization) + +## 概述 + +**(1) 总体介ç»** + +å¯è§†åŒ–å¯ä»¥ç»™æ·±åº¦å¦ä¹ 的模型è®ç»ƒå’Œæµ‹è¯•è¿‡ç¨‹æ供直观解释。在 OpenMMLab 算法库ä¸ï¼Œæˆ‘们期望å¯è§†åŒ–功能的设计能满足以下需求: + +- æ供丰富的开箱å³ç”¨å¯è§†åŒ–功能,能够满足大部分计算机视觉å¯è§†åŒ–任务 +- 高扩展性,å¯è§†åŒ–åŠŸèƒ½é€šå¸¸å¤šæ ·åŒ–ï¼Œåº”è¯¥èƒ½å¤Ÿé€šè¿‡ç®€å•æ‰©å±•å®žçŽ°å®šåˆ¶éœ€æ±‚ +- 能够在è®ç»ƒå’Œæµ‹è¯•æµç¨‹çš„ä»»æ„点ä½è¿›è¡Œå¯è§†åŒ– +- OpenMMLab å„个算法库具有统一å¯è§†åŒ–接å£ï¼Œåˆ©äºŽç”¨æˆ·ç†è§£å’Œç»´æŠ¤ + +基于上述需求,OpenMMLab 2.0 引入了绘制对象 Visualizer 和写端对象 Writer 的概念 + +- **Visualizer è´Ÿè´£å•å¼ 图片的绘制功能** + + MMEngine æ供了以 Matplotlib 库为绘制åŽç«¯çš„ `Visualizer` 类,其具备如下功能: + + - æä¾›äº†ä¸€ç³»åˆ—å’Œè§†è§‰ä»»åŠ¡æ— å…³çš„åŸºç¡€æ–¹æ³•ï¼Œä¾‹å¦‚ `draw_bboxes` å’Œ `draw_texts` ç‰ + - å„个基础方法支æŒé“¾å¼è°ƒç”¨ï¼Œæ–¹ä¾¿å åŠ ç»˜åˆ¶æ˜¾ç¤º + - 通过 `draw_featmap` æ供绘制特å¾å›¾åŠŸèƒ½ + + å„个下游算法库å¯ä»¥ç»§æ‰¿ `Visualizer` 并在 `draw` 接å£ä¸å®žçŽ°æ‰€éœ€çš„å¯è§†åŒ–功能,例如 MMDetection ä¸çš„ `DetVisualizer` 继承自 `Visualizer` 并在 `draw` 接å£ä¸å®žçŽ°å¯è§†åŒ–检测框ã€å®žä¾‹æŽ©ç å’Œè¯ä¹‰åˆ†å‰²å›¾ç‰åŠŸèƒ½ã€‚Visualizer 类的 UML 关系图如下 + + <div align="center"> + <img src="https://user-images.githubusercontent.com/17425982/154475592-7208a34b-f6cb-4171-b0be-9dbb13306862.png" > + </div> + +- **Writer 负责将å„类数æ®å†™å…¥åˆ°æŒ‡å®šåŽç«¯** + + 为了统一接å£è°ƒç”¨ï¼ŒMMEngine æ供了统一的抽象类 `BaseWriter`,和一些常用的 Writer 如 `LocalWriter` æ¥æ”¯æŒå°†æ•°æ®å†™å…¥æœ¬åœ°ï¼Œ`TensorboardWriter` æ¥æ”¯æŒå°†æ•°æ®å†™å…¥ Tensorboard,`WandbWriter` æ¥æ”¯æŒå°†æ•°æ®å†™å…¥ Wandb。用户也å¯ä»¥è‡ªå®šä¹‰ Writer æ¥å°†æ•°æ®å†™å…¥è‡ªå®šä¹‰åŽç«¯ã€‚写入的数æ®å¯ä»¥æ˜¯å›¾ç‰‡ï¼Œæ¨¡åž‹ç»“æž„å›¾ï¼Œæ ‡é‡å¦‚æ¨¡åž‹ç²¾åº¦æŒ‡æ ‡ç‰ã€‚ + + 考虑到在è®ç»ƒæˆ–者测试过程ä¸å¯èƒ½åŒæ—¶å˜åœ¨å¤šä¸ª 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> + +**(2) Writer å’Œ Visualizer 关系** + +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 或者å类对象 + +`WandbWriter` 的一个简略的演示代ç 如下 + +```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}, ...) +``` + + +## 绘制对象 Visualizer + +绘制对象 Visualizer è´Ÿè´£å•å¼ 图片的å„类绘制功能,默认绘制åŽç«¯ä¸º Matplotlib。为了统一 OpenMMLab å„个算法库的å¯è§†åŒ–接å£ï¼ŒMMEngine 定义æ供了基础绘制功能的 `Visualizer` 类,下游库å¯ä»¥ç»§æ‰¿ `Visualizer` 并实现 `draw` 接å£æ¥æ»¡è¶³è‡ªå·±çš„绘制需求。 + +### Visualizer + +`Visualizer` æ供了基础而通用的绘制功能,主è¦æŽ¥å£å¦‚下: + +**(1) ç»˜åˆ¶æ— å…³çš„åŠŸèƒ½æ€§æŽ¥å£** + +- [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) 绘制相关接å£** + +- [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) 链å¼è°ƒç”¨** + +例如用户先绘制边界框,在æ¤åŸºç¡€ä¸Šç»˜åˆ¶æ–‡æœ¬ï¼Œç»˜åˆ¶çº¿æ®µï¼Œåˆ™è°ƒç”¨è¿‡ç¨‹ä¸ºï¼š + +```python +visualizer.set_image(image) +visualizer.draw_bboxes(...).draw_texts(...).draw_lines(...) +visualizer.show() # å¯è§†åŒ–绘制结果 +``` + +**(2) å¯è§†åŒ–特å¾å›¾** + +特å¾å›¾å¯è§†åŒ–是一个常è§çš„功能,通过调用 `draw_featmap` å¯ä»¥ç›´æŽ¥å¯è§†åŒ–特å¾å›¾ï¼Œå…¶å‚数定义为: + +```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: # 图片和特å¾å›¾ç»˜åˆ¶çš„å åŠ æ¯”ä¾‹ +``` + +特å¾å›¾å¯è§†åŒ–功能较多,目å‰ä¸æ”¯æŒ Batch 输入 + +- 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 æ¥åŽ‹ç¼©é€šé“ + +```python +featmap=visualizer.draw_featmap(tensor_chw,image) +``` + +### 自定义 Visualizer + +自定义的 Visualizer ä¸å¤§éƒ¨åˆ†æƒ…况下åªéœ€è¦å®žçŽ° `get_image` å’Œ `draw` 接å£ã€‚`draw` 是最高层的用户调用接å£ï¼Œ`draw` 接å£è´Ÿè´£æ‰€æœ‰ç»˜åˆ¶åŠŸèƒ½ï¼Œä¾‹å¦‚绘制检测框ã€æ£€æµ‹æŽ©ç mask å’Œ 检测è¯ä¹‰åˆ†å‰²å›¾ç‰ç‰ã€‚ä¾æ®ä»»åŠ¡çš„ä¸åŒï¼Œ`draw` 接å£å®žçŽ°çš„å¤æ‚度也ä¸åŒã€‚ + +ä»¥ç›®æ ‡æ£€æµ‹å¯è§†åŒ–需求为例,å¯èƒ½éœ€è¦åŒæ—¶ç»˜åˆ¶è¾¹ç•Œæ¡† 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`获å–到已ç»è¢«æ³¨å†Œçš„函数。一个简略的实现如下所示 + +```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): + ... +``` + +注æ„:是å¦ä½¿ç”¨ `register_task` 装饰器函数ä¸æ˜¯å¿…须的,如果用户自定义 Visualizer,并且 `draw` 实现éžå¸¸ç®€å•ï¼Œåˆ™æ— 需考虑 `register_task`。 + +在使用 Jupyter notebook 或者其他地方ä¸éœ€è¦å†™æ•°æ®åˆ°æŒ‡å®šåŽç«¯çš„情形下,用户å¯ä»¥è‡ªå·±å®žä¾‹åŒ– visualizer。一个简å•çš„例å如下 + +```python +# 实例化 visualizer +visualizer=dict(type='DetVisualizer') +visualizer = VISUALIZERS.build(visualizer) +visualizer.draw(image, datasample) +visualizer.show() # å¯è§†åŒ–绘制结果 +``` + +## 写端 Writer + +Visualizer åªå®žçŽ°äº†å•å¼ 图片的绘制功能,但是在è®ç»ƒæˆ–者测试过程ä¸ï¼Œå¯¹ä¸€äº›å…³é”®æŒ‡æ ‡æˆ–者模型è®ç»ƒè¶…å‚的记录éžå¸¸é‡è¦ï¼Œæ¤åŠŸèƒ½é€šè¿‡å†™ç«¯ Writer 实现。为了统一接å£è°ƒç”¨ï¼ŒMMEngine æ供了统一的抽象类 `BaseWriter`,和一些常用的 Writer 如 `LocalWriter` ã€`TensorboardWriter` å’Œ `WandbWriter` 。 + +### BaseWriter + +BaseWriter 定义了对外调用的接å£è§„范,主è¦æŽ¥å£å’Œå±žæ€§å¦‚下: + +- [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 对象 + +`BaseWriter` 定义了 5 个常è§çš„写数æ®æŽ¥å£ï¼Œè€ƒè™‘到æŸäº›å†™åŽç«¯åŠŸèƒ½éžå¸¸å¼ºå¤§ï¼Œä¾‹å¦‚ Wandbï¼Œå…¶å…·å¤‡å†™è¡¨æ ¼ï¼Œå†™è§†é¢‘ç‰ç‰åŠŸèƒ½ï¼Œé’ˆå¯¹è¿™ç±»éœ€æ±‚用户å¯ä»¥ç›´æŽ¥èŽ·å– experiment 对象,然åŽè°ƒç”¨å†™åŽç«¯å¯¹è±¡æœ¬èº«çš„ API å³å¯ã€‚ + +### LocalWriterã€TensorboardWriter å’Œ WandbWriter + +`LocalWriter` æ供了将数æ®å†™å…¥åˆ°æœ¬åœ°ç£ç›˜åŠŸèƒ½ã€‚如果用户需è¦å†™å›¾ç‰‡åˆ°ç¡¬ç›˜ï¼Œåˆ™**å¿…é¡»è¦é€šè¿‡åˆå§‹åŒ–å‚æ•°æä¾› Visualizer对象**。其典型用法为: + +```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) +``` + +如果用户有自定义绘制需求,则å¯ä»¥é€šè¿‡èŽ·å–内部的 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_image=local_writer.visualizer.draw_featmap(tensor_chw) +local_writer.add_image('featmap', featmap_image) +``` + +`TensorboardWriter` æ供了将å„类数æ®å†™å…¥åˆ° Tensorboard 功能,其用法和 LocalWriter éžå¸¸ç±»ä¼¼ã€‚ 注æ„如果用户需è¦å†™å›¾ç‰‡åˆ° Tensorboard,则**å¿…é¡»è¦é€šè¿‡åˆå§‹åŒ–å‚æ•°æä¾› Visualizer对象**。 + +`WandbWriter` æ供了将å„类数æ®å†™å…¥åˆ° Wandb 功能。考虑到 Wandb 本身具备强大的图片功能,在调用 `WandbWriter` çš„ `add_image` 方法时 Visualizer 对象是å¯é€‰çš„,如果用户指定了 Visualizer 对象,则会调用 Visualizer 对象的绘制方法,å¦åˆ™ç›´æŽ¥è°ƒç”¨ Wandb 自带的图片处ç†åŠŸèƒ½ã€‚ + +## 组åˆå†™ç«¯ ComposedWriter + +考虑到在è®ç»ƒæˆ–者测试过程ä¸ï¼Œå¯èƒ½éœ€è¦åŒæ—¶è°ƒç”¨å¤šä¸ª Writer,例如想åŒæ—¶å†™åˆ°æœ¬åœ°å’Œ Wandb 端,为æ¤è®¾è®¡äº†å¯¹å¤–çš„ `ComposedWriter` 类,在è®ç»ƒæˆ–è€…æµ‹è¯•è¿‡ç¨‹ä¸ `ComposedWriter` 会ä¾æ¬¡è°ƒç”¨å„个 Writer 的接å£ï¼Œå…¶æŽ¥å£å’Œ `BaseWriter` 一致,主è¦æŽ¥å£å¦‚下: + +- [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 方法 + +为了让用户å¯ä»¥åœ¨ä»£ç çš„ä»»æ„ä½ç½®è¿›è¡Œæ•°æ®å¯è§†åŒ–,`ComposedWriter` 类继承至 [全局å¯è®¿é—®åŸºç±» BaseGlobalAccessible](./logging.md/#全局å¯è®¿é—®åŸºç±»baseglobalaccessible)。一旦继承了全局å¯è®¿é—®åŸºç±», 用户就å¯ä»¥é€šè¿‡è°ƒç”¨ `ComposedWriter` 对象的 `get_instance` æ¥èŽ·å–全局对象。其基本用法如下 + +```python +# 创建实例 +writers=[dict(type='LocalWriter', save_dir='temp_dir', visualizer=dict(type='DetVisualizer')), dict(type='WandbWriter')] + +ComposedWriter.create_instance('composed_writer', writers=writers) +``` + +一旦创建实例åŽï¼Œå¯ä»¥åœ¨ä»£ç ä»»æ„ä½ç½®èŽ·å– `ComposedWriter` 对象 + +```python +composed_writer=ComposedWriter.get_instance('composed_writer') + +# 写模型精度值 +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` 方法获å–具体对象æ¥å®žçŽ°ç‰¹å®šéœ€æ±‚ + +```python +composed_writer=ComposedWriter.get_instance('composed_writer') + +# 绘制特å¾å›¾ï¼ŒèŽ·å– LocalWriter ä¸çš„ visualizer +visualizer=composed_writer.get_visualizer(0) +featmap_image=visualizer.draw_featmap(tensor_chw) +composed_writer.add_image('featmap', featmap_image) + +# 扩展 add 功能,例如利用 Wandb å¯¹è±¡ç»˜åˆ¶è¡¨æ ¼ +wandb=composed_writer.get_experiment(1) +val_table = wandb.Table(data=my_data, columns=column_names) +wandb.log({'my_val_table': val_table}) + +# é…ç½®ä¸å˜åœ¨å¤šä¸ª Writer,在ä¸æƒ³æ”¹åŠ¨é…置情况下åªä½¿ç”¨ LocalWriter +local_writer=composed_writer.get_writer(0) +local_writer.add_image('demo_image', image, datasample) +``` diff --git a/docs/zh_cn/tutorials/visualizer.md b/docs/zh_cn/tutorials/visualizer.md deleted file mode 100644 index 0bff95c2ebd178b178410915a5209f82119df352..0000000000000000000000000000000000000000 --- a/docs/zh_cn/tutorials/visualizer.md +++ /dev/null @@ -1,269 +0,0 @@ -# å¯è§†åŒ– (Visualization) - -## 概述 - -**(1) 总体介ç»** - -å¯è§†åŒ–å¯ä»¥ç»™æ·±åº¦å¦ä¹ 的模型è®ç»ƒå’Œæµ‹è¯•è¿‡ç¨‹æ供直观解释。在 OpenMMLab 算法库ä¸ï¼Œæˆ‘们期望å¯è§†åŒ–功能的设计能满足以下需求: - -- æ供丰富的开箱å³ç”¨å¯è§†åŒ–功能,能够满足大部分计算机视觉å¯è§†åŒ–任务 -- 高扩展性,å¯è§†åŒ–åŠŸèƒ½é€šå¸¸å¤šæ ·åŒ–ï¼Œåº”è¯¥èƒ½å¤Ÿé€šè¿‡ç®€å•æ‰©å±•å®žçŽ°å®šåˆ¶éœ€æ±‚ -- 能够在è®ç»ƒå’Œæµ‹è¯•æµç¨‹çš„ä»»æ„点ä½è¿›è¡Œå¯è§†åŒ– -- OpenMMLab å„个算法库具有统一å¯è§†åŒ–接å£ï¼Œåˆ©äºŽç”¨æˆ·ç†è§£å’Œç»´æŠ¤ - -基于上述需求,OpenMMLab 2.0 引入了绘制对象 Visualizer 和写端对象 Writer 的概念 - -- **Visualizer è´Ÿè´£å•å¼ 图片的绘制功能** - - MMEngine æ供了以 Matplotlib 库为绘制åŽç«¯çš„ `Visualizer` 类,其具备如下功能: - - - æä¾›äº†ä¸€ç³»åˆ—å’Œè§†è§‰ä»»åŠ¡æ— å…³çš„åŸºç¡€æ–¹æ³•ï¼Œä¾‹å¦‚ `draw_bboxes` å’Œ `draw_texts` ç‰ - - 上述å„个基础方法支æŒé“¾å¼è°ƒç”¨ï¼Œæ–¹ä¾¿å åŠ ç»˜åˆ¶æ˜¾ç¤º - - æ供了绘制特å¾å›¾åŠŸèƒ½ - - å„个下游算法库å¯ä»¥ç»§æ‰¿ `Visualizer` 并在 `draw` 接å£å®žçŽ°æ‰€éœ€çš„å¯è§†åŒ–功能,例如 MMDetection ä¸çš„ `DetVisualizer` 继承自 `Visualizer` 并在 `draw` 接å£å®žçŽ°å¯è§†åŒ–检测框ã€å®žä¾‹æŽ©ç å’Œè¯ä¹‰åˆ†å‰²å›¾ç‰åŠŸèƒ½ã€‚Visualizer 类的 UML 关系图如下 - - <div align="center"> - <img src="https://user-images.githubusercontent.com/17425982/154475592-7208a34b-f6cb-4171-b0be-9dbb13306862.png" > - </div> - -- **Writer 负责将å„类数æ®å†™å…¥åˆ°æŒ‡å®šåŽç«¯** - - 为了统一接å£è°ƒç”¨ï¼ŒMMEngine æ供了统一的抽象类 `BaseWriter`,和一些常用的 Writer 如 `LocalWriter` æ¥æ”¯æŒå°†æ•°æ®å†™å…¥æœ¬åœ°ï¼Œ`TensorboardWriter` æ¥æ”¯æŒå°†æ•°æ®å†™å…¥ Tensorboard,`WandbWriter` æ¥æ”¯æŒå°†æ•°æ®å†™å…¥ Wandb。用户也å¯ä»¥è‡ªå®šä¹‰ Writer æ¥å°†æ•°æ®å†™å…¥è‡ªå®šä¹‰åŽç«¯ã€‚写入的数æ®å¯ä»¥æ˜¯å›¾ç‰‡ï¼Œæ¨¡åž‹ç»“æž„å›¾ï¼Œæ ‡é‡å¦‚æ¨¡åž‹ç²¾åº¦æŒ‡æ ‡ç‰ã€‚ - - 考虑到在è®ç»ƒæˆ–者测试过程ä¸åŒæ—¶å˜åœ¨å¤šä¸ª Writer 对象,例如åŒæ—¶æƒ³è¿›è¡Œæœ¬åœ°å’Œè¿œç¨‹ç«¯å†™æ•°æ®ï¼Œä¸ºæ¤è®¾è®¡äº† `ComposedWriter` 负责管ç†æ‰€æœ‰è¿è¡Œä¸å®žä¾‹åŒ–çš„ Writer 对象,其会自动管ç†æ‰€æœ‰ Writer 对象,并é历调用所有 Writer 对象的方法。Writer 类的 UML 关系图如下 - <div align="center"> - <img src="https://user-images.githubusercontent.com/17425982/154474755-080b955b-436b-4cdb-9a49-16a9f231ce81.png" > - </div> - -**(2) Writer å’Œ Visualizer 关系** - -Writer å¯¹è±¡çš„æ ¸å¿ƒåŠŸèƒ½æ˜¯å†™å„类数æ®åˆ°æŒ‡å®šåŽç«¯ä¸ï¼Œä¾‹å¦‚写图片ã€å†™æ¨¡åž‹å›¾ã€å†™è¶…å‚å’Œå†™æ¨¡åž‹ç²¾åº¦æŒ‡æ ‡ç‰ï¼ŒåŽç«¯å¯ä»¥æŒ‡å®šä¸ºæœ¬åœ°å˜å‚¨ã€Wandb å’Œ Tensorboard ç‰ç‰ã€‚在写图片过程ä¸ï¼Œé€šå¸¸å¸Œæœ›èƒ½å¤Ÿå°†é¢„æµ‹ç»“æžœæˆ–è€…æ ‡æ³¨ç»“æžœç»˜åˆ¶åˆ°å›¾ç‰‡ä¸Šï¼Œç„¶åŽå†è¿›è¡Œå†™æ“作,为æ¤åœ¨ Writer 内部维护了 Visualizer 对象,将 Visualizer 作为 Writer 的一个属性。当需è¦åˆ©ç”¨ Visualizer 对象æ¥ç»˜åˆ¶ç»“果到图片上时候,å¯ä»¥é€šè¿‡è°ƒç”¨ Writer çš„ Visualizer 属性对象进行绘制。一个简略的演示代ç 如下 - -```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, datasample=None, step=0, **kwargs): - if self._visualize: - self._visualize.draw(image, datasample) - # 调用 Writer API 写图片到åŽç«¯ - self.wandb.log({name: self.visualizer.get_image()}, ...) - ... - else: - # 调用 Writer API 汇总并写图片到åŽç«¯ - ... - - def add_scaler(self, name, value, step): - self.wandb.log({name: value}, ...) -``` - -å¯¹äºŽéž `LocalWriter` 或者ä¸éœ€è¦è°ƒç”¨å†™å›¾ç‰‡çš„ `add_image` 接å£éœ€æ±‚场景,visualizer å‚æ•°å¯ä»¥ä¸º None。 - -æ³¨æ„ `Visualizer` 仅仅有å•å›¾ç»˜åˆ¶åŠŸèƒ½ï¼Œå¦‚果想将绘制结果ä¿å˜ï¼Œä¾‹å¦‚ä¿å˜åˆ°æœ¬åœ°ã€Wandb 或者 Tensorboard,å¯ä»¥ä½¿ç”¨ Writer 写端对象。一个推è的写法如下 - -```python -# é…置文件 -writer=dict(type='LocalWriter', save_dir='demo_dir', visualizer=dict(type='DetVisualizer')) -# 实例化和调用 -writer_obj=WRITERS.build(writer) -writer_obj.add_image('demo_image', image, datasample) -``` - -在 Runner ä¸é»˜è®¤çš„实现方å¼ä¹Ÿæ˜¯ç±»ä¼¼ä¸Šè¿°å†™æ³•ï¼Œæˆ‘们也推è用户在模型ä¸å¼€å‘自定义å¯è§†åŒ–功能的时候也采用这ç§æ–¹å¼ã€‚如果用户有必è¦ç›´æŽ¥è°ƒç”¨ visualizer ä¸æŽ¥å£è¿›è¡Œç»˜åˆ¶åŠŸèƒ½ï¼Œåˆ™å¯ä»¥é‡‡ç”¨å¦‚下写法 - -```python -# é…置文件 -writer=dict(type='LocalWriter', save_dir='demo_dir', visualizer=dict(type='DetVisualizer')) -# 实例化和调用 -writer_obj=WRITERS.build(writer) -# 以调用 draw 方法为例 -writer_obj.visualizer.draw(image, datasample) -writer_obj.add_image('demo_image', writer_obj.visualizer.get_image()) -``` - -在使用 Jupyter notebook 或者其他地方ä¸éœ€è¦ writer 的情形下,用户å¯ä»¥è‡ªå·±å®žä¾‹åŒ– visualizer。一个简å•çš„例å如下 - -```python -# 实例化 visualizer -visualizer=dict(type='DetVisualizer') -visualizer = VISUALIZERS.build(visualizer) -visualizer.draw(image, datasample) -``` - - -## 绘制对象 Visualizer - -绘制对象 Visualizer è´Ÿè´£å•å¼ 图片的å„类绘制功能,默认绘制åŽç«¯ä¸º Matplotlib。为了统一 OpenMMLab å„个算法库的å¯è§†åŒ–接å£ï¼ŒMMEngine 定义æ供了基于基础绘制功能的 `Visualizer` 类,下游库å¯ä»¥ç»§æ‰¿ `Visualizer` 并实现 `draw` 接å£å®žçŽ°è‡ªå·±çš„å¯è§†åŒ–需求,例如 MMDetection çš„ [`DetVisualizer`]()。 - -### Visualizer - -`Visualizer` æ供了基础而通用的绘制功能,主è¦æŽ¥å£å¦‚下: - -**(1) ç»˜åˆ¶æ— å…³çš„åŠŸèƒ½æ€§æŽ¥å£** - -- set_image è®¾ç½®åŽŸå§‹å›¾ç‰‡æ•°æ® -- get_image 获å–绘制åŽçš„ Numpy æ ¼å¼å›¾ç‰‡æ•°æ® -- show å¯è§†åŒ– -- register_task 注册绘制函数(其作用在 *自定义 Visualizer* å°èŠ‚æè¿°) - -**(2) 绘制相关接å£** - -- draw ç”¨æˆ·ä½¿ç”¨çš„æŠ½è±¡ç»˜åˆ¶æŽ¥å£ -- draw_featmap 绘制特å¾å›¾ -- draw_bboxes 绘制å•ä¸ªæˆ–者多个边界框 -- draw_texts 绘制å•ä¸ªæˆ–者多个文本框 -- draw_lines 绘制å•ä¸ªæˆ–者多个线段 -- draw_circles 绘制å•ä¸ªæˆ–者多个圆 -- draw_polygons 绘制å•ä¸ªæˆ–者多个多边形 -- draw_binary_masks 绘制å•ä¸ªæˆ–者多个二值掩ç - -**(1) 用例 1 - 链å¼è°ƒç”¨** - -例如用户先绘制边界框,在æ¤åŸºç¡€ä¸Šç»˜åˆ¶æ–‡æœ¬ï¼Œç»˜åˆ¶çº¿æ®µï¼Œåˆ™è°ƒç”¨è¿‡ç¨‹ä¸ºï¼š - -```python -visualizer.set_image(image) -visualizer.draw_bboxes(...).draw_texts(...).draw_lines(...) -``` - -**(2) 用例 2 - å¯è§†åŒ–特å¾å›¾** - -特å¾å›¾å¯è§†åŒ–是一个常è§çš„功能,通过调用 `draw_featmap` å¯ä»¥ç›´æŽ¥å¯è§†åŒ–特å¾å›¾ï¼Œç›®å‰è¯¥å‡½æ•°æ”¯æŒå¦‚下功能: - -- 输入 4 ç»´ BCHW æ ¼å¼çš„ tensorï¼Œé€šé“ C 是 1 或者 3 时候,展开æˆä¸€å¼ 图片显示 -- 输入 4 ç»´ BCHW æ ¼å¼çš„ tensorï¼Œé€šé“ C 大于 3 时候,则支æŒé€‰æ‹©æ¿€æ´»åº¦æœ€é«˜é€šé“,展开æˆä¸€å¼ 图片显示 -- 输入 3 ç»´ CHW æ ¼å¼çš„ tensor,则选择激活度最高的 topk,然åŽæ‹¼æŽ¥æˆä¸€å¼ 图显示 - -```python -# 如果æå‰è®¾ç½®äº†å›¾ç‰‡ï¼Œåˆ™ç‰¹å¾å›¾æˆ–者图片å åŠ æ˜¾ç¤ºï¼Œå¦åˆ™åªæ˜¾ç¤ºç‰¹å¾å›¾ -visualizer.set_image(image) -visualizer.draw_featmap(...) -visualizer.save(...) -``` - -### 自定义 Visualizer - -自定义的 Visualizer ä¸å¤§éƒ¨åˆ†æƒ…况下åªéœ€è¦å®žçŽ° `get_image` å’Œ `draw` 接å£ã€‚`draw` 是最高层的用户调用接å£ï¼Œ`draw` 接å£è´Ÿè´£æ‰€æœ‰ç»˜åˆ¶åŠŸèƒ½ï¼Œä¾‹å¦‚绘制检测框ã€æ£€æµ‹æŽ©ç mask å’Œ 检测è¯ä¹‰åˆ†å‰²å›¾ç‰ç‰ã€‚ä¾æ®ä»»åŠ¡çš„ä¸åŒï¼Œ`draw` 接å£å®žçŽ°çš„å¤æ‚度也ä¸åŒã€‚ - -ä»¥ç›®æ ‡æ£€æµ‹å¯è§†åŒ–需求为例,å¯èƒ½éœ€è¦åŒæ—¶ç»˜åˆ¶è¾¹ç•Œæ¡† 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` 装饰器。 - -```python -class DetVisualizer(Visualizer): - - def get_image(self): - ... - - def draw(self, data_sample, image=None, show_gt=True, show_pred=True): - if show_gt: - for task in self.task_dict: - task_attr = 'gt_' + task - if task_attr in data_sample: - # DataType.GT 表示当å‰ç»˜åˆ¶æ ‡æ³¨æ•°æ® - self.task_dict[task](self, data_sample[task_attr], DataType.GT) - if show_pred: - for task in self.task_dict: - task_attr = 'pred_' + task - if task_attr in data_sample: - # DataType.PRED 表示当å‰ç»˜åˆ¶é¢„测结果 - self.task_dict[task](self, data_sample[task_attr], DataType.PRED) - - @Visualizer.register_task('instances') - def draw_instance(self, instances, data_type): - ... - - @Visualizer.register_task('sem_seg') - def draw_sem_seg(self, pixel_data, data_type): - ... -``` - -注æ„:是å¦ä½¿ç”¨ `register_task` 装饰器函数ä¸æ˜¯å¿…须的,如果用户自定义 Visualizer,并且 `draw `实现éžå¸¸ç®€å•ï¼Œåˆ™æ— 需考虑 `register_task`。 - -## 写端 Writer - -Visualizer åªæ˜¯å®žçŽ°äº†å•å¼ 图片的å¯è§†åŒ–功能,但是在è®ç»ƒæˆ–者测试过程ä¸ï¼Œå¯¹ä¸€äº›å…³é”®æŒ‡æ ‡æˆ–者模型è®ç»ƒè¶…å‚的记录éžå¸¸é‡è¦ï¼Œæ¤åŠŸèƒ½é€šè¿‡å†™ç«¯ Writer 实现。 - -BaseWriter 定义了对外调用的接å£è§„范,主è¦æŽ¥å£å¦‚下: - -- add_hyperparams 写超å‚,常è§çš„è®ç»ƒè¶…å‚如åˆå§‹å¦ä¹ 率 LRã€æƒé‡è¡°å‡ç³»æ•°å’Œæ‰¹å¤§å°ç‰ç‰ -- add_image 写图片 -- add_scalar å†™æ ‡é‡ -- add_graph 写模型图 -- visualizer 绘制对象,å¯ä»¥ä¸º None -- experiment 写åŽç«¯å¯¹è±¡ï¼Œä¾‹å¦‚ Wandb 对象和 Tensorboard 对象 - -`BaseWriter` 定义了 4 ç§å¸¸è§çš„写数æ®æŽ¥å£ï¼Œè€ƒè™‘到æŸäº›å†™åŽç«¯åŠŸèƒ½éžå¸¸å¼ºå¤§ï¼Œä¾‹å¦‚ Wandbï¼Œå…¶å…·å¤‡å†™è¡¨æ ¼ï¼Œå†™è§†é¢‘ç‰ç‰åŠŸèƒ½ï¼Œé’ˆå¯¹è¿™ç±»éœ€æ±‚用户å¯ä»¥ç›´æŽ¥èŽ·å– experiment 对象,然åŽè°ƒç”¨å†™åŽç«¯å¯¹è±¡æœ¬èº«çš„ API å³å¯ã€‚ - -由于 Visualizer å’Œ Writer 对象是解耦的,用户å¯ä»¥é€šè¿‡é…置文件自由组åˆå„ç§ Visualizer å’Œ Writer,例如 `WandbWriter` 绑定 `Visualizer`,表示图片上绘制结果功能由 `Visualizer` æ供,但是最终图片是写到了 Wandb 端,一个简å•çš„例å如下所示 - -```python -# é…置文件 -writer=dict(type='LocalWriter', save_dir='demo_dir', visualizer=dict(type='DetVisualizer')) -# 实例化和调用 -writer_obj=WRITERS.build(writer) -# 写图片 -writer_obj.add_image('demo_image', image, datasample) -# 写模型精度值 -writer_obj.add_scalar('mAP', 0.9) -``` - -## 组åˆå†™ç«¯ ComposedWriter - -考虑到在è®ç»ƒæˆ–者测试过程ä¸ï¼Œå¯èƒ½éœ€è¦åŒæ—¶è°ƒç”¨å¤šä¸ª Writer,例如想åŒæ—¶å†™åˆ°æœ¬åœ°å’Œ Wandb 端,为æ¤è®¾è®¡äº†å¯¹å¤–çš„ `ComposedWriter` 类,在è®ç»ƒæˆ–è€…æµ‹è¯•è¿‡ç¨‹ä¸ `ComposedWriter` 会ä¾æ¬¡è°ƒç”¨å„个 Writer,主è¦æŽ¥å£å¦‚下: - -- add_hyperparams 写超å‚,常è§çš„è®ç»ƒè¶…å‚如åˆå§‹å¦ä¹ 率 LRã€æƒé‡è¡°å‡ç³»æ•°å’Œæ‰¹å¤§å°ç‰ç‰ -- add_image 写图片 -- add_scalar å†™æ ‡é‡ -- add_graph 写模型图 -- setup_env 设置 work_dir ç‰å¿…备的环境å˜é‡ -- get_writer 获å–æŸä¸ª writer -- `__enter__` 上下文进入函数 -- `__exit__` 上下文推出函数 - -为了让用户å¯ä»¥åœ¨ä»£ç çš„ä»»æ„ä½ç½®è¿›è¡Œæ•°æ®å¯è§†åŒ–,`ComposedWriter` 类实现 `__enter__` å’Œ ` __exit__`方法,并且在 `Runner` ä¸ä½¿ä¸Šä¸‹æ–‡ç”Ÿæ•ˆï¼Œä»Žè€Œåœ¨è¯¥ä¸Šä¸‹æ–‡ä½œç”¨åŸŸå†…,用户å¯ä»¥é€šè¿‡ `get_writers` å·¥å…·å‡½æ•°èŽ·å– `ComposedWriter` 类实例,从而调用该类的å„ç§å¯è§†åŒ–和写方法。一个简å•ç²—略的实现和用例如下 - -```python -writer=WRITERS.build(cfg.writer) - -# å‡è®¾åœ¨ epoch è®ç»ƒè¿‡ç¨‹ä¸ -with ComposedWriter(writer): - while self.epoch < self._max_epochs: - for i, flow in enumerate(workflow): - ... -``` - -```python -# é…置文件写法 -writer = dict(type='WandbWriter', init_kwargs=dict(project='demo'), - visualizer= dict(type='DetLocalVisualizer', show=False)) - -# 在上下文作用域生效的任æ„ä½ç½® -composed_writer=get_writers() -composed_writer.add_image('vis_image',image, datasample, iter=iter) -composed_writer.add_scalar('mAP', val, iter=iter) -``` - -如果å˜åœ¨å¤šä¸ª writer 对象,则é…置文件å—段 writer 为列表,如下所示 - -```python -# é…置文件写法 -writer = [dict(type='LocalWriter', save_dir='demo_dir', visualizer=dict(type='DetVisualizer')), - dict(type='WandbWriter', init_kwargs=dict(project='demo'))] - -# 在上下文作用域生效的任æ„ä½ç½® -composed_writer=get_writers() -# 内部会ä¾æ¬¡è°ƒç”¨ LocalWriter å’Œ WandbWriter çš„ add_image 方法进行写图片到本地和 Wandb 远端 -composed_writer.add_image('vis_image', image, datasample, iter=iter) -composed_writer.add_scalar('mAP', val, iter=iter) -``` - -在è®ç»ƒå’Œæµ‹è¯•è¿‡ç¨‹ä¸ï¼Œç”¨æˆ·å¯ä»¥åœ¨ä¸Šä¸‹æ–‡ç”Ÿæ•ˆçš„代ç ä»»æ„ä½ç½®é€šè¿‡è°ƒç”¨ `get_writers()` 获得 ComposedWriter 对象,然åŽé€šè¿‡è¯¥å¯¹è±¡æŽ¥å£å¯ä»¥è¿›è¡Œç»˜åˆ¶æˆ–者写æ“作,内部会ä¾æ¬¡è°ƒç”¨é…置文件ä¸å®šä¹‰çš„所有 writer 的相应接å£ã€‚