自制深度学习推理框架之入门基础

文章目录

一、环境配置

1.1基础环境

①cmake 3.21以上

shell 复制代码
// 升级cmake
sudo wget https://cmake.org/files/v3.23/cmake-3.23.1.tar.gz
sudo tar -zxvf cmake-3.23.1.tar.gz
cd cmake-3.23.1
./configure
make -j8
make install

②C++ 17

③gfortran:gfortran 是 GNU Compiler Collection (GCC) 中的 Fortran 编译器。它支持 Fortran 77、Fortran 90、Fortran 95、Fortran 2003、Fortran 2008 以及部分 Fortran 2018 标准。gfortran 是一个开源编译器,广泛用于科学计算、数值模拟和工程应用中。

④OpenMP:OpenMP(Open Multi-Processing)是一组编译器指令、库例程和环境变量,用于并行编程,主要用于共享内存多处理器系统的C、C++和Fortran程序。一般编译器自带。

环境配置选择:

①WSL + docker(提供的有) 简单就是耗内存

②虚拟机Ubuntu 自己配环境 也简单毕竟网上都有

③双系统(Linux) + docker

1.2数学基础库

BLAS: BLAS是用于数值计算中基本线性代数操作的标准API。它提供了一系列底层的向量和矩阵操作。

下载地址:http://www.netlib.org/blas/blas.tgz

shell 复制代码
gfortran -c  -O3  -fPIC  *.f   # 编译所有的 .f 文件,生成 .o文件   加上了-fPIC
gcc -shared *.o -fPIC -o  libblas.so
sudo cp libblas.so /usr/local/lib/
ar rv libblas.a *.o  # 链接所有的 .o文件,生成 .a 文件  
sudo cp libblas.a /usr/local/lib  # 将库文件复制到系统库目录

②LAPACK :LAPACK是用于解决线性代数问题的库,建立在BLAS之上。它提供了更高级的线性代数例程。

下载地址:http://www.netlib.org/lapack/

shell 复制代码
// 法一:源码编译
cd lapack-3.12/
mkdir build
cd build
cp ../make.inc.example make.inc
cmake
sudo cmake --build . --target install

// apt
sudo apt-get install libblas-dev liblapack-dev

Armadillo :Armadillo是一个用于C++的高效线性代数库,旨在提供类似于MATLAB的高层次语法,同时保持接近底层BLAS和LAPACK库的性能。与BLAS和LAPACK兼容,支持使用底层高效库加速计算。

需要先安装OpenBLAS ,OpenBLAS 是一个高度优化的开源 BLAS(Basic Linear Algebra Subprograms)库,也包含了一些 LAPACK 的实现。它通过使用多线程和平台特定的优化来显著提升线性代数运算的性能。OpenBLAS 支持多种架构,包括 x86、ARM 和 PowerPC 等。

下载地址:https://sourceforge.net/projects/arma/

shell 复制代码
apt update
apt install cmake libopenblas-dev liblapack-dev libarpack2-dev libsuperlu-dev
mkdir build

cd armadillo文件夹
mkdir build
cd build
cmake ..
make -j8
make install

1.3测试与日志

benchmark:Google Benchmark是一个由Google开发的高性能微基准测试库,用于测量C++代码的执行时间。它能够帮助开发者准确评估代码性能、检测潜在的性能瓶颈,并在开发过程中进行性能调优。

下载地址:https://github.com/google/benchmark

shell 复制代码
// 使用Ubuntu的包管理工具apt来安装预编译的版本:
sudo apt update
sudo apt install -y libbenchmark-dev
// 这个包包含了Google Benchmark库和头文件。

②glog:glog是Google开发的C++库,用于高效地记录日志。它提供了便捷的接口来记录不同级别的日志信息(如信息、警告、错误和致命错误)。

下载地址:https://github.com/google/glog

shell 复制代码
mkdir build
cd build
cmake ..
make -j8
make install

③GTest: GTest是Google开发的C++测试框架,用于编写和运行单元测试。

下载地址:https://github.com/google/googletest

shell 复制代码
mkdir build
cd build
cmake ..
make -j8
make install

1.4项目介绍

下载地址:https://github.com/zjhellofss/KuiperInfer\](https://github.com/zjhellofss/KuiperInfer)

课程学习下载地址:https://github.com/zjhellofss/kuiperdatawhale

深度学习推理框架用于对已经训练完成的神经网络模型文件进行加载 ,并根据模型文件中的网络结构和权重参数 对输入图像进行预测。换句话说,深度学习推理框架就是将深度学习训练框架PytorchTensorFlow中训练完成的模型,移植到中心侧和端侧并且在运行时高效执行 。另外,与深度学习训练框架不同的是,推理框架没有梯度后向传播的过程,因为在推理阶段模型的权重已经固定,不需要利用后向传播技术进一步进行调整

例如对于一个Resnet分类网络的模型,深度学习推理框架先对模型文件中的网络结构进行读取和载入,再读取模型文件中的权重参数和其他参数、属性信息填入到Resnet网络结构中,随后推理框架将不同的图像放入到计算图的输入 中,并执行预测过程 ,从而得到其归属的类别。以下的图示是我对如上内容的总结:

KuiperInfer深度学习推理框架可以分为以下的几个模块:

  1. Operator:深度学习计算图中的计算节点,包含以下的几个部分:
    • 存储输入输出的张量 ,用于存放深度学习中各层的输入输出。比如对于一个Convolution层,需要一部分空间来保存计算的输入和输出。
    • 计算节点的类型和名称 ,计算节点类型可以有Convolution, Relu, Maxpooling等,计算节点的名称是唯一的 ,用来区分任意一个节点,可以是Convolution_1, Convolution_2等。
    • 计算节点的参数信息 ,例如卷积中的步长、卷积核的大小等。
    • 计算节点的权重信息 ,例如卷积节点中的weight, bias权重。
  2. Graph: 有多个Operator串联得到的有向无环图,规定了各个计算节点(Operator)执行的流程和顺序
  3. Layer:计算节点中运算的具体执行者Layer类先读取输入张量中的数据,然后对输入张量进行计算,得到的结果存放到计算节点的输出张量中,当然,不同的算子中Layer的计算过程会不一致
  4. Tensor: 用于存放多维数据的数据结构,方便数据在计算节点之间传递,同时该结构也封装矩阵乘、点积等与矩阵相关的基本操作。

以下的图示是对如上的模块的总结,每个节点都从输入张量input_data中读取数据,并调用该节点对应的Layer计算对应的结果,最后再将结果放入到output_data中。整个计算图第一个节点的输入也是计算图全局的输入,同时,最后一个节点的输出也是整个计算图的全局输出。

二、WSL2安装

2.1 WSL安装

安装前还需要启用使用适用于Linux的 Windows 子系统的设置。

参考连接:https://blog.csdn.net/qq_41596730/article/details/136220519

2.2 WSL指令

通过Windows cmd或powershell命令执行此操作。

  • WSL重启命令
shell 复制代码
 wsl --shutdown
  • 查看WSL名称以及状态
shell 复制代码
wsl -l -v
  • 删除、注销WSL
bash 复制代码
wsl --unregister Ubuntu(名字)

三、Docker

3.1docker安装

在Win上使用docker比较推荐:docker桌面版本 + WSL。不太推荐在WSL里面配置docker,因为很多的有关docker的systemd指令使用不了,而且在安装后开启不了docker服务,坑比较多。

1.添加Docker官方GPG密钥

shell 复制代码
curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -

2.添加Docker软件源,执行以下命令来添加Docker的软件源:

shell 复制代码
sudo add-apt-repository "deb [arch=amd64] http://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"

3.安装docker,执行以下命令来安装Docker:

shell 复制代码
sudo apt-get install docker-ce docker-ce-cli containerd.io

4.启动docker服务

shell 复制代码
sudo service docker start      // 启动Docker服务

sudo /etc/init.d/docker start  // 不能使用systemd的指令

sudo service docker stop       // 停止Docker服务

sudo service docker restart    // 重启Docker服务

5.验证

shell 复制代码
docker run hello-world

若一切正常,你将会看到类似如下显示:

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
719385e32844: Pull complete 
Digest: sha256:fc6cf906cbfa013e80938cdf0bb199fbdbb86d6e3e013783e5a766f50f5dbce0
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

3.2 SysVinit 和 systemd

下面来讲一讲为啥wsl中无法使用systemd指令,这两个都是在WSL安装docker了解到的常见的系统和服务管理器,它们负责在操作系统启动时初始化系统和管理系统进程。在wsl采用SysVinit,与市面上主流采用systemd不一样,很多的有关docker的systemd指令使用不了,但是可以与之对应的SysVinit

3.2.1 SysVinit

SysVinit 是一种较旧的初始化系统,最早起源于 UNIX 系统。它基于脚本(通常是 /etc/init.d 目录中的脚本)来启动和停止系统中的各种服务。

SysVinit 使用一个叫做 init 的进程作为系统的第一个进程(PID 1),init 进程从 /etc/inittab 文件中读取配置,按照指定的运行级别(runlevel)启动和管理服务,其简单、轻量,易于理解和手动管理,但是并行性差(启动和停止服务时是串行的),脚本管理复杂,难以实现依赖管理。

3.2.2 systemd

systemd 是一种较新的初始化系统和服务管理器,旨在替代传统的 SysVinit。它提供了并行启动、依赖管理和更丰富的功能。systemd 使用一个叫做 systemd 的进程作为系统的第一个进程(PID 1)。它读取 unit 文件来管理服务、挂载点、设备等。这些 unit 文件通常位于 /etc/systemd/system//usr/lib/systemd/system/ 目录中。systemd 通过并行启动服务和智能化的依赖管理,大大提高了系统启动的速度和可靠性。其启动速度快,支持并行启动、服务依赖管理、日志收集和更多高级功能,但是较为复杂,学习曲线陡峭,一些传统用户对其强大的集成功能有所抵触。

随着 Linux 生态的发展,systemd 已经成为许多主流 Linux 发行版的默认初始化系统,逐渐取代了 SysVinit。以下是其指令的对比。

3.3 docker指令

Docker 是一个用于创建和管理容器化应用程序的工具。以下是一些常用的 Docker 指令:

3.3.1基础指令
  • docker --version:显示 Docker 的版本信息。
  • docker info:显示 Docker 系统的详细信息。
3.3.2 镜像相关
  • docker images:列出本地的 Docker 镜像。
  • docker pull <image>:从 Docker 仓库中拉取镜像。
  • docker build -t <image_name> . :从 Dockerfile 构建镜像,并标记为 <image_name>
  • docker rmi <image>:删除指定的镜像。
3.3.3容器相关
  • docker ps:列出正在运行的容器。
  • docker ps -a:列出所有的容器,包括停止的。
  • docker run -it <image>:以交互模式运行容器。
  • docker run -d <image>:后台运行容器。
  • docker exec -it <container> /bin/bash:进入正在运行的容器。
  • docker stop <container>:停止正在运行的容器。
  • docker start <container>:启动已停止的容器。
  • docker restart <container>:重启容器。
  • docker rm <container>:删除停止的容器。
3.3.4 网络和卷
  • docker network ls:列出 Docker 网络。
  • docker volume ls:列出 Docker 卷。
  • docker network create <network_name>:创建自定义网络。
  • docker volume create <volume_name>:创建数据卷。
3.3.5 其他
  • docker logs <container>:查看容器的日志。
  • docker inspect <container_or_image>:查看容器或镜像的详细信息。
  • docker stats:显示容器的实时资源使用情况。

四、Armadillo 库

Armadillo 是一个用于 C++ 的高效线性代数库,提供了类似 MATLAB 的语法和功能。它支持矩阵和向量的基本运算、矩阵分解、统计分析等。以下是一些 Armadillo 中常用的基础运算:

  1. 矩阵和向量的创建
cpp 复制代码
#include <armadillo>
using namespace arma;

int main() {
    mat I = eye(3, 3);   // 创建 3x3 单位矩阵
    mat Z = zeros(4, 4); // 创建 4x4 全零矩阵
    mat O = ones(2, 3);  // 创建 2x3 全一矩阵
    mat A = "1.0 2.0 3.0; 4.0 5.0 6.0"; // 从列表初始化矩阵
    vec v = {1, 2, 3};   // 创建 3x1 列向量
    return 0;
}
  1. 矩阵和向量的运算
cpp 复制代码
#include <armadillo>
using namespace arma;

int main() {
    mat A = "1.0 2.0 3.0; 4.0 5.0 6.0";
    mat B = "7.0 8.0 9.0; 10.0 11.0 12.0";

    mat C = A + B;     // 矩阵加法
    mat D = A - B;     // 矩阵减法
    mat E = A * B.t(); // 矩阵乘法,注意 B.t() 是 B 的转置
    mat F = A % B;     // 矩阵点积
    mat G = 2.0 * A;   // 矩阵的数乘
    mat H = exp(A);    // 逐元素指数运算
    return 0;
}

以下是armadillo矩阵库的文档地址,以供我们在学习的过程中查阅:armadillo documentation.

五、Google Test单元测试

Google Test(也称为 gtest)是一个开源的 C++ 单元测试框架,提供了一系列工具来帮助开发者编写和运行测试。它支持自动化测试、方便的测试用例管理、易于阅读的测试输出以及集成测试和多线程测试。

假设我们有一个简单的数学函数 Add,希望对其进行测试

cpp 复制代码
int Add(int a, int b) {
    return a + b;
}

可以为它编写以下测试

cpp 复制代码
#include <gtest/gtest.h>

// 测试函数 Add
TEST(MathFunctionsTest, AddTest) {
    EXPECT_EQ(Add(1, 2), 3);     // 期望 Add(1, 2) 的结果为 3
    EXPECT_EQ(Add(-1, -1), -2);  // 期望 Add(-1, -1) 的结果为 -2
    EXPECT_EQ(Add(0, 0), 0);     // 期望 Add(0, 0) 的结果为 0
}

int main(int argc, char **argv) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

Google Test常用宏

  • 断言宏 :用于检查条件是否为真,断言失败时测试会立即停止
    • ASSERT_TRUE(condition) / ASSERT_FALSE(condition)
    • ASSERT_EQ(val1, val2) / ASSERT_NE(val1, val2)
    • ASSERT_LT(val1, val2) / ASSERT_LE(val1, val2)
    • ASSERT_GT(val1, val2) / ASSERT_GE(val1, val2)
  • 期望宏 :用于检查条件是否为真,期望失败时测试继续进行
    • EXPECT_TRUE(condition) / EXPECT_FALSE(condition)
    • EXPECT_EQ(val1, val2) / EXPECT_NE(val1, val2)
    • EXPECT_LT(val1, val2) / EXPECT_LE(val1, val2)
    • EXPECT_GT(val1, val2) / EXPECT_GE(val1, val2)
  • 测试套件和测试用例
    • TEST(TestSuiteName, TestName):定义一个测试用例
    • TEST_F(TestFixtureName, TestName):定义一个使用测试固件的测试用例

六、glog日志

Google 的 glog 库是一个高效、灵活的 C++ 日志库,用于记录日志信息。glog 提供了多种日志级别(INFOWARNINGERRORFATAL)和强大的日志记录功能。使用示例:

cpp 复制代码
#include <glog/logging.h>

int main(int argc, char* argv[]) {
    // 初始化 glog, argv[0]日志信息来源于哪个程序
    google::InitGoogleLogging(argv[0]);

    // 设置日志输出目录
    google::SetLogDestination(google::GLOG_INFO, "./logs/info_");
    google::SetLogDestination(google::GLOG_WARNING, "./logs/warning_");
    google::SetLogDestination(google::GLOG_ERROR, "./logs/error_");

    // 日志示例
    LOG(INFO) << "This is an info message";
    LOG(WARNING) << "This is a warning message";
    LOG(ERROR) << "This is an error message";

    // FATAL 日志会终止程序
    // LOG(FATAL) << "This is a fatal message";

    return 0;
}

在使用 glog 之前,需要初始化它。初始化时传入 argv[0],即程序的名称。程序结束时可以调用google::ShutdownGoogleLogging() 进行清理。

glog 提供了四个日志级别:

  • LOG(INFO):普通信息
  • LOG(WARNING):警告信息
  • LOG(ERROR):错误信息
  • LOG(FATAL):致命错误,会终止程序

可以使用 CHECK 宏进行条件检查,如果条件不满足,则会输出日志并终止程序

cpp 复制代码
int x = 5;
CHECK_EQ(x, 5) << "x should be 5";  // 如果 x 不等于 5,会输出日志并终止程序

日志的其它参数

cpp 复制代码
// 设置日志输出目录
FLAGS_log_dir = "/path/to/log/directory";

// 设置最小日志级别
FLAGS_minloglevel = 0;  // 0=INFO, 1=WARNING, 2=ERROR, 3=FATAL

// 禁止日志输出到终端
FLAGS_logtostderr = false;  // 设置为 true 则日志输出到终端
FLAGS_alsologtostderr = false;  // 设置为 true 则日志同时输出到终端和文件

// 日志文件大小限制 超过该大小会创建一个新的日志文件
FLAGS_max_log_size = 10;  // 单个日志文件最大为 10 MB
相关推荐
江_小_白18 分钟前
自动驾驶之激光雷达
人工智能·机器学习·自动驾驶
yusaisai大鱼2 小时前
TensorFlow如何调用GPU?
人工智能·tensorflow
珠海新立电子科技有限公司4 小时前
FPC柔性线路板与智能生活的融合
人工智能·生活·制造
IT古董4 小时前
【机器学习】机器学习中用到的高等数学知识-8. 图论 (Graph Theory)
人工智能·机器学习·图论
曼城周杰伦4 小时前
自然语言处理:第六十三章 阿里Qwen2 & 2.5系列
人工智能·阿里云·语言模型·自然语言处理·chatgpt·nlp·gpt-3
余炜yw5 小时前
【LSTM实战】跨越千年,赋诗成文:用LSTM重现唐诗的韵律与情感
人工智能·rnn·深度学习
莫叫石榴姐5 小时前
数据科学与SQL:组距分组分析 | 区间分布问题
大数据·人工智能·sql·深度学习·算法·机器学习·数据挖掘
96775 小时前
对抗样本存在的原因
深度学习
如若1236 小时前
利用 `OpenCV` 和 `Matplotlib` 库进行图像读取、颜色空间转换、掩膜创建、颜色替换
人工智能·opencv·matplotlib
YRr YRr6 小时前
深度学习:神经网络中的损失函数的使用
人工智能·深度学习·神经网络