J'ai du mal à exécuter l'inférence sur un modèle dans Docker lorsque l'hôte a plusieurs cœurs. Le modèle est exporté via l'exportateur PyTorch 1.0 ONNX:

torch.onnx.export(pytorch_net, dummyseq, ONNX_MODEL_PATH)

Le démarrage du serveur de modèle (enveloppé dans Flask) avec un seul cœur donne des performances acceptables (cpuset épingle le processus à des cpus spécifiques) docker run --rm -p 8081:8080 --cpus 0.5 --cpuset-cpus 0 my_container

Réponse de ab -c 1 -n 1000 http://0.0.0.0:8081/predict\?itemids\=5,100

Percentage of the requests served within a certain time (ms)
  50%      5
  66%      5
  75%      5
  80%      5
  90%      7
  95%     46
  98%     48
  99%     49

Mais l'épingler à quatre cœurs donne des statistiques complètement différentes pour le même ab-call docker run --rm -p 8081:8080 --cpus 0.5 --cpuset-cpus 0,1,2,3 my_container

Percentage of the requests served within a certain time (ms)
  50%      9
  66%     12
  75%     14
  80%     18
  90%     62
  95%     66
  98%     69
  99%     69
 100%     77 (longest request)

L'inférence du modèle se fait comme ceci, et à l'exception de ce problème, il semble fonctionner comme prévu. (Cela s'exécute dans un environnement complètement distinct de l'exportation du modèle bien sûr)

from caffe2.python import workspace
from caffe2.python.onnx.backend import Caffe2Backend as c2
from onnx import ModelProto


class Model:
    def __init__(self):
        self.predictor = create_caffe2_predictor(path)

    @staticmethod
    def create_caffe2_predictor(onnx_file_path):
        with open(onnx_file_path, 'rb') as onnx_model:
            onnx_model_proto = ModelProto()
            onnx_model_proto.ParseFromString(onnx_model.read())
            init_net, predict_net = c2.onnx_graph_to_caffe2_net(onnx_model_proto)
            predictor = workspace.Predictor(init_net, predict_net)
        return predictor


    def predict(self, numpy_array):
        return self.predictor.run({'0': numpy_array})

** wrapper flask app which calls Model.predict() on calls to /predict **

OMP_NUM_THREADS=1 est également présent dans l'environnement conteneur, qui a eu un certain effet, mais ce n'est pas le problème final.

Les statistiques de référence que vous voyez ici sont exécutées sur une machine locale avec 8 hyperthreads, donc je ne devrais pas saturer ma machine et affecter le test. Ces résultats apparaissent également dans mon environnement kubernetes, et j'obtiens une grande quantité de CFS (Completely Fair Scheduler) là-bas.

J'exécute dans un environnement kubernetes, donc il n'y a aucun moyen pour moi de contrôler le nombre de CPU exposés par l'hôte, et faire une sorte d'épinglage semble également un peu hacky.

Existe-t-il un moyen d'épingler l'inférence du modèle caffe2 à un seul processeur? Suis-je en train de faire quelque chose de mal ici? L'objet caffe2.Predictor n'est-il pas adapté à cette tâche?

Toute aide appréciée.

ÉDITER:

J'ai ajouté l'exemple reproductible le plus simple auquel je puisse penser ici, avec un docker-container et un run-script inclus : https ://github.com/NegatioN/Caffe2Struggles

5
NegatioN 13 mars 2019 à 19:48

2 réponses

Meilleure réponse

Ce n'est pas une réponse directe à la question, mais si votre objectif est de servir des modèles PyTorch (et uniquement des modèles PyTorch, comme le mien actuellement) en production, utilisez simplement PyTorch Tracing semble être le meilleur choix.

Vous pouvez ensuite le charger directement dans un frontend C ++ de manière similaire à ce que vous feriez via Caffe2, mais le suivi PyTorch semble plus bien maintenu. D'après ce que je peux voir, il n'y a pas de ralentissement de la vitesse, mais c'est beaucoup plus facile à configurer.

Un exemple de cela pour obtenir de bonnes performances sur un conteneur monocœur consiste à exécuter avec OMP_NUM_THREADS=1 comme précédemment et à exporter le modèle comme suit:

from torch import jit
### Create a model
model.eval()
traced = jit.trace(model, torch.from_numpy(an_array_with_input_size))
traced.save("traced.pt")

Et puis exécutez simplement le modèle en production en C ++ pur en suivant le guide ci-dessus, ou via l'interface Python en tant que telle:

from torch import jit
model = jit.load("traced.pt")
output = model(some_input)
3
NegatioN 19 mars 2019 à 17:15

Je pense que cela devrait fonctionner:

workspace.GlobalInit(["caffe2", "--caffe2_omp_num_threads=1"])

0
Sergii Dymchenko 13 mars 2019 à 23:18