Core ML && Caffe 入門實踐
Core ML && Caffe 入門實踐
前言
最近WWDC2017剛剛過去兩周,作為iOS客戶端開發的我,看到了蘋果高逼格的Core ML,決定花兩天研究研究。
其實機器學習在移動端應用已經有了一定的支持了,Caffe2 已經支持iOS、Android移動端上的應了,但今年iOS11中的Core ML將大大降低了機器學習的門檻。因此,寫一篇自己的學習筆記,以及分享下關於深度學習相關的理解,如有不足之處,還請大家多多指正。
Core ML簡介
iOS 11的Core ML 大大降低了開發者在蘋果設備上使用機器學習技術預測模型的門檻和成本。蘋果製定了自己的模型文件格式,統一的格式和全新的 API 設計使得 Core ML 支持蘋果生態下多個平台。Core ML 的核心是加速在 iPhone、iPad、Apple Watch 上的人工智能任務,支持深度神經網絡、循環神經網絡、卷積神經網絡、支持向量機、樹集成、線性模型等
demo演示
示例在模擬器上運行,讀取相冊圖片,使用Core ML 進行圖像識別
可以將經過Caffe框架訓練後生成的 .caffemodel 使用coremltools轉換工具轉成 .mlmodel文件.隻需要給定模型的輸入,就能得到模型的預測結果。使用了Core ML之後隻需要很少的代碼就可以構建起一個機器學習的應用。咱們隻需要關注模型的預測輸出就可以,而避免了關注複雜的模型網絡層次。.mlmodel 文件包含了權重和模型結構等信息,並可以自動生成相關的代碼,節省開發者大量時間。
Demo實踐
環境需求:
- 需求: python 2.7 (mac默認的python環境),如果沒有,請安裝指定環境的python:brew install python==2.7
- 需求: CoreMLTools點擊下載頁麵上的whl文件, 這是轉換工具,備用,
- 安裝pip ,在 pip 下安裝numpy (1.12.1+) , protobuf (3.1.0+) ,six ,setuptools ,wheel,coremltools ,virtualenv
創建:
- 新建文件夾 convert-test , 在文件夾下放入以下文件: bvlc_alexnet.caffemodel , deploy.prototxt ,synset_words.txt , 新建一個python文件,coremlconvert.py如下:
#!/usr/bin/python
import coremltools
coreml_model = coremltools.converters.caffe.convert(
('bvlc_reference_caffenet.caffemodel','deploy.prototxt'),
image_input_names='data',
class_labels = 'synset_words.txt',
)
coreml_model.author = 'Yi Xie'
coreml_model.licence = 'Unknown'
coreml_model.short_description = 'number recogation model'
coreml_model.input_description['data'] ='Input image to be classified'
coreml_model.output_description['prob'] ='Probability of each category'
coreml_model.output_description['classLabel'] ='Most likely image category'
coreml_model.save('BVLC.mlmodel')
轉換
- virtualenv -p /usr/bin/python2.7 env
- source env/bin/activate
- pip install coremltools
- python coremlconvert.py
然後就在目錄下生成對應的.mlmodel 文件
把.mlmodel 文件拖拽到 Xcode 工程中後,勾選對應的 target,這樣 Xcode9生成對應的代碼。生成的類名就是 .mlmodel文件,輸入和輸出的變量名和類型也可以在 Xcode 中查看:
- 同時也會生成相應的頭文件: BVLC.h 包含了:BVLCInput、BVLCOutput 以提供測試圖片輸入和預測結果輸出
數據格式處理
由於.mlmodel 輸入的數據格式是 CVPixelBufferRef,因此以BLVC為例,我們需要將227*227的RGB的UIImage轉換為CVPixelBufferRef數據輸入
- (CVPixelBufferRef)pixelBufferFromCGImage:(UIImage *)originImage {
CGImageRef image = originImage.CGImage;
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey,
[NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey,
nil];
CVPixelBufferRef pxbuffer = NULL;
CGFloat frameWidth = CGImageGetWidth(image);
CGFloat frameHeight = CGImageGetHeight(image);
CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault,
frameWidth,
frameHeight,
kCVPixelFormatType_32ARGB,
(__bridge CFDictionaryRef) options,
&pxbuffer);
NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL);
CVPixelBufferLockBaseAddress(pxbuffer, 0);
void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer);
NSParameterAssert(pxdata != NULL);
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(pxdata,
frameWidth,
frameHeight,
8,
CVPixelBufferGetBytesPerRow(pxbuffer),
rgbColorSpace,
(CGBitmapInfo)kCGImageAlphaNoneSkipFirst);
NSParameterAssert(context);
CGContextConcatCTM(context, CGAffineTransformIdentity);
CGContextDrawImage(context, CGRectMake(0,
0,
frameWidth,
frameHeight),
image);
CGColorSpaceRelease(rgbColorSpace);
CGContextRelease(context);
CVPixelBufferUnlockBaseAddress(pxbuffer, 0);
return pxbuffer;
}
模型調用
- (NSString*)result:(UIImage *)image {
BVLC *model = [[BVLC alloc] init];
NSError *error;
UIImage *scaledImage = [image scaleToSize:CGSizeMake(227, 227)];//調整輸入圖片尺寸
CVPixelBufferRef buffer = [image pixelBufferFromCGImage:scaledImage];
BVLCInput *input = [[catInput alloc] initWithData:buffer];
BVLCOutput *output = [model predictionFromFeatures:input error:&error];
return output.classLabel;//預測結果輸出
}
小結
使用了 Core ML之後使得model集成更加的簡潔,其支持多種的數據類型,使得模型轉化更加的廣泛
,同時其支持了硬件的優化,適配了主流的機器學習框架。
附錄1 Caffe簡單入門
caffe 簡介
Caffe 是伯克利視覺和學習中心開發的基於C++/CUDA/phthon實現的卷積神經網絡框架,提供了麵向命令、Matlab、Python的接口。該框架實現了CNN架構,速度快、由於其利用了MKL、OpenBLAS、cuBLAS等計算庫,支持GPU加速。並且他適合做圖像特征的提取。Caffe 還提供了一套模型訓練、預測、微調、發布、數據預處理、自動化測試的工具,比較適合初學者入門。另外比較常用的還有Google的TensorFlow,微軟的Theano.
caffe 環境配置
下載
- git clone https://github.com/bvlc/caffe.git
安裝
- brew install -vd snappy leveldb gflags glog szip lmdb
添加science源來安裝 OpenCV 和 hdf5
brew tap homebrew/science
-
brew install hdf5 opencv
使用Anaconda Python這個工具包含了大量的Python工具包來解決以上依賴問題而且安裝新的軟件包也特別容易。如果用Anaconda Python的話HDF5已經包含其中了 可以略過。並且opencv這裏有兩行需要改一下:
-DPYTHON_LIBRARY=#{py_prefix}/lib/libpython2.7.dylib -DPYTHON_INCLUDE_DIR=#{py_prefix}/include/python2.7
然後是BOOST和BOOST-Python 安裝這個主要是為了之後Python接口的編譯
brew install --build-from-source --with-python -vd protobuf brew install --build-from-source -vd boost boost-python
接著安裝Python依賴
for req in $(cat requirements.txt); do pip install $req; done
首先
mv Makefile.config.example Makefile.config
在編譯之前我們需要修改make.config 的幾個地方然後 CPU_ONLY := 1 的注釋去掉(刪除#),
然後就是後麵的Python路徑,用Anaconda Python的話將下麵的注釋去掉:ANACONDA_HOME := $(HOME)/anaconda PYTHON_INCLUDE := $(ANACONDA_HOME)/include \ $(ANACONDA_HOME)/include/python2.7 \ $(ANACONDA_HOME)/lib/python2.7/site-packages/numpy/core/include\
然後就可以在caffe文件夾下執行make了:
make all
make test
make runtest
make pycaffe如果出現錯誤提示為:`fatal error: caffe/proto/caffe.pb.h: No such file or directory` 解決方式: - protoc src/caffe/proto/caffe.proto --cpp_out=. - sudo mkdir include/caffe/proto - sudo mv src/caffe/proto/caffe.pb.h include/caffe/proto
注意:在訓練之前需要,確認修改腳本中所引用的.prototxt文件,將最後一行改為solver_mode:CPU
附錄2 Caffe 示例 Mnist
以最經典的Mnist作為介紹:
-
下載Mnist數據集合,可以在Caffe源碼框架的data/mnist使用get_mnist.sh進行下載,得到二進製的測試和訓練集,caffe 也可以直接以圖片集的形式作為輸入。
- cd data/minist/
- ./get_mnist.sh
-
轉換格式
下載到的原始數據為二進製文件,需要轉換為LEVELDB或者LMDB才能被Caffe識別。
執行腳本:./examples/mnist/create_mnist.sh
此時在目錄下會生成兩個目錄,每個包含兩個文件data.mdb 和 lock.mdb
- 啟動Caffe訓練
運行:
examples/mnist/train_lenet.sh
測試自己圖片
- ./build/tools/caffe.bin test -model=examples/mnist/lenet_train_test.prototxt -weights=examples/mnist/lenet_iter_10000.caffemodel
- ./build/examples/cpp_classification/classification.bin examples/mnist/deploy.prototxt examples/mnist/lenet_iter_10000.caffemodel examples/mnist/mean.binaryproto examples/mnist/synset_words.txt examples/images/3.jpg
附錄3 卷積神經網路(CNN)
概述
參考了網上一些博客,總結一下對於CNN的一些理解。**圖像分類本質上是對輸入圖像的操作,最終輸出一組最好地描述了圖像內容的分類或分類的概率**。想要讓計算機能夠區分開不同類別的圖片,以及各自的特征,這是人大腦自動進行著的過程。當我們看到一幅狗的圖片時,如果有諸如爪子或四條腿之類的明顯特征,我們便能將它歸類為狗。同樣地,**計算機也可以通過尋找諸如邊緣和曲線之類的低級特點來分類圖片,繼而通過一係列卷積層級建構出更為抽象的概念。**
卷積神經網絡,會聯想到神經科學或生物學。CNN是從視覺皮層的生物學上獲得啟發的。因為在視覺皮層有小部分細胞對特定部分的視覺區域敏感。由於大腦中的一些個體神經細胞隻有在特定方向的邊緣存在時才能做出反應。**例如,一些神經元隻對垂直邊緣興奮,另一些對水平或對角邊緣興奮。所有這些神經元都以柱狀結構的形式進行排列,而且一起工作才能產生視覺感知。這種一個係統中的特定組件有特定任務的觀點(視覺皮層的神經元細胞尋找特定特征)在機器中同樣適用,這就是 CNN 的基礎。**
CNN卷積網絡的核心思想是將:局部互聯、權值共享以及降采樣這三種結構思想結合起來獲得了某種程度的位移、尺度、形變魯棒性,以達到圖像降維的特征學習與分類
圖像的卷積
微觀角度:
如上圖:一張 32 * 32 的圖片作為輸入,經過卷積得到**C1**的6張 28 * 28的特征圖,這個過程稱為特征映射。**每層都有多個特征圖,對於每個的特征圖是由前一層通過卷積運算得來,卷積的運算的作用實質上是對特征的一種提取,同時也是忽略一些無關噪聲因素。**對於上圖,**C1**的6張特征圖是由多個5 * 5的卷積核與輸入圖像,卷積運算得來,其目的就是為提取原始圖像的多種特征。
宏觀角度:
每個濾波器可以被看成是特征標識符。這裏的特征指的是例如直邊緣、原色、曲線之類的東西。所有的圖像都共有的一些最基本的特征。
知乎上“機器之心”的例子:一組濾波器是 7 x 7 曲線檢測器,作為曲線過濾器,它將有一個像素結構,在曲線形狀旁時會產生更高的數值,以提取出圖像的中局部的曲線特征。
左圖:濾波器的像素表示;右圖:曲線檢測器可視化;對比兩圖可以看到數值和形狀的對應
從數學的角度來看,當我們將過濾器置於輸入內容的左上角時,它將計算濾波器和這一區域像素值之間的點積。
如果輸入圖像上某個形狀看起來很像過濾器表示的曲線,那麼所有點積加在一起將會得出一個很大的值!
在濾波器滑動的過程中,如果沒有檢測到曲線,這時該卷積得到的值就很小。這是因為圖像的這一部分和曲線檢測器相似性較小。這本質上卷積層的輸出就是一個激活映射。因此,在這個曲線檢測器的例子裏,激活映射將會顯示出圖像裏最像曲線的區域。過濾器越多,檢測的圖片特定特征也越多,激活映射的深度越大,對輸入的特征也就越多。
上圖是,對輸入圖像上進行第一次卷積運算得到的特征映射圖
S2層是降采樣層,根據圖像的局部相關特性,對圖像進行降采樣,減少了參數量,這就是CNN中的稀疏鏈接。降采樣層得到的就是6張14 * 14 的圖片,相當於在2 * 2的點陣中取一個點,可以設置取點的規則。
如上如所示卷積和下采樣過程:
- 卷積:用一個可訓練的濾波器fx去卷積一個輸入的圖像(隻有輸入是圖像,之後麵的每一層經過就是卷積、采樣得到隻是圖片的特征,相當於每一層的運算,在降低參數量的同時也在提取上一層的特征),然後加一個偏置bx,得到卷積層Cx。
- 降采樣:每鄰域四個像素求和變為一個像素,然後通過標量Wx+1加權,再增加偏置bx+1,然後通過一個Sigmoid激活函數,產生一個大概縮小四倍的特征映射圖Sx+1。 所以從一個平麵到下一個平麵的映射可以看作是作卷積運算,S-層可看作是模煳濾波器,起到二次特征提取的作用。隱層與隱層之間空間分辨率遞減,而每層所含的平麵數遞增,這樣可用於檢測更多的特征信息。
- 注:深度神經網絡中還存在一個問題就是梯度消失,使用Sigmoid、tanh等飽和激活函數時,在神經網絡進行誤差反向傳播時,各個層都要乘以激活函數的一階導,梯度會衰減,當網絡層數過多時,甚至會導致梯度消失。這就使得訓練收斂變慢,因此我們會使用ReLU這種非飽和激活函數,以保證收斂速度。(《21天實戰Caffe》有詳細介紹)
CNN 相比於ANN優勢在於
局部感受野:
通過感受野和權值共享減少了神經網絡需要訓練的參數的個數,假設有1000x1000像素的圖像,有1百萬個隱層神經元,那麼他們全連接的話(每個隱層神經元都連接圖像的每一個像素點),就有1000x1000x1000000=10^12個連接,也就是10^12個權值參數。然而圖像的空間聯係是局部的,就像人是通過一個局部的感受野去感受外界圖像一樣,每一個神經元都不需要對全局圖像做感受,每個神經元隻感受局部的圖像區域,然後在更高層,將這些感受不同局部的神經元綜合起來就可以得到全局的信息了。這樣就減少連接的數目,也就是減少神經網絡需要訓練的權值參數的個數了。如下圖右:假如局部感受野是10x10,隱層每個感受野隻需要和這10x10的局部圖像相連接,所以1百萬個隱層神經元就隻有一億個連接,即10^8個參數。比原來減少了四個數量級。
權值共享:
假設隱含層的每一個神經元都連接10x10個圖像區域,也就是說每一個神經元存在10x10=100個連接權值參數。如果我們每個神經元這100個參數是相同。也就是說每個神經元用的是同一個卷積核去卷積圖像。這樣我們就隻有100個參數。不管你隱層的神經元個數有多少,兩層間的連接我隻有100個參數。
如果僅僅隻依靠這100個參數,提取特征,不能很好的提取圖像的全部特征。一種卷積核就相當是提出圖像的一種特征,例如某個方向的邊緣。如果需要提取不同的特征,就要多加幾種卷積核。假設有100種濾波器,每種濾波器的參數不一樣,表示提取輸入圖像的不同特征。這樣每種濾波器去卷積圖像就得到對圖像的不同特征的放映,我們稱之為特征圖。所以100種卷積核就有100個特征圖。這100個特征圖就組成了一層神經元。一層有參數個數 = 100種卷積核x每種卷積核共享100個參數=100x100=10K。也就是說,隱層的參數個數和隱層的神經元個數無關,隻和濾波器的大小和濾波器種類的多少有關。
CNN 進一步理解
當進入下一層卷積層時,第一層的輸入是原始圖像,而第二卷積層的輸入正是第一層輸出的激活映射。也就是說,這一層的輸入大體描繪了低級特征在原始圖片中的位置。在此基礎上再采用一組濾波器(讓它通過第 2 個卷積層),輸出將是表示了更高級的特征的激活映射。**隨著網絡越深和經過更多卷積層後,將得到更為複雜特征的激活映射。越深入網絡,過濾器的感受野越大,意味著它們能夠處理更大範圍的原始輸入內容(或者說它們可以對更大區域的像素空間產生反應)。**
檢測高級特征之後,最後就是完全連接層,**這一層處理輸入內容(該輸入可能是卷積層、ReLU 層或是池化層的輸出)後會輸出一個 N 維向量,N 是該程序必須選擇的分類數量。**例如,如果你想得到一個數字分類程序,像Mnist那樣對手寫數字進行分類,10個數字,N 就等於10。這個 N 維向量中的每一數字都代表某一特定類別的概率。
針對於某一個數字分類結果矢量是: [0 0 .2 0 .55 0 0 .2 0 .05],這就意味著該圖片有 20% 的概率是 2、55% 的概率是 4、20% 的概率是 7、還有 5% 的概率是 9。完全連接層觀察上一層的輸出(其表示了更高級特征的激活映射)並確定這些特征與哪一分類最為吻合。如果像本例demo中可以檢測山地車、飛機、手機等日常物體的程序。當預測某一圖像的內容為山地車時,那麼激活映射中的高數值便會代表一些車輪或腳踏板之類的特征。同理,如預測某一圖片的內容為飛機,激活映射中的高數值便會代表諸如機翼等飛機的高級特征。完全連接層觀察高級特征和哪一分類最為吻合和擁有怎樣的特定權重,因此當計算出權重與先前層之間的點積後,你將得到不同分類的正確概率。
最後更新:2017-06-28 16:02:41