Numpy实现多层神经网络(附数据集+代码+神经网络详细推导公式+原理说明)

多层神经网络详细推导公式点击这里:多层神经网络(正向传播、反向传播)公式推导

希望静心下来认真看公式推导,然后可以尝试自己实现代码。

数据集用的还是单个神经元实验中的糖尿病数据集。Numpy实现简单神经元进行逻辑回归对糖尿病数据集二分类

完整工程项目点击这里

请务必认真看推导公式,我一开始也仅仅是看了神经网络的构成关系,然后按自己的理解去推导正向传播和反向传播过程,然后觉得自己的推导应该没问题,出于对自己的信任便按照自己推导的公式用numpy实现了多层神经网络模型,昨天我用numpy先实现了单个的神经元,当时神经元还出现了发散的情况,让我怀疑自己的推导公式或者实现有问题,但是考虑到单个神经元能力太弱存在发散现象好像也能理解,然后又实现了多层神经网络,训练非常稳,没有出现发散现象,loss也在不断随着训练下降,很开心~

当然也希望我的分享对你的学习有所帮助,如果有问题请及时指出,谢谢~

实现代码如下:

import numpy as np
import pandas as pd
import random
import queue
from sklearn.metrics import accuracy_score
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei'] #显示中文标签
class Node:#每个节点表示一个神经元,每个神经元包括对应的参数
    w=[]#权重参数
    dw=[]#每个参数下降的梯度
    dwc=1
    b=0#常数项
    a=0#输出
    id=0#每个节点的ID
    pos=0#该神经元在所在层的第几个位置

layers = [8,4,2,1]#定义4层的神经网络,第一层为输入层,8个特征,第2层4个神经元,第3层2个神经元,第4层为输出层,1个神经元
lr=0.01
step=5000


node = []
to = [[]for i in range(np.sum(layers[1:]))]
fr = [[]for i in range(np.sum(layers[1:]))]
id=0
for i in range(1,len(layers)):
    for j in range(layers[i]):
        t = Node()
        t.w = np.random.random(layers[i-1])#和上一层的数目对应
        t.dw = np.zeros(layers[i-1])#初始化为0
        t.dwc=1
        t.b = random.random()
        t.a = 0#输出
        t.id = id
        t.pos = j
        id+=1
        node.append(t)#储存下每一个节点

id1 = 0
id2 = 0
for i in range(1,len(layers)-1):#连接节点关系
    id2 += layers[i]
    for j in range(layers[i]):
        for k in range(layers[i+1]):
            to[id1+j].append(id2 + k)
            fr[id2+k].append(id1 + j)
    id1 += layers[i]


def Sigmoid(z):#激活函数
    return 1/(1+np.e**(-z))

def Loss(a,y):#损失函数
    return  -(y*np.log(a)+(1-y)*np.log(1-a))

def forward(x,y,node,layers):#前向传播
    for i in range(layers[1]):
        node[i].a = np.sum(np.array(node[i].w)*x)
    id=0
    for i in range(1,len(layers)):
        for j in range(layers[i]):
            pos = id + j
            node[pos].a = Sigmoid(node[pos].a + node[pos].b)
            for k in range(len(to[id])):
                v = to[id][k]
                node[v].a += node[v].w[node[pos].pos]*node[pos].a
        id += layers[i]
    return Loss(node[np.sum(layers[1:])-1].a,y),node

def backward(x,y,node,layers):#反向传播
    pos=len(node)-1
    node[pos].dwc = (node[pos].a-y)/(node[pos].a*(1-node[pos].a))
    for i in range(len(node[pos].dw)):
        node[pos].dw[i] += node[pos].dwc*node[pos].a*(1-node[pos].a)*node[fr[pos][i]].a
    #利用BFS广度遍历完成反向传播
    q=queue.Queue()
    for i in range(len(node[pos].dw)):
        t=Node()
        t.dwc=node[pos].dwc*node[pos].a*(1-node[pos].a)*node[pos].w[i]
        t.id=fr[pos][i]
        q.put(t)

    while q.empty()==False:
        t=q.get()
        pos=t.id
        for i in range(len(node[pos].dw)):
            if len(fr[pos])==0:#已经反向遍历到第一层
                node[pos].dw[i] += t.dwc*node[pos].a*(1-node[pos].a)*x[i]
            else:
                node[pos].dw[i] += t.dwc*node[pos].a*(1-node[pos].a)*node[fr[pos][i]].a
                t2=Node()
                t2.dwc=t.dwc*node[pos].a*(1-node[pos].a)*node[pos].w[i]
                t2.id=fr[pos][i]
                q.put(t2)
    return node

def Predict_function(x,node,layers):#预测函数
    def Single_predict(x,node,layers):#单条预测
        for i in range(layers[1]):
            node[i].a = np.sum(np.array(node[i].w)*x)
        id=0
        for i in range(1,len(layers)):
            for j in range(layers[i]):
                pos = id + j
                node[pos].a = Sigmoid(node[pos].a + node[pos].b)
                for k in range(len(to[id])):
                    v = to[id][k]
                    node[v].a += node[v].w[node[pos].pos]*node[pos].a
            id += layers[i]
        if node[np.sum(layers[1:])-1].a>=0.5:
            return 1
        else:
            return 0
    y_pred=[]
    for i in range(len(x)):
        y_pred.append(Single_predict(x,node,layers))
    return np.array(y_pred)

N=layers[0]
data=pd.read_csv('diabetes.txt',header=None).values
M=len(data)#数据集大小
x=data[:,0:N]
x=x/(np.max(x)-np.min(x))
y=data[:,-1]

train_x=x[0:int(M*0.8)]
train_y=y[0:int(M*0.8)]
#按8:2划分训练集和验证集
test_x=x[int(M*0.8):]
test_y=y[int(M*0.8):]

cur_los=[]

for index in range(step):
    loss = 0
    for i in range(len(node)):
        for j in range(len(node[i].dw)):
            node[i].dw[j]=0#清空
    M=len(train_x)
    for i in range(M):
        los,node = forward(train_x[i],train_y[i],node,layers)
        loss += los
        node=backward(train_x[i],train_y[i],node,layers)
    #更新权重
    for i in range(len(node)):
        for j in range(len(node[i].dw)):
            node[i].dw[j]/=M
            node[i].w[j]-=lr*node[i].dw[j]#乘学习率更新权重

    cur_los.append(loss/M)
    print('step=',index,'  loss=',loss/M)


y_pre = Predict_function(test_x,node,layers)
print("accuracy_score: %.4lf" % accuracy_score(y_pre,test_y))

plt.plot(cur_los)
plt.title('loss曲线')
plt.show()

运行结果
在这里插入图片描述
因为隐层神经元之间采用sigmoid作为激活函数,下降太慢了!!!训练了2000多轮,下降幅度却非常小,loss=0.645左右,此时的准确率为%65左右,现在loss还在保持下降趋势,后面可以考虑隐层采用ReLU作为激活函数的效果,应该收敛是很快的。

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值