1.1 Introdução
Como uma estrutura de aprendizado profundo de ponta a ponta, o PyTorch tem boas condições de implantação de ambiente de produção após a versão 1.0. Além de escrever API REST para implantação no lado da web (referência), também existem requisitos extensos para implantação do lado do software. Especialmente a versão 1.5 lançada recentemente fornece uma API de front-end C ++ mais estável.
A maior diferença entre o mundo industrial e o mundo acadêmico é que o modelo industrial precisa ser implantado no solo. O mundo acadêmico está mais preocupado com os requisitos de precisão do modelo e menos preocupado com o desempenho de implantação do modelo. De modo geral, após treinarmos um modelo com um framework de aprendizado profundo, Python é o suficiente para implementar uma demonstração de raciocínio simples. Mas em um ambiente de produção, a portabilidade e desempenho de velocidade do Python são muito inferiores ao C ++. Portanto, para engenheiros de algoritmos de aprendizagem profunda, Python é geralmente usado para a implementação rápida de ideias e treinamento de modelo, e C ++ é usado como uma ferramenta de produção de modelos. No momento, PyTorch pode combinar os dois perfeitamente. Os principais componentes de tecnologia que implementam a implantação do modelo PyTorch são TorchScript e libtorch .
Portanto, o processo de engenharia de algoritmo de aprendizado profundo baseado em PyTorch é aproximadamente como mostrado na figura a seguir:
1.2 TorchScript
O TorchScript pode ser considerado uma representação intermediária do modelo PyTorch, e o modelo PyTorch representado pelo TorchScript pode ser lido diretamente em C ++. PyTorch pode usar TorchScript para construir modelos serializados após a versão 1.0. O TorchScript fornece dois métodos de aplicação: Rastreamento e Script.
Os exemplos de aplicativos de rastreamento são os seguintes:
class MyModel(torch.nn.Module):
def __init__(self):
super(MyModel, self).__init__()
self.linear = torch.nn.Linear(4, 4)
def forward(self, x, h):
new_h = torch.tanh(self.linear(x) + h)
return new_h, new_h
# 创建模型实例
my_model = MyModel()
# 输入示例
x, h = torch.rand(3, 4), torch.rand(3, 4)
# torch.jit.trace方法对模型构建TorchScript
traced_model = torch.jit.trace(my_model, (x, h))
# 保存转换后的模型
traced_model.save('model.pt')
Neste código, primeiro definimos um modelo simples e criamos uma instância de modelo e, em seguida, dado um exemplo de entrada, a etapa mais crítica do método Tracing é usar o método torch.jit.trace para transformar o modelo em TorchScript. Podemos obter o objeto traced_model transformado para obter seus atributos de gráfico computacional e atributos de código. Propriedades do gráfico de cálculo:
print(traced_model.graph)
graph(%self.1 : __torch__.torch.nn.modules.module.___torch_mangle_1.Module,
%input : Float(3, 4),
%h : Float(3, 4)):
%19 : __torch__.torch.nn.modules.module.Module = prim::GetAttr[name="linear"](%self.1)
%21 : Tensor = prim::CallMethod[name="forward"](%19, %input)
%12 : int = prim::Constant[value=1]() # /var/lib/jenkins/workspace/beginner_source/Intro_to_TorchScript_tutorial.py:188:0
%13 : Float(3, 4) = aten::add(%21, %h, %12) # /var/lib/jenkins/workspace/beginner_source/Intro_to_TorchScript_tutorial.py:188:0
%14 : Float(3, 4) = aten::tanh(%13) # /var/lib/jenkins/workspace/beginner_source/Intro_to_TorchScript_tutorial.py:188:0
%15 : (Float(3, 4), Float(3, 4)) = prim::TupleConstruct(%14, %14)
return (%15)
Atributos de código:
print(traced_cell.code)
def forward(self,
input: Tensor,
h: Tensor) -> Tuple[Tensor, Tensor]:
_0 = torch.add((self.linear).forward(input, ), h, alpha=1)
_1 = torch.tanh(_0)
return (_1, _1)
Desta forma, podemos salvar todo o modelo no disco rígido, e o modelo salvo desta forma pode ser carregado em outros ambientes de linguagem.
Outra implementação do TorchScript é o método Script, que pode ser considerado um suplemento do método Tracing. Quando o código do modelo contém programas de fluxo de controle, como if ou for-loop, o método Tracing é inválido.Neste momento, o método Script pode ser usado para implementar o TorchScript. O método de implementação não é muito diferente de Tracing. A chave é substituir jit.tracing pelo método jit.script. O exemplo é o seguinte.
scripted_model = torch.jit.script(MyModel)
scripted_model.save('model.pt')
Além de Tracing e Script, também podemos misturar os dois métodos, que não serão detalhados aqui. Resumindo, o TorchScript nos fornece uma forma de representação que pode ser otimizada pelo compilador para fornecer uma execução mais eficiente.
1.3 libtorch
Depois de converter o modelo treinado no ambiente Python, precisamos do PyTorch no ambiente C ++ para ler o modelo, compilar e implantar. PyTorch neste ambiente C ++ é libtorch. Como a libtorch é normalmente usada como a interface C ++ do modelo PyTorch, a libtorch também é chamada de front-end C ++ do PyTorch.
Podemos baixar o pacote de instalação do libtorch compilado diretamente do site oficial do PyTorch. Claro, também podemos baixar o código-fonte e compilá-lo nós mesmos. Deve-se observar aqui que a versão instalada do libtorch deve ser consistente com a versão do PyTorch no ambiente Python.
Depois de instalar o libtorch, você pode simplesmente testar se está normal. Por exemplo, usamos TorchScript para converter um modelo pré-treinado, um exemplo é o seguinte:
import torch
import torchvision.models as models
vgg16 = models.vgg16()
example = torch.rand(1, 3, 224, 224).cuda()
model = model.eval()
traced_script_module = torch.jit.trace(model, example)
output = traced_script_module(torch.ones(1,3,224,224).cuda())
traced_script_module.save('vgg16-trace.pt')
print(output)
O resultado é:
tensor([[ -0.8301, -35.6095, 12.4716]], device='cuda:0',
grad_fn=<AddBackward0>)
Em seguida, mude para o ambiente C ++ e grave o arquivo CmakeLists da seguinte maneira:
cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR)
project(libtorch_test)
find_package(Torch REQUIRED)
message(STATUS "Pytorch status:")
message(STATUS "libraries: ${TORCH_LIBRARIES}")
add_executable(libtorch_test test.cpp)
target_link_libraries(libtorch_test "${TORCH_LIBRARIES}")
set_property(TARGET libtorch_test PROPERTY CXX_STANDARD 11)
Continue a escrever o código test.cpp da seguinte maneira:
#include "torch/script.h"
#include "torch/torch.h"
#include <iostream>
#include <memory>
using namespace std;
int main(int argc, const char* argv[]){
if (argc != 2) {
std::cerr << "usage: example-app <path-to-exported-script-module>\n";
return -1;
}
// 读取TorchScript转化后的模型
torch::jit::script::Module module;
try {
module = torch::jit::load(argv[1]);
}
catch (const c10::Error& e) {
std::cerr << "error loading the model\n";
return -1;
}
module->to(at::kCUDA);
assert(module != nullptr);
std::cout << "ok\n";
// 构建示例输入
std::vector<torch::jit::IValue> inputs;
inputs.push_back(torch::ones({1, 3, 224, 224}).to(at::kCUDA));
// 执行模型推理并输出tensor
at::Tensor output = module->forward(inputs).toTensor();
std::cout << output.slice(/*dim=*/1, /*start=*/0, /*end=*/5) << '\n';}
Compile test.cpp e execute-o.O resultado é o seguinte. Comparando os resultados da execução no ambiente Python, podemos descobrir que eles são basicamente os mesmos, o que também mostra que não há problema com a instalação do libtorch no ambiente atual.
ok
-0.8297, -35.6048, 12.4823
[Variable[CUDAFloatType]{1,3}]
1.4 Processo de implantação completo
Por meio da descrição anterior do TorchScript e do libtorch, na verdade, falamos basicamente sobre a implantação do PyTorch em C ++ e aqui daremos uma olhada completa em todo o processo. O processo de implantação do modelo PyTorch baseado em C ++ é o seguinte.
Primeiro passo:
Use o método torch.jit.trace para converter o modelo PyTorch em TorchScript. Um exemplo é o seguinte:
import torch
from torchvision.models import resnet18
model =resnet18()
example = torch.rand(1, 3, 224, 224)
tracing.traced_script_module = torch.jit.trace(model, example)
A segunda etapa:
Serialize o TorchScript para o arquivo de modelo .pt.
traced_script_module.save("traced_resnet_model.pt")
terceiro passo:
Importe o modelo TorchScript serializado em C ++, para isso precisamos escrever o arquivo cpp contendo o programa de chamada, o arquivo CMakeLists.txt para configuração e compilação. O conteúdo de amostra do arquivo CMakeLists.txt é o seguinte:
cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
project(custom_ops)
find_package(Torch REQUIRED)
add_executable(example-app example-app.cpp)
target_link_libraries(example-app "${TORCH_LIBRARIES}")
set_property(TARGET example-app PROPERTY CXX_STANDARD 14)
O código de exemplo de example-app.cpp que contém o programa de chamada de modelo é o seguinte:
#include <torch/script.h> // torch头文件.
#include <iostream>#include <memory>
int main(int argc, const char* argv[]) {
if (argc != 2) {
std::cerr << "usage: example-app <path-to-exported-script-module>\n";
return -1;
}
torch::jit::script::Module module;
try {
// 反序列化:导入TorchScript模型
module = torch::jit::load(argv[1]);
}
catch (const c10::Error& e) {
std::cerr << "error loading the model\n";
return -1;
}
std::cout << "ok\n";}
Depois que os dois arquivos são gravados, eles podem ser compilados:
mkdir example_test
cd example_test
cmake -DCMAKE_PREFIX_PATH=/path/to/libtorch ..
cmake --example_test . --config Release
a quarta etapa:
Adicione o código de inferência do modelo a example-app.cpp e execute:
std::vector<torch::jit::IValue> inputs;inputs.push_back(torch::ones({1, 3, 224, 224}));
// 执行推理并将模型转化为Tensor
output = module.forward(inputs).toTensor();std::cout << output.slice(/*dim=*/1, /*start=*/0, /*end=*/5) << '\n';
Acima está todo o processo de implantação do modelo PyTorch em C ++. Para tutoriais relacionados, consulte o PyTorch oficial: https://pytorch.org/tutorials/
Resumindo
A implantação do modelo é muito importante para engenheiros de algoritmo e está relacionada ao fato de seu trabalho poder gerar valor real. Da mesma forma, você também precisa ter recursos de engenharia suficientes, como MySQL, Redis, C ++, algum conhecimento e técnicas de desenvolvimento de front-end e back-end, todos os engenheiros de algoritmo precisam ser capazes de entender e usar isso.