Обработка возвращаемых результатов

ONNX Runtime Web (ORT Web) предоставляет мощные средства для работы с моделями машинного обучения непосредственно в браузере или в среде Node.js. После выполнения инференса одной из ключевых задач является корректная обработка возвращаемых результатов, представленных в виде тензоров.

Формат возвращаемых данных

Метод session.run(feeds) возвращает объект, где ключи соответствуют именам выходных узлов модели, а значения — тензоры, представленные экземплярами Tensor. Основные свойства этих объектов:

  • data — одномерный массив значений тензора, тип зависит от данных модели (Float32Array, Int32Array и т.д.).
  • dims — массив чисел, задающий форму (размерности) тензора.
  • type — тип данных элементов тензора (float32, int32, bool и др.).

Пример структуры тензора:

{
  data: Float32Array(6) [0.1, 0.9, 0.2, 0.8, 0.5, 0.5],
  dims: [2, 3],
  type: 'float32'
}

Извлечение и преобразование данных

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

function reshape(data, dims) {
  if (dims.length === 0) return data[0];
  const size = dims[0];
  const rest = dims.slice(1);
  const step = data.length / size;
  const result = [];
  for (let i = 0; i < size; i++) {
    result.push(reshape(data.slice(i * step, (i + 1) * step), rest));
  }
  return result;
}

// Использование:
const outputTensor = result['output_name'];
const outputArray = reshape(outputTensor.data, outputTensor.dims);

Работа с несколькими выходами

Модели ONNX могут иметь несколько выходов. В этом случае session.run возвращает объект, где каждый ключ — имя выходного узла. Для удобного доступа можно использовать деструктуризацию:

const { output1, output2 } = await session.run({ input: inputTensor });
const array1 = reshape(output1.data, output1.dims);
const array2 = reshape(output2.data, output2.dims);

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

Асинхронная обработка больших тензоров

Для крупных моделей или потоковой инференции может потребоваться асинхронная обработка данных. Несмотря на то, что session.run сам по себе асинхронен, дополнительная оптимизация возможна с использованием Web Workers или requestIdleCallback для выполнения преобразования больших тензоров в фоне, не блокируя UI.

function processTensorAsync(tensor) {
  return new Promise((resolve) => {
    requestIdleCallback(() => {
      resolve(reshape(tensor.data, tensor.dims));
    });
  });
}

const processedOutput = await processTensorAsync(result['output_name']);

Визуализация и постобработка

После извлечения данных тензора часто требуется их нормализация или отображение. Например, для изображений данные обычно преобразуются в диапазон [0, 255]:

const normalized = outputArray.map(row => row.map(value => Math.round(value * 255)));

Для текстовых моделей или классификаторов данные могут быть дополнительно интерпретированы с помощью функций argmax или сортировки вероятностей:

function argmax(array) {
  return array.reduce((maxIndex, value, index, arr) => value > arr[maxIndex] ? index : maxIndex, 0);
}

const predictedClass = argmax(outputArray[0]);

Особенности типов данных

Тип данных тензора напрямую влияет на способы обработки:

  • float32 — подходит для математических вычислений, нормализации, визуализации.
  • int32 — чаще используется для индексов классов или категорий.
  • bool — преобразуется в логические значения для масок или условий.
  • string — встречается в NLP-моделях, требует конкатенации и декодирования в текст.

Важно проверять свойство type перед обработкой, чтобы избежать некорректного преобразования.

Оптимизация памяти

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

const data = outputTensor.data;
let sum = 0;
for (let i = 0; i < data.length; i++) {
  sum += data[i];
}
const mean = sum / data.length;

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

Интеграция с библиотеками визуализации

Для графиков, гистограмм или изображений данные из ONNX Runtime Web удобно передавать в популярные JavaScript-библиотеки (Chart.js, D3.js, Three.js, Canvas). Перед этим массивы тензоров нужно адаптировать под формат, который ожидает библиотека: двумерные массивы, плоские массивы RGB или последовательности чисел.