一個單層的基礎神經網路實現手寫數字識別

kumfo發表於2017-12-28

先上程式碼

import tensorflow
from tensorflow.examples.tutorials.mnist import input_data
import matplotlib.pyplot as plt

# 普通的神經網路學習
# 學習訓練類
class Normal:

    weight = []
    biases = []

    def __init__(self):
        self.times = 1000
        self.mnist = []
        self.session = tensorflow.Session()
        self.xs = tensorflow.placeholder(tensorflow.float32, [None, 784])
        self.ys = tensorflow.placeholder(tensorflow.float32, [None, 10])
        self.save_path = 'learn/result/normal.ckpt'

    def run(self):
        self.import_data()
        self.train()
        self.save()

    def _setWeight(self,weight):
        self.weight = weight

    def _setBiases(self,biases):
        self.biases = biases

    def _getWeight(self):
        return self.weight

    def _getBiases(self):
        return self.biases
    # 訓練
    def train(self):

        prediction = self.add_layer(self.xs, 784, 10, activation_function=tensorflow.nn.softmax)

        cross_entropy = tensorflow.reduce_mean(
            -tensorflow.reduce_sum(
                self.ys * tensorflow.log(prediction)
                , reduction_indices=[1])
        )
        train_step = tensorflow.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)

        self.session.run(tensorflow.global_variables_initializer())

        for i in range(self.times):
            batch_xs, batch_ys = self.mnist.train.next_batch(100)
            self.session.run(train_step, feed_dict={self.xs: batch_xs, self.ys: batch_ys})
            if i % 50 == 0:
                # images 變換為 labels,images相當於x,labels相當於y
                accurary = self.computer_accurary(
                    self.mnist.test.images,
                    self.mnist.test.labels,
                    prediction
                )

    # 資料匯入
    def import_data(self):
        self.mnist = input_data.read_data_sets('MNIST_data', one_hot=True)

    # 資料儲存
    def save(self):
        saver = tensorflow.train.Saver()
        path = saver.save(self.session,self.save_path)

    # 新增隱藏層
    def add_layer(self,inputs,input_size,output_size,activation_function=None):

        weight = tensorflow.Variable(tensorflow.random_normal([input_size,output_size]),dtype=tensorflow.float32,name='weight')

        biases = tensorflow.Variable(tensorflow.zeros([1,output_size]) + 0.1,dtype=tensorflow.float32,name='biases')
        Wx_plus_b = tensorflow.matmul(inputs,weight) + biases

        self._setBiases(biases)
        self._setWeight(weight)

        if activation_function is None:
            outputs = Wx_plus_b
        else:
            outputs = activation_function(Wx_plus_b,)

        return outputs


    # 計算結果資料與實際資料的正確率
    def computer_accurary(self,x_data,y_data,tf_prediction):

        prediction = self.session.run(tf_prediction,feed_dict={self.xs:x_data,self.ys:y_data})

        # 返回兩個矩陣中最大值的索引號位置,然後進行相應位置的值大小比較並在此位置設定為True/False
        correct_predition = tensorflow.equal(tensorflow.argmax(prediction,1),tensorflow.argmax(y_data,1))

        # 進行資料格式轉換,然後進行降維求平均值
        accurary = tensorflow.reduce_mean(tensorflow.cast(correct_predition,tensorflow.float32))

        result = self.session.run(accurary,feed_dict={self.xs:x_data,self.ys:y_data})

        return result

# 識別類
class NormalRead(Normal):

    input_size = 784
    output_size = 10

    def run(self):
        self.import_data()
        self.getSaver()
        origin_input = self._getInput()
        output = self.recognize(origin_input)

        self._showImage(origin_input)
        self._showOutput(output)
        pass

    # 顯示識別結果
    def _showOutput(self,output):
        number = output.index(1)
        print('識別到的數字:',number)

    # 顯示被識別圖片
    def _showImage(self,origin_input):
        data = []
        tmp = []
        i = 1
        # 原資料轉換為可顯示的矩陣
        for v in origin_input[0]:
            if i %28 == 0:
                tmp.append(v)
                data.append(tmp)
                tmp = []
            else:
                tmp.append(v)
            i += 1

        plt.figure()
        plt.imshow(data, cmap='binary')  # 黑白顯示
        plt.show()


    def _setBiases(self,biases):
        self.biases = biases
        pass

    def _setWeight(self,weight):
        self.weight = weight
        pass

    def _getBiases(self):
        return self.biases

    def _getWeight(self):
        return self.weight

    # 獲取訓練模型
    def getSaver(self):
        weight = tensorflow.Variable(tensorflow.random_normal([self.input_size, self.output_size]), dtype=tensorflow.float32,name='weight')

        biases = tensorflow.Variable(tensorflow.zeros([1, self.output_size]) + 0.1, dtype=tensorflow.float32, name='biases')

        saver = tensorflow.train.Saver()
        saver.restore(self.session,self.save_path)

        self._setWeight(weight)
        self._setBiases(biases)

    def recognize(self,origin_input):
        input = tensorflow.placeholder(tensorflow.float32,[None,784])
        weight = self._getWeight()
        biases = self._getBiases()

        result = tensorflow.matmul(input,weight) + biases
        resultSof = tensorflow.nn.softmax(result,) # 把結果集使用softmax進行激勵
        resultSig = tensorflow.nn.sigmoid(resultSof,) # 把結果集以sigmoid函式進行激勵,用於後續分類
        output = self.session.run(resultSig,{input:origin_input})

        output = output[0]

        # 對識別結果進行分類處理
        output_tmp = []
        for item in output:
            if item < 0.6:
                output_tmp.append(0)
            else :
                output_tmp.append(1)

        return output_tmp

    def _getInput(self):
        inputs, y = self.mnist.train.next_batch(100);
        return [inputs[50]]
複製程式碼

以上是程式,整個程式基於TensorFlow來實現的,具體的TensorFlow安裝我就不說了。 整個訓練過程不做多說,我發現網上關於訓練的教程很多,但是訓練結果的教程很少。

整個程式裡,通過tensorflow.train.Saver()save進行訓練結果模型進行儲存,然後再用tensorflow.train.Saver()restore進行模型恢復然後取到訓練好的weight和baises。

這裡要注意的一個地方是因為一次性隨機取出100張手寫圖片進行批量訓練的,我在取的時候其實也是批量隨機取100張,但是我傳入識別的是一張,通過以下這段程式:

def _getInput(self):
        inputs, y = self.mnist.train.next_batch(100);
        return [inputs[50]]
複製程式碼

注意一下return這裡的資料結構,其實是取這批量的第50張,實際上這段程式寫成:

def _getInput(self):
        inputs, y = self.mnist.train.next_batch(1);
        return [inputs[0]]
複製程式碼

會更好。 因為識別的時候是需要用到訓練的隱藏層來進行的,所以在此我雖然識別的是一張圖片,但是我必須要傳入一個批量資料的這樣一個結構。

然後再識別的地方,我使用了兩個激勵函式:

resultSof = tensorflow.nn.softmax(result,) # 把結果集使用softmax進行激勵
resultSig = tensorflow.nn.sigmoid(resultSof,) # 把結果集以sigmoid函式進行激勵,用於後續分類
複製程式碼

這裡的話,第一個softmax激勵後的資料我發現得到的是以e為底的指數形式,轉換成普通的浮點數來看,不是很清楚到底是什麼,那麼我在做識別數字判斷的時候就不方便,所以再通過了一次sigmoid的激勵。

後續我通過一個迴圈判斷進行一次實際上的分類,這個原因首先要說到識別結果形式:

[0,0,0,0,0,0,0,0,1,0]
複製程式碼

像以上這個資料,表示的是8,也就是說,陣列下表第幾位為1就表示是幾,如0的表示:

[1,0,0,0,0,0,0,0,0,0]
複製程式碼

而sigmoid函式在這個地方其實就是對每個位置的資料進行了分類,我發現如果分類值小於0.52這樣的資料其實代表的是否,也就是說此位置的值對應的是0,大於0.52應該對應的是真,也就是1;而我在程式裡取的是0.6為界限做判斷。

實際上,這個界限值應該是在神經網路訓練的時候取的,而不是看識別結果來進行憑感覺取的(雖然訓練的時候的引數也是憑感覺取的)

這篇文章是我根據個人的一些理解來寫的,後續如果發現有錯誤,我會在新文章說出來,但這篇文章不做保留,方便後續檢查思考記錄的時候知道到底怎麼踩坑的。

以下是我上次寫的sigmoid函式的文章:

https://segmentfault.com/a/1190000012568846

關於其他激勵函式,可以網上找資料進行了解,很多基礎性的數學知識,放到一些比較具體的應用,會顯得非常的有意思。

相關文章