网上变频器型号源程序可靠吗

【图文】《学习OpenCV》第8章 轮廓_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
评价文档:
《学习OpenCV》第8章 轮廓
上传于|0|0|文档简介
&&《学习OpenCV》第8章 轮廓
大小:1.36MB
登录百度文库,专享文档复制特权,财富值每天免费拿!
你可能喜欢21854人阅读
opencv(11)
图像处理(15)
图像处理经常要用到形态学操作,其中首先要获取 结构元素。包括结构元素的大小及形状。
自定义一个结构元素kernel,要声明一个Mat,然后对Mat的元素赋值;这种方法灵活但略显复杂。
OpenCV提供了一个函数getStructuringElement,可以获取常用的结构元素的形状:矩形(包括线形)、椭圆(包括圆形)及十字形。
MORPH_RECT, MORPH_ELLIPSE, MORPH_CROSS
getStructuringElement的内部并没有什么优化实现,只是封装了一下功能。其原理同样是声明一个Mat,然后求形状,指定Mat的值。
十字形为单线宽。
形态学各种功能实现,都归结为腐蚀erode与膨胀dilate的组合。所以,接口函数morphologyEx的内部实现都是调用erode与dilate函数。
erode与dilate函数的实现接口是morphOp,创建形态学滤波器,并返回滤波器引擎。然后,应用通用滤波器接口方法实现。如下,
& & Ptr&FilterEngine& f = createMorphologyFilter(op, src.type(),
& & & & kernel, anchor, borderType, borderType, borderValue );
& & f-&apply( src, dst );
& & for( int i = 1; i & i++ )
& & & & f-&apply( dst, dst );
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1110487次
积分:8308
积分:8308
排名:第2247名
原创:109篇
评论:171条
(1)(2)(1)(7)(3)(1)(1)(3)(4)(2)(1)(6)(2)(1)(2)(2)(2)(8)(5)(4)(2)(3)(4)(4)(7)(4)(9)(11)(9)Opencv 英文文档地址 :
docs.opencv.org
OpenCV (Open Source Computer Vision Library: http://opencv.org) 是一个使用 BSD 许可证的开源库,包含数百个计算机视觉算法。此文档详细的描述了 OpenCV 2.x API,这主要是 C++ API,相对于
OpenCV 1.x API 的
Opencv 英文文档地址 :
docs.opencv.org
OpenCV (Open Source Computer Vision Library: http://opencv.org) 是一个使用 BSD 许可证的开源库,包含数百个计算机视觉算法。此文档详细的描述了 OpenCV 2.x API,这主要是 C++ API,相对于
OpenCV 1.x API 的 C API。
OpenCV 使用模块化的结构,这表明其包含很多共享或者静态库。OpenCV 提供如下模块:
core - 这是一个定义了基本数据结构的紧凑模块,包含大量被其他模块调用的多维的 Mat 和其他基本函数。
imgproc - 这是一个图像处理模块,包含线性和非线性的图像过滤,几何图形转换 (大小调整, 仿射和透视扭曲, 通用的基于表的重新映射), 色彩空间转换以及直方图等等。
video - 这是一个视频分析模块,包含运动评估、背景提取以及对象跟踪算法。
calib3d - 基本的多视图几何算法,实现单个和立体的摄像头校准,对象构成的评估、立体相关算法和一些 3D 构造算法。
features2d - 突出特征检测、描述以及描述器的匹配。
objdetect - 对象检测以及预定义类的检测,例如人脸、眼睛、杯子、人物、汽车等等。
highgui - 一个易用的视频捕获、图像和视频编码接口,同时提供简单的图形化界面。
gpu - 来自不同 OpenCV 模块的 GPU 加速算法。
... 其他一些助手模块,例如 FLANN 和 Google 测试封装、Python 包等等。
该文档的其他章节描述了每个模块的功能。但首先,我们需要对一些贯通整个库的通用 API 熟悉。
cv 命名空间
所有 OpenCV 的类和函数被放到了 cv 这个命名空间。所以为了在代码中访问这些功能,需要使用 cv:: 说明符或者使用 指令:
#include "opencv2/core/core.hpp"
cv::Mat H = cv::findHomography(points1, points2, CV_RANSAC, 5);
#include "opencv2/core/core.hpp"
using namespace
Mat H = findHomography(points1, points2, CV_RANSAC, 5 );
当前 OpenCV 提供的一些命名可能会跟 STL 或者其他库的命名冲突,可以使用显式的指定命名空间来解决这个冲突问题,例如:
Mat a(100, 100, CV_32F);
randu(a, Scalar::all(1), Scalar::all(std::rand()));
cv::log(a, a);
a /= std::log(2.);
自动内存管理
OpenCV 的内存处理是完全自动化的。
首先 std::vector, Mat, 以及其他数据结构提供了析构函数,可在需要的时候释放底层占用内存。这意味着好比是 Mat 这个数据结构而言,析构函数并不总是会释放内存,此举是为了便于数据共享。析构函数只是减少了所关联对象的引用计数器而已。如果引用计数器数值为 0 的时候对象才会被释放,因为再没有其他结构引用到该数据。同样的,当一个 Mat 实例被拷贝,实际上并没有发送拷贝数据的操作,只是引用计数值增1来记录使用关系。当然
Mat 还提供了 Mat::clone 方法来强制进行数据拷贝。示例如下:
Mat A(1000, 1000, CV_64F);
Mat B = A;
Mat C = B.row(3);
Mat D = B.clone();
B.row(5).copyTo(C);
B.release();
C = C.clone();
你会发现 Mat 和其他基础结构的使用时很简单的。但是其他一些高级类和用户自行创建的数据类型如何呢?是否也可以实现自动的内存管理呢?对于这些来说 OpenCV 提供了 Ptr&& 模板类,类似 C++ TR1 的 std::shared_ptr . 因此使用指针来替换的方法如下:
T* ptr = new T(...);
你可以使用:
Ptr&T& ptr = new T(...);
也就是说 Ptr&T& ptr 封装了一个指向 T 实例的指针和关联该指针的引用计数,详情请看 Ptr 的详细描述。
输出数据的自动分配
OpenCV 会自动释放内存,就如同大多数时候为输出函数的参数自动分配内存一样。因此,如果一个函数有一个或者多个输入的数组 (cv::Mat 实例) 和一些输出数组,输出的数组会实现自动的内存分配和释放。输出数组的大小和类型会根据输入数组的大小和类型来自动识别。如果需要的话函数可以提供额外的参数来帮助设定输出数组的属性。
#include "cv.h"
#include "highgui.h"
using namespace
int main(int, char**)
VideoCapture cap(0);
if(!cap.isOpened()) return -1;
Mat frame,
namedWindow("edges",1);
cvtColor(frame, edges, CV_BGR2GRAY);
GaussianBlur(edges, edges, Size(7,7), 1.5, 1.5);
Canny(edges, edges, 0, 30, 3);
imshow("edges", edges);
if(waitKey(30) &= 0) break;
一旦视频捕获模块解析到视频帧以及位深度,那么帧数组会自动通过 && 操作符进行分配。数组的边界是通过 cvtColor 函数自动分配的。它跟输入的数组具有相同的大小和位深度。因为传递了 CV_BGR2GRAY 参数给色彩转换代码,因此通道数是 1。需要注意的是帧和边界只在首次执行时候分配一次,因此紧接着的所有视频帧都具有相同的分辨率。如果你以某种方式改变了视频分辨率,那么数组就会自动重新分配。
此技术的关键组件是 Mat::create 方法,需要指定数组的大小和类型。如果数组已经有指定的大小和类型了,那么该方法什么都不做。否则它会释放之前已分配的数据,如果有的话(这一部分设计到引用计数减一并判断是否为0)然后分配一个新的缓冲区以满足数据要求。大多数函数为每个输出的数组调用 Mat::create 方法来创建,因此实现了输出数据分配的自动化。
一些值得注意的例外是 cv::mixChannels 和 cv::RNG::fill ,同时还有其他的函数和方法。他们不会分配输出数组,你必须在调用之前进行分配。
作为一个计算机视觉库,OpenCV 处理大量的图像像素,这些都是使用紧缩的每通道、形式或者值范围的 8或者16位的像素。此外图像上的特定操作,例如色彩空间转换、亮度对比度调整、锐化以及其他复杂的操作 (bi-cubic, Lanczos) 会产生超出范围的值。如果你只是存储最低的 8或者16位值,这会导致视觉上的伪影,从而影响进一步的图像分析。为了解决这个问题,我们可以使用所谓的“饱和算法”。例如,为了存储操作的结果
r 到一个 8 位的图像,你可以查找 0 到 255 中最ji,can produce values out of the available range. If you just store the lowest 8 (16) bits of the result, this results in visual artifacts and may affect a further image analysis. To solve this problem, the so-called saturation arithmetics
is used. For example, to store r, the result of an operation, to an 8-bit image, you find the nearest value within the 0..255 range:
带符号的 8 位、16类型以及不带符号的类型也使用类似的规则,在整个库的 C++ 代码都使用这个语义规则。可使用 saturate_cast&& 函数来实现类似标准 C++ 的 cast 操作。下面一行代码实现了上图中的计算公式:
I.at&(y, x) = saturate_cast&(r);
因为 cv::uchar 是一个 OpenCV 8-bit 无符号整数值,因此在优化的 SIMD 代码,例如 SSE2 指令:paddusb, packuswb 等等就会被使用到。这实现了与 C++ 代码类似的相同行为。
当结果是 32位整数时,是无法用到 Saturation 饱和的(译者注:此句该做何解?)。
固定像素类型,模板使用限制
模板是 C++ 一个非常棒的特性之一,可以实现非常强大、高效以及安全的数据结构和算法。但是大量使用模板会戏剧性的增加编译时间以及代码的体积。除此之外,很难分离一个模板的接口以及相应的实现。这用来做一些基本的算法是挺好的,但是对于计算机视觉库这样可能包含数千行代码的复杂工作就不太合适。因为这个同时需要提供其他语言的支持版本,而像 Python、Java、Matlab 等编程语言并没有模板的概念,就会导致功能受限。当前的
OpenCV 实现是基于多态以及模板之上的运行时调度。这会导致运行时调度变得非常慢(例如像素访问操作)以及无法运行(泛型 Ptr&& 实现),以及可能非常不方便(saturate_cast&&()),因此当前实现使用了小的模板类、方法和函数。在当前的 OpenCV 版本中模板的使用都是受限的。
因此,对于一些可操作的基本类型来说是有一些固定的限制。也就是说,数组元素必须是如下罗列的类型中的其中一个:
8-bit 无符号整数 (uchar)
8-bit 有符号整数 (schar)
16-bit 无符号整数 (ushort)
16-bit 有符号整数 (short)
32-bit 有符号整数 (int)
32-bit 浮点数(float)
64-bit 浮点数 (double)
一组多元素的元组,但所有元素的类型必须一致,而且必须是上面几种类型。数组的元素如果是元组,相当于是多通道数组,与单通道数组也跟元组类型相反,这些元素必须是标量类型。最大的通道数是定义为 CV_CN_MAX 的常量值,当前是 512.
对于这些基本类型,OpenCV 提供了如下枚举与之对应:
enum { CV_8U=0, CV_8S=1, CV_16U=2, CV_16S=3, CV_32S=4, CV_32F=5, CV_64F=6 };
多通道 (n-channel) 类型可通过如下的选项进行指定:
CV_8UC1 ... CV_64FC4 常量 (对应 1 到 4 的通道编号)
CV_8UC(n) ... CV_64FC(n) 或者 CV_MAKETYPE(CV_8U, n) ... CV_MAKETYPE(CV_64F, n) 宏,当通道数量超过 4 或者未知时
CV_32FC1 == CV_32F, CV_32FC2 == CV_32FC(2) == CV_MAKETYPE(CV_32F, 2), 以及 CV_MAKETYPE(depth, n) == (depth&7) + ((n-1)&&3). 意思是常量类型是根据深度形成的。占用了低位 3 比特,此外通道数量减1占用下一个 log2(CV_CN_MAX) 比特.
Mat mtx(3, 3, CV_32F);
Mat cmtx(10, 1, CV_64FC2);
Mat img(Size(1920, 1080), CV_8UC3);
Mat grayscale(image.size(), CV_MAKETYPE(image.depth(), 1));
包含更复杂元素的数组没法使用 OpenCV 进行构建和处理。此外,每个函数或者方法只能处理任何可能数组类型的一个子集。通常,算法越复杂,支持的格式子集就越小。下面是这些限制的一些典型示例:
人脸识别算法只支持 8 位灰度图以及彩色图.
线性代数函数以及大多数的机器学习算法只支持浮点数数组.
基本的函数,例如 cv:add 支持所有类型.
色彩空间转换函数支持 8位无符号、16位无符号以及32位浮点数类型.
每个函数所支持的类型的子集都是为了满足实际的需要,并且可根据用户的需求在将来进行扩展。
InputArray 和 OutputArray
很多 OpenCV 函数密集的处理2维以及多维的数组,例如使用 cpp:class:Mat 作为参数的函数,但是在某些情况下使用 std::vector&& 或者 Matx&& 更方便(例如一个点阵集合)。为了避免 API 中很多重复的代码,OpenCV 专门引入了一个 “proxy”类。一个基本的 “proxy”类就是 InputArray. 它被用来传递只读数组。而 InputArray
的派生类 OutputArray 用来在函数中指定输出数组。一般情况下你不需要关心这些中间类型(你也不能显式的定义这种类型的变量),它们都是自动被处理的。你可以假设在使用 Mat、std::vector&&、Matx&&、Vec&& 以及一些标量类型的时候会自动替换成 InputArray/OutputArray 。当一个函数包含一个可选的输入和输出数组时,你没有这样的参数也不想要有,可以传递 cv::noArray() 。
OpenCV 使用异常来表示关键错误。当输入的数据包含正确的格式以及属于指定的值范围,但是算法因为某种原因无法正确处理时就会返回特定的错误码(一般是一个布尔值变量)。
这些异常可以是 cv::Exception 类或者派生类的实例。此外 cv::Exception 是 std::exception 的派生类。因此可以使用标准的 C++ 库组件来处理这些异常。
异常是通过 CV_Error(errcode, description) 宏抛出来的,或者可以使用类 printf 函数风格的方法变种 CV_Error_(errcode, printf-spec, (printf-args)) ,或者使用断言宏 CV_Assert(condition) 检查各种条件,并在不满足的情况下抛出异常。如果你对性能非常在意的话,可以使用 CV_DbgAssert(condition)
方法,该方法只在 Debug 模式下有效。因为自动内存管理的原因,所有在发生错误时所产生的中间缓冲区会被自动的释放。你只需要在需要的时候添加 try 语句和 catch 异常即可。
catch( cv::Exception& e )
const char* err_msg = e.what();
std::cout && "exception caught: " && err_msg && std::endl;
多线程和可重入
当前的 OpenCV 版本是完全支持可重入的,这就是说相同的函数、类实例的 constant 方法或者是不同类实例的相同 non-constant 方法可以在不同的线程中调用。同时,相同的 cv::Mat 也可以在不同的线程中使用,因为这里有引用计数来实现特定架构的原子操作。
如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:yqgroup@ 进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
用云栖社区APP,舒服~
【云栖快讯】首届阿里巴巴中间件技术峰会,揭秘阿里10年分布式技术沉淀!阿里高可用体系核心缔造者、全链路压测创始人,DRDS与TDDL负责人等大咖出场,干货分享,不可错过!&&
云数据库HybridDB(ApsaraDB HybridDB)是一种在线MPP大规模并行处理数据仓库服务。云数据...
大数据开发套件(Data IDE),提供可视化开发界面、离线任务调度运维、快速数据集成、多人协同工作等功能,为您...
用于实时预测用户对物品偏好,支持企业定制推荐算法,支持A/B Test效果对比
为您提供简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本...
2017杭州云栖大会火热抢票
Loading...&img src=&/v2-c94b3cf403dba90da175_b.jpg& data-rawwidth=&770& data-rawheight=&288& class=&origin_image zh-lightbox-thumb& width=&770& data-original=&/v2-c94b3cf403dba90da175_r.jpg&&&p&首个全卷积端到端实例分割模型,coco2016分割比赛的冠军模型。&/p&&p&传统的FCN没办法应用到实例分割,主要因为卷积具有平移不变性,同一图像像素无论在什么位置都将得到相同的响应,即对位置不敏感。然而,实例分割是基于区域(region)进行操作的,同一像素在不同区域可以有不同语义。所以,我们需要一定程度的位置敏感性。&/p&&p&对于实例分割,现在普遍的做法是采用不同类型的子网络进行检测和分割,分为以下三个阶段:1)原图送入FCN得到共享的feature maps;2)ROI pooling得到每个ROI的固定维度的feature maps;3)全连接层得到每个ROI的mask(使用全连接层引进了translation-variant property,这就是使用全连接层的一个原因)。&/p&&p&然而上面提及的普遍做法存在一些缺点:1)ROI pooling在做特征变换时会损失空间信息,降低分割精度,特别是对大目标的分割精度;2)全连接层引进的参数量太多了;3)每个ROI都要运行一遍分割子网络,计算效率太低。&/p&&p&这篇论文的工作是建立在InstanceFCN的基础上,都是同一个组做的。InstanceFCN引进了位置敏感的score maps(position-sensitive score maps),相当于一定程度的translation-variant。InstanceFCN主要是用来生成mask proposal,也有着一些缺点,比如无法判断语义类别,需要一个后续的网络辅助判别,也就是说它不是一个end to end的结构。&/p&&p&建立在InstanceFCN的基础上,作者提出了他们的结构(即FCIS)。FCIS的两个子任务(目标分割和检测)不仅共享卷积特征,也共享score maps。&/p&&p&在实例分割,同一像素既可以是一个目标的前景,也可以是另一个目标的背景。每个类别输出单个score map,是不足以区分这两种情况的。因此,FCIS的一个主要创新点出现了,就是引入了inside/outside score maps。&/p&&br&&img src=&/v2-fc9d7f567cdbd22acf54_b.jpg& data-rawwidth=&829& data-rawheight=&392& class=&origin_image zh-lightbox-thumb& width=&829& data-original=&/v2-fc9d7f567cdbd22acf54_r.jpg&&&p&上图,(a)是传统FCN。(b)是InstanceFCN,注意它的position-sensitive score maps,3*3张score map,分别取ROI的不同位置区域块,再汇聚成一个ROI。(c)是FCIS,可以看出相比InstanceFCN,引进了inside/outside score maps,可以同时进行分割和分类的任务,且利用了这两个任务的相关性。之前的实例分割,分割和分类两个子任务的score maps都是不相关的,而作者觉着,这两个任务可以互补,可以利用它们的相关性,最终实验结果也验证了这个方案的正确性。&/p&&br&&img src=&/v2-c94b3cf403dba90da175_b.jpg& data-rawwidth=&770& data-rawheight=&288& class=&origin_image zh-lightbox-thumb& width=&770& data-original=&/v2-c94b3cf403dba90da175_r.jpg&&&p&上图是FCIS的整体结构框架。&/p&&blockquote&一个区域预测网络(RPN)与 FCIS 共享卷积层。RPN 产生的兴趣区域(RoI)会作用在 score maps 上,同时产生分类和分割预测。权重可学的层都是卷积层,并且在整张图像上进行计算。&/blockquote&&p&至于实验部分,作者做了很多对比实验,具体大家去看论文吧。&/p&&br&&p&参考:&/p&&p&&a href=&/?target=http%3A//blog.csdn.net/u/article/details/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&MSRA instance-aware semantic segmentation的思路线 &i class=&icon-external&&&/i&&/a& &/p&&a href=&/?target=http%3A///fully-convolutional-instance-aware-semantic-segmentation-%25e8%25ae%25ba%25e6%e7%25ac%%25ae%25b0/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Fully Convolutional Instance-aware Semantic Segmentation 论文笔记&i class=&icon-external&&&/i&&/a&&p&&a href=&/?target=https%3A///30/9/p74& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&代码开源 | COCO-16 图像分割冠军:首个全卷积端到端实例分割模型&i class=&icon-external&&&/i&&/a& &/p&
首个全卷积端到端实例分割模型,coco2016分割比赛的冠军模型。传统的FCN没办法应用到实例分割,主要因为卷积具有平移不变性,同一图像像素无论在什么位置都将得到相同的响应,即对位置不敏感。然而,实例分割是基于区域(region)进行操作的,同一像素在不…
&p&学习TensorFlow还是多看看API比较靠谱,就是翻墙比较麻烦。还好国内有人把API全都搬运过来了,传送门:
&a href=&///?target=http%3A///& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&&/span&&span class=&invisible&&/&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&&/p&&br&&p&另外,神经网络其实是很好玩的~ 这里有一篇文章就是边介绍边玩的形式,以性感美女作分析,通俗易懂。 &a href=&///?target=http%3A///& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&&/span&&span class=&invisible&&/&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a& 好玩的神经网络。&/p&
学习TensorFlow还是多看看API比较靠谱,就是翻墙比较麻烦。还好国内有人把API全都搬运过来了,传送门:
另外,神经网络其实是很好玩的~ 这里有一篇文章就是边介绍边玩的形式,以性感美女作分析,通俗易懂。 …
&p&我个人觉得这是必须的。曾经回答过类似的问题:&a href=&/question//answer/& class=&internal&&2018 年应届硕士毕业生如何拿到知名互联网公司深度学习 offer?&/a&&/p&&p&直接转贴过来吧。希望对你有用。也欢迎大家扫描我的头像二维码关注公众号,获取更多关于深度学习的知识、教材和视频教程。&/p&&p& 1、较强的编码能力:这个是最重要也是最容易忽略的两个原因:&/p&&p&1是,在学校做的很多事情都比较偏研究,工程实践的能力会比较弱;&/p&&p&2是,很 多人觉得深度学习的大多是算法岗,对于编码能力不会过多的要求。答案绝对不是,工作之后是以 产出为导向,时间很宝贵,做算法就需要实现模型,看paper,实现各种新的trick,特别是深度学习技术更新特别快,扎实代码基础是必须的。 &/p&&p&可以每天规定一两个小时刷刷题,途径:基本的数据结构知识,C++,java,面向对象知识 -& 剑指offer -& leetcode or ACM的题。多联系可以锻炼编码的思维,而写代码在以后的工作中是非常重要的。&/p&&p& 2、扎实的算法基础:&/p&&p&
(1)最好是先机器学习、数据挖掘、NLP等类似的基础,这样入门比较容易,同时需要一定的概率统计的基础,因为深度学习基础 只是涉及挺多的。&/p&&p&
(2)系统学习下深度学习的相关知识,最好是找些视频教程来看:这里推荐Yoshua Bengio和Ian Goodfellow合著的《Deep Learning》教程&/p&&p&
(3)选择一个细分方向,成为某个领域内比较懂行的人:深度学习在NLP、图像、语音等很多方向都有很多应用,很多模型:CNN、RNN、DRL
、GAN等,可以选择自己感兴趣的方向或模型深入钻研一下。&/p&&p&
(4)多看一些前沿的论文,吸收和借鉴别人的想法和经验,能走在技术的前沿。&/p&&p& 3、有机会最好找个对口的岗位实习,建议实习一定要坚持半年以上,时间越长越好,抱着学习的心态,只有时间保证才能学到真的东西。切忌浮躁。 最后,加油,相信自己一定可以的。 可以看看我个人专栏“深度学习与NLP”,&a href=&/people/lqfarmer/activities& class=&internal&&&span class=&invisible&&https://www.&/span&&span class=&visible&&/people/lqfarm&/span&&span class=&invisible&&er/activities&/span&&span class=&ellipsis&&&/span&&/a&提供很多视频教程,经典论文和教材免费下载 &/p&
我个人觉得这是必须的。曾经回答过类似的问题:直接转贴过来吧。希望对你有用。也欢迎大家扫描我的头像二维码关注公众号,获取更多关于深度学习的知识、教材和视频教程。 1、较强的编码能力:…
&p&来尝试回答下。其实题主的问题可以拆分成下面两个部分:&/p&&ul&&li&&b&为什么在CNN等结构中将原先的sigmoid、tanh换成ReLU可以取得比较好的效果?&/b&&/li&&li&&b&为什么在RNN中,将tanh换成ReLU不能取得类似的效果?&/b&&/li&&/ul&&p&先来简单讨论下第一个问题。设输入数据为 &img src=&///equation?tex=x& alt=&x& eeimg=&1&& ,对 &img src=&///equation?tex=x& alt=&x& eeimg=&1&& 的卷积操作就可以看做是 &img src=&///equation?tex=Wx%2Bb& alt=&Wx+b& eeimg=&1&& (如果不理解为什么,可以参考这个答案的前半部分:&a href=&/question//answer/& class=&internal&&如何理解深度学习中的deconvolution networks?&/a&)。我们设第一层卷积的参数为 &img src=&///equation?tex=W_1%2C+b_1& alt=&W_1, b_1& eeimg=&1&& ,第二层卷积的参数是 &img src=&///equation?tex=W_2%2C+b_2& alt=&W_2, b_2& eeimg=&1&&,依次类推。又设激活函数为 &img src=&///equation?tex=f& alt=&f& eeimg=&1&& ,每一层卷积在经过激活函数前的值为 &img src=&///equation?tex=a_i& alt=&a_i& eeimg=&1&& ,经过激活函数后的值为 &img src=&///equation?tex=f_i& alt=&f_i& eeimg=&1&& 。&/p&&p&按照上面的表示,在CNN中,输入 &img src=&///equation?tex=x& alt=&x& eeimg=&1&& ,第一层的输出就是 &img src=&///equation?tex=f_1%3Df%5BW_1x%2Bb_1%5D& alt=&f_1=f[W_1x+b_1]& eeimg=&1&& ,第二层的输出就是 &img src=&///equation?tex=f_2%3D+f%5BW_2f%5BW_1x%2Bb_1%5D%2Bb_2%5D& alt=&f_2= f[W_2f[W_1x+b_1]+b_2]& eeimg=&1&& ,第三层的输出就是 &img src=&///equation?tex=f_3%3D+f%5BW_3f%5BW_2f%5BW_1x%2Bb_1%5D%2Bb_2%5D%2Bb_3%5D& alt=&f_3= f[W_3f[W_2f[W_1x+b_1]+b_2]+b_3]& eeimg=&1&& 。设最终损失为 &img src=&///equation?tex=L& alt=&L& eeimg=&1&& ,我们来尝试从第三层开始,用BP算法推导一下损失对参数 &img src=&///equation?tex=W_1& alt=&W_1& eeimg=&1&& 的偏导数,看看会发生什么。&/p&&p&为了简洁起见,略过求导过程,最后的结果为 &img src=&///equation?tex=%5Cfrac%7B%5Cpartial%7BL%7D%7D%7B%5Cpartial%7BW_1%7D%7D%3D%5Cfrac%7B%5Cpartial%7BL%7D%7D%7B%5Cpartial%7Bf_3%7D%7D%5Cfrac%7B%5Cpartial%7Bf_3%7D%7D%7B%5Cpartial%7Ba_3%7D%7DW_3%5Cfrac%7B%5Cpartial%7Bf_2%7D%7D%7B%5Cpartial%7Ba_2%7D%7DW_2%5Cfrac%7B%5Cpartial%7Bf_1%7D%7D%7B%5Cpartial%7Ba_1%7D%7D%5Cfrac%7B%5Cpartial%7Ba_1%7D%7D%7B%5Cpartial%7BW_1%7D%7D& alt=&\frac{\partial{L}}{\partial{W_1}}=\frac{\partial{L}}{\partial{f_3}}\frac{\partial{f_3}}{\partial{a_3}}W_3\frac{\partial{f_2}}{\partial{a_2}}W_2\frac{\partial{f_1}}{\partial{a_1}}\frac{\partial{a_1}}{\partial{W_1}}& eeimg=&1&& 。&b&我们常常说原始神经网络的梯度消失问题,这里的 &img src=&///equation?tex=%5Cfrac%7B%5Cpartial%7Bf_3%7D%7D%7B%5Cpartial%7Ba_3%7D%7D& alt=&\frac{\partial{f_3}}{\partial{a_3}}& eeimg=&1&& 、 &img src=&///equation?tex=%5Cfrac%7B%5Cpartial%7Bf_2%7D%7D%7B%5Cpartial%7Ba_2%7D%7D& alt=&\frac{\partial{f_2}}{\partial{a_2}}& eeimg=&1&& 就是梯度消失的“罪魁祸首”。&/b&例如sigmoid的函数,它的导数的取值范围是(0, 0.25],也就是说对于导数中的每一个元素,我们都有 &img src=&///equation?tex=0%3C%5Cfrac%7B%5Cpartial%7Bf_3%7D%7D%7B%5Cpartial%7Ba_3%7D%7D%5Cle0.25& alt=&0&\frac{\partial{f_3}}{\partial{a_3}}\le0.25& eeimg=&1&& , &img src=&///equation?tex=0%3C%5Cfrac%7B%5Cpartial%7Bf_2%7D%7D%7B%5Cpartial%7Ba_2%7D%7D%5Cle0.25& alt=&0&\frac{\partial{f_2}}{\partial{a_2}}\le0.25& eeimg=&1&& ,小于1的数乘在一起,必然是越乘越小的。这才仅仅是3层,如果10层的话, 根据&img src=&///equation?tex=0.25%5E%7B10%7D%5Capprox0.& alt=&0.25^{10}\approx0.& eeimg=&1&& ,第10层的误差相对第一层卷积的参数 &img src=&///equation?tex=W_1& alt=&W_1& eeimg=&1&& 的梯度将是一个非常小的值,这就是所谓的“梯度消失”。&/p&&p&&b&ReLU函数的改进就是它使得 &img src=&///equation?tex=%5Cfrac%7B%5Cpartial%7Bf_3%7D%7D%7B%5Cpartial%7Ba_3%7D%7D%5Cin%5C%7B0%2C1%5C%7D& alt=&\frac{\partial{f_3}}{\partial{a_3}}\in\{0,1\}& eeimg=&1&& , &img src=&///equation?tex=%5Cfrac%7B%5Cpartial%7Bf_2%7D%7D%7B%5Cpartial%7Ba_2%7D%7D%5Cin%5C%7B0%2C1%5C%7D& alt=&\frac{\partial{f_2}}{\partial{a_2}}\in\{0,1\}& eeimg=&1&& , &img src=&///equation?tex=%5Cfrac%7B%5Cpartial%7Bf_1%7D%7D%7B%5Cpartial%7Ba_1%7D%7D%5Cin%5C%7B0%2C1%5C%7D& alt=&\frac{\partial{f_1}}{\partial{a_1}}\in\{0,1\}& eeimg=&1&&&/b& ,&b&这样的话只要一条路径上的导数都是1,无论神经网络是多少层,这一部分的乘积都始终为1,因此深层的梯度也可以传递到浅层中。&/b&&/p&&p&那为什么同样的方法在RNN中不奏效呢?其实这一点Hinton在它的IRNN论文里面(arxiv:&a href=&///?target=https%3A//arxiv.org/abs/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&[] A Simple Way to Initialize Recurrent Networks of Rectified Linear Units&i class=&icon-external&&&/i&&/a&)是很明确的提到的:&/p&&img src=&/v2-2d19e8c9b39ff6edfb31_b.jpg& data-rawwidth=&1345& data-rawheight=&277& class=&origin_image zh-lightbox-thumb& width=&1345& data-original=&/v2-2d19e8c9b39ff6edfb31_r.jpg&&&p&&b&也就是说在RNN中直接把激活函数换成ReLU会导致非常大的输出值。&/b&为了讲清楚这一点,我们先用同上面相似的符号把原始的RNN表示出来:&/p&&p&&img src=&///equation?tex=a_i%3DWf_%7Bi-1%7D%2BUx_%7Bi%7D%2Bb_i& alt=&a_i=Wf_{i-1}+Ux_{i}+b_i& eeimg=&1&&,&/p&&img src=&///equation?tex=f_i%3Df%5Ba_i%5D& alt=&f_i=f[a_i]& eeimg=&1&&&p&在这个表示中,RNN每个阶段的输入是 &img src=&///equation?tex=x_i& alt=&x_i& eeimg=&1&& ,&b&和CNN每一层使用独立的参数 &img src=&///equation?tex=W_i& alt=&W_i& eeimg=&1&& 不同,原始的RNN在每个阶段都共享一个参数 &img src=&///equation?tex=W& alt=&W& eeimg=&1&& 。&/b&如果我们假设从某一层开始输入 &img src=&///equation?tex=x_i& alt=&x_i& eeimg=&1&& 和偏置 &img src=&///equation?tex=b_i& alt=&b_i& eeimg=&1&& 都为0,那么最后得到的输出就是 &img src=&///equation?tex=f%5BW...%5BWf%5BWf%5BWf_i%5D%5D%5D%5D& alt=&f[W...[Wf[Wf[Wf_i]]]]& eeimg=&1&& ,&b&这在某种程度上相当于对参数矩阵 &img src=&///equation?tex=W& alt=&W& eeimg=&1&& 作连乘&/b&,很显然,只要 &img src=&///equation?tex=W& alt=&W& eeimg=&1&& 有一个大于1的特征值,在经过若干次连乘后都会导致结果是一个数值非常庞大的矩阵。&/p&&p&&b&另外一方面,将激活函数换成ReLU也不能解决梯度在长程上传递的问题。&/b&同样考虑 &img src=&///equation?tex=f_3& alt=&f_3& eeimg=&1&& 对 &img src=&///equation?tex=W& alt=&W& eeimg=&1&& 的导数。在CNN中,每一层的参数 &img src=&///equation?tex=W_1%2CW_2%2CW_3......& alt=&W_1,W_2,W_3......& eeimg=&1&& 是互相独立的,然而RNN中 &img src=&///equation?tex=W& alt=&W& eeimg=&1&& 参与了每个时间段的运算,因此 &img src=&///equation?tex=f_3& alt=&f_3& eeimg=&1&& 对 &img src=&///equation?tex=W& alt=&W& eeimg=&1&& 导数更复杂,写出来是 &img src=&///equation?tex=%5Cfrac%7B%5Cpartial%7Bf_3%7D%7D%7B%5Cpartial%7BW_1%7D%7D%3D%5Cfrac%7B%5Cpartial%7Bf_3%7D%7D%7B%5Cpartial%7Ba_3%7D%7Df_2%2B%5Cfrac%7B%5Cpartial%7Bf_3%7D%7D%7B%5Cpartial%7Ba_3%7D%7DW%5Cfrac%7B%5Cpartial%7Bf_2%7D%7D%7B%5Cpartial%7Ba_2%7D%7Df_1%2B%5Cfrac%7B%5Cpartial%7Bf_3%7D%7D%7B%5Cpartial%7Ba_3%7D%7DW%5Cfrac%7B%5Cpartial%7Bf_2%7D%7D%7B%5Cpartial%7Ba_2%7D%7DW%5Cfrac%7B%5Cpartial%7Bf_1%7D%7D%7B%5Cpartial%7Ba_1%7D%7D%5Cfrac%7B%5Cpartial%7Ba_1%7D%7D%7B%5Cpartial%7BW_1%7D%7D& alt=&\frac{\partial{f_3}}{\partial{W_1}}=\frac{\partial{f_3}}{\partial{a_3}}f_2+\frac{\partial{f_3}}{\partial{a_3}}W\frac{\partial{f_2}}{\partial{a_2}}f_1+\frac{\partial{f_3}}{\partial{a_3}}W\frac{\partial{f_2}}{\partial{a_2}}W\frac{\partial{f_1}}{\partial{a_1}}\frac{\partial{a_1}}{\partial{W_1}}& eeimg=&1&& 。我们可以看下最后 &img src=&///equation?tex=%5Cfrac%7B%5Cpartial%7Bf_3%7D%7D%7B%5Cpartial%7Ba_3%7D%7DW%5Cfrac%7B%5Cpartial%7Bf_2%7D%7D%7B%5Cpartial%7Ba_2%7D%7DW%5Cfrac%7B%5Cpartial%7Bf_1%7D%7D%7B%5Cpartial%7Ba_1%7D%7D%5Cfrac%7B%5Cpartial%7Ba_1%7D%7D%7B%5Cpartial%7BW_1%7D%7D& alt=&\frac{\partial{f_3}}{\partial{a_3}}W\frac{\partial{f_2}}{\partial{a_2}}W\frac{\partial{f_1}}{\partial{a_1}}\frac{\partial{a_1}}{\partial{W_1}}& eeimg=&1&& 这部分,使用ReLU后,当梯度可以传递时,有 &img src=&///equation?tex=%5Cfrac%7B%5Cpartial%7Bf_3%7D%7D%7B%5Cpartial%7Ba_3%7D%7D%3D%5Cfrac%7B%5Cpartial%7Bf_2%7D%7D%7B%5Cpartial%7Ba_2%7D%7D%3D%5Cfrac%7B%5Cpartial%7Bf_3%7D%7D%7B%5Cpartial%7Ba_1%7D%7D%3D1& alt=&\frac{\partial{f_3}}{\partial{a_3}}=\frac{\partial{f_2}}{\partial{a_2}}=\frac{\partial{f_3}}{\partial{a_1}}=1& eeimg=&1&& ,但这个式子中还是会有两个 &img src=&///equation?tex=W& alt=&W& eeimg=&1&& 的连乘。在更长程上,就会有更多 &img src=&///equation?tex=W& alt=&W& eeimg=&1&& 的连乘。对于CNN来说,这个部分是 &img src=&///equation?tex=W_1%2CW_2%2CW_3.....& alt=&W_1,W_2,W_3.....& eeimg=&1&& 进行连乘,一方面它们都是稀疏矩阵,另一方面 &img src=&///equation?tex=W_1%2CW_2%2CW_3....& alt=&W_1,W_2,W_3....& eeimg=&1&& 互不相同,很大程度上都能抵消掉梯度爆炸的影响。&/p&&p&最后,IRNN在RNN上使用了ReLU,取得了比较好的结果,其中的原因在于,它对 &img src=&///equation?tex=W& alt=&W& eeimg=&1&& 和 &img src=&///equation?tex=b_i& alt=&b_i& eeimg=&1&& 取了比较特殊的初值: &img src=&///equation?tex=W%3DI& alt=&W=I& eeimg=&1&& , &img src=&///equation?tex=b_i%3D0& alt=&b_i=0& eeimg=&1&& 。这样在梯度的式子 &img src=&///equation?tex=%5Cfrac%7B%5Cpartial%7Bf_3%7D%7D%7B%5Cpartial%7Ba_3%7D%7DW%5Cfrac%7B%5Cpartial%7Bf_2%7D%7D%7B%5Cpartial%7Ba_2%7D%7DW%5Cfrac%7B%5Cpartial%7Bf_1%7D%7D%7B%5Cpartial%7Ba_1%7D%7D%5Cfrac%7B%5Cpartial%7Ba_1%7D%7D%7B%5Cpartial%7BW_1%7D%7D& alt=&\frac{\partial{f_3}}{\partial{a_3}}W\frac{\partial{f_2}}{\partial{a_2}}W\frac{\partial{f_1}}{\partial{a_1}}\frac{\partial{a_1}}{\partial{W_1}}& eeimg=&1&& 中W尽管会连乘,但是会类似于单位矩阵 &img src=&///equation?tex=I& alt=&I& eeimg=&1&& 的连乘,不会引起太明显的梯度数值变化。另外一方面,也不会引起非常大的输出值(这点可以自行带入表达式验证)。&/p&&p&以上,如果有哪里说的不对的,还烦请各位大佬批评指正。&/p&&p&--------------------------------------&/p&&p&PS:有人知道为什么知乎的latex一打圆括号()就无法显示么。。。没办法只好把所有f()写成f[]了。。&/p&
来尝试回答下。其实题主的问题可以拆分成下面两个部分:为什么在CNN等结构中将原先的sigmoid、tanh换成ReLU可以取得比较好的效果?为什么在RNN中,将tanh换成ReLU不能取得类似的效果?先来简单讨论下第一个问题。设输入数据为 x ,对 x 的卷积操作就可以看…
&p&谢邀,关于深度学习在无人驾驶汽车上面的应用有很多:&/p&&p&(1).路径识别。无人驾驶车必须要准确的知道自己应当行驶在路的哪一边,“认路”是最基本的条件,不认路的话会产生很严重的后果,比如逆行。&/p&&p&(2).行人识别:仅仅实现认路还不够,还要能识别出哪些“物体”是在任何场景下绝对不能碰撞的“人类”,做不到这一点,所有的国家和地区都不会允许这样的技术在各种道路上推广。&/p&&p&(3).道路标志识别和信号灯识别:红灯停绿灯行,哪个路段是禁行的,哪个路段是限载的,哪处的前方会有学校,哪处是禁停的......这些人类制订的道路标识规则无人驾驶汽车必须遵从,这点无需多言。&/p&&p&(4).环境识别:外面是下雨了还是一个大晴天(下雨的话就不能走滑坡泥石流多发的道路),是有浓雾还是能见度很高(浓雾天气就要减速并开远光灯).......这些都要考虑&/p&&p&(5).障碍物及车辆识别:无人驾驶汽车要识别出哪些是“有效障碍物”(比如交警,比如路障,比如收费站....这些具有法律管制效应的“障碍物),要识别出哪些是有特殊含义的车辆(警车,消防车,救护车.......)哪些是一般意义上的车辆。&/p&&p&还有很多很多更加具体的细节应用,就不再赘言&/p&&p&不过我相信题主一定不仅仅想了解上面这些粗浅的空话,下面让我们拿出几个干货例子来说明深度学习在无人驾驶汽车中的具体应用:&/p&&p&&b&1.应用深度学习识别车辆和道路&/b&&/p&&blockquote& 相关文献:&i&An Empirical Evaluation of Deep Learning on Highway Driving——&/i&Brody Huval, Tao Wang, Sameep Tandon, Jeff Kiske, Will Song, Joel Pazhayampallil,Mykhaylo Andriluka, Pranav Rajpurkar, Toki Migimatsu, Royce Cheng-Yue,Fernando Mujica, Adam Coates, Andrew Y. Ng&/blockquote&&p&在&b&车辆识别&/b&方面,研究团队在检测系统中使用的是大于10Hz的笔记本电脑配置的GPU,设定的图像分辨率是640X480(这样的分辨率是为了在运行速率为10Hz的条件下能够探测出100m之外的车辆),使用的是基于Overfeat的卷积网络(CNN: Convolutional
Network)检测器——在识别过程中,通过提供较大分辨率的图像将完全连接的层转换为卷积层,将图像识别CNN转换成“滑动窗口”检测器。在将完全连接的层(其已经产生单个最终特征向量)转换为卷积层之后,产生最终特征向量的网格。 每个特征向量结果表示原始像素空间内的略有不同的“上下文视图位置”( context view location)。 为了确定这个窗口在像素空间中的步幅(stride size ),可以简单地将每个卷积或池层(pool layer)上的步幅相乘在一起。(研究团队使用的网络具有32像素的步幅)。该网格中的每个最终特征向量可以预测物体的存在; 一旦检测到对象,相同的特征会被用于预测单个边界框( single bounding box )。 如果分类器不能在整个输入视图内辨别对象的任何部分,则不能识别任何对象,系统会预测“无对象”。这将导致分类器产生较大的模糊性(ambiguities),其仅能预测单个对象,因为两个不同的对象可以很容易地出现在最终特征向量的环境视图中(通常大于输入图像分辨率的50%)。&/p&&p&在网络具有大小为355×355像素的环境视图下,为了确保图像中的所有对象被至少分类一次,通过“skip gram kernels”的方法来减少环境视图的步幅并且使用四个不同尺度的输入图像来对图像拍摄许多不同的环境视图。在此之后分类器会被训练到“当对象出现在其整个环境视图内的任何地方时”便被激活的程度。然而这么做会带来两个问题:首先,基于Sermanet等人提出的预测边界框(predicted bounding box )和实际边界(actual bounding)之间存在L2损失( L2 loss),为了使这种损失最小化,当两个物体出现时,预测两个有效边界框位置时产生的模糊性会被网络错误的处理。视图内的框(boxes)往往导致边界框合并算法( bounding box merging algorithm)的问题——错误的判定在两个地面实况对象之间一定存在第三对象——这可能导致ADAS系统发生问题(该系统会错误地判定存在事实上不存在的汽车,并且错误地应用紧急中断:emergency breaking机制)。其次,应用合并算法&img src=&///equation?tex=O%28n%5E2%29& alt=&O(n^2)& eeimg=&1&&
(&img src=&///equation?tex=n& alt=&n& eeimg=&1&& 是视图中的框数)时,因为边界框合并不像CNN那样容易并行化,在无效实现或存在太多预测边界框的情况下,这种合并可能成为实时系统的瓶颈。&/p&&p&为解决上述问题,掩模检测器( mask
detector )被引入(下图所示)&/p&&img src=&/v2-778cd2d66b920d2334baa1_b.png& class=&content_image&&&p&为了区分多个附近的对象,不同的部分检测器输出对象掩模,然后从中提取边界框。 检测器必须拍摄许多组图像,并且对每组图像的每个部分运行多个CNN。 由于实现这一过程所需的时间略长(5-6s),“滑动窗口”检测器将与掩模检测器相结合,以产生对象掩码( object mask)并执行边界盒回归( bounding
regression)(下图所示)&/p&&img src=&/v2-777b624ecca7ea34bae9bd4cc036ae67_b.png& class=&content_image&&&p&在&b&道路识别&/b&方面,用于车辆检测的CNN可以通过添加附加类别的方式扩展用于车道边界检测。不同之处在于道路回归预测需要六个维度的尺寸值(四个维度尺寸用以指示车道边界的局部线段的两个端点。剩余的两个维度指示端点的深度)而车辆类的回归预测需要五个维度的尺寸值(四个维度尺寸用于边界框,一个维度尺寸用于深度)。图下示出了覆盖在示例图像上的车道边界地面实况标签。绿色图块表示检测器被训练到的位置,并且由回归标签表示的线段被明确地绘制。线段的端部连接以形成连续样条。线段的深度被颜色编码,使得最近的段是红色的,最远的段是蓝色的。由于数据收集方法为车道标签,研究团队能够获得地面实况。尽管有可能对象被遮挡,但这迫使神经网络学习比简单的油漆检测器更多更复杂的预测方法,并且必须使用环境来预测存在遮挡的车道。&/p&&br&&br&&img src=&/v2-ba91d4ec1164ffe027af4bf435d4ee47_b.png& class=&content_image&&&br&&img src=&/v2-cbad8ab7ca1f0_b.png& class=&content_image&&&p&&b&2.应用深度学习识别预料之外的障碍物&/b&&/p&&blockquote&相关文献:Detecting Unexpected Obstacles for Self-Driving Cars:Fusing Deep Learning and Geometric Modeling——Sebastian Ramos , Stefan Gehrig , Peter Pinggera , Uwe Franke and Carsten Rother&/blockquote&&p&该研究团队使用的识别障碍物的核心框架如下:&/p&&img src=&/v2-61bf350dec90c570ef6382_b.png& class=&content_image&&&p&UON(Unexpected Obstacle Network)预料外障碍物网络&/p&&p&SGM(Semi Global Matching)半全球匹配&/p&&p&算法程序框架如下:&/p&&img src=&/v2-cc1e6a6ce220cec444e81e_b.png& class=&content_image&&&p&使用两个并行和独立的通道来处理输入图像对,以最初执行像素级语义推理以及深度估计。 随后,获得两个独立的基于3D Stixel的障碍物表示,第一个利用语义,第二个利用场景的几何属性。 最后,以概率方式融合两个通道以生成道路上的潜在障碍物的整体3D Stixel表示。&/p&&p&另外推荐一些关于深度学习应用于无人驾驶的好的网站:&/p&&p&&a href=&///?target=http%3A//www.cs.toronto.edu/%7Eurtasun/courses/CSC2541/CSC2541_Winter16.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Visual Perception for Autonomous Driving&i class=&icon-external&&&/i&&/a&&/p&&p&&a href=&///?target=https%3A///How-important-is-deep-learning-in-autonomous-driving& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&How important is deep learning in autonomous driving?&i class=&icon-external&&&/i&&/a&&/p&&p&&a href=&///?target=https%3A///parallelforall/deep-learning-self-driving-cars/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&End-to-End Deep Learning for Self-Driving Cars&i class=&icon-external&&&/i&&/a&&/p&&p&&a href=&///?target=https%3A//handong1587.github.io/deep_learning//dl-and-autonomous-driving.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Deep Learning and Autonomous Driving&i class=&icon-external&&&/i&&/a&&/p&
谢邀,关于深度学习在无人驾驶汽车上面的应用有很多:(1).路径识别。无人驾驶车必须要准确的知道自己应当行驶在路的哪一边,“认路”是最基本的条件,不认路的话会产生很严重的后果,比如逆行。(2).行人识别:仅仅实现认路还不够,还要能识别出哪些“物体”…
谢邀,第一次回答问题,尽我所能。检测车道线是自动驾驶或辅助驾驶中很基础的问题,传感器只需要摄像头足矣,主要是检测的算法。传统的计算机视觉算法是利用边缘检测(Edge detection),霍夫变换(Hough transform)等算法,把车道线从道路图片中提取分离出来。这是典型的人工设计特征(Hand crafted feature)的方法,需要较高的计算机视觉方面的专家知识。&br&如今火热的深度学习(Deep learning),正是取代人工设计特征,让计算机自行学习所需要的特征的技术。这里推荐百度吴恩达的学生Brody写的一篇论文:An Empirical Evaluation of Deep Learning on Highway Driving。文中作者搭建了一个卷积神经网络(Convolutional neural network)来进行车道线的检测:&br&&img src=&/v2-9ee33facff5a17e179c3dfd_b.png& data-rawwidth=&516& data-rawheight=&442& class=&origin_image zh-lightbox-thumb& width=&516& data-original=&/v2-9ee33facff5a17e179c3dfd_r.png&&这里我再简单介绍下文章的内容。作者在车上安装了激光雷达,摄像头和GPS来采集训练数据。深度学习的一个弊端就是需要海量的数据训练神经网络。期待百度等大公司们开放他们的数据集,就像李飞飞的ImageNet一样,造福大众(Just kidding~)。另外我知道有个叫KITTI的小型无人驾驶数据集,学术界用来测试算法的。有了数据以后,作者用了Overfeat算法做车辆和车道线的目标检测。这个算法很经典,是燕大神(Yann Lecun)的学生开发的,但也有点老了。这个领域发展飞快,就我所知,更快更准的算法有Fast-RCNN, Faster-RCNN, YOLO等。作者的实验结果证明,深度学习用于车道的检测是可行的,50米内车道检测的三个指标:召回率,准确率,精确率都几乎是100%。最后,作者慷慨的开源了基于Caffe的源代码: &a href=&///?target=http%3A///brodyh/caffe& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&/brodyh/caffe&/span&&span class=&invisible&&&/span&&i class=&icon-external&&&/i&&/a&,但完全没有注释也是服了。。。
谢邀,第一次回答问题,尽我所能。检测车道线是自动驾驶或辅助驾驶中很基础的问题,传感器只需要摄像头足矣,主要是检测的算法。传统的计算机视觉算法是利用边缘检测(Edge detection),霍夫变换(Hough transform)等算法,把车道线从道路图片中提取分离出来…
&img src=&/v2-0efc3a02cfc396f7fac1_b.png& data-rawwidth=&581& data-rawheight=&328& class=&origin_image zh-lightbox-thumb& width=&581& data-original=&/v2-0efc3a02cfc396f7fac1_r.png&&&p&&a href=&/people/xiaoxinwang& class=&internal&&王小新&/a& 编译自 Medium &/p&&p&量子位 出品 | 公众号 QbitAI&/p&&p&找到马路上的车道线,对于人类来说非常容易,但对计算机来说,一点阴影、反光、道路颜色的微小变化、或者车道线被部分遮挡,都会带来很大的困难。&/p&&p&正在Udacity学习自动驾驶课程的Michael Virgo写了两篇博客文章,介绍了如何构建检测模型。&/p&&p&&b&以下内容编译自他的文章:&/b&&/p&&p&在Udacity无人车纳米学位第一学期课程的五个项目中,有两个是关于车道检测的。&/p&&p&其中第一个项目介绍了一些基本的计算机视觉技术,如Canny边缘检测。&/p&&p&&img src=&/v2-a_b.png& data-rawwidth=&765& data-rawheight=&212& class=&origin_image zh-lightbox-thumb& width=&765& data-original=&/v2-a_r.png&&图1:Canny边缘检测&/p&&p&第二个项目深入介绍了一种方法,称为透视变换(perspective transformation),能将图像中的某些点延伸到目标位置。根据透视原理,车辆视角拍摄的照片上,车道线会在远方聚拢;而进行变换之后,我们会得到一张鸟瞰图。&/p&&p&&img src=&/v2-03bef198cda_b.png& data-rawwidth=&1018& data-rawheight=&312& class=&origin_image zh-lightbox-thumb& width=&1018& data-original=&/v2-03bef198cda_r.png&&图2:透视变换前后的图像&/p&&p&在遍历图像时,如暗色的道路变为亮色的车道线时,像素值会变化。在透视变换前,利用梯度和颜色阈值得到一张二值图像,当像素值高于阈值时设置为1。在透视变换后,可在该图像上运行滑动窗口,来计算特定车道线的多项式拟合曲线。&/p&&p&&img src=&/v2-6ddb39da00aad02519bdd_b.png& data-rawwidth=&1017& data-rawheight=&312& class=&origin_image zh-lightbox-thumb& width=&1017& data-original=&/v2-6ddb39da00aad02519bdd_r.png&&图3:阈值为S的二值图像&/p&&p&&img src=&/v2-3b75a0fed11a0d9ea893f29_b.png& data-rawwidth=&990& data-rawheight=&311& class=&origin_image zh-lightbox-thumb& width=&990& data-original=&/v2-3b75a0fed11a0d9ea893f29_r.png&&图4:原二值图像和透视变换后的二值图像&/p&&p&&img src=&/v2-2ce2a9b3f6e4cd432b38ebd_b.png& data-rawwidth=&618& data-rawheight=&172& class=&origin_image zh-lightbox-thumb& width=&618& data-original=&/v2-2ce2a9b3f6e4cd432b38ebd_r.png&&图5:应用滑动窗口的二值图像和输出结果&/p&&p&这种技术效果看起来不错,但实际存在很多限制。首先,透视变换操作会对相机有一些具体的要求,在变换前需要调正图像,而且摄像机的安装和道路本身的倾斜都会影响变换效果。其次,各种梯度和颜色阈值只在小部分情况下适用,故前面提到的计算机识别车道线的各种问题在这里变得更加突出。最后,这种技术处理起来很慢:实现车道检测功能的程序大约每秒能处理4.5帧(FPS),而汽车摄像头的帧数可能会在30 FPS或以上。&/p&&p&&img src=&/v2-2c8c0a04ccd6de21bba91_b.png& data-rawwidth=&1600& data-rawheight=&900& class=&origin_image zh-lightbox-thumb& width=&1600& data-original=&/v2-2c8c0a04ccd6de21bba91_r.png&&图6:车道检测出错的例子&/p&&p&我已经成功地应用深度学习技术完成了这两个车道检测项目,接下来我将对项目实现过程进行详细介绍。&/p&&h1&创建数据集&/h1&&p&虽然目前有大量用于训练自动驾驶技术的数据集产生,但大多数都没有对图像中的车道进行标注。我决定创建一个新的数据集,这是最关键的也是最耗时的一部分。为训练深层神经网络而针对性地创建新数据集,也是一个有趣且有意义的挑战。&/p&&p&收集数据是很容易的。在我住处的周围,有很多不同类型的道路可以开车去收集数据。根据我以往的项目经验,一个各类数据平衡的数据集非常重要。我收集了高速公路、辅路、盘山路、夜晚和雨天的数据。在这个过程中,我利用手机拍摄了超过21,000帧视频图像。&/p&&p&在提取出所有图片帧后,我注意到了一个问题:在亮度高且开车速度较慢时视频中的图像质量较高,在高速公路夜间驾驶和雨中驾驶时视频质量较为模糊,且这两种情景下的图像都存在很多问题。为了神经网络能更好地学习相关信息,我不得不检查和筛选每张图像。最终,训练集的图片数量减少到14,000张,但仍然存在一些轻度模糊的图像,希望不影响车道检测的效果。&/p&&p&&img src=&/v2-8fe50bd1f94c6158fce393dfd2c3f754_b.jpg& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/v2-8fe50bd1f94c6158fce393dfd2c3f754_r.jpg&&图7:一张被删除掉的模糊图像,但是车道检测模型在该图像上的实际效果很好。&/p&&p&为了标注数据集,我用了自己原来做过的一个计算机视觉算法,不是在输出图片上标注预测的车道线位置,而是输出六个多项式系数,以二次函数Ax2+Bx+C的形式来描述这两条车道线。&/p&&p&但是,我希望深度神经网络能具有更好的效果,所以我决定统一使用红线来手动绘出真实的车道线,以便接下来可以使用红色阈值来更好地检测车道线。我最初以为需要处理14000张图像,但这将会花费太长时间,不切合实际;同时,如果在低速时多张相近的图像同时存在,则预测模型的准确率可能会虚高。考虑到时间对数据集的影响,我决定从每10张图像中抽取一张,从而创建了只具有1400张训练图像的原始数据集。&/p&&p&&img src=&/v2-b8edaee76ca6c82ca839eb_b.png& data-rawwidth=&707& data-rawheight=&197& class=&origin_image zh-lightbox-thumb& width=&707& data-original=&/v2-b8edaee76ca6c82ca839eb_r.png&&图8:由于最终的模型仍能在模糊的夜间图像中检测到车道线,看来这个方法提高了模型的鲁棒性。&/p&&p&此后,我根据以往项目做法创建了一个程序,在道路图像上使用了传统的CV检测模型,用拟合出的多项式,来重新绘制车道线。这种做法可以节省图像处理的时间,但在我检查实际效果时发现了一个明显的问题:虽然我已经用大量弯曲道路的图像来训练这个传统模型,但是仍然不能检测到所有的车道线。在训练集的1400张图像中,大约有450张无法使用,出现问题的样本主要是弯曲道路图像。&/p&&p&然而,我意识到这是由于算法的滑动窗口机制,导致这个模型本身存在问题。如果一条车道线在图像边缘停止了,原始的滑动窗口将沿着图像边缘垂直向上迭代,导致该算法相信该线往该方向延伸。我们可以通过判断滑动窗口是否触及图像边缘来解决这一问题,如果滑动窗口触及边缘,且已在图像里迭代若干步(这么设置是防止模型开始时被误判断触及边缘),那么滑动窗口就停止工作。&/p&&p&&img src=&/v2-2fdcdffcf76fb1_b.png& data-rawwidth=&738& data-rawheight=&206& class=&origin_image zh-lightbox-thumb& width=&738& data-original=&/v2-2fdcdffcf76fb1_r.png&&图9:在弯曲道路图像上建立一系列滑动窗口,前后处理效果对比&/p&&p&从图里看出,这个效果很好,故障率降低了一半,从原来的约450张减少到约225张。我想通过检查标签的实际分布情况来分析无法使用的剩下图像。我通过直方图来检查六个系数的实际分布,结果显示拟合曲线仍趋于直线。我还尝试增大弯曲道路图像的所占比例,但问题没有解决:对于极其弯曲的道路,检测出的车道仍然很笔直。&/p&&p&&img src=&/v2-2ae8a0452a_b.png& data-rawwidth=&441& data-rawheight=&295& class=&origin_image zh-lightbox-thumb& width=&441& data-original=&/v2-2ae8a0452a_r.png&&图10:一个车道线系数的分布图:明显以直线为中心。&/p&&p&我借用了以前参加过的一个交通标志检测项目的经验。那个项目的数据集中,有些类别图片非常少,我对它们进行小幅度旋转操作,增加了该类数据的比例,大大提高了准确率。&/p&&p&我再次使用这种方法,对于某一特定分布范围之外的任何系数标签,对图像进行小幅度旋转并同时保持相同的标签。对于其中的三个系数,我大约各处理了400、100和30张图像,部分图片可能进行了三次调整操作。&/p&&p&&img src=&/v2-adbe49d540_b.png& data-rawwidth=&443& data-rawheight=&297& class=&origin_image zh-lightbox-thumb& width=&443& data-original=&/v2-adbe49d540_r.png&&图11:对部分图像进行旋转后,单个系数分布更为均匀。&/p&&p&在旋转图像后,每个系数的分布更为合理。当然,我还对数据集及标签进行了一些快速预处理操作。训练集的图像从最初的720 x 1280以不同倍数缩小,并进行归一化,有助于模型加快收敛。我也使了用sklearn库中的StandardScaler函数来归一化图像标签,归一化时请确保保存缩放信息,因为在最后一步需要恢复操作。标签归一化后会降低训练时的Loss值,但是绘制回到原始图像后,最终的结果也提升不大。&/p&&h1&引入深度学习&/h1&&p&你可能会想,接下来不采用透视变换方法了么?是的,但是为了创建一个初始的模型结构,我想基于这个特定的数据集,将深度学习方法和传统CV检测模型的效果做对比。因此网络输入是做了透视变换后的道路图像,在逻辑上,神经网络可能更容易学习到相关参数。&/p&&p&在这里,我使用了一个与行为克隆(Behavioral Cloning)项目中类似的模型结构,包括一个批归一化(BN)层,接上多个卷积层、一个池化层、一个扁平层和多个全连接层。最终的全连接层的输出大小为6,也就是要预测的车道线系数数量。我还使用Keras库中ImageDataGenerator函数,主要通过旋转、偏移高度和垂直翻转来试图增强模型的泛化能力,因为水平翻转可能会误导网络去识别车道信息。经过对模型结构、超参数和输入图像大小的微调后,该模型的效果不错,但是对输入输出的透视变换操作极其依赖。总体来说,这个模型不能让我满意。&/p&&h1&新的车道检测模型&/h1&&p&当我发现深度学习方法在这个模型上效果不错时,我决定创建一个能在没有进行透视变换的前提下检测车道线的模型。我沿用了这个原来的结构,还添加了一个裁剪层,切除了输入图像的上三分之一。我认为,对于任何一张道路图像,这部分会包含很少关于车道检测的信息。而且该模型经过训练后轻松达到与透视变换模型相近的效果,所以我知道该模型的输入图像不必是经过透视变换后的数据集了。&/p&&p&在这一点上,我想将一些不同相机拍摄的额外数据输入该模型,以解决相机的扭曲问题,所以我还使用了一些从Udacity其他项目中获得的视频数据集。然而,我们需要为新数据创建对应标签,因为之前用于标记图像的透视变换方法不适用于这些视频。这也使我意识到了另一个大问题:图像标签本身就是鸟瞰图中的多项式系数,就意味着在预测和绘制车道线后,仍然需要反向变换,恢复到原始图像的视角。&/p&&p&这就导致了该模型具有特殊性,仅能在我录制的视频中查看车道检测效果。我意识到该模型也许已经能正确地检测车道,但是在后期预测中使用了透视变换,所以无法重现良好的视觉效果。我认为,该模型似乎在转换视角上存在困难,所以可以通过查看各层的激活情况,来判断该模型是否已经学会检测车道。&/p&&h1&keras-vis的激活图&/h1&&p&我很快找到了所需的keras-vis库。keras-vis库很好上手,只需将训练好的模型传给对应函数,就可以返回对应层的激活图。这个函数一般在分类神经网络中辨别各类特征,但在这里我用来可视化多项式系数。&/p&&p&&img src=&/v2-14a411cbe7db04ee923e7f7_b.png& data-rawwidth=&991& data-rawheight=&174& class=&origin_image zh-lightbox-thumb& width=&991& data-original=&/v2-14a411cbe7db04ee923e7f7_r.png&&图12:前几个网络层的激活图(请注意裁剪层处于这些网络层之前)&/p&&p&看到这些卷积神经网络处理后的道路图像,这是一件很有趣的事。虽然第一层的图像效果看起来不错,但是这种方法还存在一些问题。&/p&&p&首先,该模型在处理多张弯曲道路的图像后,得到了车道的一条线。该模型已经学习到,两条车道线之间存在联系,因为在大多数情况下,车道线是互相平行的,所以如果识别出一条线,那就可以推理出另一条线的所在位置。可能是由于使用过生成器翻转图像,所以在处理天空部分时激活值大幅变化。该模型会把图像中的天空误定位为车道,所以如果想在原始视频中标出车道,必须以某种方式删除这部分被错误激活的区域。&/p&&p&&img src=&/v2-81c0e50b2e5f7877ad1fcbdcf52db9cb_b.png& data-rawwidth=&989& data-rawheight=&174& class=&origin_image zh-lightbox-thumb& width=&989& data-original=&/v2-81c0e50b2e5f7877ad1fcbdcf52db9cb_r.png&&图13:可视化深层网络&/p&&p&第二个问题更加难解决。由于在弯曲道路的图像中倾向于对单车道线和天空区域进行激活,在笔直道路的图像中通常会激活图片底部的汽车本身,或者是汽车前方的区域,而不是标出车道的位置。我进一步增加了直道图像的数量,检测效果有时会变好。但是在弯道和直道之间,激活规律没有任何一致性,所以不能深入研究这种方法。&/p&&h1&迁移学习&/h1&&p&我还使用keras-vis库尝试了迁移学习(Transfer Learning)的方法。在之前的行为克隆项目中,我已经使一辆模拟车已经学会了根据训练好的神经网络呈现出的图像来引导自动驾驶。这个我使用了超过20,000张图像训练得到的模型,会怎么看待车道呢?&/p&&p&&img src=&/v2-f2afa645f6cec324d7028_b.jpg& data-rawwidth=&320& data-rawheight=&160& class=&content_image& width=&320&&图14:一张模拟车的输入图像&/p&&p&这个问题的答案是整条道路,因为Udacity模拟器里没有隔离出多条车道,但是我想知道我是否可以使用迁移学习来将模型的注意力集中在车道上。我希望利用这个项目20000张数据集的基础上,添加一些用于车道检测的新数据,并进行一些额外的训练,希望能有更好的激活效果。在从该项目加载训练模型之后,使用model.pop()函数移除了最终用于输出转向角的输出层,并将其替换为输出六个标签系数。在训练前,需要根据模型的输入来调整输入图像的大小,否则不能正常运行。&/p&&p&经过一些额外的训练与输入自己的数据集,这个模型效果略有提升,从检测整个道路,开始转为识别车道线。但是,该模型仍然存在一致性问题,如哪些图像区域该被激活和对应的激活程度等等。&/p&&h1&一种完全卷积方法&/h1&&p&我感觉这些方法行不通,所以开始寻找一种新的方法。最近我看到一些小组在处理汽车拍摄的图像时采取图像分割的方法:将一个给定的图像分成如道路、汽车、人行道和建筑物等等多类对象。SegNet网络是一种很有趣的图像分割技术,在使用时可直接调用标准的模型结构。这种标准模型包含了带有BN层和Relu激活层的卷积结构、上采样层、池化层以及从中点到网络输出的反卷积层。这种方法没有添加全连接层,就能直接构建出一个完全卷积神经网络。&/p&&p&这似乎也是一个不错的方法,但是车道线可能还会以错误的方式被绘制,为什么不采用神经网络来直接预测车道线本身?所以我调整一下,网络的输入仍是道路图像,但输出为经过绘制的车道线图像。由于我是用绿线标出车道线的,我决定让模型的输出“过滤器”只对RGB的“G”通道起作用。对于RB通道,使用了两个空白过滤器,使相应图像与原车道图像结合。这种方法去除了水平翻转图像会对预测系数产生不良影响的担忧,也加倍了数据集的样本量。在使用这种方法预处理数据时,我可以同时翻转道路图像和车道图像标签。&/p&&p&&img src=&/v2-b8bf238d0e0ecbee7bd95c8_b.png& data-rawwidth=&442& data-rawheight=&222& class=&origin_image zh-lightbox-thumb& width=&442& data-original=&/v2-b8bf238d0e0ecbee7bd95c8_r.png&&图15:作为新标签的车道图像&/p&&p&在这里,我重新整理了数据集:&/p&&ol&&li&&p&在原始数据集中有1,420张图像(在10帧中取1帧操作后),并删除了227个不能合适标注的图像;&/p&&/li&&li&&p&在弯曲道路的视频中,一共有1636张图像,我从中挑选了568张图像;&/p&&/li&&li&&p&在Udacity常规项目里给出的视频中,又挑出了另外217张图像;&/p&&/li&&li&&p&上述总计1,978张图像;&/p&&/li&&li&&p&样本量还太少,且各类分布不均匀,所以小幅度图像旋转后效果不好。在针对性调整后,得到了6,382张图像;&/p&&/li&&li&&p&再次通过水平翻转,样本量加倍,得到了12,764张图片。&/p&&/li&&/ol&&p&只要确保道路图像、系数标签和车道图像标签三者互相关联,我仍然可以旋转这些带有这些新标签的图像,无需考虑各系数的分布情况。&/p&&p&由于我从没使用过完全卷积神经网络,所以我按照SegNet网络结构来严格构建网络。幸运的是,我可以用Keras库快速构建,需要注意的是在添加反卷积层后,要确保网络输出图像和输入大小保持一致。在池化层之后,我将输入图像的长宽比调整为80 x 160 x 3(原始宽高比为90 x 160 x 3),并完全镜像前半部模型。因为所使用的池化层大小为2 x 2,如果输入维度为90,很快就不能被2整除,导致模型后半部分构建镜像时出现问题。&/p&&p&&img src=&/v2-fbd77c31d87caaffadc5_b.png& data-rawwidth=&632& data-rawheight=&222& class=&origin_image zh-lightbox-thumb& width=&632& data-original=&/v2-fbd77c31d87caaffadc5_r.png&&图16:SegNet网络结构示意图(从左到右)&/p&&p&在模型训练时存在一些小问题,网络过大导致内存溢出,所以我为每个卷积层和反卷积层添加了BN层和Dropout层,这是减小网络大小和防止过拟合的最有效方法。我在网络的开头设置了BN层,在网络中添加了Dropout层。由于只使用了“G”颜色通道,最终反卷积层的输出大小为80 x 160,这样更容易地与原始路面图像匹配。虽然我调小了输入图像,但是没有大影响。我也将道路图像标记除以255,进行归一化,这能改善收敛时间和最终结果,但是意味着在预测后需要对输出乘以255来恢复维度。&/p&&p&&img src=&/v2-c65382dfe717ae1ef7514_b.png& data-rawwidth=&628& data-rawheight=&329& class=&origin_image zh-lightbox-thumb& width=&628& data-original=&/v2-c65382dfe717ae1ef7514_r.png&&图17:不同模型的效果对比&/p&&p&从视频中可以看出,最终的预测效果不错。但是该视频已经被训练过,所以实际效果可能会虚高。为了检测实际效果,我们使用了车道检测项目中的另一个测试视频,发现该网络对这个视频的预测效果也很好。虽然在处理高速公路立交桥的阴影时存在小问题,但我们很高兴用这个视频证明了该模型的效果。此外,该模型比原有模型的处理速度更快,通常在GPU加速下每秒能处理25-29帧,实时可达到30 FPS。在没有GPU加速时,其每秒5.5FPS的处理速度仍然比每秒为4.5 FPS的CV模型稍快一些。&/p&&p&&img src=&/v2-fb4d47af3b825e_b.png& data-rawwidth=&680& data-rawheight=&165& class=&origin_image zh-lightbox-thumb& width=&680& data-original=&/v2-fb4d47af3b825e_r.png&&图18:传统的CV模型与SegNet模型做对比,在这里CV模型误认为两条车道线都在右边。&/p&&h1&改进方向&/h1&&p&这就是我利用深度学习实现车道检测的全部过程。虽然不是很完美,但它比传统的CV模型更强大。我们也尝试在难度更大的测试视频中识别车道线,从结果中发现了一些问题:在光线和阴影的过渡时或者当强光照到车窗时无法准确预测车道线。以下是我接下来改进模型的一些方向:&/p&&ol&&li&&p&更多数据集。这是应用深度学习方法很重要的一点,通过获取在不同条件下(如光线和阴影过渡时)和更多不同相机的数据,可以进一步提升该模型;&/p&&/li&&li&&p&加入循环神经网络(Recurrent Neural Network)。我认为如果结合RNN网络强大的时间信息预测能力,这将是一个非常棒的方法。接下来我将研究递归方法在定位方面的应用,希望能在这方面再创建一种新的车道检测方法;&/p&&/li&&li&&p&使用没有或只有一条车道线的道路数据集。因为在郊区或部分公路不会标记车道线,所以这种模型有更强的推广性;&/p&&/li&&li&&p&扩展模型,用来检测更多的对象。类比于图像分割,可以添加车辆和行人检测的功能。上述模型只使用了“G”通道,接下来我们可以使用但不限于“R”和“B”通道,这种方法可能会优于常规的图像分割方法。&/p&&/li&&/ol&&h1&相关链接&/h1&&p&该项目的完整程序请查看Github链接:&/p&&a href=&/?target=https%3A///mvirgo/MLND-Capstone& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&mvirgo/MLND-Capstone&i class=&icon-external&&&/i&&/a&&p&【完】&/p&&p&One More Thing…&/p&&p&今天AI界还有哪些事值得关注?在量子位(QbitAI)公众号对话界面回复“今天”,看我们全网搜罗的AI行业和研究动态。笔芯~&/p&
编译自 Medium 量子位 出品 | 公众号 QbitAI找到马路上的车道线,对于人类来说非常容易,但对计算机来说,一点阴影、反光、道路颜色的微小变化、或者车道线被部分遮挡,都会带来很大的困难。正在Udacity学习自动驾驶课程的Michael Virgo写了两篇博客…
&img src=&/v2-6b6e486dfdbee89fc2af5f_b.jpg& data-rawwidth=&1024& data-rawheight=&600& class=&origin_image zh-lightbox-thumb& width=&1024& data-original=&/v2-6b6e486dfdbee89fc2af5f_r.jpg&&&p&斯坦福大学的课程 CS231n (Convolutional Neural Networks for Visual Recognition) 作为深度学习和计算机视觉方面的重要基础课程,在学界广受推崇。今年 4 月,CS231n 再度开课,全新的 CS231n Spring 2017 仍旧由李飞飞带头,带来了很多新鲜的内容。今天机器之心给大家分享的是其中的第八讲——深度学习软件(Deep Learning Software)。主要内容有:CPU 和 GPU 的对比;深度学习框架简介;TensorFlow 和 PyTorch 的实例;以及各种深度学习框架的比较。&/p&&p&&b&一、 CPU 和 GPU&/b&&/p&&p& CPU:核芯的数量更少;&/p&&p&
但是每一个核芯的速度更快,性能更强;&/p&&p&
更适用于处理连续性(sequential)任务。&/p&&p& GPU:核芯的数量更多;&/p&&p&
但是每一个核芯的处理速度较慢;&/p&&p&
更适用于并行(parallel)任务。&/p&&img src=&/v2-8f718d0b7fdea53bbca376ded59c5885_b.png& data-rawheight=&468& data-rawwidth=&716& class=&origin_image zh-lightbox-thumb& width=&716& data-original=&/v2-8f718d0b7fdea53bbca376ded59c5885_r.png&&&br&&p&&b&二、深度学习框架简介&/b&&/p&&p&去年我们还仅有 Caffe、Torch、Theano 和 TensorFlow 这些深度学习框架可供使用;但是到了今年,在此基础上我们又新增加了 Caffe2、Pytorch、TensorFlow、PaddlePaddle、 CNTK、MXNet 等等一系列新的框架,可谓「百花齐放」。如今最常用的框架当数 Pytorch 和 TensorFlow 了, 而 Caffe 和 Caffe2 次之。&/p&&img src=&/v2-aec45eaa75b561c88fc73_b.png& data-rawheight=&280& data-rawwidth=&523& class=&origin_image zh-lightbox-thumb& width=&523& data-original=&/v2-aec45eaa75b561c88fc73_r.png&&&br&&p&深度学习框架的关键点在于:&/p&&p&(1)易于建造大型的计算机图形;&/p&&p&(2)易于在计算机图形中进行梯度计算;&/p&&p&(3)能在 GPU 上高效运行(cuDNN, cuBLA 等)&/p&&p&&b&三、TensorFlow 简单实例&/b&&/p&&p&下面我们将详细说明一个在 TensorFlow 下训练神经网络的简单实例:即用随机数据训练一个两层的网络,激活函数为 ReLU。&/p&&p&&b&a. 定义计算机图形&/b&&/p&&img src=&/v2-ec2f3b2e22c3a00cf195d9eaeb2b166c_b.png& data-rawheight=&399& data-rawwidth=&868& class=&origin_image zh-lightbox-thumb& width=&868& data-original=&/v2-ec2f3b2e22c3a00cf195d9eaeb2b166c_r.png&&&br&&p&1. 为输入 x,权重系数 w1、w2, 和目标函数 y 创建 placeholder:&/p&&img src=&/v2-3b418fe02f5e187a9d7c69dc99c661d6_b.png& data-rawheight=&145& data-rawwidth=&708& class=&origin_image zh-lightbox-thumb& width=&708& data-original=&/v2-3b418fe02f5e187a9d7c69dc99c661d6_r.png&&&br&&p&2. 定义前向传输:这是为了计算 y 的预测值和误差损失(loss);实际上这里是没有计算过程的——仅仅是为了创建图形!&img src=&/v2-455757ebd410efb06a99f6_b.png& data-rawheight=&131& data-rawwidth=&863& class=&origin_image zh-lightbox-thumb& width=&863& data-original=&/v2-455757ebd410efb06a99f6_r.png&&&/p&&br&&p&3. 告诉 Tensorflow 去计算关于 w1 和 w2 的梯度损失;这里仍然不产生计算过程——仅仅是为了创建图形。&img src=&/v2-45a16d9cbee0e3ecb85b2d_b.png& data-rawheight=&68& data-rawwidth=&796& class=&origin_image zh-lightbox-thumb& width=&796& data-original=&/v2-45a16d9cbee0e3ecb85b2d_r.png&&&/p&&br&&p&&b&b. 运行&/b&&/p&&p&现在已经完成了创建图形的步骤,所以我们进入对图形进行运算的部分。&img src=&/v2-196e25b0bb7e5e0dbf868d_b.png& data-rawheight=&274& data-rawwidth=&823& class=&origin_image zh-lightbox-thumb& width=&823& data-original=&/v2-196e25b0bb7e5e0dbf868d_r.png&&&/p&&br&&p&创建 Numpy 数组,这个数组将会被填进上方的 placeholder 中。&img src=&/v2-ea0762fac2eeff43fd96_b.png& data-rawheight=&132& data-rawwidth=&788& class=&origin_image zh-lightbox-thumb& width=&788& data-original=&/v2-ea0762fac2eeff43fd96_r.png&&&/p&&br&&p&对图形进行运算:将 x、y、w1、w2 输入到 numpy 数组中;得到关于损失(loss),w1 梯度和 w2 梯度的 numpy 数组。&img src=&/v2-050aeede88cba143d377_b.png& data-rawheight=&122& data-rawwidth=&847& class=&origin_image zh-lightbox-thumb& width=&847& data-original=&/v2-050aeede88cba143d377_r.png&&&/p&&br&&p&训练网络:反复对图形进行运算,用梯度(gradient)来更新权重(weights)。&img src=&/v2-ba40a53b6c0268_b.png& data-rawheight=&192& data-rawwidth=&713& class=&origin_image zh-lightbox-thumb& width=&713& data-original=&/v2-ba40a53b6c0268_r.png&&&/p&&br&&p&把 w1 和 w2 的相应函数从 placeholder() 改为 Variable()。&img src=&/v2-dcaf87e96bcb_b.png& data-rawheight=&81& data-rawwidth=&695& class=&origin_image zh-lightbox-thumb& width=&695& data-original=&/v2-dcaf87e96bcb_r.png&&&/p&&br&&p&添加 assign 操作来更新 w1 和 w2(图形的一部分)。&img src=&/v2-f5fb926ce2c563f4bffde_b.png& data-rawheight=&112& data-rawwidth=&809& class=&origin_image zh-lightbox-thumb& width=&809& data-original=&/v2-f5fb926ce2c563f4bffde_r.png&&&/p&&br&&p&对图形进行一次运算来初始化 w1 和 w2,然后进行多次迭代训练。&img src=&/v2-715b227efe281bbebdd65a7c_b.png& data-rawheight=&210& data-rawwidth=&897& class=&origin_image zh-lightbox-thumb& width=&897& data-original=&/v2-715b227efe281bbebdd65a7c_r.png&&&/p&&br&&p&完整代码如下:&img src=&/v2-68afd820a2c_b.png& data-rawheight=&718& data-rawwidth=&895& class=&origin_image zh-lightbox-thumb& width=&895& data-original=&/v2-68afd820a2c_r.png&&&/p&&br&&p&但是产生一个问题:误差损失(loss)并没有下降!这是因为 Assign 指令实际上并没有被执行。&img src=&/v2-7ffdbac690a4_b.png& data-rawheight=&275& data-rawwidth=&397& class=&content_image& width=&397&&&/p&&br&&p&这时我们就需要添加虚拟图形节点,并且告诉图形去计算虚拟节点。&img src=&/v2-1dbf6167a_b.png& data-rawheight=&714& data-rawwidth=&792& class=&origin_image zh-lightbox-thumb& width=&792& data-original=&/v2-1dbf6167a_r.png&&&/p&&br&&p&可以使用 optimizer 来计算梯度和更新权重系数;记得要执行 optimizer 的输出!&img src=&/v2-beeeaadf057be31cc8027b_b.png& data-rawheight=&379& data-rawwidth=&844& class=&origin_image zh-lightbox-thumb& width=&844& data-original=&/v2-beeeaadf057be31cc8027b_r.png&&&/p&&br&&p&使用预先定义的常用损失函数:&img src=&/v2-dc84c682d7ee3c1d216397_b.png& data-rawheight=&403& data-rawwidth=&943& class=&origin_image zh-lightbox-thumb& width=&943& data-original=&/v2-dc84c682d7ee3c1d216397_r.png&&&/p&&br&&p&使用 Xavier 进行初始化;tf.layer 会自动设置权重系数(weight)和偏置项(bias)!&img src=&/v2-7fb1b75ba7dcc94bb08f6d2a_b.png& data-rawheight=&711& data-rawwidth=&937& class=&origin_image zh-lightbox-thumb& width=&937& data-original=&/v2-7fb1b75ba7dcc94bb08f6d2a_r.png&&&/p&&br&&p&&b&c. 高级 Wrapper——Keras&/b&&/p&&p&Keras 可以理解为是一个在 TensorFlow 顶部的 layer,它可以让一些工作变得更加简单(也支持 Theano 后端)。&img src=&/v2-f4ca08ec6a7da31a24f63365e5faeb99_b.png& data-rawheight=&460& data-rawwidth=&545& class=&origin_image zh-lightbox-thumb& width=&545& data-original=&/v2-f4ca08ec6a7da31a24f63365e5faeb99_r.png&&&/p&&br&&p&把模型目标定义成一系列的 layer :&img src=&/v2-dfc440d4ac_b.png& data-rawheight=&115& data-rawwidth=&521& class=&origin_image zh-lightbox-thumb& width=&521& data-original=&/v2-dfc440d4ac_r.png&&&/p&&br&&p&定义优化器目标(optimizer object):&img src=&/v2-ae1ad62f6_b.png& data-rawheight=&39& data-rawwidth=&520& class=&origin_image zh-lightbox-thumb& width=&520& data-original=&/v2-ae1ad62f6_r.png&&&/p&&br&&p&创建模型,明确规定损失函数(loss function):&img src=&/v2-fa73facde6_b.png& data-rawheight=&59& data-rawwidth=&517& class=&origin_image zh-lightbox-thumb& width=&517& data-original=&/v2-fa73facde6_r.png&&&/p&&br&&p&仅用一行代码就能训练模型!&img src=&/v2-7dfcba38c08eee83b612bbcd_b.png& data-rawheight=&58& data-rawwidth=&518& class=&origin_image zh-lightbox-thumb& width=&518& data-original=&/v2-7dfcba38c08eee83b612bbcd_r.png&&&/p&&br&&p&除了 Keras, 还有一些其他类型的高级容器(Wrapper)可供使用:&img src=&/v2-05ebf796c35368afbdbdd0dc3c9ea385_b.png& data-rawheight=&412& data-rawwidth=&830& class=&origin_image zh-lightbox-thumb& width=&830& data-original=&/v2-05ebf796c35368afbdbdd0dc3c9ea385_r.png&&&/p&&br&&p&&b&四、PyTorch 实例&/b&&/p&&p&PyTorch 是 Facebook 推出的深度学习框架,不论是在工业界还是学术界,它都得到了广泛的应用。它包括三个等级的抽象概念:&/p&&ul&&li&张量(Tensor):命令式的多维数组对象(ndarray),在 GPU 上运行;&/li&&li&变量(Varaible):计算型图形(computational graph)的节点;用于存储数据和梯度(gradient)&/li&&li&模块(Module):代表一个神经网络层;可以存储状态(state), 也可以存储可学习的权重系数(learnable weights)&/li&&/ul&&p&PyTorch 和 TensorFlow 中抽象概念的等价对应关系:&img src=&/v2-8e799a89bcbb_b.png& data-rawheight=&189& data-rawwidth=&757& class=&origin_image zh-lightbox-thumb& width=&757& data-original=&/v2-8e799a89bcbb_r.png&&&/p&&p&a. Pytorch 中的张量(Tensor)设置&/p&&p&PyTorch 中的张量就像 numpy 中的数组,但是这些张量可以在 GPU 上运行;&/p&&p&这里我们用 PyTorch 的张量设置了一个两层网络:&img src=&/v2-7eda3896183ffda11e923_b.png& data-rawheight=&544& data-rawwidth=&422& class=&origin_image zh-lightbox-thumb& width=&422& data-original=&/v2-7eda3896183ffda11e923_r.png&&&/p&&br&&p&下面我们来分步解读:&/p&&p&1. 为数据和权重(weights)创建随机张量:&img src=&/v2-a7fb307f70f16e75ce6c5e_b.png& data-rawheight=&110& data-rawwidth=&372& class=&content_image& width=&372&&&/p&&br&&p&2. 设置前向传播:计算预测值(prediction)和损失(loss):&img src=&/v2-74cdfbb5c98b_b.png& data-rawheight=&76& data-rawwidth=&371& class=&content_image& width=&371&&&/p&&br&&p&3. 设置反向传播:计算梯度(gradients):&img src=&/v2-1eec48a301f37bfa5dac58fe43a099de_b.png& data-rawheight=&125& data-rawwidth=&371& class=&content_image& width=&371&&&/p&&br&&p&4. 梯度下降(Gradient descent)和权重(weights)相对应:&img src=&/v2-30912ced327f4d226ceaa86_b.png& data-rawheight=&60& data-rawwidth=&369& class=&content_image& width=&369&&&/p&&br&&p&5. 为了在 GPU 上运行,将张量(tensors)设置为 cuda 数据类型:&img src=&/v2-495bf5a3c0f6b2a50aca8bca311f49a1_b.png& data-rawheight=&69& data-rawwidth=&369& class=&content_image& width=&369&&&/p&&br&&p&b. PyTorch 中的 Autogradient 设置&/p&&p&PyTorch 的张量(Tensors)和变量(Variables)拥有相同的应用编程接口 API。变量(Variables)可以记忆它们是怎么产生的(因为反向传播的缘故)。&img src=&/v2-7077fdaf6654d85edd46_b.png& data-rawheight=&380& data-rawwidth=&525& class=&origin_image zh-lightbox-thumb& width=&525& data-original=&/v2-7077fdaf6654d85edd46_r.png&&&/p&&br&&p&下面仍进行分步解读:&/p&&p&1. 我们不希望(损失 loss 的)梯度和数据(data)有相关性,但我们希望梯度和权重(weights)是相关的。相关设置如图: &img src=&/v2-35db1f58bff_b.png& data-rawheight=&105& data-rawwidth=&522& class=&origin_image zh-lightbox-thumb& width=&522& data-original=&/v2-35db1f58bff_r.png&&&/p&&br&&p&2. 这里的前向传播看上去和上述张量(Tensor)的对应版本很相似,但是需要注意的是现在这里全部都是变量(variable)。&img src=&/v2-f117dfc641bd443f781af9b864e7a48c_b.png& data-rawheight=&46& data-rawwidth=&532& class=&origin_image zh-lightbox-thumb& width=&532& data-original=&/v2-f117dfc641bd443f781af9b864e7a48c_r.png&&&/p&&br&&p&3. 计算损失函数对 w1 和 w2 的梯度(开始的时候梯度置零):&img src=&/v2-b78ab0e5fd0f06e99b272d_b.png& data-rawheight=&69& data-rawwidth=&530& class=&origin_image zh-lightbox-thumb& width=&530& data-original=&/v2-b78ab0e5fd0f06e99b272d_r.png&&&/p&&br&&p&4. 让梯度和权重(weights)相对应:&img src=&/v2-0b9cfee5c84f677da48cd40_b.png& data-rawheight=&62& data-rawwidth=&529& class=&origin_image zh-lightbox-thumb& width=&529& data-original=&/v2-0b9cfee5c84f677da48cd40_r.png&&&/p&&br&&p&C. 定义新型 Autograd 函数&/p&&p&通过张量的前向和反向传播来定义你自己的 autograd 函数:&img src=&/v2-159d9fba4ff1f776c6a961_b.png& data-rawheight=&296& data-rawwidth=&521& class=}

我要回帖

更多关于 变频器型号 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信