TensorFlow遷移學習的識別花試驗

最近學習了TensorFlow,發現一個模型叫vgg16,而後搭建環境跑了一下,以爲十分神奇,並且準確率十分的高。又上了一節選修課,關於人工智能,老師讓作一個關於人工智能的試驗,因而以爲vgg16很不錯,能夠直接用。python

但發現vgg16是訓練好的模型,拿來直接用太沒水平,因而網上發現說能夠用vgg16進行遷移學習。git

我理解的遷移學習:windows

遷移學習符合人們學習的過程,若是要學習同樣新東西,咱們確定會運用或是借鑑以前的學習經驗,這樣可以快速的把握要點,可以快速的學習。遷移學習也是如此。數組

vgg16模型是前人訓練出的可以識別1000種物品的模型,並且識別率很高,它的模型如圖:網絡

能夠數出綠色的模塊一共有16層,經過多層的卷積和池化,會提取圖片特徵值,而後把圖片壓縮成一個一維數組輸入到全鏈接層中,圖中有三層全鏈接層fc1,fc2,fc3,再通過softmax輸出機率分佈的預測結果。app

進過試驗,這個模型可以很好的識別花,可是若是要在此基礎上識別多種花,還須要在此基礎上進行訓練,可是訓練的過程將簡化不少。接下來經過代碼來說解函數

項目:學習

Transfer_learning:測試

  --checkpoint    #用來保存模型優化

      --   #自動生成的四個文件

  --flower_photos   #圖片

       --daisy

  --dandeline

  --roses

  --sunflowers

  --tulips

        #vgg16模型

  --utils.py

 --vgg16.npy

 --vgg16.py

  --app.py

  --ftrain.py

  --get_features.py

  --transfer_test.py

  --transfer_train.py

  --codes.npy #存儲圖片特徵值

  --labels #存儲圖片的標籤

  --1.jpg  #檢測圖片

  --.......

第一步獲取圖片的特徵,對於vgg16模型,fc1層以前所作的就是提取圖片特徵,咱們無需再費事去訓練模型去提取圖片的特徵,而是直接使用它提供的完善的模型去提取特徵,而且它可以很好的把握圖片的特徵,而後它這些特徵存儲起來,用於下面的訓練。

get_features.py

#coding=utf-8
import os
import numpy as np
import tensorflow as tf
import vgg16
import utils


#接下來咱們將 flower_photos 文件夾中的花朵圖片都載入到進來,而且用圖片所在的子文件夾做爲標籤值。
data_dir = 'flower_photos/'
contents = os.listdir(data_dir)
classes = [each for each in contents if os.path.isdir(data_dir + each)]

#利用vgg16計算獲得特徵值
# 首先設置計算batch的值,若是運算平臺的內存越大,這個值能夠設置得越高
batch_size = 10
# 用codes_list來存儲特徵值
codes_list = []
# 用labels來存儲花的類別
labels = []
# batch數組用來臨時存儲圖片數據
batch = []

codes = None


with tf.Session() as sess:
    # 構建VGG16模型對象
    vgg = vgg16.Vgg16()
    input_ = tf.placeholder(tf.float32, [None, 224, 224, 3])
    with tf.name_scope("content_vgg"):
        # 載入VGG16模型
        vgg.build(input_)
    
    # 對每一個不一樣種類的花分別用VGG16計算特徵值
    for each in classes:
        print ("Starting {} images".format(each))
        class_path = data_dir + each
        files = os.listdir(class_path)
        for ii, file in enumerate(files, 1):
            # 載入圖片並放入batch數組中
            img = utils.load_image(os.path.join(class_path, file))
            batch.append(img.reshape((1, 224, 224, 3)))
            labels.append(each)
            
            # 若是圖片數量到了batch_size則開始具體的運算
            if ii % batch_size == 0 or ii == len(files):
                images = np.concatenate(batch)

                feed_dict = {input_: images}
                # 計算特徵值
                codes_batch = sess.run(vgg.relu6, feed_dict=feed_dict)
                
                # 將結果放入到codes數組中
                if codes is None:
                    codes = codes_batch
                else:
                    codes = np.concatenate((codes, codes_batch))
                
                # 清空數組準備下一個batch的計算
                batch = []
                print ('{} images processed'.format(ii))
#code is a two-dimensional array including features of all pictures
#這樣咱們就能夠獲得一個 codes 數組,和一個 labels 數組,分別存儲了全部花朵的特徵值和類別。
#能夠用以下的代碼將這兩個數組保存到硬盤上:        
#with open('codes', 'w') as f:
np.save("codes.npy",codes)
    #codes.tofile(f)
    #not good size of file is too big
    #pickle.dump(codes,f)
    
import csv
with open('labels', 'w') as f:
    writer = csv.writer(f, delimiter='\n')
    writer.writerow(labels)
    #pickle.dump(labels,f)
    

進過上面代碼咱們已經獲得了圖片的特徵值和標籤,接下來,咱們須要設置一個全鏈接層來訓練,下面代碼中增長了一層256個節點和5個節點的全鏈接層。

ftrain.py

#coding=utf-8
import os
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
from sklearn.preprocessing import LabelBinarizer
import vgg16
import utils
from sklearn.model_selection import StratifiedShuffleSplit

#模型保存的路徑和名稱
MODEL_SAVE_PATH="./checkpoints/"
MODEL_NAME="flowers.ckpt"
LABELS = "labels"
CODES = "codes.npy"
codes = None
label = []
labels = []

if CODES:
    codes = np.load(CODES)
else:
    print ("No such file,please run get_feature.py first")

if LABELS:
    with open(LABELS,"r") as f: 
        label = f.readlines()
        for line in label:
            line = line.strip()
            labels.append(line)
else:
    print ("No such file,please run get_feature.py first")
#準備訓練集,驗證集和測試集
#首先我把 labels 數組中的分類標籤用 One Hot Encode 的方式替換
lb = LabelBinarizer()
lb.fit(labels)
labels_vecs = lb.transform(labels)
#return codes,labels,labels_vecs

'''
接下來就是抽取數據,由於不一樣類型的花的數據數量並非徹底同樣的,
並且 labels 數組中的數據也尚未被打亂,
因此最合適的方法是使用 StratifiedShuffleSplit 方法來進行分層隨機劃分。
假設咱們使用訓練集:驗證集:測試集 = 8:1:1,那麼代碼以下:
'''
ss = StratifiedShuffleSplit(n_splits=1, test_size=0.2)

train_idx, val_idx = next(ss.split(codes, labels))

half_val_len = int(len(val_idx)/2)
val_idx, test_idx = val_idx[:half_val_len], val_idx[half_val_len:]

train_x, train_y = codes[train_idx], labels_vecs[train_idx]
val_x, val_y = codes[val_idx], labels_vecs[val_idx]
test_x, test_y = codes[test_idx], labels_vecs[test_idx]

print ("Train shapes (x, y):", train_x.shape, train_y.shape)
print ("Validation shapes (x, y):", val_x.shape, val_y.shape)
print ("Test shapes (x, y):", test_x.shape, test_y.shape)

#訓練網絡
'''
分好了數據集以後,就能夠開始對數據集進行訓練了,
假設咱們使用一個 256 維的全鏈接層,
一個 5 維的全鏈接層(由於咱們要分類五種不一樣類的花朵),
和一個 softmax 層。固然,這裏的網絡結構能夠任意修改,
你能夠不斷嘗試其餘的結構以找到合適的結構。
'''
# 輸入數據的維度

# 標籤數據的維度
labels_ = tf.placeholder(tf.int64, shape=[None, labels_vecs.shape[1]])
inputs_ = tf.placeholder(tf.float32, shape=[None, codes.shape[1]])
# 加入一個256維的全鏈接的層

fc = tf.contrib.layers.fully_connected(inputs_, 256)

# 加入一個5維的全鏈接層
logits = tf.contrib.layers.fully_connected(fc, labels_vecs.shape[1], activation_fn=None)

# 獲得最後的預測分佈
predicted = tf.nn.softmax(logits)

# 計算cross entropy值
cross_entropy = tf.nn.softmax_cross_entropy_with_logits(labels=labels_, logits=logits)

# 計算損失函數
cost = tf.reduce_mean(cross_entropy)

# 採用用得最普遍的AdamOptimizer優化器
optimizer = tf.train.AdamOptimizer().minimize(cost)
correct_pred = tf.equal(tf.argmax(predicted, 1), tf.argmax(labels_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))


#爲了方便把數據分紅一個個 batch 以下降內存的使用,還能夠再用一個函數專門用來生成 batch。
def get_batches(x, y, n_batches=10):
    """ 這是一個生成器函數,按照n_batches的大小將數據劃分了小塊 """
    batch_size = len(x)//n_batches
    
    for ii in range(0, n_batches*batch_size, batch_size):
        # 若是不是最後一個batch,那麼這個batch中應該有batch_size個數據
        if ii != (n_batches-1)*batch_size:
            X, Y = x[ii: ii+batch_size], y[ii: ii+batch_size] 
        # 不然的話,那剩餘的不夠batch_size的數據都湊入到一個batch中
        else:
            X, Y = x[ii:], y[ii:]
        # 生成器語法,返回X和Y
        yield X, Y

通過上面的代碼,已經把圖片集分紅三部分,並且也設置好全鏈接成,接下來須要把圖片特徵喂入,開始訓練模型。

transfer_train.py

import os
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
import vgg16
import utils
import ftrain


#運行
# 運行多少輪次
epochs = 20
# 統計訓練效果的頻率
iteration = 0
# 保存模型的保存器
saver = tf.train.Saver()

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    coord = tf.train.Coordinator()#4
    threads = tf.train.start_queue_runners(sess=sess, coord=coord)#5
    
    for e in range(epochs):
        for x, y in ftrain.get_batches(ftrain.train_x, ftrain.train_y):
            feed = {ftrain.inputs_: x,
                    ftrain.labels_: y}
            # 訓練模型
            
            loss, _ = sess.run([ftrain.cost, ftrain.optimizer], feed_dict=feed)
            print ("Epoch: {}/{}".format(e+1, epochs),
                "Iteration: {}".format(iteration),
                "Training loss: {:.5f}".format(loss))
            iteration += 1
            
            if iteration % 5 == 0:
                feed = {ftrain.inputs_: ftrain.val_x,
                        ftrain.labels_: ftrain.val_y}
                
                val_acc = sess.run(ftrain.accuracy, feed_dict=feed)
                # 輸出用驗證機驗證訓練進度
                print ("Epoch: {}/{}".format(e, epochs),
                    "Iteration: {}".format(iteration),
                    "Validation Acc: {:.4f}".format(val_acc))
        # 保存模型
    saver.save(sess, os.path.join(ftrain.MODEL_SAVE_PATH, ftrain.MODEL_NAME))

在控制檯的輸出結果中,咱們能夠看到隨着迭代次數的增長,損失值在不斷的下降,精確性也在提升。把訓練好的模型保存起來,接下來用測試集來測試模型。

 

transfer_test.py

#coding=utf-8
import os
import numpy as np
import tensorflow as tf
import ftrain
import vgg16
import utils


#用測試集來測試模型效果
saver = tf.train.Saver()
with tf.Session() as sess:
    saver.restore(sess,tf.train.latest_checkpoint(ftrain.MODEL_SAVE_PATH))
    feed = {ftrain.inputs_: ftrain.test_x,
            ftrain.labels_: ftrain.test_y}  
    test_acc = sess.run(ftrain.accuracy,feed_dict=feed)
    print ("Test accuracy: {:.4f}".format(test_acc))

測試代碼,加載訓練好的模型,而後把測試集代碼喂入,計算精確度,控制檯能夠看到結果,個人結果達到90%以上

而後能夠應用這個模型,來識別圖片了,這個模型能夠識別5中話,能夠本身增長。

app.py

#coding=utf-8
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
import vgg16
import utils
import ftrain

def per_picture():
    #deal with picture
    testPicArr = []
    img_path = input('Input the path and image name:')
    img_ready = utils.load_image(img_path) 
    testPicArr.append(img_ready.reshape((1,224,224,3)))
    images = np.concatenate(testPicArr)
    return images


labels_vecs = ['daisy','dandelion','roses','sunflower','tulips']
labels_vecs = np.array(labels_vecs)
fig=plt.figure(u"Top-5 預測結果") 

saver = tf.train.Saver()
with tf.Session() as sess:
            #圖片預處理
    images = per_picture()
            #輸入vgg16中計算特徵值
    vgg = vgg16.Vgg16()
    input_ = tf.placeholder(tf.float32, [None, 224, 224, 3])
    with tf.name_scope("content_vgg"):
    # 載入VGG16模型
        vgg.build(input_)
    feed_dict = {input_: images}
    # 計算特徵值
    codes_batch = sess.run(vgg.relu6, feed_dict=feed_dict)
    #返回y矩陣中最大值的下標,若是是二維的加1
    preValue = tf.argmax(ftrain.predicted, 1)   
    #加載訓練好的新模型
    saver.restore(sess, tf.train.latest_checkpoint(ftrain.MODEL_SAVE_PATH))
    #計算預測值
    preValue = sess.run(preValue, feed_dict={ftrain.inputs_:codes_batch})
    print ("The prediction flower is:", labels_vecs[preValue])
    probability = sess.run(ftrain.predicted, feed_dict={ftrain.inputs_:codes_batch})
    top5 = np.argsort(probability[0])
    print ("top5:",top5)
    values = []
    bar_label = []
    for n, i in enumerate(top5): 
        print ("n:",n)
        print ("i:",i)
        values.append(probability[0][i]) 
        bar_label.append(labels_vecs[i]) 
        print (i, ":", labels_vecs[i], "----", utils.percent(probability[0][i])) 
    ax = fig.add_subplot(111) 
    ax.bar(range(len(values)), values, tick_label=bar_label, width=0.5, fc='g')
    ax.set_ylabel(u'probabilityit') 
    ax.set_title(u'Top-5') 
    for a,b in zip(range(len(values)), values):
        ax.text(a, b+0.0005, utils.percent(b), ha='center', va = 'bottom', fontsize=7)   
    plt.show() 

上面代碼過程是,首先要經過vgg16提取該圖片的特徵值,而後進行圖片預處理,加載訓練好的模型,輸入進去,得到結果。

結果展現以下:

       

能夠看到準確率很高,我每種圖片只訓練了144張,數據集有800多張。

以上代碼能夠在windows 下python3的環境運行。

代碼和數據集參考自:https://cosx.org/2017/10/transfer-learning/

 

對於實現以上過程遇到的問題:

 (1)首先對遷移學習的理解

  對於這個簡單的試驗,遷移學習主要體如今使用訓練好的vgg16模型(存儲在vgg16.npy中)提取圖片的特徵值,而後再對這些特徵值訓練。

(2)模型的保存和加載

  爲了不反覆的去提取圖片特徵值(這個很耗時間),把特徵值保存在codes.npy,把圖片的標籤存儲在labels中。還有就是存儲訓練模型,這裏由於對TensorFlow模型不太瞭解,因此出一個問題

存儲模型後,在另外一個文件中加載模型發現訓練的模型和沒訓練的模型同樣

  緣由是由於在另外一個文件中使用的不是同一個saver,我又對saver進行了初始化,致使使用了一個嶄新的模型。

(3)模型保存優化部分:

  若是你不給tf.train.Saver()傳入任何參數,那麼saver將處理graph中的全部變量。其中每個變量都以變量建立時傳入的名稱被保存。

有時候在檢查點文件中明肯定義變量的名稱頗有用。舉個例子,你也許已經訓練獲得了一個模型,其中有個變量命名爲"weights",你想把它的值恢復到一個新的變量"params"中。

有時候僅保存和恢復模型的一部分變量頗有用。再舉個例子,你也許訓練獲得了一個5層神經網絡,如今想訓練一個6層的新模型,能夠將以前5層模型的參數導入到新模型的前5層中。

你能夠經過給tf.train.Saver()構造函數傳入Python字典,很容易地定義須要保持的變量及對應名稱:鍵對應使用的名稱,值對應被管理的變量。

相關文章
相關標籤/搜索