【深度学习】手动实现全连接神经网络(FCNN)

🌻个人主页:相洋同学
🥇学习在于行动、总结和坚持,共勉!

神经网络的本质就是通过参数、线性函数与激活函数来拟合特征与目标之间的真实函数关系。

01 神经网络简介

1.1 引入

神经网络是一门重要的机器学习技术,是一种模拟人脑的神经网络以期能够实现类人工智能的机器学习技术。

人脑中的神经网络是一个非常复杂的组织。成人的大脑中估计有1000亿个神经元之多。

机器学习中的神经网络是如何实现这种模拟,并达到如此惊人的效果的?本篇文章将从简单介绍,并手动基于numpy来复现一个神经网络结构,以期能够全面了解神经网络的构建过程。

1.2 神经元

我们首先来看一下构成大脑的基本结构:神经元

 神经元的树突用来接收其他细胞传输来的信号,突触用来传输电信号。其内部可以对信息进行处理。

我们再来看一下深度学习中的神经网络结构:

神经网络一般由输入层、若干个隐藏层和输出层构成,整体来看我们似乎看不出什么联系。我们将单独的神经元抽离出来看

在单个神经元中,左边的输入点类似树突来接收信息,右边的点类似突触用来传送信息。所以深度学习中的神经网络结构与人脑的神经网络结构极其类似。

1.3 如何理解神经元

       世界具有普遍联系,事物具有各种特征。自然界中人类通过学习和探索现实世界的特征总结成规律和模式来指导自己的行为。

       通过事物的一部分特征来推测事物的另一部分特征是神经网络的主要功能。那为什么要设置不同层数的网络结构呢?

       我们可以这样来想:我们都知道,事物具有表面现象和内部特征和联系。通过深层次,多维度的特征抽象,我们能更好的推测事物现象。

更多的关于神经网络发展历史和拓展参照这篇文章:

神经网络——最易懂最清晰的一篇文章

本文着重复现神经网络结构

02 复现神经网络结构

2.1 神经网络总体工作流程

要想复现神经网络结构,我们必须了解基于神经网络的深度学习任务训练全流程:

 总体来说神经网络训练体现在以下几个阶段:

1.参数随机初始化:需要提前根据输入样本特征维度隐藏层维度输出层维度随机初始化参数;

2.前向传播:将样本矩阵输入模型以计算出预测值;

3.反向传播:根据预测值和真实值通过损失函数来计算出损失loss,通过对损失函数求导利用优化器对模型权重进行更新。

4.迭代更新参数:将新的样本送入模型继续前向传播和反向传播,直到达到预定的训练频次或提前达到训练目标。

2.2 神经网络代码实现(使用numpy库)

基于神经网络的结构,我们先对任务进行描述和分解

目标:总目标是撰写一个神经网络结构能够完成简单分类任务

1.模型初始化,输入应当包含batch_size,学习率,输入维度,隐藏层维度,输出维度;

2.实现损失函数和激活函数的封装,便于调用;

3.撰写前向传播方法,能够根据模型参数计算出预测值;

4.撰写反向传播方法,能够根据真实值和预测值来更新模型参数;

5.构造数据集,并实现模型的训练,最后输入自己构造的样本进行预测。

2.2.1 模型初始化

值得注意的是我们一定要时刻关注模型参数的维度。

我们在这里构造一个两层的网络,假设其输入维度是n*m,隐藏层维度是h,输出层维度是1(假设我们构建一个二分类任务)

则其w1维度应该为m*h,w2维度为h*1,这样我们才能得到n*h的隐藏层,和n*1的输出层(矩阵内积)

该代码还设计了字典memory来存储中间状态的矩阵,以便进行反向传播。

class FullyConnectedNetwork:'''这是一个自定义的简单的神经网络,有一个隐藏层和一个输出层'''def __init__(self, batch_size, learning_rate, input_size, hidden_size, output_size):'''初始化神经网络:batch_size: 批量大小:learning_rate: 学习率:param input_size: 输入层大小,就是输入特征的维度(特征数量):param hidden_size: 隐藏层大小:param output_size: 输出层大小,就是输出的维度(分类数量)'''self.batch_size = batch_sizeself.learning_rate = learning_rateself.input_size = input_sizeself.hidden_size = hidden_sizeself.output_size = output_sizeself.W1 = np.random.randn(input_size, hidden_size)self.b1 = np.zeros((1, hidden_size))self.W2 = np.random.randn(hidden_size, output_size)self.b2 = np.zeros((1, output_size))self.memory = {}
2.2.2 实现损失函数和激活函数的封装

为了更加清楚得了解反向传播的过程,本文手动封装了损失函数与激活函数,并对损失函数和激活函数的导数进行提前封装。

关于损失函数还有均方差等不同的方式,激活函数也有softmax,relu等一系列激活函数。这里我们选用sigmoid以计算概率实现二分类。

    def sigmoid(self, x):'''sigmoid激活函数:param x: 特征向量矩阵:return: sigmoid(x)'''return 1 / (1 + np.exp(-x))def sigmoid_derivative(self, x):'''sigmoid激活函数的导数:param x: 特征向量矩阵:return: sigmoid(x)*(1-sigmoid(x))'''return x * (1 - x)def cross_entropy(self, y_pred, y_true):'''交叉熵损失函数:param y_pred: 预测值:param y_true: 真实值:return: 损失值'''return -np.mean(y_true * np.log(y_pred) + (1 - y_true) * np.log(1 - y_pred))def cross_entropy_derivative(self, y_pred, y_true):'''交叉熵损失函数的导数:param y_pred: 预测值:param y_true: 真实值:return: 损失函数对y_pred的导数'''return - (y_true / y_pred) + (1 - y_true) / (1 - y_pred)
2.2.3 前向传播

前向传播的本质是做矩阵内积,这个过程我们保存中间态A1,Z1和A2,Z2以方便进行反向传播

    def forward(self, X):'''前向传播:param X: 输入的特征向量矩阵:return: 返回输出层的输出值(预测值)'''Z1 = np.dot(X, self.W1) + self.b1A1 = self.sigmoid(Z1)self.memory['A1'] = A1self.memory['Z1'] = Z1Z2 = np.dot(A1, self.W2) + self.b2A2 = self.sigmoid(Z2)self.memory['A2'] = A2self.memory['Z2'] = Z2return A2
 2.3.4 反向传播

针对模型的反向传播过程涉及到链式求导的知识,需要分别计算输出层的梯度,输出层的权重和偏执梯度,隐藏层的梯度,隐藏层权重和偏执的梯度,最后根据梯度更新参数。

关于梯度下降参考这篇文章:【深度学习】梯度下降与反向传播

后序还会对神经网络中的反向传播过程进行深层次的拆解,敬请期待

这里先罗列一下整个过程:

    def backward(self, X, y_true, y_pred):'''反向传播:param X:'''dA2 = self.cross_entropy_derivative(y_pred, y_true)dZ2 = dA2 * self.sigmoid_derivative(self.memory['A2'])dW2 = 1. / self.batch_size * np.dot(self.memory['A1'].T, dZ2)db2 = 1. / self.batch_size * np.sum(dZ2, axis=0, keepdims=True)dA1 = np.dot(dZ2, self.W2.T)dZ1 = dA1 * self.sigmoid_derivative(self.memory['A1'])dW1 = 1. / self.batch_size * np.dot(X.T, dZ1)db1 = 1. / self.batch_size * np.sum(dZ1, axis=0, keepdims=True)return dW1, db1, dW2, db2def update_parameters(self, dW1, db1, dW2, db2):'''更新参数:param dW1::param db1::param dW2::param db2:'''self.W1 -= self.learning_rate * dW1self.b1 -= self.learning_rate * db1self.W2 -= self.learning_rate * dW2self.b2 -= self.learning_rate * db2
2.3.5 构建数据集

最后本文设计了如下任务:随机生层一批具有五个特征的样本,如果样本的第二个数大于第四个数,标记为1,否则为零

def build_simple():"""构建一个简单的样本:随机生成5个特征判断第二个特征是否大于第四个特征,如果大于,目标值记为1,否则记为0:return: list(1*5), list(1*1)"""simple = np.random.randint(0,10,size=(5))if simple[1] > simple[3]:y = 1else:y = 0return simple, ydef build_data(num):"""生成数据集:param num: 生成样本数量:return: 返回X,y"""X = []y = []for i in range(num):x, y_ = build_simple()X.append(x)y.append(y_)return np.array(X), np.array(y).reshape(-1,1)
2.3.6 模型训练

这里的训练轮数,样本数均可以进行修改,可以多多尝试,体验不同参数下的训练。

def main():# 定义神经网络batch_size = 10num_epochs = 10000FCNN = FullyConnectedNetwork(batch_size=batch_size, learning_rate=0.1, input_size=5, hidden_size=5, output_size=1)X,y = build_data(100000)# 训练神经网络for i in range(num_epochs):# 前向传播x_ = X[i*batch_size:(i+1)*batch_size,:]y_ = y[i*batch_size:(i+1)*batch_size,:]y_pred = FCNN.forward(x_)# 计算损失loss = FCNN.cross_entropy(y_pred, y_)# 反向传播dW1, db1, dW2, db2 = FCNN.backward(x_, y_, y_pred)# 更新参数FCNN.update_parameters(dW1, db1, dW2, db2)if i % 100 == 0:print("第{}次训练,损失为{}".format(i, loss))print('==========================')# 定义输入数据test = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [5, 8, 5, 4, 1], [2, 3, 1, 1, 1]])# 预测输出y_pred = FCNN.forward(test)y_pred = np.squeeze(y_pred)for i in range(len(y_pred)):if y_pred[i] > 0.5:y_pred[i] = 1else:y_pred[i] = 0for i in range(len(test)):print("输入数据为{}".format(test[i]))print("预测输出为{}".format(y_pred[i]))

测试结果:

可以看到我们的训练训练结果还是相当不错,这里没有写评估准确率的逻辑,学有余力的朋友可以尝试写一下。

 参考文章:

自己动手写神经网络(一)——初步搭建全连接神经网络框架-CSDN博客

神经网络——最易懂最清晰的一篇文章-CSDN博客

下面是完整代码:

'''标题: 手动实现神经网络作者:相洋同学日期:2024年3月18日
'''
import numpy as npclass FullyConnectedNetwork:'''这是一个自定义的简单的神经网络,有一个隐藏层和一个输出层'''def __init__(self, batch_size, learning_rate, input_size, hidden_size, output_size):'''初始化神经网络:batch_size: 批量大小:learning_rate: 学习率:param input_size: 输入层大小,就是输入特征的维度(特征数量):param hidden_size: 隐藏层大小:param output_size: 输出层大小,就是输出的维度(分类数量)'''self.batch_size = batch_sizeself.learning_rate = learning_rateself.input_size = input_sizeself.hidden_size = hidden_sizeself.output_size = output_sizeself.W1 = np.random.randn(input_size, hidden_size)self.b1 = np.zeros((1, hidden_size))self.W2 = np.random.randn(hidden_size, output_size)self.b2 = np.zeros((1, output_size))self.memory = {}def sigmoid(self, x):'''sigmoid激活函数:param x: 特征向量矩阵:return: sigmoid(x)'''return 1 / (1 + np.exp(-x))def sigmoid_derivative(self, x):'''sigmoid激活函数的导数:param x: 特征向量矩阵:return: sigmoid(x)*(1-sigmoid(x))'''return x * (1 - x)def cross_entropy(self, y_pred, y_true):'''交叉熵损失函数:param y_pred: 预测值:param y_true: 真实值:return: 损失值'''return -np.mean(y_true * np.log(y_pred) + (1 - y_true) * np.log(1 - y_pred))def cross_entropy_derivative(self, y_pred, y_true):'''交叉熵损失函数的导数:param y_pred: 预测值:param y_true: 真实值:return: 损失函数对y_pred的导数'''return - (y_true / y_pred) + (1 - y_true) / (1 - y_pred)def forward(self, X):'''前向传播:param X: 输入的特征向量矩阵:return: 返回输出层的输出值(预测值)'''Z1 = np.dot(X, self.W1) + self.b1A1 = self.sigmoid(Z1)self.memory['A1'] = A1self.memory['Z1'] = Z1Z2 = np.dot(A1, self.W2) + self.b2A2 = self.sigmoid(Z2)self.memory['A2'] = A2self.memory['Z2'] = Z2return A2def backward(self, X, y_true, y_pred):'''反向传播:param X:'''dA2 = self.cross_entropy_derivative(y_pred, y_true)dZ2 = dA2 * self.sigmoid_derivative(self.memory['A2'])dW2 = 1. / self.batch_size * np.dot(self.memory['A1'].T, dZ2)db2 = 1. / self.batch_size * np.sum(dZ2, axis=0, keepdims=True)dA1 = np.dot(dZ2, self.W2.T)dZ1 = dA1 * self.sigmoid_derivative(self.memory['A1'])dW1 = 1. / self.batch_size * np.dot(X.T, dZ1)db1 = 1. / self.batch_size * np.sum(dZ1, axis=0, keepdims=True)return dW1, db1, dW2, db2def update_parameters(self, dW1, db1, dW2, db2):'''更新参数:param dW1::param db1::param dW2::param db2:'''self.W1 -= self.learning_rate * dW1self.b1 -= self.learning_rate * db1self.W2 -= self.learning_rate * dW2self.b2 -= self.learning_rate * db2def build_simple():"""构建一个简单的样本:随机生成5个特征判断第二个特征是否大于第四个特征,如果大于,目标值记为1,否则记为0:return: list(1*5), list(1*1)"""simple = np.random.randint(0,10,size=(5))if simple[1] > simple[3]:y = 1else:y = 0return simple, ydef build_data(num):"""生成数据集:param num: 生成样本数量:return: 返回X,y"""X = []y = []for i in range(num):x, y_ = build_simple()X.append(x)y.append(y_)return np.array(X), np.array(y).reshape(-1,1)def main():# 定义神经网络batch_size = 10num_epochs = 10000FCNN = FullyConnectedNetwork(batch_size=batch_size, learning_rate=0.1, input_size=5, hidden_size=5, output_size=1)X,y = build_data(100000)# 训练神经网络for i in range(num_epochs):# 前向传播x_ = X[i*batch_size:(i+1)*batch_size,:]y_ = y[i*batch_size:(i+1)*batch_size,:]y_pred = FCNN.forward(x_)# 计算损失loss = FCNN.cross_entropy(y_pred, y_)# 反向传播dW1, db1, dW2, db2 = FCNN.backward(x_, y_, y_pred)# 更新参数FCNN.update_parameters(dW1, db1, dW2, db2)if i % 100 == 0:print("第{}次训练,损失为{}".format(i, loss))print('==========================')# 定义输入数据test = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [5, 8, 5, 4, 1], [2, 3, 1, 1, 1]])# 预测输出y_pred = FCNN.forward(test)y_pred = np.squeeze(y_pred)for i in range(len(y_pred)):if y_pred[i] > 0.5:y_pred[i] = 1else:y_pred[i] = 0for i in range(len(test)):print("输入数据为{}".format(test[i]))print("预测输出为{}".format(y_pred[i]))if __name__ == '__main__':main()

以上

互联网是最好的课本,实践是最好的老师,AI是最好的学习助手

行动起来,共勉

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

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

相关文章

光度立体法的简化求解(已知特殊光源方向)

原理这个博主写的很好 如何获取物体表面的法向量?好好谈谈光度立体法-CSDN博客 Photometric Stereo 光度立体三维重建(一)——介绍-CSDN博客 ​ 不过当图片较大的时候,比如4048*4000这种量级的,矩阵很大,速度要10秒,加了openmp也需要2s; …

项目中遇到的sql问题记录

有一张表,表结构及数据如下: INSERT INTO test.test_approve(approve_no, tra_date, tablename, part_dt) VALUES (approve001, 2021-02-18 00:00:00, tableA, 2024-03-18); INSERT INTO test.test_approve(approve_no, tra_date, tablename, part_dt) …

深度学习03价值学习

Q*类似于先知,知道动作的后果 价值学习是得到一个近似的价值函数

【ARM】DSTREAM上面的各个指示灯代表什么意思?

【更多软件使用问题请点击亿道电子官方网站查询】 1、 文档目标 对于DStream仿真器上面的指示灯亮灭代表的意义进行分析。 2、 问题场景 主要对于DStream仿真器的使用过程中,不同的情况下面仿真器的指示灯会进行相应的亮灭。了解一下不同指示灯的亮灭所提示的信息…

【nfs报错】rpc mount export: RPC: Unable to receive; errno = No route to host

NFS错误 问题现象解决方法 写在前面 这两天搭建几台服务器,需要使用nfs服务,于是六台选其一做服务端,其余做客户端,搭建过程写在centos7离线搭建NFS共享文件,但是访问共享时出现报错:rpc mount export: RPC…

2023 re:Invent 使用 PartyRock 和 Amazon Bedrock 安全高效构建 AI 应用程序

前言 “ Your Data , Your AI , Your Future .(你的数据,你的 AI ,你的未来。) 亚马逊云科技开发者社区为开发者们提供全球的开发技术资源。这里有技术文档、开发案例、技术专栏、培训视频、活动与竞赛等。帮助中国开发者对接世界…

GitLab/Github从头开始配置秘钥

1、下载git安装包 CNPM Binaries Mirrorhttps://registry.npmmirror.com/binary.html?pathgit-for-windows/ 拉到页面最底部选择 点进文件夹下载32位或者64位的版本,我的是64位就选择64的版本进行安装 2、傻瓜式安装 3、在相应的文件夹右键选择 UserName为你的用…

Flutter-自定义图片3D画廊

效果 需求 3D画廊效果 设计内容 StackGestureDetectorTransformPositioned数学三角函数 代码实现 具体代码大概300行 import dart:math;import package:flutter/material.dart; import package:flutter_xy/widgets/xy_app_bar.dart;import ../../r.dart;class ImageSwitc…

用C语言打造自己的Unix风格ls命令

在Unix或类Unix操作系统中,ls是一个非常基础且实用的命令,它用于列出当前目录或指定目录下的文件和子目录。下面,我们将通过C语言编写一个简化的ls命令,展示如何利用dirent.h头文件提供的函数接口实现这一功能。 c #include &quo…

【3DsMax】UVW展开——以制作牙膏盒为例

效果 步骤 1. 从网上下载牙膏盒贴图,我下载的贴图地址为(牙膏盒贴图链接) 2. 打开3DsMax,创建一个长方体,设置长宽高分别为180、45、40毫米 打开材质编辑器,点击漫反射后的按钮 双击“位图” 将材质赋予长…

阿里云云服务器ECS端口多个端口号开通教程

阿里云云服务器ECS端口多个端口号开通教程 1、登录到ECS云服务器管理控制台 2、左侧栏找到【实例与镜像】>>【实例】,找到目标ECS实例,点击实例ID进入到实例详情页 3、切换到【安全组】页面,点击右侧【配置规则】,如下图&…

HTML静态网页成品作业(HTML+CSS)——抗击疫情网页(4个页面)

🎉不定期分享源码,关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 🏷️本套采用HTMLCSS,未使用Javacsript代码,共有4个页面。 二、作品演示 三、代…

图论题目集一(代码 注解)

目录 题目一&#xff1a; 题目二&#xff1a; 题目三&#xff1a; 题目四&#xff1a; 题目五&#xff1a; 题目六&#xff1a; 题目七&#xff1a; 题目一&#xff1a; #include<iostream> #include<queue> #include<cstring> using namespace st…

<商务世界>《第15课 投标文件一般包含的子文件》

1 响应文件封面 招标文件中的响应文件封面是投标人或参与者在提交响应文件时所使用的封面设计。这个封面不仅仅是文件的外包装&#xff0c;更是投标人形象和专业素质的直观展示&#xff0c;对于给招标方留下良好的第一印象至关重要。 首先&#xff0c;响应文件封面通常会包含…

硬盘哨兵Hard Disk Sentinel Pro V6.20.0.0 便携版

Hard Disk Sentinel 是一款功能强大的硬盘监控和分析软件&#xff0c;专为 Windows 用户设计。它可以实时监测硬盘驱动器&#xff08;HDD&#xff09;、固态硬盘&#xff08;SSD&#xff09;、混合硬盘&#xff08;SSHD&#xff09;、NVMe SSD、RAID 数组和外部 RAID 盒子的健康…

uniapp可视范围高度 - 用户屏幕可操作的屏幕高度 - 适用于APP、H5@公众号、纯H5@Chrome

可视范围高度 let heightPx uni.getWindowInfo().windowHeight uni.getWindowInfo().windowTop 官方手册 uni.getWindowInfo() | uni-app官网uni-app,uniCloud,serverless,uni.getWindowInfo()https://uniapp.dcloud.net.cn/api/system/getWindowInfo.html 实测数据 uni.ge…

十、MySQL主从架构配置

一、资源配置 主库&#xff1a;192.168.134.132 从库&#xff1a;192.168.134.133 从库&#xff1a;192.168.134.134 二、主从同步基本原理&#xff1a; master用户写入数据&#xff0c;会生成event记录到binary log中&#xff0c;slave会从master读取binlog来进行数据同步…

Java安全 反序列化(1) URLDNS链原理分析

Java安全 反序列化(1) URLDNS链原理分析 文章目录 Java安全 反序列化(1) URLDNS链原理分析前置知识应用分析payload1.新建HashMap类2.新建URL类3.获取URL 的 Class对象4.通过反射访问URL内部变量5.通过反射为URL中类赋值6.调用HashMap#put方法传入key和value7.再次通过反射为UR…

面向未来的前沿人工智能监管

策制定者应该为未来十年人工智能系统更加强大的世界做好准备。这些发展可能会在人工智能科学没有根本性突破的情况下发生&#xff0c;只需扩展当今的技术以在更多数据和计算上训练更大的模型即可。 用于训练前沿人工智能模型的计算量在未来十年可能会显着增加。到 2020 年代末…

03.生命周期和工程化开发入门

一、Vue生命周期 思考&#xff1a;什么时候可以发送初始化渲染请求&#xff1f;&#xff08;越早越好&#xff09;什么时候可以开始操作dom&#xff1f;&#xff08;至少dom得渲染出来&#xff09; Vue生命周期&#xff1a;就是一个Vue实例从创建 到 销毁 的整个过程。 生命…