前几章,我们学习了PyTorch的基本使用、能够定义和修改自己的模型、常用的训练技巧和PyTorch的可视化。
PyTorch的强大,跟PyTorch的活跃的开源社区离不开关系,开源社区围绕PyTorch所生产的一些列工具包和程序,这些优秀的工具包极大地方便了PyTorch在特定领域的使用。比如:
接下来,会介绍PyTorch针对某个领域(图像、视频、文本等)的其中某一具代表性的工具包进行详细介绍,主要包括工具包的作者、所在机构、数据预处理工具、数据扩增、常用模型结构的预定义、预训练模型权重、常用损失函数、常用评测指标、封装好的训练&测试模块及可视化工具。
" The torchvision package consists of popular datasets, model architectures, and common image transformations for computer vision. "
其包括如下库:
随着传播媒介和视频平台的发展,视频逐渐成伪新一代的主流媒体,使得视频相关的深度学习模型越来越受关注,但是针对视频的深度学习模型有许多缺点:
PyTorchVideo因此而生,提供了加速视频理解研究所需的可重用、模块化和高效的组件,它支持不同的深度学习视频组件,如视频模型、视频数据集和视频特定转换。其组件如下图:
对应的官方链接
PyTorchVideo提供了model zoo,使得可以使用各种先进的预训练模型及其评判基准,其亮点如下:
其安装可以使用pip,对应命令pip install pytorchvideo
,需满足如下版本约束:
其model zoo和benchmark可参考链接
提供了三种使用方法,并给了对应的tutorial
from torchinfo import summary
import torchvision.models as models
resnet = models.resnet18()
summary(resnet, (1, 3, 224, 224))
其中,可以看到torchinfo提供了更加详细的信息,包括每个模块的信息(每一层的信息,输出shape和参数量)、模型总共参数量、模型大小、前向/反向传播需要的内存,参数内存、预估总共信息。
PyTorch官方用于自然语言处理NLP的工具包为torchtext,由于NLP和CV在数据处理中的布通,因此NLP的工具包torchtext和torchvision等CV相关工具功能上也有差异,如:
更多可参考链接,torchtext官方文档
torchtext可以方便地对文本进行预处理,如截断补长、构建词表等,其主要组成部分:
安装可以使用pip,命令为pip install torchtext
其使用主要如下:
TEXT.build_vocab(train)
NLP中部分任务的评测不是通过准确率等指标完成,比如机器翻译任务常用BLEU (bilingual evaluation understudy) score来评价预测文本和标签文本之间的相似程度。在torchtext可以调用torchtext.data.metrics.bleu_score来完成
由于NLP常用的网络结构比较固定,其模型主要通过torch.nn模块实现,比如torch.nn.LSTM,torch.nn.RNN等
对于文本研究而言,当下Transformer已经成为了绝对的主流,因此PyTorch生态中的HuggingFace等工具包也受到了越来越广泛的关注。
深度学习最终需要部署在手机端、开发板、嵌入式设备等终端,才能解决问题。但是通常情况下这些终端设备由于各种限制无法使用训练好的权重进行推理,因此需要将得到的权重变换从而部署到设备中,因此出现了以下模型部署pipeline:
这里将以ONNX为例介绍模型的部署,其流程通常如下:
ONNX**( Open Neural Network Exchange)** 是 Facebook (现Meta) 和微软在2017年共同发布的,用于标准描述计算图的一种格式。各类设备只要基于兼容ONNX即可运行相关的模型。同时,各设备只要针对ONNX标准进行模型性能优化,就可以使得所有兼容ONNX的框架受益。目前ONNX支持如下深度学习框架和多种推理引擎:
ONNX Runtime 是由微软维护的一个跨平台机器学习推理加速器,它直接对接ONNX,可以直接读取.onnx文件并实现推理,不需要再把 .onnx 格式的文件转换成其他格式的文件。
ONNX和ONNX Runtime的适配关系参考链接。
安装过程如下:
pip install onnx
pip install onnxruntime # use cpu
pip install onnxruntime-gpu # use gpu
ONNX Runtime和CUDA之间的适配关系参考链接。
使用torch.onnx.export()
把模型转换成 ONNX 格式的函数。模型导成onnx格式前,我们必须调用model.eval()
或者model.train(False)
以确保我们的模型处在推理模式下,避免因为dropout
或batchnorm
等运算符在推理和训练模式下的不同产生错误。
import torch.onnx
# 转换的onnx格式的名称,文件后缀需为.onnx
onnx_file_name = "xxxxxx.onnx"
# 我们需要转换的模型,将torch_model设置为自己的模型
model = torch_model
# 加载权重,将model.pth转换为自己的模型权重
# 如果模型的权重是使用多卡训练出来,我们需要去除权重中多的module. 具体操作可以见5.4节
model = model.load_state_dict(torch.load("model.pth"))
# 导出模型前,必须调用model.eval()或者model.train(False)
model.eval()
# dummy_input就是一个输入的实例,仅提供输入shape、type等信息
batch_size = 1 # 随机的取值,当设置dynamic_axes后影响不大
dummy_input = torch.randn(batch_size, 1, 224, 224, requires_grad=True)
# 这组输入对应的模型输出
output = model(dummy_input)
# 导出模型
torch.onnx.export(model, # 模型的名称
dummy_input, # 一组实例化输入
onnx_file_name, # 文件保存路径/名称
export_params=True, # 如果指定为True或默认, 参数也会被导出. 如果你要导出一个没训练过的就设为 False.
opset_version=10, # ONNX 算子集的版本,当前已更新到15
do_constant_folding=True, # 是否执行常量折叠优化
input_names = ['input'], # 输入模型的张量的名称
output_names = ['output'], # 输出模型的张量的名称
# dynamic_axes将batch_size的维度指定为动态,
# 后续进行推理的数据可以与导出的dummy_input的batch_size不同
dynamic_axes={'input' : {0 : 'batch_size'},
'output' : {0 : 'batch_size'}})
模型成功导出后得到一个ONNX格式的文件,可以使用onnx.checker.check_model()检测模型是否可用:
import onnx
# 我们可以使用异常处理的方法进行检验
try:
# 当我们的模型不可用时,将会报出异常
onnx.checker.check_model(self.onnx_model)
except onnx.checker.ValidationError as e:
print("The model is invalid: %s"%e)
else:
# 模型可用时,将不会报出异常,并会输出“The model is valid!”
print("The model is valid!")
ONNX格式的模型可以使用Netron可视化,下载链接。这里给出官网的squeezenet例子链接。
完成模型的转化,检测后,即可使用ONNX Runtime运行转化后的模型查看推理结果。
# 导入onnxruntime
import onnxruntime
# 需要进行推理的onnx模型文件名称
onnx_file_name = "xxxxxx.onnx"
# onnxruntime.InferenceSession用于获取一个 ONNX Runtime 推理器
ort_session = onnxruntime.InferenceSession(onnx_file_name)
# 构建字典的输入数据,字典的key需要与我们构建onnx模型时的input_names相同
# 输入的input_img 也需要改变为ndarray格式
ort_inputs = {'input': input_img}
# 我们更建议使用下面这种方法,因为避免了手动输入key
# ort_inputs = {ort_session.get_inputs()[0].name:input_img}
# run是进行模型的推理,第一个参数为输出张量名的列表,一般情况可以设置为None
# 第二个参数为构建的输入值的字典
# 由于返回的结果被列表嵌套,因此我们需要进行[0]的索引
ort_output = ort_session.run(None,ort_inputs)[0]
# output = {ort_session.get_outputs()[0].name}
# ort_output = ort_session.run([output], ort_inputs)[0]
这里需注意:
def to_numpy(tensor):
return tensor.detach().cpu().numpy() if tensor.requires_grad else tensor.cpu().numpy()
这里给出官网的链接供参考。