From ef5b5668d32d20074008e659dd1c1de3e9671ee4 Mon Sep 17 00:00:00 2001 From: wizardforcel <562826179@qq.com> Date: Sun, 21 Oct 2018 16:00:59 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BE=B9=E7=95=8C=E6=A1=86=20=3D>=20=E8=BE=B9?= =?UTF-8?q?=E6=A1=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zh/dl10.md | 10 +++++----- zh/dl11.md | 2 +- zh/dl12.md | 2 +- zh/dl14.md | 10 +++++----- zh/dl8.md | 36 +++++++++++++++++------------------ zh/dl9.md | 56 +++++++++++++++++++++++++++--------------------------- 6 files changed, 58 insertions(+), 58 deletions(-) diff --git a/zh/dl10.md b/zh/dl10.md index 04fe2b8..6db5ac1 100644 --- a/zh/dl10.md +++ b/zh/dl10.md @@ -8,9 +8,9 @@ * 许多学生正在努力学习上周的材料,所以如果你觉得很难,那很好。杰里米预先把它放在那里的是因为我们有一些东西要不断思考,并慢慢努力,所以在第14课,你将得到第二个飞跃。 * 要理解这些碎片,你需要理解卷积层输出、感受野和损失函数的维度 - 无论如何,这些都是你所有需要了解的深度学习研究的内容。 -* 一个关键的问题是我们从简单的东西开始 - 单个对象分类器,没有分类器的单个对象边界框,然后是单个对象分类器和边界框。 除了首先我们必须解决匹配问题之外,我们迁移到多个对象上实际几乎相同。 我们最终创建了比我们的基础真实边界框所需的激活更多的激活数量,因此我们将每个地面实况对象与这些激活的子集相匹配。 一旦我们完成了这个,我们对每个匹配所做的损失函数几乎与这个损失函数(即单个对象分类器和边界框的损失函数)相同。 +* 一个关键的问题是我们从简单的东西开始 - 单个对象分类器,没有分类器的单个对象边框,然后是单个对象分类器和边框。 除了首先我们必须解决匹配问题之外,我们迁移到多个对象上实际几乎相同。 我们最终创建了比我们的基础真实边框所需的激活更多的激活数量,因此我们将每个地面实况对象与这些激活的子集相匹配。 一旦我们完成了这个,我们对每个匹配所做的损失函数几乎与这个损失函数(即单个对象分类器和边框的损失函数)相同。 * 如果你感觉困难,请返回第8课,确保你了解了数据集,DataLoader,最重要的是了解损失函数。 -* 因此,一旦我们有了可以预测一个对象的类和边界框的东西,我们通过创建更多的激活来进入多个对象 [[2:40](https://youtu.be/h5Tz7gZT9Fo%3Ft%3D2m40s)] 。 之后,我们必须处理匹配问题,处理了一个匹配问题之后,我们将每个锚箱移入移出一点,再移到周围一点,所以他们试图与特定的地面实例对象进行对齐。 +* 因此,一旦我们有了可以预测一个对象的类和边框的东西,我们通过创建更多的激活来进入多个对象 [[2:40](https://youtu.be/h5Tz7gZT9Fo%3Ft%3D2m40s)] 。 之后,我们必须处理匹配问题,处理了一个匹配问题之后,我们将每个锚箱移入移出一点,再移到周围一点,所以他们试图与特定的地面实例对象进行对齐。 * 我们谈到了我们如何利用网络的卷积性质来尝试激活具有与我们所预测的基本事实对象类似的接受场。 Chloe提供了以下奇妙的图片来讨论什么是SSD_MultiHead.forward,一行一行地看: ![](../img/1_BbxbH3gWu8RHMTuXZlDasA.png) @@ -24,7 +24,7 @@ Chloe在这里所做的是她特别关注路径中每个点处张量的维数, ![](../img/1_gwvD-lSxiUH_EFq9n-ofOQ.png) * 这是你必须记住这个`pbd.set_trace()`的地方 。 我刚刚上课前进入`SSD_MultiHead.forward`并输入了`pdb.set_trace()`,然后运行了一个批处理。 之后我可以打印出所有这些的大小。我们可能有错误,这就是为什么我们有调试器并知道如何检查过程中的一些很小的问题。 -* 然后我们讨论了增加_k_ [[5:49](https://youtu.be/h5Tz7gZT9Fo%3Ft%3D5m49s)] ,这是每个卷积网格单元的锚箱数量,我们可以用不同的缩放比、宽高比进行处理,这给我们提供了大量的激活,因此预测了边界框。 +* 然后我们讨论了增加_k_ [[5:49](https://youtu.be/h5Tz7gZT9Fo%3Ft%3D5m49s)] ,这是每个卷积网格单元的锚箱数量,我们可以用不同的缩放比、宽高比进行处理,这给我们提供了大量的激活,因此预测了边框。 * 然后我们使用非最大抑制来减小数值。 * 非最大抑制是一种粗糙的、丑陋的和完全的启发式,我们甚至没有讨论代码,因为它看起来很可怕。 最近有人提出了一篇论文,试图用一个端到端的卷积网来取代那个NMS( [https://arxiv.org/abs/1705.02950](https://arxiv.org/abs/1705.02950) )。 @@ -71,7 +71,7 @@ Chloe在这里所做的是她特别关注路径中每个点处张量的维数, * 今天,我们将看看同样的想法,看看它如何适用于NLP。 * 在下一课中,我们将进一步说明NLP和计算机视觉是否可以让你实现同样的基本想法,我们如何将两者结合起来。 实际上,我们将学习一个可以学习从图像中找到单词结构,从单词结构中找到图像或从图像中找到图像的模型。 如果你想进一步做一些事情,比如从一个图像到一个句子(即图像字幕),或者从一个句子到一个我们开始做的图像,一个短语到图像,那上面内容将构成基础。 * 从那里之后,我们必须更深入地进入计算机视觉,思考我们可以用预训练的网络和定制头的想法做些什么。 因此,我们将研究各种图像增强,例如增加低分辨率照片的分辨率以猜测缺少的内容或在照片上添加艺术滤镜,或将马的照片更改为斑马照片等。 -* 最后,这将再次带我们回到边界框。为了达到这个目的,我们首先要学习分割,这不仅仅是找出边界框的位置,而是要弄清楚图像中每个像素的一部分 - 所以这个像素是一个人的一部分,这个像素是一辆车的一部分。 然后我们将用这个想法,特别是一个名为UNet的想法,结果证明了这个UNet的想法可以应用于边界框 - 它被称为特征金字塔。 我们将使用它来获得非常好的带有边界框的结果。这是我们从这里开始的路。 这一切都将建立在彼此的基础上,但将我们带入许多不同的领域。 +* 最后,这将再次带我们回到边框。为了达到这个目的,我们首先要学习分割,这不仅仅是找出边框的位置,而是要弄清楚图像中每个像素的一部分 - 所以这个像素是一个人的一部分,这个像素是一辆车的一部分。 然后我们将用这个想法,特别是一个名为UNet的想法,结果证明了这个UNet的想法可以应用于边框 - 它被称为特征金字塔。 我们将使用它来获得非常好的带有边框的结果。这是我们从这里开始的路。 这一切都将建立在彼此的基础上,但将我们带入许多不同的领域。 #### torchtext to fastai.text [[18:56](https://youtu.be/h5Tz7gZT9Fo%3Ft%3D18m56s)] : @@ -694,7 +694,7 @@ epoch trn_loss val_loss accuracy [3.9084756, 0.3115861900150776] ``` -**问题** :你在一周 [[1:25:24]( data-href=)] 的纸质阅读与敲代码的比例是多少? 天哪,你觉得怎么样,雷切尔? 你看我。 我的意思是,结果显示更多时间是在敲代码,对吗? “更多时间在敲代码。 我觉得每周也真的不一样“(瑞秋)。 有了这些边界框的东西,有所有这些文件,没有通过它们的地图,所以我甚至不知道首先阅读哪一个,然后我读了引用,并且不理解它们中的任何一个。 所以在我甚至不知道如何开始敲代码之前,还有几周的阅读文章。 这是不寻常的。 每当我开始阅读一篇论文时,我总是相信,我不够聪明去理解它,无论论文是什么。 不知怎的,我最终做到了。 但我试着花费尽可能多的时间来敲代码。 +**问题** :你在一周 [[1:25:24]( data-href=)] 的纸质阅读与敲代码的比例是多少? 天哪,你觉得怎么样,雷切尔? 你看我。 我的意思是,结果显示更多时间是在敲代码,对吗? “更多时间在敲代码。 我觉得每周也真的不一样“(瑞秋)。 有了这些边框的东西,有所有这些文件,没有通过它们的地图,所以我甚至不知道首先阅读哪一个,然后我读了引用,并且不理解它们中的任何一个。 所以在我甚至不知道如何开始敲代码之前,还有几周的阅读文章。 这是不寻常的。 每当我开始阅读一篇论文时,我总是相信,我不够聪明去理解它,无论论文是什么。 不知怎的,我最终做到了。 但我试着花费尽可能多的时间来敲代码。 几乎总是在我读完一篇论文 [[1:26:34]( data-href=)] 之后,即使在我读到这个问题之后,我正试图解决这个问题,我会停在那里尝试实现一些我认为可能会解决这个问题的想法。 然后我会回去阅读论文,我读到了关于这些问题的一些内容,我将如何解决这些问题,我会像“哦,这是一个好主意”,然后我会尝试实现这些。 这就是为什么,例如,实际上我没有实现SSD。 我的定制头与他们的头不一样。 这是因为我有点读了它的要点,然后我尽力创造出最好的东西,然后回到论文中,试着看看为什么。 所以当我到达焦点丢失纸时,雷切尔会告诉你,我为什么不能找到小物体而疯狂? 怎么总是预测背景? 我读了焦点丢失文件,我就像“这就是为什么!!”当你深刻理解他们试图解决的问题时,会好得多。 我确实找到绝大多数时间,当我读到解决问题的那篇文章的时候,我就像“是的,但是我想出了这三个想法,他们没有尝试。”然后你突然意识到你有新的想法。 或者,如果你只是盲目地实施论文,那么你往往没有更好的方法去做这些见解。 diff --git a/zh/dl11.md b/zh/dl11.md index 09d3fdc..7581704 100644 --- a/zh/dl11.md +++ b/zh/dl11.md @@ -58,7 +58,7 @@ PATH = Path('data/translate') TMP_PATH = PATH/'tmp' TMP_PATH.mkdir(exist_ok= **True** ) fname='giga-fren.release2.fixed' en_fname = PATH/f' **{fname}** .en' fr_fname = PATH/f' **{fname}** .fr' ``` -对于边界框,所有有趣的东西都在损失函数中,但对于神经翻译,所有有趣的东西都将在他的体系结构中 [[13:01](https://youtu.be/tY0n9OT5_nA%3Ft%3D13m1s)] 。 让我们快速完成这一切,杰里米希望你特别考虑的事情之一就是我们正在做的任务以及我们如何在语言建模与神经翻译之间做到这一点的关系或相似之处。 +对于边框,所有有趣的东西都在损失函数中,但对于神经翻译,所有有趣的东西都将在他的体系结构中 [[13:01](https://youtu.be/tY0n9OT5_nA%3Ft%3D13m1s)] 。 让我们快速完成这一切,杰里米希望你特别考虑的事情之一就是我们正在做的任务以及我们如何在语言建模与神经翻译之间做到这一点的关系或相似之处。 ![](../img/1_458KhA7uSET5eH3fe4EhDw.png) diff --git a/zh/dl12.md b/zh/dl12.md index ae678da..ba48937 100644 --- a/zh/dl12.md +++ b/zh/dl12.md @@ -612,7 +612,7 @@ GAN的基本思想是它是一个生成模型 [[51:23](https://youtu.be/ondivPiw * 识别斑马的GAN损失 * 我们两台发电机的循环一致性损失 -我们在这里有一个lambda,希望我们已经习惯了这个想法,现在就是当你有两种不同的损失时,你可以将参数放在那里,你可以将它们相乘,所以它们的大小相同 [[1:59: 23](https://youtu.be/ondivPiwQho%3Ft%3D1h59m23s)] 。当我们进行本地化时,与分类器丢失相比,我们的边界框丢失做了类似的事情。 +我们在这里有一个lambda,希望我们已经习惯了这个想法,现在就是当你有两种不同的损失时,你可以将参数放在那里,你可以将它们相乘,所以它们的大小相同 [[1:59: 23](https://youtu.be/ondivPiwQho%3Ft%3D1h59m23s)] 。当我们进行本地化时,与分类器丢失相比,我们的边框丢失做了类似的事情。 然后,对于这种损失函数,我们将尝试最大化鉴别器区分的能力,同时最小化发生器的能力。因此,发电机和鉴别器将相互对立。当你在论文中看到这个_最小的_东西时,它基本上意味着这个想法,在你的训练循环中,有一件事是试图让事情变得更好,另一件事是试图让事情变得更糟,并且有很多方法可以做到但是最常见的是,你会在两者之间交替。你会经常看到这在数学论文中被称为min-max。因此,当你看到min-max时,你应该立即考虑**对抗训练**。 diff --git a/zh/dl14.md b/zh/dl14.md index e0431ee..c667611 100644 --- a/zh/dl14.md +++ b/zh/dl14.md @@ -124,7 +124,7 @@ Alena Harley做了一些非常有趣的事情,她试图找出如果你只用 img_fn = PATH/'train'/'n01558993'/'n01558993_9684.JPEG' ``` -我们需要做的下一件事是按照惯例创建我们的转换 [[18:13](https://youtu.be/nG3tT31nPmQ%3Ft%3D18m13s)] 。 我们将使用`tfm_y`参数,就像我们对边界框所做的那样,而不是使用`TfmType.COORD`我们将使用`TfmType.PIXEL` 。 这告诉我们的转换框架你的_y_值是包含普通像素的图像,所以你对_x_做的任何事情,你也需要对_y_做同样的事情。 你需要确保你使用的任何数据增强转换都具有相同的参数。 +我们需要做的下一件事是按照惯例创建我们的转换 [[18:13](https://youtu.be/nG3tT31nPmQ%3Ft%3D18m13s)] 。 我们将使用`tfm_y`参数,就像我们对边框所做的那样,而不是使用`TfmType.COORD`我们将使用`TfmType.PIXEL` 。 这告诉我们的转换框架你的_y_值是包含普通像素的图像,所以你对_x_做的任何事情,你也需要对_y_做同样的事情。 你需要确保你使用的任何数据增强转换都具有相同的参数。 ``` tfms = tfms_from_model(arch, sz_lr, tfm_y=TfmType.PIXEL, aug_tfms=aug_tfms, sz_y=sz_hr) datasets = ImageData.get_ds(MatchedFilesDataset, (trn_x,trn_y), (val_x,val_y), tfms, path=PATH_TRN) md = ImageData(PATH, datasets, bs, num_workers=16, classes= **None** ) @@ -1022,7 +1022,7 @@ _,axes = plt.subplots(1,2,figsize =(14,7))show_img(x [ None ],0 最后,我们来谈谈细分。这来自着名的CamVid数据集,它是学术分割数据集的典型示例。基本上你可以看到我们做的是我们从图片开始(它们实际上是这个数据集中的视频帧),我们有一些标签,它们实际上不是颜色 - 每个标签都有一个ID,ID被映射到颜色。所以红色可能是1,紫色可能是2,浅粉红色可能是3,所以所有的建筑都是一类,所有的汽车都是另一类,所有人都是另一类,所有的道路都是另一类,等等。所以我们在这里实际做的是每个像素的多类分类。你可以看到,有时候这种多类分类真的很棘手 - 就像这些分支一样。虽然,有时标签真的不那么好。你可以看到,这非常粗糙。这就是我们要做的事情。 -我们将进行分割,因此它很像边界框。但是,我们实际上要用它的类来标记每个像素,而不仅仅是在每个东西周围找到一个方框。实际上,它实际上要容易得多,因为它非常适合我们的CNN样式,我们可以创建任何CNN,其中输出是N×M网格,包含从0到C的整数,其中有C类。然后我们可以使用softmax激活的交叉熵损失,我们就完成了。我实际上可以在那里停止课程,你可以使用你在第1课和第2课中学到的完全相同的方法,你会得到一个非常好的结果。所以首先要说的是,这实际上并不是一件非常艰难的事情。但我们会尽力做到这一点。 +我们将进行分割,因此它很像边框。但是,我们实际上要用它的类来标记每个像素,而不仅仅是在每个东西周围找到一个方框。实际上,它实际上要容易得多,因为它非常适合我们的CNN样式,我们可以创建任何CNN,其中输出是N×M网格,包含从0到C的整数,其中有C类。然后我们可以使用softmax激活的交叉熵损失,我们就完成了。我实际上可以在那里停止课程,你可以使用你在第1课和第2课中学到的完全相同的方法,你会得到一个非常好的结果。所以首先要说的是,这实际上并不是一件非常艰难的事情。但我们会尽力做到这一点。 #### 这样简单 [[1:31:26](https://youtu.be/nG3tT31nPmQ%3Ft%3D1h31m26s)] @@ -1842,7 +1842,7 @@ TRAIN_DN = 'train' MASKS_DN = 'train_masks_png' sz = 128 bs = 64 nw = 16 让我们试试U-Net吧。 我称之为U-net(ish),因为按照惯例,我正在创建我自己的有点hacky版本 - 尝试保持与你习惯的类似的东西,并做我觉得有意义的事情。 所以你应该有足够的机会通过查看确切的网格大小来至少使这个更真实的U-net,并看看这里(左上方的转换)大小是如何下降的。 所以他们显然没有添加任何填充,然后有一些裁剪正在进行 - 有一些差异。 但有一件事是因为我想利用转移学习 - 这意味着我不能完全使用U-Net。 -所以这是另一个重要的机会,如果你创建U-Net下行路径然后在末尾添加分类器然后在ImageNet上训练它。 你现在已经拥有了ImageNet训练分类器,专门设计为U-Net的良好骨干。 那么你现在应该能够回来并且非常接近赢得这场旧比赛(实际上并不是那么久 - 这是最近的比赛)。 因为之前没有预先训练好的网络。 但是如果你想想YOLO v3做了什么,基本上就是这样。 他们创建了一个DarkNet,他们在ImageNet上预先训练了它,然后他们用它作为边界框的基础。 同样,这种预先训练的想法不仅仅是为了分类而是为其他事物而设计 - 这只是没有人做过的事情。 但正如我们所展示的那样,你现在可以在三小时内以25美元的价格训练ImageNet。 如果社区中的人有兴趣这样做,希望我能得到你可以帮助你的学分,所以如果你这样做,设置它的工作并给我一个脚本,我可以为你运行它。 但就目前而言,我们还没有。 所以我们将使用ResNet。 +所以这是另一个重要的机会,如果你创建U-Net下行路径然后在末尾添加分类器然后在ImageNet上训练它。 你现在已经拥有了ImageNet训练分类器,专门设计为U-Net的良好骨干。 那么你现在应该能够回来并且非常接近赢得这场旧比赛(实际上并不是那么久 - 这是最近的比赛)。 因为之前没有预先训练好的网络。 但是如果你想想YOLO v3做了什么,基本上就是这样。 他们创建了一个DarkNet,他们在ImageNet上预先训练了它,然后他们用它作为边框的基础。 同样,这种预先训练的想法不仅仅是为了分类而是为其他事物而设计 - 这只是没有人做过的事情。 但正如我们所展示的那样,你现在可以在三小时内以25美元的价格训练ImageNet。 如果社区中的人有兴趣这样做,希望我能得到你可以帮助你的学分,所以如果你这样做,设置它的工作并给我一个脚本,我可以为你运行它。 但就目前而言,我们还没有。 所以我们将使用ResNet。 ``` **class** **SaveFeatures** (): features= **None** **def** __init__(self, m): self.hook = m.register_forward_hook(self.hook_fn) **def** hook_fn(self, module, input, output): self.features = output **def** remove(self): self.hook.remove() @@ -2218,9 +2218,9 @@ TRAIN_DN = 'train' MASKS_DN = 'train_masks_png' sz = 128 bs = 64 nw = 16 ![](../img/1_1eNTc9dNtmuxTryHf1XGpA.png) -### 回到边界框 [[1:58:15](https://youtu.be/nG3tT31nPmQ%3Ft%3D1h58m15s)] +### 回到边框 [[1:58:15](https://youtu.be/nG3tT31nPmQ%3Ft%3D1h58m15s)] -好的,就是这样。 我想提到的最后一件事现在是回到边界框,因为你可能还记得,我说我们的边界框模型在小物体上仍然表现不佳。 所以希望你能猜到我要去哪里,这就是对于边界框模型,记住我们如何在不同的网格单元中输出模型的输出。 而那些早期的网格尺寸不是很好的。 我们该如何解决? U-Net吧! 让我们有一个交叉连接的向上路径。 那么我们就打算做一个U-Net,然后将它们输出来。 因为现在那些更精细的网格单元具有该路径的所有信息,该路径,该路径以及用于杠杆的路径。 当然,这是深度学习,这意味着你不能写一篇论文,说我们只是使用U-Net作为边界框。 你必须发明一个新词,所以这被称为特征金字塔网络或FPN。 这在RetinaNet论文中使用,它是在早期专门关于FPN的论文中创建的。 如果内存服务正常,他们会简单地引用U-Net论文,但它们听起来有点让人觉得有些人可能会觉得有些微不足道。 但实际上,FPN是U-Nets。 +好的,就是这样。 我想提到的最后一件事现在是回到边框,因为你可能还记得,我说我们的边框模型在小物体上仍然表现不佳。 所以希望你能猜到我要去哪里,这就是对于边框模型,记住我们如何在不同的网格单元中输出模型的输出。 而那些早期的网格尺寸不是很好的。 我们该如何解决? U-Net吧! 让我们有一个交叉连接的向上路径。 那么我们就打算做一个U-Net,然后将它们输出来。 因为现在那些更精细的网格单元具有该路径的所有信息,该路径,该路径以及用于杠杆的路径。 当然,这是深度学习,这意味着你不能写一篇论文,说我们只是使用U-Net作为边框。 你必须发明一个新词,所以这被称为特征金字塔网络或FPN。 这在RetinaNet论文中使用,它是在早期专门关于FPN的论文中创建的。 如果内存服务正常,他们会简单地引用U-Net论文,但它们听起来有点让人觉得有些人可能会觉得有些微不足道。 但实际上,FPN是U-Nets。 我没有向你展示它的实现,但这将是一个有趣的事情,也许对我们中的一些人来说,我知道有些学生一直试图让它在论坛上运作良好。 所以是的,尝试有趣的事情。 因此,我认为在本课程以及我提到的其他内容之后要考虑的一些事情是使用FPN,也可能尝试使用Kerem的DynamicUnet。 它们都是有趣的东西。 diff --git a/zh/dl8.md b/zh/dl8.md index 15c1d0b..10e817d 100644 --- a/zh/dl8.md +++ b/zh/dl8.md @@ -126,11 +126,11 @@ Jeremy喜欢建立模型的方式: 这不是闻所未闻的 - 我们在第1部分的行星卫星数据中做到了这一点。 -**2.围绕我们分类的边界框。** +**2.围绕我们分类的边框。** -边界框有一个非常具体的定义,它是一个矩形,矩形的对象完全适合它,但它不比它必须大。 +边框有一个非常具体的定义,它是一个矩形,矩形的对象完全适合它,但它不比它必须大。 -我们的工作是获取以这种方式标记的数据和未标记的数据,以生成对象的类和每个对象的边界框。 需要注意的一点是,标记此类数据通常更为昂贵 [[37:09](https://youtu.be/Z0ssNAbe81M%3Ft%3D37m09s)] 。 对于对象检测数据集,给注释器一个对象类列表,并要求它们查找图片中任何类型的每一个以及它们的位置。 在这种情况下,为什么没有标记树或跳跃? 这是因为对于这个特定的数据集,它们不是要求注释者找到的类之一,因此不是这个特定问题的一部分。 +我们的工作是获取以这种方式标记的数据和未标记的数据,以生成对象的类和每个对象的边框。 需要注意的一点是,标记此类数据通常更为昂贵 [[37:09](https://youtu.be/Z0ssNAbe81M%3Ft%3D37m09s)] 。 对于对象检测数据集,给注释器一个对象类列表,并要求它们查找图片中任何类型的每一个以及它们的位置。 在这种情况下,为什么没有标记树或跳跃? 这是因为对于这个特定的数据集,它们不是要求注释者找到的类之一,因此不是这个特定问题的一部分。 #### 阶段 [[38:33](https://youtu.be/Z0ssNAbe81M%3Ft%3D38m33s)] : @@ -186,7 +186,7 @@ Jeremy喜欢建立模型的方式: #### 加载注释 -除了图像之外,还有_注释_ - 显示每个对象所在位置的_边界框_ 。 这些是手工贴上的。 原始版本采用XML [[47:59](https://youtu.be/Z0ssNAbe81M%3Ft%3D47m59s)] ,现在有点[难以使用](https://youtu.be/Z0ssNAbe81M%3Ft%3D47m59s) ,因此我们使用了最新的JSON版本,你可以从此[链接](https://storage.googleapis.com/coco-dataset/external/PASCAL_VOC.zip)下载。 +除了图像之外,还有_注释_ - 显示每个对象所在位置的_边框_ 。 这些是手工贴上的。 原始版本采用XML [[47:59](https://youtu.be/Z0ssNAbe81M%3Ft%3D47m59s)] ,现在有点[难以使用](https://youtu.be/Z0ssNAbe81M%3Ft%3D47m59s) ,因此我们使用了最新的JSON版本,你可以从此[链接](https://storage.googleapis.com/coco-dataset/external/PASCAL_VOC.zip)下载。 你可以在此处看到`pathlib`如何包含打开文件的功能(以及许多其他功能)。 @@ -198,7 +198,7 @@ Jeremy喜欢建立模型的方式: _dict_keys(['images', 'type', 'annotations', 'categories'])_ ``` -这里`/`不是除以它是路径斜线 [[45:55](https://youtu.be/Z0ssNAbe81M%3Ft%3D45m55s)] 。 `PATH/`让你的孩子走在那条路上。 `PATH/'pascal_train2007.json'`返回一个具有`open`方法的`pathlib`对象。 此JSON文件不包含图像,而是包含边界框和对象的类。 +这里`/`不是除以它是路径斜线 [[45:55](https://youtu.be/Z0ssNAbe81M%3Ft%3D45m55s)] 。 `PATH/`让你的孩子走在那条路上。 `PATH/'pascal_train2007.json'`返回一个具有`open`方法的`pathlib`对象。 此JSON文件不包含图像,而是包含边框和对象的类。 ``` IMAGES,ANNOTATIONS,CATEGORIES = ['images', 'annotations', 'categories'] @@ -289,7 +289,7 @@ Jeremy喜欢建立模型的方式: 只要你想拥有新密钥的默认字典条目 [[55:05](https://youtu.be/Z0ssNAbe81M%3Ft%3D55m05s)] , [defaultdict就很有用](https://youtu.be/Z0ssNAbe81M%3Ft%3D55m05s) 。 如果你尝试访问不存在的键,它会神奇地使其自身存在,并且它将自身设置为等于你指定的函数的返回值(在本例中为`lambda:[]` )。 -在这里,我们创建一个从图像ID到注释列表的dict(边界框和类ID的元组)。 +在这里,我们创建一个从图像ID到注释列表的dict(边框和类ID的元组)。 我们将VOC的高度/宽度转换为左上/右下,并将x / y坐标切换为与numpy一致。 如果给定的数据集是蹩脚的格式,请花一些时间使事情保持一致,并按照你希望的方式制作它们 [[1:01:24](https://youtu.be/Z0ssNAbe81M%3Ft%3D1h1m24s)] @@ -305,7 +305,7 @@ Jeremy喜欢建立模型的方式: **例1** -* `[ 96, 155, 269, 350]` [96,155,269,350](https://youtu.be/Z0ssNAbe81M%3Ft%3D59m53s)] :一个边界框 [[59:53](https://youtu.be/Z0ssNAbe81M%3Ft%3D59m53s)] 。 如上所述,当我们创建边界框时,我们做了几件事。 首先是我们切换x和y坐标。 其原因在于计算机视觉领域,当你说“我的屏幕是640×480”时,它是高度的宽度。 或者,数学世界,当你说“我的数组是640乘480”时,它是逐列的。 所以枕头图像库倾向于按宽度或逐行逐行进行处理,而numpy则是相反的方式。 第二个是我们要通过描述左上角xy坐标和右下角xy坐标来做事情 - 而不是x,y,高度,宽度。 +* `[ 96, 155, 269, 350]` [96,155,269,350](https://youtu.be/Z0ssNAbe81M%3Ft%3D59m53s)] :一个边框 [[59:53](https://youtu.be/Z0ssNAbe81M%3Ft%3D59m53s)] 。 如上所述,当我们创建边框时,我们做了几件事。 首先是我们切换x和y坐标。 其原因在于计算机视觉领域,当你说“我的屏幕是640×480”时,它是高度的宽度。 或者,数学世界,当你说“我的数组是640乘480”时,它是逐列的。 所以枕头图像库倾向于按宽度或逐行逐行进行处理,而numpy则是相反的方式。 第二个是我们要通过描述左上角xy坐标和右下角xy坐标来做事情 - 而不是x,y,高度,宽度。 * `7` :班级标签/类别 ``` @@ -350,7 +350,7 @@ Jeremy喜欢建立模型的方式: _('person', 'horse')_ ``` -有些lib采用VOC格式的边界框,所以这让我们在需要时转换回来 [[1:02:23](https://youtu.be/Z0ssNAbe81M%3Ft%3D1h2m23s)] : +有些lib采用VOC格式的边框,所以这让我们在需要时转换回来 [[1:02:23](https://youtu.be/Z0ssNAbe81M%3Ft%3D1h2m23s)] : ``` def bb_hw(a): return np.array([a[1],a[0],a[3]-a[1],a[2]-a[0]]) @@ -459,7 +459,7 @@ Matplotlib之所以如此命名是因为它最初是Matlab绘图库的克隆。 不要试图一下子解决所有问题,而是让我们不断进步。 我们知道如何找到每个图像中最大的对象并对其进行分类,所以让我们从那里开始。 Jeremy每天参加Kaggle比赛的时间是半小时 [[1:24:00](https://youtu.be/Z0ssNAbe81M%3Ft%3D1h24m)] 。 在那个半小时结束时,提交一些东西并尝试使它比昨天好一点。 -我们需要做的第一件事是遍历图像中的每个边界框并获得最大的边界框。 _lambda函数_只是一种定义内联匿名函数的方法。 在这里,我们用它来描述如何为每个图像排序注释 - 通过限制框大小(降序)。 +我们需要做的第一件事是遍历图像中的每个边框并获得最大的边框。 _lambda函数_只是一种定义内联匿名函数的方法。 在这里,我们用它来描述如何为每个图像排序注释 - 通过限制框大小(降序)。 我们从右下角减去左上角并乘以( `np.product` )值得到一个区域`lambda x: np.product(x[0][-2:]-x[0][:2])` 。 @@ -473,7 +473,7 @@ Matplotlib之所以如此命名是因为它最初是Matlab绘图库的克隆。 trn_lrg_anno = {a: get_lrg(b) for a,b in trn_anno.items()} ``` -现在我们有一个从图像id到单个边界框的字典 - 这个图像的最大值。 +现在我们有一个从图像id到单个边框的字典 - 这个图像的最大值。 ``` b,c = trn_lrg_anno[23] b = bb_hw(b) ax = show_img(open_image(IMG_PATH/trn_fns[23]), figsize=(5,10)) draw_rect(ax, b) draw_text(ax, b[:2], cats[c], sz=16) @@ -507,7 +507,7 @@ Matplotlib之所以如此命名是因为它最初是Matlab绘图库的克隆。 有一点不同的是`crop_type` 。 在fast.ai中创建224 x 224图像的默认策略是首先调整它的大小,使最小边为224.然后在训练期间采用随机平方裁剪。 在验证期间,除非我们使用数据增强,否则我们采用中心作物。 -对于边界框,我们不希望这样做,因为与图像网不同,我们关心的东西几乎在中间并且非常大,对象检测中的很多东西都很小并且接近边缘。 通过将`crop_type`设置为`CropType.NO` ,它将不会裁剪,因此,为了使其成为正方形,它会使它 [[1:32:09](https://youtu.be/Z0ssNAbe81M%3Ft%3D1h32m9s)] 。 一般来说,如果你裁剪而不是挤压,许多计算机视觉模型的效果会好一点,但是如果你压扁它们仍然可以很好地工作。 在这种情况下,我们绝对不想裁剪,所以这完全没问题。 +对于边框,我们不希望这样做,因为与图像网不同,我们关心的东西几乎在中间并且非常大,对象检测中的很多东西都很小并且接近边缘。 通过将`crop_type`设置为`CropType.NO` ,它将不会裁剪,因此,为了使其成为正方形,它会使它 [[1:32:09](https://youtu.be/Z0ssNAbe81M%3Ft%3D1h32m9s)] 。 一般来说,如果你裁剪而不是挤压,许多计算机视觉模型的效果会好一点,但是如果你压扁它们仍然可以很好地工作。 在这种情况下,我们绝对不想裁剪,所以这完全没问题。 ``` x,y=next(iter(md.val_dl)) show_img(md.val_ds.denorm(to_np(x))[0]); @@ -626,11 +626,11 @@ Matplotlib之所以如此命名是因为它最初是Matlab绘图库的克隆。 ![](../img/1_aztHN3af_MxEhHS71_SUDQ.png) -#### 创建边界框 [[1:52:51](https://youtu.be/Z0ssNAbe81M%3Ft%3D1h52m51s)] +#### 创建边框 [[1:52:51](https://youtu.be/Z0ssNAbe81M%3Ft%3D1h52m51s)] -在最大的对象周围创建一个边界框可能看起来像你之前没有做过的事情,但实际上它完全是你以前做过的事情。 我们可以创建回归而不是分类神经网络。 分类神经网络是具有sigmoid或softmax输出的网络,我们使用交叉熵,二进制交叉熵或负对数似然丢失函数。 这基本上是什么使它成为分类器。 如果我们最后没有softmax或sigmoid并且我们使用均方误差作为损失函数,它现在是一个回归模型,它预测连续数而不是类别。 我们也知道我们可以像行星竞赛那样有多个输出(多重分类)。 如果我们结合这两个想法并进行多列回归怎么办? +在最大的对象周围创建一个边框可能看起来像你之前没有做过的事情,但实际上它完全是你以前做过的事情。 我们可以创建回归而不是分类神经网络。 分类神经网络是具有sigmoid或softmax输出的网络,我们使用交叉熵,二进制交叉熵或负对数似然丢失函数。 这基本上是什么使它成为分类器。 如果我们最后没有softmax或sigmoid并且我们使用均方误差作为损失函数,它现在是一个回归模型,它预测连续数而不是类别。 我们也知道我们可以像行星竞赛那样有多个输出(多重分类)。 如果我们结合这两个想法并进行多列回归怎么办? -这就是你在考虑差异化编程的地方。 它不像“我如何创建边界框模型?”但它更像是: +这就是你在考虑差异化编程的地方。 它不像“我如何创建边框模型?”但它更像是: * 我们需要四个数字,因此,我们需要一个具有4个激活的神经网络 * 对于损失函数,什么是函数,当它较低时意味着四个数字更好? 均方损失函数! @@ -639,7 +639,7 @@ Matplotlib之所以如此命名是因为它最初是Matlab绘图库的克隆。 #### 仅限Bbox [[1:55:27](https://youtu.be/Z0ssNAbe81M%3Ft%3D1h55m27s)] -现在我们将尝试找到最大对象的边界框。 这只是一个带有4个输出的回归。 因此,我们可以使用具有多个“标签”的CSV。 如果你记得第1部分要进行多标签分类,则多个标签必须以空格分隔,并且文件名以逗号分隔。 +现在我们将尝试找到最大对象的边框。 这只是一个带有4个输出的回归。 因此,我们可以使用具有多个“标签”的CSV。 如果你记得第1部分要进行多标签分类,则多个标签必须以空格分隔,并且文件名以逗号分隔。 ``` BB_CSV = PATH/'tmp/bb.csv' bb = np.array([trn_lrg_anno[o][0] for o in trn_ids]) bbs = [' '.join(str(p) for p in o) for o in bb] @@ -673,7 +673,7 @@ Matplotlib之所以如此命名是因为它最初是Matlab绘图库的克隆。 tfms = tfms_from_model(f_model, sz, crop_type=CropType.NO, **tfm_y=TfmType.COORD** ) md = ImageClassifierData.from_csv(PATH, JPEGS, BB_CSV, tfms=tfms, **continuous=True** ) ``` -下周我们将看看`TfmType.COORD` ,但是现在,我们才意识到当我们进行缩放和数据增强时,需要在边界框中进行,而不仅仅是图像。 +下周我们将看看`TfmType.COORD` ,但是现在,我们才意识到当我们进行缩放和数据增强时,需要在边框中进行,而不仅仅是图像。 ``` x,y=next(iter(md.val_dl)) @@ -697,7 +697,7 @@ Matplotlib之所以如此命名是因为它最初是Matlab绘图库的克隆。 fastai允许你使用`custom_head`在`custom_head`上添加自己的模块,而不是默认添加的自适应池和完全连接的网络。 在这种情况下,我们不想进行任何池化,因为我们需要知道每个网格单元的激活。 -最后一层有4次激活,每个边界框坐标一次。 我们的目标是连续的,而不是分类的,因此使用的MSE损失函数不对模块输出执行任何sigmoid或softmax。 +最后一层有4次激活,每个边框坐标一次。 我们的目标是连续的,而不是分类的,因此使用的MSE损失函数不对模块输出执行任何sigmoid或softmax。 ``` head_reg4 = nn.Sequential(Flatten(), nn.Linear(25088,4)) learn = ConvLearner.pretrained(f_model, md, **custom_head** =head_reg4) learn.opt_fn = optim.Adam learn.crit = nn.L1Loss() @@ -764,7 +764,7 @@ fastai允许你使用`custom_head`在`custom_head`上添加自己的模块,而 ![](../img/1_xM98QR8U9kz3MJZDHfz7BA.png) -我们将在下周对此进行更多修改。 在本课之前,如果你被问到“你知道如何创建一个边界框模型吗?”,你可能会说“不,没有人教过我”。 但问题实际上是: +我们将在下周对此进行更多修改。 在本课之前,如果你被问到“你知道如何创建一个边框模型吗?”,你可能会说“不,没有人教过我”。 但问题实际上是: * 你能创建一个有4个连续输出的模型吗? 是。 * 如果这4个输出接近4个其他数字,你能创建一个更低的损失函数吗? 是 diff --git a/zh/dl9.md b/zh/dl9.md index a390913..7b50f02 100644 --- a/zh/dl9.md +++ b/zh/dl9.md @@ -14,8 +14,8 @@ * 如何跳过fastai源 * matplotlib OO API * Lambda函数 -* 边界框坐标 -* 定制头; 边界框回归 +* 边框坐标 +* 定制头; 边框回归 ![](../img/1_2nxK3zuKRnDCu_3qVhSMnw.png) @@ -28,12 +28,12 @@ ![](../img/1_E3Z5vKnp6ZkfuLR83979RA.png) -### 数据增强和边界框 [[2:58](https://youtu.be/0frKXR-2PBY%3Ft%3D2m58s)] +### 数据增强和边框 [[2:58](https://youtu.be/0frKXR-2PBY%3Ft%3D2m58s)] [笔记本](https://github.com/fastai/fastai/blob/master/courses/dl2/pascal.ipynb) **快餐的尴尬粗糙边缘:** -_分类器_是具有因变量的任何分类或二项式。 与_回归_相反,任何具有因变量的东西都是连续的。 命名有点令人困惑,但将来会被整理出来。 这里, `continuous`是`True`因为我们的因变量是边界框的坐标 - 因此这实际上是一个回归数据。 +_分类器_是具有因变量的任何分类或二项式。 与_回归_相反,任何具有因变量的东西都是连续的。 命名有点令人困惑,但将来会被整理出来。 这里, `continuous`是`True`因为我们的因变量是边框的坐标 - 因此这实际上是一个回归数据。 ``` tfms = tfms_from_model(f_model, sz, crop_type=CropType.NO, aug_tfms=augs) md = Image **Classifier** Data.from_csv(PATH, JPEGS, BB_CSV, tfms=tfms, **continuous=True** , bs=4) @@ -63,7 +63,7 @@ _分类器_是具有因变量的任何分类或二项式。 与_回归_相反, ![](../img/1_QMa_SUUVOypZHKaAuXDkSw.png) -正如你所看到的,图像旋转并且光线变化,但是边界框_没有移动_并且_位于错误的位置_ [[6:17](https://youtu.be/0frKXR-2PBY%3Ft%3D6m17s)] 。 当你的因变量是像素值或以某种方式连接到自变量时,这是数据增强的问题 - 它们需要一起增强。 正如你在边界框坐标`[ 115\. 63\. 240\. 311.]`中所看到的,我们的图像是224乘224 - 所以它既不缩放也不裁剪。 因变量需要经历所有几何变换作为自变量。 +正如你所看到的,图像旋转并且光线变化,但是边框_没有移动_并且_位于错误的位置_ [[6:17](https://youtu.be/0frKXR-2PBY%3Ft%3D6m17s)] 。 当你的因变量是像素值或以某种方式连接到自变量时,这是数据增强的问题 - 它们需要一起增强。 正如你在边框坐标`[ 115\. 63\. 240\. 311.]`中所看到的,我们的图像是224乘224 - 所以它既不缩放也不裁剪。 因变量需要经历所有几何变换作为自变量。 要做到这一点 [[7:10](https://youtu.be/0frKXR-2PBY%3Ft%3D7m10s)] ,每个转换都有一个可选的`tfm_y`参数: @@ -87,7 +87,7 @@ _分类器_是具有因变量的任何分类或二项式。 与_回归_相反, ![](../img/1__ge-RyZpEIQ5fiSvo207rA.png) -现在,边界框随图像移动并位于正确的位置。 你可能会注意到,有时它看起来很奇怪,就像底行中间的那样。 这是我们所拥有信息的约束。 如果对象占据原始边界框的角,则在图像旋转后,新的边界框需要更大。 所以你必须**小心不要使用边界框进行太高的旋转,**因为没有足够的信息让它们保持准确。 如果我们在做多边形或分段,我们就不会遇到这个问题。 +现在,边框随图像移动并位于正确的位置。 你可能会注意到,有时它看起来很奇怪,就像底行中间的那样。 这是我们所拥有信息的约束。 如果对象占据原始边框的角,则在图像旋转后,新的边框需要更大。 所以你必须**小心不要使用边框进行太高的旋转,**因为没有足够的信息让它们保持准确。 如果我们在做多边形或分段,我们就不会遇到这个问题。 ![](../img/1_4V4sjFZxn-y2cU9tCJPEUw.png) @@ -107,7 +107,7 @@ _分类器_是具有因变量的任何分类或二项式。 与_回归_相反, #### custom_head [[9:34](https://youtu.be/0frKXR-2PBY%3Ft%3D9m34s)] -`learn.summary()`将通过模型运行一小批数据,并在每一层打印出张量的大小。 正如你所看到的,在`Flatten`层之前,张量的形状为512乘7乘7.所以如果它是1级张量(即单个向量),它的长度将是25088(512 * 7 * 7)并且这就是为什么我们的自定义标题的输入大小是25088.输出大小是4,因为它是边界框坐标。 +`learn.summary()`将通过模型运行一小批数据,并在每一层打印出张量的大小。 正如你所看到的,在`Flatten`层之前,张量的形状为512乘7乘7.所以如果它是1级张量(即单个向量),它的长度将是25088(512 * 7 * 7)并且这就是为什么我们的自定义标题的输入大小是25088.输出大小是4,因为它是边框坐标。 ``` head_reg4 = nn.Sequential(Flatten(), nn.Linear(25088,4)) learn = ConvLearner.pretrained(f_model, md, custom_head=head_reg4) learn.opt_fn = optim.Adam learn.crit = nn.L1Loss() @@ -127,7 +127,7 @@ _分类器_是具有因变量的任何分类或二项式。 与_回归_相反, #### 1.提供数据 -我们需要一个`ModelData`对象,其独立变量是图像,而因变量是边界框坐标和类标签的元组。 有几种方法可以做到这一点,但这里有一个特别懒惰和方便的方法,Jeremy提出的方法是创建两个`ModelData`对象,表示我们想要的两个不同的因变量(一个带有边界框坐标,一个带有类)。 +我们需要一个`ModelData`对象,其独立变量是图像,而因变量是边框坐标和类标签的元组。 有几种方法可以做到这一点,但这里有一个特别懒惰和方便的方法,Jeremy提出的方法是创建两个`ModelData`对象,表示我们想要的两个不同的因变量(一个带有边框坐标,一个带有类)。 ``` f_model=resnet34 sz=224 bs=64 @@ -155,7 +155,7 @@ _分类器_是具有因变量的任何分类或二项式。 与_回归_相反, * `y2` :包含其他因变量 * `(x, (y,self.y2[i]))` : `(x, (y,self.y2[i]))`返回一个自变量和两个因变量的组合。 -我们将使用它将类添加到边界框标签。 +我们将使用它将类添加到边框标签。 ``` trn_ds2 = ConcatLblDataset(md.trn_ds, md2.trn_y) val_ds2 = ConcatLblDataset(md.val_ds, md2.val_y) @@ -195,9 +195,9 @@ _分类器_是具有因变量的任何分类或二项式。 与_回归_相反, #### 2.选择建筑 [[13:54](https://youtu.be/0frKXR-2PBY%3Ft%3D13m54s)] -该体系结构将与我们用于分类器和边界框回归的体系结构相同,但我们将仅将它们组合在一起。 换句话说,如果我们有`c`类,那么我们在最后一层中需要的激活次数是4加`c` 。 4用于边界框坐标和`c`概率(每个类一个)。 +该体系结构将与我们用于分类器和边框回归的体系结构相同,但我们将仅将它们组合在一起。 换句话说,如果我们有`c`类,那么我们在最后一层中需要的激活次数是4加`c` 。 4用于边框坐标和`c`概率(每个类一个)。 -这次我们将使用额外的线性层,加上一些 Dropout ,以帮助我们训练更灵活的模型。 一般来说,我们希望我们的自定义头能够自己解决问题,如果它所连接的预训练骨干是合适的。 所以在这种情况下,我们试图做很多 - 分类器和边界框回归,所以只是单个线性层似乎不够。 如果你想知道为什么在第一个`ReLU`之后没有`BatchNorm1d` ,ResNet主干已经将`BatchNorm1d`作为其最后一层。 +这次我们将使用额外的线性层,加上一些 Dropout ,以帮助我们训练更灵活的模型。 一般来说,我们希望我们的自定义头能够自己解决问题,如果它所连接的预训练骨干是合适的。 所以在这种情况下,我们试图做很多 - 分类器和边框回归,所以只是单个线性层似乎不够。 如果你想知道为什么在第一个`ReLU`之后没有`BatchNorm1d` ,ResNet主干已经将`BatchNorm1d`作为其最后一层。 ``` head_reg4 = nn.Sequential( Flatten(), nn.ReLU(), nn.Dropout(0.5), nn.Linear(25088,256), nn.ReLU(), nn.BatchNorm1d(256), nn.Dropout(0.5), nn.Linear(256, **4+len(cats)** ), ) models = ConvnetBuilder(f_model, 0, 0, 0, custom_head=head_reg4) learn = ConvLearner(md, models) learn.opt_fn = optim.Adam @@ -225,7 +225,7 @@ _分类器_是具有因变量的任何分类或二项式。 与_回归_相反, * `input` :激活 * `target` :基本事实 -* `bb_t,c_t = target` :我们的自定义数据集返回一个包含边界框坐标和类的元组。 这项任务将对它们进行解构。 +* `bb_t,c_t = target` :我们的自定义数据集返回一个包含边框坐标和类的元组。 这项任务将对它们进行解构。 * `bb_i,c_i = input[:, :4], input[:, 4:]` :第一个`:`用于批量维度。 * `b_i = F.sigmoid(bb_i)*224` :我们知道我们的图像是224乘`Sigmoid`将强制它在0和1之间,并将它乘以224以帮助我们的神经网络在它的范围内成为。 @@ -277,9 +277,9 @@ _分类器_是具有因变量的任何分类或二项式。 与_回归_相反, [30.213772, 0.81580528616905212, 18.551488876342773] ``` -检测精度低至80,与以前相同。 这并不奇怪,因为ResNet旨在进行分类,因此我们不希望以这种简单的方式改进事物。 它当然不是为了进行边界框回归而设计的。 它显然实际上是以不关心几何的方式设计的 - 它需要最后7到7个激活网格并将它们平均放在一起扔掉所有关于来自何处的信息。 +检测精度低至80,与以前相同。 这并不奇怪,因为ResNet旨在进行分类,因此我们不希望以这种简单的方式改进事物。 它当然不是为了进行边框回归而设计的。 它显然实际上是以不关心几何的方式设计的 - 它需要最后7到7个激活网格并将它们平均放在一起扔掉所有关于来自何处的信息。 -有趣的是,当我们同时进行准确性(分类)和边界框时,L1似乎比我们刚进行边界框回归时要好一些 [[22:46](https://youtu.be/0frKXR-2PBY%3Ft%3D22m46s)] 。 如果这对你来说是违反直觉的,那么这将是本课后要考虑的主要事项之一,因为这是一个非常重要的想法。 这个想法是这样的 - 弄清楚图像中的主要对象是什么,是一种困难的部分。 然后确定边界框的确切位置以及它的类别是一个简单的部分。 所以当你有一个网络既说对象是什么,对象在哪里时,它就会分享关于找到对象的所有计算。 所有共享计算都非常有效。 当我们返回传播类和地方中的错误时,这就是有助于计算找到最大对象的所有信息。 因此,只要你有多个任务分享这些任务完成工作所需要的概念,他们很可能应该至少共享网络的某些层。 今天晚些时候,我们将看一个模型,其中除了最后一层之外,大多数层都是共享的。 +有趣的是,当我们同时进行准确性(分类)和边框时,L1似乎比我们刚进行边框回归时要好一些 [[22:46](https://youtu.be/0frKXR-2PBY%3Ft%3D22m46s)] 。 如果这对你来说是违反直觉的,那么这将是本课后要考虑的主要事项之一,因为这是一个非常重要的想法。 这个想法是这样的 - 弄清楚图像中的主要对象是什么,是一种困难的部分。 然后确定边框的确切位置以及它的类别是一个简单的部分。 所以当你有一个网络既说对象是什么,对象在哪里时,它就会分享关于找到对象的所有计算。 所有共享计算都非常有效。 当我们返回传播类和地方中的错误时,这就是有助于计算找到最大对象的所有信息。 因此,只要你有多个任务分享这些任务完成工作所需要的概念,他们很可能应该至少共享网络的某些层。 今天晚些时候,我们将看一个模型,其中除了最后一层之外,大多数层都是共享的。 结果如下 [[24:34](https://youtu.be/0frKXR-2PBY%3Ft%3D24m34s)] 。 和以前一样,当图像中有单个主要对象时,它做得很好。 @@ -417,7 +417,7 @@ _分类器_是具有因变量的任何分类或二项式。 与_回归_相反, #### SSD和YOLO [[29:10](https://youtu.be/0frKXR-2PBY%3Ft%3D29m10s)] -我们有一个输入图像,它通过一个转换网络,输出一个大小为`4+c`的向量,其中`c=len(cats)` 。 这为我们提供了一个最大物体的物体探测器。 现在让我们创建一个找到16个对象的对象。 显而易见的方法是采用最后一个线性层而不是`4+c`输出,我们可以有`16x(4+c)`输出。 这给了我们16组类概率和16组边界框坐标。 然后我们只需要一个损失函数来检查这16组边界框是否正确表示了图像中最多16个对象(我们稍后会进入损失函数)。 +我们有一个输入图像,它通过一个转换网络,输出一个大小为`4+c`的向量,其中`c=len(cats)` 。 这为我们提供了一个最大物体的物体探测器。 现在让我们创建一个找到16个对象的对象。 显而易见的方法是采用最后一个线性层而不是`4+c`输出,我们可以有`16x(4+c)`输出。 这给了我们16组类概率和16组边框坐标。 然后我们只需要一个损失函数来检查这16组边框是否正确表示了图像中最多16个对象(我们稍后会进入损失函数)。 ![](../img/1_fPHmCosDHcrHmtKvWFK9Mg.png) @@ -472,7 +472,7 @@ _分类器_是具有因变量的任何分类或二项式。 与_回归_相反, 1. 我们从ReLU和 Dropout 开始 2. 然后迈步1卷积。 我们从步幅1卷积开始的原因是因为它根本不会改变几何 - 它只是让我们添加一个额外的计算层。 它让我们不仅创建一个线性层,而且现在我们在自定义头中有一个小的神经网络。 `StdConv`在上面定义 - 它执行卷积,ReLU,BatchNorm和dropout。 你看到的大多数研究代码都不会定义这样的类,而是一次又一次地写出整个事物。 不要那样。 重复的代码会导致错误和理解不足。 3. 跨步2卷积 [[44:56](https://youtu.be/0frKXR-2PBY%3Ft%3D44m56s)] -4. 最后,步骤3的输出为`4x4` ,并传递给`OutConv` 。 `OutConv`有两个独立的卷积层,每个卷层都是步长1,因此它不会改变输入的几何形状。 其中一个是类的数量的长度(现在忽略`k`而`+1`是“背景” - 即没有检测到对象),另一个的长度是4.而不是有一个输出`4+c`转换层,让我们有两个转换层,并在列表中返回它们的输出。 这允许这些层专门化一点点。 我们谈到了这个想法,当你有多个任务时,他们可以共享层,但他们不必共享所有层。 在这种情况下,我们创建分类器以及创建和创建边界框回归的两个任务共享除最后一个层之外的每个层。 +4. 最后,步骤3的输出为`4x4` ,并传递给`OutConv` 。 `OutConv`有两个独立的卷积层,每个卷层都是步长1,因此它不会改变输入的几何形状。 其中一个是类的数量的长度(现在忽略`k`而`+1`是“背景” - 即没有检测到对象),另一个的长度是4.而不是有一个输出`4+c`转换层,让我们有两个转换层,并在列表中返回它们的输出。 这允许这些层专门化一点点。 我们谈到了这个想法,当你有多个任务时,他们可以共享层,但他们不必共享所有层。 在这种情况下,我们创建分类器以及创建和创建边框回归的两个任务共享除最后一个层之外的每个层。 5. 最后,我们弄平了卷积,因为杰里米写了损失函数,期望压低张量,但我们可以完全重写它不要那样做。 #### [Fastai编码风格](https://github.com/fastai/fastai/blob/master/docs/style.md) [[42:58](https://youtu.be/0frKXR-2PBY%3Ft%3D42m58s)] @@ -481,7 +481,7 @@ _分类器_是具有因变量的任何分类或二项式。 与_回归_相反, #### 损失函数 [[47:44](https://youtu.be/0frKXR-2PBY%3Ft%3D47m44s)] -损失函数需要查看这16组激活中的每一组,每组激活具有四个边界框坐标和`c+1`类概率,并确定这些激活是否离最近该网格单元的对象很近或远离在图像中。 如果没有,那么它是否正确预测背景。 事实证明这很难。 +损失函数需要查看这16组激活中的每一组,每组激活具有四个边框坐标和`c+1`类概率,并确定这些激活是否离最近该网格单元的对象很近或远离在图像中。 如果没有,那么它是否正确预测背景。 事实证明这很难。 #### 匹配问题 [[48:43](https://youtu.be/0frKXR-2PBY%3Ft%3D48m43s)] @@ -493,7 +493,7 @@ _分类器_是具有因变量的任何分类或二项式。 与_回归_相反, ![](../img/1_8M9x-WgHNasmuLSJNbKoaQ.png) -我们的因变量看起来像左边的变量,我们的最终卷积层将是`4x4x(c+1)`在这种情况下`c=20` 。 然后我们将其展平成一个向量。 我们的目标是提出一个函数,它接受一个因变量以及最终从模型中出来的一些特定的激活,并且如果这些激活不是地面实况边界框的良好反映,则返回更高的数字; 如果它是一个很好的反映,或更低的数字。 +我们的因变量看起来像左边的变量,我们的最终卷积层将是`4x4x(c+1)`在这种情况下`c=20` 。 然后我们将其展平成一个向量。 我们的目标是提出一个函数,它接受一个因变量以及最终从模型中出来的一些特定的激活,并且如果这些激活不是地面实况边框的良好反映,则返回更高的数字; 如果它是一个很好的反映,或更低的数字。 #### 测试 [[51:58](https://youtu.be/0frKXR-2PBY%3Ft%3D51m58s)] @@ -515,7 +515,7 @@ _分类器_是具有因变量的任何分类或二项式。 与_回归_相反, _(Variable containing:_ _0.6786 0.4866 0.9911 0.6250_ _0.7098 0.0848 0.9911 0.5491_ _0.5134 0.8304 0.6696 0.9063_ _[torch.cuda.FloatTensor of size 3x4 (GPU 0)], Variable containing:_ _8_ _10_ _17_ _[torch.cuda.LongTensor of size 3 (GPU 0)])_ ``` -请注意,边界框坐标已缩放到0到1之间 - 基本上我们将图像视为1x1,因此它们相对于图像的大小。 +请注意,边框坐标已缩放到0到1之间 - 基本上我们将图像视为1x1,因此它们相对于图像的大小。 我们已经有`show_ground_truth`函数。 这个`torch_gt` (gt:地面实况)函数只是将张量转换为numpy数组。 @@ -653,7 +653,7 @@ _分类器_是具有因变量的任何分类或二项式。 与_回归_相反, 这就是匹配阶段 [[1:02:29](https://youtu.be/0frKXR-2PBY%3Ft%3D1h2m29s)] 。 对于L1损失,我们可以: 1. 采取匹配的激活( `pos_idx = [11, 13, 14]` ) -2. 从那些真实的边界框中减去 +2. 从那些真实的边框中减去 3. 取差异的绝对值 4. 采取的意思。 @@ -667,7 +667,7 @@ _分类器_是具有因变量的任何分类或二项式。 与_回归_相反, _(Variable containing:_ _1.00000e-02 *_ _6.5887_ _[torch.cuda.FloatTensor of size 1 (GPU 0)], Variable containing:_ _1.0331_ _[torch.cuda.FloatTensor of size 1 (GPU 0)])_ ``` -我们最终会得到16个预测的边界框,其中大多数都是背景框。 如果你想知道它在背景的边界框中预测了什么,答案是完全忽略它。 +我们最终会得到16个预测的边框,其中大多数都是背景框。 如果你想知道它在背景的边框中预测了什么,答案是完全忽略它。 ``` fig, axes = plt.subplots(3, 4, figsize=(16, 12)) **for** idx,ax **in** enumerate(axes.flat): ima=md.val_ds.ds.denorm(to_np(x))[idx] bbox,clas = get_y(y[0][idx], y[1][idx]) ima=md.val_ds.ds.denorm(to_np(x))[idx] bbox,clas = get_y(bbox,clas); bbox,clas a_ic = actn_to_bb(b_bb[idx], anchors) torch_gt(ax, ima, a_ic, b_clas[idx].max(1)[1], b_clas[idx].max(1)[0].sigmoid(), 0.01) plt.tight_layout() @@ -683,7 +683,7 @@ _分类器_是具有因变量的任何分类或二项式。 与_回归_相反, **def** actn_to_bb(actn, anchors): actn_bbs = torch.tanh(actn) actn_centers = (actn_bbs[:,:2]/2 * grid_sizes) + anchors[:,:2] actn_hw = (actn_bbs[:,2:]/2+1) * anchors[:,2:] **return** hw2corners(actn_centers, actn_hw) ``` -我们抓住激活,我们将它们穿过`tanh` (记住`tanh`与sigmoid的形状相同,除了它被缩放到介于-1和1之间),这迫使它在该范围内。 然后我们抓住锚箱的实际位置,我们将根据激活的值除以2( `actn_bbs[:,:2]/2` )来移动它们。 换句话说,每个预测的边界框可以从其默认位置移动最多50%的网格大小。 它的高度和宽度同样如此 - 它可以是默认尺寸的两倍大或一半大。 +我们抓住激活,我们将它们穿过`tanh` (记住`tanh`与sigmoid的形状相同,除了它被缩放到介于-1和1之间),这迫使它在该范围内。 然后我们抓住锚箱的实际位置,我们将根据激活的值除以2( `actn_bbs[:,:2]/2` )来移动它们。 换句话说,每个预测的边框可以从其默认位置移动最多50%的网格大小。 它的高度和宽度同样如此 - 它可以是默认尺寸的两倍大或一半大。 #### 调整2.我们实际上使用二元交叉熵损失而不是交叉熵 [[1:05:36](https://youtu.be/0frKXR-2PBY%3Ft%3D1h5m36s)] @@ -705,7 +705,7 @@ _分类器_是具有因变量的任何分类或二项式。 与_回归_相反, * 自定义丢失功能 * 一种计算Jaccard指数的方法 -* 一种将激活转换为边界框的方法 +* 一种将激活转换为边框的方法 * 一种将锚箱映射到地面实况的方法 现在剩下的就是SSD丢失功能。 @@ -724,10 +724,10 @@ _分类器_是具有因变量的任何分类或二项式。 与_回归_相反, **def** get_y(bbox,clas): bbox = bbox.view(-1,4)/sz bb_keep = ((bbox[:,2]-bbox[:,0])>0).nonzero()[:,0] **return** bbox[bb_keep],clas[bb_keep] ``` -你在互联网上找到的许多代码都不适用于小批量。 它只做我们不想要的一件事。 在这种情况下,所有这些函数( `get_y` , `actn_to_bb` , `map_to_ground_truth` )都`actn_to_bb` , `map_to_ground_truth`不是一个小批量,而是一次只有一大堆基础事件。 数据加载器一次被送入一个小批量进行卷积层。 因为我们可以_在每个图像中_具有_不同数量的地面实况对象,_但是张量必须是严格的矩形形状,fastai会自动用零填充它(任何更短的目标值) [[1:11:08](https://youtu.be/0frKXR-2PBY%3Ft%3D1h11m8s)] 。 这是最近添加的,非常方便的东西,但这确实意味着你必须确保你摆脱那些零。 所以`get_y`摆脱了任何只是填充的边界框。 +你在互联网上找到的许多代码都不适用于小批量。 它只做我们不想要的一件事。 在这种情况下,所有这些函数( `get_y` , `actn_to_bb` , `map_to_ground_truth` )都`actn_to_bb` , `map_to_ground_truth`不是一个小批量,而是一次只有一大堆基础事件。 数据加载器一次被送入一个小批量进行卷积层。 因为我们可以_在每个图像中_具有_不同数量的地面实况对象,_但是张量必须是严格的矩形形状,fastai会自动用零填充它(任何更短的目标值) [[1:11:08](https://youtu.be/0frKXR-2PBY%3Ft%3D1h11m8s)] 。 这是最近添加的,非常方便的东西,但这确实意味着你必须确保你摆脱那些零。 所以`get_y`摆脱了任何只是填充的边框。 1. 摆脱填充 -2. 将激活转到边界框 +2. 将激活转到边框 3. 做Jaccard 4. 做map_to_ground_truth 5. 检查重叠是否大于0.4~0.5左右(不同的纸张使用不同的值) @@ -841,10 +841,10 @@ anc_grids = [4, 2, 1] anc_zooms = [0.75, 1., 1.3] anc_ratios = [(1., 1.), (1., ![](../img/1_C67J9RhTAiz9MCD-ebpp_w.png) -* 我们有一个地面实况的向量(4个边界框坐标和一个类的集合) +* 我们有一个地面实况的向量(4个边框坐标和一个类的集合) * 我们有一个神经网络,需要一些输入并输出一些输出激活 * 比较激活和基本事实,计算损失,找出其衍生物,并根据学习率的微分时间调整权重。 -* 我们需要一个能够接受基本事实和激活的损失函数,并输出一个数字,说明这些激活有多好。 要做到这一点,我们需要取`m`基础事实对象中的每一个,并决定哪一组`(4+c)`激活对该对象负责 [[1:21:58](https://youtu.be/0frKXR-2PBY%3Ft%3D1h21m58s)] - 我们应该比较哪一个来决定是否该类是正确的,边界框是否接近(匹配问题)。 +* 我们需要一个能够接受基本事实和激活的损失函数,并输出一个数字,说明这些激活有多好。 要做到这一点,我们需要取`m`基础事实对象中的每一个,并决定哪一组`(4+c)`激活对该对象负责 [[1:21:58](https://youtu.be/0frKXR-2PBY%3Ft%3D1h21m58s)] - 我们应该比较哪一个来决定是否该类是正确的,边框是否接近(匹配问题)。 * 由于我们使用的是SSD方法,所以我们匹配的不是任意的 [[1:23:18](https://youtu.be/0frKXR-2PBY%3Ft%3D1h23m18s)] 。 我们希望匹配一组激活,这些激活的感受野具有真实物体所在的最大密度。 * 损失函数需要是一些一致的任务。 如果在第一个图像中,左上角的对象与前4 + c激活相对应,而在第二个图像中,我们扔掉了东西,突然它现在正在进行最后的4 + c激活,神经网络不知道是什么学习。 * 解决匹配问题后,其余部分与单个对象检测相同。 @@ -1035,7 +1035,7 @@ learn.fit(lrs, 1, cycle_len=10, use_clr=(20,10)) #### 非最大抑制 [[1:52:15](https://youtu.be/0frKXR-2PBY%3Ft%3D1h52m15s)] -我们要做的就是我们要经历每一对这些边界框,如果它们重叠超过一定数量,比如0.5,使用Jaccard并且它们都预测同一个类,我们将假设它们是同样的事情,我们将选择具有更高`p`值的那个。 +我们要做的就是我们要经历每一对这些边框,如果它们重叠超过一定数量,比如0.5,使用Jaccard并且它们都预测同一个类,我们将假设它们是同样的事情,我们将选择具有更高`p`值的那个。 这是非常无聊的代码,杰里米自己没有写它并复制别人的。 没理由特别经历它。 -- GitLab