《Pyhon神经网络编程》手写数字识别示例代码和资料

72 阅读3分钟

前言

最近读完了《Pyhon神经网络编程》这本书,按照书中的代码实现了一个简单的3层神经网络。 这个神经网络只有3层,分别是输入层、隐藏层和输出层,不过这个简单的神经网络却可以执行非常复杂的任务,比如下面的手写数字识别任务。

神经网络是一个模拟神经元系统的软件架构,可以处理非常复杂的任务。底层的数学原理非常的复杂,不过从科普的角度去认识神经网络确实简单的,这本书就是如此,从最简单的线性回归开始逐步演进到神经网络,非常的通俗易懂,非常的令人印象深刻。

由于书中给出的数据文件链接已经失效,我从其他网站搜索到了训练和测试的文件,现在将它集合到一个仓库中供大家学习使用。

NeuralNetwork: python神经网络编程-相关资料

实现代码

import numpy.random
import scipy.special


class neuralNetwork:

    def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate):
        """输入层 隐藏层 输出层 学习率"""
        self.inodes = inputnodes
        self.hnodes = hiddennodes
        self.onodes = outputnodes
        self.lr = learningrate

        # 参数矩阵 输入-隐藏层 隐藏-输出层
        # 常规方法
        # rand 范围是 [0,1) 减小0.5 -> [-0.5,0.5)
        # self.wih = (numpy.random.rand(self.hnodes, self.inodes) - 0.5)
        # self.who = (numpy.random.rand(self.onodes, self.hnodes) - 0.5)
        # 复杂方法,计算链接数量的随机权重(正态分布 1/sqrt(传入连接数目)
        # 中心点 0.0 标准方差 pow(self.hnodes, -0.5) = self.hnodes^(-1/2)
        self.wih = numpy.random.normal(0.0, pow(self.hnodes, -0.5), (self.hnodes, self.inodes))
        self.who = numpy.random.normal(0.0, pow(self.onodes, -0.5), (self.onodes, self.hnodes))
        # 定义激活函数
        # expit 就是sigmoid函数
        self.activation_function = lambda x: scipy.special.expit(x)
        # 逆激活函数
        self.inverse_activation_function = lambda x: scipy.special.logit(x)

    def train(self, inputs_list, target_list):
        """训练 1、前向传播输入计算(和query类似) 2、反向误差传播计算"""
        inputs = numpy.array(inputs_list, ndmin=2).T
        targets = numpy.array(target_list, ndmin=2).T

        # 计算输出
        hidden_inputs = numpy.dot(self.wih, inputs)
        hidden_outputs = self.activation_function(hidden_inputs)
        final_inputs = numpy.dot(self.who, hidden_outputs)
        final_outputs = self.activation_function(final_inputs)

        # 计算误差
        output_errors = targets - final_outputs
        # 隐藏层的误差
        hidden_errors = numpy.dot(self.who.T, output_errors)

        # 更新权重
        # Wj,k =  learning_rate * Ek * sigmoid(Ok) * (1-sigmoid(Ok) . T(Oj)
        # 隐藏层-输出层的权重更新
        self.who += self.lr * numpy.dot((output_errors * final_outputs * (1.0 - final_outputs)),
                                        numpy.transpose(hidden_outputs))
        # 输入层-隐藏层的权重更新
        self.wih += self.lr * numpy.dot((hidden_errors * hidden_outputs * (1.0 - hidden_outputs)),
                                        numpy.transpose(inputs))

    def query(self, inputs_list):
        """
        接收输入,经过隐藏层、输出层后,返回输出
        :return:
        """
        # 转成2d数组
        inputs = numpy.array(inputs_list, ndmin=2).T
        # 计算去隐藏层的输入
        hidden_inputs = numpy.dot(self.wih, inputs)
        # 计算隐藏层的输出
        hidden_outputs = self.activation_function(hidden_inputs)
        # 计算去输出层的输入
        final_inputs = numpy.dot(self.who, hidden_outputs)
        # 计算输出层的输出
        final_outputs = self.activation_function(final_inputs)
        return final_outputs

    # 反向查询,即将概率数组输出作为输入反向生成原始图片的操作
    def backquery(self, targets_list):
        # transpose the targets list to a vertical array
        final_outputs = numpy.array(targets_list, ndmin=2).T

        # calculate the signal into the final output layer
        final_inputs = self.inverse_activation_function(final_outputs)

        # calculate the signal out of the hidden layer
        hidden_outputs = numpy.dot(self.who.T, final_inputs)
        # scale them back to 0.01 to .99
        hidden_outputs -= numpy.min(hidden_outputs)
        hidden_outputs /= numpy.max(hidden_outputs)
        hidden_outputs *= 0.98
        hidden_outputs += 0.01

        # calculate the signal into the hidden layer
        hidden_inputs = self.inverse_activation_function(hidden_outputs)

        # calculate the signal out of the input layer
        inputs = numpy.dot(self.wih.T, hidden_inputs)
        # scale them back to 0.01 to .99
        inputs -= numpy.min(inputs)
        inputs /= numpy.max(inputs)
        inputs *= 0.98
        inputs += 0.01

        return inputs


# print(n.wih, n.who)
# print(n.query([1.0, 0.5, -1.5]))


input_nodes = 784  # 28*28
hidden_nodes = 200
output_nodes = 10  # 10个数字的概率
learningrate = 0.2
n = neuralNetwork(input_nodes, hidden_nodes, output_nodes, learningrate)

training_data_file = open("mnist_train.csv", 'r')
training_data_list = training_data_file.readlines()
training_data_file.close()
# 训练
for record in training_data_list:
    all_values = record.strip().split(",")
    scaled_input = numpy.asarray(all_values[1:]).astype(numpy.float32)
    # 读取训练数据并处理,把数据范围归一化到(0,0.99] 避免0值
    inputs = scaled_input / 255.0 * 0.99 + 0.01
    # 目标输出的格式化,转成概率数组 (0,1)
    targets = numpy.zeros(output_nodes) + 0.01
    targets[int(all_values[0])] = 0.99
    n.train(inputs, targets)

# 测试
test_data_file = open("mnist_test.csv", 'r')
test_data_list = test_data_file.readlines()
test_data_file.close()

scorecard = []
for record in test_data_list:
    all_values = record.strip().split(",")
    scaled_input = numpy.asarray(all_values[1:]).astype(numpy.float32)
    inputs = scaled_input / 255.0 * 0.99 + 0.01
    output = n.query(inputs)
    # 找出数值最大的索引
    label = numpy.argmax(output)
    correct = int(all_values[0])
    if label == correct:
        scorecard.append(1)
    else:
        scorecard.append(0)
scorecard_array = numpy.asarray(scorecard)
print("score=", scorecard_array.sum() / scorecard_array.size)

# 反向查询示例代码
import matplotlib.pyplot
import matplotlib

label = 0
targets = numpy.zeros(output_nodes) + 0.01
targets[label] = 0.99
print(targets)
image_data = n.backquery(targets)

matplotlib.use('TkAgg')  # 设置后端
matplotlib.pyplot.imshow(image_data.reshape(28, 28), cmap='Greys', interpolation='None')
matplotlib.pyplot.show()