日撸java三百行day71-73

news2025/1/12 23:15:41

文章目录

  • 说明
  • BP神经网络
    • 1.基础知识
    • 3 代码理解
      • 3.1 数据的初始化
      • 3.2 训练过程 train方法
      • 3.3 forward 前向传播函数
      • 3.4 backPropagation反向传播函数

说明

闵老师的文章链接: 日撸 Java 三百行(总述)_minfanphd的博客-CSDN博客
自己也把手敲的代码放在了github上维护:https://github.com/fulisha-ok/sampledata

BP神经网络

1.基础知识

BP神经网络由多个神经元按层次排列而成,通常分为输入层、隐藏层和输出层,其中隐藏层可以为多层,结构如下所示。BP神经网络通过前向传播和反向传播两个过程来进行训练。

  • 前向传播
    将输入数据从输入层经过各层的神经元传递至输出层的过程,每个神经元将输入值与权重相乘并求加权和,经过激活函数处理后输出给下一层的神经元。这样一层层传递,直到输出层得到最终的预测结果。前向传播的过程相对来说比较简单。
  • 反向传播
    根据预测结果与真实标签之间的差异,通过调整网络中的权重来减小预测误差的过程。反向传播利用梯度下降优化算法,从输出层开始,根据误差计算每个神经元的梯度,并将梯度信息传递回前一层,以调整权重。反向传播的推导过程我认为是有点难度的,具体的推导过程可以参考文章和这篇
    在这里插入图片描述

3 代码理解

3.1 数据的初始化

因为在代码中几个成员变量的声明需要理解,不然对代码的理解会有些困难,因此我通过图形来画出对这些变量的一个初始赋值情况:
在这里插入图片描述
假设我tempLayerNodes的值为{ 4, 5, 3 },代表的意思就是一共有3层,其中输入层有4个结点,隐藏层有5个结点,输出层有3个结点。我们通过SimpleAnn构造函数,数据会初始化为什么样呢?如下图所示:
在这里插入图片描述
经过初始化:

  • layerNodeValues:layerNodeValues[i],其中i代表第i层,layerNodeValues[i][j]代表每i层第j结点在前向传播过程具体的值:值应该是加权和+激活函数最后得出的值
  • layerNodeErrors:layerNodeErrors[i],其中i代表第i层,layerNodeErrors[i][j]代表在反向传播过程中每i层第j结点的误差值
  • edgeWeights 这是一个三维数组,存储了边的权重值。edgeWeights[i]表示第i层,edgeWeights[i][j][k]表示从第i层的第j个节点到第i+1层的第k个结点
  • edgeWeightsDelta 与edgeWeights的维度相同,记录在反向传播过程中,每条边权重需要更新的量
    :edgeWeights ,edgeWeightsDelta 在初始化时公式如下,表明我们是加入了偏置项,如下图的edgeWeights[0][4]: [0.63, 0.68, 0.86, 0.32, 0.98]就是边的偏置项
 edgeWeights[l] = new double[layerNumNodes[l] + 1][layerNumNodes[l + 1]];
 edgeWeightsDelta[l] = new double[layerNumNodes[l] + 1][layerNumNodes[l + 1]];

在这里插入图片描述

3.2 训练过程 train方法

这一个for循环是对我们读入的数据样本进行训练,tempInput就是我们没一次样本的输入数据,tempTarget是预期的实际值。forward是前向传播函数和backPropagation是反向传播函数

/**
     * Train using the dataset.
     */
    public void train() {
        double[] tempInput = new double[dataset.numAttributes() - 1];
        double[] tempTarget = new double[dataset.numClasses()];
        for (int i = 0; i < dataset.numInstances(); i++) {
            // Fill the data.
            for (int j = 0; j < tempInput.length; j++) {
                tempInput[j] = dataset.instance(i).value(j);
            }

            // Fill the class label.
            Arrays.fill(tempTarget, 0);
            tempTarget[(int) dataset.instance(i).classValue()] = 1;

            // Train with this instance.
            forward(tempInput);
            backPropagation(tempTarget);
        }
    }

3.3 forward 前向传播函数

将输入数据经过网络各层处理,最后一层输出结点的值

 public double[] forward(double[] paraInput) {
        // Initialize the input layer.
        for (int i = 0; i < layerNodeValues[0].length; i++) {
            layerNodeValues[0][i] = paraInput[i];
        }

        // Calculate the node values of each layer.
        double z;
        for (int l = 1; l < numLayers; l++) {
            for (int j = 0; j < layerNodeValues[l].length; j++) {
                // Initialize according to the offset, which is always +1 -偏置项(offset)
                z = edgeWeights[l - 1][layerNodeValues[l - 1].length][j];
                // Weighted sum on all edges for this node.
                for (int i = 0; i < layerNodeValues[l - 1].length; i++) {
                    z += edgeWeights[l - 1][i][j] * layerNodeValues[l - 1][i];
                }

                // Sigmoid activation.
                // This line should be changed for other activation functions.
                layerNodeValues[l][j] = 1 / (1 + Math.exp(-z));
            }
        }

        return layerNodeValues[numLayers - 1];
    }

假设我们读入的样本是

  • layerNodeValues[0][i] 初始化我们的输入数据
  • 函数中的三层循环
    最外层循环中,int l = 1; l < numLayers; l++表示我们从第二层开始到最后一层
    内两层循环中:计算当前层的每个结点的加权和并通过激活函数更新值。具体计算方式:
    • z = edgeWeights[l - 1][layerNodeValues[l - 1].length][j]; 获取边的偏置项数据
    • 计算加权和 最内层循环:z += edgeWeights[l - 1][i][j] * layerNodeValues[l - 1][i];
    • 激活函数:sigmoid函数
      σ ( x ) = 1 1 + e − x \sigma (x) = \frac{1}{ 1 +e^{-x}} σ(x)=1+ex1

例如图示计算 第一层第0个结点的值,即layerNodeValues[1][0]的值:
在这里插入图片描述

3.4 backPropagation反向传播函数

虽然代码行数挺少,但感觉这个过程的公式推导挺难,需要去百度自行消化。可以参考这个公式(公式推导具体过程参考文章)
在这里插入图片描述
其中 δ k K \delta _{k}^{K} δkK是我们的误差项, ∂ L ∂ w j k \frac{\partial L}{\partial w_{jk}} wjkL表示损失函数(L)关于权重 w j k w_{jk} wjk的偏导数(也可以理解为梯度)。
我们结合上面的公式可以知道,我们在反向传播函数中更新权重是结合梯度下降来进行优化参数的。

 public void backPropagation(double[] paraTarget) {
        // Step 1. Initialize the output layer error.
        int l = numLayers - 1;
        for (int j = 0; j < layerNodeErrors[l].length; j++) {
            layerNodeErrors[l][j] = layerNodeValues[l][j] * (1 - layerNodeValues[l][j])
                    * (paraTarget[j] - layerNodeValues[l][j]);
        }

        // Step 2. Back-propagation even for l == 0
        while (l > 0) {
            l--;
            // Layer l, for each node.
            for (int j = 0; j < layerNumNodes[l]; j++) {
                double z = 0.0;
                // For each node of the next layer.
                for (int i = 0; i < layerNumNodes[l + 1]; i++) {
                    if (l > 0) {
                        z += layerNodeErrors[l + 1][i] * edgeWeights[l][j][i];
                    }

                    // Weight adjusting.
                    edgeWeightsDelta[l][j][i] = mobp * edgeWeightsDelta[l][j][i]
                            + learningRate * layerNodeErrors[l + 1][i] * layerNodeValues[l][j];
                    edgeWeights[l][j][i] += edgeWeightsDelta[l][j][i];
                    if (j == layerNumNodes[l] - 1) {
                        // Weight adjusting for the offset part.
                        edgeWeightsDelta[l][j + 1][i] = mobp * edgeWeightsDelta[l][j + 1][i]
                                + learningRate * layerNodeErrors[l + 1][i];
                        edgeWeights[l][j + 1][i] += edgeWeightsDelta[l][j + 1][i];
                    }
                }

                // Record the error according to the differential of Sigmoid.
                // This line should be changed for other activation functions.
                layerNodeErrors[l][j] = layerNodeValues[l][j] * (1 - layerNodeValues[l][j]) * z;
            }
        }
    }
  • 第一个for循环计算输出结点的误差值。
    误差值: δ k K = ( o k − t k ) ∗ o k ∗ ( 1 − o k ) δ_k^K = (o_k - t_k) * o_k * (1 - o_k) δkK=(oktk)ok(1ok),其中 o k {o_k } ok为激活函数输出的值

    • layerNodeValues[l][j]在当前的权重下的输出结果
    • (paraTarget[j] - layerNodeValues[l][j]):真是值和预测值之间的差异
    • layerNodeValues[l][j] 和补数 (1 - layerNodeValues[l][j]) 的乘积,即为激活函数(Sigmoid 函数)的导数
  • while循环 更新权重
    edgeWeightsDelta:调整第 l 层的第 j 个节点与第 l+1 层的第 i 个节点之间的连接上的权重增量,利用梯度下降算法来调整(结合了参数mobp和learningRate 系数):(mobp动力系数,加速收敛;learningRate学习率控制权重调整的幅度),而在代码中有一个if判断j == layerNumNodes[l] - 1是考虑偏置结点。
    layerNodeErrors[l + 1][i]是我们已经知道的他下一层的误差值(输出层和隐藏层的误差值计算不一样哦~)

edgeWeightsDelta[l][j][i] = mobp * edgeWeightsDelta[l][j][i] + learningRate * layerNodeErrors[l + 1][i] * layerNodeValues[l][j];
edgeWeights[l][j][i] += edgeWeightsDelta[l][j][i];

更新当前层的结点误差(主要还是隐藏层)。

layerNodeErrors[l][j] = layerNodeValues[l][j] * (1 - layerNodeValues[l][j]) * z;

运行结果:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/703743.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Vue2】Vant2上传文件使用formData方式,base64图片转Blob再转File上传

文章目录 前言一、base64转换为 Blob 对象的方法二、使用步骤1.引入工具类js2.编写formData上传方法3.api方法中的request代码 三、实际操作1.html代码2.js代码 总结 前言 vant2上传组件传送门 使用vant2组件中的uploader组件 <van-uploader v-model"fileList" …

打破常规之路,创新永不停歇!ADSCOPE成功斩获2023第十一届TopDigital“年度最佳营销技术公司”奖项!

2023年6月29日&#xff0c;第十一届TopDigital创新营销奖获奖结果正式揭晓&#xff0c;ADSCOPE凭借先进的广告变现技术&#xff0c;创新的变现理念&#xff0c;从海内外657家参赛企业提交的3052件作品中突出重围&#xff0c;成功斩获“TopDigital创新营销奖—年度最佳营销技术公…

el-table多级表头处理方法,了解lebel和prop的真实含义,template的意义,减少全局定义变量。

Element - The worlds most popular Vue UI framework 官网地址 其原理只需要在 el-table-column 里面嵌套 el-table-column&#xff0c;就可以实现多级表头。 要实现的效果如下图所示&#xff1a; <div class"c-table" id"tablePrint"><el-tabl…

【Java-SpringBoot+Vue+MySql】前后端分离项目云端部署

目录 部署环境&#xff1a; 安装MYSQL&#xff1a; 安装Nginx 安装配置JDK 远程连接数据库 前端打包 后端打包 心得&#xff1a; 部署环境&#xff1a; CentOS7.6 MySQL5.7 JDK1.8 Nginx1.8 下载MySQL MySQL :: Download MySQL Community Server (Archived Versions) …

qt Qss 边框渐变

目录 背景渐变 方案一 Qss 方案二 paintEvent函数 方案三 QGraphicsDropShadowEffect投影效果 背景渐变 QT里面背景是可以渐变&#xff0c;其中qlineargradient里面参数意思 spread&#xff1a;渐变方式&#xff08;具体可以查看qt帮助文档搜索PadSpread&#xff09; 坐…

vue2模拟无限级评论

目录 一、效果展示 二、代码展示 2.1、主页面 2.2、评论父页面组件 2.3、评论多级页面(递归组件) 一、效果展示 二、代码展示 2.1、主页面 <template><div><h1>姓名:{{ $store.state.userInfo1.username }}</h1><button:class"{ aacti…

【加密算法】5 种常见的摘要、加密算法

大家平时的工作中&#xff0c;可能也在很多地方用到了加密、解密&#xff0c;比如&#xff1a; 用户的密码不能明文存储&#xff0c;要存储加密后的密文用户的银行卡号、身份证号之类的敏感数据&#xff0c;需要加密传输还有一些重要接口&#xff0c;比如支付&#xff0c;客户…

数据结构和算法的概念以及时间复杂度空间复杂度详解

⭐️ 什么是数据结构&#xff1f; 百度百科给数据结构的定义&#xff1a; 数据结构(Data Structure)是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。 数据结构就是数据在内存中的存储方式。 ⭐️ 什么是算法&#xff1f; 百度百…

基于高精度三维机器视觉的新能源汽车锂电池表面缺陷检测

​Part.1 行业背景 ​随着新能源汽车在全球范围内成为焦点发展领域&#xff0c;企业对电池质量控制和检测的要求也变得更加严格。在机器视觉行业迅速发展的背景下&#xff0c;市场上提供了功能强大且种类齐全的3D相机系列&#xff0c;可以满足锂电池从电芯到模组各个工艺和工位…

IAM风险CTF挑战赛

wiz启动了一个名为“The Big IAM Challenge”云安全CTF挑战赛。旨在让白帽子识别和利用 IAM错误配置&#xff0c;并从现实场景中学习&#xff0c;从而更好的认识和了解IAM相关的风险。比赛包括6个场景&#xff0c;每个场景都专注于各种AWS服务中常见的IAM配置错误。 Challenge…

【Docker】云原生利用Docker确保环境安全、部署的安全性、安全问题的主要表现和新兴技术产生的详细讲解

前言 Docker 是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux或Windows操作系统的机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。 &#x1f4d5;作者简介&#xff1a;热…

Vue键盘事件

1.Vue中常用的按键别名&#xff1a; 回车enter 删除delete&#xff08;捕获“删除”和“退格”键&#xff09; 推出esc 空格space 换行tab&#xff08;特殊&#xff0c;必须配合keydown使用&#xff09; 上up 下down 左left 右right 2.Vue未提供别名的按键&#xff0c;可以使用…

Arduino驱动BH1750模块实现光照强度采集

Arduino驱动BH1750模块实现光照强度采集 简介特征电气参数接线程序结果 简介 BH1750FVI是一个用于I2 C总线接口的数字环境光传感器IC。该IC最适合于获取调节手机液晶显示屏和键盘背光功率的环境光数据。在高分辨率下探测大范围是可能的。(1 - 65535 lx )。BH1750FVI可以应用于…

Matlab 回归分析与预测

统计分析 回归分析与预测 数理统计—回归分析 回归分析类型 回归分析目的 一元线性回归 多元线性回归的案例 %{ [B,BINT,R,RINT,STATS] regress(Y,X) [B,BINT,R,RINT,STATS] regress(Y,X,ALPHA) 参数解释&#xff1a;B&#xff1a; 回归系数&#xff0c;是个向量&…

小程序官方tabbar和自定义tabbar

uniapp官方tabbar&#xff1a; 打开项目中的 pages.json 文件。 在 JSON 对象中添加一个名为 tabBar 的字段&#xff0c;并设置其值为一个对象。 在 tabBar 对象中&#xff0c;配置 color 和 selectedColor 字段来定义 TabBar 的默认颜色和选中项的颜色。示例&#xff1a; …

功放IC 2018和功放IC HX8358A的区别

概述&#xff1a; 2018功放IC&#xff0c;目前在市面的情况是品牌多、杂&#xff0c;芯片的工作电压和喇叭输出功率不统一。经常出现低电压芯片用在高电压的产品上面&#xff0c;导致芯片容易损坏&#xff0c;给用户带来一定的麻烦。但它的销售量可能已超过8002的功放芯片了&am…

6.19、JAVA IO流 File 字节流 字符流

IO简介 1 流Stream 在学习IO流之前,我们首先需要学习的概念就是Stream流 为了方便理解,我们可以把数据的读写操作抽象成数据在"管道"中流动,但需注意: 1.流只能单方向流动 2.输入流用来读取 → in 3.输出流用来写出 → out 4.数据只能从头到尾顺序的读写一次 所以以…

Python 基本数据类型(六)

文章目录 每日一句正能量Tuple&#xff08;元组&#xff09;结语 每日一句正能量 一生要做的几件事情一管理好自己的身体。二管理好自己的情绪&#xff0c;正面思维。三服务好自己的家庭&#xff0c;让家人生活幸福。四做好本职工作&#xff0c;做一两件特别完美&#xff0c;石…

剑指 Offer 51: 数组中的逆序对

这道题归根结底就是一个归并问题&#xff0c;逆序对本质上就是比较大小&#xff0c;如果两边作为一个整体比较过那么就可以排序合并&#xff08;因为这个过程每一步都计算了count的值&#xff0c;所以合并起来是可以的&#xff09;。 下面的k应该是mid1&#xff08;从中间的右…

智“绘“城市:智慧环卫可视化运营管理系统

前言 随着我国城镇化的不断推进&#xff0c;城市的规模、数量不断增加&#xff0c;城市的人口数量也快速增长&#xff0c;造成的城镇生活垃圾、建筑垃圾也随之增长&#xff0c;这造成人们对环卫服务的需求增加。而与此同时&#xff0c;随着经济社会的快速发展&#xff0c;人们…