最近學習了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字典,很容易地定義須要保持的變量及對應名稱:鍵對應使用的名稱,值對應被管理的變量。