Как запустить модели ONNX Runtime в Windows

Как запустить модели ONNX Runtime в Windows

ONNX Runtime позволяет запускать модели машинного обучения на обычном компьютере под управлением Windows, не устанавливая полный набор инструментов для обучения нейросетей. Это особенно удобно, когда модель уже обучена: например, нужно распознавать объекты на изображении, классифицировать текст, обрабатывать звук или выполнять локальные функции генеративного сервиса.

В таком сценарии Windows-компьютер превращается в компактную AI-платформу, а приложение получает предсказуемый и относительно быстрый движок инференса.

Главное преимущество ONNX Runtime - универсальность. Модель в формате ONNX можно запускать через Python, C#, C++, Java и другие языки, выбирать между процессором и видеокартой, подключать оптимизации и измерять реальную производительность. Но на практике результат зависит не только от самого файла модели.

Важны версия Python, разрядность системы, драйвер GPU, выбранный Execution Provider, размеры входных данных и даже способ передачи массивов в рантайм.

Ниже разберём полный путь: от подготовки Windows и установки пакетов до запуска первой модели, подключения NVIDIA или DirectML, диагностики ошибок, оптимизации скорости и упаковки решения для конечного пользователя.

Примеры ориентированы прежде всего на Python, однако принципы пригодятся и разработчикам приложений на C# или C++.

Что такое ONNX Runtime и зачем он нужен в Windows

ONNX Runtime движок выполнения моделей машинного обучения. Он не обучает нейросеть и не заменяет фреймворки вроде PyTorch или TensorFlow. Его задача проще и практичнее: загрузить уже подготовленную модель, принять входные данные, выполнить вычисления и вернуть результат.

Такой режим называется инференсом, то есть применением обученной модели.

Формат ONNX выступает своеобразным промежуточным стандартом. Модель можно обучить в PyTorch, экспортировать в ONNX, а затем запускать на Windows, Linux или встраиваемом устройстве. Благодаря этому разработчику не обязательно доставлять пользователю весь PyTorch со всеми зависимостями.

Для финального приложения часто достаточно самого файла модели, ONNX Runtime и небольшого программного кода.

Внутри ONNX-файла описаны граф вычислений, параметры слоёв, формы входов и выходов. Однако файл не определяет, где именно будут выполняться операции. Этим занимается Execution Provider - провайдер выполнения.

В зависимости от установленного пакета ONNX Runtime вычисления могут идти на CPU, видеокарте NVIDIA через CUDA, встроенной или дискретной графике через DirectML, а в некоторых конфигурациях - через TensorRT или специализированные аппаратные ускорители.

Вариант Когда подходит Что требуется
CPUExecutionProvider Универсальный запуск, сервер без GPU, тестирование Только процессор и пакет onnxruntime
CUDAExecutionProvider NVIDIA GPU и высокая скорость инференса Совместимые драйверы, CUDA-зависимости, onnxruntime-gpu
DirectMLExecutionProvider AMD, Intel, NVIDIA и разные модели видеокарт Windows 10 или новее, пакет onnxruntime-directml
TensorRTExecutionProvider Максимальная производительность на поддерживаемых NVIDIA GPU TensorRT, CUDA и тщательная настройка модели

Важное уточнение: наличие видеокарты само по себе не означает, что ONNX Runtime автоматически будет ею пользоваться. Если установить обычный пакет onnxruntime, приложение обычно работает на CPU.

Чтобы задействовать ускоритель, нужно установить соответствующую сборку, проверить список доступных провайдеров и явно выбрать нужный порядок их использования.

ONNX Runtime особенно полезен в прикладных Hi-Tech-задачах.

Он подходит для локального распознавания лиц и объектов, OCR, анализа камер наблюдения, классификации документов, голосовых интерфейсов, предиктивного обслуживания оборудования и автономных функций умного дома.

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

Подготовка Windows и выбор конфигурации

Перед установкой стоит определить, на каком железе будет выполняться модель. Для простого теста подойдёт почти любой современный компьютер с Windows 10 или Windows 11.

Но если планируется обработка видео в реальном времени, запуск нескольких потоков или работа с крупной языковой моделью, требования заметно возрастают.

Узким местом может стать не только вычислительная мощность, но и объём оперативной памяти, пропускная способность диска или видеопамять.

Для первого запуска удобно использовать 64-разрядный Python. Слишком старые версии Python могут конфликтовать с актуальными колёсами ONNX Runtime, а 32-разрядная система ограничивает доступную память и часто исключается из современных сценариев.

На практике разумно создавать отдельное виртуальное окружение под каждый проект: так версии библиотек не начинают спорить друг с другом.

  • Windows 10 или Windows 11 с актуальными обновлениями;
  • 64-разрядный Python совместимой версии;
  • не менее 8 ГБ оперативной памяти для небольших моделей;
  • свободное место для Python-пакетов, модели и временных файлов;
  • драйвер видеокарты, если планируется аппаратное ускорение;
  • права пользователя на создание окружений и установку пакетов.

Для проверки разрядности Windows можно открыть сведения о системе и посмотреть тип системы. В командной строке полезно выполнить python --version или py --version.

Если команда не найдена, Python не установлен или его путь не добавлен в переменную PATH. В таком случае лучше переустановить Python и включить пункт добавления интерпретатора в PATH либо использовать команду запуска через модуль py.

Видеокарту следует оценивать не по одному названию, а по реальным задачам. Для нейросетей важны поддержка нужного API, объём VRAM, вычислительные блоки и стабильность драйвера.

NVIDIA обычно даёт самый прямой путь через CUDA, но это не означает, что любая CUDA-версия совместима с любым пакетом ONNX Runtime. У AMD и Intel часто удобнее начинать с DirectML: он не требует привязывать проект к одной архитектуре, хотя производительность может отличаться.

Если задача - просто проверить модель, не стоит сразу тратить время на настройку GPU. Сначала добейтесь корректного запуска на CPU.

Это даёт контрольную точку: станет понятно, что файл модели читается, входные данные сформированы правильно, а ошибка ускорителя не смешивается с ошибкой самого приложения.

Установка Python-пакетов и создание проекта

Создайте отдельную папку проекта, например C:\ai\onnx-demo, и откройте в ней PowerShell. Виртуальное окружение создаётся командой py -m venv.venv.

Затем его нужно активировать: .venv\Scripts\Activate.ps1. Если PowerShell блокирует выполнение скрипта, это связано с политикой безопасности Windows.

Временно разрешить сценарии для текущего пользователя можно соответствующей настройкой ExecutionPolicy, но менять её следует осознанно и только на доверенной машине.

После активации в начале строки терминала появится имя окружения. Теперь обновите инструменты установки командой python -m pip install --upgrade pip. Такой формат надёжнее, чем отдельный вызов pip: он гарантирует, что пакет устанавливается именно в активный интерпретатор.

Для CPU-режима достаточно установить onnxruntime, а для работы с массивами и изображениями понадобятся numpy и, например, Pillow.

py -m venv.venv
.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install onnxruntime numpy pillow

Существует важное правило: не следует устанавливать в одно окружение одновременно пакеты onnxruntime и onnxruntime-gpu, если нет специальной причины и полного понимания зависимостей. Они предоставляют один и тот же Python-модуль, поэтому могут перезаписывать файлы друг друга.

Для GPU-режима обычно удаляют CPU-сборку и ставят подходящий пакет ускорения.

Для DirectML применяется отдельная установка, например python -m pip install onnxruntime-directml. После этого желательно убедиться, что в окружении не остался конфликтующий пакет. Проверить установленную конфигурацию можно командами python -m pip list и python -m pip show onnxruntime-directml.

Если проект должен быть воспроизводимым, сохраните зависимости в файл:

python -m pip freeze > requirements.txt

Однако файл requirements.txt, созданный на одной машине, не всегда идеально переносится на другую. В нём могут оказаться версии, зависящие от архитектуры, драйверов и конкретного Python.

Для внутреннего проекта это нормально, но для распространения приложения лучше зафиксировать проверенный набор версий и описать требования к Windows и GPU отдельно.

Если установка завершается ошибкой, сначала проверьте доступ к репозиторию пакетов, версию Python и наличие свободного места. Сообщение вроде "No matching distribution found" часто означает не поломку ONNX Runtime, а отсутствие колеса под выбранную комбинацию Python и Windows.

В таком случае помогает установка поддерживаемой версии Python в новое окружение, а не бесконечное повторение той же команды.

Получение и проверка ONNX-модели

ONNX Runtime не создаёт модель из воздуха: нужен готовый файл с расширением .onnx. Его можно экспортировать самостоятельно из PyTorch или TensorFlow либо получить из открытого каталога моделей, внутреннего хранилища компании и другого доверенного источника.

Скачанный файл следует рассматривать как исполняемый артефакт: модель может быть большой, содержать нестандартные операции и требовать внешние файлы весов.

Не каждая ONNX-модель запускается одинаково. В описании должны быть указаны назначение модели, порядок каналов изображения, размер входа, тип данных и ожидаемая нормализация.

Например, одна модель ждёт тензор формы [1, 3, 224, 224] в формате float32, а другая принимает изображение [1, 224, 224, 3]. Если перепутать каналы или диапазон значений, программа может отработать без единой ошибки, но результат будет бессмысленным.

Для первичной проверки удобно установить пакет onnx и выполнить проверку графа:

python -m pip install onnx
import onnx

model = onnx.load("model.onnx")
onnx.checker.check_model(model)
print("Модель прошла базовую проверку")

Проверка не гарантирует, что модель будет поддержана конкретным провайдером. Она лишь показывает, что структура файла соответствует базовым требованиям ONNX.

Ограничения могут появиться позже: например, CPU понимает нужную операцию, а выбранный GPU-провайдер - нет. Поэтому модель важно тестировать в том окружении, где она будет использоваться в реальном продукте.

Полезно посмотреть входы и выходы средствами самого ONNX Runtime. Это позволит не гадать о названиях тензоров и их формах:

import onnxruntime as ort

session = ort.InferenceSession(
 "model.onnx",
 providers=["CPUExecutionProvider"]
)

print("Входы:")
for item in session.get_inputs():
 print(item.name, item.shape, item.type)

print("Выходы:")
for item in session.get_outputs():
 print(item.name, item.shape, item.type)

Форма может содержать символические размеры, например batch или height. Это означает динамический вход.

Динамичность удобна, но иногда мешает оптимизации, особенно на GPU. Если приложение всегда работает с одним размером изображения, фиксированный shape способен упростить планирование вычислений и немного снизить задержку.

В крупных моделях рядом с основным файлом могут находиться дополнительные файлы весов. Перемещение только model.onnx в другую папку приведёт к ошибке загрузки. Храните связанный набор файлов вместе и учитывайте регистр имён, относительные пути и права доступа.

На Windows также стоит избегать чрезмерно длинных путей: некоторые инструменты и старые библиотеки обрабатывают их нестабильно.

Первый запуск модели на процессоре

Начинать лучше с CPUExecutionProvider. Он работает почти на любой современной Windows-системе и служит эталоном для дальнейшего сравнения.

Создание сессии выглядит просто: передаём путь к ONNX-файлу, а затем формируем словарь, где ключом является имя входного тензора, полученное через session.get_inputs().

import numpy as np
import onnxruntime as ort

model_path = "model.onnx"

session = ort.InferenceSession(
 model_path,
 providers=["CPUExecutionProvider"]
)

input_info = session.get_inputs()[0]
input_name = input_info.name

sample = np.random.rand(1, 3, 224, 224).astype(np.float32)
outputs = session.run(None, {input_name: sample})

for index, output in enumerate(outputs):
 print(index, output.shape, output.dtype)

Этот пример не обещает осмысленного результата, потому что случайный массив не является настоящим изображением или текстом.

Его задача - проверить сам конвейер: файл открывается, входной тензор подходит по форме и типу, вычисления завершаются, а выходы возвращаются в Python. Для полноценного теста нужно реализовать препроцессинг и постпроцессинг под конкретную модель.

Частая ошибка - передача массива типа float64. NumPy по умолчанию нередко создаёт именно такой тип, а модель ожидает float32. В результате появляется сообщение о несовпадении типов или происходит лишнее преобразование.

Используйте astype(np.float32) явно. Для моделей, работающих с целыми числами, применяйте нужный тип, например int64 или uint8, строго по описанию входа.

Загрузка сессии может занимать заметное время, особенно у крупных моделей. Поэтому не создавайте InferenceSession при каждом изображении.

Создайте её один раз при запуске приложения и переиспользуйте. Это одно из самых ощутимых практических улучшений: повторное открытие графа и подготовка оптимизированного представления способны сделать поток обработки в разы медленнее.

Пример класса-обёртки помогает отделить загрузку модели от вызова:

class OnnxRunner:
 def init(self, path):
 self.session = ort.InferenceSession(
 path,
 providers=["CPUExecutionProvider"]
 )
 self.input_name = self.session.get_inputs()[0].name

 def predict(self, array):
 array = np.asarray(array, dtype=np.float32)
 return self.session.run(None, {self.input_name: array})

runner = OnnxRunner("model.onnx")
result = runner.predict(sample)
print(result[0])

Если модель имеет несколько входов, нельзя ограничиваться первым элементом списка. Получите все входы, сопоставьте имена с нужными данными и передайте полный словарь.

Схожая ситуация встречается в моделях для текста, где отдельно подаются идентификаторы токенов, маска внимания и иногда идентификаторы сегментов.

Результат также не всегда является готовой надписью.

Детектор объектов возвращает координаты, классы и оценки уверенности; классификатор - массив логитов или вероятностей; модель распознавания речи - последовательность токенов.

Постпроцессинг - часть алгоритма, а не косметическая деталь. Например, для классификации обычно применяют softmax, выбирают максимальный индекс и переводят его в название класса.

Подключение видеокарты NVIDIA через CUDA

Для NVIDIA обычно используется пакет ONNX Runtime с поддержкой CUDA. Он позволяет перенести поддерживаемые операции на GPU, но требует совместимости нескольких компонентов: драйвера NVIDIA, CUDA-зависимостей, версии Python и самой сборки ONNX Runtime.

Поэтому GPU-настройка не сводится к одной команде установки.

Перед изменениями проверьте, видит ли Windows видеокарту и работает ли драйвер. Утилита командной строки NVIDIA обычно показывает модель адаптера, версию драйвера и доступную видеопамять. Если драйвер не установлен корректно, ONNX Runtime не сможет исправить ситуацию самостоятельно.

Сначала добейтесь нормального состояния драйвера, а уже потом разбирайтесь с Python-пакетами.

Типичный сценарий начинается с удаления CPU-сборки и установки GPU-варианта:

python -m pip uninstall onnxruntime onnxruntime-gpu
python -m pip install onnxruntime-gpu

Конкретные версии следует подбирать по таблице совместимости выбранного релиза.

Пакет может ожидать определённую ветку CUDA или содержать часть необходимых библиотек, но не весь набор. Если версия драйвера слишком старая, а CUDA-зависимости не находятся, появятся ошибки загрузки DLL или провайдер CUDA не будет доступен.

Проверить список провайдеров можно так:

import onnxruntime as ort

print(ort.get_available_providers())

Если в списке есть CUDAExecutionProvider, это ещё не стопроцентная гарантия успешного запуска конкретной модели, но хороший знак. Далее создайте сессию с явным приоритетом CUDA:

session = ort.InferenceSession(
 "model.onnx",
 providers=[
 "CUDAExecutionProvider",
 "CPUExecutionProvider"
 ]
)

print(session.get_providers())

CPU в этом списке играет роль запасного варианта. Если отдельная операция не поддерживается CUDA-провайдером, рантайм может передать её CPU, хотя такие переходы между устройствами иногда снижают производительность.

Для диагностики полезно временно оставить только CUDA и посмотреть, на каком месте возникнет ошибка, но в рабочем приложении резервный CPU часто повышает устойчивость.

Важный параметр - размер пакета, или batch size. Обработка одного изображения за вызов может быть оптимальной для интерактивного приложения, но при массовой обработке сотен файлов GPU лучше загружается пакетами. С другой стороны, увеличение batch требует больше VRAM.

При переполнении памяти приложение получит ошибку, поэтому размер нужно подбирать экспериментально, начиная с небольших значений.

Не сравнивайте CPU и GPU по одному короткому запуску. Первое выполнение включает создание сессии, подготовку ядер и возможную инициализацию CUDA.

Измеряйте несколько десятков повторов после прогрева и разделяйте время препроцессинга, самого инференса и постпроцессинга. Иначе можно решить, что видеокарта медленнее, хотя в измерение случайно попала стоимость холодного старта.

Универсальное ускорение через DirectML

DirectML - аппаратный API Windows для задач машинного обучения. Его ценность в том, что он не привязан только к NVIDIA. В зависимости от драйвера и возможностей устройства через него могут работать видеокарты AMD, Intel и NVIDIA, а также некоторые встроенные графические адаптеры.

Это удобный вариант для настольных ПК и ноутбуков, где нельзя гарантировать конкретного производителя GPU.

Установка обычно выполняется в отдельном окружении:

python -m pip uninstall onnxruntime onnxruntime-gpu
python -m pip install onnxruntime-directml

Затем проверьте наличие провайдера:

import onnxruntime as ort

print(ort.get_available_providers())

Для создания сессии указывается DirectMLExecutionProvider:

session = ort.InferenceSession(
 "model.onnx",
 providers=[
 "DmlExecutionProvider",
 "CPUExecutionProvider"
 ]
)

В некоторых версиях и примерах имя провайдера отображается в форме DmlExecutionProvider, поэтому ориентируйтесь на фактический список, который печатает установленная библиотека.

Регистр и точное написание имеют значение. Если указать несуществующее имя, рантайм либо выдаст предупреждение, либо перейдёт к CPU, и пользователь заметит это только по низкой скорости.

DirectML не является гарантией ускорения любой модели. Поддержка операторов, динамических форм и конкретных типов данных зависит от реализации и драйвера.

Иногда простая модель классификации работает отлично, а сложный граф с нестандартными операциями частично уходит на CPU.

Для приложений реального времени особенно важно проверить не только среднее время, но и задержку отдельных кадров: переходы между устройствами могут создавать неприятные скачки.

На ноутбуках Windows может выбрать встроенную графику вместо дискретной. Это зависит от параметров энергопотребления и настроек графики в системе.

Если производительность неожиданно низкая, проверьте, какой адаптер назначен приложению. Также учитывайте, что встроенная графика использует общую оперативную память, поэтому крупная модель способна замедлить не только инференс, но и сам интерфейс Windows.

DirectML часто разумно выбирать как компромисс. Он проще в развёртывании на разнородном парке компьютеров, чем CUDA, но обычно требует более тщательного тестирования.

В коммерческом продукте полезно сделать автоматический выбор: попробовать DirectML, проверить успешный запуск тестового запроса, а при неудаче перейти на CPU и записать понятное сообщение в журнал.

Настройка сессии и оптимизация производительности

После корректного запуска можно переходить к оптимизации. Первая настройка - уровень оптимизации графа. ONNX Runtime умеет объединять операции, удалять лишние узлы и выбирать более эффективные варианты вычислений.

Для большинства моделей подходит стандартная оптимизация, однако при повторных запусках можно сохранить оптимизированную модель, чтобы не выполнять эту работу при каждом старте.

import onnxruntime as ort

options = ort.SessionOptions()
options.graph_optimization_level = (
 ort.GraphOptimizationLevel.ORT_ENABLE_ALL
)

session = ort.InferenceSession(
 "model.onnx",
 sess_options=options,
 providers=["CPUExecutionProvider"]
)

На CPU важны параметры потоков. intra_op_num_threads задаёт число потоков внутри одной операции, а inter_op_num_threads влияет на параллельное выполнение независимых операций.

Установка максимального количества потоков не всегда ускоряет программу: другие процессы Windows, обработка изображения и запись результата тоже требуют ресурсов.

options = ort.SessionOptions()
options.intra_op_num_threads = 4
options.inter_op_num_threads = 1

session = ort.InferenceSession(
 "model.onnx",
 sess_options=options,
 providers=["CPUExecutionProvider"]
)

Оптимальное значение зависит от процессора и характера модели. Для одного запроса за раз часто эффективнее использовать несколько потоков внутри операции. Для большого числа независимых запросов можно распределять работу между задачами приложения, но чрезмерный параллелизм приводит к конкуренции за ядра и память.

Измеряйте скорость именно в целевом режиме, а не в искусственном микротесте.

Большой эффект даёт правильная работа с памятью. Не создавайте лишние копии массивов без необходимости, используйте заранее выделенные буферы и следите за непрерывностью NumPy-массива. Преобразование изображения из формата HWC в CHW, изменение размера и нормализация должны выполняться последовательно и предсказуемо.

Если препроцессинг занимает большую долю времени, ускорение самого графа почти не изменит общую задержку.

Квантизация уменьшает размер модели и может ускорить вычисления, особенно на CPU. При динамической квантизации веса или активации преобразуются в более компактные представления, например int8. Но точность может снизиться, а не все операции и провайдеры одинаково хорошо поддерживают выбранный формат.

Поэтому после квантизации нужно сравнивать не только миллисекунды, но и качество на контрольном наборе данных.

FP16 иногда помогает на GPU, если аппарат и провайдер хорошо работают с половинной точностью. На обычном CPU такая модель не обязательно станет быстрее. Более того, некоторые операции могут потребовать преобразования типов, что сведёт выигрыш к нулю.

Оптимизация должна исходить из профиля конкретной модели, а не из модного списка техник.

Включить профилирование можно средствами SessionOptions:

options = ort.SessionOptions()
options.enable_profiling = True

session = ort.InferenceSession(
 "model.onnx",
 sess_options=options,
 providers=["CPUExecutionProvider"]
)

session.run(None, {input_name: sample})
profile_file = session.end_profiling()
print(profile_file)

Профиль помогает увидеть узкие места: тяжёлые операторы, лишние преобразования, задержки копирования и распределение нагрузки между провайдерами. Без профилирования разработчик часто оптимизирует не тот участок.

Например, нейросеть занимает 12 миллисекунд, а декодирование изображения - 40. В таком случае замена CPU на GPU даст менее заметный эффект, чем улучшение работы с файлами.

Интеграция ONNX Runtime в приложение Windows

Для прототипов Python удобен, но конечное Windows-приложение может быть написано на C# или C++. Для.NET существует пакет ONNX Runtime, а для GPU применяются соответствующие варианты с поддержкой CUDA или DirectML.

Общая логика остаётся той же: создать окружение, загрузить модель, получить описание входов, сформировать тензор и вызвать сессию.

В C# важно правильно управлять временем жизни объектов. Сессия модели должна жить столько же, сколько сервис инференса, а не создаваться на каждый запрос. Тензоры и буферы следует освобождать своевременно, особенно при обработке видеопотока.

При утечке памяти приложение может выглядеть стабильным несколько минут, а затем начать тормозить или завершиться с ошибкой.

Для десктопного интерфейса нельзя выполнять тяжёлый инференс в главном UI-потоке.

Иначе окно перестанет отвечать, кнопки будут запаздывать, а Windows отметит приложение как зависшее. Используйте фоновые задачи, очереди запросов и механизм отмены.

Для камеры полезно ограничивать очередь кадров: нет смысла обрабатывать каждый старый кадр, если пользователь уже видит более новый.

Хорошая архитектура разделяет приложение на несколько этапов:

  • загрузка модели и проверка доступного провайдера;
  • получение данных из камеры, файла, микрофона или сети;
  • препроцессинг и приведение к нужной форме;
  • инференс через единственную долгоживущую сессию;
  • постпроцессинг, фильтрация и визуализация результата;
  • логирование ошибок, времени и состояния оборудования.

Если приложение распространяется как самостоятельный установщик, нужно включить файл модели, DLL ONNX Runtime и связанные зависимости. Путь к модели не должен зависеть от текущей рабочей папки: при запуске через ярлык она может отличаться от каталога программы.

Формируйте путь относительно расположения исполняемого файла или используйте настройки приложения.

Для Python-проектов применяют PyInstaller и похожие инструменты упаковки. Но автоматический сборщик не всегда находит ONNX-файлы, внешние веса и провайдерные DLL.

Их нужно добавлять явно и проверять сборку на чистой Windows-машине без установленного Python. Тест на компьютере разработчика недостаточен: там случайно могут присутствовать CUDA-библиотеки, Visual C++ Runtime и другие компоненты.

Логи должны сообщать пользователю не только "ошибка запуска модели", а полезную причину: какой файл не найден, какой вход ожидался, какой провайдер выбран, сколько памяти доступно и на каком этапе произошёл сбой. При этом не стоит записывать в журнал персональные изображения, аудио и токены доступа.

Для Hi-Tech-продуктов конфиденциальность логов так же важна, как скорость.

Диагностика типичных ошибок

Одна из самых частых проблем - несовпадение формы входа. Сообщение может выглядеть сложно, но смысл обычно прост: модель ждёт одну размерность, а приложение передало другую.

Проверьте порядок каналов, размер batch и соответствие динамическим осям. Для изображения особенно часто встречается путаница между HWC и CHW: библиотека Pillow отдаёт данные как высота, ширина, каналы, а многие нейросети ожидают каналы, высота, ширина.

Вторая популярная причина - неверный тип. Модель может ожидать float32, а получает float64, либо ждёт int64, а получает int32.

Посмотрите поле input.type и приводите данные явно. Не полагайтесь на автоматическое преобразование: оно может привести к ошибке, дополнительным копиям или незаметной потере качества.

Если появляется ошибка о DLL, проверьте разрядность Python, пакет Visual C++ Runtime, драйвер GPU и совместимость CUDA. Не копируйте случайные DLL из интернета в системные каталоги: это создаёт новые проблемы безопасности и версий. Лучше переустановить официальные компоненты в виртуальном окружении и использовать подтверждённую комбинацию релизов.

Когда GPU-провайдер не появляется в списке, возможны несколько сценариев:

  • установлен не тот пакет ONNX Runtime;
  • CPU- и GPU-пакеты конфликтуют в окружении;
  • драйвер устройства слишком старый;
  • не хватает зависимостей CUDA или DirectML недоступен;
  • используется неподдерживаемая версия Python;
  • программа запускается другим интерпретатором, не тем, где установлен пакет.

Последний пункт особенно коварен. В терминале пакет может быть установлен, а IDE запускает системный Python или другой виртуальный environment. Выведите sys.executable из кода и сравните путь с активным интерпретатором.

Это простая проверка, которая часто экономит час поисков "невидимой" установки.

import sys
import onnxruntime as ort

print(sys.executable)
print(ort.file)
print(ort.get_available_providers())

Если модель запускается, но работает медленно, проверьте, действительно ли вычисления идут на GPU. Наличие провайдера в списке недостаточно.

Смотрите загрузку GPU системными средствами, используйте профилирование и попробуйте тестовую модель. Часть операций может выполняться на CPU, а постоянное копирование данных между CPU и GPU иногда делает ускорение отрицательным.

Ошибки нехватки памяти требуют комплексного подхода. Уменьшите batch size, разрешение входного изображения или размер контекста, отключите параллельные запросы и убедитесь, что в памяти нет нескольких дублирующих сессий.

Для видеопотока не храните бесконечную очередь кадров. Если модель крупная, рассмотрите квантизацию или разделение функциональности на несколько более компактных моделей.

Для воспроизводимости сохраняйте сведения о версии Windows, Python, ONNX Runtime, драйвере, процессоре, GPU и модели.

При обновлении одного компонента результат может измениться. Небольшой диагностический отчёт, созданный при запуске, значительно упрощает поддержку продукта на десятках разных компьютеров.

Тестирование качества, скорости и надёжности

Корректный запуск - только начало. Нужно убедиться, что модель выдаёт ожидаемые результаты. Подготовьте небольшой контрольный набор входов с заранее известными ответами.

Для классификатора сравнивайте метки и вероятности, для детектора - координаты и классы, для OCR - распознанный текст, для генеративных задач - допустимые статистические или смысловые критерии.

Скорость измеряйте в нескольких режимах. Холодный старт показывает, сколько времени пользователь ждёт загрузку модели. Прогретый инференс отражает работу уже готовой сессии. Длительный тест выявляет утечки памяти, перегрев и падение частот.

Если приложение обрабатывает камеру, важны средняя частота кадров и задержка от захвата до появления результата, а не только красивое значение среднего FPS.

Метрика Что показывает Почему важна
Время загрузки Затраты на создание сессии Влияет на скорость старта приложения
Средняя задержка Типичное время одного инференса Помогает оценить производительность
Перцентиль p95 Задержку, ниже которой укладывается около 95% запросов Показывает редкие тормоза
Потребление памяти Объём RAM и VRAM во время работы Влияет на стабильность
Качество модели Точность, полноту, F1 или другую целевую метрику Не даёт ускорению испортить результат

Пример простого измерения в Python:

import time
import numpy as np

warmup = 5
iterations = 30

for _ in range(warmup):
 session.run(None, {input_name: sample})

started = time.perf_counter()

for _ in range(iterations):
 session.run(None, {input_name: sample})

elapsed = time.perf_counter() - started
print("Среднее время, мс:", elapsed / iterations * 1000)

Для более честного сравнения фиксируйте одинаковые входы, размер batch и настройки потоков. Не запускайте параллельно браузер с десятками вкладок, видеомонтаж и игру, если хотите получить сопоставимые цифры.

Но в финальном тесте, наоборот, полезно проверить нагрузку, похожую на реальную: несколько камер, одновременная запись, работа интерфейса и фоновые службы.

Проверяйте поведение при плохих данных: пустом файле, повреждённом изображении, неправильном разрешении, пропавшей камере, нехватке памяти и неожиданном отключении GPU. Приложение должно завершать отдельный запрос с понятной ошибкой, а не падать целиком.

Для сервисов полезны тайм-ауты и ограничение размера входных данных.

Если модель используется в чувствительной области, тестируйте не только среднее качество.

Сравнивайте результаты на разных условиях освещения, качества записи, языках, разрешениях и типах устройств. Аппаратное ускорение не должно менять ответ сильнее, чем допускает задача.

Небольшие расхождения из-за округления возможны, но их границы нужно заранее определить.

Практический сценарий: локальный анализ изображения

Рассмотрим типовой сценарий для Windows: пользователь выбирает фотографию, приложение изменяет размер изображения, передаёт его классификатору и показывает наиболее вероятный класс.

Такой пример полезен не сложностью, а тем, что объединяет все важные этапы: чтение модели, проверку входа, препроцессинг, инференс, постпроцессинг и обработку ошибок.

Установим Pillow и используем модель, которая ожидает изображение размером 224 на 224 в формате CHW. Конкретная нормализация зависит от обучения.

Ниже приведён распространённый вариант с переводом пикселей в диапазон от нуля до единицы и нормализацией по среднему и стандартному отклонению.

from PIL import Image
import numpy as np
import onnxruntime as ort

session = ort.InferenceSession(
 "classifier.onnx",
 providers=["CPUExecutionProvider"]
)

input_info = session.get_inputs()[0]
input_name = input_info.name

def prepare_image(path):
 image = Image.open(path).convert("RGB")
 image = image.resize((224, 224))

 array = np.asarray(image).astype(np.float32) / 255.0
 mean = np.array([0.485, 0.456, 0.406], dtype=np.float32)
 std = np.array([0.229, 0.224, 0.225], dtype=np.float32)

 array = (array - mean) / std
 array = np.transpose(array, (2, 0, 1))
 array = np.expand_dims(array, axis=0)

 return array

data = prepare_image("photo.jpg")
outputs = session.run(None, {input_name: data})
logits = outputs[0][0]

best_index = int(np.argmax(logits))
print("Индекс класса:", best_index)

Здесь есть несколько мест, которые нельзя копировать вслепую. Размер 224 на 224, порядок каналов, значения mean и std должны соответствовать именно вашей модели.

У детектора объектов будет другой конвейер, а некоторые современные модели используют letterbox с сохранением пропорций вместо простого растягивания. Ошибка на этапе подготовки может испортить результат сильнее, чем выбор CPU вместо GPU.

Если выходом являются логиты, для получения вероятностей применяют softmax:

def softmax(values):
 values = values - np.max(values)
 exp_values = np.exp(values)
 return exp_values / np.sum(exp_values)

probabilities = softmax(logits)
confidence = float(probabilities[best_index])

print("Уверенность:", confidence)

В реальном приложении добавьте файл со списком названий классов и проверку индекса.

Если уверенность ниже установленного порога, лучше показать "не уверен" или предложить повторить снимок, чем выдавать случайную метку с видом абсолютной истины. Порог зависит от модели и должен подбираться на тестовых данных.

Для интерфейса Windows можно добавить кнопку выбора файла, индикатор загрузки и вывод времени инференса. Однако не выполняйте session.run в обработчике кнопки, если модель тяжёлая.

Передайте задачу в фоновый поток, заблокируйте повторный запуск и верните результат в UI-поток после завершения. Это мелочь только на бумаге: именно такие детали определяют ощущение качества приложения.

Безопасность и распространение моделей

ONNX-файл обычно воспринимают как пассивные данные, но на практике он участвует в вычислениях и может быть очень большим. Используйте модели из доверенных источников, проверяйте контрольные суммы и не разрешайте приложению автоматически загружать произвольные файлы без ограничений.

Для сетевого сервиса ограничивайте размер входа и время обработки, чтобы модель нельзя было использовать для простого отказа в обслуживании.

Не храните ключи API, пароли и персональные данные внутри файла конфигурации рядом с моделью. Если приложение работает локально, продумайте права доступа к каталогу моделей.

Если там находятся закрытые разработки, обычные права пользователя Windows лучше, чем полный доступ для всех. Для корпоративного развёртывания пригодятся цифровая подпись установщика и контроль целостности обновлений.

Локальный инференс часто выбирают ради конфиденциальности, но он не делает систему автоматически безопасной. Временные изображения могут оставаться в кэше, журналы - содержать фрагменты запросов, а диагностические дампы - сохранять чувствительные данные. Настройте очистку временных файлов и минимизируйте содержимое логов.

Особенно внимательно относитесь к камерам, микрофонам и медицинским или биометрическим данным.

При обновлении модели храните версию рядом с артефактом и фиксируйте изменения. Новая модель может требовать другой формы входа, иной нормализации или нового списка классов.

Простая замена файла без проверки способна привести к тихому ухудшению качества. Безопасный процесс обновления включает тестовый запуск, контроль метрик и возможность отката на предыдущую версию.

Для офлайн-приложений удобно поставлять CPU-режим как гарантированный базовый вариант, а GPU подключать автоматически при успешной проверке. Такой подход снижает число неудачных установок: даже если на компьютере пользователя отсутствует нужный драйвер, основная функция остаётся доступной.

При этом стоит предупредить, что скорость на CPU будет ниже, особенно для видео и крупных моделей.

Рекомендуемый порядок запуска проекта

Практический процесс лучше строить поэтапно, не пытаясь настроить всё сразу. Сначала создайте чистое виртуальное окружение, установите CPU-версию и проверьте модель на одном тестовом входе. Затем выведите имена, формы и типы входов и выходов.

После этого реализуйте корректный препроцессинг и убедитесь, что результат соответствует контрольному примеру.

Следующий шаг - измерить базовую производительность. Зафиксируйте время загрузки, прогретую задержку, потребление памяти и качество.

Эта цифра станет точкой сравнения. Подключая CUDA или DirectML, меняйте только один фактор за раз. Если одновременно обновить драйвер, Python, ONNX Runtime и саму модель, невозможно понять, что именно повлияло на результат.

  1. Проверить версию Windows, Python и разрядность системы.
  2. Создать отдельное виртуальное окружение.
  3. Установить CPU-пакет и зависимости проекта.
  4. Проверить ONNX-файл и описать его входы и выходы.
  5. Запустить один тестовый запрос на CPU.
  6. Реализовать препроцессинг и постпроцессинг.
  7. Снять базовые метрики качества и скорости.
  8. Подключить CUDA или DirectML при необходимости.
  9. Повторить тесты на целевом компьютере.
  10. Упаковать приложение и проверить его на чистой Windows-системе.

Такой порядок кажется медленнее только в начале. На деле он экономит время: большая часть проблем локализуется в конкретном слое. Если CPU-запуск не работает, нет смысла обвинять видеодрайвер.

Если CPU работает, а CUDA нет, область поиска уже ограничена провайдером и его зависимостями. Если всё работает, но медленно, помогут профилирование и разбор этапов конвейера.

Для производственного приложения добавьте автоматическую диагностику при старте. Она может проверять существование модели, доступные Execution Providers, форму пробного входа и успешность короткого тестового инференса.

Не обязательно показывать пользователю технические детали, но записывайте их в журнал, чтобы служба поддержки могла быстро понять конфигурацию машины.

Наконец, документируйте требования. Укажите минимальную версию Windows, поддерживаемые GPU, нужный объём памяти, поведение при отсутствии ускорения и пример ожидаемой скорости. Это лучше, чем обещать "работу на любом компьютере".

ONNX Runtime действительно универсален, но универсальность не отменяет ограничений конкретной модели и железа.

Запуск ONNX Runtime в Windows не требует превращать компьютер в лабораторию машинного обучения. Достаточно правильно разделить задачу на подготовку окружения, проверку модели, создание сессии, подготовку данных и измерение результата.

Для первого прототипа почти всегда разумно выбрать CPU, а затем осознанно подключать CUDA или DirectML. Так проще отделить ошибки модели от проблем драйвера и быстрее получить рабочий результат.

В конечном счёте производительность определяется всей цепочкой, а не одной видеокартой. Быстрая модель с неправильным препроцессингом бесполезна, мощный GPU не спасает от лишних копирований, а идеально настроенная сессия не компенсирует неподходящую версию Python.

Проверяйте входы, измеряйте задержки, следите за памятью и тестируйте приложение на той Windows-конфигурации, где оно будет работать. Такой подход превращает ONNX Runtime из "ещё одной библиотеки" в надёжную основу локального AI-продукта.

Короткие ответы на частые вопросы

Можно ли запускать ONNX Runtime без видеокарты? Да. Пакет onnxruntime работает на CPU и подходит для тестов, серверов и небольших моделей. Скорость зависит от архитектуры модели и процессора.

Почему установленная видеокарта не ускоряет модель? Проверьте пакет ONNX Runtime, список доступных провайдеров, драйвер и порядок провайдеров в сессии. Также часть операций может не поддерживаться GPU и выполняться на CPU.

Нужно ли создавать сессию для каждого изображения? Нет. Сессию лучше создать один раз и переиспользовать. Повторная загрузка модели заметно увеличивает задержку и расход памяти.