轻轻松松搞懂神经网络

定义

神经网络并不是一个很复杂的东西,在最简单的神经网络里面只是涉及到简单的广义线性回归以及梯度下降。

应用:神经网络的应用非常广泛最简单的就是数据预测,也应为可以进行数据预测,并且由于神经网络的运算拓扑结构还可以用于分类。举个例子就是,如果我们对一个图片进行特征处理,之后对抽取的特征进行预测分类,那么一来就可以实现简单的图片识别等等(当然这里涉及到卷积神经网络等等)

本博文讲述最简单的神经网络算法,并手动实现一个神经网络(基于Python)

基本原理

基本过渡

其实用最简单的过程描述神经网络的工作原理其实“暴力求解”

举个简单的例子(用梯度下降算法演示一元线性回归) 假设我们有 一个数集合

x = [1 2 3 4 5]

输出集合

y = [1 2 3 4 5]

我们假设有一个关系 y = w*x +b

现在们需要做的就是知道 w 和 b 的值。

最简单的做法你肯定知道,那就是 先随机生成 w b 的值,然后我们通过误差来修改 w b 对应的值。那么在这里为了计算误差我们这里需要一个方式来计算误差的值并且修改对应的 w b 的值,例如我们通过 方差 来计算 D(y实际,y 预测) 。

假设此时 x = 1 初始 w= 2 , b = 1 那么计算之后 (当然实际上我们使用的是x的平均值和y的平均值)

y预测=3 方差 = 4

此时我们可以套用梯度下降算法

​ 这个方差叫做损失函数 loss = (w*x + b - y)^2

​ 分别对 w , b 求偏导,之后我们得到

​ w` = w - w偏导值x步长

​ b` = b - b偏导值x步长

​ 之后进入循环我们最终得到了 w = 1 b = 0

从整个过程来看我似乎只是介绍了如何使用梯度下降,但是我们如果使用一个流程图的话你会看到这玩意

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SfmUJKGj-1637168974515)(C:\Users\31395\AppData\Roaming\Typora\typora-user-images\image-20211118000825120.png)]

其实这个就相当于了一个“神经网络”只是中间只有一个节点(隐含层)

基本神经网络结构

到了这一步我们可以引入神经网络的概念,前面我们发现我们计算 w b 的时候是有一个随机初始值(当然一般也可以默认从0开始)通过一个节点,那么问题来了对于复杂的拟合,预测,初始的 w b 值不同导致的结果可能也不同,而且一个节点似乎不够严谨 于是,参考生物界(其实我更想说是三个臭皮匠顶过一个诸葛亮)我们可以划分多个节点,之后把x的值按照权重拆开然后丢到节点里面然后运算,之后再按照一定的权重去组合回来,最后我们得到了每个节点的 w b 值最后我们又组合在了一起。

那么复杂了一下就变成了这样:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WuUA46CU-1637168974517)(C:\Users\31395\AppData\Roaming\Typora\typora-user-images\image-20211118001919079.png)]

然后发现这样好像还不够于是你见到了很多网上唬人的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-biM3zIzs-1637168974518)(C:\Users\31395\AppData\Roaming\Typora\typora-user-images\image-20211118002011197.png)]

或者

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dq8gQ09b-1637168974521)(C:\Users\31395\AppData\Roaming\Typora\typora-user-images\image-20211118002027255.png)]

不过虽然是这样的但是总体的原理和前面是类似的。

动手实现

这里的话我没有选择自己重新写一个,而是直接用了别人实现的一个简单的Dome。毕竟自己手写的玩意还是不如现成的框架,有很多需要考虑的细节,但是还是那句话基本原理类似。

目标

我们这一次要模拟的神经网络就是

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cC7tQYPh-1637168974523)(C:\Users\31395\AppData\Roaming\Typora\typora-user-images\image-20211118001919079.png)]

通过前面的例子也应该明白了,我们第一件事情那就是选择一个合适的(或者说是猜测方程)拟合方程,作为隐藏层。

那么在这里叫做 激活函数

我们选择最常用的

def sigmoid (x):
    return 1/(1+numpy.exp(-x))

这个也是我数学建模的时候直接用的一个激活函数(当然我当时是用MATLAB工具箱做的)

使用激活函数

现在我们选择了一个激活函数,那么接下来就是前面说的按照权重划分X的值,然后带入激活函数运算,之后按照权重输出。当然这个权重我们是不知道的,我们的训练其实也是确定这个权重,确定每一层的权重,最后我们是可以导出一个很大复杂的方程的。(没错我数学建模的时候也是这样干的,因为不会多目标优化,所以直接通过神经网络拟合出每一个目标之间的权重关系,得到很复杂的方程然后跑遗传。参考:http://www.nnetinfo.com/text/show/4)

这里的代码按照那个图片就是这样的

def feedforward(self,x):
        h1 = x[0]*self.w1+x[1]*self.w2+self.b1
        h1f = sigmoid(h1)
        h2 = x[0]*self.w3+x[1]*self.w4+self.b2
        h2f = sigmoid(h2)
        o1 = h1f*self.w5+h2f*self.w6+self.b3
        of = sigmoid(o1)

然后我们初始化就是随机给个值先,然后后面训练

class nerualnetwo():
    def __init__(self):
       self.w1 = numpy.random.normal()
       self.w2 = numpy.random.normal()
       self.w3 = numpy.random.normal()
       self.w4 = numpy.random.normal()
       self.w5 = numpy.random.normal()
       self.w6 = numpy.random.normal()
       self.b1 = numpy.random.normal()
       self.b2 = numpy.random.normal()
       self.b3 = numpy.random.normal()

损失函数

这个就是对比前面的例子,我怎么知道这个东西 w b 是好的呢,所以需要修正,这里用的还是 方差

def mse_loss(y_tr,y_pre):
    return((y_tr - y_pre)**2).mean()

反馈优化(基于梯度下降)

其实这个优化每一个节点的权重不一定要用梯度下降,只是这个是实现起来最简单的,也是最好理解的。(也是我能写的)

这里用梯度下降就要用到求导了。但是这里有点区别

我们有两个层面的梯度下降:

输入到隐含层

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bmmSnilC-1637168974524)(C:\Users\31395\AppData\Roaming\Typora\typora-user-images\image-20211118003938664.png)]

隐含层到输出

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lqomyGa0-1637168974525)(C:\Users\31395\AppData\Roaming\Typora\typora-user-images\image-20211118003949609.png)]

所以这块要求导的有两个函数

def der_sigmoid(x):
    return sigmoid(x)*(1-sigmoid(x))


der_L_y_pre = -2*(y_tr-y_pre)

这里注意 -2*(y_tr-y_pre) 其实是 y_pre 也就是y预测求偏导

由于有两层整个 梯度下降串联起来就是 这样

self.w1 -= learn_rate * der_L_y_pre * der_y_pre_h1 * der_h1_w1

self.b1 -= learn_rate * der_L_y_pre * der_y_pre_h1 * der_h1_b1

其中:der_h1_b1 = der_sigmoid(valcell[0])

​ *der_h1_w1 = der_sigmoid(valcell[0])x[0]

也就是分别求的偏导 对 w b

至于 valcell 是 h1,h1f,h2,h2f,o1,of

要带入嘛

梯度修正

这个就好说,其实总体就是步入循环嘛,不过这里是直接算了 1000 次

下面也是整个运算的核心

def train(self,data,all_y_tr):
        epochs = 1000
        learn_rate = 0.1
        for i in range(epochs):
            for x , y_tr in zip(data,all_y_tr):
                valcell = self.feedforward(x)
                y_pre = valcell[5]
                der_L_y_pre = -2*(y_tr-y_pre)
                der_y_pre_h1 = der_sigmoid(valcell[4])*self.w5
                der_y_pre_h2 = der_sigmoid(valcell[4])*self.w6
                der_h1_w1 = der_sigmoid(valcell[0])*x[0]
                der_h1_w2 = der_sigmoid(valcell[0])*x[1]
                der_h2_w3 = der_sigmoid(valcell[2])*x[0]
                der_h2_w4 = der_sigmoid(valcell[2])*x[1]
                der_y_pre_w5 = der_sigmoid(valcell[4])*valcell[1]
                der_y_pre_w6 = der_sigmoid(valcell[4])*valcell[3]
                der_y_pre_b3 = der_sigmoid(valcell[4])
                der_h1_b1 = der_sigmoid(valcell[0])
                der_h2_b2 = der_sigmoid(valcell[2])
#重新赋予权值和偏置
                self.w1 -= learn_rate * der_L_y_pre * der_y_pre_h1 * der_h1_w1
                self.w2 -= learn_rate * der_L_y_pre * der_y_pre_h1 * der_h1_w2
                self.w3 -= learn_rate * der_L_y_pre * der_y_pre_h2 * der_h2_w3
                self.w4 -= learn_rate * der_L_y_pre * der_y_pre_h2 * der_h2_w4
                self.w5 -= learn_rate * der_L_y_pre * der_y_pre_w5
                self.w6 -= learn_rate * der_L_y_pre * der_y_pre_w6
                self.b1 -= learn_rate * der_L_y_pre * der_y_pre_h1 * der_h1_b1
                self.b2 -= learn_rate * der_L_y_pre * der_y_pre_h2 * der_h2_b2
                self.b3 -= learn_rate * der_L_y_pre *der_y_pre_b3
              #每10步输出一次当前损失值
                if i % 10 ==0 :
                    y_pred = numpy.apply_along_axis(self.simulate,1,data)
                    loss = mse_loss (all_y_tr , y_pred)
                    print(i,loss)

此时我们训练好了之后就知道了每一层的权重,就可以参与运算了

运算函数

def simulate (self,x):
        h1 = x[0]*self.w1+x[1]*self.w2+self.b1
        h1f = sigmoid(h1)
        h2 = x[0]*self.w3+x[1]*self.w4+self.b2
        h2f = sigmoid(h2)
        o1 = h1f*self.w5+h2f*self.w6+self.b3
        of = sigmoid(o1)

这个其实就是运算模型,把模型训练好了权值就知道了,带入方程就好了。

总体代码

import numpy

def sigmoid (x):
    return 1/(1+numpy.exp(-x))

def der_sigmoid(x):
    return sigmoid(x)*(1-sigmoid(x))

def mse_loss(y_tr,y_pre):
    return((y_tr - y_pre)**2).mean()


class nerualnetwo():
    def __init__(self):
       self.w1 = numpy.random.normal()
       self.w2 = numpy.random.normal()
       self.w3 = numpy.random.normal()
       self.w4 = numpy.random.normal()
       self.w5 = numpy.random.normal()
       self.w6 = numpy.random.normal()
       self.b1 = numpy.random.normal()
       self.b2 = numpy.random.normal()
       self.b3 = numpy.random.normal()
    def feedforward(self,x):
        h1 = x[0]*self.w1+x[1]*self.w2+self.b1
        h1f = sigmoid(h1)
        h2 = x[0]*self.w3+x[1]*self.w4+self.b2
        h2f = sigmoid(h2)
        o1 = h1f*self.w5+h2f*self.w6+self.b3
        of = sigmoid(o1)
        return h1,h1f,h2,h2f,o1,of
    def simulate (self,x):
        h1 = x[0]*self.w1+x[1]*self.w2+self.b1
        h1f = sigmoid(h1)
        h2 = x[0]*self.w3+x[1]*self.w4+self.b2
        h2f = sigmoid(h2)
        o1 = h1f*self.w5+h2f*self.w6+self.b3
        of = sigmoid(o1)
        return of
    def train(self,data,all_y_tr):
        epochs = 1000
        learn_rate = 0.1
        for i in range(epochs):
            for x , y_tr in zip(data,all_y_tr):
                valcell = self.feedforward(x)
                y_pre = valcell[5]
                der_L_y_pre = -2*(y_tr-y_pre)
                der_y_pre_h1 = der_sigmoid(valcell[4])*self.w5
                der_y_pre_h2 = der_sigmoid(valcell[4])*self.w6
                der_h1_w1 = der_sigmoid(valcell[0])*x[0]
                der_h1_w2 = der_sigmoid(valcell[0])*x[1]
                der_h2_w3 = der_sigmoid(valcell[2])*x[0]
                der_h2_w4 = der_sigmoid(valcell[2])*x[1]
                der_y_pre_w5 = der_sigmoid(valcell[4])*valcell[1]
                der_y_pre_w6 = der_sigmoid(valcell[4])*valcell[3]
                der_y_pre_b3 = der_sigmoid(valcell[4])
                der_h1_b1 = der_sigmoid(valcell[0])
                der_h2_b2 = der_sigmoid(valcell[2])

                self.w1 -= learn_rate * der_L_y_pre * der_y_pre_h1 * der_h1_w1
                self.w2 -= learn_rate * der_L_y_pre * der_y_pre_h1 * der_h1_w2
                self.w3 -= learn_rate * der_L_y_pre * der_y_pre_h2 * der_h2_w3
                self.w4 -= learn_rate * der_L_y_pre * der_y_pre_h2 * der_h2_w4
                self.w5 -= learn_rate * der_L_y_pre * der_y_pre_w5
                self.w6 -= learn_rate * der_L_y_pre * der_y_pre_w6
                self.b1 -= learn_rate * der_L_y_pre * der_y_pre_h1 * der_h1_b1
                self.b2 -= learn_rate * der_L_y_pre * der_y_pre_h2 * der_h2_b2
                self.b3 -= learn_rate * der_L_y_pre *der_y_pre_b3
                if i % 10 ==0 :
                    y_pred = numpy.apply_along_axis(self.simulate,1,data)
                    loss = mse_loss (all_y_tr , y_pred)
                    print(i,loss)

                    
if __name__ == "__main__":
    data = numpy.array([[-2, -1],[25, 6],[17, 4],[-15, -6]])
    all_y_trues = numpy.array([1,0,0,1])
    ner = nerualnetwo()

    ner.train(data,all_y_trues)

总结

其实到这里最基本的神经网路就是这个样子,最核心的就是两个玩意当然这里面还有很多细节。

激活函数

自动修正权值

选择合适的激活函数,激活,然后使用自我修正的方式让损失函数最小。那么随着神经网络的层数变多,那么也就随之复杂了,当然准确率不一定和层数成正比,这个我是测试过的。

参考:

https://zhuanlan.zhihu.com/p/58964140

https://blog.csdn.net/Syuhen/article/details/107402472

http://www.nnetinfo.com/text/show/4

猜你喜欢

转载自blog.csdn.net/FUTEROX/article/details/121391754