1. 程式人生 > >Tensorflow框架(四)

Tensorflow框架(四)

《TensorFlow 實戰Google深度學習框架》文中對這一章的描述個人感覺過於簡單,尤其是針對一些經典的卷積網路。所以本章重點介紹如何搭建這些經典的網路結構,對於模型的細節部分,本章不允以過多介紹,有興趣的可以查閱其他資料文獻

一、卷積層

卷積層就是對過濾器的定義

  • 前兩個維度分別表示了過濾器的尺寸(也就是長了寬)   
  • 第三個維度表示了當前層的深度(其實過濾器的深度等於當前層的深度)
  • 第四個維度表示了過濾器的個數(書上說是過濾器深度,不過個人覺得過濾器個數更直觀)

注意過濾器與過濾器之間引數不共享,過濾器在當前圖層滑動時權重共享

# 定義過濾器權重與偏置
filter_weight = tf.get_variable(
    'weights', [5, 5, 3, 16],
    initializer = tf.truncated_normal_initializer(stddev = 0.1))
biases = tf.get_variable('biases', [16], initializer = tf.constant_initializer(0.1))

# 定義過濾器
# input是四個維度張量,以平面圖為例,第一個維度可以表示第幾張圖片,後三個維度代表該圖
# strides = [1,1,1,1]的第一個維度和第四個維度一定是1,步長只對矩陣的長和寬有效
# SAME表示邊緣填充空白,經過卷積變換後圖像與未變換前尺寸一樣,如果是VALID表示不新增
conv = tf.nn.conv2d(input, filter_weight, strides = [1, 1, 1, 1], padding = 'SAME')

# 為每一個過濾器卷積得到的圖層加上偏執
bias = tf.nn.bias_add(conv, biases)

# Relu函式啟用
actived_conv = tf.nn.relu(bias)

 

二、池化層

常用的有最大池化層和平均池化層

# 最大池化層
# ksize維度裡第一個和第四個必須為1,第二個和第三個維度表示過濾器尺寸
pool = tf.nn.max_pool(actived_conv, ksize = [1,3,3,1],
                      strides = [1,2,2,1], padding = 'SAME')
# 平均池化層
pool = tf.nn.avg_pool(actived_conv, ksize = [1,3,3,1],
                      strides = [1,2,2,1], padding = 'SAME')

 

三、LeNet-5網路

下圖給的是論文中的LeNet-5網路模型,在下述程式碼中我們並不重建這個模型,而是用一種類似的網路結構。需要指出的是,書中的程式碼個人感覺有點生澀難懂,而且由於使用的是梯度下降法,會導致模型收斂速度很慢。因此修改使用Adam優化演算法。

下述中的程式碼不儲存模型引數,實施列印預測測試集的效果。 

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

# 載入資料集
mnist = input_data.read_data_sets("C:/Users/14981/Desktop/Deep Learning/", one_hot = True)

# 載入引數
times = 20
batch_size = 128
regularization = 0.0001
moving_average_decay = 0.99
learning_rate_base = 0.8
learning_rate_decay = 0.99
batch_num = mnist.train.num_examples // batch_size

# 通用函式
def weight_variable(name, shape):
    initial = tf.get_variable(name, shape,
                           initializer = tf.truncated_normal_initializer(stddev=0.1))
    return initial

def bias_variable(name, shape):
    initial = tf.get_variable(name, shape,
                             initializer = tf.constant_initializer(0.1))
    return initial

def inference(input_tensor, regularizer, keep_prob):
    images = tf.reshape(input_tensor, [-1,28,28,1])
    
    with tf.variable_scope('layer1-conv1'):
        conv1_weights = weight_variable('weight', [5,5,1,32])
        conv1_biases = bias_variable('bias', [32])
        conv1 = tf.nn.conv2d(images, conv1_weights, strides = [1,1,1,1], padding = 'SAME')
        relu1 = tf.nn.relu(conv1 + conv1_biases)
    with tf.name_scope('layer2-pool1'):
        pool1 = tf.nn.max_pool(relu1, ksize = [1,2,2,1], strides = [1,2,2,1], padding = 'SAME')
    
    with tf.variable_scope('layer3-conv2'):
        conv2_weights = weight_variable('weight', [5,5,32,64])
        conv2_biases = bias_variable('bias', [64])
        conv2 = tf.nn.conv2d(pool1, conv2_weights, strides = [1,1,1,1], padding = 'SAME')
        relu2 = tf.nn.relu(conv2 + conv2_biases)
    with tf.name_scope('layer4-pool2'):
        pool1 = tf.nn.max_pool(relu2, ksize = [1,2,2,1], strides = [1,2,2,1], padding = 'SAME')
    
    # 注意此時圖片的維度被壓縮成了7*7*64
    pool_shape = tf.reshape(pool1, (-1,7*7*64))
    
    with tf.variable_scope('layer5-fc1'):
        fc1_weights = weight_variable('weight', [7*7*64,512])
        if regularizer != None:
            tf.add_to_collection('losses', regularizer(fc1_weights))
        fc1_biases = bias_variable('bias', [512])
        fc1 = tf.nn.relu(tf.matmul(pool_shape, fc1_weights) + fc1_biases)
        # dropout優化演算法
        fc1 = tf.nn.dropout(fc1, keep_prob)
    
    with tf.variable_scope('layer6-fc2'):
        fc2_weights = weight_variable('weight', [512, 10])
        if regularizer != None:
            tf.add_to_collection('losses', regularizer(fc2_weights))
        fc2_biases = bias_variable('bias',[10])
        logit = tf.nn.softmax(tf.matmul(fc1, fc2_weights) + fc2_biases)
    
    return logit

def train(mnist):
    x = tf.placeholder(tf.float32, [None,784], name = 'x-input')
    y = tf.placeholder(tf.float32, [None,10], name = 'y-input')
    keep_prob = tf.placeholder(tf.float32)
    
    regularizer = tf.contrib.layers.l2_regularizer(regularization)
    logit = inference(x, regularizer, keep_prob)
    
    # 定義滑動平均模型
    global_step = tf.Variable(0, trainable = False)
    variable_averages = tf.train.ExponentialMovingAverage(moving_average_decay)
    variable_averages_op = variable_averages.apply(tf.trainable_variables())
    
    # 定義損失函式
    cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(
        logits = logit, labels = tf.argmax(y, 1))
    cross_entropy_mean = tf.reduce_mean(cross_entropy)
    loss = cross_entropy_mean + tf.add_n(tf.get_collection('losses'))
    
    # 定義學習率衰減
    learning_rate = tf.train.exponential_decay(
        learning_rate_base,
        global_step,
        mnist.train.num_examples, # 總玩所有資料需要的迭代次數
        learning_rate_decay
        )
    train_step = tf.train.AdamOptimizer(1e-4).\
    minimize(loss, global_step = global_step)
    
    # 定義反向傳播滑動平均模型
    with tf.control_dependencies([train_step, variable_averages_op]):
        train_op = tf.no_op(name = 'train')
    
    # 準確率
    correction_prediction = tf.equal(tf.argmax(logit, 1), tf.argmax(y, 1))
    accuracy = tf.reduce_mean(tf.cast(correction_prediction, tf.float32))
    
    # 引數初始化
    init = tf.global_variables_initializer()
    
    with tf.Session() as sess:
        sess.run(init)
        # 總共的批次共迭代times次
        for epoch in range(times):
            for batch in range(batch_num):
                xs, ys = mnist.train.next_batch(batch_size)
                _, loss_value, step = sess.run([train_op, loss, global_step],
                                               feed_dict = {x:xs,y:ys,keep_prob:0.7})
            acc = sess.run(accuracy,
                           feed_dict = {x:mnist.test.images, y:mnist.test.labels,keep_prob:1.0})
            print("After %d, Testing Accuracy = %g" %(epoch, acc))

# 訓練
train(mnist)

 

四、經典網路模型

後續會單獨整理一篇由經典網路模型的文章

 

五、遷移學習

本段程式碼摘自書上,經個人實際執行後結果正確,同時做了更詳細的程式碼註釋,並對書中冗餘的程式碼進行了優化。

首先是處理資料集,並最終將資料集的資訊儲存在.npy檔案中

import numpy as np
import os.path
import glob
from scipy.misc import imread, imresize

# 輸入檔案位置
input_data = 'C:\\Users\\guesthost\\Desktop\\深度學習資料集\\flower_photos\\flower_photos'
# 輸出檔案位置
output_data = 'C:\\Users\\guesthost\\Desktop\\深度學習資料集\\flower_processed_data.npy'

# 測試資料與驗證資料的比例
validation_percentage = 10
test_percentage = 10

def create_image_lists(testing_percentage, validation_percentage):
    # 在目錄樹中游走以便讀取目錄下的資料夾名稱
    sub_dirs = [x[0] for x in os.walk(input_data)]
    is_root_dir = True
    
    training_images = []
    training_labels = []
    testing_images = []
    testing_labels = []
    validation_images = []
    validation_labels = []
    current_label = 0
    
    for sub_dir in sub_dirs:
        ret = 1
        # 忽略根目錄
        if is_root_dir:
            is_root_dir = False
            continue
            
        extensions = ['jpg', 'jpeg']
        file_list = []
        dir_name = os.path.join(sub_dir)
        for extension in extensions:
            # 讀取當前目錄下所有以extension為字尾的檔案
            file_glob = os.path.join(input_data, dir_name, '*.' + extension)
            file_list.extend(glob.glob(file_glob))
        if not file_list:
            continue

        # 對file_list列表下所有圖片進行處理
        for file_name in file_list:
            # 對圖片進行解碼
            image = imread(file_name)
            image_value = imresize(image, [299, 299])

            # 隨機劃分資料集
            # 根據訓練集:驗證集:測試集 = 8:1:1比例來劃分資料
            chance = np.random.randint(100)
            if chance < validation_percentage:
                validation_images.append(image_value)
                validation_labels.append(current_label)
            elif chance < (test_percentage + validation_percentage):
                testing_images.append(image_value)
                testing_labels.append(current_label)
            else:
                training_images.append(image_value)
                training_labels.append(current_label)
            print("%d picture is completed" % ret)
            ret += 1
        print("%d dir is completed!" % current_label)
        current_label += 1
            
    # 獲取隨機狀態
    state = np.random.get_state()
    np.random.shuffle(training_images)
    # 以打亂訓練集影象矩陣的狀態同樣打亂訓練集影象標籤
    np.random.set_state(state)
    np.random.shuffle(training_labels)
    
    return np.asarray([training_images, training_labels,
                      validation_images, validation_labels,
                      testing_images, testing_labels])
    
def main():
    processed_data = create_image_lists(test_percentage, validation_percentage)
    np.save(output_data, processed_data)
        
if __name__ == '__main__':
    main()

使用Google訓練好的Inception-v3網路模型做遷移學習

import tensorflow as tf
import tensorflow.contrib.slim.python.slim.nets.inception_v3 as inception_v3
import tensorflow.contrib.slim as slim
import load_dataset as load
import numpy as np

# 輸入資料路徑
Input_data = load.output_data
# 預先訓練好的模型路徑
ckpt_file = 'C:/Users/guesthost/Desktop/機器學習/深度學習/深度學習預訓練模型/inception_v3_2016_08_28/inception_v3.ckpt'
# 訓練好的模型路徑
train_file = 'C:/Users/guesthost/Desktop/機器學習/深度學習/深度學習預訓練模型/my_inception_model/my_inception_v3_model'

input_data = load.output_data
learning_rate = 1e-4
steps = 300
batch = 32
n_classes = 5

checkpoint_exclude_scopes = 'InceptionV3/Logits,InceptionV3/AuxLogits'
trainable_scopes = 'InceptionV3/Logits, InceptionV3/AuxLogits'

# 獲取所有需要從訓練好的模型中載入的引數
def get_tuned_variables():
    exclusions = [scope.strip() for scope in checkpoint_exclude_scopes.split(',')]
    variables_to_restore = []
    
    # 獲取模型所有變數的列表
    # 之後篩選出除了op名稱的開頭為exclusion的以外的op作為需要訓練的引數返回
    for var in slim.get_model_variables():
        excluded = False
        for exclusion in exclusions:
            if var.op.name.startswith(exclusion):
                excluded = True
                break
        if not excluded:
            variables_to_restore.append(var)
    return variables_to_restore

# 獲取所有需要訓練的變數列表
def get_trainable_variables():
    scopes = [scope.strip() for scope in trainable_scopes.split(',')]
    variables_to_train = []
    
    for scope in scopes:
        # 獲取tf.GraphKeys圖物件中所有可訓練的變數
        variables = tf.get_collection(
            tf.GraphKeys.TRAINABLE_VARIABLES, scope)
        variables_to_train.extend(variables)
    return variables_to_train

def main():
    processed_data = np.load(input_data)
    training_images = processed_data[0]
    n_training_example = len(training_images)
    training_labels = processed_data[1]
    validation_images = processed_data[2]
    validation_labels = processed_data[3]
    testing_images = processed_data[4]
    testing_labels = processed_data[5]
    print("%d trianing examples, %d validation examples and %d testing examples."\
          %(n_training_example, len(validation_images), len(testing_images)))
    
    images = tf.placeholder(tf.float32, shape = [None, 299, 299, 3], name = 'x-input')
    labels = tf.placeholder(tf.int64, shape = [None], name = 'labels')
    
    # 載入模型的預設變數
    with slim.arg_scope(inception_v3.inception_v3_arg_scope()):
        logits, _ = inception_v3.inception_v3(images, num_classes = n_classes)
    
    # 交叉熵損失
    tf.losses.softmax_cross_entropy(tf.one_hot(labels, n_classes), logits = logits, weights = 1.0)

    # 定義訓練過程
    train_step = tf.train.RMSPropOptimizer(learning_rate).minimize(tf.losses.get_total_loss())
    
    # 計算正確率
    with tf.name_scope('evaluation'):
        correct_prediction = tf.equal(tf.argmax(logits, 1), labels)
        evaluation_step = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
    
    # 定義從ckpt檔案中返回指定的變數的函式
    # 這裡的get_tuned_variables() 表示返回需要訓練的變數
    # ignore_missing_vars = True 表示若沒有變數時,返回None
    load_fn = slim.assign_from_checkpoint_fn(ckpt_file, get_tuned_variables(), ignore_missing_vars = True)
    
    # 定義儲存新的訓練好得模型
    saver = tf.train.Saver()
    with tf.Session() as sess:
        # 初始化需要在模型載入之前,否則初始化過程會將已經載入好的變數衝重新賦值
        init = tf.global_variables_initializer()
        sess.run(init)
        
        print("Loading tuned variables from %s" % ckpt_file)
        load_fn(sess)
        start = 0
        end = batch
        for i in range(steps):
            start = (i * batch) % n_training_example
            end = min(start + batch, n_training_example)
            
            sess.run(train_step, feed_dict = {images:training_images[start:end],labels:training_labels[start:end]})
            
            # 列印驗證集準確率
            if i % 30 == 0 or i + 1 == steps:
                saver.save(sess, train_file, global_step = i)
                validation_accuracy = sess.run(evaluation_step, feed_dict = {images:validation_images, labels:validation_labels})
                print("Step %d Validation accuracy = %.1f%%" %(i, validation_accuracy * 100))
            
            # 列印測試集準確率
        test_accuracy = sess.run(evaluation_step, feed_dict = {images:testing_images, labels:testing_labels})
        print('Final test accuracy = %.1f%%' %(test_accuracy * 100))
            
main()