BP神经网络—训练鸢尾花数据集

导入包以及函数准备,这里用的tanh为激活函数

import math
import random
import numpy as np
from sklearn.metrics import classification_report  

from sklearn.preprocessing import OneHotEncoder
random.seed(0)

# 生成区间[a, b)内的随机数
def rand(a, b):
    return (b-a)*random.random() + a

# 函数 sigmoid,这里采用 tanh
def sigmoid(x):
    return 1/(1+np.exp(-x))

# 函数 sigmoid 的派生函数, 为了得到输出 (即:y)  求导
def dsigmoid(y):
    return y*(1-y)

BP神经网络的训练过程的基本步骤: 

  1. 初始化网络参数

    • 定义网络的结构,包括输入层、隐藏层和输出层的节点数。
    • 初始化连接输入层和隐藏层的权重矩阵(通常为随机值)和偏置项。
    • 初始化连接隐藏层和输出层的权重矩阵和偏置项。
  2. 前向传播(Forward Propagation)

    • 将训练数据输入到网络的输入层。
    • 对每个隐藏层节点,计算加权和并通过激活函数得到节点的输出。
    • 将隐藏层的输出作为输入,重复上述过程,计算输出层的输出。
    • 计算模型的预测输出。
  3. 计算误差

    • 将网络的预测输出与真实标签进行比较,计算误差。常用的误差计算方法包括均方误差(Mean Squared Error)等。
  4. 反向传播(Backward Propagation)

    • 从输出层开始,计算每个神经元对误差的贡献,即误差对权重的偏导数。
    • 通过链式法则,将误差逐层传播回隐藏层,计算隐藏层每个神经元对误差的贡献。
    • 更新权重和偏置项,以减小误差。这里使用梯度下降等优化算法。
  5. 重复迭代

    • 重复进行前向传播、计算误差、反向传播和权重更新的过程,直到满足停止条件,如达到最大迭代次数或误差降到足够小。

下面是代码:

class BPNN:
    ''' 三层反向传播神经网络 '''
    def __init__(self, ni, nh1,nh2, no):
        # 输入层、隐藏层、输出层的节点(数)
        self.ni = ni + 1 # 增加一个偏置节点
        self.nh1 = nh1
        self.nh2 = nh2
        self.no = no

        # 激活神经网络的所有节点(向量)
        self.inputL = [1.0]*self.ni
        self.hiddenL1 = [1.0]*self.nh1
        self.hiddenL2 = [1.0]*self.nh2
        self.outputL = [1.0]*self.no
        
        # 建立权重(矩阵)
        self.wi = np.ones((self.ni, self.nh1))
        self.wt = np.ones((self.nh1, self.nh2))
        self.wo = np.ones((self.nh2, self.no))
        # 设为随机值
        for i in range(self.ni):
            for j in range(self.nh1):
                self.wi[i][j] = rand(-0.2, 0.2)
        for t in range(self.nh1):
            for j in range(self.nh2):
                self.wt[t][j] = rand(-1.0, 1.0)
        for j in range(self.nh2):
            for k in range(self.no):
                self.wo[j][k] = rand(-2.0, 2.0)


    def forward(self, inputs):
        if len(inputs) != self.ni-1:
            raise ValueError('与输入层节点数不符!')

        # 激活输入层
        for i in range(self.ni-1):
            self.inputL[i] = inputs[i]

        # 激活隐藏层L1
        for j in range(self.nh1):
            sum = 0.0
            for i in range(self.ni):
                sum = sum + self.inputL[i] * self.wi[i][j]
            self.hiddenL1[j] = sigmoid(sum)
        # 激活隐藏层L2
        for j in range(self.nh2):
            sum = 0.0
            for i in range(self.nh1):
                sum = sum + self.hiddenL1[i] * self.wt[i][j]
            self.hiddenL2[j] = sigmoid(sum)
        # 激活输出层
        for k in range(self.no):
            sum = 0.0
            for j in range(self.nh2):
                sum = sum + self.hiddenL2[j] * self.wo[j][k]
            self.outputL[k] = sigmoid(sum)

        return self.outputL[:]

    def backPropagate(self, targets, N):
        ''' 反向传播 '''
        if len(targets) != self.no:
            raise ValueError('与输出层节点数不符!')

        # 计算输出层的误差
        output_deltas = [0.0] * self.no
        for k in range(self.no):
            error = targets[k]-self.outputL[k]  ##yk-Ok
            output_deltas[k] = dsigmoid(self.outputL[k]) * error
            
        # 更新输出层权重
        for j in range(self.nh2):
            for k in range(self.no):
                change = self.hiddenL2[j]*output_deltas[k]
                self.wo[j][k] = self.wo[j][k]+N*change
                
        # 计算隐藏层L2的误差
        hidden2_deltas = [0.0] * self.nh2
        for j in range(self.nh2):
            error = 0.0
            for k in range(self.no):
                error = error * output_deltas[k]*self.wo[j][k]
            hidden2_deltas[j] = dsigmoid(self.hiddenL2[j]) * error
 

    # 更新隐藏层L2的权重
        for i in range(self.nh1):
            for j in range(self.nh2):
                change = self.hiddenL1[i]*hidden2_deltas[j] #Oi*deltsj
                self.wt[i][j] = self.wt[i][j] + N*change 
            
            
        # 计算隐藏层L1的误差
        hidden1_deltas = [0.0] * self.nh1
        for j in range(self.nh1):
            error = 0.0
            for k in range(self.nh2):
                error = error + hidden2_deltas[k]*self.wt[j][k]
            hidden1_deltas[j] = dsigmoid(self.hiddenL1[j]) * error
            
        # 更新输入层权重
        for i in range(self.ni):
            for j in range(self.nh1):
                change = hidden1_deltas[j]*self.inputL[i]
                self.wi[i][j] = self.wi[i][j] + N*change


        # 计算误差
        error = 0.0
        for k in range(len(targets)):
            error = error + 0.5*(targets[k]-self.outputL[k])**2
        return error

    def test(self, patterns):
        y_pred=[]
        for p in patterns:
            y_pred.append(self.forward(p))
        return y_pred

    def weights(self):
        print('输入层权重:')
        for i in range(self.ni):
            print(self.wi[i])
        print()
        print('输出层权重:')
        for j in range(self.no):    
            print(self.wo[j])

    def train(self, x_data,y_data, iterations=10000, N=1):  
        for i in range(iterations):
            error = 0.0
            for p in range(len(x_data)):
                inputs = x_data[p]
                targets = y_data[p]
                self.forward(inputs)
                error = error + self.backPropagate(targets , N)
            if i % 500 == 0:
                print('误差 %-.5f' % error)            

然后导入鸢尾花的数据集

train_data = np.loadtxt(r'E:\data_code\iris-train.txt', delimiter='\t')
test_data =  np.loadtxt(r'E:\data_code\iris-test.txt', delimiter='\t')

使用One-hot独热编码处理数据

def onehot_y(y,cols):
    res=[]
    for i in range(len(y)):
        y_array=np.zeros(cols)
        y_array[int(y[i])]=1
        res.append(y_array)
    return np.array(res)
x_data = np.array(train_data[:,:4])
y_data = np.array(train_data[:,4])

y_onehot = onehot_y(y_data,3)

令神经网络结构为:[4,10,6,3]—— 其中,输入层为4个节点,第一个隐含层神经元个数为10个节点;第二个隐含层神经元个数为6个节点,输出层为3个节点

bpnet = BPNN(4,10,6,3)
bpnet.train(x_data,y_onehot)

a2e9b3ae7d9e422e8b89249d442aedd5.png

bpnet.weights()

75f82c94938840feb7be93cdd50f6c22.png

def show_accuracy(net,X,y):
    h = net.test(X)
    y_pred = np.argmax(h,axis=1)
    print(classification_report(y,y_pred))
x_test = np.array(test_data[:,:4])
y_test = np.array(test_data[:,4])
show_accuracy(bpnet,x_test,y_test)

a9db9611e39c4858b15e0d5a07e65e44.png

这是得到的答案

注意这里学习率被我改的比较离谱,因为正常都是0.1、0.01、0.001酱紫 (但是尝试过发现误差很大,准确率也不高,所以改成了1)

主要是这个数据可能因为噪声或者别的什么原因,需要很快的收敛速度,从误差也可以看到前两个数字也就是前1000左右那边下降的很快,然后后面非常平缓,或许要更大的迭代次数才能使得准确率更高

下面是N=0.01和0.1的正确率:

016c81a6c2db4dcb862a253926c27b3a.png

e6f2df053a4143b6998d8e2541171812.png

可以加个动量来加速梯度下降

具体.....0.9上下也不错了,准备摆烂(doge/别骂

任何问题欢迎指正~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值