實現遷徙學習-《Tensorflow 實戰Google深度學習框架》程式碼詳解
為了實現遷徙學習,首先是資料集的下載
#利用curl下載資料集 curl -o flower_photos.tgz http://download.tensorflow.org/example_images/flower_photos.tgz #在當前路徑下對下載的資料集進行解壓 tar xzf flower_photos.tgz
- 下載谷歌提供的訓練好的Inception-v3模型
wget -P /Volumes/Cu/QianXi_Learning --no-check-certificate https://storage.googleapis.com/download.tensorflow.org/models/inception_dec_2015.zip
wget -P是將模型下載到指定的資料集中
加入--no-check-certificate是因為wget在使用HTTPS協議時,預設會去驗證網站的證書,而這個證書驗證經常會失敗,為了解決這個問題而新增的
- 解壓所訓練好的模型
和書上提供的解壓不一樣,因為我的終端已經是在根目錄下,因此直接
unzip /Volumes/Cu/QianXi_Learning/inception_dec_2015.zip
下面開始正式的利用下列程式碼實現遷徙學習
遷徙學習的程式碼如下
1 # -*- coding: utf-8 -*- 2 importglob 3 import os.path 4 import random 5 import numpy as np 6 import tensorflow as tf 7 from tensorflow.python.platform import gfile
1. 進行模型和樣本引數和路徑的設定
1 #Inception_v3模型瓶頸層的節點個數 2 BOTTLENECK_TENSOR_SIZE = 2048 3 BOTTLENECK_TENSOR_NAME = 'pool_3/_reshape:0' 4 JPEG_DATA_TENSOR_NAME = 'DecodeJpeg/contents:0' 5 6 7 MODEL_DIR = '/Volumes/Cu/QianXi_Learning/inception_dec_2015' 8 MODEL_FILE= 'tensorflow_inception_graph.pb' 9 10 # 因為一個訓練資料會被使用多次,所以可以將原始影象通過Inception-v3模型計算得到的特徵向量儲存在檔案中,免去重複的計算。 11 CACHE_DIR = '/Volumes/Cu/QianXi_Learning/bottleneck' 12 INPUT_DATA = '/Volumes/Cu/QianXi_Learning/flower_photos' 13 14 # 驗證的資料百分比 15 VALIDATION_PERCENTAGE = 10 16 # 測試的資料百分比 17 TEST_PERCENTAGE = 10 18 19 #神經網路引數的設定 20 LEARNING_RATE = 0.01 21 STEPS = 4000 22 BATCH = 100
在Inception_v3模型中代表瓶頸層結果的張量名稱,在谷歌提供的Inception+v3模型中,這個張量的名稱就是'pool_3/_reshape:0',並且在模型準備的時候,我們就已經將訓練好的Inception_v3模型存放到已有的資料夾中,因此只需要指定其路徑與模型名稱即可,對於訓練網路的其它引數,也不再進行贅述
2. 最基本的模型引數與路徑設定完之後,我們就會開始對模型進行定義一系列的函式,首先是把樣本中的所有圖片列表化並且將其按照訓練、驗證、測試資料分開
1 #這個函式從資料資料夾中讀取所有的圖片列表並把樣本中所有的圖片列表並按訓練、驗證、測試資料分開 2 #在函式中testing_percentage, validation_percentage這兩個引數指定了測試資料集和驗證資料集的大小 3 def create_image_lists(testing_percentage, validation_percentage): 4 result = {} 5 # 獲取當前目錄下所有的子目錄 6 sub_dirs = [x[0] for x in os.walk(INPUT_DATA)] 7 # 得到的第一個目錄是當前目錄,不需要考慮 8 is_root_dir = True 9 for sub_dir in sub_dirs: 10 if is_root_dir: 11 is_root_dir = False 12 continue 13 14 #獲取當前目錄下所有有效圖片檔案 15 extensions = ['jpg', 'jpeg', 'JPG', 'JPEG'] 16 17 file_list = [] 18 dir_name = os.path.basename(sub_dir) 19 for extension in extensions: 20 file_glob = os.path.join(INPUT_DATA, dir_name, '*.' + extension) 21 file_list.extend(glob.glob(file_glob)) 22 if not file_list: continue 23 24 #通過目錄名獲取類別的名稱 25 label_name = dir_name.lower() 26 27 # 初始化當前類別的訓練資料集,測試資料集和驗證資料集 28 training_images = [] 29 testing_images = [] 30 validation_images = [] 31 for file_name in file_list: 32 base_name = os.path.basename(file_name) 33 34 # 隨機將資料分到訓練資料集,測試資料集和驗證資料集 35 chance = np.random.randint(100) 36 if chance < validation_percentage: 37 validation_images.append(base_name) 38 elif chance < (testing_percentage + validation_percentage): 39 testing_images.append(base_name) 40 else: 41 training_images.append(base_name) 42 43 #將當前類別的資料放入結果字典 44 result[label_name] = { 45 'dir': dir_name, 46 'training': training_images, 47 'testing': testing_images, 48 'validation': validation_images, 49 } 50 #返回整理好的所有資料 51 return result
對於這個函式,其形參就是testing_percentage和validation_percentage,我們將結果存放在一個字典中
3. 定義函式通過類別名稱、所屬資料集和圖片編號獲取一張圖片的地址
1 #這個函式通過類別名稱、所屬資料集和圖片編號獲取一張圖片的地址 2 #image_lists引數給出了所有圖片的資訊 3 #image_dir引數給出了根目錄 4 #label_name引數定義了類別的名稱 5 #index引數給定了需要獲取的圖片的編號 6 #category引數指定了需要獲取的圖片實在訓練資料集,測試資料集還是驗證資料集 7 def get_image_path(image_lists, image_dir, label_name, index, category): 8 #獲取給定類別中所有圖片的資訊 9 label_lists = image_lists[label_name] 10 #根據所屬資料集的名稱獲取集合中的全部圖片資訊 11 category_list = label_lists[category] 12 mod_index = index % len(category_list) 13 # 獲取圖片的檔名 14 base_name = category_list[mod_index] 15 sub_dir = label_lists['dir'] 16 #最終的地址為資料根目錄的地址加上類別的資料夾加上圖片的名稱 17 full_path = os.path.join(image_dir, sub_dir, base_name) 18 return full_path
4. 定義函式獲取Inception_v3模型處理後的特徵向量的檔案地址
1 #定義函式獲取Inception-v3模型處理之後的特徵向量的檔案地址 2 def get_bottleneck_path(image_lists, label_name, index, category): 3 return get_image_path(image_lists, CACHE_DIR, label_name, index, category) + '.txt'
5. 定義函式使用載入好的Inception_v3模型處理每一張圖片,得到這個圖片的特徵向量
1 #定義函式使用載入的訓練好的Inception-v3模型處理一張圖片,得到這個圖片的特徵向量 2 #這個過程實際上就是將當前圖片作為輸入計算瓶頸張量的值,這個瓶頸張量的值就是這張圖片的新的特徵向量 3 def run_bottleneck_on_image(sess, image_data, image_data_tensor, bottleneck_tensor): 4 5 bottleneck_values = sess.run(bottleneck_tensor, {image_data_tensor: image_data}) 6 #經過卷積神經網路處理的結果是一個四維陣列,需要將這個結果壓縮成一個特徵向量(一維陣列) 7 bottleneck_values = np.squeeze(bottleneck_values) 8 return bottleneck_values
6. 函式獲取一張圖片經過Inception_v3模型處理之後的特徵向量,這個函式會先試圖尋找已經計算且儲存下來的特徵向量,如果找不到則先計算這個特徵向量,然後儲存到檔案
1 #這個函式獲取一張圖片經過Inception_v3模型處理之後的特徵向量,這個函式會先試圖尋找已經計算且儲存下來的特徵向量,如果找不到則先計算這個特徵向量,然後儲存到檔案 2 def get_or_create_bottleneck(sess, image_lists, label_name, index, category, jpeg_data_tensor, bottleneck_tensor): 3 label_lists = image_lists[label_name] 4 sub_dir = label_lists['dir'] 5 sub_dir_path = os.path.join(CACHE_DIR, sub_dir) 6 if not os.path.exists(sub_dir_path): os.makedirs(sub_dir_path) 7 bottleneck_path = get_bottleneck_path(image_lists, label_name, index, category) 8 if not os.path.exists(bottleneck_path): 9 10 image_path = get_image_path(image_lists, INPUT_DATA, label_name, index, category) 11 12 image_data = gfile.FastGFile(image_path, 'rb').read() 13 14 bottleneck_values = run_bottleneck_on_image(sess, image_data, jpeg_data_tensor, bottleneck_tensor) 15 16 bottleneck_string = ','.join(str(x) for x in bottleneck_values) 17 with open(bottleneck_path, 'w') as bottleneck_file: 18 bottleneck_file.write(bottleneck_string) 19 else: 20 21 with open(bottleneck_path, 'r') as bottleneck_file: 22 bottleneck_string = bottleneck_file.read() 23 bottleneck_values = [float(x) for x in bottleneck_string.split(',')] 24 25 return bottleneck_values
7. 定義一個函式隨機獲取一個batch的圖片作為訓練資料
1 #這個函式隨機獲取一個batch的圖片作為訓練資料 2 def get_random_cached_bottlenecks(sess, n_classes, image_lists, how_many, category, jpeg_data_tensor, bottleneck_tensor): 3 bottlenecks = [] 4 ground_truths = [] 5 for _ in range(how_many): 6 label_index = random.randrange(n_classes) 7 label_name = list(image_lists.keys())[label_index] 8 image_index = random.randrange(65536) 9 bottleneck = get_or_create_bottleneck( 10 sess, image_lists, label_name, image_index, category, jpeg_data_tensor, bottleneck_tensor) 11 ground_truth = np.zeros(n_classes, dtype=np.float32) 12 ground_truth[label_index] = 1.0 13 bottlenecks.append(bottleneck) 14 ground_truths.append(ground_truth) 15 16 return bottlenecks, ground_truths
8. 定義一個函式獲取全部的測試資料,並計算正確率
1 #這個函式獲取全部的測試資料,並計算正確率 2 def get_test_bottlenecks(sess, image_lists, n_classes, jpeg_data_tensor, bottleneck_tensor): 3 bottlenecks = [] 4 ground_truths = [] 5 label_name_list = list(image_lists.keys()) 6 for label_index, label_name in enumerate(label_name_list): 7 category = 'testing' 8 for index, unused_base_name in enumerate(image_lists[label_name][category]): 9 bottleneck = get_or_create_bottleneck(sess, image_lists, label_name, index, category,jpeg_data_tensor, bottleneck_tensor) 10 ground_truth = np.zeros(n_classes, dtype=np.float32) 11 ground_truth[label_index] = 1.0 12 bottlenecks.append(bottleneck) 13 ground_truths.append(ground_truth) 14 return bottlenecks, ground_truths
9. 定義主函式
1 #定義主函式 2 def main(_): 3 image_lists = create_image_lists(TEST_PERCENTAGE, VALIDATION_PERCENTAGE) 4 n_classes = len(image_lists.keys()) 5 6 # 讀取已經訓練好的Inception-v3模型。 7 with gfile.FastGFile(os.path.join(MODEL_DIR, MODEL_FILE), 'rb') as f: 8 graph_def = tf.GraphDef() 9 graph_def.ParseFromString(f.read()) 10 bottleneck_tensor, jpeg_data_tensor = tf.import_graph_def( 11 graph_def, return_elements=[BOTTLENECK_TENSOR_NAME, JPEG_DATA_TENSOR_NAME]) 12 13 # 定義新的神經網路輸入 14 bottleneck_input = tf.placeholder(tf.float32, [None, BOTTLENECK_TENSOR_SIZE], name='BottleneckInputPlaceholder') 15 ground_truth_input = tf.placeholder(tf.float32, [None, n_classes], name='GroundTruthInput') 16 17 # 定義一層全連結層 18 with tf.name_scope('final_training_ops'): 19 weights = tf.Variable(tf.truncated_normal([BOTTLENECK_TENSOR_SIZE, n_classes], stddev=0.001)) 20 biases = tf.Variable(tf.zeros([n_classes])) 21 logits = tf.matmul(bottleneck_input, weights) + biases 22 final_tensor = tf.nn.softmax(logits) 23 24 # 定義交叉熵損失函式。 25 cross_entropy = tf.nn.softmax_cross_entropy_with_logits(logits, ground_truth_input) 26 cross_entropy_mean = tf.reduce_mean(cross_entropy) 27 train_step = tf.train.GradientDescentOptimizer(LEARNING_RATE).minimize(cross_entropy_mean) 28 29 # 計算正確率。 30 with tf.name_scope('evaluation'): 31 correct_prediction = tf.equal(tf.argmax(final_tensor, 1), tf.argmax(ground_truth_input, 1)) 32 evaluation_step = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) 33 34 with tf.Session() as sess: 35 init = tf.global_variables_initializer() 36 sess.run(init) 37 # 訓練過程。 38 for i in range(STEPS): 39 40 train_bottlenecks, train_ground_truth = get_random_cached_bottlenecks( 41 sess, n_classes, image_lists, BATCH, 'training', jpeg_data_tensor, bottleneck_tensor) 42 sess.run(train_step, 43 feed_dict={bottleneck_input: train_bottlenecks, ground_truth_input: train_ground_truth}) 44 45 if i % 100 == 0 or i + 1 == STEPS: 46 validation_bottlenecks, validation_ground_truth = get_random_cached_bottlenecks( 47 sess, n_classes, image_lists, BATCH, 'validation', jpeg_data_tensor, bottleneck_tensor) 48 validation_accuracy = sess.run(evaluation_step, feed_dict={ 49 bottleneck_input: validation_bottlenecks, ground_truth_input: validation_ground_truth}) 50 print('Step %d: Validation accuracy on random sampled %d examples = %.1f%%' % 51 (i, BATCH, validation_accuracy * 100)) 52 53 # 在最後的測試資料上測試正確率。 54 test_bottlenecks, test_ground_truth = get_test_bottlenecks( 55 sess, image_lists, n_classes, jpeg_data_tensor, bottleneck_tensor) 56 test_accuracy = sess.run(evaluation_step, feed_dict={ 57 bottleneck_input: test_bottlenecks, ground_truth_input: test_ground_truth}) 58 print('Final test accuracy = %.1f%%' % (test_accuracy * 100)) 59 60 61 if __name__ == '__main__': 62 tf.app.run()