基于vpk180边缘场景下分布式神经网络训练模型部署

本项目目标在于针对边缘场景实现P2P的分布式训练过程,设计方案为将神经网络训练过程对应的裸机程序部署在了PS端的ARM Cortex-A72核上,传输方案采用开发板板载的GTM收发器硬件资源通过外部QSFP-DD光模块光传输至对端,最终完成了设计目标。

整个项目的实现细节可分为如下几个重要部分,同时整个系统的实现框图及实际部署图如下图所示:

欲部署神经网络模型的介绍及相关公式的推导;
PS端ARM Cortex-A72裸机训练程序部署;
PL端基于aurora64b66b自定义协议的GTM板间光传输工程搭建;
PL与PS间通过AXI-FULL总线进行数据交互。

内部模块系统框图如下:

接下来,将按照整个项目几个重要部分详细论述:

一、神经网络模型的介绍及相关公式的推导

1.1 神经网络模型相关介绍

本次推导的网络模型结构如下,其中模型层数共3层,各层神经元个数可重定义。以输入层神经元2个,隐藏层神将元3个,输出层神将元1个为例,对应模型图如下:

1.2 反向传播过程公式推导

二、PS端ARM Cortex-A72裸机训练程序部署

对应代码如下所示:

#include <stdio.h>
#include <stdlib.h>
#include "platform.h"
#include "xil_printf.h"
#include "xgpio.h"
#include "xparameters.h"
#include "xstatus.h"
#include "math.h"
#include "string.h"
#include "xtimer_config.h"
#include "xiltimer.h"
#include "xil_types.h"
#include "time.h"// 模型参数结构体
typedef struct {int input_size; // 输入层的大小int hidden_size; // 隐藏层的大小int output_size; // 输出层的大小double *W1; // 第一层的权重double *b1; // 第一层的偏置double *W2; // 第二层的权重double *b2; // 第二层的偏置
} NeuralNet;//函数声明
double sigmoid(double x);
void init_net(NeuralNet *net);
void forward(NeuralNet *net, double *input, double *output);
void backward(NeuralNet *net, double *input, double *target, double learning_rate);
void train(NeuralNet *net, double *inputs, double *targets, int num_epochs, int num_inputs, double learning_rate);
void predict(NeuralNet *net, double *input, double *output);//gpio0 baseaddr 
//gpio0用了两个通道,通道一作为输入用于传输channel_up的拉高情况
//gpio0的通道二作为输出,用于传输将参数写到Bram完成的标志
#define XPAR_AXI_GPIO_0_BASEADDR 0x80000000
//gpio1 baseaddr
//gpio1只用了一个通道,作为输入,用于传输将参数写到另一块板子BRAM中完成的标志
#define XPAR_XGPIO_1_BASEADDR 0x80010000
//AXI_Bram baseaddr
#define XPAR_AXI_BRAM_0_BASEADDRESS 0x80020000 //结束地址为0x80022000 //gpio初始化用到的结构体变量
XGpio Axi_gpio0;
XGpio_Config *Xgpio_cfgptr0;
XGpio Axi_gpio1;
XGpio_Config *Xgpio_cfgptr1;//gpio初始化函数声明
void gpio_init();int main()
{init_platform();gpio_init();//u32 *LitePtr = (u32*) XPAR_AXI_BRAM_0_BASEADDRESS;//u32 wrdata = 0;while(XGpio_DiscreteRead( &Axi_gpio0, 1) == 0x00){}//channel_up link flagprintf("Aurora is linked!\n\r");u64 begin_time;u64 end_time;long int time_dif;float time_cost;XTime_GetTime(&begin_time);//计时开始时间NeuralNet net;net.input_size = 2;net.hidden_size = 3;net.output_size = 1;init_net(&net);double inputs[] = {0,0,0,1,1,0,1,1};//训练数据集double targets[] = {0,1,1,0};//将数据集分成两份//double inputs[]={0,0,0,1};//double targets[]={0,1};train(&net, inputs, targets, 1, 4, 0.001);//训练一次      //打印各层参数,共有13个参数for(int i=0;i<net.input_size*net.hidden_size;i++){printf("The first layer of training weight parameters are as follows:W1[%d]=%f\n\r",i,net.W1[i]);//6个参数}for(int i=0;i<net.hidden_size;i++){printf("The first layer training bias parameters are as follows:b1[%d]=%f\n\r",i,net.b1[i]);//3个参数}for(int i=0;i<net.hidden_size*net.output_size;i++){printf("The second layer training weight parameters are as follows:W2[%d]=%f\n\r",i,net.W2[i]);//3个参数}for(int i=0;i<net.output_size;i++){printf("The second layer training bias parameters are as follows:b2[%d]=%f\n\r",i,net.b2[i]);//1个参数}// //对训练完的参数进行量化,量化成32位有符号定点数*8192// int w1_storage[6]={0,0,0,0,0,0};// int b1_storage[3]={0,0,0};// int w2_storage[3]={0,0,0};// int b2_storage[1]={0};int32_t *LitePtr = (u32*) XPAR_AXI_BRAM_0_BASEADDRESS;//将存储空间指向BRAM的基地址// //w1量化// for(int i=0;i<net.input_size*net.hidden_size;i++){//     w1_storage[i] = net.W1[i]*8192;// }// //w1写入Bram 0-5地址// for(int i=0;i<net.input_size*net.hidden_size;i++){//      *(LitePtr+i) =  w1_storage[i];//0-5// }// //b1量化// for(int i=0;i<net.hidden_size;i++){//     b1_storage[i] = net.b1[i]*8192;// }// //b1写入BRAM 6-8地址// *(LitePtr+6) = b1_storage[0];// *(LitePtr+7) = b1_storage[1];// *(LitePtr+8) = b1_storage[2];// //w2量化// for(int i=0;i<net.hidden_size*net.output_size;i++){//     w2_storage[i] = net.W2[i]*8192;// }// //w2写入Bram 9-11地址// *(LitePtr+9) =  w2_storage[0];// *(LitePtr+10) = w2_storage[1];// *(LitePtr+11) = w2_storage[2];// //b2量化// for(int i=0;i<net.output_size;i++){//     b2_storage[i] = net.b2[i]*8192;// }// //将量化完的b2写入12地址// *(LitePtr+12) = b2_storage[0];*LitePtr = net.W1[0]*8192;*(LitePtr+1) = net.W1[1]*8192;//利用AXI_GPIO通知PL端已写完XGpio_DiscreteWrite(&Axi_gpio0, 2, 0x01);//另外一块板已收到数据,并将数据写入PS中while(XGpio_DiscreteRead( &Axi_gpio1, 1) == 0x00){}//write to ps end//这部分会将收到的数据存到指定的地址,接着来作reduce操作,做完reduce操作计数结束// //read data from Bram// LitePtr = (u32*) XPAR_AXI_BRAM_0_BASEADDRESS;// for(int i=9;i<18;i++){//     printf("write data is %d",*(LitePtr+i));// }XTime_GetTime(&end_time);time_dif = end_time - begin_time;time_cost =(float)(time_dif)/COUNTS_PER_SECOND;printf("time_cost = %.4fs",time_cost);// for(int i=0;i<9;i++){//     *LitePtr++ = wrdata++;//0-9// }// XGpio_DiscreteWrite(&Axi_gpio0, 2, 0x01);// while(XGpio_DiscreteRead( &Axi_gpio1, 1) == 0x00){}//write to ps end// //read data from Bram// LitePtr = (u32*) XPAR_AXI_BRAM_0_BASEADDRESS;// for(int i=9;i<18;i++){//     printf("write data is %d",*(LitePtr+i));// }cleanup_platform();return 0;
}//gpio init
void gpio_init(){Xgpio_cfgptr0 = XGpio_LookupConfig(XPAR_AXI_GPIO_0_BASEADDR);XGpio_CfgInitialize(&Axi_gpio0,Xgpio_cfgptr0,Xgpio_cfgptr0->BaseAddress);XGpio_SetDataDirection(&Axi_gpio0, 1,1);//channel 1:inputXGpio_SetDataDirection(&Axi_gpio0, 2,0);//channel 2:outputXgpio_cfgptr1 = XGpio_LookupConfig(XPAR_AXI_GPIO_1_BASEADDR);XGpio_CfgInitialize(&Axi_gpio1,Xgpio_cfgptr1,Xgpio_cfgptr1->BaseAddress);XGpio_SetDataDirection(&Axi_gpio1, 1,1);//channel 1:input//XGpio_SetDataDirection(&Axi_gpio0, 2,0);//channel 2:output
}double sigmoid(double x) {return 1 / (1 + exp(-x));
}// 初始化权重和偏置参数
void init_net(NeuralNet *net) {net->W1 = (double*) malloc(net->input_size * net->hidden_size * sizeof(double));net->b1 = (double*) malloc(net->hidden_size * sizeof(double));net->W2 = (double*) malloc(net->hidden_size * net->output_size * sizeof(double));net->b2 = (double*) malloc(net->output_size * sizeof(double));//srand(time(NULL));for (int i = 0; i < net->input_size * net->hidden_size; i++) {net->W1[i] = (double)rand()/RAND_MAX;}for (int i = 0; i < net->hidden_size * net->output_size; i++) {net->W2[i] = (double)rand()/RAND_MAX;}for (int i = 0; i < net->hidden_size; i++) {net->b1[i] = (double)rand()/RAND_MAX;}for (int i = 0; i < net->output_size; i++) {net->b2[i] = (double)rand()/RAND_MAX;}
}//前向传播
void forward(NeuralNet *net, double *input, double *output) {double *h1 = (double*) malloc(net->hidden_size * sizeof(double));double *h2 = (double*) malloc(net->output_size * sizeof(double));// 第一层的计算for (int i = 0; i < net->hidden_size; i++) {h1[i] = 0;for(int j = 0; j < net->input_size; j++){h1[i] += input[j] * net->W1[j*net->hidden_size+i];}h1[i] += net->b1[i];h1[i] = tanh(h1[i]);}// 第二层的计算for (int i = 0; i < net->output_size; i++) {h2[i] = 0;for (int j = 0; j < net->hidden_size; j++) {h2[i] += h1[j] * net->W2[j*net->output_size+i];}h2[i] += net->b2[i];}// 输出的激活函数    for (int i = 0; i < net->output_size; i++) {output[i] = sigmoid(h2[i]);}free(h1);free(h2);
}// 反向传播
void backward(NeuralNet *net, double *input, double *target, double learning_rate) {double *h1 = (double*) malloc(net->hidden_size * sizeof(double));double *h2 = (double*) malloc(net->output_size * sizeof(double));double *delta1 = (double*) malloc(net->hidden_size * sizeof(double));double *delta2 = (double*) malloc(net->output_size * sizeof(double));// 第一层的计算for (int i = 0; i < net->hidden_size; i++) {h1[i] = 0;for (int j = 0; j < net->input_size; j++) {h1[i] += input[j] * net->W1[j*net->hidden_size+i];}h1[i] += net->b1[i];h1[i] = tanh(h1[i]);}// 第二层的计算for (int i = 0; i < net->output_size; i++) {h2[i] = 0;for (int j = 0; j < net->hidden_size; j++) {h2[i] += h1[j] * net->W2[j*net->output_size+i];}h2[i] += net->b2[i];}// 输出激活函数    for (int i = 0; i < net->output_size; i++) {h2[i] = sigmoid(h2[i]);delta2[i] = h2[i] * (1-h2[i]) * (target[i]-h2[i]);}// 第一层的误差传递  for (int i = 0; i < net->hidden_size; i++) {delta1[i] = 0;for (int j = 0; j < net->output_size; j++) {delta1[i] += delta2[j] * net->W2[i*net->output_size+j];}delta1[i] *= (1-h1[i]) * (1+h1[i]);}// 权重和偏置的更新for (int i = 0; i < net->hidden_size; i++) {for (int j = 0; j < net->input_size; j++) {net->W1[j*net->hidden_size+i] += learning_rate * delta1[i] * input[j];}net->b1[i] += learning_rate * delta1[i];}for (int i = 0; i < net->output_size; i++) {for (int j = 0; j < net->hidden_size; j++) {net->W2[j*net->output_size+i] += learning_rate * delta2[i] * h1[j];}net->b2[i] += learning_rate * delta2[i];}free(h1);free(h2);free(delta1);free(delta2);
}// 训练神经网络
void train(NeuralNet *net, double *inputs, double *targets, int num_epochs, int num_inputs, double learning_rate) {for (int i = 0; i < num_epochs; i++) {for (int j = 0; j < num_inputs; j++) {double *input = &inputs[j*net->input_size];double *target = &targets[j*net->output_size];backward(net, input, target, learning_rate);}}
}// 使用神经网络进行预测
void predict(NeuralNet *net, double *input, double *output) {forward(net, input, output);
}

三、PL端基于aurora64b66b自定义协议的GTM板间光传输工程搭建

这部分采用xilinx官方提供的aurora64B66B IP,IP配置如下:

配置完成后点击run block automation后的界面如下:

通过阅读官方手册可知,CIPS与ZYNQ系列不同点在于即使不同PS端也要将其加入工程,因为由它启动硬件系统的初始化工作。

这里主要注意在example基础上,将参考时钟、初始化时钟及复位信号处理好,对QSFP-DD模块自回环测试或者仿真观察lane_up及channel_up信号拉高即完成对aurora64b66b自定义协议的配置工作。拉高界面如下:

四、PL与PS间通过AXI-FULL总线进行数据交互

同ZYNQ系列芯片类似,PL与PS端交互有对应的AXI接口,本次选用主机LPD接口:

整个数据交互通过AXI-GPIO IP和AXI BRAM Controller配合真双端口RAM实现(一侧连接到PS,另一端连接到PL),整个交互流程为PL端channel up信号拉高后,通过AXI-GPIO发送给PS端;PS端收到channel up信号并判断本机训练完后,将梯度数据存入BRAM中,存完告知PL端;PL端收到反馈信号后,将读出的数据传给aurora顶层模块,同时通过fifo进行跨时钟域,并在握手成功的情况下与上~empty信号启动读数据;对板收到数据后同样写入BRAM,并由PS读出,读取数据完成后,将数据做reduce操作,完成后继续训练,这样整个过程便完成了。

对应的模块框图及中间结果记录如下:

建链成功标志:

ps向BRAM写完的标志:

存入参数:

五、训练时间对比

训练时间统计如下:

2个数据集单板训练时间为0.000006s,4个数据集单板训练时间为0.0000095S。训练完写入bram过程时间为:0.0000006s。通过回环将数据传入另一块板子ram中整个通讯过程消耗的时间为:0.0000020s。则说明即使在数据量较小的情况下,两板间通信带来的开销仍对分布式训练时间加速有益处。

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

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

相关文章

ros项目dual_arm_pick-place(urdf文件可视化查看)

前言 一直想写一些项目的讲解&#xff0c;今天&#xff08;2024.12.05&#xff09;可以说正式开始了。 dual_arm_pick-place项目&#xff0c;是关于两个机械臂协同传递物品。 正文 这次的话&#xff0c;给大家讲一下里面的urdf文件。 这篇文章主要来看一下项目中的urdf文件…

spring boot 配置文件加载的加载和使用

文章目录 添加配置信息使用ConfigurationProperties注入属性使用Value注入属性两种方式对比分析自定义配置添加自定义配置加载并使用配置信息 添加配置信息 在实际开发中&#xff0c;应用程序的配置文件可能会变得非常复杂&#xff0c;属性的数量也会很多。为了更好地组织和管…

17. Threejs案例-Three.js创建多个立方体

17. Threejs案例-Three.js创建多个立方体 实现效果 知识点 WebGLRenderer (WebGL渲染器) WebGLRenderer 是 Three.js 中用于渲染 WebGL 场景的核心类。它负责将场景中的对象渲染到画布上。 构造器 new THREE.WebGLRenderer(parameters) 参数类型描述parametersObject可选…

QNX的内存布局和启动入口

参考资料: QNX官网文档 内存布局 添加图片注释,不超过 140 字(可选) 查看系统内存布局 # pidin syspage=asinfo Header size=0x00000108, Total Size=0x0000d1b0, #Cpu=8, Type=257 Section:asinfo offset:0x0000bdf0 size:0x00000d00 elsize:0x000000200000

【成功解决】:VS2019(Visual Studio 2019)遇到E2870问题:此配置中不支持 128 位浮点类型

起因:项目中需要用json来操作数据,就引了cJSON库(cJSON.h和cJSON.c文件),但是发现编译报错如下 E2870 此配置中不支持 128 位浮点类型 test0 ...\usr\include\x86_64-linux-gnu\bits\floatn.h 75 然后先新建了个工程来检查问题(甚至在这之前还以为是cjson…

【LeetCode】122.买卖股票的最佳时机II

文章目录 题目链接&#xff1a;题目描述&#xff1a;解题思路一&#xff08;贪心算法&#xff09;&#xff1a;解体思路二&#xff08;动态规划&#xff09;&#xff1a; 题目链接&#xff1a; 122.买卖股票的最佳时机II 题目描述&#xff1a; 解题思路一&#xff08;贪心算法…

【源码解读】SpringMMVC执行流程

直接进入主题&#xff0c;当我们执行一条请求的时候&#xff0c;是会进入到org.springframework.web.servlet.DispatcherServlet类的doDispatch方法中 这个方法是从HandleMapping中获取对应的Handle 其实就是得到我们需要执行的某个方法 接下来判断这个mapperhandle是否为空 如…

【时时三省】(C语言基础)结构体的变量定义和初始化

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 有了结构体类型&#xff0c;那如果定义变量&#xff0c;其实很简单。 示例&#xff1a; 这个就是结构体变量的基础创建 初始化 比如里面只剩一个s3 s3里面有两个成员 第一个给c的值 第二个给…

与火山引擎合作深化,观测云携一站式监控解决方案登陆万有商城

近日&#xff0c;观测云正式宣布入驻火山引擎的万有商城。作为一款全栈式数据观测与分析平台&#xff0c;观测云的加入不仅丰富了火山引擎生态&#xff0c;也为广大企业用户带来了更便捷的数字化工具&#xff0c;助力企业快速实现业务监控与优化。 从全球覆盖到本地深耕&#x…

【2025最新计算机毕业设计】基于SpringBoot+Vue旅游路线推荐系统【提供源码+答辩PPT+文档+项目部署】

作者简介&#xff1a;✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流。✌ 主要内容&#xff1a;&#x1f31f;Java项目、Python项目、前端项目、PHP、ASP.NET、人工智能…

Docker安装Valkey教程(图文教程)

本章教程,介绍如何使用Docker启动Valkey的具体步骤。 一、什么是Valkey? Valkey 是一个高性能的开源键值存储系统,支持多种工作负载,包括缓存、消息队列和主数据库。它可以作为独立守护进程或集群运行,并提供复制和高可用性功能。 Valkey 原生支持多种数据类型,如字符串、…

漫画之家系统:Spring Boot技术下的漫画社区构建

3 系统分析 3.1系统可行性分析 3.1.1经济可行性 由于本系统是作为毕业设计系统&#xff0c;且系统本身存在一些技术层面的缺陷&#xff0c;并不能直接用于商业用途&#xff0c;只想要通过该系统的开发提高自身学术水平&#xff0c;不需要特定服务器等额外花费。所有创造及工作过…

10a大电流稳压芯片_24v转3.3v稳压芯片,高效率DC-DC变换器10A输出电流芯片-AH1514

### AH1514——高性能的大电流稳压芯片 在现代电子电路设计中&#xff0c;对于能够满足大电流、高效率转换以及稳定电压输出的芯片需求日益增长。AH1514芯片作为一款出色的DC-DC变换器&#xff0c;以其独特的性能特点&#xff0c;在众多应用场景中展现出了卓越的优势. ### 一…

三轴云台之数字变倍功能篇

一、数字变倍的定义 数字变倍是指通过电子方式对图像进行放大或缩小的过程。与光学变焦不同&#xff0c;数字变焦不会改变镜头的焦距&#xff0c;而是通过图像处理算法对图像进行放大或缩小&#xff0c;从而改变图像的视野范围。 二、三轴云台数字变倍的特点 灵活性&#xff…

K8S的ingress介绍和安装ingress

1 ingress介绍 1.1 ingress架构图 1.2 ingress相关概念 ingress诞生背景&#xff1a; 在没有ingress之前&#xff0c;只能基于svc的NodePort或者LoadBalancer实现内部的pod对外访问&#xff0c;如果遇到多个服务要监听80端口时。 很明显无论哪种类型都无法实现&#xff0c;如…

无公网IP实现飞牛云手机APP远程连接飞牛云NAS管理传输文件

文章目录 前言1. 本地连接测试2. 飞牛云安装Cpolar3. 配置公网连接地址4. 飞牛云APP连接测试5. 固定APP远程地址6. 固定APP地址测试 前言 随着科技的不断发展&#xff0c;远程访问和控制内网设备的需求日益增加。在众多内网设备中&#xff0c;网络附加存储&#xff08;NAS&…

第一篇:k8s架构与组件详解

没有那么多花里胡哨&#xff0c;直接进行一个K8s架构与组件的学习。 一、K8s架构 在Master通常上包括 kube-apiserver、etcd 存储、kube-controller-manager、cloud-controller-manager、kube-scheduler 和用于 K8s 服务的 DNS 服务器&#xff08;插件&#xff09;。这些对集群…

Leetcode—3001. 捕获黑皇后需要的最少移动次数【中等】

2024每日刷题&#xff08;201&#xff09; Leetcode—3001. 捕获黑皇后需要的最少移动次数 C实现代码 class Solution { public:int minMovesToCaptureTheQueen(int a, int b, int c, int d, int e, int f) {// 车跟皇后在同一行if(a e) {// 象是否挡在车和皇后中间return (…

visual studio2019开发过程中遇到的问题和有帮助的插件

文章目录 1. 注释中有中文导致报错2. 打开一个vs2013或者vs2010等老的项目兼容性3. LNK2019 unresolved external symbol main referenced in function __tmainCRTStartup4. image watch插件/扩展使用 1. 注释中有中文导致报错 C4819 The file contains a character that cann…

如何在PortainerCE中创建NextCloud网盘并随时随地管理文件

文章目录 前言1. 在PortainerCE中创建NextCloud容器2. 公网远程访问本地NextCloud容器2.1 内网穿透工具安装3.2 创建远程连接公网地址 3. 固定NextCloud私有云盘公网地址 前言 大家好&#xff01;今天我们要来聊聊如何在本地使用Portainer CE的可视化界面创建一个属于你自己的…