for epoch in range(40):
for i, data in enumerate(dataloader, 0):
inputs, labels = data
optimizer.zero_grad()
outputs = net(inputs)
cost = loss(outputs, labels)
cost.backward()
optimizer.step()
all_train_iter=all_train_iter+10
all_train_iters.append(all_train_iter)
all_train_costs.append(cost.item())
if i % 6 == 0:
print('[%d, %5d] loss: %.3f' % (epoch + 1, i + 1, cost.item()))
今年的ICRA比赛的一大亮点是新增了哨岗,使用单目相机识别场地中的机器人,提供小地图。传统的目标检测算法都是预测出能框住目标的矩形框(如下图所示),但是用在机器人定位上不太合适,误差太大。
真正符合需求的是目标的6D姿态检测,比较出名的就是
SSD-6D
和YOLO-6D
(毕竟目标检测领域就它俩最快),但其实这个快是在有给力的GPU的前提下的,我试了一下用我的破电脑的CPU跑yolo-tiny,竟然要2S一帧,人家的GPU效果是145fps,差距未免太大。上图便是6D的目标检测效果,大致思路是物体中心的栅格负责检测物体的八个角点,但其实我的需求只需要检测地面的四个角点,所以我的模型还可以简化。
另外YOLO的网络太过于庞大,它不仅要检测目标,还要目标分类,支持上千种物体,我完全用不上,我只有一个类,所以我决定自己设计网络结构。
网络结构
我设计的网络结构如下:
采用的FCN全卷积神经网络结构,因此预测的时候支持任意分辨率输入。
训练时的输入我将缩放至416*416,外加有3个颜色通道,因此输入维度为3x416x416,层与层之间我加入了一个大小为3,步长为1,paddind为1的卷积层和一个大小为2,步长为2的卷积层,实现了卷积和下采样操作,最后得到一个48x6x6的输出。
最后一层是预测层,我删去了类别,同时增加了坐标输出,因为有4个角点,所以一共需要八个输出,另外还有一个置信度,表示的是当前栅格为底面中心点的概率,所以一共是九个维度的输出。
从上面的分析可以看出,我把图像分成了6x6的小格子,每个格子负责判断自己是否是底面中心,并预测出四个角点的位置,最后选取置信度较高的格子预测出的角点位置作为最终的角点位置。
需要注意的是,最后的网络层输出需要使用sigmoid函数将其输出限制在0-1之间,因此预测出的角点坐标和真实的图像坐标之间还有一个公式换算,我设计的换算公式如下图所示:
其中
img_scale
是卷积后图片缩小的比例,在我这里就是416/6。损失函数
损失函数分为两部分,首先要根据图片的标签得出哪个栅格是底盘的中心。如果栅格不是底盘的中心,那么该栅格的损失函数为置信度的平方,既需要使其置信度越低越好,如果是底盘的中心,那么该栅格的损失函数为(1-置信度)^2和四个预测角点与真实角点的距离的平方和,既要使该栅格的置信度越接近1越好,角点越接近真实值越好。
公式如下:
这里为了让损失函数快速收敛,我在每一层网络中还加入了了BatchNorm层,使输出正则化。
代码实现
我使用
pytorch
来搭建网络,之前想使用百度的paddle paddle(飞浆),毕竟有免费GPU白嫖,然而捣鼓一番发现不好用,特别是自定义损失函数很不好用,又回到pytorch才发现,真香!定义网络结构
定义数据读取函数
定义绘图函数用于可视化
定义损失函数及优化方法
开始训练
绘制损失函数曲线并保存模型
我的损失函数曲线如下,还是非常好看的:
使用模型预测
检测结果如下:
最后实测416x416的图片检测在我的垃圾电脑的CPU上也能达到20fps,还是不错的。
完