Caffe2: загрузка модели ONNX и вывод однопотоковый на многоядерном хосте / докере - PullRequest
6 голосов
/ 13 марта 2019

У меня проблемы с выполнением логического вывода на модели в докере, когда на хосте несколько ядер. Модель экспортируется через PyTorch 1.0 ONNX экспортер:

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

Запуск сервера модели (в Flask) с одним ядром обеспечивает приемлемую производительность (процессор привязывает процесс к конкретному процессору) docker run --rm -p 8081:8080 --cpus 0.5 --cpuset-cpus 0 my_container

ответ от 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

Но закрепление его на четырех ядрах дает совершенно разные характеристики для одного и того же ab-вызова 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)

Вывод модели сделан следующим образом, и, кроме этой проблемы, он, кажется, работает как ожидалось. (Это работает в совершенно отдельной среде от экспорта модели, конечно)

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 также присутствует в контейнерной среде, которая имела некоторый эффект, но это не конечная проблема.

Статистические данные, которые вы видите здесь, запускаются на локальной машине с 8 гиперзадачами, поэтому я не должен насыщать свою машину и влиять на тест. Эти результаты также отображаются в моей среде kubernetes, и я получаю там большое количество CFS (полностью честного планировщика).

Я работаю в среде kubernetes, поэтому у меня нет возможности контролировать, сколько процессоров выставляет хост, и выполнение некоторого закрепления там тоже кажется немного хакерским.

Есть ли способ привязать вывод модели caffe2 к одному процессору? Я делаю что-то явно не так здесь? Разве объект caffe2.Predictor не подходит для этой задачи?

Любая помощь приветствуется.

EDIT:

Я добавил простейший из всех возможных воспроизводимых примеров, о которых я могу подумать, с включенным Docker-контейнером и скриптом выполнения: https://github.com/NegatioN/Caffe2Struggles

Ответы [ 2 ]

3 голосов
/ 19 марта 2019

Это не прямой ответ на вопрос, но если ваша цель - обслуживать модели PyTorch (и только те модели PyTorch, как у меня сейчас), простое использование PyTorch Tracing кажется лучший выбор.

Затем вы можете загрузить его непосредственно в интерфейс C ++, аналогично тому, как вы делали бы это через Caffe2, но трассировка PyTorch кажется более удобной. Из того, что я вижу, замедления нет, но его гораздо проще настроить.

Примером этого для достижения хорошей производительности в одноядерном контейнере является запуск с OMP_NUM_THREADS=1, как и раньше, и экспорт модели следующим образом:

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")

А затем просто запустите производственную модель на чистом C ++, следуя приведенному выше руководству, или через интерфейс Python как таковой:

from torch import jit
model = jit.load("traced.pt")
output = model(some_input)
0 голосов
/ 14 марта 2019

Я думаю, что это должно работать:

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

...