简介
- 深度学习(deep learning)是机器学习的分支,是一种以人工神经网络为架构,对资料进行表征学习的算法。
- 深度学习的好处是用非监督式或半监督式的特征学习和分层提取高效算法来替代手工获取特征。
- 表征学习的目标是寻求更好的表示方法并建立更好的模型来从大规模未标记数据中学习这些表示方法。表示方法来自神经科学,并松散地建立在类似神经系统中的信息处理和对通信模式的理解上
- 至今已有深度学习框架,如
- 深度神经网络
- 卷积神经网络
- 深度置信网络
- 循环神经网络
- 深度学习的基础是机器学习中的分散表示(distributed representation)。分散表示假定观测值是由不同因子相互作用生成。在此基础上,深度学习进一步假定这一相互作用的过程可分为多个层次,代表对观测值的多层抽象。不同的层数和层的规模可用于不同程度的抽象。
神经网络(西瓜书)
- 神经元模型
- 神经网络是由具有适应性的简单单元组成的广泛并行互连的网络,它的组织能够模拟生物神经系统对真实世界物体所做出的交互反应。[Kohonen, 1988]
- 神经网络中最基本的成分是神经元(neuron)模型,即上述定义中的“简单单元”
- 感知机与多层网络
- 感知机(Perceptron)由两层神经元组成,输入层接受外界输入信号后传递给输出层,输出层是M-P神经元,亦称“阈值逻辑单元”(threshold logic unit)
- 输出层与输入层之间的一层神经元,被称为隐层或隐含层(hidden layer),隐含层和输出层神经元都是拥有激活函数的功能神经元
- 常见的神经网络,每层神经元与下一层神经元全互连,神经元之间不存在同层连接,也不存在跨层连接,这样的神经网络结构通常称为“多层前馈神经网络”(multi-layer feedforward neural networks),其中输入层神经元接受外界输入,隐层与输出层神经元对信号进行加工,最终结果由输出层神经元输出;换言之,输入层神经元仅是接受输入,不进行函数处理,隐层与输出层包含功能神经元。
- “前馈”并不意味着网络中信号不能向后传,而是指网络拓扑结构上不存在环或回路
- 只需要包含隐层,即可称为多层网络。
- 神经网络的学习过程,就是根据训练数据来调整神经元之间的“连接权”(connection weight)以及每个功能神经元的阈值;换言之,神经网络“学”到的东西,蕴涵在连接权与阈值中。
- 误差逆传播算法(亦称“反向传播算法”)
- 多层网络的学习能力比单层感知机强得多,想要训练多层网络,简单感知机学习规则显然不够,需要更加强大的学习算法,误差逆传播算法(errorBackPropagation, BP)就是其中最杰出的代码,它是迄今为止最成功的神经网络学习算法。
- BP算法不仅可用于多层前馈神经网络,还可用于其他类型的神经网路,例如训练递归神经网络,但是通常说“BP网络”时,一般是指用BP算法训练的多层前馈神经网络。
- 深度学习
- 理论上来说,参数越多的模型复杂度越高、容量越大,这意味着它能够完成更复杂的学习任务。但是在一般情形下,复杂模型的训练效率低,易陷入过拟合,因此难以受到人们青睐
- 我们可以从另外一个角度来理解深度学习。无论是DBN还是CNN,其多隐层堆叠、每层对上一层的输出进行处理的机制,可看作是在对输入信号进行逐层加工,从而把初始的、与输出目标之间联系不太密切的输入表示,转化成与输出目标联系更密切的表示,使得原来仅基于最后一层输出映射难以完成的任务成为可能。换言之,通过多层处理,逐渐将初始的“低层”特征表示转化为“高层”特征表示后,用“简单模型”即可完成复杂的分类等学习任务,由此可将深度学习理解为“特征学习”(feature learning)或“表示学习”(representation learning)
深度神经网络(Deep Neural Network, DNN)
- 深度神经网络是一种具备至少一个隐层的神经网络。与浅层神经网络类似,深度神经网络也能够为复杂非线性系统提供建模,但多出的层次为模型提供了更高的抽象层次,因而提高了模型的能力。深度神经网络通常都是前馈神经网络,但也有语言建模方面的研究将其拓展到循环神经网络。卷积神经网络在计算机视觉领域得到了成功的应用。
- 深度神经网络是一种判别模型,可以使用反向传播算法进行训练。权重更新可以使用下式进行随机梯度下降法求解。
- 与其它神经网络模型类似,如果仅仅是简单地训练,深度神经网络可能会存在很多问题。常见的两类问题是过拟合和过长的运算时间。
Cmake & VScode
Create a CMake project
- makdir cmakeQuickStart
- cd cmakeQuickStart
- code .
- Open the Command Palette and run the CMake:Quick Start command
- main.cpp , CMakeLists.txt(which tells the CMake tools how to build your project)
- CMake Tools 插件能够给C/C++插件提供信息,实现IntelliSense、代码补全、注释浏览、文件跳转等功能
Opencv DNN
框架
- 在深度学习初始阶段,每个深度学习研究者都需要写大量的重复代码。为了提高工作效率,这些研究者就将这些代码写成了一个框架放到网上让所有研究者一起使用。接着,网上就出现了不同的框架。随着时间的推移,最为好用的几个框架被大量的人使用从而流行了起来。全世界最为流行的深度学习框架有
- PaddlePaddle(Baidu)
- PaddlePaddle是Baidu研发的开源开放的深度学习平台,是国内最早开源、也是当前唯一一个功能完备的深度学习平台。
- Tensorflow(Google)
- Caffe
- Theano
- MXNet
- Torch(NYU/Facebook)
- Torch是Facebook的开源机器学习库、科学计算框架和基于Lua编程语言的脚本语言。
- PyTorch
- PaddlePaddle(Baidu)
PyTorch
- PyTorch是一个开源的Python机器学习库,基于Torch,用于自然语言处理等应用程序
TensorFlow(符号数学系统)
- TensorFlow是一个基于数据流变成(dataflow programming)的符号数学系统,被广泛应用于各类机器学习(machine learning)算法的编程实现,其前身是谷歌的神经网络算法库DistBelief。
Caffe
- Caffe(Convolutional Architecture for Fast Feature Embedding),卷积神经网络框架,是一个兼具表达性、速度和四维模块化的深度学习框架。
- 结构:
- 简单来讲,Caffe中的数据结构是以Blobs-layers-Net形式存在。其中,Blobs是通过4维向量形式(num, channel, height, width)存储网络中所有权重,激活值以及正向反向的数据。
- 作为Caffe的标准数据格式,Blob提供了统一内存借口。Layers表示的是神经网络中具体层,例如卷积层等,是Caffe模型的本质内容和执行计算的基本单元。layer层接受底层输入的Blobs,向高层输出Blobs。在每层会实现前向传播,后向传播。Net是由多个层连接在一起,组成的有向无环图。一个网络讲最初的data数据层加载数据开始到最后的loss层组合为整体。
ONNX
- 了解Open Neural Network Exchange(ONNX)来帮助优化机器学习模型的推理。推理或模型评分是将部署的模型用于预测(通常针对生产数据)的阶段。
- 什么是ONNX?
- ONNX(Open Neural Network Exchange),开放神经网络交换,是一种模型IR,用于在各种深度学习训练和推理框架转换的一个中间表示格式。在实际业务中,可以使用Pytorch或者TensorFlow训练模型,导出ONNX格式,然后在转换成目标设备上支撑的模型格式,比如TensorRT Engine、 NCNN、MNN等格式。ONNX定义了一组和环境、平台均无关的标准格式,来增强各种AI模型的可交互性,开放性较强。
- 换句话说,无论你使用何种训练框架训练模型(例如TensorFlow/Pytorch/OneFlow/Paddle),在训练完毕后你都可以将这些框架的模型统一转换为ONNX这种统一的格式进行存储,转为ONNX模型,然后就可以放在其他框架上面去推理(inference)。
- ProtoBuf简介
- 在分析ONNX组织格式前,需要了解Protobuf。ONNX作为一个文件格式,自然需要一定的规则去读取想要的信息或者写入需要保存的信息。ONNX使用的是Protobuf这个序列化数据结构去存储神经网络的圈中信息。熟悉Caffe(深度学习框架)或者Caffe2的应该知道,它们的模型存储数据结构协议也是Protobuf。
- Protobuf是一种轻便高校的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。目前提供了C++、Java、Python三种语言的API
- ONNX中最核心的部分是onnx.proto。它定义了ONNX这个数据协议的规则和一些其他信息,其中最核心的几个对象:
- ModelProto
- 包含了一些版本信息,生产者信息和一个GraphProto
- GraphProto
- 包含了四个repeated数组,分别是
- node(NodeProto类型)
- 存放模型中所有的计算节点
- input(ValueInfoProto类型
- 存放模型的输入节点
- output(ValueInfoProto类型)
- 存放模型中所有的输出节点
- initializer(TensorProto类型)
- 存放模型的所有权重参数
- node(NodeProto类型)
- 包含了四个repeated数组,分别是
- NodeProto
- ValueInfoProto
- TensorProto
- AttributeProto
- ModelProto
使用Google inception-5h实现图像分类
- DNN模块介绍:
- OpenCV的DNN模块是在OpenCV3.3合并到OpenCV release中的,它最早是在扩展模块中的,可以导入caffe、tensorflow、pytorch等深度学习框架训练生成的模型文件,从而正向传递实现预测功能
- 加载模型读取网络信息
- 模型可以使用readNet API来加载
Net cv::dnn::readNet(const String& model, const String& config = String(), const String& framework = String());
- model是训练好的二进制网络权重文件,支持多种框架训练出来的模型
- config是二进制模型的描述文件,不同的框架配置文件有不同的扩展名
- framework则声明模型对应框架名称
- 也可以使用
Net readNetFromTensorflow(const String& model, const String& config = String());
Net readNetFromCaffe(const String& prototxt, const String& caffeModel = String());
- 其他等API直接加载对应框架训练出来的模型
- 模型可以使用readNet API来加载
- 模型正向传递预测
使用模型实现预测的时候,需要读取图像作为输入,网络模型支持的输入数据是四维的输入,所以要把读取到的Mat对象转换为四维张量,OpenCV提供的API如下
1
2
3
4
5
6
7
8
9
10
11{
Mat blobFromImage(
InputArray image,
double scalefactor = 1.0,
const Size& size = Size(),
const Scalar& mean = Scalar(),
bool swapRB = false,
bool crop = false,
int ddepth = CV_32F
)
}- image:输入图像
- scalefactor:默认是1.0
- size表示网络接受的数据大小
- mean表示训练时数据集的均值
- swapRB是否互换Red与Blur通道
- crop是剪切
- ddepth是数据类型
模型的输入和正向传递则使用如下两个API
void setInput(InputArray blob, const String& name = "");
Mat forward(const String& outputName = String());
- 输出预测结果
- 首先读取标签文件,定义一个读取文件的额函数read_class_names();
模块(DNN)的应用–图像分类
训练(training)和推理(inference)
- 训练(training)包含了前向传播和后向传播两个阶段,针对的是训练集,训练时通过误差反向传播来不断修改网络权值(weights)
- 训练一个神经网络时,训练数据被输入该网络的第一层,同时一个神经元会基于所执行的任务为该输入分配一个权重–即该输入正确与否的程度
- 在一个用于图像识别的网络中,第一层可能是用来寻找图像的边缘。第二层可能是寻找这些边所构成的形状–矩形或者是圆形。第三层可能是寻找特定的特征–比如闪亮的眼睛或者按钮式的鼻子。每一层都会将图像传递给下一层,直到最后一层;最后的输出由该网络所产生的所有这些权重总体决定。
- 推理(inference)只包含前向传播一个阶段,针对的是除了训练集之外的新数据。可以是测试机,但不完全是,更多的是整个数据集之外的数据。其实就是针对新数据进行预测,预测时,速度是一个很重要的因素。
- 神经网络训练好了,已经为推理做好了准备
- 准确分配加权了的神经网络本质上是一个笨重的巨型数据库。此前,为了让它学习而准备的各种东西已经远远多余它在完成某一特定任务时所需要的内容
- 在现实世界中,如果有人想要使用这些训练,真正所需要的是一种可以保持学习并能将其学习成果应用于其从未见过的数据的快速应用,这就是推理:只需要少的多的真实数据,就能快速得出正确答案(实际上是对什么是正确的预测)
- 训练是为了让“猫”这个线索得以抽象,推理就是利用这个抽象在应用场景下给出是不是“猫”这个结论。
数据预处理与名字解释
- 深度学习中的数据预处理有哪些方式?
- 数据归一化。包括高斯归一化、最大最小值归一化等
- 白化。许多深度学习算法都以来于白化来获得更好的特征。所谓的白化,以PCA白化来说,就是对PCA降维后的数据的每一列除以其特征值的根号。
- 为什么需要图像预处理?
- 图像预处理的主要目的是消除图像中无关的信息,恢复有用的真实信息,增强有关信息的可检测性和最大限度地简化数据,从而改进特征抽取,图像分隔,匹配和识别的可靠性。
- 为什么需要对数据进行归一化处理,归一化的方式有哪些?
- 为了后面数据处理的方便,归一化的确可以避免一些不必要的数值问题
- 为了程序运行时收敛加快
- 同一量纲。样本数据的评价标准不一样,需要对其量纲化,统一评价标准。这算是应用层面的需求
- 避免神经元饱和。当神经元的激活在接近0或者1时会饱和,在这些区域,梯度几乎为0,这样,再反向传播过程中,局部梯度就会接近0,这样会有效地“杀死”梯度
- 保证输出数据中数值小的不被吞食
- 归一化的方式主要有:线性归一化、标准差归一化、非线性归一化
- 数据增强的方式有哪些?
- 翻转、旋转、缩放、裁剪、平移、尺度变化、添加噪声、色彩抖动、亮度调整、对比度增强
- 数据增强可以分为两类:离线增强和在线增强
- 离线增强:直接对数据集进行处理,数据的数目会变成增强因子乘以原数据集的数目,这种方法常常用于数据集很小的时候。
- 在线增强:这种增强的方法用于,获得batch数据之后,对这个batch的数据进行增强,如旋转、平移、翻转等相应的变化,由于有些数据集不能接受线性级别的增长,这种方法用于大的数据集
后处理
整体上说,后处理的CPU实现分为三部分:
- 框信息的预处理,类似一个decode过程
- NMS过程,实现框的选择和合并
- NMS过程是后处理的重点.基本原理是针对每一个类,每次循环选取这个类中概率最高的框与其他所有框计算IOU.如果某个框与最大概率框的IOU大于nms_thresh.这个框对应的概率被设为-1,表示被过滤掉了.
- IOU,为两个框重叠面积与总面积的比值
- 框排序,当选出的结果框数量大于maxBoxNum时选取score最大的maxBoxNum个
吴恩达 深度学习课程
- Binary Classification(二分分类)
- 在二分分类问题中,目标是训练处一个分类器,它以图片的特征向量x作为输入,预测输出的结果标签y是1还是0,也就是预测图片中是否有猫
- logistic regression(logistic 回归)
- 它是一个学习算法,用在监督学习问题中输出标签是0还是1时,二元分类问题
雷锋网
https://www.leiphone.com/category/ai/DZeAwe2qgx8JhbU8.html
大部分深度学习框架都包含以下五个核心组件
- 张量 (
Tensor
) - 基于张量的各种操作
- 计算图(
Computation Graph
) - 自动微分工具(
Automatic Differentiation
)工具 BLAS, cuBLAS, cuDNN
等扩展包
张量
张量是所有深度学习框架中最核心的组件,因为后续的所有运算和优化算法都是基于张量进行的
几何代数中定义的张量是, 基于向量和矩阵的推广,通俗一点理解的话,可以将标量视为零阶张量,矢量视为一阶张量,那么据栈就是二阶张量
举例来说,可以将任意一张RGB彩色图片表示称一个三阶张量, 三个纬度分别是图片的高度,宽度和色彩数据。
将这一定义进行扩展,我们也可以用四阶张量表示一个包含多张图片的数据集,其中的四个纬度分别是 – 图片在数据集中的编号,图片高度,宽度,以及色彩数据。
将各种各样的数据抽象成张量表示,然后再输入神经网络模型进行后续处理是一种非常必要且高效的策略。
因为,如果没有这一步骤,我们就需要根据各种不同类型的数据组织形式定义各种不同类型的数据操作,这会浪费大量的开发这精力。更关键的是,当数据处理完成后,我们还可以方便地将张量再转回想要的格式。
基于张量的各种操作
整个神经网络都可以简单视为为了达到某种目的,针对输入张量进行的一系列操作过程。而所谓的“学习”就是不断纠正神经网络的实际输出结果和预期结果之间的误差的过程。
这里的一系列操作包含的范围很宽,可以是简单的矩阵乘法,也可以是卷积,池化和LSTM等稍复杂的运算。而且各框架支持的张量操作通常也不尽相同。
需要指出的是,大部分的张量操作都是基于类实现的(而且是抽象类),而并不是函数(这一点可能要归功于大部分的深度学习框架都是用面向对象的编程语言实现的)。
这种实现思路一方面允许开发这将各种类似的操作汇总在一起,方便组织管理。另一方面也保证了整个代码的复用性,扩展性和对外接口的统一。总体上让整个框架更灵活和易于扩展,为将来的发展预留了空间。
计算图(Computation Graph)
有了张量和基于张量的各种操作之后,下一步就是将各种操作整合起来,输出需要的结果
但不幸的是,随着操作种类和数量的增多,有可能引发各种意想不到的问题,包括多个操作之间应该并行还是顺次执行,如何协同各种不同的底层设备,以及如何避免各种类型的冗余操作等。这些问题有可能拉低整个深度学习网络的运行效率或者引入不必要的Bug,而计算图正是为解决这一问题产生的。
计算图首次被引入人工智能领域是2009年的论文
<Learning Deep Architectures for AI>
。作者用不同的占位符构成操作结点,以字母构成变量结点,再以有向线段将这些结点连接起来,组成一个表征运算逻辑关系的清晰明了的图型数据结构,这就是最初的计算图后来随着技术的不断演进,加上脚本语言和低级语言各自不同的特点(概括地说,脚本语言建模方便但执行缓慢,低级语言正好相反),因此业界逐渐形成了一种开发框架:前端用Python等脚本语言建模,后端用C++等低级语言执行(这里低级是就应用层而言),以此综合了两者的优点。
这种开发框架大大降低了传统框架做跨设备计算时的代码耦合度,也避免了每次后端变动都需要修改前端的维护开销。而这里,在前端和后端之间起到的关键耦合作用的就是计算图。
将计算图作为前后端之间的中间表示(Intermediate Representations)可以带来良好的交互性,开发这可以将Tensor对象作为数据结构,函数/方法作为操作类型,将特定的操作类型应用于特定的数据结构,从而定义出类似MATLAB的强大建模语言。
因为计算图的引入,开发者得以从宏观上俯瞰整个神经网络的内部结构,就好像编译器可以从整个代码的角度决定如何分配寄存器那样,计算图也可以从宏观上决定代码运行时的GPU内存分配,以及分布式环境中不同底层设备间的相互协作方式。除此之外,现在也有许多深度学习框架将计算图应用于模型调试,可以实时输出当前某一操作类型的文本描述。
自动微分(Automatic Differentiation)工具
计算图带来的另一个好处是让模型训练阶段的梯度计算变得模块化且更为便捷,也就是自动微分法。
我们可以将神经网络视为由许多非线性过程组成的一个复杂的函数体,而计算图则以模块化的方式完整表征了这一函数体的内部逻辑关系,因此微分这一复杂函数体,即求取模型梯度的方法就变成了在计算图中简单地从输入到输出进行一次完整遍历的过程。与自动微分对应,业内更传统的做法是符号微分。
BLAS, CUBLAS, CUDNN
等扩展包
通过上述所有模块,已经可以搭建一个全功能的深度学习框架:
- 将待处理数据转换为张量,针对张量施加各种需要的操作,通过自动微分对模型展开训练,然后得到输出结果开始测试
- 这个时候还缺 – 运算效率
由于此前的大部分实现都是基于高级语言的,而即时是执行最简单的操作,高级语言也会比低级语言消耗更多的CPU周期,更何况是结构复杂的深度神经网络,因此运算缓慢就成了高级语言的一个天然的缺陷。
目前针对这一个问题有两种解决方案
- 第一种方法是模拟传统的编译器。就好像传统编译器会把高级语言编译成特定平台的汇编语言实现高效运行一样,这种方法将高级语言转换为C语言,然后在C语言基础上编译,执行。为了实现这种转换,每一种张量操作的实现代码就会预先加入C语言的转换部分,然后由编译器在编译阶段将这些由C语言实现的张量操作综合在一起。目前
pyCUDA
和Cpython
等编译器都已经实现了这一功能 - 第二种方法就是,利用脚本语言实现前端建模,用低级语言实现后端运行,这意味着高级语言和低级语言之间的交互都发生在框架内部,因此每次的后端变动都不需要修改前端,也不需要完整编译(只需要通过修改编译参数进行部分编译),因此整体速度也就更快
- 第一种方法是模拟传统的编译器。就好像传统编译器会把高级语言编译成特定平台的汇编语言实现高效运行一样,这种方法将高级语言转换为C语言,然后在C语言基础上编译,执行。为了实现这种转换,每一种张量操作的实现代码就会预先加入C语言的转换部分,然后由编译器在编译阶段将这些由C语言实现的张量操作综合在一起。目前
除此之外,由于低级语言的最优化编程难度很高,而且大部分的基础操作其实也都有公开的最优解决方案,因此另一个显著的加速手段就是利用现成的扩展包。
例如,最初用
Fortran
实现的BLAS(基础线形代数子程序)就是一个非常优秀的基本矩阵(张量)运算库,此外,还有英特尔的MKL(Math Kernel Library
)等为了向开发者提供尽量简单的接口,大部分深度学习框架通常都会将普通的概念抽象化,这可能是造成许多用户感知不到上述五点核心组件的重要原因。
sigmoid 归一化
The sigmoid function is a mathematical function that has a characteristic that can take any real value and map it to between 0 to 1 shaped like the letter “S”.
Sigmoid
功能在机器学习中扮演激活函数的作用,该功能用于在机器学习模型中添加非线性。基本上,该函数确定要传递的值作为输出以及不通过输出传递的值。机器学习和深度学习中使用了7种类型的激活功能Sigmoid
神经元本质上是深神经网络的基础。这些sigmoid神经元与感知性相似,但是它们恰好被稍微修饰,因此来自Sigmoid神经元的输出比Perceptron的步骤功能输出更光滑