基于MATLAB手搓人工神经网络

news/2024/9/22 13:23:53

神经网络

基本概念

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

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

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

神经网络中的参数:以输入层和第一个隐藏层为例。假设输入数据是一个\(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% 网络propertiesshape       % 各层神经元layers      % 神经元之间的权重偏置endmethodsfunction obj = NetWork(network_shape)obj.shape = network_shape;obj.layers = cell(1, length(network_shape) - 1);for i = 1:length(network_shape) - 1obj.layers{i} = Layer(network_shape(i), network_shape(i+1));endendfunction 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));elselayer_output = Normalize(SoftMax(layer_out));endoutputs{i+1} = layer_output;endendfunction [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});endfor i = length(obj.layers):-1:1opts(i) = opts(i).zero_gard();if i == length(obj.layers)gradient = (outputs{end}(dataRange,:) - target_vector(dataRange,:)) / size(target_vector(dataRange,:), 1); % 计算输出层的梯度elsesigmoid_derivative_value = outputs{i+1}(dataRange,:).*(1- outputs{i+1}(dataRange,:));gradient =  sigmoid_derivative_value .* (gradient * obj.layers{i+1}.weights'); % 使用当前层的权重矩阵来计算梯度endopts(i) = opts(i).optimize(gradient, epoch);obj.layers{i} = obj.layers{i}.update_weights_and_biases(lr, opts(i).grad,outputs{i}(dataRange,:));endendend
end

Layer.m

classdef Layer% Class representing a neural network layerpropertiesweightsbiasesendmethodsfunction obj = Layer(n_input, n_neurons)obj.weights = randn(n_input, n_neurons);obj.biases = randn(1, n_neurons);endfunction output = layer_forward(obj, inputs)output = inputs * obj.weights + obj.biases;endfunction 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;endend
end

Adam.m

classdef Adam% Adam算法优化器% 在梯度变化较大的方向上小步快跑,在梯度变化较小的方向上大步慢跑propertiesbeta1       % 动量项参数1beta2       % 动量项参数2mt          % 一阶矩估计vt          % 二阶矩估计grad        % 优化后的梯度endmethodsfunction obj = Adam(beta1,beta2)%   初始化obj.beta1 = beta1;obj.beta2 = beta2;obj.mt = 0;obj.vt = 0;endfunction 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);endfunction obj = zero_gard(obj) % 清空梯度%obj.mt = 0;%obj.vt = 0;obj.grad = 0;endend
end

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

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

相关文章

drawcall优化 - 无限滚动容器

这是一个前端项目常见的一个问题dc优化。比如一个复杂的背包容器500个元素,如果使用粗暴的实例化500个元素进去,最终的dc无疑会是灾难的。在内存管理和性能角度上讲也是铺张浪费的。 看见效果如下 dc 1091 但实际上500个元素itemCell,在一个屏幕视界内能看到和交互的也就6~…

java所有集合的相互关系是什么

Java集合框架是一个设计精良、功能强大的工具集,用于存储和操作对象集合。它大致分为以下几类,并且各类之间存在一定的层级关系和功能差异:### 顶级接口(根接口)1. **Collection**: 是最基础的集合接口,所有集合类都直接或间接实现了这个接口。它定义了基本的集合操作,如…

mysql 锁

1,介绍锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中,除传统的计算资源(CPU、RAM、I/O)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个…

Windows系统安装Jmeter教程

1、进入官网https://jmeter.apache.org/download_jmeter.cgi 选择与Jdk适配的版本下载:2、解压压缩包3、配置环境变量 进入到系统配置环境变量的界面:在下方的系统变量那里,新建变量:变量名为:JMETER_HOME 变量地址:Jmeter的安装路径 如图所示:4、运行JMETER 在JMeter的…

XeLaTeX 无法编译含有经过 pdfcrop 裁剪的 PDF 文件的文档

今天在写 LaTeX 文档时踩了个大坑,我在文档里插入了一个 PDF 图片之后文档无法编译了。 于是我去掉多余代码,做了一个最小工作示例: \documentclass{article} \usepackage{graphicx} \begin{document}\includegraphics{my_image.pdf}\end{document}就是这样一个简单的代码,…

Jenkins - [02] 安装部署

题记部分 一、Jenkins是什么Jenkins,原名Hudson,2011年改为现在的名字,它是一个开源的实现持续集成的软件工具。官网:https://www.jenkins.io/ 官网:https://www.jenkins.io/zh/ (中文)Jenkins能实时监控集成中存在的错误,提供详细的日志文件和提醒功能,还能用图表的…

OpenTelemetry agent 对 Spring Boot 应用的影响:一次 SPI 失效的

背景 前段时间公司领导让我排查一个关于在 JDK21 环境中使用 Spring Boot 配合一个 JDK18 新增的一个 SPI(java.net.spi.InetAddressResolverProvider) 不生效的问题。 但这个不生效的前置条件有点多:JDK 的版本得在 18+ SpringBoot3.x 还在额外再配合使用 -javaagent:opentel…

easyUI datagrid 数据不显示,tablebody 高度为0 的问题

如下图,接口请求回来数据,表头也显示出来了。 打开元素,可以看到table的bbody里面是有数据的,数据也渲染到了页面表格中,但是table的height为0。 排查以下问题:检查数据源格式是否正确,如是 JSON 数组还是符合 easyui 要求的对象。确保在 datagrid 初始化后正确触发数据…