学习神经网络算法(python复现)

参考

  1. 学习神经网络算法时,发现了一份很好的帖子,重复进行了相关实验:https://juejin.im/post/6844903798704455694

环境搭建

  1. 安装virtualenv,进入虚拟环境进行相应配置;
  2. 安装 jupyter matplotlib numpy scipy;
#安装virtualenv
pip3 install virtualenv
#创建虚拟环境
virtualenv env
#windows下启动虚拟环境
env\Scripts\activate
#安装依赖
pip3 install --upgrade matplotlib jupyter numpy scipy
#启动jupyter
jupyter notebook

算法实现

搭建神经网络,简单勾勒出神经网络的样子,需要3个函数:

  1. 初始化函数-设定输入层,隐含层,输出层,随机生成权重
  2. 训练,学习相关样本,调整权重
  3. 查询,根据给定的权重,获取预测结果

code

# 引入依赖库
import numpy as np
import scipy.special
import matplotlib.pyplot
# 神经网络类定义
class neuralNetwork:
    def __init__(self,inputnodes,hiddennodes,outputnodes,learningrate):
        """初始化神经网络"""
        #设置输入层,隐藏层,输出层结点的数量
        self.inodes = inputnodes
        self.hnodes = hiddennodes
        self.onodes = outputnodes
        #连接权重,随机生成输入层到隐藏层和隐藏层到输出层的权重
        # rand()生成指定维度矩阵的0-1范围的随机数,减去0.5后权重值位于-0.5-0.5之间
        self.wih = np.random.rand(self.hnodes, self.inodes) - 0.5
        self.who = np.random.rand(self.onodes,self.hnodes) - 0.5
        #学习率
        self.lr = learningrate
        #将激活函数设置为sigmoid函数
        self.activation_function = lambda x: scipy.special.expit(x)
        pass
    
    def query(self,inputs_list):
        """查询神经网络"""
        #将输入的数组转化为1个二维数组,第二个输出参数表示维度(并转置)
        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(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)
        )      
        pass

# 进行训练
# 设置每一层的结点数量
input_nodes = 784
hidden_nodes = 100
output_nodes = 10

# 学习率
learning_rate = 0.1

# 创建神经网络模型
n = neuralNetwork(input_nodes, hidden_nodes, output_nodes, learning_rate)

#加载训练数据 1000个
training_data_file = open("./dataset/mnist_train_100.csv",'r')
training_data_lists = training_data_file.readlines()
training_data_file.close()

# 训练神经网络
# epochs 表示训练次数
epochs = 10
for e in range(epochs):
    # 遍历所有数据进行训练
    for record in training_data_lists:
        # 数据通过","分割,变成一个数组
        all_values = record.split(',')
        # 分离出图片的像素点到一个单独的数组
        inputs = (np.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01
        # 创建目标输出至(0-9出现的概率,默认全部是0.01)
        targets = np.zeros(output_nodes) + 0.01
        # all_values[0] 表示手写数字的真实值,将该数字的概率设为0.99
        targets[int(all_values[0])] = 0.99
        n.train(inputs,targets)
        pass
    pass

# 训练完毕
print('done')

#加载测试数据
test_data_file = open("./dataset/mnist_test_10.csv",'r')
test_data_list = test_data_file.readlines()
test_data_file.close()

# 测试神经网络
# 记录所有训练值,正确存1,错误存0
scorecard = []

# 遍历所有数据进行测试
for record in test_data_list:
    #数据通过','分割变成1个数组
    all_values = record.split(',')
    #第一个数字为标签(正确答案)
    correct_label = int(all_values[0])
    #取出测试的数据并进行归一化处理
    inputs = (np.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01
    # 查询神经网络
    outputs = n.query(inputs)
    # 取出输出最大值作为预测输出
    label = np.argmax(outputs)
    # 打印出真实值与查询值
    print('act: ', correct_label, 'pre: ',label)
    if(label == correct_label):
        #神经网络查询结果与真实值匹配,记录数组存入1
        scorecard.append(1)
    else:
        #神经网络查询结果与真实值不匹配,记录数组存入0
        scorecard.append(0)
        pass
    pass

#计算神经网络训练成功率
scorecard_array = np.asarray(scorecard)
print("performance = ", scorecard_array.sum() / scorecard_array.size)

思考

  1. 神经网络结点反向计算得到每个点的误差后,通过梯度下降法对权重进行更新,更新的公式参考西瓜书的章节如下,梯度下降法迭代公式的实现如下图:

  2. 计算神经网络隐层的反向误差时,使用权重矩阵的转置进行计算:

  3. 神经网络的权值矩阵初始化至0.5~-0.5之间;

  4. 神经网络输入的数据需要进行归一化处理,处理至合适范围;

  5. 输入结点x有h个,隐层结点n个,则输入层到隐藏的权重矩阵为 n × \times × h(n行h列) , 隐藏结点n个,输出层结点k个,则隐层到输出层的权重矩阵为k × \times ×h,从误差(实际值与输出预测值之差)反向计算隐藏误差时,误差左乘权重矩阵的转置即可获得相应的结果;

训练结果