按照美东时间算,今天是大年初一;按照北京时间算,今天是大年初二。距离回国还有一周零两天,并收到了申请季的第一封official offer。

其实我的大四上并没有太多值得总结的,浑浑噩噩了几个月,在一月初听说自己把自己作死了,于是开始(试图)洗心革面重新做人,在最后的最后好歹捞回来了一点。当然其实我自己的功劳只能占不到20%,另外80%是靠着几位实验室师兄接济给救回来的。因此,当我收到了offer之后并没有感到开心,相反,这其中需要反思的事情远多于可以开心的事情。

在这几个月里遇到了形形色色的人,包括小部分不太善良的人以及大多比较善良的人,人性的复杂性远超前三年生活的环境。还被传授了一个人生哲理:“大多数没有利益冲突的人还是会帮我的”。隐约感觉一月发生了太多的事情,以至于我并不能想起从八月到十二月这五个月都做了些什么。这其中包括:

  • 做了一些苦力工作并发现发不了paper;
  • 发觉我并没有给实验室的项目做什么contribution,因此没什么值得跟老板汇报的;
  • 由于自己求生欲太弱(主要)和可能被坑了(次要)而导致申请要GG了,于是决定再挣扎一下;
  • 在某学长指引下,求助了另一学长;
  • 发愤图强了几周,做了些可以和老板汇报的东西;
  • 在某几位学长的帮助下拿到了offer;
  • 并请教了某大佬接受了一些如何发paper的指导。

此外接受了一些外界的批评,主要包括两部分:

  • 求生欲太弱
  • 依赖性太强

当然如果让我自己总结,可能我的根本问题是:

  • 没用的东西想得太多

然而对于做research依赖性太强这一点,我发现我真的不会做research,在这半年里也没学会如何做research,这导致我根本没有独立的资本啊。


在最近的一段时间反思了一下大学以来的生活,突然意识到我根本没有利用好T大的平台。在上大学后的很长一段时间,我都没有意识到高中和大学是有很大区别的。大学的评价指标不再是高考考好这么简单,评价指标的多元化意味着我应该开拓自己的视野并选择一条适合自己的路线。然而在大学期间,我已经狭隘到唯一注意过的评价机制就是成绩。然而,我本来就属于考好一次下一次就会考砸,考砸一次下一次发愤图强一下也许就能考好,这就导致了大学成绩波澜起伏,总成绩也就没那么好看。假如有个最大退步奖,我肯定能是有力的竞争者。与此同时,很多同学早早的进了实验室,并做出了很多不错的工作,然而我在实验室投入的经历过少,总是想着成绩好一点再在实验室多投入一些,一拖再拖就没有了在实验室做出一些工作的机会。结果到了申请的时候才发现,成绩早就不是申请PhD的硬通货了,大多数有力的竞争者都手握几篇顶会,而我这种成绩没刷到多好,又没有paper的就只能吃瓜。而且在T大的生活节奏太快,大多数时间又过于压抑,慢慢地就忘记了自己当时的目标是什么,大多数时间只是随着大家走,对各种事情都缺乏热情。

另外一点是,大学期间在做决策方面的独立性不够,xjb听了很多意见后,发现还是相信自己的判断比较好。我深刻地体会到留学机构的各种讲座几乎都没什么意义,申请这件事说白了就是如果自己的各种条件都很好,想去哪里就能去哪里;如果条件不够,就只能靠运气,跟各色留学机构也没什么关系。至于别人的建议,事实的部分可以拿来参考,其他的大道理听听就可以了。

其实我们都活在一定的评价体系里,想要往上走,就要满足评价指标,不管是考试也好,还是美帝这种软性指标也好。最近冷静了一些,又重新想了想自己真正的目标是什么,希望再接下来的几年里,不管多忙都可以记着,并朝那个方向努力。


竟然没有找到什么值得highlight的progress,真是太悲哀了。

今天是2018年8月31号,8月的最后一天,根据博客显示,上一次更新是21天前。再过一周,我就在Pittsburgh度过第一个月;再过一周,我就要21岁了。

我实在无法回忆起在没更博客的三周里,我都做了些什么。可是我现在什么都不想做,不想调代码,不想背单词,也不想去超市采购,不想离开实验室,因此我决定用这点时间记录些什么。

我大概很快就度过了特别开心愉悦的一段时间,然后重新开始丧了起来。当然,如果总结过去的经验,我好像在每个开学初都会丧那么一阵。丧的时候,觉得这是我人生21年来最难熬的一段时间。这次丧的特点主要包括:

  • 睡不着觉。入睡是不困难的,问题在于很早就会醒过来,最早的一天三点多就醒了。窗外总是很吵,醒后睡不着是常态,耳塞都没有太大的用处。到今天起,我应该有好几天只睡了五六个小时。这个问题似乎被一些同学理解成了我很拼,但其实这样的时候我很难学习,只能靠和很多人聊天熬过去。我很希望自己能多睡一点,但是我做不到,好在白天还可以靠着咖啡和debug保持清醒。
  • 压力大到喘不过气来,哭都哭不出来。中间有几天天天靠深吸气和叹气保持呼吸顺畅,压抑到很想哭,但哭不出来。我尝试了去健身房work out,但是尽管跑步除了很多汗,从跑步机上下来就像运动这件事没发生过一样。两天前和爸妈打电话时大哭一场,昨天的情绪终于有了些起色,大概哭这件事还是更有用一点吧。
  • 害怕没人在周围的时候。这是我最近常驻实验室的最大原因,至少在实验室的时候,能看到个人影,让我觉得舒服一点。自己坐在家里时,会觉得很冷清。因此在计划里,我周末也会前往实验室坐着,不过如果没有其他人来的话,就要另做考虑了。

我前往学校的Counselling and Psychological Services想要预约一次咨询,然而这个只针对students,而我现在的身份是faculty或者staff,昨天工作人员说帮我确认一下是否可以预约咨询,到现在我还没有收到回复。事实上,即使可以预约到咨询,也是下周三的事了,那时候我的精神状态如何还很难预测。

最近由于睡眠不佳,导致吃饭不大好,再加上做了很多运动,体重有骤降的趋势。现在我已经瘦了五六斤了。照这个趋势下去可能回国的时候我就快要瘦到小学时的麻杆身材了。不过,瘦大概是一件让我快乐的事。实验室里有个我认为身材非常标准的男生在减肥,很震惊;从某种意义上来说,也许很多人追求瘦和白已经到了病态的程度。我对身材的期待也就是正常的体重和稍微结实一点的线条就好了,我这个骨架大概也不支持我瘦到那种可能被风刮走的程度。

这块区域是真的没什么可吃的,以至于尽管每天很饿,吃饭都依旧是个煎熬。iNoodle已经吃腻了,今天中午跑去小亚洲买了份鱼片,刚吃的时候觉得这是我在匹兹堡吃到过的最好吃的饭了,但是很快就齁的不行,最后也没有完全吃完就扔掉了。昨天和爸妈打电话,他们让我不要省钱,吃点好的,每个月开销八九千一万也是可以接受的,但是我找不到什么好的吃的,拿7.5刀的iNoodle填填肚子就完事了。

由于上周没去超市采购,家里也没什么可以吃的了。上上周买了块牛排,到现在都冻在冰箱里懒得做。做饭实在很麻烦,现在我连扔进微波炉里加热都觉得很烦,所以我打算买点能直接吃的,或者很好熟的东西随便凑活一下就好了。

我终于向洗衣机妥协了。尽管它可能(一定)很脏,但是说服自己的理由还是那句“洗衣机本身就是让脏的东西变干净的东西”。而且说起来1.5刀一次也不能称之为贵。既然它可以节约我的时间,还可以给我的腰减轻负担,那么为什么要委屈自己手洗呢。

由于最近做了过多的家务,主要是做早餐、洗碗、洗衣服,我手上的角质层明显增厚。在加上我手欠,整天都在抠来抠去,导致手上起皮很厉害,触感非常不美好。

实验室的人因为各种原因走了,现在这个屋的中国人只有两个。我好像失去了娱乐的机会。我很想去看碟中谍6,但竟然一时不知道该去找谁看。室友对这部电影表示了一丝鄙视,没办法,作为一个不太高雅的人,我就是爱看这种惊险刺激的商业片。昨天问室友,我的电影喜好是不是有点男生啊;室友答,相当男生。好吧,其实我还看阿汤哥的脸呢。

我很喜欢实验室,在实验室坐着是美好的,某种意义上来说,是无忧无虑的。我觉得我快要饿了,于是拿起来去中餐厅买饭的时给的lucky cookie。现在桌子上有三张lucky纸条,分别写着“A happy event will take place in your home.”“One dreamed of becoming somebody. Another remained awake and became.”和“An optimist is always able to see the bright side of other people’s troubles.”第一句最lucky,第二句,嗯大概我只是“remained awake”。

带我的postdoc交给我的任务卡住了,可是听说他现在正在闭关修炼赶AAAI的paper,可能又要出个大武功了吧。下午找实验室另一个学长聊了,问了一些问题,获得了一些合理的实验建议。每次找他聊总是能获得很多有用的信息,但是他们的屋子离我有点遥远。

各种细微的情绪已经通过各种渠道发泄了出去,在这篇文章里就不再提及了。写完这篇文章大概也不早了,我决定前往超市进行一些采购,这样周末就可以快乐地坐在实验室里了。如果我能约到人看碟中谍6那可能会更快乐,但是活着就很不容易了,为什么还要追求这么多呢。

九月九号是我生日,每次说起这个日期都会想到《九月九日忆山东兄弟》。现在这首诗的标题和前两句很符合我的心境:

独在异乡为异客,每逢佳节倍思亲。

希望我们都有美好的未来 :)

(最后放几张最近看来的很搞笑的图片/表情包吧)


图形学


无题


成熟的IDE


走之前的寝室

本质上,这篇文章依旧属于周记。但由于一些显而易见的原因,我决定赋予其姓名。

今天是来到匹斯堡的第四天,如果包括到达的那一天的话。其实这篇文章是我在飞机上开始筹划的,但是由于以下三点原因,今天才开始写:

1. 这是一段糟糕的旅程,搞得我戾气太重
2. 各种手续都没有办妥
3. 以上都是借口,其实是我拖延症复发了

鉴于这是一篇有名字的周记,自然待遇要和其他的周记有所不同,所以格式什么的就不必在乎了。因此,本文将基本按照时间顺序叙述。


走之前的多肉,它们几个月前不是这个样子的

Part 0

故事要从从家中出发开始。

2018年8月7日,星期二,清晨五点,我由于焦虑和兴奋的叠加态情绪早早醒来,起床后吃了温热的西红柿炒鸡蛋和蛋炒饭。这是一个美好的开端,但并不意味着将有一个美好的过程。

六点半,我爸妈和我一起从家中出发上车。彼时雷声大作,暴雨倾盆,一度让我感到飞机可能无法准时起飞了。然而神奇的是,当车进入北京后,雨水从有到无,到达北京机场时,窗外已经没有下雨的倾向了。于是乎,我和我爸妈一起走入航站楼,合了张影,喝了离开国内前的最后一杯咖啡,拥抱,然后我就刷票进站了。

不论是进站时还是现在想到那个时候,我都止不住想哭,就仿佛是一瞬间,突然意识到我该长大了。我甚至还记得在进站后我曾回头看过几眼,但是我在亮出,我爸妈在暗处,我根本无法看到他们。

大概从那一刻起,才算是这趟旅程的起点。


北京机场的美式

Part 1

进站后要先坐轻轨摆渡,然后手持新版护照走自助通道出关,经过了在机场里瞎转悠找书店但没找到的无聊两小时后,我终于可以上飞机了。由于我买的是最末等的经济舱,排队是属于Group4,只能站在离登机口最远的地方排队(甚至我刚开始都没有看到Group4的登机口),目送其他所有人上飞机后才能登机。这里是经济舱没人权的开始。

我真的再也不想坐美联航了,这大概是我坐过的最差的一次航班。我的膝盖到前面的椅子距离不到10cm,空间极小。而且飞机的座椅上有一种奇怪的酸臭味,并顺带传染给了我的衣服,十分崩溃。飞机上一共有三顿饭:第一顿饭是pasta和鸡肉米饭二选一,我选了鸡肉米饭。套餐里配了一个不知道加了多少奇怪的香料烘焙出来的面包,基本很难下咽,而且我还把黄油扔了,据邻座说涂上黄油还能好一点。这顿饭还附送了一盒八喜冰淇淋,这大概是唯一一点希望。第二顿饭是邻座口中最好吃的一顿,内容是牛肉汉堡。是的,牛肉汉堡,但不是麦当劳的那种牛肉汉堡,而是两片冰冷的面板里夹着两片冰冷的熏牛肉。第三顿饭发生在飞机降落前一个小时,当时我甚至觉得不可能再送饭了,但这是我心目中最好吃的一顿,内容是一坨炒鸡蛋一坨煮土豆块和一根香肠。吃这种不带奇怪味道的温热食品的几分钟真是飞机上的巅峰时刻。

在飞机上坐到四个小时的时候腰就开始酸,然后忍到了下飞机的时候,从腰到屁股到大腿几乎已经酸到发麻了,幸好走下飞机之后好了一些。

坐飞机比较有趣的是认识了邻座的两个留学生,颇有一种天下华人一家亲的感觉。邻座两个人一个是学土木的,一个是学化学的。听他们讲了很多美国的“风土人情”,总之还是很有趣的。

下了飞机之后,作为英语文盲生活白痴的我几乎就是靠着两个邻座小伙伴活过来的。过海关的时候突然很紧张,不懂自己在紧张些什么。还好我们三个人顺利的过了海关,然后一路拿行李、重新托运、转机。

因为我们三个人转机不一样,所以就彼此分开了。幸运的是,当我发现我要独自面对接下来的路程的时候,又遇到了一个和我一起转机到匹兹堡的人。于是乎我又在别人的帮助下一路上飞机、下飞机、取行李,最后坐taxi到了住处。

经历了这一路之后,我觉得我甚至可以写一份白痴生存指南,又名如何依靠别人活下去,管它之前认不认识。

Part 2

接下来就是到匹兹堡的生活了。概括成一句话就是我从来没有经历过的无比闲又很琐碎的生活。


住处附近的一个教堂

由于时差,周三清晨很早就醒过来了。为了打发见系里秘书前的无聊生活,我体验了一下资本主义特色视频网站youtube(好吧之前就没少上过)。之后前往超市购买了一点点吃的,并去办理了手机卡。随后去见了Alison,收获了办学生卡材料一张,校园地图一张,和学校附近办手机卡的地图一张。感慨一句,美帝就是奢侈啊,单面彩打了三张地图。随后在Alison的嘱托下,去办了学生卡,联系了老板并申请了andrew id(一个学校账号一样的东西)。在晚上和飞机上认识的小伙伴进行了一次约饭,吃了我到匹兹堡以来吃过的最好吃的一顿,虽然内容不过就是个金枪鱼三明治而已。顺便还发现了自己无法饮酒的事实,这不禁让我感到我还年轻。

Tuna-melt三明治

由于时差,周四清晨很早就醒过来了。为了打发orientation前的无聊生活,我体验了一下资本主义特色视频网站youtube。见到了室友,室友是一个很可爱的妹子。随后进行了一个小时的orientation meeting,然后之前跑去了实验室组会。我很努力的听了一个半小时,然而只听懂了1%。随后和老板简短的meet了一下,收获了草莓夹心巧克力一枚,LTI杯子一个。


LTI杯子和学生卡

由于时差,周五清晨很早就醒过来了。为了打发见实验室学长前的无聊生活,我又睡了过去,然后体验了一下资本主页特色视频网站youtube。随后前往学校找两个很senior的学长聊了天,了解了实验室里所有项目,总之人都非常nice。后面一个学长给我讲了实验室一个大项目,其中一些内容让我深刻地感受到了我对CV一无所知,不禁开始怀疑我之前这一年在实验室到底干了些什么。随后我向他讲了我之前在实验室做过的一个项目,并说可能听起来比较naive,所幸没有被学长婊。学长怼(大概是怼?)了一些听起来觉得自己非常牛逼的“小朋友”,心中大喜,我就是永远感受不到自己牛逼,大概因为我是真菜吧。之后跟着学长到了学校里一个亚洲餐厅,虽然不咋中餐好吃,但总比三明治咽的下去。我还不知道该怎么把午餐钱还给人家。下午的时候,我一个人跑去PNC办了学生账户,听说我会收到三张卡。办卡的体验就是听了半个小时的托福听力,我深刻意识到了自己在口语和听力上的不足。


PNC给的一沓材料


学校里的亚洲餐厅

由于时差,我也不知道周六和周日清晨会在几点醒过来。理论上,我将获得我在大学期间前三丰富的周末生活。计划中包含一顿亚洲早餐、一次电影和一次聚会。希望我有一个美好的周末。

不知不觉已经在匹兹堡度过了三天。方便程度远远低于紫荆八号楼,但是好在发现这个城市里散布着一些我曾经认识和我刚刚认识的人,所以我的白痴生存指南可能还可以继续下去。

希望我们都有美好的未来,加油吧;)

首先,我们先来回忆一下YOLO网络在做什么。

现在,我们有一张大小为512*512的狗的图片,现在我想预测出狗的bounding box。我们先把图片按照步长32划分成16*16的网格,通过YOLO网络,这个网格中的每个格子都会预测出3个bounding box,这3个bounding box的中心都落在这个格子里。如果网络效果不错的话,红色格子对应的3个预测里就有一个是狗的bounding box(也就是黄色框)。对于每一个bounding box,我们都会预测出(4+1+n)个参数,其中前面4个参数和bounding box的中心坐标以及长宽有关,第五个参数表示这个bounding box里有物体的概率和这个bounding box和真实bounding box的IOU的成绩,最后n个参数表示如果这个bounding box里有物体,那么这个物体属于某一类的概率有多大,也就是分类任务的输出。

因此,对于这张图片,我们会预测出16*16*3个bounding box,首先我们先根据第五个参数,筛出可能包含物体的bounding box。然后使用非最大抑制(non-maximum suppression)找到这些bounding box里比较准确的那些bounding box。

使用的YOLOv3-tiny网络结果如图所示,其中蓝色的部分用于特征提取,然后经过几个卷积层的调整接到YOLO层来预测出小尺度的bounding box。此外,我们特征提取部分的特征上采样然后和更浅的层的特征连接起来用于预测尺度大一些的bounding box。由于训练过程中观察到网络不太好收敛,所以可以先不管右侧这个分支,只训练左侧这一路,经过3000个epoch之后,再训练整个网络,这样可以使网络收敛得更快一些。

YOLO主页上提供了很详细的教程,讲解如何使用作者提供的YOLO实现,其中包括如何使用pretrain的model,如何在VOC和COCO数据集上训练。在这篇文章中,我们来看一下如何能将作者提供的YOLO实现应用到自己的数据集上。

安装YOLO

首先,我们需要跟着主页上的教程安装YOLO。

1
2
3
git clone https://github.com/pjreddie/darknet
cd darknet
make

如果想使用GPU的话,要在make之前把Makefile中前两行置1。

1
2
GPU=1
CUDNN=1

调整我们的数据集

此链接中有我制作的数据集。我们来看一下数据集里都需要什么内容:

  • 训练集和验证集的图片images/
  • 训练集和验证集的label labels/:目录下的每个label文件名和图片对应,把图片文件名的格式.png/jpg/...改成.txt。label文件中,从第一行开始每行表示一个bounding box。每个bounding box包含5个参数,依次是类别(从0开始),x,y,w,h。其中(x,y)是bounding box的中心点位置,w表示bounding box的宽度,h表示bounding box的高度。注意,后四个参数都要除以图片的尺寸以获得[0,1]的数。

    1
    0 0.4140625 0.51953125 0.125 0.17578125
  • 一个文本文件,包含了训练集中所有图片的路径train.txt

    1
    2
    3
    4
    5
    /home/robot/Desktop/PVZ/data/images/1.png
    /home/robot/Desktop/PVZ/data/images/2.png
    /home/robot/Desktop/PVZ/data/images/3.png
    /home/robot/Desktop/PVZ/data/images/4.png
    ......
  • 一个文本文件,包含了验证集中所有图片的路径valid.txt:和train.txt类似。

  • 一个文本文件,包含了所有类别的名字zombie.name:从第一行开始每行写一个名字,第一行为第0类的名字,第二行为第1类的名字,以此类推。比如在我们的数据集中只有一类,这类名字叫Marty,因此只有一行,上面写着Marty。
    1
    Marty

到现在为止,我们的数据集就已经创建好了,接下来我们要做的就是把数据喂进YOLO中进行训练。

使用YOLO训练

接下来,我们来看一下如何训练。由于我在项目中使用的是YOLOv3-tiny这个网络,所以下面都以这个网络为例。

darknet/cfg/中有很多.cfg文件,这个文件描述了网络结构和一些训练参数,因为darknet实现了一套自己的底层框架,所以如果你先构建新的网络,也需要按照这个格式来。

打开darknet/cfg/yolov3-tiny.cfg文件,为了能使用我们自己的数据集,还要对其做一些调整。为了方便起见,我们创建两个.cfg文件,yolov3-tiny.cfg用于训练和yolov3-tina-testing.cfg用于测试。

  • yolov3-tiny.cfg文件中我们将[net]中的batch size设为64,subdivision设为2。在yolov3-tiny.cfg-testing文件中我们将[net]中的batch size设为1,subdivision设为1。
    yolov3-tiny.cfg

    1
    2
    3
    # Testing
    batch=1
    subdivisions=1

    yolov3-tiny-testing.cfg

    1
    2
    3
    # Testing
    batch=64
    subdivisions=2
  • 根据自己的数据集调整[net]中的widthheight,比如在我的数据集中图片是512*512的,所以有

    1
    2
    width=512
    height=512
  • 接下来,我们要把所有yolo层里的classes改成自己的数据集中的类别数。比如我的数据集中只有一类,因此classes=1

  • 最后也是最重要的一步,我们需要对每个yolo层前的卷积层的输出channel数做调整。根据上一篇文章,我们知道输出时每个格子对应的channel为\3*(4+1+num_classes\),所以要把filters改为这个数。比如在我的数据集中,只有一类,那输出channel数为18,因此改成filters=18

在修改完网络后,我们还需要建立一个新文件zombie.data,里面包含了训练时所用的一些目录的信息:

1
2
3
4
5
classes = 1
train = /home/robot/Desktop/PVZ/data/train.txt
valid = /home/robot/Desktop/PVZ/data/valid.txt
names = /home/robot/Desktop/PVZ/data/zombie.name
backup = backup

其中classes表示数据集中的类别数目,train指向上一节提到的train.txt所在位置,valid指向上一节提到的valid.txt所在位置,names指向类别名字的文件zombie.name所在位置,backup表示中间存储的目录,之后训练过程中存储下来的weights文件都可以在这个目录下找到。

现在我们就可以开始训练了,参照主页中的格式运行darknet:

1
./darknet detector train zombie.data yolov3-tiny.cfg darknet.conv.weights

最后一个命令行参数是pretrain的权重文件,关于YOLOv3-tiny的pretrain model可以在此链接处下载。

训练好后,我们在backup的目录下找到weights文件,然后进行测试:

1
./darknet detector test zombie.data yolov3-tiny-testing.cfg backup/[weights file]

然后就可以看到目录下出现了一个predictions.jpg文件,如图:


predictions.jpg

原文链接:YOLOv3: An Incremental Improvement

YOLOv3是YOLO系列的最新版本,总体来讲变化不算特别大。(I didn’t do a whole lot of research this year. Spent a lot of time on Twitter. Played around with GANs a little. ——作者如是说)但是为了完成这个项目,我还是读了一遍paper。事实上,参考文献中列出了一个非常详细YOLOv3的教程,教程中包括了一些关于YOLOv3的细节说明,并手把手教读者如何从cfg文件开始构建网络,加载模型参数并完成预测。这个教程使用PyTorch实现的,非常有参考意义。美中不足的是,这个教程只包含了detection部分,而没有告诉你如何使用PyTorch从头开始训练网络。

在这篇博客中,我依旧按照paper中的逻辑讲解YOLOv3,更多的细节可以在后面的文章中看到。

YOLOv3

预测Bounding Box

在YOLOv3中,我们依旧使用类似于YOLOv2的方法预测bounding box,对于每个bounding box,给出四个参数\(t_x,t_y,t_w,t_h\),然后通过变换

$$\begin{align}
b_x&=\sigma(t_x)+c_x \\
b_y&=\sigma(t_y)+c_y \\
b_w&=p_we^{t_w} \\
b_h&=p_he^{t_h} \\
\end{align}$$

就可以得到bounding box的真实值。关于参数的解释,详见上一篇文章,这里不再解释。

在训练中,我们使用误差的平方和作为loss。如果真实值是\(\hat{t_{*}}\)而预测值是\(t_{*}\),那么误差就是\((\hat{t_{*}} -t_{*}\)。需要额外说明的是和bounding box有关的第五个参数\(t_o\),在YOLOv3中这个参数依旧表示置信值\(Pr(\text{Object})*\text{IOU}(b, \text{Object}))\)。\(t_o\)的真实值\(\hat{t}_o\)是这样定义的:如果这个\(t_o\)对应的bounding box prior和某个物体的真实的bounding box重叠的比例高于其他bounding box prior,那么\(\hat{t}_o=1\),我们称之为这个bounding box prior分配给某个物体的真实值。(注:按照我的理解,bounding box prior和真实的bounding box的重叠比例是这么得到的。和YOLOv2相同,YOLOv3中的每个prediction都对应于一个anchor,这个anchor将用于计算bounding box的宽度和高度。bounding box prior以anchor作为宽度和高度,以真实bounding box的中心作为中心,然后就可以求bounding box prior和真实bounding box的重叠比例了。)

在计算loss的时候,如果一个预测和某个真实物体的bounding box重叠超过某个threshold(0.5),那么我们忽略掉这个预测的loss。如果一个预测和某个真实物体的bounding box prior没有被分配给某个物体的真实值,那么我们只将置信值\(t_o\)的误差加入loss,而忽略掉分类误差和定位误差。

预测分类

考虑到使用softmax作为分类器时,我们假设了每个bounding box只包含一个类别的物体,而实际上每个bounding box可能包含多个类别的物体,因此我们对每个类别使用独立的逻辑分类器。我们使用binary cross-entropy loss作为预测分类的loss。

基于不同尺度的预测

YOLOv3依旧使用一组anchor来辅助不同尺度的预测,不同之处在于,YOLOv3使用了9个anchor,将这9个anchor分成3组看作3个不同的尺度。在每个预测层中,我们只使用其中一组3个anchor,相应的每个格子给出3个预测bounding box。

我们按照如下方法使用这3个不同的尺度。首先在基础特征提取器上,我们连接几个卷积层,然后接第一个尺度的预测层。然后,取出之前两层的特征图升采样两倍,再和之前某一层的特征图接起来,再通过几个卷积层,接入下一个尺度的预测层。以此类推。

特征提取

YOLOv3融合了YOLOv2中Darknet-19和ResNet的想法,提出了新的特征提取器,如图。


Darknet-53

效果


不同model比较

无效的尝试

作者在文中还提到了一些尝试过但效果不好的做法:

  • 使用anchor box预测机制来预测anchor box的x和y偏移。
  • 使用linear activation作为x和y预测的激励而不是使用logistic activation。
  • 使用focal loss。
  • 使用双IOU threshold。当一个预测和某个真实值的threshold大于0.7或小于0.3时,考虑这个预测中\(t_o\)的误差。这个操作会使得mAP降低。

原文链接:YOLO9000: Better, Faster,Stronger

YOLO9000是YOLO系列的第二版,在后面都是用YOLOv2表示。在YOLO的基础上,YOLOv2提出了诸多改进,核心就是让YOLO更快、更准确。YOLOv2中的模型YOLO9000号称可以检测9000个类别,并且可以支持一些类别没有detection label的情况下,来进行detection的工作,当然这部分的准确率还不算太高。

在这篇博客里,我将按照论文里的逻辑,分成Better、Faster和Stronger三个部分来描述论文里提出的改进。

Better

YOLOv2和YOLOv1相比,准确率得到了一些提升。在YOLOv1中比较显著的localization error较高以及recall低的问题,在YOLOv2中都得到了一些改善。为了使模型准确率更高,YOLOv2做出了以下改变:

  • Batch Normalization:YOLOv2在所有卷积层后面加了batch normalization,并删除了原来的dropout层,这使得mAP有2%的提升。

  • High Resolution Classifier:在YOLOv2中,使用了448*448的分辨率。训练时,首先在ImageNet上对分类网络finetune 10个epoch,然后再finetune detection部分。这个操作使得mAP上涨4个点。

  • Anchor Boxes and Prediction:YOLOv1中使用全连接层来直接预测bounding box,但在YOLOv2中,我们改用anchor boxes来预测bounding box。

    如果我们打开yolo的代码仓库,找到里面的.cfg文件,可以看到在网络配置里有这么一行(以yolov2-tiny.cfg为例)。

    1
    anchors =  0.57273, 0.677385, 1.87446, 2.06253, 3.33843, 5.47434, 7.88282, 3.52778, 9.77052, 9.16828

    这就是yolov2-tiny网络中使用的anchor,这其中每两个为一对,分别对应于x方向和y方向,上面这行显示的就是5个anchor。

    那么这些anchor是怎么生成的呢?首先我们拿到训练数据里所有bounding box的宽度和高度\((w,h)\),并假设这些bounding box的中心都在同一个点上,这样我们就得到了一组以同一个点为中心的长方形,对这些\((w,h)\)做k-means clustering,就可以得到\(k\)个聚类中心,也就是我们要的\(k\)个anchor,在实验中,我们发现\(k=5\)时的效果最好。为了更符合我们的任务,做k-means clustering时不使用欧几里得距离作为距离的衡量,而是使用IOU的大小作为距离的衡量,也即:
    $$d(\text{box},\text{centroid})=1-\text{IOU}(\text{box},\text{centroid})$$

    现在,我们获得了所有的anchor,接下来,我们需要调整网络的输出。


    Bounding Box

    和YOLOv1一样,如果输入图像分辨率为416*416,我们可以将输入图像划分为13*13个格子,其中每个格子大小为32*32,这样我们就会输出一个13*13的feature map,其中feature map的每一维也是一个向量,对应于中心点落在这个格子里的那些bounding box。在YOLOv2中,我们在每个格子里预测出5个bounding box,也就是五组中心坐标\((b_x,b_y)\),宽度\(b_w\),高度\(b_h\)以及置信值\(Pr(\text{Object})*\text{IOU}(b, \text{Object}))\)。值得注意的是,YOLOv2中不在直接输出这5个数,而是输出变化前的5个值\(t_x,t_y,t_w,t_h,t_o\),然后经过变换

    $$\begin{align}
    b_x&=\sigma(t_x)+c_x \\
    b_y&=\sigma(t_y)+c_y \\
    b_w&=p_we^{t_w} \\
    b_h&=p_he^{t_h} \\
    Pr(\text{Object})*\text{IOU}(b, \text{Object}))&=\sigma(t_o)
    \end{align}$$

    就可以得到\(b_x,b_y,b_w,b_h,Pr(\text{Object})*\text{IOU}(b, \text{Object}))\)。其中\(\sigma(x)\)表示Sigmoid函数,\((c_x,c_y)\)是这个格子左上角的坐标,\(p_w,p_h\)就是我们直接求出的anchor,5个bounding box正好对应于5个不同的anchor。

  • Fine-Grained Features:修改后的YOLOv2在13*13的特征图上进行预测,但是这样的分辨率可能和对于小尺度不利。Faster R-CNN和SSD在不同的特征图上跑proposal network来获得不同的分辨率,但在YOLOv2里,我们直接使用一个passthrough layer将前面层里的26*26分辨率的特征图和低分辨率的特征图用类似于ResNet中连Identity Mapping的方法连起来(我们把26*26*512特征图转换成13*13*2048,然后就可以和13*13的特征图连起来)。然后我们把这个特征图上做检测。这可以使performance提高1%。

  • Multi-Scale Training:YOLOv2把416*416作为输入的分辨率,但是由于网络里只有卷积核和max pooling,因此希望YOLOv2在应对不同分辨率的输入时有鲁棒性。我们可以在训练里不固定输入的大小,而是每10个epoch就随机选取另一个大小的图像,由于我们在网络里降采样的倍数是32,所以我们让所有的分辨率都是32的倍数({320; 352; …; 608}),这样网络就需要不停的学习预测不同分辨率的图像。

    在低分辨率的图像中,YOLOv2可以达到90FPS的速度,mAP和Fast R-CNN差不多。在高分辨率的图像中,YOLOv2仍可以达到实时的速度,78.6mAP。

Faster

YOLOv2没有使用VGG-16作为baseline,而是构建了一个新网络Darknet-19,这个网络包含19和卷积层和5个maxpooling,结构如下:


Darknet-19

Stronger

YOLOv2使用一种机制,可以同时训练classification和detection两部分的数据。细节还没有研究清楚。

原文链接:You Only Look Once: Unified, Real-Time Object Detection

机器人小学期做了一个奇怪的项目,大概是用v-rep这个平台仿真了一个简化版植物大战僵尸(当然,除了有植物有僵尸并且植物会打僵尸,僵尸试图吃点植物外没有什么相同之处)。事实证明使用一个机器人仿真平台来搞游戏虚拟器还是非常灾难的。

我在项目里负责目标检测部分,简单来说,就是给植物装了一个视觉传感器,我要使用视觉传感器传回来的图像,检测出图像中每个僵尸的bounding box。由于对检测速度要求比较高,因此就使用了YOLO这个网络。

YOLO已经有了三版(v1、v2、v3)。每一个版本在上一版本的基础上做出了一些修改,使得速度更快,效果更好。本文只包括YOLOv1,也就是最早的一版,和最新的YOLOv3相比,还是有不少差异的。

YOLOv1

回忆R-CNN的做法,R-CNN首先使用regional proposal方法选出一些可能的bounding box,然后对这些bounding box做分类,最后再对这些bounding box做筛选、微调。R-CNN把object detection这个任务分成了几个部分,包括选择bounding box和分类等,每部分都由一个专门的网络负责。这样带来的问题是,速度慢并且很难优化。因此YOLO针对这个问题作出了改进:在YOLO里,我们将整张图片作为输入,获得bounding box和分类都在同一个网络中完成(look once),也就是说我们把一整个detection部分看成了一个回归问题。这个做法带来的好处包括:

  • 速度快:Titan X上可以做到45fps,fast version可以做到150fps。
  • 直接使用整张图片使得我们可以获得更多的全局信息,相比于Fast R-CNN,YOLO的背景识别错误更少。
  • 泛化能力更强。

网络

结构

YOLOv1有两种网络,一个是24个卷积层的YOLO,还有一个简化版的只有9个卷积层的Fast YOLO。下面以24个卷积层的版本为例。

Network

网络包含24个卷积层和2个全连接层,通过卷积层提取特征,然后用全连接层预测概率和坐标。

在具体实现中,除了最后一层的激活函数为ReLU外,其他各层的激活函数均为leaky ReLU,定义如下:

$$\phi(x)=
\begin{cases}
x, &\text{if $x>0$} \\
0.1x, &\text{otherwise} \
\end{cases}$$

输入和输出

网络将整个图片作为输入,最后输出一个大小为\(S*S*(B*5+C)\)(注意Tensor大小与v3有所不同)的Tensor。

我们对于这个输出Tensor做一点解释。我们将输入图像划分成\(S*S\)的网格,对于网格中的每个格子,我们要预测\(B\)个中心落在这个格子里的bounding box。每个bounding box有5个参数,分别是中点的\(x\)、\(y\)坐标以及宽度\(w\)和高度\(h\),此外,还包含一个置信值\(Pr(Object)*IOU_{pred}^{truth}\)表示这个bounding box里包含一个物体的概率乘上它和真实值的IOU。最后,对于每个格子,我们还要预测\(C\)个参数,其中第\(i\)个参数\(Pr(C_i|Object)\)表示在格子里有物体的情况下,这个物体属于第\(i\)类的概率。

根据置信值和属于各类别的概率,我们可以求出各类别的置信值:

$$Pr(Class_i|Object)*Pr(Object)*IOU_{pred}^{truth}$$

额外说明一点,\(x\)、\(y\)是相对这个格子的坐标,并且归一化到了\([0,1]\)区间;宽度\(w\)和高度\(h\)是相对于正常图片的,也归一化到了\([0,1]\)区间。


输出

损失函数

最后,我们还需要定义损失函数。

为了便于优化,我们使用误差的平方和作为损失函数,但是这会带来两方面的问题:

  • 定位错误和分类错误同等对待。
  • 在一张图片中往往有很多格子是不包含物体的,这写格子的置信值会逐渐趋于0,这会淹没那些包含物体的格子的误差。(置信值confidence score为为\(Pr(Object)*IOU_{pred}^{truth}\)。)
  • 不同尺寸的物体在计算错误时也被平等对待了。但是很明显,相同的bounding box的误差出现在预测尺寸较大的物体可能并不显眼,但是如果出现在预测尺寸较小的物体上时就会很严重。

为了解决前两个问题,我们赋予定位错误和预测是否包含物体的错误不同的权重,其中定位错误的权重\(\lambda_{coord}=5\),预测是否包含物体的错误的权重\(\lambda_{noobj}\)。

为了解决第三个问题,我们不直接使用bounding box的高度\(w\)和宽度\(h\)计算,而是使用它们开根号后计算误差。

最后,我们得到一个形如下式的损失函数,

$$\begin{align} &\lambda_{coord}\sum_{i=0}^{S^2}\sum_{j=1}^{B}\mathbb{1}_{ij}^{obj}[(x_i-\hat{x}_i)^2+(y_i-\hat{y}_i)^2] \\ +&\lambda_{coord}\sum_{i=0}^{S^2}\sum_{j=1}^{B}\mathbb{1}_{ij}^{obj}[(\sqrt{w_i}-\sqrt{\hat{w}_i})^2+(\sqrt{h_i}-\sqrt{\hat{h}_i})^2] \\ +&\sum_{i=0}^{S^2}\sum_{j=1}^{B}\mathbb{1}_{ij}^{obj}(C_i-\hat{C}_i)^2+\lambda_{noobj}\sum_{i=0}^{S^2}\sum_{j=1}^{B}\mathbb{1}_{ij}^{obj}(C_i-\hat{C}_i)^2 \\ +&\sum_{i=1}^{S^2}\mathbb{1}_{i}^{obj}\sum_{c\in classes}(p_i(c)-\hat{p}_i(c))^2 \end{align}$$

\(\mathbb{1}_{ij}^{obj}\)表示第\(i\)个格子里的第\(j\)个预测“负责”预测这个物体,\(\mathbb{1}_i^{obj}\)表示物体\(obj\)出现在第\(i\)个格子里。(“负责”预测这个物体是指这个预测的bounding box和某个真实值的IOU是所有预测中最大的,IOU为Intersection of Units,即两个bounding box面积的交/并。)

Summary

  • 小学期
    • 小学期还剩下最后两周。
    • 这周生成了所有数据,把yolo的官方代码跑起来了,并训练出了一个可以使用的模型。目前正在拿pytorch重新实现yolo。
      一个充满戾气的license
  • 实验室
    • 开始跑了代码,效果大概还是和假设不符,现在应该快跑完了。
    • 重新读了ResNet和Inception系列,发现之前还是漏了些东西没看。读了Xception,补上了之前不懂的depth-wise convolution。
  • 去CMU相关事宜
    • 被之前的房东鸽了,于是重新找了个住处。
    • 我爸帮我买完了机票。
  • 休息
    • 和sll同学进行了一次愉快的玩耍。我们去了一家日料店,满足心愿吃到了河豚。河豚的口感和想象很不一样,有点脆。店里提供的河豚蘸料是柠檬酱油,吃不习惯,所以最后还是蘸了普通酱油。随后去看了我不是药神,这是最近看的最好的国内电影了。
      吃河豚first try
    • 周五中午和ssh去五道口吃了鱼水饺,味道还是不错的,而且很山东的,很亲切。
      鱼水饺
    • 看完了Good Luck Charlie。
    • 由于这周下雨比较多,只完成了一次游泳和一次跑步。
    • 用在路上的时间把《禅与摩托车维修艺术》看到了第二部分。摘一段很有趣的观点。
      《禅与摩托车维修艺术》节选

Todo List

  • 小学期
    • 把自己写的yolo调出来。
  • 实验室
    • 先把结果向老师汇报一下,然后思考一下下一步怎么做比较好。
  • 去CMU相关事宜
    • 订家具,主要是床。
    • 收拾行李。
  • 休息
    • 打算继续阅读《禅与摩托车维修艺术》。

这次是真正意义上的周总结了,因为写完这个就要洗漱睡觉了。这周在机房的时间效率还可以,但是在寝室基本什么正事都没干。所以希望下周的效率能再高一点。

我决定开始认真地减肥和生活,首先从加快每件事的实现速率、减少花在社交网络的时间、提高学习时间、规律运动以及提高洗衣服频率做起。

Summary

  • 小学期进入到第二周了,还剩三周。
  • 上手了v-rep,并完成了给物体生成bounding box,接下来生成数据集应该比较轻松,但是由于暂时用不到先不生成了。
  • 去沈阳办完了签证,直接通过了,没有被check很开心。
  • 查了成绩,没有到理想成绩,不过这是我成绩最好的一个学期了,不过again依旧没办法弥补我上学期屎一样的GPA。(科技史期末论文写了7000+字的ImageNet然后拿了A,很妙。感谢ImageNet!感谢Fei-fei Li!)
  • 看完了切尔诺贝利·禁区。脑洞很大,剧情还说得过去,男女主很好看。去沈阳途中开始看Good Luck Charlie,目前看完了第一季,正在看第二季,我发现这部剧对我的听力和口语有轻微的帮助,而且十分无脑搞笑,适合我。

Todo List

  • 订机票和租房(Important!)并开始准备出发的行李,目前毫无头绪。
  • De一个大概和v-rep这个软件有点关系的诡异初速度bug。
  • 读YOLO系列,并完成Object Detection的网络。
  • 完成实验室的若干猜想的实验,并重新过一下一些知名网络。
  • 大概最近有一次高中聚餐。
  • 看完Good Luck Charlie,太有趣了。
  • 想去看我不是药神,还没有抽出时间来。
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×