BP神经网络(Back Propagation Neural Network)算法原理推导与Python实现详解

简介: BP神经网络(Back Propagation Neural Network)算法原理推导与Python实现详解

正文


##BP神经网络算法推导

给定训练集:

D={(x1,y1),(x2,y2),...,(xm,ym)},xiRI,yiRO

即数据有D 个特征,标签为O 维实值向量。


因此,我们定义一个拥有I 个输入层神经元、O个输出层神经元的神经网络,且设该网络的隐藏层神经元个数为H。

其中,隐藏层第h 个神经元的阀值用γ h 表示,输出层第o 个神经元的阀值用θ  表示。

输入层第i ii个神经元与隐藏层第h hh个神经元之间的连接权重为ν i h,记隐藏层第h hh个神经元接收到的输入为α h = ∑ i = 1 I ν i h x i  

隐藏层第h hh个神经元与输出层第o oo个神经元之间的连接权重为ω h o ,记输出层第o oo个神经元接收到的输入为β o = ∑ h = 1 H ω h o b h 其中b h 为隐藏层第h 个神经元的输出。

假设隐藏层和输出层都使用Sigmoid函数作为激活函数

Sigmoid函数:

5.png

对于训练集中的一个训练例k :6.png假设神经网络的输出为7.png,则有:

8.png

由此可以得到,神经网络在训练例k kk上的均方误差为:

9.png

BP是一个迭代学习算法,迭代的每一轮都会对权重进行更新,基于梯度下降算法和链式求导法则,我们可以得到:

1、对隐藏层第h hh个神经元与输出层第o oo个神经元之间的连接权重ω h o  的更新估计式为:

10.png

其中

11.png

因此,权重ω h o 的更新估计式为:


12.png

2、对输入层第i ii个神经元与隐藏层第h hh个神经元之间的连接权重ν i h 的更新估计式为:


13.png

其中:

14.png15.png16.png

因此,权重ν i h  的更新估计式为

18.png


##BP神经网络Python实现

该神经网络被设置为三层:一层输入层、一层隐藏层、一层输出层

样本集:

特征一 特征二 标签
0 0 0
0 1 1
1 0 1
1 1 0


可以看出,这就是一个异或样本集,使用这个样本集可以展现出神经网络与感知机在处理非线性可分问题上的差别。

import math
import random
# 用于设置权重矩阵的大小并给定初始权重
def weight_matrix(row, col, weight=0.0):
    weightMat = []
    for _ in range(row):
        weightMat.append([weight] * col)
    return weightMat
# 用于给权重矩阵内的每元素生成一个初始随机权重
def random_weight(parm_1, parm_2):
    return (parm_1 - 1) * random.random() + parm_2
# Sigmoid激活函数
def sigmoid(x):
    return 1.0 / (1.0 + math.exp(-x))
# Sigmoid激活函数的导函数
def sigmoid_derivate(x):
    return x * (1 - x)
# 定义BP神经网络类
class BPNeuralNetwork:
    def __init__(self):
        # 定义输入层、隐藏层、输出层,所有层的神经元个数都初始化为0
        self.input_num, self.hidden_num, self.output_num = 0, 0, 0
        # 定义输入层、隐藏层、输出层的值矩阵,并在setup函数中初始化
        self.input_values, self.hidden_values, self.output_values = [], [], []
        # 定义输入-隐藏层、隐藏-输出层权重矩阵,并在setup函数中设置大小并初始化
        self.input_hidden_weights, self.hidden_output_weights = [], []
    # 神经网络的初始化函数
    # 四个参数分别代表:对象自身、输入层神经元个数、隐藏层神经元个数、输出层神经元个数
    def setup(self, input_num, hidden_num, output_num):
        # 设置输入层、隐藏层、输出层的神经元个数,其中输入层包含偏置项因此数量+1
        self.input_num, self.hidden_num, self.output_num = input_num + 1, hidden_num, output_num
        # 初始化输入层、隐藏层、输出层的值矩阵,均初始化为1
        self.input_values = [1.0] * self.input_num
        self.hidden_values = [1.0] * self.hidden_num
        self.output_values = [1.0] * self.output_num
        # 设置输入-隐藏层、隐藏-输出层权重矩阵的大小
        self.input_hidden_weights = weight_matrix(self.input_num, self.hidden_num)
        self.hidden_output_weights = weight_matrix(self.hidden_num, self.output_num)
        # 初始化输入-隐藏层、隐藏-输出层的权重矩阵
        for i in range(self.input_num):
            for h in range(self.hidden_num):
                self.input_hidden_weights[i][h] = random_weight(-0.2, 0.2)
        for h in range(self.hidden_num):
            for o in range(self.output_num):
                self.hidden_output_weights[h][0] = random_weight(-0.2, 0.2)
    # 神经网络的前向预测
    # 两个参数分别代表:对象自身、单个数据
    def predict(self, data):
        # 将数据放入输入层,-1是由于输入层中的偏置项不需要接收数据
        for i in range(self.input_num - 1):
            self.input_values[i] = data[i]
        # 隐藏层计算
        for h in range(self.hidden_num):
            # 激活函数的参数
            total = 0.0
            # 激活函数的参数值由输入层权重和输入层的值确定
            for i in range(self.input_num):
                total += self.input_values[i] * self.input_hidden_weights[i][h]
            # 将经过激活函数处理的输入层的值赋给隐藏层
            self.hidden_values[h] = sigmoid(total - 0)
        # 输出层计算
        for o in range(self.output_num):
            total = 0.0
            for h in range(self.hidden_num):
                total += self.hidden_values[h] * self.hidden_output_weights[h][o]
            self.output_values[o] = sigmoid(total - 0)
        return self.output_values[:]
    # 神经网络的反向传播
    # 四个参数分别代表:对象自身、单个数据、数据对应的标签、学习率(步长)
    # 本函数皆为数学推导的实现
    def back_propagate(self, data, label, learn):
        # 反向传播前先进行前向预测
        self.predict(data)
        # 计算输出层的误差
        output_datas = [0.0] * self.output_num
        for o in range(self.output_num):
            error = label[o] - self.output_values[o]
            output_datas[o] = sigmoid_derivate(self.output_values[o]) * error
        # 计算隐藏层的误差
        hidden_datas = [0.0] * self.hidden_num
        for h in range(self.hidden_num):
            error = 0.0
            for o in range(self.output_num):
                error += output_datas[o] * self.hidden_output_weights[h][o]
            hidden_datas[h] = sigmoid_derivate(self.hidden_values[h]) * error
        # 更新隐藏-输出层权重
        for h in range(self.hidden_num):
            for o in range(self.output_num):
                self.hidden_output_weights[h][o] += learn * output_datas[o] * self.hidden_values[h]
        # 更新输入-隐藏层权重
        for i in range(self.input_num):
            for h in range(self.hidden_num):
                self.input_hidden_weights[i][h] += learn * hidden_datas[h] * self.input_values[i]
        # 计算样本的均方误差
        error = 0
        for o in range(len(label)):
            error += 0.5 * (label[o] - self.output_values[o]) ** 2
        return error
    # 神经网络训练函数
    # 四个参数分别代表:对象自身、数据集、标签、最大循环次数、学习率、终止误差
    def train(self, datas, labels, limit=50000, learn=0.05, stop_error=0.02):
        for i in range(limit):
            error = 0
            for i in range(len(datas)):
                data = datas[i]
                label = labels[i]
                error += self.back_propagate(data, label, learn)
            if error <= stop_error:
                break
    # 神经网络验证函数
    def test(self):
        # 数据集及其标签
        datas = [[0, 0], [0, 1], [1, 0], [1, 1]]
        labels = [[0], [1], [1], [0]]
        # 调用神经网络的初始化函数并传入参数作为输入层、隐藏层、输出层的神经元个数
        # 其中输入层的神经元个数应与数据集的特征数保持一致
        self.setup(2, 5, 1)
        self.train(datas, labels)
        for data in datas:
            print(self.predict(data))
# 定义BP神经网络对象并调用其进行预测
if __name__ == '__main__':
    nn = BPNeuralNetwork()
    nn.test()


神经网络训练结果:

[0.018648283776391633]
[0.9754998553712237]
[0.9806999914518663]
[0.02997622156919269]


该结果与真实值labels[0, 1, 1, 0]基本类似,可以认为神经网络在预测异或这类非线性可分问题上是有效的。



相关文章
|
7天前
|
数据采集 缓存 定位技术
网络延迟对Python爬虫速度的影响分析
网络延迟对Python爬虫速度的影响分析
|
8天前
|
Python
Python中的异步编程:使用asyncio和aiohttp实现高效网络请求
【10月更文挑战第34天】在Python的世界里,异步编程是提高效率的利器。本文将带你了解如何使用asyncio和aiohttp库来编写高效的网络请求代码。我们将通过一个简单的示例来展示如何利用这些工具来并发地处理多个网络请求,从而提高程序的整体性能。准备好让你的Python代码飞起来吧!
24 2
|
15天前
|
数据采集 存储 JSON
Python网络爬虫:Scrapy框架的实战应用与技巧分享
【10月更文挑战第27天】本文介绍了Python网络爬虫Scrapy框架的实战应用与技巧。首先讲解了如何创建Scrapy项目、定义爬虫、处理JSON响应、设置User-Agent和代理,以及存储爬取的数据。通过具体示例,帮助读者掌握Scrapy的核心功能和使用方法,提升数据采集效率。
59 6
|
19天前
|
安全 网络安全 数据安全/隐私保护
|
6天前
|
运维 物联网 网络虚拟化
网络功能虚拟化(NFV):定义、原理及应用前景
网络功能虚拟化(NFV):定义、原理及应用前景
22 3
|
4天前
|
机器学习/深度学习 人工智能 算法
基于Python深度学习的【垃圾识别系统】实现~TensorFlow+人工智能+算法网络
垃圾识别分类系统。本系统采用Python作为主要编程语言,通过收集了5种常见的垃圾数据集('塑料', '玻璃', '纸张', '纸板', '金属'),然后基于TensorFlow搭建卷积神经网络算法模型,通过对图像数据集进行多轮迭代训练,最后得到一个识别精度较高的模型文件。然后使用Django搭建Web网页端可视化操作界面,实现用户在网页端上传一张垃圾图片识别其名称。
21 0
基于Python深度学习的【垃圾识别系统】实现~TensorFlow+人工智能+算法网络
|
9天前
|
搜索推荐 Python
快速排序的 Python 实践:从原理到优化,打造你的排序利器!
本文介绍了 Python 中的快速排序算法,从基本原理、实现代码到优化方法进行了详细探讨。快速排序采用分治策略,通过选择基准元素将数组分为两部分,递归排序。文章还对比了快速排序与冒泡排序的性能,展示了优化前后快速排序的差异。通过这些分析,帮助读者理解快速排序的优势及优化的重要性,从而在实际应用中选择合适的排序算法和优化策略,提升程序性能。
23 1
|
16天前
|
数据采集 前端开发 中间件
Python网络爬虫:Scrapy框架的实战应用与技巧分享
【10月更文挑战第26天】Python是一种强大的编程语言,在数据抓取和网络爬虫领域应用广泛。Scrapy作为高效灵活的爬虫框架,为开发者提供了强大的工具集。本文通过实战案例,详细解析Scrapy框架的应用与技巧,并附上示例代码。文章介绍了Scrapy的基本概念、创建项目、编写简单爬虫、高级特性和技巧等内容。
39 4
|
17天前
|
网络协议 安全 算法
网络空间安全之一个WH的超前沿全栈技术深入学习之路(9):WireShark 简介和抓包原理及实战过程一条龙全线分析——就怕你学成黑客啦!
实战:WireShark 抓包及快速定位数据包技巧、使用 WireShark 对常用协议抓包并分析原理 、WireShark 抓包解决服务器被黑上不了网等具体操作详解步骤;精典图示举例说明、注意点及常见报错问题所对应的解决方法IKUN和I原们你这要是学不会我直接退出江湖;好吧!!!
网络空间安全之一个WH的超前沿全栈技术深入学习之路(9):WireShark 简介和抓包原理及实战过程一条龙全线分析——就怕你学成黑客啦!
|
20天前
|
机器学习/深度学习 人工智能 算法
【车辆车型识别】Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+算法模型
车辆车型识别,使用Python作为主要编程语言,通过收集多种车辆车型图像数据集,然后基于TensorFlow搭建卷积网络算法模型,并对数据集进行训练,最后得到一个识别精度较高的模型文件。再基于Django搭建web网页端操作界面,实现用户上传一张车辆图片识别其类型。
65 0
【车辆车型识别】Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+算法模型