TensorFlow迁移学习的识别花试验
最近學習了TensorFlow,發現一個模型叫vgg16,然后搭建環境跑了一下,覺得十分神奇,而且準確率十分的高。又上了一節選修課,關于人工智能,老師讓做一個關于人工智能的試驗,于是覺得vgg16很不錯,可以直接用。
但發現vgg16是訓練好的模型,拿來直接用太沒水平,于是網上發現說可以用vgg16進行遷移學習。
我理解的遷移學習:
遷移學習符合人們學習的過程,如果要學習一樣新東西,我們肯定會運用或是借鑒之前的學習經驗,這樣能夠快速的把握要點,能夠快速的學習。遷移學習也是如此。
vgg16模型是前人訓練出的能夠識別1000種物品的模型,而且識別率很高,它的模型如圖:
可以數出綠色的模塊一共有16層,通過多層的卷積和池化,會提取圖片特征值,然后把圖片壓縮成一個一維數組輸入到全連接層中,圖中有三層全連接層fc1,fc2,fc3,再經過softmax輸出概率分布的預測結果。
進過試驗,這個模型能夠很好的識別花,但是如果要在此基礎上識別多種花,還需要在此基礎上進行訓練,但是訓練的過程將簡化很多。接下來通過代碼來講解
項目:
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 = Nonewith 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 + eachfiles = 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_batchelse: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_batchesfor 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和Yyield 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()#4threads = tf.train.start_queue_runners(sess=sess, coord=coord)#5for 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 += 1if 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 ftraindef per_picture():#deal with picturetestPicArr = []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 imageslabels_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矩陣中最大值的下標,如果是二維的加1preValue = 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字典,很容易地定義需要保持的變量及對應名稱:鍵對應使用的名稱,值對應被管理的變量。
轉載于:https://www.cnblogs.com/zhxuxu/p/9689778.html
總結
以上是生活随笔為你收集整理的TensorFlow迁移学习的识别花试验的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java自学小段 产生随机数
- 下一篇: 返回json格式