如果还是嫌数据集太小,下面是透明背景的四轴飞行器素材图,可以放到显卡上动态生成训练样例。
//编辑:请往下翻,已经准备了视频素材。
//编辑:本文原标题为《四轴飞行器Training Examples》。
//编辑:tensorflow在windows上安装方法请往下翻。
//编辑:请往下翻,更新了数据集。
//编辑:请往更下翻,已经应用了新方案。
要训练就要有样例,没有样例就要自己造。
用Image Magick加一点点js脚本生成,一共1000张。
csv文件里面记录了每张图的文件名,以及飞行器在图中的xy坐标(x向右,y向下,原点在图像中央),以及飞行器在图中的相对尺寸。
打算做End-to-end,用来解决毕业论文(以及一个学校项目)。
[修改于 8年0个月前 - 2016/12/12 16:06:41]
很快我发现128x128的patch在cpu上慢得无法接受。对各项参数进行调整之后,我生成了新的训练集。
新训练集是64x64黑白图像,其中50%的图像有无人机,50%的图像没有。在训练“图像中有没有无人机”分类器的过程中,我发现如果这个比例不是50%,比如说有无人机的图像占70%,会导致模型朝“所有图片中都有无人机”的方向收敛,因为预测“全都有无人机”的error(0.3)低于随机预测的error(0.5)。
图像背景大多为天空,少部分为景物。
训练采用tensorflow后端 + keras前端,小规模的CNN,图像上下左右翻转作为数据增强,目前训练集(2500)和测试集(500)的accuracy都超过了0.83。肉眼识别率还没做,但是可以感受一下。
图中灰色的表示判断正确,红色的表示判断错误。从训练结果来看,天空背景下准确率较高,花草树木背景下准确率较低。
下面是所用的网络结构(keras):
<code class="language-python">model = Sequential() model.add(Convolution2D(16, 7, 7, #border_mode='same', input_shape=X_train.shape[1:])) model.add(Activation('relu')) model.add(Convolution2D(32, 5, 5, #border_mode='same', input_shape=X_train.shape[1:])) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Dropout(0.25)) model.add(Convolution2D(32, 7, 7)) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Dropout(0.25)) model.add(Convolution2D(64, 7, 7)) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(4, 4))) model.add(Dropout(0.5)) model.add(Flatten()) model.add(Dense(16)) model.add(Activation('relu')) model.add(Dropout(0.1)) model.add(Dense(nb_classes)) model.add(Activation('softmax')) </code>
如果你认真地读完了我上面写的帖子,我目前最新的总结是:classifier可以当detector用,但这不是最优方案。
对网络结构进行一些调整之后,我用1000个epoch、100/batch 把这个数据集跑到了0.9的准确率(期间验证了一点,SGDR确实是可以加速收敛的),考虑到数据集中存在的大量bug(比如全黑背景+全黑的无人机),这个准确率已经很棒了。
但是也遇到一些问题。数据集中用来生成背景的素材图,干扰主要是建筑物,从conv1的feature来看,网络已经学会了怎么从建筑物外墙棋盘般的背景中区分出无人机:
问题是这样效率比较低,比如我要丰富dataset,引入更多类型的背景,那么conv1的feature可能就不够用了,而加大conv1的后果就是……慢。尤其是,人眼在跟踪飞行器的时候,是处于一个在线学习的状态,即便是以前没有见过的无人机,看到在飞之后,就可以学习其外观并保持跟踪;即便是以前没有见过的背景,也应该可以实时适应,在视觉上能够积极地将无人机与背景进行区分(利用相对运动信息)。现在把feature全都做成死的、全都依赖训练数据,在实际环境中工作起来和人脑性能会差很远。所以要设计一个robust的tracker,很可能需要 cnn + lstm + very large real-world dataset,并按照抽象层次进行分阶段训练(从静态图片逐渐过渡到动态、从二维图像过渡到三维场景分析,而不是直接end-to-end)。将来会考虑用blender或者OSG生成这样的dataset。但在这之前,还是先认真刷论文吧。
下图:经过训练的binary classifier,可以通过sliding window的笨方法,在图中大致标记出无人机的所在。cnn输入窗口的宽度是64,因此我每32个像素predict一次,保证窗口之间的重叠,而且比逐个像素slide要快1000倍。图中每个命中的64x64窗口,用一个56x56矩形进行了标记。
(请注意:每个小方格是32x32,predict window是64x64,每滑动32像素predict一次。框线是56x56.)
这其实是这两年ILSVRC做bounding box的主要方法:首先训练一个分类器网络(经过这3年的磨练,大家已经都学会了),然后用滑动窗口方法对图像应用这个分类器(然后对输出还要加一点SVM之类的魔法),以确定object在图中的位置。它们(比如R-CNN及其变种)的特点就是blackbox,naive,simple code,very slow。为了提速,大家提出了很多修修补补又三年的方法,不过始终都显得……不太美观,经常是一堆tricks然后结尾必提我们用多少个百分点的误差换了多少倍的速度……
结果就生产了一大堆大家都很清楚两年之后一定会变成废纸的文章,有点像当年手机厂商张口闭口都是跑分的情况。R-CNN minus R XXXXXXXXXXXXXXXXX/abs/1506.06981 特别阐述了这一点。值得一提的是,他说:
对RPN的态度是Removing。
He Kaiming同学也一连发了几篇文章,表达对R-CNN(及其变体)效果的赞赏以及方法的不敢恭维,在此就不列出了。刚刚读了他的 R-FCN: Object Detection via Region-based Fully Convolutional Networks XXXXXXXXXXXXXXXXX/abs/1605.06409 ,其中提到:
意思就是说,我们做图像分类的那个架构,是希望它对位移不敏感,所以越深、对位移越不敏感的卷积网络效果就越好。而做检测刚好相反,我们需要的结果恰好是对位移敏感的,所以用分类的那个架构来对付检测,就像用扫把洗碗,降低了比赛的观赏性质,对围观群众造成了brain damage。
一个美观优雅高速高效的detector究竟要怎么实现、怎么训练;作为最终产品的tracker要怎么利用detector的训练成果;这些都是接下来需要我去阅读思考的问题。
总结:Zhu Songchun说过,ML解决的仅仅是CV中的实现部分,如果不能确定到底要解决的是什么问题,再快的显卡也不管用。
引用 Cirno:确实是需要做detection,但是苦于没有比较优雅的实现,所以采用了先classifier后detector的naive方法。此处需要更多文献,非常感谢推荐。
我开始看到以为是要实现的是 detection,提取 bounding box 那种。有点想试试用 HOG SVM或者LDA做一个sliding window detector 作为baseline看……
引用 Cirno:conv1(7x7)中有很多网格状的weight,我认为它们对应的是训练样本中大量的建筑物背景。当背景为建筑物,且前景无人机尺寸较小的时候,无人机的外形会和建筑物表面的纹理混在一起(这类样本挺多的,说实话肉眼都不太好识别),要将它们有效地区分开来,cnn就必须能够检测到建筑物表面纹理的不连续,以从中分辨出无人机。
我非常奇怪一点是,为什么你表示从 conv1 的输出能看出 object level 的效果。conv1 只是提取low level的 edge response,如果在存在有复杂背景的情况下,仍然能……
前两天Google推出了windows版的tensorflow,现在大家可以在windows上玩tensorflow了。我用tensorflow的原因是它资源很丰富、背景很雄厚。用keras的原因是简单爽快可扩展,用python的原因是这个语言对tensor(高维矩阵)进行操作非常直观方便。
简单介绍一下windows版tensorflow的安装过程:
如果需要用Keras框架,不要用pip install,检测到系统为windows之后会傻傻地跑去下载theano(因为以前tensorflow是没有windows版本的)。我直接从github拖源码,然后Python XXXXXXXX就可以了。到装theano那一步我Ctrl-C了。完美的方案应该是把系统检测代码删掉,让用户自己选择是要tensorflow还是theano。
import keras的时候也会遇到问题,keras检测到windows系统(同时检测不到~/.keras/keras.json文件的话)就自动切到theano后端,由于没装theano也会报错。解决方法是手动改keras源码,我已经向作者提交了pull request(更新:作者没有通过request,有需要自己动手吧)。
CUDA的驱动程序在我这边有点问题,GT640M显卡在XXXXXssion()的一瞬间会跳出一个错误,提示我的显卡无法弹出,然后把我整个系统卡死。所以我暂时还是在用CPU训练。
FCN和R-FCN论文指出,可以直接用全卷积网络(FCN, Fully-Convolutional Network)代替传统的conv-fc架构,这样便可以适应任意尺寸的输入,并且保留了输入图像中所包含的物体位置信息,可以直接根据卷积结果生成heat map和bounding box,省去了耗时的regression步骤。
受到以上论文启发,我也修改了网络架构和训练方法。这是我目前正在训练的架构,左边是输入图像,右边是输出heat map。
这个架构有4个卷积层,其中conv1和conv2的作用是feature卷积,conv3和conv4则是代替传统架构中fc层的作用(!)。
因为使用了3x3和4x4两次pooling,最终输出的3x3heat map,覆盖的大概是原图中心36x36的范围。所以对于每个训练样本,我生成了一张36x36的heat map,将目标所在位置设为1.0,背景设为0.0 ,然后将这张map通过双线性插值,缩小到3x3,作为训练的ground truth。而训练样本中四轴飞行器的几何中心,也都落在图片中央36x36范围内。
那么,要怎么设计loss function呢?我们提供的ground truth取值范围在(0,1);而ReLU网络输出的取值范围是(0,+inf)。所以不能用mean squared error。因为架构特殊,Keras也没有提供我需要的误差函数,只好自己拼凑一个了:
<code class="language-python">def my_fancy_loss(y_true, y_pred): # y_true within (0,1 # y_pred within (0,+inf yt = tf.reshape(y_true,[-1,9]) # flatten yp = K.softmax(tf.reshape(y_pred,[-1,9])) # flatten, then softmax return 1.0 - K.mean(yp*yt, axis=-1) </code>
代码中K是Keras后端,是tf的一层wrapper。因为Keras没有wrap tf.reshape
函数,所以我直接混用了tf。
首先把3x3的预测值y_pred
和3x3的ground truthy_true
都展平成9维矢量,然后对预测值进行softmax,以将其取值范围压缩到(0,1)。然后将两个矢量逐项相乘,最后取平均,再取反。所以如果要降低误差,就要令逐项相乘的和尽量大。因为前面有softmax的存在,要让逐项相乘的和尽量大,预测值的最大值点就必须和ground truth的最大值点的位置接近,尽管最大值的绝对值可以不同。
为了本次训练,我从网上找了更多的无人机素材,生成了5000张64x64训练样例。考虑到每个人训练目的不同,这次我就不直接发生成好的dataset了,直接发透明的png素材。
不过,为什么要训练6个小时?看着loss图像下降得那么平缓,我怀疑是学习率设低了。paper里经常用的0.1学习率是针对paper里面提到的loss设置的,这次我用了自定义的loss,学习率也应该改一下再使用。
于是我把学习率从0.1改成了1,最开始那段收敛加速了10倍,可怜了我宝贵的时间。。。
提一点: 根据Yoshua Bengio教授提出的 XXXXXXXXXXXXXXXXX/abs/1301.4083,带有指导、循序渐进的学习(又称大纲学习, curriculum learning)能使误差函数的梯度相比直接端到端学习变得更加平滑,从而加速学习的过程,极大降低在学习过程中陷入局部最优的概率,对于某些问题则能够极大节省网络容量和训练时间。
我这次设计的heat map学习也体现了这一点:由于使用的误差函数与位置相关,网络很快就能搞明白需要识别的特征有哪些、在哪里,从而极大地加速收敛。这就类似小朋友用手指指着某个物体,问大人那是什么,从而有针对性地学习其特征。换句话讲,如果ImageNet的classification允许通过人工方法将所有的概念在图中指给神经网络看,我认为top-5误差完全可以降低到接近1%,而且也不用跑一遍等几天,而应该是几个小时就好。
(大多数读者)读到这里一定会感慨,这篇文章黑话太多了,根本就看不懂,谁来写个教程啊!!!!!
说实话,如果你不了解机器学习,能坚持看到这里已经很不错了。所以我决定给看到这里的同学们写点什么。
tensorflow相关教程网上很多,但是新手直接跑去看的话,很有可能会发出跟上面一模一样的抱怨,明明每个字都我都认识,可是连在一起怎么就看不懂了呢……下面我根据我的经验,推荐一个入门机器学习的路线。
学数学
最低标准:手绘正弦余弦对数指数的图像以及它们的导数和切线的图像,手绘二元函数(z=x^2+y^2)的图像以及其偏导数和切面的图像。如果做不到,找一个高中数学老师也花不了几千,毕竟比读一次大学要轻松多了
学编程
最低标准:写一个程序发给你周围的人,并从他们那里收集10个赞。如果做不到,参加一个培训班也花不了几千,毕竟比读一次大学要轻松多了
学一点数学和图形编程
最低标准:用一种编程语言,绘制1的图像。
学一点数字图像处理
最低标准:用一种编程语言,计算一张国旗的图片中有多少颗星星。
学《机器学习》
最低标准:捏着鼻子把Andrew Ng的《Machine Learning》教程看完,把他提到的实验都做了。不要借助任何框架,直接用你熟悉的编程语言写一遍。
如果你真的做到了以上几点,你就应该(自动地)开始看论文了。Yann LeCun 和 Yoshua Bengio的论文多翻翻吧。
OK,现在你能看懂大部分黑话了。
下一步就应该让模型学会从视频中区分图像的背景和前景(利用相对运动信息)。
现在的模型虽然已经能够识别静态图像中的无人机,但如果无人机的外观跟背景图案太接近,我们的模型表现就很差。但是人眼可以做到,飞机飞到树林后面我们仍然可以通过缝隙跟踪,所以计算机应该也是可以做到的。
在生活实践中我们发现,如果一只鸟停在树上,我们肉眼一般是很难注意到的,因为像树枝这样杂乱的背景,相当于把目标淹没在噪声中了。但是只要这只鸟开始飞行,即便是在树林后面穿过,我们也能注意到它,这是因为肉眼有视觉残留效应,当我们的视线快速掠过树枝跟踪这只鸟的时候,对树枝产生的噪声进行了滑动平均,令鸟的图像变得明显、清晰了。这种视觉残留效应其实是必须的,因为人眼视网膜后的神经结构类似于一个差分压缩算法,输出带宽有限,如果不作滑动平均、让噪声全部都进入大脑,就会挤占真正重要的信息所需的传输带宽,这样就会导致我们的动态视觉非常差。
所以要在复杂背景下跟踪一架无人机,我们需要:
因此这应该可以利用CNN+LSTM实现,现在关于attention based的机器学习论文其实也挺多的,总之需要大量的阅读。
论文倒是找到一篇,08年的,理论解释得非常好,但是方法都是经典方法。
我们现在知道:将物体的32x32照片分成10类(CIFAR-10),在机器学习领域属于EASY。人类大脑的视觉处理系统对物体的轮廓和细节敏感,并能够将这些轮廓和细节组成更高级的抽象概念,最终判定物体的类别。于是科学家用CNN函数拟合了大脑的这种视觉处理的过程,所以我们现在获得了非常好的分类器。
但这并不等于我们解决了机器视觉的问题。很多人眼拥有的能力,目前大家手中的神经网络还不具备。
首先,人眼的分辨率是很高的,按照这个分辨率,45度视野下接近于8k视频。那么,假如我们要用CNN方法找到8k视频图像中的一架无人机,我们就必须用卷积核对整张图片进行卷积,而我上面展示的最简单的神经网络,也差不多要对整张图卷积100次。这对计算机来说已经很辛苦了,换成人类的大脑则更是不可能的。
那人眼是怎么做到的呢?人眼虽然分辨率很高,但这个高分辨率仅限于中心非常小的一部分,而四周的分辨率是很低的。
下面是人眼视锥细胞(色觉,高分辨率)和视杆细胞(光感,低分辨率)的密度分布图。
视网膜中心的视锥细胞分布是这样的(左为正常人,右为红绿色盲):
所以当有一架无人机在飞的时候,人眼先利用四周低分辨率的视杆细胞感应到无人机的存在,然后控制眼球四周的肌肉,旋转眼球将光轴指向目标,从而获得目标的高分辨率图像,然后接着对目标再进行分类判定。
(图片都来自维基百科)
这样一来,人脑就可以用非常低的带宽,获得极高分辨率的视觉。所以未来实用的机器视觉应用,根据我的设想应该是这样的:
其次,并不是所有的视觉能力都可以后天学习。
以CIFAR-10为例,里面的很多类别是人造的物体,这些物体回到100年前是没有的,但是今天的人类可以毫无压力地学习他们的外观特征,说明对物体的外观特征的分类能力,是可以通过学习获得的,而不是天生的。但是,人类还有很多视觉能力不是通过后天学习获得的,而是天生就有的。
比如我上面一楼提到的动态视觉能力,就属于天生的。看下面这张图,你能找到两架无人机分别在哪里吗?
很困难,因为我们看的是一张静态图片。假如图像是动态的,我们就可以利用上面一楼提到的动态视觉能力。只要无人机稍微有点晃动,即使我们的视线没有盯着它看,敏感的视杆细胞也能马上感受到亮度的微小变化,从而发现无人机。等到我们把视线中心转过去,就可以利用视觉残留滤波的方法,判断那到底是不是无人机。所以要实现robust的追踪,这些问题都是要解决的。
不过这样一来,我们就得把像CNN这样的开环系统,改成多级反馈系统(类似RNN)。每一级之间如何连接,整个架构怎么训练,仍然需要继续刷论文。而且由于动态视觉能力并不是后天习得的,而是通过无数代自然选择进化留下的,因此直接套用现成的机器学习算法是浪费时间,必须先把Human Vision吃透。
本楼是给对这个问题有兴趣的读者做的一个简单介绍,也欢迎大家提出不同意见。
两架无人机分别在这里:
人眼的分辨率分布:
blender的最大优点是可以用python进行交互。
经过一番google,我写了一组脚本,输出每一帧中无人机的屏幕坐标,作为训练用的ground truth.
get_obj_coord.py
<code class="language-python"># small helper script for CV+ML trainning ground truth data generation. # by Qin Yongliang import bpy import bpy_extras # get coordinate of object relative to screen. def get_obj_coord(object_name): target = bpy.data.objects[object_name] # get object's location relative to world. target_loc = target.matrix_world.to_translation() cam = bpy.data.objects['Camera'] scene = bpy.context.scene # relative to screen, where (0,0) represents left bottom of camera frame. co2d = bpy_extras.object_utils.world_to_camera_view(scene,cam,target_loc) return co2d # get coordinate of object named 'Drone'. def get_drone_coord(): c = get_obj_coord('Drone') print('drone at screen coord:',c) return c </code>
然后运行下面的脚本:
<code class="language-python">import get_obj_coord as g import bpy scene = bpy.context.scene fs = scene.frame_start fe = scene.frame_end def get_drone_coord_list(startframe,endframe): list = [] for i in range(startframe,endframe): scene.frame_set(i) c = g.get_drone_coord() list.append(c) return list file = open("c:/coords.csv", "w") list = get_drone_coord_list(fs,fe) file.write('x,y,z\n') for c in list: file.write('{},{},{}\n'.format(c.x,c.y,c.z)) file.close() </code>
效果:
其中x和y是屏幕坐标,(0,0)是取景框左下角,(1,1)是取景框右上角。z是从相机到目标的距离。
时段 | 个数 |
---|---|
{{f.startingTime}}点 - {{f.endTime}}点 | {{f.fileCount}} |
200字以内,仅用于支线交流,主线讨论请采用回复功能。