本文为 AI 研习社编译的技术博客,原标题 :
Visualization of Deep Learning Feature Maps in Mini Autonomous Vehicles
作者 |* Nelson Fernandez*
翻译 | 刘刘1126
校对、审核 | 酱番梨 整理 | Pita
原文链接:
几个月前,我们开始制造Axionaut,一种小型自动无线电控制(RC)汽车,并且在巴黎参加了一些比赛。到目前为止一切顺利,我们已经取得了不错的成绩。尽管如此,大家总是对控制汽车的卷积神经网络内部发生了什么感到好奇。
我们看到一些关于如何显示特征映射和过滤器方面很好的文章,它们在理解和编写神经网络特征映射时非常有用。我们还看到了一些很酷的Nvidia视频,视频展示了自动驾驶汽车上神经网络实时激活的样子(但是,它们是怎么做到的呢?)。
所以我们决定从头开始,尝试在原型中复制这种体验。为此,我们使用了预先训练过的Keras Convnet自动驾驶模型,还有一些在训练和比赛时拍摄的视频。
有了这个良好的开端,我们花了几天时间来寻找一些经典问题的答案,比如“网络是如何看待世界的”和“网络实际上到底关注什么”。
我们把体验的结果显示在这里,请用科学的方法访问:
实现
如果你对我们是如何做到这一点感到好奇的话,首先你需要了解卷积层的特征映射在检测视野内相关特征时是如何激活的。这方面Harsh Pokharna的某篇文章做了很好的解释。
在这种情况下,我们的汽车变成了一个检测车道的“专家
就像人一样,在不考虑其他因素(其他车辆、路标、行人或目的地)的情况下,车道会给我们提供如何做出正确决策的相关信息。我们应该向左转?向右转?继续直行?
好吧,让我们回到正题。我们需要做的第一件事是找到感兴趣的卷积层,并且绘制需要激活的热图。为此,我们使用了这个庞大的存储库的一个稍微修改过的版本。
完全重建一次激活意味着要考虑到这里提到的“深”卷积层的作用。
为了简化,我们要估算出单个卷积层的激活量,这里使用三次样条插值法,而不是逆卷积。在对网络上所有特征映射进行可视化检测后,我们选择了第二个卷积层。
def pre_process(img):
""" Prepare image to be feed to a Keras convolutive model.
img: 3-channel Numpy array image of size.
Returns a resized and smoothed 250x70 RGB image.
"""
# Crop relevant area from input image
img = crop(img,600,1080,0,1800)
# Resize to model's input size (250,70)
img = cv2.resize(img,(250,70), interpolation = cv2.INTER_LANCZOS4)#INTER_CUBIC)
# Smooth image with mean filter
kernel = np.ones((2,2),np.float32)/4
img = cv2.filter2D(img,-1,kernel)
return img
# Save input image ROI
cropped_img = crop(imgs[0],600,1080,0,1800)
# Transform original image to model's input size (250, 70, 3)
dst = pre_process(imgs[0])
# Get desired feature map (2nd convolutive layer)
layer_name = model.layers[6].name
# Input image to Keras model. Don't forget the mini-batch dimension
activations = get_activations(model,np.expand_dims(dst, axis=0),
print_shape_only=True,
layer_name=layer_name)
# Normalize heatmap to pixels from (0 - 255)
target_map = norm_pixels(activations[0][0][:,:,1])
我们将结果显示在这里:
输入的图像 第二层卷积的特征映射在这一点上,很明显网络主要是在响应车道。下一步是将原始输入图像与激活重叠,以一种不损坏原始图像形状和颜色的方式将响应高的区域清晰地叠加在一起。
这里要用到OpenCV!首先创建一个二进制掩码,用来分割出激活最高的部分,同时棑除掉其他的。由于激活映射的区域较小,我们还需要向上采样。然后,使用按位运算获得最终合并的图像。
最先要做的按位运算是将二进制掩码和激活映射进行“与”运算。该操作可以用OpenCV轻松实现,并且分割出映射里激活最高的部分。
# Get numpy array from matplotlib figure
target_map = get_figure(target_map, cmap='inferno').astype('uint8')
# Crop unnecessary regions from canvas
target_map = crop(target_map,9,63,33,232)
# Resize to original image size
target_map = cv2.resize(target_map,(1800,480), interpolation = cv2.INTER_CUBIC)
# Create a binary mask. You may want to change the binary threshold
img2gray = cv2.cvtColor(target_map,cv2.COLOR_BGR2GRAY)
ret, mask = cv2.threshold(img2gray, 80, 255, cv2.THRESH_BINARY)
# Apply mask to activation map
dst_map = cv2.bitwise_and(target_map, target_map, mask=mask)
二进制掩码
按位运算中掩码和特征向量的“与”运算
就像预期的那样,我们最终得到了一条完全由卷积神经网络切割出来的、清晰的车道。
现在我猜你已经想到了,为了要完成最终的图像,我们需要做的第二步按位运算:与运算。图像中的冷蓝色是由于Matplotlib (RGB)和OpenCV (BGR)颜色格式不同导致的。你可以捣腾这个会变色的色图来得到不同的颜色。
# Convert RGB to BGR. (optional)
dst_map = cv2.cvtColor(dst_map, cv2.COLOR_RGB2BGR)
# Add feature map and original image
merged_img = cv2.add(dst_map, cropped_img)
就这样,我们在对网络激活做合理估算的基础上,将输入图像与特征映射做最后整合。
image最终整合后的图像
现在,让我们用一段视频来呈现这个结果吧。
# Create video writer
writer = cv2.VideoWriter("sample_output.avi",
cv2.VideoWriter_fourcc(*"MJPG"),
30,(1800,480))
for frame in tqdm(merged_maps):
writer.write(frame.astype('uint8'))
writer.release()
想要继续查看该篇文章相关链接和参考文献?
长按链接点击打开或点击迷你自动驾驶汽车深度学习特征映射的可视化:
https://ai.yanxishe.com/page/TextTranslation/1206
AI研习社每日更新精彩内容,观看更多精彩内容:
CVPR 2018 最牛逼的十篇论文
深度学习目标检测算法综述
在2018年用“笨办法”学数据科学体会分享
等你来译:
如何在神经NLP处理中引用语义结构
(Python)用Mask R-CNN检测空闲车位
高级DQNs:利用深度强化学习玩吃豆人游戏
深度强化学习新趋势:谷歌如何把好奇心引入强化学习智能体
扫描二维码即可开始答题
网友评论