加入收藏 | 设为首页 | 会员中心 | 我要投稿 云计算网_泰州站长网 (http://www.0523zz.com/)- 视觉智能、AI应用、CDN、行业物联网、智能数字人!
当前位置: 首页 > 服务器 > 搭建环境 > Unix > 正文

8个计算机视觉深度学习中常见的Bug

发布时间:2019-12-23 08:43:23 所属栏目:Unix 来源:站长网
导读:副标题#e# 给大家总结了8个计算机视觉深度学习中的常见bug,相信大家或多或少都遇到过,希望能帮助大家避免一些问题。 人是不完美的,我们经常在软件中犯错误。有时这些错误很容易发现:你的代码根本不能工作,你的应用程序崩溃等等。但是有些bug是隐藏的,
副标题[/!--empirenews.page--]

给大家总结了8个计算机视觉深度学习中的常见bug,相信大家或多或少都遇到过,希望能帮助大家避免一些问题。

8个计算机视觉深度学习中常见的Bug

人是不完美的,我们经常在软件中犯错误。有时这些错误很容易发现:你的代码根本不能工作,你的应用程序崩溃等等。但是有些bug是隐藏的,这使得它们更加危险。

在解决深度学习问题时,由于一些不确定性,很容易出现这种类型的bug:很容易看到web应用程序路由请求是否正确,而不容易检查你的梯度下降步骤是否正确。然而,有很多错误是可以避免的。

8个计算机视觉深度学习中常见的Bug

我想分享一些我的经验,关于我在过去两年的计算机视觉工作中看到或制造的错误。我(在会议上)谈到过这个话题(https://datafest.ru/ia/),很多人在会后告诉我:“是的,我也有很多这样的bug。”我希望我的文章可以帮助你至少避免其中的一些问题。

1. 翻转图片以及关键点.

假设在关键点检测的问题上。数据看起来像一对图像和一系列的关键点元组。其中每个关键点是一对x和y坐标。

让我们对这个数据进行基础的增强:

def flip_img_and_keypoints(img: np.ndarray, kpts: Sequence[Sequence[int]]):  

 img = np.fliplr(img) 

 h, w, *_ = img.shape 

 kpts = [(y, w - x) for y, x in kpts] 

 return img, kpts 

看起来是正确的,嗯?我们把它可视化。

image = np.ones((10, 10), dtype=np.float32) 

kpts = [(0, 1), (2, 2)] 

image_flipped, kpts_flipped = flip_img_and_keypoints(image, kpts) 

img1 = image.copy() 

for y, x in kpts: 

 img1[y, x] = 0 

img2 = image_flipped.copy() 

for y, x in kpts_flipped: 

 img2[y, x] = 0 

  

_ = plt.imshow(np.hstack((img1, img2))) 

8个计算机视觉深度学习中常见的Bug

不对称,看起来很奇怪!如果我们检查极值呢?

image = np.ones((10, 10), dtype=np.float32) 

不好!这是一个典型的off-by-one错误。正确的代码是这样的:

def flip_img_and_keypoints(img: np.ndarray, kpts: Sequence[Sequence[int]]):  

 img = np.fliplr(img) 

 h, w, *_ = img.shape 

 kpts = [(y, w - x - 1) for y, x in kpts] 

 return img, kpts 

我们通过可视化发现了这个问题,但是,使用“x = 0”点进行单元测试也会有所帮助。一个有趣的事实是:有一个团队中有三个人(包括我自己)独立地犯了几乎相同的错误。

2. 继续是关键点相关的问题

即使在上面的函数被修复之后,仍然存在危险。现在更多的是语义,而不仅仅是一段代码。

假设需要用两只手掌来增强图像。看起来很安全:手是左,右翻转。

8个计算机视觉深度学习中常见的Bug


但是等等!我们对关键点的语义并不很了解。如果这个关键点的意思是这样的:

kpts = [ 

 (20, 20), # left pinky 

 (20, 200), # right pinky 

 ... 

 ] 

8个计算机视觉深度学习中常见的Bug

这意味着增强实际上改变了语义:左变成右,右变成左,但我们不交换数组中的关键点索引。它会给训练带来大量的噪音和更糟糕的度量。

我们应该吸取一个教训:

在应用增强或其他花哨的功能之前,了解并考虑数据结构和语义

保持你的实验原子性:添加一个小的变化(例如一个新的变换),检查它如何进行,如果分数提高才加进去。

3. 编写自己的损失函数

熟悉语义分割问题的人可能知道IoU指标。不幸的是,我们不能直接用SGD来优化它,所以常用的方法是用可微损失函数来近似它。

def iou_continuous_loss(y_pred, y_true): 

 eps = 1e-6 

 def _sum(x): 

 return x.sum(-1).sum(-1) 

 numerator = (_sum(y_true * y_pred) + eps) 

 denominator = (_sum(y_true ** 2) + _sum(y_pred ** 2) 

 - _sum(y_true * y_pred) + eps) 

 return (numerator / denominator).mean() 

看起来不错,我们先做个小的检查:

In [3]: ones = np.ones((1, 3, 10, 10)) 

 ...: x1 = iou_continuous_loss(ones * 0.01, ones) 

 ...: x2 = iou_continuous_loss(ones * 0.99, ones) 

In [4]: x1, x2 

Out[4]: (0.010099999897990103, 0.9998990001020204) 

在 x1中,我们计算了一些与ground truth完全不同的东西的损失,而 x2则是非常接近ground truth的东西的结果。我们预计 x1会很大,因为预测是错误的, x2应该接近于零。怎么了?

上面的函数是对metric的一个很好的近似。metric不是一种损失:它通常(包括这种情况)越高越好。当我们使用SGD来最小化损失时,我们应该使用一些相反的东西:

def iou_continuous(y_pred, y_true): 

 eps = 1e-6 

 def _sum(x): 

 return x.sum(-1).sum(-1) 

 numerator = (_sum(y_true * y_pred) + eps) 

 denominator = (_sum(y_true ** 2) + _sum(y_pred ** 2) 

 - _sum(y_true * y_pred) + eps) 

 return (numerator / denominator).mean() 

def iou_continuous_loss(y_pred, y_true): 

 return 1 - iou_continuous(y_pred, y_true) 

这些问题可以从两个方面来确定:

编写一个单元测试,检查损失的方向:形式化的期望,更接近ground truth应该输出更低的损失。

运行一个健全的检查,让你的模型在单个batch中过拟合。

4. 当我们使用Pytorch的时候

假设有一个预先训练好的模型,开始做infer。

from ceevee.base import AbstractPredictor 

class MySuperPredictor(AbstractPredictor): 

 def __init__(self, 

 weights_path: str, 

 ): 

 super().__init__() 

 self.model = self._load_model(weights_path=weights_path) 

 def process(self, x, *kw): 

 with torch.no_grad(): 

 res = self.model(x) 

 return res 

 @staticmethod 

 def _load_model(weights_path): 

 model = ModelClass() 

 weights = torch.load(weights_path, map_location='cpu') 

 model.load_state_dict(weights) 

 return model 

这个代码正确吗?也许!这确实适用于某些模型。例如,当模型没有dropout或norm层,如 torch.nn.BatchNorm2d。或者当模型需要为每个图像使用实际的norm统计量时(例如,许多基于pix2pix的架构需要它)。

但是对于大多数计算机视觉应用程序来说,代码忽略了一些重要的东西:切换到评估模式。

如果试图将动态PyTorch图转换为静态PyTorch图,,这个问题很容易识别。 torch.jit用于这种转换。

In [3]: model = nn.Sequential( 

 ...: nn.Linear(10, 10), 

 ...: nn.Dropout(.5) 

 ...: ) 

 ...: 

 ...: traced_model = torch.jit.trace(model, torch.rand(10)) 

(编辑:云计算网_泰州站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

推荐文章
    热点阅读