首页 > 其他分享 >基于MATLAB手搓人工神经网络

基于MATLAB手搓人工神经网络

时间:2024-05-14 15:31:46浏览次数:29  
标签:基于 frac hat end 人工神经网络 obj alpha omega MATLAB

神经网络

基本概念

​ 神经网络,模拟生物神经网络,节点称为神经元。神经网络分层命名,直接接收输入数据的是输入层,输出结果的是输出层,输入层与输出层之间的是隐藏层

前向传播:数据从输入层开始,逐层向前传播计算,直到输出层得到输出结果。

反向传播:将输出层输出的结果与真实值进行比较,得到一个差异(损失函数值),根据这个差异逐层调整网络中的参数,方向与前向传播相反。

神经网络中的参数:以输入层和第一个隐藏层为例。假设输入数据是一个\(m\)维向量\(x\)(即输入层有m个神经元),给定第一个隐藏层有\(n\)个神经元,层之间数据的传递过程是一个基于权重\(\omega\)偏置\(b\)的线性运算,\(\omega\)是一个\(n*m\)维矩阵,\(b\)是一个\(n\)维向量,计算过程为 \(z=\omega*x^T+b\),\(z\)是该隐藏层神经元的输入值(除了输入层神经元外其余层的神经元均有一个输入和输出值),\(\omega\)在图里就是神经元之间的连线。

​ 整个训练神经网络的过程本质就是不停地在各层前向传播、计算误差并反向传播来得到一组较好的网络参数(各层之间的权重和偏置)。

激活函数:前面提到除了输入层神经元外其余层的神经元均有一个输入\(z\)和输出值\(f(z)\),\(f\)是该神经元的激活函数,目的是引入非线性关系(如果没有这个激活函数,那么无论有多少隐藏层,最后得到的整个系统输入输出关系本质还是一个线性的)。

​ 损失函数:分类问题的结果是离散值,平方损失函数如果用于分类问题,分类正确的误差都是一样的,分类错误的误差都是一样,不具有距离的意义,在分类错误的情况下无法判断优化的好坏。并且平方损失误差还和预测结果错误的样本有联系,在分类问题中只关注分类正确的结果。所以 平方损失函数不适用于分类问题。交叉熵损失函数针对只分类正确的预测结果,和分类错误的预测结果没有联系,而分类任务中很少考虑分类错误的结果,主要考虑让正确的分类尽可能大。而在回归中,考虑错误的结果就很必要了,需要让回归的函数尽可能满足所有的样本包括错误的样本,所以交叉熵损失函数不适合回归。

常用的激活函数:

​ 关于Sigmoid

\[\begin{align} s&=sigmoid(x)=\frac{1}{1+exp(-x)}\\ \frac{\alpha s}{\alpha x}&=\frac{1}{1+exp(-x)}·\frac{exp(-x)}{1+exp(-x)}=s·(1-s)\\ \end{align} \]

​ 关于Softmax函数及其导数:

\[\begin{align} s_i&=softmax(x_i)=\frac{exp(x_i)}{\sum_{N \atop j=1}{exp(x_j)}}\\ \frac{\alpha s_i}{\alpha x_j}&=s_i-s_j^2 \quad\quad(i=j)\\ \frac{\alpha s_i}{\alpha x_j}&=-s_i·s_j \quad\quad(i \neq j) \end{align} \]

传播过程

​ 以如下网络为例分析传播过程。

​ \(x_i\)表示输入层的输入,$\delta_i \(表示隐藏层输入,\)\beta_i\(表示\)\delta_i\(经过激活函数之后的值,\)z_i \(表示输出层的输入,\)\hat{y}i\(表示\)z_i\(经过激活函数之后的值即整个网络最终的输出,用\)y_i\(表示真实值。\)\omega_1\(是输入层与隐藏层之间的权重矩阵,\)b_1\(是偏置,\)\omega_2\(是隐藏层与输出层之间的权重矩阵,\)b_2\(是偏置,具体对应神经元之间的权重和偏置用\)\omega,b_{k-ij}\(表示。激活函数用\)f$表示。

前向传播

​ \(x = [x_1,x_2,x_3]\),\(\delta=[\delta_1,\delta_2,\delta_3]\)​

​ 输入数据后传播一层

\[\delta = x*\omega_1+b_1 \]

​ 经过激活函数\(f\)后

\[\beta = f(\delta) \]

​ 传播到下一层

\[z = \beta*\omega_2+b_2 \]

​ 经过激活函数\(f\)后

\[\hat y = f(z) \]

计算损失

​ 以交叉熵损失函数为例进行说明。\(y_{ij}\)表示第\(i\)个样本的真实标签的第\(j\)个类别的值(0或1),\(\hat{y}\)表示模型预测的第\(i\)个样本的第\(j\)个类别的概率,\(N\)为样本数量。

\[L =- \frac{1}{N} \sum_{i}^{N} \sum_{j}^{classNum}y_{ij}\log(\hat{y}_{ij}) \]

​ \(y\)用向量表示时

\[L =- \frac{1}{N} \sum_{i}^{N} y_{i}\log(\hat{y}_{i}) \]

​ 调整参数(权重矩阵和偏置):让损失尽可能快的降低,需要沿着损失函数的其梯度下降,给出学习率\(η\)作为下降的步长,\(Δω\)和\(Δb\)分别是\(Loss\)对\(ω\)和\(b\)的导数。

\[ω=\omega-η*Δω \]

\[b=\omega-η*Δb \]

调整隐藏层和输出层的参数

​ 权重参数的变化量

\[\begin{align} Δ\omega_2&=\frac{\alpha L}{\alpha \omega_2}=\frac{\alpha Loss}{\alpha \hat{y}}·\frac{\alpha \hat{y}}{\alpha z}·\frac{\alpha z}{\alpha \omega_2}\\ \frac{\alpha L}{\alpha \hat{y}}&= -\frac{1}{N}\sum_{i=1}^{N}\frac{y_i}{\hat{y}_i}\\ \frac{\alpha L}{\alpha \hat{y}}·\frac{\alpha \hat{y}}{\alpha z_j}&= -\frac{1}{N}\sum_{i=1}^{N}\frac{y_i} {\hat{y}_i}\frac{\alpha \hat{y_i}}{\alpha z_j}\\&=-\frac{1}{N}[\frac{y_j}{\hat{y}_j}·(\frac{\hat{y}_j}{z_j}+\sum_{i \neq j}^{N}\frac{\hat{y}_i}{z_j})]\\&=-\frac{1}{N}[({y_i}-{\hat{y}_i{y_i}})-\sum_{i \neq j}^{N}{\hat{y}_i}{y_i}]\\&=\frac{1}{N}(\hat{y}_i-y_i)\\ \frac{\alpha z}{\alpha \omega_2}&=\beta\\ Δ\omega_2 &= \frac{1}{N}(\hat{y}_i-y_i)·\beta\\ \end{align} \]

​ 同理可得偏置参数的变化量

\[Δb_2 = \frac{1}{N}(\hat{y}_i-y_i) \]

调整隐藏层和输出层的参数

\[\begin{align} Δ\omega_1&=\frac{\alpha L}{\alpha \omega_1}=\frac{\alpha Loss}{\alpha \hat{y}}·\frac{\alpha \hat{y}}{\alpha z}·\frac{\alpha z}{\alpha \beta}·\frac{\alpha \beta}{\alpha \delta}·\frac{\alpha \delta}{\alpha \omega_1}\\ \frac{\alpha z}{\alpha \beta} &= \omega_2\\ \frac{\alpha \beta}{\alpha \delta} &= \frac{\alpha f(\delta)}{\alpha \delta}=f'(\delta)=f(\delta)(1-f(\delta))\quad对于sigmoid\\ \frac{\alpha \delta}{\alpha \omega_1} &= x\\ \end{align} \]

​ $\frac{\alpha Loss}{\alpha \hat{y}}·\frac{\alpha \hat{y}}{\alpha z} $S 是上一个反向传播的Layer梯度的一部分,从这一计算过程中可以发现,我们在做反向传播进行计算的过程也存在梯度的逐层传播。

​ 那么这里定义

\[gradient_1= \frac{1}{N}(\hat{y}_i-y_i) \]

​ 即

\[\begin{align} Δ\omega_2&=gradient_1·\beta\\ Δb_2&=gradient_1\\ \end{align} \]

​ 那么

\[\begin{align} Δ\omega_1&=gradient_1·\omega_2·f'(\delta)·x\\ Δb_1&=gradient_1·\omega_2·f'(\delta)\\ \end{align} \]

​ 定义

\[gradient_2=gradient_1·\omega_2·f'(\delta) \]

​ 则

\[\begin{align} Δ\omega_1&=gradient_2·x\\ Δb_1&=gradient_2\\ \end{align} \]

​ 至此,一轮传播已经完成。

递推一般情况

​ 激活函数记为\(f\),假设隐藏层\(n\)与隐藏层\(n+1\)之间各参数已知,即\(gradient_n\)、\(w_n\)等,则反向传播至隐藏层\(n-1\)与隐藏层\(n\)时

\[\begin{align} Δ\omega_{n-1}&=gradient_n·\omega_n·f'(\delta)·x\\ Δb_{n-1}&=gradient_n·\omega_n·f'(\delta)\\ gradient_{n-1}&=gradient_n·\omega_n·f'(\delta) \end{align} \]

​ \(x\)表示这个Layer的输入,\(gradient_n\)表示前一个Layer的梯度,\(\omega_n\)表示前一个Layer的权重,\(\delta\)是当前Layer的输出(通过激活函数之前)。

优化梯度过程

The method computes individual adaptive learning rates for different parameters from estimates of first and second moments of the gradients. 该方法通过估计梯度的第一阶矩和第二阶矩来计算不同参数的个体自适应学习率

结合两种方法实现:AdaGrad很好地处理稀疏梯度,RMSProp很好地处理在线和非平稳环境

Adam的一些优点是参数更新的大小对梯度的重新缩放是不变的,它的步长大约由步长超参数限定,它不需要一个平稳目标,它与稀疏梯度一起工作,并且它自然地执行一种步长退火形式。

​ 当我们使用 \(m\) 和 \(v\) 来更新参数时,目的是为了根据历史梯度信息来调整学习率,以便更好地指导参数的更新方向和步长。具体地说,\(m\) 反映了梯度的变化趋势,而 \(v\) 反映了梯度的变化幅度。因此,将 \(m\) 除以根号 \(v\) 可以使得参数更新的步长更加稳定,并且能够在梯度变化幅度较大时缩小步长,在梯度变化幅度较小时放大步长,从而提高了优化算法的性能和收敛速度。

​ 在更新参数时,我们希望能够在梯度变化较大的方向上小步快跑,在梯度变化较小的方向上大步慢跑。\(m/\sqrt{v}\) 的形式可以很好地实现这一目标,因为它同时考虑了梯度的变化趋势(\(m\))和变化幅度(\(\sqrt{v}\)),从而使得参数更新更加智能和适应性。如图蓝色虚线表示使用优化器之前的效果,红色为使用优化器后

代码实现

代码链接
链接:https://pan.baidu.com/s/1bFJ8k7ZoV3ki5yOVHMgdQg?pwd=q9ei
提取码:q9ei

NetWork.m

classdef NetWork
    % 网络
    properties
        shape       % 各层神经元
        layers      % 神经元之间的权重偏置

    end
    
    methods
        function obj = NetWork(network_shape)
            obj.shape = network_shape;
            obj.layers = cell(1, length(network_shape) - 1);
            for i = 1:length(network_shape) - 1
                obj.layers{i} = Layer(network_shape(i), network_shape(i+1));
            end
        end
        
        function outputs = network_forward(obj, inputs)
            addpath("functions\");
            outputs = cell(1, length(obj.layers) + 1);
            outputs{1} = inputs;
            for i = 1:length(obj.layers)
                layer_out = obj.layers{i}.layer_forward(outputs{i});
                if i < length(obj.layers)
                    layer_output = Normalize(sigmoid(layer_out));
                else
                    layer_output = Normalize(SoftMax(layer_out));
                end
                outputs{i+1} = layer_output;
            end
        end
        
        function [opts,obj] = network_backward(obj, outputs, target_vector, lr, opts, method, epoch)
            % 网络的反向传播
            % outputs: 每一层的输出
            addpath("functions\");
            if method == "SGD"      % 随机梯度下降,每次只选一个数据
                dataRange = randi([1,height(outputs{1})]);
            elseif method == "MBGD"     % 最小批次随机梯度下降,每次只选一个batch的数据
                batchSize = 32; % 定义批次大小
                % 整个程序中未使用batch,故随机选择batchSize个数据进行梯度下降
                dataRange = randi([1, height(outputs{1})], 1, batchSize);
            else      % 批量梯度下降,每次用所有数据
                dataRange = 1:height(outputs{1});
            end
            for i = length(obj.layers):-1:1
                opts(i) = opts(i).zero_gard();
                if i == length(obj.layers)
                    gradient = (outputs{end}(dataRange,:) - target_vector(dataRange,:)) / size(target_vector(dataRange,:), 1); % 计算输出层的梯度
                else
                    sigmoid_derivative_value = outputs{i+1}(dataRange,:).*(1- outputs{i+1}(dataRange,:));
                    gradient =  sigmoid_derivative_value .* (gradient * obj.layers{i+1}.weights'); % 使用当前层的权重矩阵来计算梯度
                end
                opts(i) = opts(i).optimize(gradient, epoch);
                
                obj.layers{i} = obj.layers{i}.update_weights_and_biases(lr, opts(i).grad,outputs{i}(dataRange,:));
            end
        end

    end
end


Layer.m

classdef Layer
    % Class representing a neural network layer
    
    properties
        weights
        biases
    end
    
    methods
        function obj = Layer(n_input, n_neurons)
            obj.weights = randn(n_input, n_neurons);
            obj.biases = randn(1, n_neurons);
        end
        
        function output = layer_forward(obj, inputs)
            output = inputs * obj.weights + obj.biases;
        end

        function obj = update_weights_and_biases(obj, lr, gradient, layerInput)
            delta_weights = layerInput' * gradient;
            delta_biases = mean(gradient, 1);
            obj.weights = obj.weights - lr * delta_weights;
            obj.biases = obj.biases - lr * delta_biases;
        end

    end
end

Adam.m

classdef Adam
    % Adam算法优化器
    % 在梯度变化较大的方向上小步快跑,在梯度变化较小的方向上大步慢跑
    properties
        beta1       % 动量项参数1
        beta2       % 动量项参数2
        mt          % 一阶矩估计
        vt          % 二阶矩估计
        grad        % 优化后的梯度
    end

    methods
        function obj = Adam(beta1,beta2)
            %   初始化
            obj.beta1 = beta1;
            obj.beta2 = beta2;
            obj.mt = 0;
            obj.vt = 0;
        end

        function obj = optimize(obj,gt,t)
            % 更新一阶矩和二阶矩参数
            % gt是当前梯度
            epsilon = 1e-8;     % 微小值,防止分母为0
            %obj = obj.update_moment(gt,epoch);
            obj.mt = obj.beta1 * obj.mt + ( 1 - obj.beta1 ) * gt;
            obj.vt = obj.beta2 * obj.vt + ( 1 - obj.beta2 ) * (gt.*gt);
            mt_hat = obj.mt / ( 1 - obj.beta1^t );
            vt_hat = obj.vt / ( 1 - obj.beta2^t );
            obj.grad = mt_hat ./ (sqrt(vt_hat)+epsilon);
        end
        function obj = zero_gard(obj) 
            % 清空梯度
            %obj.mt = 0;
            %obj.vt = 0;
            obj.grad = 0;
        end
    end
end

标签:基于,frac,hat,end,人工神经网络,obj,alpha,omega,MATLAB
From: https://www.cnblogs.com/hair-is-decreasing/p/18191394

相关文章

  • 基于Python实现MapReduce
    一、什么是MapReduce首先,将这个单词分解为Map、Reduce。Map阶段:在这个阶段,输入数据集被分割成小块,并由多个Map任务处理。每个Map任务将输入数据映射为一系列(key,value)对,并生成中间结果。Reduce阶段:在这个阶段,中间结果被重新分组和排序,以便相同key的中间结果被传递到同一个R......
  • 基于角色的访问控制并根据不同的场景显示不同的反馈信息
    要实现基于角色的访问控制(RBAC),并根据不同的场景(如菜单项、页面、组件)显示不同的反馈信息(如隐藏、禁用、提示等),可以设计一套完整的解决方案。这个方案需要结合权限管理、上下文、路由控制和条件渲染等多个方面。以下是一个详细的实现方案:1.设置角色和权限首先,定义你的角色和权限......
  • React基于RBAC的权限控制
    简单实现基于RBAC(Role-BasedAccessControl,基于角色的访问控制)的权限控制,可以通过定义角色和权限,然后将权限分配给不同的角色来实现。用户根据其角色获得相应的权限,进而访问特定的路由、页面组件或者操作。以下是在React应用中实现RBAC的一个简单示例。这个示例包括了路由保护......
  • 基于FPGA的贪吃蛇游戏 之代码解析
    基于FPGA的贪吃蛇游戏之代码解析1. 代码结构代码结构包含7格.v文件。  下面依次解析。   2. 代码解析(1) seg_display.v数码管的译码模块是最熟悉,最简单的模块了。这里是共阳极的数码管,用case语句编码即可。从上图可以看到,这个模块被例化了3次,分别驱动3个数码......
  • 基于高斯混合模型的视频背景提取和人员跟踪算法matlab仿真
    1.算法运行效果图预览    2.算法运行软件版本MATLAB2013B 3.算法理论概述      基于高斯混合模型(GaussianMixtureModel,GMM)的视频背景提取和人员跟踪算法是一种广泛应用的计算机视觉方法,主要用于分离视频序列中的静态背景和动态前景(比如人物运动)。 ......
  • m基于遗传优化的LDPC码OMS译码算法最优偏移参数计算和误码率matlab仿真
    1.算法仿真效果matlab2022a仿真结果如下:   2.算法涉及理论知识概要       低密度奇偶校验码(Low-DensityParity-CheckCodes,LDPCcodes)因其优秀的纠错能力和接近香农极限的性能而广泛应用于现代通信系统中。有序统计译码(OrderedStatisticsDecoding,OSD)......
  • 基于访问数量的断路器
    1新建一个微服务模块我们在cloud-payment-service模块中新增一个controller以提供给其它微服务模块调用。@RestControllerpublicclassPayCircuitController{@GetMapping("/pay/circuit/{id}")publicStringmyCircuit(@PathVariable("id")Integerid){......
  • 【k8s】基于猪齿鱼部署相关概念辨析
    相关链接:https://golangguide.top/架构/云原生/核心知识点/k8s到底是什么.html集群命名空间NODEDeploymentpodcontainer......
  • Markdown TMS-基于Markdown的本地测试管理系统
    MarkdownTMS简介我们通常习惯使用线的测试平台或者XMind、Excel在来管理手工测试用例。由Jetbrains公司推出的MarkdownTMS则提供了另一种管理手工测试用例的思路。相比于XMind、Excel来说,Markdown是纯文本文件,几乎不需要安装额外的软件进行查看和编辑。使用Markdown+Git即......
  • 【WCH蓝牙系列芯片】-基于CH582开发板—主机枚举从机所有服务和特征
    -------------------------------------------------------------------------------------------------------------------------------------在使用沁恒的CH582蓝牙芯片的过程中,有时需要主机去连接蓝牙从机进行通信,主机在使用过程中工作流程是: 1、 蓝牙初始化完成后,开始扫描......