天天看點

從零到LeNet實作(二)附代碼詳解

經過這兩天的閱讀,算是稍稍掌握了tensorflow的一些用法。

占位符

我們定義Lenet類,在類裡建構網絡。

建構類的執行個體需要初始化内部參數,内部參數通過self.***定義實作。

但是訓練過程我們用的資料每一次是不同的,是以通過定義占位符實作,定義資料的格式和大小,在每一次訓練時進行傳遞不同的資料。

占位符的使用在(一)裡講過了。

再說一下,tf.cast:用于改變某個張量的資料類型

tensorflow有一個命名域的問題:

https://blog.csdn.net/u012436149/article/details/53081454

本次代碼中網絡的建構是通過slim庫實作的,這個庫相當于對tf自帶的conv又進行了一次封裝。

https://blog.csdn.net/Cyiano/article/details/75006883

https://www.jianshu.com/p/4b608c2313e2

關于slim庫的一些資料放在這裡了。建議看到我這篇文章得大家看一下撒。

tip1:建構網絡的過程中,我們往往需要随時檢視現在網絡的結果,但這些網絡都是張量,是以通過get_shape()函數檢視。

net = slim.max_pool2d(net, [2, 2], scope='pool4')
print(net.get_shape())      

就像這樣。

tip2:卷積過程中,我們總是會遇到一個padding的參數,它有兩種選擇,“SAME”"VALID"。

從零到LeNet實作(二)附代碼詳解

根據上述描述我們就可以知道;假設一個28*28的圖像,經過5*5的卷積核,步長為1,那麼卷積後的結果依舊是28*28.

其他的在代碼裡都有實作啦~~

不算上ui界面,這次代碼就算是看完啦~了解了很多東西~嘎嘎嘎~

但是好像寫的不夠清晰。沒事,以後有機會。

接下來,我要自己手撸一遍!

#!usr/bin/python
# -*- coding:<encoding name> -*-
import tensorflow as tf
import tensorflow.contrib.slim as slim
import config as cfg
import pdb
class Lenet:
    def __init__(self):
        # 占位符資料
        self.raw_input_image = tf.placeholder(tf.float32, [None, 784])
        self.input_images = tf.reshape(self.raw_input_image, [-1, 28, 28, 1])
        self.raw_input_label = tf.placeholder("float", [None, 10])
        # tf.cast() 改變資料類型
        self.input_labels = tf.cast(self.raw_input_label,tf.int32)
        self.dropout = cfg.KEEP_PROB

        with tf.variable_scope("Lenet") as scope:
            self.train_digits = self.construct_net(True)
            scope.reuse_variables()
            self.pred_digits = self.construct_net(False)
        #tf.argmax(vector, 1):傳回的是vector中的最大值的索引号
        self.prediction = tf.argmax(self.pred_digits, 1)
        self.correct_prediction = tf.equal(tf.argmax(self.pred_digits, 1), tf.argmax(self.input_labels, 1))
        self.train_accuracy = tf.reduce_mean(tf.cast(self.correct_prediction, "float"))
        # print('ddddd')


        #通過交叉熵函數求得loss值;
        self.loss = slim.losses.softmax_cross_entropy(self.train_digits, self.input_labels)
        #初始化學習率
        self.lr = cfg.LEARNING_RATE
        #根據學習率反向傳播優化參數
        self.train_op = tf.train.AdamOptimizer(self.lr).minimize(self.loss)


    def construct_net(self,is_trained = True):
        with slim.arg_scope([slim.conv2d], padding='VALID',
                            weights_initializer=tf.truncated_normal_initializer(stddev=0.01),
                            weights_regularizer=slim.l2_regularizer(0.0005)):
            # 卷積預設經過relu;slim是對tf中卷積的簡化;輸入的圖檔大小為28*28;經過5*5的卷積核,因為是SAME,是以還是28*28;
            # print(self.input_images.get_shape())
            net = slim.conv2d(self.input_images, 6, [5, 5], 1, padding='SAME', scope='conv1')
            # print(net.get_shape())
            #max_pool的卷積核大小為2*2;步長為1;經過max 變成14*14
            net = slim.max_pool2d(net, [2, 2], scope='pool2')
            # print(net.get_shape())

            #14*14經過卷積變成10*10
            net = slim.conv2d(net, 16, [5, 5], 1, scope='conv3')
            # print(net.get_shape())
            #經過pool,變成5*5
            net = slim.max_pool2d(net, [2, 2], scope='pool4')
            # print(net.get_shape())
            # 經過卷積變成1*1
            net1 = slim.conv2d(net, 120, [5, 5], 1, scope='conv5')

            # n1 = net1.get_shape()
            # print(n1)
            # 将120個1*1的撕扯成一個120維的長向量
            net2 = slim.flatten(net1, scope='flat6')
            # print(net2.get_shape())
            #展開成84維
            net3 = slim.fully_connected(net2, 84, scope='fc7')
            # print(net3.get_shape())
            net4 = slim.dropout(net3, self.dropout, is_training=is_trained, scope='dropout8')
            # print(net4.get_shape())
            digits = slim.fully_connected(net4, 10, scope='fc9')
            # pdb.set_trace()
        return digits