手動搭建神經網絡應用於手寫數字識別

import numpy as np
import scipy.special
import matplotlib.pyplot as plt
from PIL import Image

class neuralNetwork :

    # 用於神經網絡初始化
    def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate):
        # 輸入層節點數
        self.inodes = inputnodes
        # 隱層節點數
        self.hnodes = hiddennodes
        # 輸出層節點數
        self.onodes = outputnodes
        # 學習率
        self.lr = learningrate

        # 初始化輸入層與隱層之間的權重
        self.wih = np.random.normal(0.0, pow(self.hnodes, -0.5), (self.hnodes, self.inodes))
        # 初始化隱層與輸出層之間的權重
        self.who = np.random.normal(0.0, pow(self.onodes, -0.5), (self.onodes, self.hnodes))

        # 激活函數(S函數)
        self.activation_function = lambda x: scipy.special.expit(x)
        # 逆激活函數
        self.iactivation_function = lambda y: scipy.special.logit(y)

    # 設置權重
    def setweights(self, wih, who):
        self.wih = wih
        self.who = who

    # 神經網絡學習訓練
    def train(self, inputs_list, targets_list):
        # 將輸入數據轉化成二維矩陣
        inputs = np.array(inputs_list, ndmin=2).T
        # 將輸入標籤轉化成二維矩陣
        targets = np.array(targets_list, ndmin=2).T

        # 計算隱層的輸入
        hidden_inputs = np.dot(self.wih, inputs)
        # 計算隱層的輸出
        hidden_outputs = self.activation_function(hidden_inputs)

        # 計算輸出層的輸入
        final_inputs = np.dot(self.who, hidden_outputs)
        # 計算輸出層的輸出
        final_outputs = self.activation_function(final_inputs)

        # 計算輸出層誤差
        output_errors = targets - final_outputs
        # 計算隱層誤差
        hidden_errors = np.dot(self.who.T, output_errors)

        # 更新隱層與輸出層之間的權重
        self.who += self.lr * np.dot((output_errors * final_outputs * (1.0 - final_outputs)), np.transpose(hidden_outputs))
        # 更新隱層與輸出層之間的權重
        self.wih += self.lr * np.dot((hidden_errors * hidden_outputs * (1.0 - hidden_outputs)), np.transpose(inputs))

    # 根據標籤逆向生成圖片
    def makeinput(self, outputs_list):
        # 將標記數據(輸出層的輸出)轉化成二維矩陣
        final_outputs = np.array(outputs_list, ndmin=2).T
        # 計算輸出層的輸入
        final_inputs = self.iactivation_function(final_outputs)

        # 計算隱層的輸出
        hidden_outputs = np.dot(self.who.T, final_inputs)
        # 處理下限使得下限爲0
        hidden_outputs -= np.min(hidden_outputs)
        # 處理上限使得上限爲1
        hidden_outputs /= np.max(hidden_outputs)
        hidden_outputs *= 0.98
        hidden_outputs += 0.01
        # 計算隱層的輸入
        hidden_inputs = self.iactivation_function(hidden_outputs)
        # 計算輸入層的數據
        inputs = np.dot(self.wih.T, hidden_inputs)
        inputs -= np.min(inputs)
        inputs /= np.max(inputs)
        inputs *= 255
        print(inputs)
        return inputs


    # 神經網絡測試
    def test(self, inputs_list):
        # 將輸入數據轉化成二維矩陣
        inputs = np.array(inputs_list, ndmin=2).T

        # 計算隱層的輸入
        hidden_inputs = np.dot(self.wih, inputs)
        # 計算隱層的輸出
        hidden_outputs = self.activation_function(hidden_inputs)

        # 計算輸出層的輸入
        final_inputs = np.dot(self.who, hidden_outputs)
        # 計算輸出層的輸出
        final_outputs = self.activation_function(final_inputs)

        return final_outputs

# 訓練神經網絡
def train_neuralnetwork():
    # 初始化 784(28 * 28)個輸入節點,100個隱層節點,10個輸出節點(0~9)
    input_nodes = 784
    hidden_nodes = 200
    output_nodes = 10

    # 學習率0.3
    learning_rate = 0.1
    # 訓練次數
    epochs = 5
    # 初始化神經網絡實例
    n = neuralNetwork(input_nodes, hidden_nodes, output_nodes, learning_rate)

    # 讀取訓練集
    training_data_file = open('mnist_dataset/mnist_train.csv', 'r')
    training_data_list = training_data_file.readlines()
    training_data_file.close()

    # 訓練數據
    for e in range(epochs):
        for record in training_data_list:
            all_values = record.split(',')
            # 輸入數據範圍(0.01~1)
            inputs = np.asfarray(all_values[1:]) / 255.0 * 0.99 + 0.01
            # 標記數據(相應標記爲0.99,其餘0.01)
            targets = np.zeros(output_nodes) + 0.01
            targets[int(all_values[0])] = 0.99
            n.train(inputs, targets)

    np.savetxt('weights/who_60000.csv', n.who, delimiter=',')
    np.savetxt('weights/wih_60000.csv', n.wih, delimiter=',')

# 驗證性能
def pre_acc():

    # 初始化 784(28 * 28)個輸入節點,100個隱層節點,10個輸出節點(0~9)
    input_nodes = 784
    hidden_nodes = 200
    output_nodes = 10

    # 學習率0.1
    learning_rate = 0.1

    # 初始化神經網絡實例
    n = neuralNetwork(input_nodes, hidden_nodes, output_nodes, learning_rate)

    # 讀取測試數據
    test_data_file = open('mnist_dataset/mnist_test.csv', 'r')
    test_data_list = test_data_file.readlines()
    test_data_file.close()

    # 性能分析(正確率)
    wih = np.loadtxt(open('weights/wih_60000.csv'), delimiter=",", skiprows=0)
    who = np.loadtxt(open('weights/who_60000.csv'), delimiter=",", skiprows=0)
    n.setweights(wih, who)
    scorecard = []
    for record in test_data_list:
        # 讀取測試集中的每個數據,進行預測
        test_data = record.split(',')
        correct_label = int(test_data[0])
        outputs = n.test(np.asfarray(test_data[1:]) / 255.0 * 0.99 + 0.01)
        # 找到每次預測結果中最大值的索引(即,預測的標籤)
        pre_label = np.argmax(outputs)
        # 如果預測正確,則得1分,否則得0分
        if pre_label == correct_label:
            scorecard.append(1)
        else:
            scorecard.append(0)
    print(scorecard)
    # 計算正確率
    scorecard_array = np.asarray(scorecard)
    accuracy = scorecard_array.sum() / scorecard_array.size
    print(accuracy)

# 單個預測
def pre_one():
    # 初始化 784(28 * 28)個輸入節點,100個隱層節點,10個輸出節點(0~9)
    input_nodes = 784
    hidden_nodes = 200
    output_nodes = 10

    # 學習率0.1
    learning_rate = 0.1
    # 初始化神經網絡實例
    n = neuralNetwork(input_nodes, hidden_nodes, output_nodes, learning_rate)

    # 讀取測試數據
    test_data_file = open('mnist_dataset/mnist_test_10.csv', 'r')
    test_data_list = test_data_file.readlines()
    test_data_file.close()

    # 打印測試數據標籤
    test_data = test_data_list[0].split(',')
    print('原標籤:', test_data[0])

    # 生成標籤圖片
    image_array = np.asfarray(test_data[1:]).reshape(28, 28)
    plt.imshow(image_array, cmap='Greys', interpolation='None')
    plt.show()

    # 利用神經網絡預測
    wih = np.loadtxt(open('weights/wih_60000.csv'), delimiter=",", skiprows=0)
    who = np.loadtxt(open('weights/who_60000.csv'), delimiter=",", skiprows=0)
    n.setweights(wih, who)
    results = n.test(np.asfarray(test_data[1:]) / 255.0 * 0.99 + 0.01)
    pre_label = np.argmax(results)
    print('預測結果:', pre_label)
    print(results)

# 根據標籤逆向生成圖片
def pre_imakepic():
    # 初始化 784(28 * 28)個輸入節點,100個隱層節點,10個輸出節點(0~9)
    input_nodes = 784
    hidden_nodes = 200
    output_nodes = 10

    # 學習率0.1
    learning_rate = 0.1
    # 初始化神經網絡實例
    n = neuralNetwork(input_nodes, hidden_nodes, output_nodes, learning_rate)

    # 要製作的標籤
    want_label = 0
    # 生成相應的輸出層的輸出數據
    label_data = np.zeros(output_nodes) + 0.01
    label_data[int(want_label)] = 0.99
    wih = np.loadtxt(open('weights/wih_60000.csv'), delimiter=",", skiprows=0)
    who = np.loadtxt(open('weights/who_60000.csv'), delimiter=",", skiprows=0)
    n.setweights(wih, who)
    res_inputs = n.makeinput(label_data)

    # 生成標籤圖片
    image_array = np.asfarray(res_inputs).reshape(28, 28)
    plt.imshow(image_array, cmap='Greys', interpolation='None')
    plt.show()

# 手寫照片識別
def pre_pic():
    # 初始化 784(28 * 28)個輸入節點,100個隱層節點,10個輸出節點(0~9)
    input_nodes = 784
    hidden_nodes = 200
    output_nodes = 10

    # 學習率0.1
    learning_rate = 0.1
    # 初始化神經網絡實例
    n = neuralNetwork(input_nodes, hidden_nodes, output_nodes, learning_rate)

    # 讀取帶預測圖片
    img = Image.open('picture/2.jpg').convert('L')
    newimg = img.resize((28, 28), Image.ADAPTIVE)
    # newimg.save('picture/2_resize.jpg')
    test_pic = np.array(newimg)

    # 利用神經網絡預測
    wih = np.loadtxt(open('weights/wih_60000.csv'), delimiter=",", skiprows=0)
    who = np.loadtxt(open('weights/who_60000.csv'), delimiter=",", skiprows=0)
    n.setweights(wih, who)
    # 正常255表示白色,但mnist數據集255表示黑色,所以現實圖片顏色應該翻轉一下
    results = n.test(np.asfarray(255.0 - test_pic.flatten()) / 255.0 * 0.99 + 0.01)
    pre_label = np.argmax(results)
    print('預測結果:', pre_label)
    print(results)

if __name__ == "__main__":
    # train_neuralnetwork()
    pre_pic()
    # pre_imakepic()
    # pre_one()
    # pre_acc()

注:代碼是對《Python神經網絡編程》中代碼的擴展。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章