Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TVM] inference script #412

Merged
merged 20 commits into from
Nov 7, 2023

Conversation

ismukhin
Copy link
Contributor

@ismukhin ismukhin commented Oct 9, 2023

TODO:

  • Поддержка MXNet
  • Поддержка PyTorch (модели из torchvision модуля)
  • Поддержка ONNX (некоторые модели последних версий падают на стороне TVM)
  • Запуск инференса для вывода (1 итерация)
  • Замеры времени
  • Поддержка бенчмарка
  • Тест запуска бенчмарка
  • README для скриптов инференса и для бенчмарка

@ismukhin ismukhin marked this pull request as draft October 9, 2023 19:50
@ismukhin
Copy link
Contributor Author

ismukhin commented Oct 9, 2023

@valentina-kustikova, я унаследовал io_model_wrapper и transformer от MXNet, чтобы примерно накинуть шаблон для скрипта, думаю, их можно переписать средствами TVM, в документации вроде есть. Но по поводу версии TVM: документация на их сайте неполная, вероятно, его нужно действительно собирать из исходников для нормальной работы.

@valentina-kustikova
Copy link
Contributor

@Rodimkov, в текущем PR будет располагаться реализация вывода. Просьба - отслеживать состояние реализации.

@ismukhin
Copy link
Contributor Author

@valentina-kustikova, решил попробовать разделить эти скрипты по фреймворкам. Тогда вопрос: имеет ли смысл их засунуть в отдельную папку TVM?
Еще есть небольшая проблема: скрипт не хочет работать без строчки import mxnet падает с ошибкой free(): invalid size Аварийный останов (образ памяти сброшен на диск)
Хотя в самом скрипте inference_tvm_mxnet.py я не использую что-либо из mxnet, только в вспомогательном модуле.

src/inference/inference_tvm_mxnet.py Show resolved Hide resolved
src/inference/inference_tvm_mxnet.py Show resolved Hide resolved
src/inference/inference_tvm_mxnet.py Show resolved Hide resolved
src/inference/inference_tvm_mxnet.py Outdated Show resolved Hide resolved
src/inference/tvm_auxiliary.py Outdated Show resolved Hide resolved
@ismukhin
Copy link
Contributor Author

ismukhin commented Oct 20, 2023

@valentina-kustikova, пока промежуточный результат примерно такой:

  1. Если писать скрипты под каждый фреймворк, то нужно будет делать много наследований (под каждый фреймворк) в модулях бенчмарка под TVM.
  2. В файл .csv почему-то не записывается input blob size, хотя структура идентична MXNet, в котором он записывается.
  3. Если писать 1 общий скрипт, то вместо множества наследований будут проверки (например, для PyTorch и MXNet проверять расширение файла модели внутри скрипта инференса .pt и .json соответственно) параметров командной строки.

По итогу получается, что в обоих случаях придется в конфигах таскать много параметров

return f'{common_params}'


class MXNet_TVMProcess(TVMProcess):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Может, назвать класс TVMProcessMXNetFormat?

model, params = tvm.relay.frontend.from_mxnet(net, shape_dict)
with tvm.transform.PassContext(opt_level=3):
lib = tvm.relay.build(model, target=target, params=params)
module = tvm.contrib.graph_executor.GraphModule(lib["default"](dev))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Может быть, конвертацию под каждый фреймворк стоит вынести в отдельный метод?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Предлагаю сделать их приватными и вызывать в обертке в зависимости от фреймворка

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

def convert_model(self, framework):
    if framework == 'mxnet':
        _convert_from_mxnet(self)

Как-то так примерно

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

def _convert_from_mxnet(self):
    import mxnet, gluoncv

    if self.args['device'] == 'CPU':
        context = mxnet.cpu()
    model_name = self.args['model_name']

    log.info(f'Loading network \"{model_name}\" from GluonCV model zoo')
    net = gluoncv.model_zoo.get_model(model_name, pretrained=True, ctx=context)

    shape_dict = {self.args['input_name']: self.args['input_shape']}

    log.info('Creating graph module from MXNet model')
    model, params = tvm.relay.frontend.from_mxnet(net, shape_dict)
    with tvm.transform.PassContext(opt_level=3):
        lib = tvm.relay.build(model, target=target, params=params)
    module = tvm.contrib.graph_executor.GraphModule(lib["default"](dev))
    return module

def convert_model(self, framework):
    if framework == 'mxnet':
        module = self._convert_from_mxnet()
        return module
    elif framework == 'pytorch':
        ...

@valentina-kustikova
Copy link
Contributor

@valentina-kustikova, пока промежуточный результат примерно такой:

  1. Если писать скрипты под каждый фреймворк, то нужно будет делать много наследований (под каждый фреймворк) в модулях бенчмарка под TVM.
  2. В файл .csv почему-то не записывается input blob size, хотя структура идентична MXNet, в котором он записывается.
  3. Если писать 1 общий скрипт, то вместо множества наследований будут проверки (например, для PyTorch и MXNet проверять расширение файла модели внутри скрипта инференса .pt и .json соответственно) параметров командной строки.

По итогу получается, что в обоих случаях придется в конфигах таскать много параметров

Вопросы 1 и 3 связанные, договорились, что делаем несколько скриптов.

Вопрос 2: input blob size в csv получается в результате парсинга того, что логирует скрипт инференса вот в этой строке:

log.info(f'Shape for input layer {args.input_name}: {args.input_shape}')

@ismukhin
Copy link
Contributor Author

ismukhin commented Oct 25, 2023

@valentina-kustikova, немного переделал вспомогательный модуль, потому что проблему #412 (comment) я недооценил, она возникает, на самом деле, у всех фреймворков, просто я не создавал объекты device как у MXNet, поэтому сложилось впечатление, что это только у MXNet
Также не могу понять, почему падает smoke test для tvm_pytorch, я делаю тоже самое, что и в скрипте инференса для PyTorch, очень странно. На моей машине скрипт работает нормально

@valentina-kustikova
Copy link
Contributor

@valentina-kustikova, немного переделал вспомогательный модуль, потому что проблему #412 (comment) я недооценил, она возникает, на самом деле, у всех фреймворков, просто я не создавал объекты device как у MXNet, поэтому сложилось впечатление, что это только у MXNet Также не могу понять, почему падает smoke test для tvm_pytorch, я делаю тоже самое, что и в скрипте инференса для PyTorch, очень странно. На моей машине скрипт работает нормально

@ismukhin, не понятно, удалось ли побороть первую проблему?

По второму в трассе тестов (если по ним нажать) написано: Traceback (most recent call last):\n', ' File "/home/runner/work/dl-benchmark/dl-benchmark/src/inference/inference_tvm_pytorch.py", line 208, in main\n', ' graph_module = converter.get_graph_module()\n', ' File "/home/runner/work/dl-benchmark/dl-benchmark/src/inference/tvm_auxiliary.py", line 30, in get_graph_module\n', ' module = self._convert_model_from_framework(target, dev)\n', ' File "/home/runner/work/dl-benchmark/dl-benchmark/src/inference/inference_tvm_pytorch.py", line 143, in _convert_model_from_framework\n', ' pt_model = pt_model(weights=True)\n', "TypeError: init() got an unexpected keyword argument 'weights'\n", т.е. pt_model получает неожиданный аргумент, у него просто нет такого аргумента. Проверять работу тестов можно локально, запуская скрипт, там пишется лог с ошибками.

@ismukhin
Copy link
Contributor Author

@valentina-kustikova, немного переделал вспомогательный модуль, потому что проблему #412 (comment) я недооценил, она возникает, на самом деле, у всех фреймворков, просто я не создавал объекты device как у MXNet, поэтому сложилось впечатление, что это только у MXNet Также не могу понять, почему падает smoke test для tvm_pytorch, я делаю тоже самое, что и в скрипте инференса для PyTorch, очень странно. На моей машине скрипт работает нормально

@ismukhin, не понятно, удалось ли побороть первую проблему?

По второму в трассе тестов (если по ним нажать) написано: Traceback (most recent call last):\n', ' File "/home/runner/work/dl-benchmark/dl-benchmark/src/inference/inference_tvm_pytorch.py", line 208, in main\n', ' graph_module = converter.get_graph_module()\n', ' File "/home/runner/work/dl-benchmark/dl-benchmark/src/inference/tvm_auxiliary.py", line 30, in get_graph_module\n', ' module = self._convert_model_from_framework(target, dev)\n', ' File "/home/runner/work/dl-benchmark/dl-benchmark/src/inference/inference_tvm_pytorch.py", line 143, in _convert_model_from_framework\n', ' pt_model = pt_model(weights=True)\n', "TypeError: init() got an unexpected keyword argument 'weights'\n", т.е. pt_model получает неожиданный аргумент, у него просто нет такого аргумента. Проверять работу тестов можно локально, запуская скрипт, там пишется лог с ошибками.

Первую проблему удалось решить в таком варианте, который я залил.
А вот со smoke_test'ом вопрос в том, что в скрипте inference_pytorch.py во время загрузки модели делается тоже самое и переменная спокойно принимает аргумент weights. Локально у меня работает это дело без ошибок, не падает, а вот в smoke_test почему-то падает.
Надо внимательно посмотреть ещё раз.

@valentina-kustikova
Copy link
Contributor

@valentina-kustikova, немного переделал вспомогательный модуль, потому что проблему #412 (comment) я недооценил, она возникает, на самом деле, у всех фреймворков, просто я не создавал объекты device как у MXNet, поэтому сложилось впечатление, что это только у MXNet Также не могу понять, почему падает smoke test для tvm_pytorch, я делаю тоже самое, что и в скрипте инференса для PyTorch, очень странно. На моей машине скрипт работает нормально

@ismukhin, не понятно, удалось ли побороть первую проблему?
По второму в трассе тестов (если по ним нажать) написано: Traceback (most recent call last):\n', ' File "/home/runner/work/dl-benchmark/dl-benchmark/src/inference/inference_tvm_pytorch.py", line 208, in main\n', ' graph_module = converter.get_graph_module()\n', ' File "/home/runner/work/dl-benchmark/dl-benchmark/src/inference/tvm_auxiliary.py", line 30, in get_graph_module\n', ' module = self._convert_model_from_framework(target, dev)\n', ' File "/home/runner/work/dl-benchmark/dl-benchmark/src/inference/inference_tvm_pytorch.py", line 143, in _convert_model_from_framework\n', ' pt_model = pt_model(weights=True)\n', "TypeError: init() got an unexpected keyword argument 'weights'\n", т.е. pt_model получает неожиданный аргумент, у него просто нет такого аргумента. Проверять работу тестов можно локально, запуская скрипт, там пишется лог с ошибками.

Первую проблему удалось решить в таком варианте, который я залил. А вот со smoke_test'ом вопрос в том, что в скрипте inference_pytorch.py во время загрузки модели делается тоже самое и переменная спокойно принимает аргумент weights. Локально у меня работает это дело без ошибок, не падает, а вот в smoke_test почему-то падает. Надо внимательно посмотреть ещё раз.

Надо попробовать локально запустить smoke-тест, только ваш и посмотреть результат.

@ismukhin
Copy link
Contributor Author

@valentina-kustikova, оказалось, что имя модели нужно задавать только буквами нижнего регистра.

@valentina-kustikova
Copy link
Contributor

@ismukhin, можно ревьюить все, что сделано? Статус пока драфт, поэтому и спрашиваю.

@ismukhin
Copy link
Contributor Author

@ismukhin, можно ревьюить все, что сделано? Статус пока драфт, поэтому и спрашиваю.

Можно

@ismukhin
Copy link
Contributor Author

ismukhin commented Oct 25, 2023

В PyTorch пока что поддержка только моделей из torchvision модуля, поддержку моделей из файлов, думаю, занесу вместе с TF и TFLite

@valentina-kustikova
Copy link
Contributor

В PyTorch пока что поддержка только моделей из torchvision модуля, поддержку моделей из файлов, думаю, занесу вместе с TF и TFLite

Хорошо, тогда переводите из состояния драфт, оставлю ревью.

@Rodimkov, предлагаю вам тоже посмотреть разработанный код.

@ismukhin ismukhin marked this pull request as ready for review October 25, 2023 18:30
Copy link
Contributor

@valentina-kustikova valentina-kustikova left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Нужно еще обновить README с описанием инференс-скриптов + шаблонный файл конфигурации + README к шаблонному файлу конфигурации.


common_params = (f'-i {dataset} '
f'-is {input_shape} -b {batch_size} -ni {iteration} '
f'--report_path {self.report_path}')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Лучше в 2 строки написать, а не 3.

common_params = (f'-m {model_json} -w {model_params} ')
else:
raise Exception('Incorrect model parameters. Set model name or file names.')
path_to_sync_script = Path.joinpath(self.inference_script_root,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Лучше переименовать в path_to_script, поскольку здесь явно режимов нет.

common_params = (f'-m {model_json} -w {model_params} ')
else:
raise Exception('Incorrect model parameters. Set model name or file names.')
path_to_sync_script = Path.joinpath(self.inference_script_root,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Лучше переименовать в path_to_script, поскольку здесь явно режимов нет.

def _fill_command_line(self):
model = self._test.model.model
common_params = f'-m {model} '
path_to_sync_script = Path.joinpath(self.inference_script_root,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Лучше переименовать в path_to_script, поскольку здесь явно режимов нет.

shape_dict = {self.args['input_name']: self.args['input_shape']}
log.info('Creating graph module from MXNet model')
model, params = tvm.relay.frontend.from_mxnet(net, shape_dict)
with tvm.transform.PassContext(opt_level=3):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вот здесь выставляется уровень оптимизации, но не понятно, почему именно такой?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

В TVM по умолчанию 2, если не указывать явно. Но в примерах и прочем используют 3. Возможно стоит задать параметром, это влияет на производительность.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А на этот параметр есть какие-то ограничения? Потому что я выставляю и 10, и 100, и 1000 и оно работает.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Как я понимаю там стоит условие, что оптимизация используется если ее уровень не меньше указанного в opt_level. Максимальный уровень у оптимизаций 4, то есть при opt_level=4 и выше будут включены все оптимизации.

log.info('Converting output tensor to print results')
res = prepare_output(result, args.task, args.output_names)
log.info('Inference results')
io.process_output(res, log)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Во всех скриптах вывода есть параметр raw_output, поэтому печать выполняется так, как показано ниже. В целом по структуре (логические отступы и прочее) основной функции надо посмотреть в других скриптах вывода, например, в onnx

if not args.raw_output:
            if args.number_iter == 1:
                try:
                    log.info('Converting output tensor to print results')
                    result = prepare_output(result, args.output_names, args.model_name, args.task, args)
                    log.info('Inference results')
                    io.process_output(result, log)
                except Exception as ex:
                    log.warning('Error when printing inference results. {0}'.format(str(ex)))

        log.info(f'Performance results:\n{json.dumps(inference_result, indent=4)}')

log.info('Converting output tensor to print results')
res = prepare_output(result, args.task, args.output_names)
log.info('Inference results')
io.process_output(res, log)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Аналогичный комментарий.

module.set_input(input_name, slice_input[input_name])
module.run()
res = module.get_output(0)
return res
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А мы можем вот эти три функции вытащить в отдельный скрипт? Если я правильно вижу, то они дублируются для всех фреймворков.

log.info('Converting output tensor to print results')
res = prepare_output(result, args.task, args.output_names)
log.info('Inference results')
io.process_output(res, log)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Аналогичный комментарий касательно структуры скрипта.

if device == 'CPU':
log.info(f'Inference will be executed on {device}')
target = tvm.target.Target('llvm')
dev = tvm.cpu(0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А если передан не CPU? Тем более, что вроде бы для MXNet фигурирует NVIDIA_GPU.

self._converting['mean'][1],
self._converting['mean'][2]])
for i in range(image.shape[2]):
image[:, :, i] /= 255
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А зачем тут всегда делать на 255?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Я предполагаю, что std и mean подаются в диапазоне от [0,1], поэтому все пиксели изображения нормируются. В любом случае, нормировка — опциональный параметр. И я пока что имел дело только с ImageNET'овскими std и mean, которые бывают как нормированные, так и не нормированные.

@ismukhin
Copy link
Contributor Author

@valentina-kustikova, исправления выложил

@@ -17,6 +17,7 @@
- [OpenCV][opencv].
- [MXNet][mxnet].
- [PyTorch][pytorch].
- [TVM][tvm].
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

В основном readme в корне репозитория тоже надо добавить такую строчку.

@valentina-kustikova
Copy link
Contributor

@maslovaz, @n-berezina-nn, написали поддержку бенчмаркинга средствами TVM моделей в формате MXNet, PyTorch, ONNRuntime. Посмотрите, пожалуйста. Отдельным пулл-реквестом добавим поддержку бенчмаркинга моделей в формате TensorFlow, TensorFlow lite и оптимизированных моделей в формате TVM.

@valentina-kustikova
Copy link
Contributor

TODO:

  • Поддержка MXNet
  • Поддержка PyTorch
  • Поддержка ONNX (падают на стороне TVM)
  • Запуск инференса для вывода (1 итерация)
  • Замеры времени
  • Поддержка бенчмарка
  • Тест запуска бенчмарка

@ismukhin, обновите, пожалуйста, этот статус в соответствии с тем, что сейчас сделано. Спасибо!

@valentina-kustikova
Copy link
Contributor

@maslovaz, @n-berezina-nn, написали поддержку бенчмаркинга средствами TVM моделей в формате MXNet, PyTorch, ONNRuntime. Посмотрите, пожалуйста. Отдельным пулл-реквестом добавим поддержку бенчмаркинга моделей в формате TensorFlow, TensorFlow lite и оптимизированных моделей в формате TVM.

@maslovaz, @n-berezina-nn, посмотрите, или мы заливаем?

@valentina-kustikova valentina-kustikova merged commit 8bbd440 into itlab-vision:master Nov 7, 2023
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants