神经网络与BP算法(代码实现)

鸢尾花数据集

from sklearn.datasets import load_iris

X = load_iris().data
y = load_iris().target
print(f'特征空间:{X}')
print(f'目标值:{y}')

特征空间:
[[5.1 3.5 1.4 0.2]
[4.9 3. 1.4 0.2]
[4.7 3.2 1.3 0.2]
[4.6 3.1 1.5 0.2]
[5. 3.6 1.4 0.2]
[5.4 3.9 1.7 0.4]
[4.6 3.4 1.4 0.3]
[5. 3.4 1.5 0.2]
[4.4 2.9 1.4 0.2]
[4.9 3.1 1.5 0.1]
[5.4 3.7 1.5 0.2]
[4.8 3.4 1.6 0.2]
[4.8 3. 1.4 0.1]
[4.3 3. 1.1 0.1]
[5.8 4. 1.2 0.2]
[5.7 4.4 1.5 0.4]
[5.4 3.9 1.3 0.4]
[5.1 3.5 1.4 0.3]
[5.7 3.8 1.7 0.3]
[5.1 3.8 1.5 0.3]
[5.4 3.4 1.7 0.2]
[5.1 3.7 1.5 0.4]
[4.6 3.6 1. 0.2]
[5.1 3.3 1.7 0.5]
[4.8 3.4 1.9 0.2]
[5. 3. 1.6 0.2]
[5. 3.4 1.6 0.4]
[5.2 3.5 1.5 0.2]
…]
目标值:
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
2 2]

神经网络的代码实现

import math
import numpy as np
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import minmax_scale


class ANN:
    def __init__(self, hidden_num, input_node_num, print_node_num, hidden_node_num, learning_rate):
        self.hn = hidden_num  # 隐藏层数
        self.inn = input_node_num  # 输入层节点数
        self.pnn = print_node_num  # 输出层节点数
        self.hnn1 = int(hidden_node_num)  # 第一层隐藏层节点数
        self.lr = learning_rate  # 学习率

    @staticmethod
    def sigmoid(w, x):  # 神经元
        return 1 / (1 + np.exp(-np.dot(w, x)))

    def one_hidden_FC(self):  # 单个隐藏层的全连接(full connected)
        wih, Wih, whp, Whp = [], [], [], []
        for i1 in range(self.hnn1):
            for j1 in range(self.inn):
                wi = np.random.rand(1)
            wih.append(wi[0])
            Wih.append(wih)  # 输入层与隐藏层之间的所有权重
        for p in range(self.pnn):
            for q in range(self.hnn1):
                wi = np.random.rand(1)
            whp.append(wi[0])
            Whp.append(whp)  # 隐藏层与输出层之间的所有权重
        Wih, Whp = np.array(Wih), np.array(Whp)
        return [Wih, Whp]

    # def more_hidden_FC(self):  # 多个隐藏层的全连接(full connected)
    #     pass

    @staticmethod
    def hidden_output_output(Wih, Whp, x):  # 隐藏层与输出层的输出值
        A, Y = [], []
        for i2 in range(len(Wih)):
            ai = ANN.sigmoid(Wih[i2], x)
            A.append(ai)  # 隐藏层的输出值
        for j2 in range(len(Whp)):
            y = ANN.sigmoid(Whp[j2], A)
            Y.append(y)  # 输出层的输出值
        return [A, Y]

    def output_error(self, x, yi):  # 输出层的误差  yi:真实值  y:预测值
        error_list, y = [], ANN.hidden_output_output(ANN.one_hidden_FC(self)[0], ANN.one_hidden_FC(self)[1], x)[1]
        for i3 in range(self.pnn):
            error = y[i3] * (1 - y[i3]) * (yi - y[i3])
            error_list.append(error)
        return error_list  # len(error_list) == pnn

    def hidden_error(self, Whp, A, oe):  # 隐藏层的误差  oe:output_error  Whp:隐藏层与输出层之间的权值  A:隐藏层的输出
        error, error_list = 0, []
        for i4 in range(self.hnn1):
            for j in range(self.pnn):
                error_part = A[i4] * (1 - A[i4]) * Whp[i4][j] * oe[j]
                error += error_part
            error_list.append(error)
        return error_list

    def one_hidden_BP(self, Wih, Whp, x, lr, yi):  # 单个隐藏层-反向传播算法(Back Propagation)  lr:学习率
        whp_new, Whp_new, wih_new, Wih_new = [], [], [], []
        Whp_trans, Wih_trans = Whp.transpose(), Wih.transpose()
        for i5 in range(self.hnn1):
            for j in range(self.pnn):
                Whp_trans[i5][j] += lr * ANN.output_error(self, x, yi)[j] * \
                                   ANN.hidden_output_output(ANN.one_hidden_FC(self)[0],
                                                            ANN.one_hidden_FC(self)[1], x)[0][i5]  # 更新隐藏层与输出层之间的权重
            whp_new.append(Whp_trans[i5][j])
            Whp_new.append(whp_new)
        Whp_new_arr = np.array(Whp_new)
        for p in range(self.inn):
            for q in range(self.hnn1):
                Wih_trans[p][q] += lr * ANN.hidden_error(self, Whp_new_arr,
                                                         ANN.hidden_output_output(ANN.one_hidden_FC(self)[0],
                                                                                  ANN.one_hidden_FC(self)[1], x)[0],
                                                         ANN.output_error(self, x, yi))[q] * x[p]  # 更新输入层与隐藏层之间的权重
            wih_new.append(Wih_trans[p][q])
            Wih_new.append(wih_new)
        Wih_new_arr = np.array(Wih_new)
        return [Wih_new_arr.transpose(), Whp_new_arr.transpose()]

    # def more_hidden_BP(self):  # 多个隐藏层-反向传播算法(Back Propagation)
    #     return

训练与测试

if __name__ == '__main__':
    # 鸢尾花数据预处理
    X = load_iris().data
    y = load_iris().target
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
    a, b, c, d = np.array(X_train), np.ones(len(X_train)), np.array(X_test), np.ones(len(X_test))
    X_train, X_test, y_train, y_test = np.insert(a, 0, b, axis=1), np.insert(c, 0, d, axis=1), np.array(y_train), np.array(y_test)
    print(f'数据集目标真实值:{y_train}')
    print(f'测试集目标真实值:{y_test}')

    # BP之前的预测值与损失
    input_node_num, print_node_num = 5, 5
    inn_multi_pnn = input_node_num * print_node_num
    hidden_node_num1 = math.sqrt(inn_multi_pnn)
    ann = ANN(1, input_node_num, print_node_num, hidden_node_num1, 0.5)
    Wih, Whp = ann.one_hidden_FC()[0], ann.one_hidden_FC()[1]

    Y_pre_pre = []
    for x1 in range(len(X_train)):
        y_pre_pre = np.sum(ann.hidden_output_output(Wih, Whp, X_train[x1])[1]) / print_node_num
        Y_pre_pre.append(y_pre_pre)
    Y_pre_pre = np.array(Y_pre_pre)
    print(f'进行BP算法之前的目标预测值:{Y_pre_pre}')

    Loss_pre = 0
    for s1 in range(len(y_train)):
        loss_pre = (1 / 2) * (y_train[s1] - Y_pre_pre[s1]) ** 2
        Loss_pre += loss_pre
    Loss_pre_aver = Loss_pre / len(y_train)
    print(f'进行BP算法之前的平均损失:{Loss_pre_aver}')

    # 进入BP更新所有的权重,这里采用随机梯度下降法
    count, train_data = 1, np.append(X_train, np.array(y_train)[:, np.newaxis], axis=1)
    while count < 200:
        lr = ann.lr / count  # 缩小学习率
        np.random.shuffle(train_data)
        X_Train, y_Train = train_data[:, 0:-1], train_data[:, -1]
        Wih_zero, Whp_zero = np.zeros_like(Wih), np.zeros_like(Whp)
        for i in range(len(X_train)):
            Wihna, Whpna = ann.one_hidden_BP(Wih, Whp, X_Train[i], lr, y_Train[i])[0], \
                           ann.one_hidden_BP(Wih, Whp, X_Train[i], lr, y_Train[i])[1]
            Wih_zero += Wihna
            Whp_zero += Whpna
        Wih, Whp = (Wih_zero / len(X_Train)), (Whp_zero / len(X_Train))
        count += 1

    # BP之后的预测值与损失
    Wih_new, Whp_new, Y_behind_pre = Wih, Whp, []
    for x2 in X_train:
        y_behind_pre = np.sum(ann.hidden_output_output(Wih_new, Whp_new, x2)[1]) / print_node_num
        Y_behind_pre.append(y_behind_pre)
    Y_behind_pre = np.array(Y_behind_pre)
    print(f'进行BP算法之后的目标预测值:{Y_behind_pre}')

    Loss_behind = 0
    for s2 in range(len(y_train)):
        loss_behind = (1 / 2) * (y_train[s2] - Y_behind_pre[s2]) ** 2
        Loss_behind += loss_behind
    Loss_behind_aver = Loss_behind / len(y_train)
    print(f'进行BP算法之后的平均损失:{Loss_behind_aver}')

    # 测试与评估
    Y_test_pre = []
    for x3 in X_test:
        y_test_pre = np.sum(ann.hidden_output_output(Wih_new, Whp_new, x3)[1]) / print_node_num
        Y_test_pre.append(y_test_pre)
    Y_test_pre = np.array(Y_test_pre)
    print(f'测试集的目标预测值:{Y_test_pre}')

    test_Loss = 0
    for s3 in range(len(y_test)):
        test_loss = (1 / 2) * (y_test[s3] - Y_test_pre[s3]) ** 2
        test_Loss += test_loss
    test_Loss_aver = test_Loss / len(y_test)
    print(f'测试集的平均损失:{test_Loss_aver}')

运行结果

数据集目标真实值:
[2 0 2 1 0 1 1 1 1 1 1 2 2 2 0 0 0 0 2 1 0 2 2 0 1 1 0 2 0 1 0 2 2 0 0 2 0
 1 1 2 0 1 1 2 2 1 2 2 0 0 2 2 2 2 2 1 1 2 0 2 1 0 2 2 0 2 2 2 2 0 0 2 2 2
 1 0 1 0 0 1 0 2 1 0 2 1 1 1 1 2 2 1 1 1 2 0 2 1 1 0 0 0 0 0 0 0 2 0 2 1 1
 0 0 1 1 2 2 2 0 2]
测试集目标真实值:
[1 0 0 1 0 2 0 2 1 1 1 2 2 0 1 0 1 2 1 0 1 1 1 0 0 1 1 1 0 0]

进行BP算法之前的目标预测值:
[0.95358437 0.95293946 0.95357806 0.95357565 0.95215735 0.95349937
 0.9535721  0.95339961 0.95336813 0.9535622  0.95338073 0.95358438
 0.95357384 0.95358075 0.95275053 0.95326864 0.95313547 0.95317084
 0.95357977 0.95355051 0.95247272 0.95358561 0.95357275 0.95246984
 0.95353004 0.95346372 0.95308123 0.95358242 0.95328152 0.95355286
 0.95297633 0.95357651 0.95358199 0.95309172 0.95276685 0.95357728
 0.95303167 0.9535244  0.95351466 0.95358512 0.95303296 0.95347749
 0.95357594 0.95358712 0.95357949 0.95354221 0.95355623 0.95358596
 0.95277082 0.95284331 0.95358211 0.9535851  0.95358672 0.95358851
 0.95358242 0.95351957 0.95353949 0.95358442 0.95333449 0.95358136
 0.95351939 0.95317617 0.95358138 0.95358546 0.95297613 0.95358165
 0.95358455 0.95356967 0.95357384 0.95291504 0.95284283 0.95358587
 0.95358291 0.95356937 0.95352678 0.95262859 0.95355589 0.95275914
 0.95258763 0.95356351 0.95293526 0.95358712 0.95356571 0.95319253
 0.95358763 0.95356097 0.95356243 0.95353473 0.95356407 0.95358475
 0.95356977 0.95357479 0.95357103 0.95356173 0.95358679 0.95330895
 0.95357682 0.95356577 0.95353947 0.95272987 0.95338016 0.95303203
 0.9529777  0.95313444 0.95258472 0.95147526 0.95357467 0.95291091
 0.95356875 0.95356211 0.95354819 0.9525402  0.95316707 0.95355547
 0.95336063 0.95358038 0.95358808 0.9535511  0.9530053  0.95357283]
进行BP算法之前的平均损失:0.353186539001048

进行BP算法之后的目标预测值:
[1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]
进行BP算法之后的平均损失:0.35000000003129444

测试集的目标预测值:
[1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
 1. 1. 1. 1. 1. 1.]
测试集的平均损失:0.2666666665414888

评估与总结

  1. 评估
    结合上面的运行结果,在训练阶段,将训练数据输入到BP之前和BP之后的神经网络中去,得到的预测值与真实值分别取平均误差,后者比前者略小,说明一开始的神经网络模型在经过BP算法和数据的训练之后取得了成效。但成效甚微,究其原因我认为有一下几点:
    (1)代码本身可能存在逻辑上的问题(究竟是什么逻辑问题,我也不知道。我恳请各路大神指点一二,在此评论留言,本人将不胜感激)。
    (2)训练次数较少(代码中仅训练了200次,但200次就运行了将近半小时,实在是有点慢,幸好最后的结果满足我的预期)。
    (3)因为采用了sigmoid函数,运行过程中可能出现了梯度消失

  2. 总结
    在进行全连接神经网络的编程之前,通过任老师的讲解,我对神经网络有了一个初步的认识,大致的实现流程也有了。但是,在编程的过程中,许多问题开始浮出水面。
    (1)神经网络的输入层的一个节点对应的到底是训练数据的一条数据[x1, x2, x3, x4,…]还是这一条数据[x1, x2, x3, x4,…]中的x1呢? 我的答案是后者
    (2)神经网络的输出层为什么会有怎么多节点呢?我之所以有这个疑问,是因为无论是回归还是分类,对应的输出结果都只有一个,而输出层却又怎么多节点,难道输出有多个吗? 我的答案:输出层的所有结点得出的结果求和取平均。
    (3)其他问题:权重的表示以及与隐藏层有关的问题。(我遇到的问题实在是太多了,每一个多十分费神,现在想起来都令我头大,这里就不一一列举了。如果有遇到和我类似或者不同的问题的小伙伴欢迎在评论区留言,我们共同探讨,加深理解。

    总之,解决完这些问题后,编程也慢慢地步入了正轨。虽然编程问题依旧很多,但好在有惊无险的将程序编出来了。但这仅仅是进军深度学习的第一步,后面还要跟着任老师继续学习卷积神经网络CNN、循环神经网络RNN、长短时记忆网络LSTM等等等等。往后的道路艰难且漫长,正所谓“长路漫漫,唯剑作伴”,深度学习的第一把“剑”便是赢取其他“宝剑”的开始。

参考链接

零基础入门深度学习(3) - 神经网络和反向传播算法

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值