GhostNet图像分类模型
1. 方案简介
1.1 算法流程介绍
1. 模型背景
GhostNet 是一款轻量级卷积神经网络模型,由 华为诺亚方舟实验室(Noah’s Ark Lab) 于 2020年在论文《GhostNet: More Features from Cheap Operations》中首次提出,并发表在 CVPR 2020 上。该模型旨在为资源受限设备(如移动终端、嵌入式系统、AI芯片等)提供更高效率的神经网络结构,在降低模型计算复杂度和参数规模的同时,尽可能保持较高的分类精度。GhostNet 针对传统卷积神经网络中的“计算冗余”问题提出了一种创新的解决方案:Ghost Module(幽灵模块)。研究者观察到,神经网络中许多特征图之间存在较强的冗余性,因此并不需要使用高成本的标准卷积操作来生成所有特征图。GhostNet 通过少量真实卷积生成部分“原始特征”,然后使用一系列**低成本的线性操作(如深度可分离卷积)**在这些原始特征的基础上“扩展”出更多“Ghost”特征图,从而大大减少计算量。
GhostNet 的主要特点如下:
- 参数量和计算量大幅减少,特别适合部署在 FPGA、NPU 等定制硬件上;
- 兼容主流轻量化框架(如 MobileNet、ShuffleNet 等),可作为替代结构无缝集成;
- 结构设计简单,易于硬件并行优化和流水线加速;
- 在 ImageNet 等标准数据集上,GhostNet 在同等或更低计算开销下实现了优于同类模型的分类精度。
GhostNet 击败了其他 SOTA 轻量级 CNN,例如 MobileNetV3 和 FBNet。

2. 算法流程图
graph TD
A[输入图像] --> B[前处理]
B --> C[Stage1: 标准卷积]
C --> D[Stage2-4: Ghost模块堆叠]
D --> E[全局平均池化]
E --> F[全连接层]
F --> G[后处理]
3. 前处理(Host端CPU处理)
前处理流程:
graph LR
A[输入图像路径] --> B[图像加载]
B --> C[尺寸变换]
C --> D[张量转换]
D --> E[DMA准备]
subgraph 前处理流程
B -->|cv::IMREAD_COLOR| B1["示例:/data/dog.jpg"]
C --> C1[Resize短边224px] --> C2["示例:1280x720→256x256"]
C2 --> C3[rCenterCrop] --> C4["示例:224x224"]
D --> D1["数据类型转换 uint8→float32"]
D1 --> D2["归一化 /255.0"]
D2 --> D3["通道转换 BGR→RGB"]
D3 --> D4["添加Batch维度 [1,3,224,224]"]
E --> E1["配置DMA参数"] --> E2["Host→NPU数据传输"]
end
style B fill:#F8CECC,stroke:#B85450
style C fill:#D5E8D4,stroke:#82B366
style D fill:#DAE8FC,stroke:#6C8EBF
style E fill:#FFF2CC,stroke:#D6B656
关键操作:
PicPre img(img_path, cv::IMREAD_COLOR); // 图像加载
img.Resize(resize_hw, PicPre::SHORT_SIDE).rCenterCrop(crop_hw); // 调整尺寸+中心裁剪
Tensor img_tensor = CvMat2Tensor(img.dst_img, network); // OpenCV Mat转Tensor
dmaInit(run_sim, netinfo.ImageMake_on, img_tensor, device); // DMA传输初始化
具体处理流程:
- 图像加载:通过OpenCV读取原始图像(
cv::IMREAD_COLOR保持RGB格式)- 示例:输入
img_path为"/data/dog.jpg"
- 示例:输入
- 尺寸变换:
Resize:将短边缩放到指定尺寸(如256px),保持长宽比rCenterCrop:居中裁剪到模型输入尺寸(如224x224)- 示例:原始图像1280x720 → Resize为256x256 → 中心裁剪224x224
- 张量转换:
CvMat2Tensor完成:- 数据类型转换(uint8 → float32)
- 归一化(如除以255.0)
- 通道顺序转换(BGR → RGB,若模型需要)
- 添加Batch维度(shape变为[1,3,224,224])
- DMA准备:
dmaInit配置DMA传输参数,将Tensor数据从Host内存复制到NPU设备内存
4. 后处理(Host端CPU处理)
后处理机制:
graph LR
A[NPU输出张量] --> B[数据回传]
B --> C[结果解析]
C --> D[概率计算]
D --> E[结果筛选]
E --> F[可视化处理]
subgraph 后处理流程
B --> B1["to(HostDevice::MemRegion())"]
B1 --> B2["示例:shape=[1,1000]"]
C --> C1["(float*)host_tensor_class.data().cptr()"]
D --> D1["softmax转换"] --> D2["示例:[2.1,-1.3,0.8]→[0.85,0.02,0.11]"]
E --> E1["topK(5)"] --> E2["示例:[(232,0.92),(123,0.07)]"]
F --> F1["cv::putText绘制"] --> F2["示例:Labrador Retriever : 0.92"]
end
style B fill:#F8CECC,stroke:#B85450
style C fill:#D5E8D4,stroke:#82B366
style D fill:#DAE8FC,stroke:#6C8EBF
style E fill:#FFF2CC,stroke:#D6B656
style F fill:#E1D5E7,stroke:#9673A6
关键操作:
// 数据回传
auto host_tensor_class = output_tensors[0].to(HostDevice::MemRegion());
// 结果解析
auto outputs_class = (float*)host_tensor_class.data().cptr();
// Softmax归一化
auto softmax_class = softmax(outputs_class, 1, 0, N_CLASS);
// Top-K筛选
auto topk_res = topK(softmax_class, 5);
// 结果可视化
cv::putText(img.ori_img, text.str(), ...);
具体处理流程:
- 数据回传:
to(HostDevice::MemRegion())将NPU输出张量复制回Host内存- 示例:输出张量shape为[1,1000],表示ImageNet分类结果
- 概率计算:
softmax将原始logits转换为概率分布- 示例:输出
[2.1, -1.3, 0.8,...]→[0.85, 0.02, 0.11,...]
- 结果筛选:
topK提取前5个最高概率的类别- 示例:返回
[(232, 0.92), (123, 0.07), ...](类别ID+概率)
- 可视化处理:
- 通过OpenCV将结果绘制到原始图像
- 示例:在图像左上角添加"Labrador Retriever : 0.92"等文字
1.2 硬件资源分配
1. 硬件映射策略
graph TD
subgraph CPU后端
E1[Softmax] --> E2[Top-K筛选]
E2 --> E3[可视化渲染]
end
subgraph NPU
D1[Ghost主卷积] --> D2[深度卷积]
D2 --> D3[特征融合]
end
subgraph ImageMake模块
C1[数据归一化] --> C2[排布转换]
C2 --> C3[通道对齐]
end
subgraph CPU前端
B1[图像加载] --> B2[动态Resize]
B2 --> B3[中心裁剪]
end
A[输入图像] --> B[CPU前处理]
B --> C[ImageMake硬件加速]
C --> D[NPU推理]
D --> E[CPU后处理]
2. 硬件资源分配方案总表
| 处理阶段 | 主要操作 | 硬件分配 | 代码对应位置 | 优化措施 |
|---|---|---|---|---|
| 1. 前处理部分 | ||||
| - 图像加载 | 读取图像文件,格式转换 | CPU | PicPre img(img_path, cv::IMREAD_COLOR) |
|
| - 动态Resize | 短边缩放(保持长宽比) | CPU | img.Resize(resize_hw) |
|
| - 中心裁剪 | 裁剪至模型输入尺寸 | CPU | .rCenterCrop(crop_hw) |
|
| 2. ImageMake硬件加速 | ||||
| - 数据归一化 | y=(x–mean)*scale 代替传统归一化 |
FPGA | dmaInit(..., mean, scale) |
硬件定点化运算 |
| - 排布转换 | 四周Padding(最大255像素) | FPGA | 自动完成 | 动态配置Padding参数 |
| - 通道对齐 | 补通道至4对齐,位宽转换 | FPGA | AlignAxis硬件替代 |
自动通道填充策略 |
| 3. NPU计算部分 | ||||
| - Ghost主卷积 | 常规卷积生成部分特征 | NPU | session.forward()中的Conv2D |
核融合(Conv+BN+ReLU6) |
| - 深度卷积 | 分组卷积生成剩余特征 | NPU | session.forward()中的DWConv |
|
| - 特征拼接 | 合并主卷积与廉价操作输出 | NPU | 编译器自动优化 | 张量拼接硬件指令加速 |
| 4. 后处理优化 | ||||
| - Softmax | 将输出转换为概率分布 | CPU | softmax(outputs_class,...) |
|
| - Top-K筛选 | 提取前5类别 | CPU | topK(softmax_class,5) |
|
| - 可视化渲染 | 添加分类结果文字标签 | CPU | cv::putText() |
3. 硬件-软件交互示意图
sequenceDiagram
participant Host as Host CPU
participant NPU as NPU/PL
participant DDR as 设备内存
Host->>Host: 图像加载/Resize/Crop
Host->>DDR: DMA传输预处理数据
NPU->>NPU: 执行模型推理
DDR->>Host: DMA回传输出张量
Host->>Host: Softmax/TopK处理
Host->>Host: 结果可视化与保存
2. 模型导出说明
在部署 GhostNet 网络时,整体计算流程可分为两部分:Host 侧(CPU)处理 与 Device 侧(硬件加速)处理。
2.1 Host 侧处理(前后处理)
GhostNet 部署流程中的前处理和后处理通常由 Host CPU 完成,主要包括:
- 前处理
- 图像加载:如通过 OpenCV / PIL 等方式读取图像;
- 动态 Resize:根据模型输入要求进行等比缩放;
- 中心裁剪:对缩放后的图像进行中心裁剪,保持输入维度一致;
- 后处理:
- Softmax:对分类 logits 输出进行归一化;
- Top-K 筛选:从 softmax 结果中选出概率最高的前 K 类别。
2.2 Device 侧处理(GhostNet 主干)
1)基础卷积算子:
- 常规卷积:
nn.Conv2d直接实现 - 深度可分离卷积:通过
nn.Conv2d的groups参数实现分组卷积 - 批归一化:
nn.BatchNorm2d直接实现 - 激活函数:
- ReLU:
nn.ReLU或F.relu - hard_sigmoid:通过
F.relu6(x + 3.) / 6.数学表达式实现
- ReLU:
2)Ghost模块特有算子:
- 主卷积:
nn.Conv2d+nn.BatchNorm2d+nn.ReLU组合 - 廉价操作:分组
nn.Conv2d+nn.BatchNorm2d+nn.ReLU组合 - 特征拼接:
torch.cat直接实现
3)注意力机制算子:
- 全局平均池化:
nn.AdaptiveAvgPool2d(1) - 通道压缩/扩展:
nn.Conv2d实现的1x1卷积 - 门控函数:
hard_sigmoid函数实现
4)特殊操作函数:
_make_divisible:通过数学运算int(v + divisor / 2) // divisor * divisor实现hard_sigmoid:通过F.relu6(x + 3.) / 6.实现
5)结构模块:
- GhostBottleneck:组合多个
nn.Conv2d、nn.BatchNorm2d和nn.ReLU - ConvBnAct:
nn.Conv2d+nn.BatchNorm2d+nn.ReLU顺序组合
GhostNet 与 Icraft 硬件支持算子对应表
| GhostNet 功能组件 | PyTorch 实现方式 | Icraft 对应算子组合 |
|---|---|---|
| 常规卷积 | nn.Conv2d |
Conv2d |
| 分组卷积 | nn.Conv2d(groups=...) |
Conv2d(设置 groups 参数) |
| 深度可分离卷积 | nn.Conv2d(groups=in_channels) |
Conv2d |
| 批归一化 | nn.BatchNorm2d |
BatchNorm2d |
| ReLU 激活函数 | nn.ReLU 或 F.relu |
ReLU 或 Hardtanh 或 Swish |
| HardSigmoid | F.relu6(x + 3.) / 6. |
Hardsigmoid |
| HardSwish | x * F.relu6(x + 3.) / 6. |
Hardswish |
| Ghost 主卷积模块 | Conv2d + BN + ReLU |
Conv2d + BatchNorm2d + ReLU |
| Ghost 廉价操作模块 | 分组Conv2d + BN + ReLU |
Conv2d + BatchNorm2d + ReLU(groups>1) |
| 特征拼接 | torch.cat([x1, x2], dim=1) |
Concat |
| AdaptiveAvgPool2d(1) | nn.AdaptiveAvgPool2d(1) |
AvgPool2d(kernel=size=feature map) |
| 1x1卷积(压缩/扩展) | nn.Conv2d(kernel_size=1) |
Conv2d |
| 通道注意力门控 | HardSigmoid |
Hardsigmoid |
| _make_divisible | int(v + divisor / 2) // divisor * divisor |
非算子,由 前处理代码逻辑实现 |
| GhostBottleneck | 多个 Conv + BN + ReLU + Add + SE 模块组合 | Conv2d + BatchNorm2d + ReLU + Concat + Add |
| ConvBnAct 快速模块 | Conv2d + BatchNorm2d + ReLU |
Conv2d + BatchNorm2d + ReLU |
| 通道间注意力(SE) | GAP → Conv1x1 压缩 → ReLU → Conv1x1 扩展 → HardSigmoid | AvgPool2d + Conv2d + ReLU + Conv2d + Hardsigmoid |
2.3 导出过程和1_scripts中的脚本运行说明
直接加载模型,只导出GhostNet 主干,前后处理不保存在在导出模型中。
├── 0_infer.py >框架下推断脚本
├── 1_save.py >trace模型保存onnx/pt脚本
├── 2_save_infer.py >onnx/pt推断脚本
- 0_infer.py
- 可以推理一张图(或其他格式数据)并得到最终结果,目的是为了快速跑通推理代码,为后续剥离/移植前后处理做准备。为了实现快速不能用整个数据集作为输入。
- 用法: python 0_infer.py
- 1_save.py
- 保存模型。
- 导出的模型默认存放至
2_compile/fmodel路径下,如../2_compile/fmodel/ghostnet_224x224.pt - 用法: python 1_save.py
- 2_save_infer.py
- 用修改后保存的模型做前向推理,目的是为了与0_infer的结果对比,确保导出静态模型的正确性。
- 用法: python 2_save_infer.py
3. cpu处理部分c++工程移植说明
3.1 cpu环节实现的功能
CPU(Host 端)承担了模型部署与推理流程中大量关键的前处理、后处理和控制逻辑。
1)配置与初始化(控制逻辑)
- 读取 YAML 配置文件:
- 包括模型路径、仿真与实卡运行开关、是否可视化输出、是否保存结果等。
- 加载图像路径、类别标签文件、网络输入尺寸等信息。
- 构建模型路径、打开设备并加载模型:
- 根据配置动态拼接 JSON 模型路径和 RAW 参数路径。
- 判断是否在仿真(sim)还是实际设备(实卡)上运行。
- 加载网络结构并初始化
Session和Device。
2)前处理(Preprocess)
这是典型的 在 CPU 上运行的图像前处理操作,目标是将原始图片转为模型输入格式:
- 图像读取:
- 使用 OpenCV 加载图像为 BGR 格式。
- 图像缩放与裁剪:
- 首先根据短边进行等比例缩放(
Resize(resize_hw, PicPre::SHORT_SIDE))。 - 然后执行中心裁剪(
rCenterCrop(crop_hw))获得定尺寸输入。
- 首先根据短边进行等比例缩放(
- 图像转换为张量:
- 将裁剪后的图像转为 NCHW 格式、float 类型张量,用于输入模型(
CvMat2Tensor())。
- 将裁剪后的图像转为 NCHW 格式、float 类型张量,用于输入模型(
- 输入 DMA 传输:
- 将 CPU 中的图像张量传输给设备端执行推理(
dmaInit())。
- 将 CPU 中的图像张量传输给设备端执行推理(
3)模型执行控制
- 执行模型前向推理:
- 由 CPU 调用
session.forward()启动一次推理流程,控制输入和获取输出。 - 返回的是在设备端计算完成的结果张量。
- 由 CPU 调用
- 设备端输出转回 Host 内存:
- 将设备端输出张量(float*)搬运回 CPU 内存空间供后处理使用(
to(HostDevice::MemRegion()))。
- 将设备端输出张量(float*)搬运回 CPU 内存空间供后处理使用(
4)后处理(Postprocess)
这些操作也是由 CPU 进行的,主要包括:
- Softmax 计算:
- 将模型输出转化为类别概率分布。
- Top-K 筛选:
- 从 softmax 输出中选取概率最高的前 5 个类别及其概率。
- 可视化与输出处理:
- 在图像上用 OpenCV
putText写入类别名称和概率。 - 根据平台(Windows 或 Linux)显示图像、保存图像或保存分类结果为 TXT 文件。
- 在图像上用 OpenCV
3.2 各环节耗时
命令行输入icraft run ./config/GhostNet_8.toml,以仿真int8量化后的模型为例,单一输入图像对理时间输如下:
// icraft run ./config/GhostNet_8.toml
[05/18/25 13:33:38.384] [I] network wall: 0.0139 ms
Load Json Time: 74.988 ms
Load Raw Time: 60.878 ms
opId: 0 opType: Input wall: 20.548 ms mem: 0.000 ms hard: 0.000 ms other: 20.548 ms
opId: 1 opType: Resize wall: 2.022 ms mem: 0.000 ms hard: 0.000 ms other: 2.022 ms
opId: 846 opType: ImageMake wall: 8.844 ms mem: 0.000 ms hard: 0.000 ms other: 8.844 ms
opId: 1300 opType: HardOp wall: 801.274 ms mem: 0.000 ms hard: 0.000 ms other: 801.274 ms
opId: 1101 opType: Cast wall: 0.101 ms mem: 0.000 ms hard: 0.000 ms other: 0.101 ms
opId: 1102 opType: PruneAxis wall: 0.092 ms mem: 0.000 ms hard: 0.000 ms other: 0.092 ms
opId: 437 opType: Output wall: 0.012 ms mem: 0.000 ms hard: 0.000 ms other: 0.012 ms
当前仿真系统的cpu配置情况
D:\dtpw\Desktop\icraft_workplace\ghostnet\2_compile>wmic cpu get name,numberofcores,numberoflogicalprocessors,maxclockspeed
MaxClockSpeed Name NumberOfCores NumberOfLogicalProcessors
1800 AMD Ryzen 7 4800U with Radeon Graphics 8 16
分析
- 核心推理耗时在
HardOp,即模型主干部分(801ms),这是真正的前向计算阶段; - 前处理部分(Resize + ImageMake)耗时约 10.8ms;
- 类型转换和输出阶段耗时非常低(<0.2ms);
- 输入阶段 Input 的 20ms 大概率包括从文件加载图像时间;
- 虽然
network wall显示为0.0139ms,但它不是整个模型推理的时间,而是启动模型调度器的一个时间片段,不代表整体执行。
4. ICRAFT编译
编译通过
-
使用运行时仿真得到的仿真效果截图或数据,需提供下述表格
比赛报告中要求有:框架动态图推理、导出静态模型推理、解析组件仿真、量化组件仿真、指令组件仿真的仿真效果截图或数据。对于解析组件仿真、量化组件仿真、指令组件仿真等中间过程仿真该怎么做?文档中并没写明,只能参考[docs/Part 2_2 icraft tools.md · spiger/TUTORIAL - 码云 - 开源中国](https://gitee.com/mxh-spiger/tutorial-runtime/blob/tt3.7.1/docs/Part 2_2 icraft tools.md#34-结果分析)中的
icraft run工作简单输出每个组件的仿真。
1. 框架动态图推理
-
输入图片
-
-
框架推理
-
python 0_infer.py -
输出结果:
-
D:\dtpw\Desktop\icraft_workplace\ghostnet\1_scripts>python 0_infer.py input size = torch.Size([3, 224, 224]) output size = torch.Size([1000]) --------Classification results---------- Samoyed: 0.6540 //萨摩耶 Pomeranian: 0.0577 //博美犬 Eskimo dog: 0.0179 //爱斯基摩犬 white wolf: 0.0108 //白狼 Arctic fox: 0.0107 //北极狐
-
-
2. 导出静态模型推理
-
输入图片:同上
-
导出静态模型推理
-
python 2_save_infer.py -
D:\dtpw\Desktop\icraft_workplace\ghostnet\1_scripts>python 2_save_infer.py input size = torch.Size([3, 224, 224]) GhostNet模型加载完成 output size = torch.Size([1000]) --------分类结果-------- Samoyed: 0.6540 Pomeranian: 0.0577 Eskimo dog: 0.0179 white wolf: 0.0108 Arctic fox: 0.0107
-
3. 解析组件仿真
-
命令:
-
# GhostNet_8.toml文件中 [run] log_time = true log_io = true dump_format = "SFB" json = "../3_deploy/modelzoo/GhostNet/imodel/8/GhostNet_parsed.json" raw = "../3_deploy/modelzoo/GhostNet/imodel/8/GhostNet_parsed.raw" input = "./qtset/imagenet/ILSVRC2012_val_00000003.JPEG" backends = "Host"
-
命令行运行
\ghostnet\2_compile>icraft run ./config/GhostNet_8.toml
-
-
输出截图
-
4. 量化组件仿真
-
命令:
-
# GhostNet_8.toml文件中 [run] log_time = true log_io = true dump_format = "SFB" json = "../3_deploy/modelzoo/GhostNet/imodel/8/GhostNet_quantized.json" raw = "../3_deploy/modelzoo/GhostNet/imodel/8/GhostNet_quantized.raw" input = "./qtset/imagenet/ILSVRC2012_val_00000003.JPEG" backends = "Host"
-
命令行运行
\ghostnet\2_compile>icraft run ./config/GhostNet_8.toml
-
-
输出截图
-
5. 指令组件仿真
-
命令:
-
# GhostNet_8.toml文件中 [run] log_time = true log_io = true dump_format = "SFB" json = "../3_deploy/modelzoo/GhostNet/imodel/8/GhostNet_BY.json" raw = "../3_deploy/modelzoo/GhostNet/imodel/8/GhostNet_BY.raw" input = "./qtset/imagenet/ILSVRC2012_val_00000003.JPEG" backends = "Host"
-
命令行运行
\ghostnet\2_compile>icraft run ./config/GhostNet_8.toml
-
-
输出截图
-
6. 完整的c++工程仿真输出(c++ runtime)
-
在
3_deploy/modelzoo/GhostNet/build_win目录下运行:Release\GhostNet.exe ..\cfg\GhostNet_8.yaml #注意,windows中一般用\,而/可以用但有些时假会报错 Release\GhostNet.exe ..\cfg\GhostNet_16.yaml Release\GhostNet.exe ..\cfg\GhostNet_mix.yaml
-
输出截图
-
- 分类结果在右上角,用蓝色字体标出,可放大验证。
-
7. 8bit,16bit,混合精度的精度测试结果
- 分别运行以下命令,得到8bit,16bit,混合精度模型在imagenet2012val的前5000张图像上的分类结果。
Release\GhostNet.exe ..\cfg\GhostNet_test_8.yaml #注意,windows中一般用\,而/可以用但有些时假会报错
Release\GhostNet.exe ..\cfg\GhostNet_test_16.yaml
Release\GhostNet.exe ..\cfg\GhostNet_test_mix.yaml
结果分别保存在
3_deploy\modelzoo\GhostNet\io\output\8\val_5000
3_deploy\modelzoo\GhostNet\io\output\16\val_5000
3_deploy\modelzoo\GhostNet\io\output\mix\val_5000
-
运行精度测试命令
Release\GhostNet.exe ..\cfg\GhostNet_test_8.yaml后,使用metrics测试脚本,根据保存的测试结果计算出精度指标。metrics测试脚本使用与配置说明:
- 精度测试脚本存放于:https://gitee.com/mxh-spiger/benchmark
- 本模型使用
./topn/topn_acc.py - 输入命令
benchmark-master\topn>python .\topn_acc.py D:\dtpw\Desktop\icraft_workplace\ghostnet\3_deploy\modelzoo\GhostNet\io\output\8\val_5000 --gtPath ImageNet2012_val5000.txt resDir字段设置为预测结果存放路径
-
精度结果
-
GhostNet input shape 精度 int8 [1, 3, 224, 224] top1: 64.92%;top5: 86.1% int16 [1, 3, 224, 224] top1: 68.26%;top5: 88.26%
-
由于混合精度在编译过程不能通过,负责人回复为组件bug,所以没有混合精度的测试结果。
具体bug见:
https://gitee.com/mxh-spiger/fwb/issues/IC8QZ3
8. 编译过程的一些特殊说明
-
注意,windows中的命令行中,一般用左,右/可以用但有些时假会报错
-
编译命令注意事项
-
cd 3_deploy\modelzoo\GhostNet mkdir -p build_win cd build_win cmake -G "Visual Studio 17 2022" -A x64 .. # 不要直接cmake .. cmake --build . --config Release
-
不能直接使用
cmake ..命令,因为如果系统上多种编译器,且默认cmake使用的编译器不是visualstudio2022会出错,所以使用-G "Visual Studio 17 2022" -A x64命令,让cmake使用visualstudio2022编译器编译。
-