Skip to content

Commit

Permalink
Merge pull request #13 from Tsumugii24/main
Browse files Browse the repository at this point in the history
update chapter 5
  • Loading branch information
hscspring authored Oct 27, 2024
2 parents cd99460 + a6bb803 commit 077d581
Show file tree
Hide file tree
Showing 13 changed files with 1,154 additions and 141 deletions.
48 changes: 36 additions & 12 deletions docs/chapter5/5.1 模型格式.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@

#### 5.1.1 ckpt

`.ckpt`文件格式通常用于保存模型的检查点(checkpoint),以便在训练过程中定期保存模型的状态,便于在意外中断后恢复训练
`.ckpt` 文件格式通常用于保存模型的检查点(`checkpoint`),以便在训练过程中定期保存模型的状态,便于在意外中断后恢复训练或使用中间的模型状态进行后续操作。例如在论文 [The Llama 3 Herd of Models]([2407.21783 (arxiv.org)](https://arxiv.org/pdf/2407.21783)) 中便提到LLaMA3系列模型的训练使用了 `NVIDIA H100` 的GPU集群,而在GPU出现故障后,就会马上启动容灾策略,从上一个 `checkpoint` 中恢复,继续训练

例如,使用 TensorFlow 训练的模型通常会保存为 `.ckpt` 格式。[Pytorch](https://geek-docs.com/pytorch/pytorch-top-tutorials/1000100_pytorch_index.html) 框架中也经常使用 `.ckpt` 作为模型文件格式。[Pytorch](https://geek-docs.com/pytorch/pytorch-top-tutorials/1000100_pytorch_index.html) 提供了更简单和更高层次的API,用于训练和管理深度学习模型。`.ckpt` 文件保存了模型的参数和优化器的状态,并且通常还包含训练的元数据信息。
例如,使用 `TensorFlow` 训练的模型通常会保存为 `.ckpt` 格式。[Pytorch](https://geek-docs.com/pytorch/pytorch-top-tutorials/1000100_pytorch_index.html) 框架中也经常使用 `.ckpt` 作为模型文件格式。[Pytorch](https://geek-docs.com/pytorch/pytorch-top-tutorials/1000100_pytorch_index.html) 提供了更简单和更高层次的 `API`,用于训练和管理深度学习模型。`.ckpt` 文件保存了模型的参数和优化器的状态,并且通常还包含训练的元数据信息。

```python
import tensorflow as tf
Expand Down Expand Up @@ -150,7 +150,7 @@ loaded_model.load_state_dict(torch.load('model.pth'))

```

在上面的示例中,我们首先定义了一个简单的神经网络模型,然后将其保存为` .pth` 文件,文件名为model.pth。接下来,我们创建了一个新的模型实例 `loaded_model` ,并使用 `load_state_dict()` 函数加载了 `.pth` 文件中的模型参数。
在上面的示例中,我们首先定义了一个简单的神经网络模型,然后将其保存为` .pth` 文件,文件名为 `model.pth`。接下来,我们创建了一个新的模型实例 `loaded_model` ,并使用 `load_state_dict()` 函数加载了 `.pth` 文件中的模型参数。

保存的 `.pth` 文件可以用于加载模型并进行推理。下面是一个使用 `.pth` 文件进行推理的示例:

Expand All @@ -167,7 +167,7 @@ print(output)

#### 5.1.4 bin

`.bin`文件格式在一些深度学习框架中被使用,例如 `Hugging Face``Transformers` 库通常将模型保存为 `.bin` 格式,它可以用来存储任意类型的数据。
`.bin` 文件格式是适用范围最广的,数据和模型权重以二进制的形式存储,在很多深度学习框架中被广泛使用,例如 `Hugging Face``Transformers` 库通常将模型保存为 `.bin` 格式,它可以用来存储任意类型的数据。下面是示例代码:

```python
from transformers import BertModel
Expand All @@ -187,10 +187,20 @@ model = BertModel.from_pretrained('bert_model')

#### 5.1.5 safetensors

`safetensors`是一种由 `Hugging Face` 推出的一种新型安全模型存储格式,强调安全性和高效性,尤其在分布式训练和推理场景中被广泛使用。其特别关注模型安全性、隐私保护和快速加载。它仅包含模型的权重参数,而不包括执行代码,这样可以减少模型文件大小,提高加载速度。
`safetensors` 是一种由 `Hugging Face` 推出的一种新型安全模型存储格式,强调安全性和高效性,尤其在分布式训练和推理场景中被广泛使用。其特别关注模型安全性、隐私保护和快速加载。它仅包含模型的权重参数,而不包括执行代码,这样可以减少模型文件大小,提高加载速度。其以更加紧凑、跨框架的方式存储 `Dict[str, Tensor]` ,主要存储的内容为 `tensor` 的名字(字符串)及内容(权重)。

![img](./images/fig5-9.webp)

其官网对文件格式的详细内容进行了解释,本质上就是一个JSON文件加上若干binary形式的buffer。对于tensor而言,它只存储了数据类型、形状、对应的数据区域起点和终点。因此它只支持存储dense and contiguous tensor,也就是稠密张量(没有stride,没有view等信息)。本质上它就像存储了一大块连续的内存,甚至可以直接把文件映射到内存里(使用Python的`mmap`模块)。

如果熟悉 `text-to-image` 领域的读者可能会很熟悉,现在的 `Stable Diffusion` 的各种开源文生图模型绝大多数都是以 `safetensors` 格式保存的,如图所示。

![image-20241020203054947](./images/fig5-6.png)

而更为早期的文生图模型可能还有一些保留了 `.ckpt` 格式,但由于可能会导致恶意代码的注入而没有`safetensor` 安全而逐渐被过渡。关于 `Stable Diffusion``SafeTensors vs CheckPoint` 的更详细的讨论可以进一步参考 [SafeTensors vs Ckpt for Stable Diffusion](https://blog.trendaitools.com/safetensors-vs-ckpt/)

```python
# 用SDXL举例
# SDXL举例
import torch
from diffusers import StableDiffusionXLPipeline, UNet2DConditionModel, EulerDiscreteScheduler
from huggingface_hub import hf_hub_download
Expand All @@ -215,24 +225,38 @@ pipe("A girl smiling", num_inference_steps=4, guidance_scale=0).images[0].save("



当然现在 `Hugging Face` 上的大多数主流大语言模型 `Large Language Model` 的权重文件也都是以 `safetensors` 保存的,对于 `LLM` 来说,使用 `Transformers library` 加载模型的代码会更简单。

```python
# Load model directly
from transformers import AutoTokenizer, AutoModelForCausalLM

tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3.2-1B")
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-3.2-1B")
```



![image-20241020204213070](./images/fig5-7.png)

![image-20241020204306628](./images/fig5-8.png)

#### 5.1.6 Netron 模型可视化工具介绍

![fig5-1](./images/fig5-1.png)

Netron 是一个开源的神经网络模型可视化工具,支持多种模型格式的可视化,包括 ONNXTensorFlowKerasCaffePyTorch 以及 Core ML 等。
`Netron` 是一个开源的神经网络模型可视化工具,支持多种模型格式的可视化,包括 `ONNX``TensorFlow``Keras``Caffe``PyTorch ` 等。

**使用 Netron 可视化模型:**

1. 下载并安装 Netron [Netron 官网](https://netron.app)
2. 打开 Netron,加载需要可视化的模型文件
1. [Netron 官网](https://netron.app)下载并安装 Netron;
2. 打开 Netron,加载需要可视化的模型文件
3. 在 Netron 界面中浏览和分析模型的结构。

**yolov5m**的网络结构可视化如图所示
**yolov5m**为例,其网络结构可视化如下图所示

![fig5-1](./images/fig5-2.png)

通过 Netron,可以方便地查看模型的各层结构和参数,有助于理解模型的内部工作原理,并进行调试和优化。
通过 `Netron`,可以方便地查看模型的各层结构和参数,有助于理解模型的内部工作原理,并进行调试和优化。

在后续章节中,我们将深入探讨如何将不同格式的模型转换为通用的中间表示(IR),并在不同的推理引擎中部署这些模型。
在后续章节中,我们将深入探讨如何将不同格式的模型转换为通用的中间表示(`IR`),并在不同的推理引擎中部署这些模型。
84 changes: 64 additions & 20 deletions docs/chapter5/5.2 通用模型表示.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
### 5.2 通用模型表示

在深度学习领域,`ONNX`(Open Neural Network Exchange)是一种通用的模型表示格式。它旨在提高不同深度学习框架之间的互操作性,使得模型可以在不同的框架和硬件平台上运行。
在深度学习领域,`ONNX``Open Neural Network Exchange`)是一种通用的模型表示格式。它旨在提高不同深度学习框架之间的互操作性,使得模型可以在不同的框架和硬件平台上运行。

#### 5.2.1 ONNX简介

Expand Down Expand Up @@ -31,21 +31,21 @@ print(onnx_model)
- `Tensor`:描述了模型中的数据,包括权重和输入输出数据。
- `Operator`:定义了模型中的计算操作。

`ONNX` 使用 `protobuf`(Protocol Buffers)作为其主要协议,来定义模型的结构和数据。这使得 `ONNX` 模型具有良好的可扩展性和跨平台兼容性。
`ONNX` 使用 `protobuf``Protocol Buffers`)作为其主要协议,来定义模型的结构和数据。这使得 `ONNX` 模型具有良好的可扩展性和跨平台兼容性。



#### 5.2.4 Pytorch转ONNX格式实践

TorchScript 是一种序列化和优化 PyTorch 模型的格式,在优化过程中,一个torch.nn.Module 模型会被转换成 TorchScript 的 torch.jit.ScriptModule 模型。现在, TorchScript 也被常当成一种中间表示使用。我们在[**其他文章**](https://cloud.tencent.com/developer/tools/blog-entry?target=http%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzI4MDcxNTY2MQ%3D%3D%26mid%3D2247489703%26idx%3D1%26sn%3D70fb7d9f40dd32349b75222c91a4838e%26chksm%3Debb51045dcc29953b9a2f2de8b482266db6f0b26d0331c8758d153b71591885ebf8b6ac321b5%26scene%3D21%23wechat_redirect&source=article&objectId=2010575)中对 TorchScript 有详细的介绍,这里介绍 TorchScript 仅用于说明 PyTorch 模型转 ONNX的原理
`TorchScript` 是一种序列化和优化 `PyTorch` 模型的格式,在优化过程中,一个 `torch.nn.Module` 模型会被转换成 `TorchScript``torch.jit.ScriptModule` 模型。现在, `TorchScript` 也被常当成一种中间表示使用。接下来介绍 `TorchScript` 并说明 `PyTorch` 模型转 `ONNX` 的原理

torch.onnx.export 中需要的模型实际上是一个 torch.jit.ScriptModule。而要把普通 PyTorch 模型转一个这样的 TorchScript 模型,有跟踪(trace)和记录(script)两种导出计算图的方法。如果给 torch.onnx.export 传入了一个普通 PyTorch 模型(torch.nn.Module),那么这个模型会默认使用跟踪的方法导出。这一过程如下图所示:
`torch.onnx.export` 中需要的模型实际上是一个 `torch.jit.ScriptModule`。而要把普通 `PyTorch` 模型转一个这样的 `TorchScript` 模型,有跟踪(trace)和记录(script)两种导出计算图的方法。如果给 `torch.onnx.export` 传入了一个普通 `PyTorch` 模型(`torch.nn.Module`),那么这个模型会默认使用跟踪的方法导出。这一过程如下图所示:

![fig5-3](./images/fig5-3.png)

##### 安装必需的依赖项

由于 ONNX 导出器使用 `onnx``onnxscript` 将 PyTorch 运算符转换为 ONNX 运算符,因此我们需要安装它们
由于 `ONNX` 导出器使用 `onnx``onnxscript``PyTorch` 运算符转换为 `ONNX` 运算符,因此需要先进行安装

```bash
pip install onnx
Expand All @@ -54,7 +54,7 @@ pip install onnxscript

##### 编写一个简单的图像分类器模型

一旦设置好环境,让我们开始使用 PyTorch 对图像分类器进行建模
安装好必要的依赖后,就可以开始使用 `PyTorch` 对图像分类器进行建模

```python
import torch
Expand Down Expand Up @@ -84,55 +84,55 @@ class MyModel(nn.Module):

##### 将模型导出为 ONNX 格式

现在我们已经定义了模型,我们需要实例化它并创建一个随机的 32x32 输入。接下来,我们可以将模型导出为 ONNX 格式。
现在我们已经定义了模型,我们需要实例化它并创建一个随机的 `32x32` 的输入。接下来,我们可以将模型导出为 `ONNX` 格式。

```python
torch_model = MyModel()
torch_input = torch.randn(1, 1, 32, 32)
onnx_program = torch.onnx.dynamo_export(torch_model, torch_input)
```

正如我们所看到的,我们不需要对模型进行任何代码更改。生成的 ONNX 模型存储在 `torch.onnx.ONNXProgram` 中,作为二进制 protobuf 文件。
在这个过程中,我们不需要对模型进行任何代码更改。生成的 `ONNX` 模型存储在 `torch.onnx.ONNXProgram` 中,作为二进制 `protobuf` 文件。

##### 将 ONNX 模型保存在文件中

虽然在许多应用程序中将导出的模型加载到内存中很有用,但我们可以使用以下代码将其保存到磁盘
虽然在许多应用程序中将导出的模型加载到内存中很有用,但我们可以使用以下代码将其保存到本地路径

```python
onnx_program.save("my_image_classifier.onnx")
onnx_program.save("./path_to_your_disk/my_image_classifier.onnx")
```

我们可以使用以下代码将 ONNX 文件重新加载到内存中并检查它是否格式正确
我们可以使用以下代码将 `ONNX` 文件重新加载到内存中并检查它是否格式正确

```python
import onnx
onnx_model = onnx.load("my_image_classifier.onnx")
onnx_model = onnx.load("./path_to_your_disk/my_image_classifier.onnx")
onnx.checker.check_model(onnx_model)
```

##### 使用 Netron 可视化 ONNX 模型图

现在我们已经将模型保存在文件中,我们可以使用 [Netron](https://github.com/lutzroeder/netron) 对其进行可视化。Netron 可以安装在 macos、Linux 或 Windows 计算机上,也可以直接从浏览器运行。让我们通过打开以下链接来尝试网络版本:https://netron.app/。
在上一章中我们已经下载并简单介绍了 **Netron**现在我们已经将模型保存在文件中,我们可以使用 [Netron](https://github.com/lutzroeder/netron) 对其进行可视化。让我们通过打开以下链接来尝试网络版本:https://netron.app/。

打开 Netron 后,我们可以将 `my_image_classifier.onnx` 文件拖放到浏览器中,或在单击**打开模型**按钮后选择它。

![fig5-4](./images/fig5-4.png)

至此,我们已成功将 PyTorch 模型导出为 ONNX 格式,并使用 Netron 对其进行了可视化。
至此,我们已成功将 `PyTorch` 模型导出为 `ONNX` 格式,并使用 `Netron` 对其进行了可视化。

##### 使用 ONNX Runtime 执行 ONNX 模型

最后一步是使用 ONNX Runtime 执行 ONNX 模型,但在执行此操作之前,需要先安装 ONNX Runtime。
最后一步是使用 ONNX Runtime 执行 ONNX 模型,但在执行此操作之前,需要先安装 `ONNX Runtime`

```python
pip install onnxruntime
```

ONNX 标准不支持 PyTorch 所支持的所有数据结构和类型,因此我们需要在将 PyTorch 输入馈送到 ONNX Runtime 之前将其调整为 ONNX 格式。在我们的示例中,输入碰巧是相同的,但在更复杂的模型中,它可能比原始 PyTorch 模型有更多输入。
`ONNX` 标准不支持 `PyTorch` 所支持的所有数据结构和类型,因此我们需要在将 `PyTorch` 输入馈送到 `ONNX Runtime` 之前将其调整为 `ONNX` 格式。在我们的示例中,输入碰巧是相同的,但在更复杂的模型中,它可能比原始 `PyTorch` 模型有更多输入。

ONNX Runtime 需要一个附加步骤,该步骤涉及将所有 PyTorch 张量转换为 Numpy(在 CPU 中),并将它们包装在字典中,其中键是包含输入名称的字符串,而 numpy 张量为值。
`ONNX Runtime` 需要一个附加步骤,该步骤涉及将所有 `PyTorch` 张量转换为 `Numpy`(在 CPU 中),并将它们包装在字典中,其中键是包含输入名称的字符串,而 `Numpy` 张量为值。

现在,我们可以创建一个*ONNX Runtime 推理会话*,使用经过处理的输入执行 ONNX 模型并获取输出。在本教程中,ONNX Runtime 在 CPU 上执行,但也可以在 GPU 上执行。
现在,我们可以创建一个*ONNX Runtime 推理会话*,使用经过处理的输入执行 ONNX 模型并获取输出。在本章中,ONNX Runtime 在 CPU 上执行,也可以在 GPU 上执行。

```python
import onnxruntime
Expand All @@ -153,9 +153,9 @@ onnxruntime_outputs = ort_session.run(None, onnxruntime_input)

##### 将 PyTorch 结果与 ONNX Runtime 的结果进行比较

确定导出模型是否良好的最佳方法是通过针对 PyTorch 进行数值评估,PyTorch 是我们的真实来源。
确定导出模型是否良好的最佳方法是通过针对 `PyTorch` 进行数值评估,`PyTorch` 是我们的真实来源。

为此,我们需要使用相同的输入执行 PyTorch 模型,并将结果与 ONNX Runtime 的结果进行比较。在比较结果之前,我们需要转换 PyTorch 的输出以匹配 ONNX 的格式。
为此,我们需要使用相同的输入执行 ` PyTorch `模型,并将结果与 `ONNX Runtime` 的结果进行比较。在比较结果之前,我们需要转换 `PyTorch` 的输出以匹配 `ONNX` 的格式。

```python
torch_outputs = torch_model(torch_input)
Expand All @@ -170,3 +170,47 @@ print(f"Output length: {len(onnxruntime_outputs)}")
print(f"Sample output: {onnxruntime_outputs}")
```



最后我们来通过一个实际的项目例子来进一步感受一下模型之间的格式转换,相关代码和结果在 `experiments` 文件夹下首先下载 `requirements.txt` 中的环境依赖。

除此之外还需要额外安装 `onnx` 的相关依赖

```bash
$ pip install -r requirements.txt coremltools onnx onnx-simplifier onnxruntime openvino-dev tensorflow-cpu # for CPU
$ pip install -r requirements.txt coremltools onnx onnx-simplifier onnxruntime-gpu openvino-dev tensorflow # for GPU
```

接着我们以上一章最后下载并可视化的模型 `yolov5s.pt` 为例来进行格式转换实践——

```
Format | `export.py --include` | Model
--- | --- | ---
PyTorch | - | yolov5s.pt
TorchScript | `torchscript` | yolov5s.torchscript
ONNX | `onnx` | yolov5s.onnx
OpenVINO | `openvino` | yolov5s_openvino_model/
TensorRT | `engine` | yolov5s.engine
CoreML | `coreml` | yolov5s.mlmodel
TensorFlow SavedModel | `saved_model` | yolov5s_saved_model/
TensorFlow GraphDef | `pb` | yolov5s.pb
TensorFlow Lite | `tflite` | yolov5s.tflite
TensorFlow Edge TPU | `edgetpu` | yolov5s_edgetpu.tflite
TensorFlow.js | `tfjs` | yolov5s_web_model/
PaddlePaddle | `paddle` | yolov5s_paddle_model/
```

格式转化命令也很简单,导出的模型格式和对应参数如上表所示,现在我们将原先的模型权重转化为 `onnx` 的中间表示

```bash
python export.py --weights yolov5s.pt --include onnx
```

导出后的模型为 `onnx` 格式的 `yolov5s.onnx`

当然我们也可以尝试使用新的模型格式其进行推理

```bash
python detect.py --weights yolov5s.onnx
```

Loading

0 comments on commit 077d581

Please sign in to comment.