基礎部分完成,從本篇開始,會略微的增長一些難度。
一般說,在解決問題的時候,大多程序員都會在網上搜索,尋找一些類似相近的案例做爲參考。這個方式在機器學習領域一樣有效。惋惜早期的時候,各公司的保密仍是作的比較嚴格,時至今日有了很大改善,但在整個IT行業中,機器學習領域,各公司的研發成果保密仍然是最嚴重的。
所以,ImageNet對機器學習的推進更是難能難得和功不可沒。在機器學習尚處於摸索階段,你們在都沒有大規模投資的狀況下艱苦研究的時候,ImageNet提供了一個迄今也是最大的已標註視覺大數據集,讓我的即使在家裏也可能把精力集中在算法和理論的研究。
ImageNet還連續舉辦了多屆機器識別大賽,比賽結果一次次刷新機器學習識別紀錄,最新的成績已經超過人眼在圖像識別方面的平均水平。誕生了不少優秀的算法或者模型。
諸如:LeNet、AlexNet、GoogLeNet、VGG、ResNet等模型,都已經經過大賽證實了本身的能力,並獲得普遍的應用。
從這幾個優勝算法的經驗看,獲獎的機器學習系統基本有這樣幾個特色:html
今天以VGG爲例完成一個圖像識別的完整代碼。這裏的完整特別指不一樣於前幾篇的學習中,使用已經歸一化完成的圖片,本次使用真實的圖片文件進行識別。python
VGG-19是2014年在ImageNet大賽中奪冠的算法,總體模型定義超過了19層的卷積及神經網絡。下圖是vgg系列網絡的結構示意圖:
這裏選擇這個案例有三個緣由:一是能夠從上圖看到,雖然模型很複雜,但沒有超過咱們如今掌握的基礎算法,全部用到的算法咱們都已經學過;二是這個複雜的模型完成ImageNet學習用時很長,但網上已經有完整的訓練數據能夠直接下載使用,從而大大的下降使用門檻。也就是說,咱們搭建好模型,就能夠用來對圖片進行識別預測;三則是這是真正普遍應用的模型,在吻合的領域徹底能夠直接用於商用化。
訓練數據下載:點擊下載
分類標籤下載:點擊查看,請跳轉頁面後手工下載txt文件。
兩個文件下載後不要修改文件名,直接放置到./data/目錄。
注意的準備事項
本篇的代碼中又多用了一些第三方的擴展庫須要在運行以前先安裝,跟前面安裝的newpy是同樣的,可使用pip安裝:git
pip2 install scipy pillow
源文件分爲兩個文件,一個是vgg.py
,用於實現vgg的模型,而且提供接口用於預測圖片;另外一個是主程序文件,用什麼名字都不要緊,我用的是picRegn.py
。
vgg.py:程序員
#這個程序至關於一個庫,不會直接執行, #因此開始沒有用於腳本模式的標誌 # -*- coding=UTF-8 -*- import tensorflow as tf import numpy as np import scipy.io as sio netmat_path = 'data/imagenet-vgg-verydeep-19.mat' ##定義卷積層 def _conv_layer(input, weight, bias): conv = tf.nn.conv2d(input, tf.constant(weight), strides=(1, 1, 1, 1), padding='SAME') return tf.nn.bias_add(conv, bias) ##定義池化層 def _pool_layer(input): return tf.nn.max_pool(input, ksize=(1, 2, 2, 1), strides=(1, 2, 2, 1), padding='SAME') ##定義全連接層 def _fc_layer(input, weights, bias): shape = input.get_shape().as_list() dim = 1 for d in shape[1:]: dim *= d x = tf.reshape(input, [-1, dim]) fc = tf.nn.bias_add(tf.matmul(x, weights), bias) return fc ##定義softmax分類輸出層 def _softmax_preds(input): preds = tf.nn.softmax(input, name='prediction') return preds ##圖片處裏前減去均值(歸一化的部分工做) def _preprocess(image, mean_pixel): return image - mean_pixel ##加均值,用於還原可顯示圖片 def _unprocess(image, mean_pixel): return image + mean_pixel ##構建cnn前向傳播網絡 def net(data, input_image): #根據vgg19的標準定義深層卷積網絡 layers = ( 'conv1_1', 'relu1_1', 'conv1_2', 'relu1_2', 'pool1', 'conv2_1', 'relu2_1', 'conv2_2', 'relu2_2', 'pool2', 'conv3_1', 'relu3_1', 'conv3_2', 'relu3_2', 'conv3_3', 'relu3_3', 'conv3_4', 'relu3_4', 'pool3', 'conv4_1', 'relu4_1', 'conv4_2', 'relu4_2', 'conv4_3', 'relu4_3', 'conv4_4', 'relu4_4', 'pool4', 'conv5_1', 'relu5_1', 'conv5_2', 'relu5_2', 'conv5_3', 'relu5_3', 'conv5_4', 'relu5_4', 'pool5', 'fc6', 'relu6', 'fc7', 'relu7', 'fc8', 'softmax' ) weights = data['layers'][0] net = {} current = input_image #枚舉全部的層 for i, name in enumerate(layers): #取名字的前4個字母做爲本層的類型 kind = name[:4] #根據conv/relu/pool/soft這幾種狀況,調用對應函數定義相應層 #每次定義層以上一層的輸出爲輸入,將整個網絡鏈接起來 if kind == 'conv': kernels, bias = weights[i][0][0][0][0] kernels = np.transpose(kernels, (1, 0, 2, 3)) bias = bias.reshape(-1) current = _conv_layer(current, kernels, bias) elif kind == 'relu': current = tf.nn.relu(current) elif kind == 'pool': current = _pool_layer(current) elif kind == 'soft': current = _softmax_preds(current) #處理最後的全鏈接層 kind2 = name[:2] if kind2 == 'fc': kernels1, bias1 = weights[i][0][0][0][0] kernels1 = kernels1.reshape(-1, kernels1.shape[-1]) bias1 = bias1.reshape(-1) current = _fc_layer(current, kernels1, bias1) net[name] = current assert len(net) == len(layers) return net, layers def predict(image): global data,mean_pixel #統一減去均值 image_pre = _preprocess(image, mean_pixel) #拉成數組 image_pre = np.expand_dims(image_pre, axis=0) #轉成浮點矩陣 image_preTensor = tf.to_float(tf.convert_to_tensor(image_pre)) #定義深度VGG19神經網絡及載入圖片數據用於預測 nets, layers = net(data, image_preTensor) #取分類層數據 preds = nets['softmax'] #從分類轉成整數的索引, #實際上.eval纔是真正開始tensorFlow計算,之前那麼多工做都是在建模型 predsSortIndex = np.argsort(-preds[0].eval()) #返回預測數據 return predsSortIndex,preds #載入訓練好的矩陣文件, #loadmat是載入matlab的數據數據文件 data = sio.loadmat(netmat_path) mean = data['normalization'][0][0][0] #獲取圖片像素均值用於圖片的歸一化 mean_pixel = np.mean(mean, axis=(0, 1))
picRegn.py:github
#!/usr/bin/env python # -*- coding=UTF-8 -*- import vgg import os,sys import numpy as np import scipy.misc import tensorflow as tf import argparse FLAGS = None ##讀取圖片並把圖片尺寸歸一化 def _get_img(src, img_size=False): img = scipy.misc.imread(src, mode='RGB') if not (len(img.shape) == 3 and img.shape[2] == 3): img = np.dstack((img, img, img)) if img_size != False: img = scipy.misc.imresize(img, img_size) return img.astype(np.float32) ##獲取路徑中文件列表 def list_files(in_path): files = [] for (dirpath, dirnames, filenames) in os.walk(in_path): files.extend(filenames) break return files ##獲取文件路徑列表dir+filename def _get_files(img_dir): files = list_files(img_dir) return [os.path.join(img_dir, x) for x in files] ##得到圖片的分類標籤值 def _get_allClassificationName(file_path): f = open(file_path, 'r') lines = f.readlines() f.close() return lines def main(_): ##加載ImageNet mat標籤 lines = _get_allClassificationName('data/synset_words.txt') images = _get_files(FLAGS.image_dir) ##獲取圖片路徑中文件列表 with tf.Session() as sess: for i, imgPath in enumerate(images): ##加載圖片並壓縮到標準格式=>224x224x3色 image = _get_img(imgPath, (224, 224, 3)) predsSortIndex,preds = vgg.predict(image) print('#####%s#######' % imgPath) for i in range(3): ##輸出前3種分類 nIndex = predsSortIndex classificationName = lines[nIndex[i]] ##分類名稱 problity = preds[0][nIndex[i]] ##某一類型機率 print('%d.ClassificationName=%s Problity=%f' % ((i + 1), classificationName, problity.eval())) sess.close() if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('-i','--image_dir', type=str, default='images/', help='Pic files folder path') FLAGS, unparsed = parser.parse_known_args() tf.app.run(main=main, argv=[sys.argv[0]] + unparsed)
咱們先從主程序講起,由於主程序結構比較簡單。
主程序定義了一個參數,若是運行時沒有給定參數,默認從./images/
文件夾列出全部文件,而後逐個識別。
程序首先讀入了圖片分類的標籤庫。與此對應已經訓練好的vgg數據在vgg.py中讀入,咱們後面再講。
接着程序列出文件夾中全部圖片,逐個讀入。讀入的圖片統一將尺寸變動爲長寬均爲224,顏色爲RGB的圖片(歸一化)。
隨後使用vgg算法識別給定的圖片矩陣。返回值已經從分類變成了索引,使用索引在標籤庫中查找就能夠獲得圖片所屬的分類。程序會列出最類似的前三個分類及其類似度。
本例中所使用的圖片就是在百度圖片上隨意搜索的,顯示以下:
最後的執行結果爲:算法
$ ./picRegn.py #####images/leopard.jpeg####### 1.ClassificationName=n02128385 leopard, Panthera pardus Problity=0.941652 2.ClassificationName=n02128925 jaguar, panther, Panthera onca, Felis onca Problity=0.031439 3.ClassificationName=n02130308 cheetah, chetah, Acinonyx jubatus Problity=0.024339
注意這個網絡比較深,訓練數據集也很大,所以執行這個程序建議至少是16G內存8核以上CPU,固然若是有GPU支持就更好了。json
接着說重點vgg.py。
首先是全局部分,這部分在主程序一開頭引入vgg包的時候就會執行。載入已經訓練好的數據集,這個數據集實際是matlab/octave格式的,python能夠很好的支持直接讀入或者存儲matlab數據集用於同其它項目共享資源。如同咱們第二篇講過的,至少迄今,matlab/octave仍是學術界的主流研究方式。而機器學習算法的源頭基本在學術界,也就是所謂「論文驅動」的研發模式。
隨後根據訓練數據集的狀況獲得其中使用的圖片數據均值,這個均值用於在輸入圖片的時候也作相同的歸一化動做。這樣本幅圖片同之前訓練的結果集才具備可比較性,才談得上用之前的訓練結果識別本圖片。
vgg.py最主要的函數是net,其它函數都是圍繞這個函數而工做的。在這個函數中,首先完整定義一個vgg19的模型。這個定義跟咱們之前用的方式很是不一樣。在咱們之前學習的時候,由於結構比較簡單,都是直接逐行的用命令進行模型的搭建。而在這個19層的網絡中,用手工搭建已經太繁瑣了,所以用了一個字符串數組layers
,將模型以字符串的形式存入數組。而後循環遍歷這個數組,根據該層名稱調用相應的子程序定義相應層的算法。並將每一層的輸入,定義爲上一層的輸出,從而將網絡逐層關聯在一塊兒,最終完成完整的網絡。
這種方法在複雜網絡中會常常用到,目前我據說比較深的網絡已經超過了1000層之多。這一篇裏,這部分是學習的重點,學會了這個,你才能真正的在網上搜索成熟的算法、模型,並拿來應用。
vgg19的網絡看上面展開的圖反而不必定容易理解,我再轉帖一張vgg-16網絡的結構化示意圖供參考(vgg-19網絡除了層數其它基本相似):
整個vgg包對外的接口實際上主要是使用函數:predict
,這個函數接受一個參數,參數是一個224x224x3的圖片矩陣。函數中會對圖片統一減去均值,而後轉換成張量(矩陣),而且所有變成浮點數據。隨後送入剛纔說過的net,用構建的網絡對圖片進行識別。
一個要注意的狀況是,其實剛纔說到的全部工做都只是對數據模型的描述、定義和組裝。真正執行TensorFlow運算在下面preds[0].eval()
這裏,其實你看到evel()就應當明白了,咱們前面的文章講過,這裏是使用當前默認的session來執行一個模型並返回結果。
一樣是圖像識別,用這個程序進行圖像識別,跟使用百度之類的雲API有什麼區別嗎?都學到第八篇了,這種區別不管如何你也應當能說得出來,否則我要傷心死了:)。雲API是全部工做架構在雲端,提供接口調用,按使用數量付費。使用這裏的程序則是要本身搭建服務器,定義、實現接口,在本身的服務器上運行。如何取捨,其實不只僅是技術問題,更重要是對於業務和企業戰略的決策。
講解就到這裏,建議再多找幾張圖片樣本,識別來試一試吧。數組
用做圖像識別的深度卷機網絡,還能夠作一些頗有意思的事情,這裏就是一個例子。
《A Neural Algorithm of Artistic Style》,這篇論文認爲,既然深度學習網絡識別圖片的主要理論依據,是找出圖片的各項特徵。那這些特徵,同藝術品的繪畫風格是否有共通性呢?隨後證實的確是可行的。在這裏有了一個完整的實現,用於將一副風格鮮明的藝術品:
以及一副普通的照片:
合成爲一副風格類似的藝術做品:
能夠看到,這種合成的水平,比日常的Photoshop濾鏡效果可強太多了。固然,合成過程由於是機器學習過程,而不是簡單的識別,因此運算擬合的過程,時間至關長。
源碼有一點長,請直接移步到做者github網頁去看,這裏只作一個簡單的講解。bash
首先也是用vgg.py定義了vgg網絡,由於本例中vgg網絡不是用於識別的,因此取消了最後的3個全鏈接層、相關激活層還有softmax分類層。
vgg.py的代碼對照咱們上面的實現來讀很容易理解,幾乎都是相同的。因此這裏補充一句,這一類的經典實現,保存到本身的代碼庫吧,就好像一把瑞士軍刀,用的時候,拿出來簡單改改就能夠投產。
neural_style.py是主程序,主要定義和解析命令行參數,其中有大量的常量用於優化合成的效果。網上還有不少其它實現,但這個實現效果最好,跟這些複雜的參數有很大關係,由於顯然做者使用這些配置靈活的參數已經進行了長時間的算法調優。主程序中其它的工做就是讀寫圖片文件、基本的初始化性質的工做。
stylize.py是算法實現的主要部分,具體的實現若是有興趣,建議先讀一下論文,才能理解算法是如何實現的。
大概方法是:首先從隱藏層中抽出接近內容原圖的隱藏層和接近風格圖特徵的隱藏層。若是你還記得前面第五篇mnist例子中咱們將權重值可視化以後造成的圖片,你就能大概理解這裏抽取中間隱藏層的含義了。
而後使用vgg19網絡分別抽取兩張圖片的特徵,抽取的時候公式是不一樣的。在過程當中,經過剛纔說的指定的兩組隱藏層來計算代價函數值,公式在論文中有。並以此代價值使用一樣的反向傳播算法擬合風格圖及內容圖的抽象部分(隱藏層部分)。隨後在完成1000次的擬合以後,使用算法完成兩張圖的合成圖(結果圖片),最終合成圖同時最接近風格圖片以及內容原圖。從而獲得一張藝術品化的照片。
合成的算法在源碼中有英文的解釋,大體分紅五步:服務器
下面的引文中有一篇稍微詳細的用中文解釋了一下這個算法,可是主要的部分仍是建議你讀英文論文。
本篇開始說了其它一些一樣獲獎的算法,好比LeNet、AlexNet、GoogLeNet、ResNet,可是由於理解起來可能不如vgg-19方便,因此沒在這裏介紹。
但這些算法一樣很是優秀,後來的獲獎者更是超出vgg不少。這些能夠在網上搜索實現代碼,也有一些優秀的實現直接用pip就能夠找到,能夠用pip2 search tensorflow
搜索,由於使用tensorflow實現的算法通常都會有tensorflow的關鍵字標籤。
這種類型的算法基本上掌握了機器學習的基本理念,直接上手使用就好。具體實現原理,有興趣就找論文讀一讀,沒興趣就當作黑盒來使用就好。
(待續...)
使用Imagenet VGG-19模型進行圖片識別
使用tensorflow實現VGG19網絡
CNN的發展史
vgg model
深刻理解Neural Style