神经网络(network1版)

此版是copy的别人的代码,对其进行了注释理解,其中的公式推导可参考BP神经网络-从推导到实现_river_19的博客-CSDN博客

代码目录结构:

 其中mnist.pkl.gz文件的下载地址:https://academictorrents.com/browse.php?search=mnist.pkl.gz

""" mnist_loader.py文件代码 """
import pickle
import gzip
import numpy as np
def load_data():
    f = gzip.open('mnist.pkl.gz', 'rb')
    training_data, validation_data, test_data = pickle.load(f, encoding='bytes')
    f.close()
    return training_data, validation_data, test_data
def vectorized_result(j):
    e = np.zeros((10, 1))
    e[j] = 1.0
    return e
def load_data_wrapper():
    tr_data, va_data, te_d = load_data()
    # training data
    training_inputs = [np.reshape(x, (784, 1)) for x in tr_data[0]]
    training_results = [vectorized_result(y) for y in tr_data[1]]
    training_data = zip(training_inputs, training_results)
    # validation data
    validation_inputs = [np.reshape(x, (784, 1)) for x in va_data[0]]
    validation_results = [vectorized_result(y) for y in va_data[1]]
    validation_data = zip(validation_inputs, validation_results)
    # test_data
    test_inputs = [np.reshape(x, (784, 1)) for x in te_d[0]]
    test_data = zip(test_inputs, te_d[1])
    return(training_data, validation_data, test_data)


def get_Dataset():
    t, v, tt = load_data_wrapper()
    validation_data = list(v)
    # training_data = list(t) + validation_data
    training_data = list(t)
    testing_data = list(tt)

    # len_t = len(training_data)
    # len_tdi = len(training_data[0][0])
    # len_tl = len(training_data[0][1])
    # x_train = np.zeros((len_t, len_tdi))
    # y_train = np.zeros((len_t, len_tl))
    # for i in range(len_t):
    #     x_train[i] = np.array(training_data[i][0]).transpose()
    #     y_train[i] = np.array(training_data[i][1]).transpose()
    #
    # len_tt = len(testing_data)
    # x_test = np.zeros((len_tt, len_tdi))
    # y_test = np.zeros(len_tt)
    # for i in range(len_tt):
    #     x_test[i] = np.array(testing_data[i][0]).transpose()
    #     y_test[i] = testing_data[i][1]
    # return x_train, y_train, x_test, y_test
    return training_data, validation_data, testing_data




""" test.py文件代码 """
from BPNetwork_better import mnist_loader
from BPNetwork_better import  network1
from BPNetwork_better import  network2

training_data, validation_data, test_data = mnist_loader.load_data_wrapper()
training_data = list(training_data)  #training_data是一个长度为5万的列表,即training_data列表里有五万个长度为2的元组,每个元组是一个样本,且每个元组里有该样本的特征向量和标记向量

net = network1.Network([784, 30, 10])   #初始化每层(从下往上)的神经元个数,此句其实是new了一个对象
net.SGD(training_data, 30, 10, 3.0, test_data = test_data)  #30表示训练的总次数,10表示把样本分批后每批有10个样本,一份一份训练,3.0表示学习率



""" network1文件代码 """
import random

import numpy as np

def sigmoid(Z):
    return 1.0 / (1 + np.exp(-Z))  #此处是e的矩阵次方

#导数
def sigmoid_prime(Z):
    return sigmoid(Z) * (1 - sigmoid(Z))

class Network(object):
    #网络初始化
    def __init__(self,sizes):
        self.num_layers = len(sizes) #sizes列表的数据表示各层神经元个数(从下层到上层) 输入层784 隐层30 输出层10;len(sizes)表示此神经网络的总层数(3层)
        self.sizes = sizes
        self.biases = [np.random.randn(y,1) for y in sizes[1:]]    #返回一个y*1的随机矩阵  偏置b初始化[30*1, 10*1],因为b是一个数,所以是*1,因为第一层(输入层)没有偏置,所以下标从1开始
        self.weights = [np.random.randn(y, x) for x, y in zip(sizes[:-1], sizes[1:])]
        #权重w初始化[30*784, 10*30],因为第一层(输入层)没有权重,所以y下标从1开始,又因为每个神经元的权重w是个向量,因为输入层有784个属性值要输入到隐层,所以隐层的每个w是784维的;因为隐层有30个属性值要输入到输出层,所以输出层的w的是30维的   zip()函数用法:https://www.runoob.com/python/python-func-zip.html
        #上边的weights矩阵是已每个输入为行向量拼构成的矩阵,而下面的mini_batch一小批样本矩阵是由列向量(每个样本的属性是一个784*1的列向量)构成的,目的是方便做矩阵乘法运算


    #随机梯度下降算法
    def SGD(self, training_data, epochs, mini_batch_size, eta, test_data = None):  #epochs表示训练的总次数, mini_batch_size表示把样本分批后每批有mini_batch_size个样本,一份一份训练,eta表示学习率 #默认没有test_data,但输入也可以有test_data,此写法可提高代码的扩展性
        training_data = list(training_data) #五万个样本
        n = len(training_data)    #训练样本的总个数

        if test_data: #如果有测试样本
            test_data = list(test_data) #一万个测试样本
            n_test = len(test_data)

        for j in range(epochs):
            random.shuffle(training_data)  #shuffle() 方法将序列的所有元素随机排序。https://www.runoob.com/python/func-number-shuffle.html     此处是为了提高样本的随机性,已此增强泛化性
            mini_batches = [training_data[k: k + mini_batch_size] for k in range(0, n, mini_batch_size)]    #将五万个样本分成(50000/mini_batch_size)批,每批里有mini_batch_size个样本(mini_batches里有(50000/mini_batch_size)个列表),分别运行
            """上行代码等价于
            mini_batches = []
            for k in range(0, n, mini_batch_size):
                mini_batches.append(training_data[k: k + mini_batch_size])
            """
            lastsumE = 0  # 此次迭代所有样本的累计误差
            for mini_batch in mini_batches:    #mini_batch一小批(10个)样本构成的列表,其中每个样本都是一个元组(每个元组长度为2,第一部分为784*1的特征向量,第二部分为10*1的标记向量),即列表里包含10个元组
                sumE = self.updata_mini_batch(mini_batch, eta) #一批一批运行,每运行一批(updata_mini_batch函数执行一次),所有神经元的w和b全都更新一次
                lastsumE = lastsumE + sumE
            print("此次训练后所有样本的累计误差为:" + str(lastsumE))
            #测试
            if test_data:
                print("第{}次迭代 : 预测正确{}次 / 预测总次数为{}".format(j, self.evaluate(test_data), n_test))
            else:
                print("Epoch {} complete".format(j))


    #小批量梯度下降算法(先算出10个样本所得到的平均梯度,再更新一次所有神经元的w和b,而非每算一个样本所得到的梯度就更新一次所有神经元的w和b)
    def updata_mini_batch(self, mini_batch, eta):
        nabla_b = [np.zeros(b.shape) for b in self.biases]
        nabla_w = [np.zeros(w.shape) for w in self.weights]
        E = 0       #单个样本的误差
        sumE = 0    #一小批样本的累计误差
        for x, y in mini_batch:     #一个一个样本(一批总共10个样本)进行反向传播计算出每个样本对于所有神经元的w,b更新公式中的梯度,并累加起来  (x, y)是一个样本元组,x是特征向量(784*1),y是标记向量(10*1)
            delta_nabla_b, delta_nabla_w, E = self.backprop(x, y, E)
            sumE = sumE + E
            nabla_b = [nb +dnb for nb, dnb in zip(nabla_b,delta_nabla_b)]     #nabla_b是前一个样本的梯度,delta_nabla_b是当前样本的梯度。因为是小批量梯度下降算法,所以此处是梯度的累加,后面计算时还要 除 len(mini_batch)来得到平均梯度
            nabla_w = [nw + dnw for nw, dnw in zip(nabla_w, delta_nabla_w)]
        self.weights = [w - (eta / len(mini_batch)) * nw for w, nw in zip(self.weights, nabla_w)]   #更新偏置值(/len(mini_batch)*nw即平均梯度)
        self.biases = [b - (eta / len(mini_batch)) * nb for b, nb in zip(self.biases, nabla_b)]      #更新权值值
        #只不过更新w和b,是一批一批更新的(先算出10个样本所得到的平均梯度,再更新一次所有神经元的w和b,而非每算一个样本所得到的梯度就更新一次所有神经元的w和b)。因为如果每有一个样本都要更新一次所有神经元的偏置和权重,计算量太大,此处是先算出10个样本所得到的的平均梯度,用平均梯度更新一次所有神经元的w和b的
        return sumE  # 返回此批样本的累计误差

    #反向传播(从后向前层算出通过一个训练样本所得到的每个神经元的w和b的更新公式中的梯度)
    def backprop(self, x, y, E):
        nabla_b = [np.zeros(b.shape) for b in self.biases]  #zeros()函数用法:https://blog.csdn.net/lens___/article/details/83927880
        nabla_w = [np.zeros(w.shape) for w in self.weights] #每一个神经元w,b更新要用的梯度,初始化为0

        activation = x    #输出值(激活值)(输入层的激活值就是x,因为输入层前面没有其他层了)
        activations = [x]   #用列表一列列地存储每一层的输出值(激活值)
        zs = [] #用列表一列列地存储每一层(输入层除外)的输入值
        #算出每一层的输入值和输出值,下面求梯度要用
        for b,w in zip(self.biases, self.weights):  #循环次数=总层数-1
            z = np.dot(w, activation) + b   #w是30*784,activation是784*1,是输入层的输入值;所以z是30*1的,是隐层的30个接收值
            zs.append(z)
            activation = sigmoid(z)   #通过激活函数映射将隐层的输出值(激活值)(此神经网络选择的激活函数是sigmoid函数)
            activations.append(activation)
        E = sum(sum((activations[2] - y) * (activations[2] - y)))/2     #单个样本的误差(观察用)

        #计算最后一层(输出层)的单个样本的误差,用误差算梯度
        delta = self.cost_derivative(activations[-1], y) * sigmoid_prime(zs[-1])                                #流程2公式
        nabla_b[-1] = delta    #最后一层偏置更新公式里的梯度                                                         #流程4中b的公式
        nabla_w[-1] = np.dot(delta, activations[-2].transpose())  #最后一层权值更新公式里的梯度                       #流程4中w的公式
        #计算从倒数第二层至第二层的单个样本的误差,用误差算梯度
        for i in range(2, self.num_layers):
            z = zs[-i]
            sp = sigmoid_prime(z)
            delta = np.dot(self.weights[-i +1].transpose(), delta) * sp   #前一层的误差                           #流程3公式
            nabla_b[-i] = delta    #前一层偏置更新公式里的梯度                                                       #流程4中b的公式
            nabla_w[-i] = np.dot(delta, activations[-i - 1].transpose())    #前一层权值更新公式里的梯度              #流程4中w的公式

        return (nabla_b, nabla_w, E)    #返回每一个神经元w,b更新要用的梯度


    #估值函数
    def evaluate(self, test_data):
        test_results = [(np.argmax(self.feedforward(x)), y) for (x, y) in test_data]
        return sum(int(x == y) for (x, y) in test_results)


    # 前向运算(通过输入层输入的x,直接计算出输出层输出的值)
    def feedforward(self, a):
        for b, w in zip(self.biases,
                        self.weights):  # 从biases和weights中取出一一对应的b和w  zip()函数用法:https://www.runoob.com/python/python-func-zip.html
            a = sigmoid(np.dot(w, a) + b)
        return a


    def cost_derivative(self, output_activations, y):
        return (output_activations - y)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值