python和numpy纯手写3层神经网络,干货满满

python和numpy纯手写3层神经网络,干货满满

前言

在上一篇转载的文章中,我们已经使用numpy手写了两层神经网络,尽管这篇文章只是增添了一层,但是,还是在实践中给我造成了不少的困难,主要的困难点在于感觉自己理解了BP反向传播,但是真正动手写的时候,还是迷迷糊糊的。所以,这里也给大家提个醒,不要眼高手低,尽量去实践自己学的东西。

在看这篇文章时,建议大家先把上一篇手写2层神经网络的文章看一下:

(转载)百度架构师带你使用numpy手动构建神经网络

网络结构

网络图形

共有三层神经网络结构

  • 第一层 输入层,有13个神经元,所需参数是13*13+13=182个
  • 第二层 隐藏层,有13个神经元,所需参数是13+1=14个
  • 第三层 输出层,有1个神经元 即为输出结果

知识讲解

偏导数

偏导数这个概念咱们在本科的教程中已经学过了,就是这个函数沿该坐标轴正方向的变化率。这个有什么用呢,我们先来看个图:

img

从图中,我们可以很容易的去找到该图像的最低点,也就是图中大概T点的位置。但是,假如你不知道呢,如何去慢慢的找到这个最低点呢?咱们先来看P0点的切线斜率,也等于该点的导数值,设为α。因为这个切线与x轴的角度小于90°,所以p0点的倒数值大于0.我们有了这个讯息,相当于知道了方向,于是就有了这个公式:x = x-axα a 为步长。由于α>0,所以x的值在慢慢减小,逐渐靠近T。当x在T点时,该点的切线为水平线,即导数为0,所以此时x=x-ax0,x值不再变化,此时x为该函数的最小值的取值点。

上面说的都是x,y二维坐标系,扩展为多维坐标系时,导数的概念就变成了偏导数,但是原理都是一样的,朝着最低点的方向前进。然后上述过程在多维时就变成了梯度下降法

梯度下降法

这里我们举一个上篇文章中的例子。一个想从山峰走向山底的盲人,他看不见坡谷在哪(无法逆向求解出$Loss&导数为0时的参数值),但可以伸脚探索身边的坡度(当前点的导数值,也称为梯度)。那么,求解Loss函数最小值可以“从当前的参数取值,一步步的按照下坡的方向下降,直到走到最低点”实现。

具体计算

下面我们使用比较简单的三层神经网络模型来做计算:

  • 我们先进行前向传播,这里我们没有使用激活函数:

在这里插入图片描述

  • 我们先对隐藏层进行反向传播:

在这里插入图片描述

  • 紧接着对输入层进行反向传播:

在这里插入图片描述

从上面这几张图的计算过程,我们可以得出一个结论:某一神经元偏导数的值(也有地方叫残差)=之后与它相连线的权值✖该神经元本身的值✖(预测值-准确值)

代码过程

`

import numpy as np
import matplotlib.pyplot as plt
'''
项目介绍:
    此项目是使用numpy搭建神经网络模型,预测波士顿房价。
数据介绍:
    每一条房屋数据有14个值,前13个值是房屋属性,比如面积,长度等,最后一个是房屋价格,共506条数据。
模型介绍:
    在combat1.py文件二层神经网络模型的基础上,此文件将搭建三层神经网络,其中包含输入层(13)、隐藏层(13)、输出层(1)
'''
#数据导入以及处理
def deal_data():
    #读取文件数据,此时数据形状是(7084,),即所有数据在一行中
    housingdata = np.fromfile('data/housing.data',sep=' ')

    #修改数据格式,将每一条房屋数据放在一行中。
    housingdata = np.array(housingdata).reshape((-1,14))#此时数据形状为(506,14)

    #对数据的前13个属性进行归一化操作,有助于提高模型精准度,这里使用max-min归一化方式。公式为(x-min)/(max-min)
    for i in range(13):
        Max =  np.max(housingdata[:,i])
        Min = np.min(housingdata[:,i])
        housingdata[:,i]=(housingdata[:,i]-Min)/(Max-Min)

    #依据2-8原则,80%的数据作为训练数据,20%数据作为测试数据;此时训练数据是405条,测试数据是101条
    Splitdata = round(len(housingdata)*0.8)
    Train = housingdata[:Splitdata]#训练数据集
    Test = housingdata[Splitdata:]#测试数据集
    return Train,Test

#模型设计以及配置
#首先确定有13个权值参数w,并随机初始化
class Model_Config(object):
    def __init__(self,firstnetnum,secondnetnum):
        np.random.seed(1)
        self.w0 = np.random.randn(firstnetnum*secondnetnum,1).reshape(firstnetnum,secondnetnum)
        self.w1 = np.random.randn(secondnetnum,1)
        self.b0 = np.random.randn(firstnetnum,1).reshape(1,firstnetnum)
        self.b1 = np.random.randn(1,1)
     #计算预测值
    def forward(self,x):
        hidden1 = np.dot(x,self.w0)+self.b0
        y = np.dot(hidden1,self.w1)+self.b1
        return hidden1,y
    #设置损失函数,这里使用差平方损失函数计算方式
    def loss(self,z,y):
        error = z-y
        cost = error*error
        avg_cost = np.mean(cost)
        return avg_cost
    #计算梯度
    def back(self,x,y):
        hidden1,z = self.forward(x)
        #hidden层的梯度
        gradient_w1 = (z-y)*hidden1
        gradient_w1 = np.mean(gradient_w1,axis=0)#这里注意,axis=0必须写上,否则默认将这个数组变成一维的求平均
        gradient_w1 = gradient_w1[:,np.newaxis]#
        gradient_b1 = (z-y)
        gradient_b1 = np.mean(gradient_b1)
        gradient_w0 = np.zeros(shape=(13,13))
        for i in range(len(x)):
            data = x[i,:]
            data = data[:,np.newaxis]
            # print("data.shape",data.shape)
            w1 = self.w1.reshape(1,13)
            # print("self.w1.shape",w1.shape)
            gradient_w01 = (z-y)[i]*np.dot(data,w1)
            # print("gradient_w01.shape:",gradient_w01.shape)
            gradient_w0+=gradient_w01
        gradient_w0 = gradient_w0/len(x)
        w2 = self.w1.reshape(1,13)
        gradient_b0 =np.mean((z-y)*w2,axis=0)

        return gradient_w1,gradient_b1,gradient_w0,gradient_b0
        #输入层的梯度
        #(z-y)x*self.w1
        # gradient_w0 = np.zeros(shape=(13,13))
        # gradient_w01 = gradient_w1.reshape(1,13)
        # for i in range(13):
        #     data = x[:,i]
        #     data = data[:,np.newaxis]
        #     gradient = data*gradient_w01
        #     gradient = np.mean(gradient,axis=0)
        #     gradient_w0[i,:] = gradient
        # gradient_b0 = gradient_b1*self.b0
        # return gradient_w1,gradient_b1,gradient_w0,gradient_b0

    #使用梯度更新权值参数w
    def update(self,gradient_w1,gradient_b1,gradient_w0,gradient_b0,learning_rate):
        self.w1 = self.w1-learning_rate*gradient_w1
        self.b1 = self.b1-learning_rate*gradient_b1
        self.w0 = self.w0-learning_rate*gradient_w0
        self.b0 = self.b0-learning_rate*gradient_b0

    #开始训练
    def train(self,epoch_num,x,y,learning_rate):
        #循环迭代
        losses=[]
        for i in range(epoch_num):
            _,z = self.forward(x)
            avg_loss = self.loss(z,y)
            gradient_w1,gradient_b1,gradient_w0,gradient_b0 = self.back(x,y)
            self.update(gradient_w1,gradient_b1,gradient_w0,gradient_b0,learning_rate)
            losses.append(avg_loss)
            #每进行20此迭代,显示一下当前的损失值
            if(i%20==0):
                print("iter:{},loss:{}".format(i,avg_loss))

        return losses
def showpeocess(loss,epoch_num):
    plt.title("The Process Of Train")
    plt.plot([i for i in range(epoch_num)],loss)
    plt.xlabel("epoch_num")
    plt.ylabel("loss")
    plt.show()
if __name__ == '__main__':
    Train,Test = deal_data()
    np.random.shuffle(Train)
    #只获取前13个属性的数据
    x = Train[:,:-1]
    y = Train[:,-1:]
    epoch_num = 1000#设置迭代次数
    Model = Model_Config(13,13)
    losses = Model.train(epoch_num=epoch_num,x=x,y=y,learning_rate=0.001)
    showpeocess(loss=losses,epoch_num=epoch_num)

`

闲话

我大概花了一个下午的时间才终于写出来三层的神经网络结构,中间这个反向传播我一直没弄清楚。大家看完理论之后,记住一定要进行实战,手动写一下。

我把这个项目已经上传到GitHub上,大家可以去下载相关数据文件和项目文件。

项目地址

博主目前正在GitHub上创建针对机器学习和人工智能新手入门项目集合,目的是让初学者延续学习热情,如果你有简单上手的项目,可以博客下面留言,也欢迎各位大佬去GitHub点个⭐,支持一下!

[ Be-Friendly-To-New-People]

发布了87 篇原创文章 · 获赞 76 · 访问量 24万+

猜你喜欢

转载自blog.csdn.net/lzx159951/article/details/105099166