产品以后的发展需要添加更多科技元素,如下:
以下以物体识别为重点,使用到了深度学习技术,以下我们将逐一展开。
本次分享的重点是深度学习+caffe+ncnn,其余部分将简单的进行带过。
注:本文主要以有监督的学习为主(分类和回归问题),无监督学习不作为分享范围。
物体识别(俗称Object detection),是近年来兴起的技术,这也是人工智能的首要目标之一。这是一种完全模拟人眼识别的技术,即使在物体旋转、变色、部分展示等的情况下,同样具备优秀的识别效果。
人工智能目标是:
在驾驶领域中,引入物体识别技术将大大降低交通事故发生的概率。
近期产品提出物体识别的相应需求,能识别出家长和老师发送图片中的物体,来开展相应的业务。以下是一张海边游玩的照片。
深度学习分别属于人工智能和机器学习的一部分。与深度学习相并行的另外一种机器学习方法,被称之为传统的机器学习(例如svm、逻辑回归)。
人工神经网络是一个分层的有向图,第一层输入节点接受输入的信息,也称为输入层。来自这些点的数值按照它们输出的弧的权重(wn),进行线性加权(得到G),然后再做一次函数变化f(G),赋给第二层的节点Y。
第二层的节点照此将数值向后传递,直到第三层节点,如此一层层传递,直到最后一层,最后一层又被称为输出层。在模式分类时,一个模式(图像、语音、文字等)的特征值(比如坐标),从输入层开始,按照上面的规则和公式一层层向后传递。最后在输出层,哪个节点的数值最大,输入的模式就被分在了哪一类。
------------------引自《数学之美—Google大脑和人工神经网络》
神经网络与传统的机器学习相比:
参考:https://www.cnblogs.com/wj-1314/p/9754072.html
可以简单的理解为是线性分类。假设训练数据集是线性可分的,感知机学习的目标是求得一个能够将训练数据集正实例点和负实例点完全正确分开的分离超平面。
感知机重点包含以下两点:
数学公式略,将在其他博文中进行阐述。
感知机的缺陷:只支持线性分类,对于简单的异或问题也不能进行分类。以下是异或问题中最简单的情况,可以看到无论哪一条直线都不能进行完美的分割。
针对于感知机不能表达非线性的问题,激活函数诞生了。
在人工神经网络中,规定神经元函数只能对输入变量(指向他的节点的值)线性组合后的结果进行一次非线性变换。
这次非线性变化成为“激活函数”
----《数学之美》
为了神经网络的通用性,每一层的非线性变化都选择同一类函数。
激活函数包括:
激活函数参考博客:https://blog.csdn.net/tyhj_sf/article/details/79932893
多层感知机(MLP,Multilayer Perceptron)也叫人工神经网络(ANN,Artificial Neural Network),除了输入输出层,它中间可以有多个隐层,最简单的MLP只含一个隐层,即三层的结构
多层感知机等同于全连接层,信息每向下传递一个神经元,都会产生n*n的计算量(计算量会随着输入的维度增大而变得非常恐怖)
卷积神经网络是近年发展起来的,并引起广泛重视的一种高效识别方法,20世纪60年代,Hubel和Wiesel在研究猫脑皮层中用于局部敏感和方向选择的神经元时发现其独特的网络结构可以有效地降低反馈神经网络的复杂性,继而提出了卷积神经网络(Convolutional Neural Networks-简称CNN)
参考资料:https://www.cnblogs.com/wj-1314/p/9754072.html
卷积的原理与人眼观察事物极其相似,过程都是边缘 -> 局部 -> 整体。卷积神经网络的核心就是使用多个局部特征来影响分类结果。
全连接的计算量非常大。而且是以整体来计算,没有进行有效的特征提取,即使权重最终达到了收敛效果,也不能很好的进行预测。
卷积神经网络则不同,每次卷积完毕后,传入下一层的数据大大降低了维度,而且经过卷积,获得了众多局部特征,后面的层对这些特征数据进行训练,将变得既简单又高效。
简单来说,卷积层用于特征提取。
根据卷积核的滑动,获取到一个新的特征数据(Convolved Feature)。这个特征数据不是那么直观,来看一下实物的效果。
针对于不同的通道(rgb)进行卷积后,会得到边缘轮廓的图。
池化层用于降低参数,而降低参数的方法当然是删除参数,保留最有效的参数。
一般我们有最大池化和平均池化。
需要注意的是,池化层一般放在卷积层后面。所以池化层池化的是卷积层的输出。
本博客重点关注有监督的训练。在训练之前需要有一批标注好的数据,即训练数据。为了能在训练过程中动态的观测模型的准确率,同样也需要一批标注数据(推荐为训练数据/测试数据比例为4/1)
前向传播,即训练数据在神经网络里从输入层到输出层的方向传播一次,最终得到输出层的输出结果,即在每个分类的概率值。
用数据语言来表达:假设C为一个成本函数(Cost Function),它表示根据人工神经网络的输出值(分类结果概率)和实际训练数据中的输出值之间的差距。现在,训练人工神经网络的问题就变成了一个最优化的问题,说的通俗点就是数学中的“找最大(最小)值”的问题。解决最优化为题的常用方法是梯度下降法(Gradient Descent)
注:这里存在一个局部最优解的问题,不过已经被证明:在深度神经网络中,即使收敛过程进入了局部最优,最终结果也与全局最优没有明显差异。
2015年11月谷歌(Google)出品,基于Python和C++编写。GitHub上最热,谷歌搜索最多,使用人数最多(笔者也是其中之一),大多数网上招聘工作描述中也提到了它。由于Google在深度学习领域的巨大影响力和强大的推广能力,TensorFlow一经推出就获得了极大的关注,并迅速成为如今用户最多的深度学习框架。2019年3月已发布最新的TensorFlow2.0 版本。
官方网站:https://www.tensorflow.org/
优点:
缺点:
Keras 于2015年3月首次发布,拥有“为人类而不是机器设计的API”,得到Google的支持。它是一个用于快速构建深度学习原型的高层神经网络库,由纯Python编写而成,以TensorFlow,CNTK,Theano和MXNet为底层引擎,提供简单易用的API接口,能够极大地减少一般应用下用户的工作量。
如果你是深度学习的初学者,想要快速入门,建议从Keras开始。
官方网站:https://keras.io
优点:
缺点:
PyTorch于2016年10月发布,是一款专注于直接处理数组表达式的低级API。 前身是 Torch(一个基于 Lua 语言的深度学习库)。Facebook 人工智能研究院对PyTorch提供了强力支持。 PyTorch 支持动态计算图,为更具数学倾向的用户提供了更低层次的方法和更多的灵活性,目前许多新发表的论文都采用PyTorch作为论文实现的工具,成为学术研究的首选解决方案。
如果你是一名科研工作者,倾向于理解你的模型真正在做什么,那么就考虑选择PyTorch。
官方网站:https://pytorch.org/
Caffe的全称是Convolutional Architecture for Fast Feature Embedding,它是一个清晰、高效的深度学习框架,于2013年底由加州大学伯克利分校开发,核心语言是C++。它支持命令行、Python和MATLAB接口。Caffe的一个重要特色是可以在不编写代码的情况下训练和部署模型。
具体介绍见下文。
我们暂时先选择caffe框架作为分享目标,原因如下:
推荐ubuntu系统,参考博客:https://blog.csdn.net/wangjie5540/article/details/97786182
mac系统也可搭建,参考博客:https://blog.csdn.net/wangjie5540/article/details/99571832
caffe使用了众多的依赖库,再加上caffe年久失修,出现编译兼容问题也不奇怪。推荐大家在ubuntu上进行编译,可以驱动gpu,mac的最新14.14不支持n卡,据说需要降到14.13才可以。
针对于神经网络的模型,caffe设计了对应的概念。并且使用caffe来训练模型的过程非常简单,甚至都不用写一行代码,仅仅改改配置文件也同样能直接开始训练(当然,用python去写训练过程的话,会更加的灵活)。
caffe倾向于先把数据集转化成lmdb或者hdf5格式,然后输入神经网络进行训练。
超参数配置文件。
超参数的概念如下:
在机器学习的上下文中,超参数是在开始学习过程之前设置值的参数,而不是通过训练得到的参数数据。通常情况下,需要对超参数进行优化,给学习机选择一组最优超参数,以提高学习的性能和效果
http://ethereon.github.io/netscope/#/editor
下面是对lenet的可视化,可以清楚的看到lenet的全貌。
总的来说,Caffe使用Blob来交流数据,其是Caffe中标准的数组与统一的内存接口,它是多功能的,在不同的应用场景具有不同的含义,如可以是:batches of images, model parameters, and derivatives for optimization等
作者:沤江一流
链接:https://www.jianshu.com/p/0ac09c3ffec0
layer是caffe神经网络的基本组成单元。layer包括不同的种类:
不同的层次对应了神经网络中的不通过概念。另外,每种类型的层在caffe的源码中有特定的实现,之后会在其他博客中展开讨论,这里不再赘述。
net是指神经网络定义,可以认为是layer的总和。在python版本编程中会出现net的概念。
forward和backward在python编程版本中会出现,手动执行会触发一次前向传播或反向传播(python实现的caffe训练最终的本质是在循环中每次手动触发forward函数,重点看一下本博客的caffe+mnist的python实现注释版
)。
参考博客:https://blog.csdn.net/wangjie5540/article/details/98615226
python实现mnist(注释版):https://gitee.com/simple_projects/caffe_learning/blob/master/01-learning-lenet-mine.ipynb
非常类似mnist数据集,参考:https://blog.csdn.net/wangjie5540/article/details/98615226
参考:https://github.com/shicai/MobileNet-Caffe.git
shicai的mobilenet提供了预训练模型,可以直接进行数据预测。
大家肯定想,预训练模型没什么意思,要自己训练才来实际。
参考:https://blog.csdn.net/lwplwf/article/details/82415525,可以使用cifar10进行模型训练。
(ps,作者用cpu训练了几天,最终迭代了19w次,最终准确率在70%,没有达到很好的收敛效果。之后准备好gpu的机器,还会再次训练,以观后效)
注:cifar10的数据集进行模型训练的时候,出现mean的概念(即均值,rgb每个通道上的像素的平均值)。这样做的目的是让个像素的值以0为对称点进行均匀分布,训练过程更容易收敛。有两种获取均值的方法:
以上两种方法,暂时没有证实哪个效果最好。
参考:https://github.com/weiliu89/caffe.git
voc的数据集需要在外网下载,时间要好久好久。。我已经把这些数据都下载完毕,上传到云盘。以下是下载地址:https://download.csdn.net/download/wangjie5540/11598810
由于本次是迁移训练,预训练模型是vggnet,还需要下载vggnet,地址是:https://download.csdn.net/download/wangjie5540/11603902
注:博主用cpu训练跑了两天,还没有跑到第一次的快照地点。如果想自己训练的话,就选gpu的机器吧。
git地址:https://github.com/Tencent/ncnn
ncnn 是一个为手机端极致优化的高性能神经网络前向计算框架。ncnn 从设计之初深刻考虑手机端的部署和使用。无第三方依赖,跨平台,手机端 cpu 的速度快于目前所有已知的开源框架。基于 ncnn,开发者能够将深度学习算法轻松移植到手机端高效执行,开发出人工智能 APP,将 AI 带到你的指尖。ncnn 目前已在腾讯多款应用中使用,如 QQ,Qzone,微信,天天P图等。
支持大部分常用的 CNN 网络
先下载release版本的ncnn,我这里现在ncnn-20190611
$ cd
$ mkdir -p build-android-armv7
$ cd build-android-armv7
$ cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
-DANDROID_ABI="armeabi-v7a" -DANDROID_ARM_NEON=ON \
-DANDROID_PLATFORM=android-14 ..
# if you want to enable vulkan, platform api version >= android-24 is needed
$ cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
-DANDROID_ABI="armeabi-v7a" -DANDROID_ARM_NEON=ON \
-DANDROID_PLATFORM=android-24 -DNCNN_VULKAN=ON ..
$ make -j4
$ make install
pick build-android-armv7/install folder for further jni usage
编译完成后,生成以下文件
支持库和头文件供android的arm-v7a使用
首先需要编译caffe的环境。
参考:
https://blog.csdn.net/wangjie5540/article/details/97786182
https://blog.csdn.net/wangjie5540/article/details/99571832
├── README.md
├── app
│ ├── build.gradle // android工程配置
│ ├── proguard-rules.pro // 混淆配置
│ └── src
│ ├── androidTest
│ │ └── java
│ │ └── com
│ │ └── ztjy
│ │ └── ncnndemo
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── assets
│ │ │ ├── mobilenet_v2.bin // mobilenet权重文件
│ │ │ ├── mobilenet_v2.param.bin // 神经网络参数文件
│ │ │ └── synset.txt // 分类索引文件
│ │ ├── cpp
│ │ │ ├── CMakeLists.txt
│ │ │ ├── include // ncnn的头文件
│ │ │ │ ├── allocator.h
│ │ │ │ ├── benchmark.h
│ │ │ │ ├── blob.h
│ │ │ │ ├── caffe.pb.h
│ │ │ │ ├── command.h
│ │ │ │ ├── cpu.h
│ │ │ │ ├── gpu.h
│ │ │ │ ├── layer.h
│ │ │ │ ├── layer_type.h
│ │ │ │ ├── layer_type_enum.h
│ │ │ │ ├── mat.h
│ │ │ │ ├── mobilenet_v2.id.h
│ │ │ │ ├── mobilenet_v2.mem.h
│ │ │ │ ├── modelbin.h
│ │ │ │ ├── net.h
│ │ │ │ ├── opencv.h
│ │ │ │ ├── option.h
│ │ │ │ ├── paramdict.h
│ │ │ │ ├── pipeline.h
│ │ │ │ └── platform.h
│ │ │ ├── jniLibs
│ │ │ │ └── armeabi-v7a
│ │ │ │ └── libncnn.a // ncnn静态库
│ │ │ └── native-lib.cpp
│ │ ├── java
│ │ │ └── com
│ │ │ └── ztjy
│ │ │ └── ncnndemo
│ │ │ ├── MainActivity.java // 主窗体
│ │ │ ├── NcnnJni.java // jni交互文件
│ │ │ └── PhotoUtil.java // 图片工具文类(从相册中取文件)
│ │ └── res
│ │ ├── drawable
│ │ │ └── ic_launcher_background.xml
│ │ ├── drawable-v24
│ │ │ └── ic_launcher_foreground.xml
│ │ ├── layout
│ │ │ └── activity_main.xml
│ │ ├── mipmap-anydpi-v26
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── ztjy
│ └── ncnndemo
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
https://www.processon.com/view/link/5d5cfe08e4b08b95b82695bb
java层代码
// predict image
private void predict_image(String image_path) {
// picture to float array
Bitmap bmp = PhotoUtil.getScaleBitmap(image_path);
Bitmap rgba = bmp.copy(Bitmap.Config.ARGB_8888, true);
// resize to 227x227
Bitmap input_bmp = Bitmap.createScaledBitmap(rgba, ddims[2], ddims[3], false);
try {
// Data format conversion takes too long
// Log.d("inputData", Arrays.toString(inputData));
long start = System.currentTimeMillis();
// get predict result(调用jni层代码,进行预测)
float[] result = squeezencnn.detect(input_bmp);
long end = System.currentTimeMillis();
Log.d(TAG, "origin predict result:" + Arrays.toString(result));
long time = end - start;
Log.d(TAG, String.valueOf(result.length));
// show predict result and time
int r = get_max_result(result);
Log.d(TAG, r + "");
Log.d(TAG, resultLabel.toString());
Log.d(TAG, resultLabel.get(r));
Log.d(TAG, result[r] + "");
Log.d(TAG, time + "");
String show_text = "result:" + r + ",name:" + resultLabel.get(r) + "\nprobability:" + result[r] + "\ntime:" + time + "ms";
Log.d(TAG, show_text);
result_text.setText(show_text);
} catch (Exception e) {
e.printStackTrace();
}
}
jni层代码
// public native String Detect(Bitmap bitmap);
JNIEXPORT jfloatArray JNICALL
Java_com_ztjy_ncnndemo_NcnnJni_detect(JNIEnv *env, jobject thiz, jobject bitmap) {
// ncnn from bitmap
ncnn::Mat in;
{
AndroidBitmapInfo info;
// 获取位图信息
AndroidBitmap_getInfo(env, bitmap, &info);
// 获取位图宽高
int width = info.width;
int height = info.height;
// demo只支持rgba格式的图片
if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888)
return NULL;
void *indata;
AndroidBitmap_lockPixels(env, bitmap, &indata);
// 把像素转换成data,并指定通道顺序
in = ncnn::Mat::from_pixels((const unsigned char *) indata, ncnn::Mat::PIXEL_RGBA2BGR,
width, height);
AndroidBitmap_unlockPixels(env, bitmap);
}
// ncnn_net
//
std::vector<float> cls_scores;
{
// 减去均值和乘上比例(来源与caffe神经网络的配置文件,即prototxt文件)
const float mean_vals[3] = {
103.94f, 116.78f, 123.68f};
const float scale[3] = {
0.017f, 0.017f, 0.017f};
// 设置均值与标准化参数
in.substract_mean_normalize(mean_vals, scale);
// 创建ncnn前向传播的结果提取器
ncnn::Extractor ex = ncnn_net.create_extractor();
// 如果不加密使用ex.input("data", in);
ex.input(mobilenet_v2_param_id::BLOB_data, in);
ncnn::Mat out;
// 如果不加密是使用ex.extract("prob", out);
ex.extract(mobilenet_v2_param_id::BLOB_prob, out);
int output_size = out.w;
jfloat *output[output_size];
for (int j = 0; j < out.w; j++) {
output[j] = &out[j];
}
// 获取结果预测结果,并返回给android层
jfloatArray jOutputData = env->NewFloatArray(output_size);
if (jOutputData == nullptr) return nullptr;
env->SetFloatArrayRegion(jOutputData, 0, output_size,
reinterpret_cast<const jfloat *>(*output)); // copy
return jOutputData;
}
}
https://github.com/chehongshu/ncnnforandroid_objectiondetection_Mobilenetssd.git
ncnn+mobilenet-ssd
唐宇迪-深度学习Caffe框架入门视频课程:https://edu.csdn.net/course/detail/3506
会写代码的好厨师-Caffe实战入门:https://www.imooc.com/learn/1040
caffe官网:https://github.com/BVLC/caffe
ncnn:https://github.com/Tencent/ncnn.git
ncnn+mobilenet-ssd:https://github.com/chehongshu/ncnnforandroid_objectiondetection_Mobilenetssd
shicai_mobilenet:https://github.com/shicai/MobileNet-Caffe.git
weiliu89:https://github.com/weiliu89/caffe.git(需要check_out到ssd分支)
在Android手机上使用腾讯的ncnn实现图像分类:
https://blog.csdn.net/qq_33200967/article/details/82421089