ort.Tensor представляет собой ключевой объект библиотеки
ONNX Runtime Web (ORT Web) для хранения и обработки данных модели.
Каждый тензор содержит три основных элемента:
data) — одно- или многомерный
массив значений, соответствующих типу данных тензора (например,
Float32Array, Int32Array и др.).dims) — массив целых чисел,
задающий размерность тензора по каждой оси.type) — строковое
обозначение типа данных, например float32,
int32, bool.Создание тензора вручную осуществляется через конструктор
ort.Tensor:
const tensor = new ort.Tensor('float32', new Float32Array([1.0, 2.0, 3.0, 4.0]), [2, 2]);
В этом примере создается тензор размером 2×2 с четырьмя элементами
типа float32.
Доступ к данным тензора осуществляется через свойство
tensor.data. Оно возвращает плоский массив значений. Для
работы с многомерными структурами необходимо самостоятельно реализовать
развертку индексов или использовать вспомогательные функции.
Пример преобразования плоского массива в матрицу 2×2:
function reshape2D(data, rows, cols) {
const result = [];
for (let i = 0; i < rows; i++) {
const row = [];
for (let j = 0; j < cols; j++) {
row.push(data[i * cols + j]);
}
result.push(row);
}
return result;
}
const matrix = reshape2D(tensor.data, 2, 2);
reshape2D позволяет создавать более наглядное
представление тензора для последующей постобработки.
ONNX Runtime Web не предоставляет встроенных методов для сложной
матричной арифметики, поэтому операции над ort.Tensor
выполняются вручную через массивы TypedArray. Наиболее
часто используемые операции:
function addTensors(a, b) {
if (a.data.length !== b.data.length) {
throw new Error("Размеры тензоров должны совпадать");
}
const result = new Float32Array(a.data.length);
for (let i = 0; i < a.data.length; i++) {
result[i] = a.data[i] + b.data[i];
}
return new ort.Tensor('float32', result, a.dims);
}
Аналогично реализуется вычитание, умножение и деление поэлементно.
Для операций типа matmul (матрица × матрица) необходимо
явно учитывать форму тензоров:
function matmul(a, b) {
const [m, k1] = a.dims;
const [k2, n] = b.dims;
if (k1 !== k2) throw new Error("Несовпадение размерностей для умножения матриц");
const result = new Float32Array(m * n);
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
let sum = 0;
for (let k = 0; k < k1; k++) {
sum += a.data[i * k1 + k] * b.data[k * n + j];
}
result[i * n + j] = sum;
}
}
return new ort.Tensor('float32', result, [m, n]);
}
Этот метод подходит для любых двумерных тензоров и обеспечивает полное управление вычислением.
Модели ONNX часто возвращают несколько выходов или тензоры со сложной структурой (например, батч прогнозов, bounding boxes, вероятности классов). В таких случаях необходима ручная десериализация и агрегация данных:
const outputTensor = session.run(inputs)['output'];
const probabilities = outputTensor.data;
const topIndex = probabilities.indexOf(Math.max(...probabilities));
Для батчей прогнозов необходимо учитывать дополнительную размерность:
function batchArgmax(tensor) {
const [batchSize, numClasses] = tensor.dims;
const result = [];
for (let i = 0; i < batchSize; i++) {
let maxIndex = 0;
let maxValue = tensor.data[i * numClasses];
for (let j = 1; j < numClasses; j++) {
const value = tensor.data[i * numClasses + j];
if (value > maxValue) {
maxValue = value;
maxIndex = j;
}
}
result.push(maxIndex);
}
return result;
}
Модели детекции объектов возвращают несколько тензоров: координаты, вероятности и классы. Объединение их в структуру объектов:
function parseDetections(boxesTensor, scoresTensor, classesTensor, threshold = 0.5) {
const detections = [];
const numBoxes = boxesTensor.dims[0];
for (let i = 0; i < numBoxes; i++) {
const score = scoresTensor.data[i];
if (score < threshold) continue;
const box = boxesTensor.data.slice(i * 4, i * 4 + 4);
const classId = classesTensor.data[i];
detections.push({ box, score, classId });
}
return detections;
}
Такой подход позволяет полностью контролировать логику фильтрации, нормализации координат и объединения результатов.
В некоторых случаях модель возвращает сырые предсказания, требующие
дополнительной активации (softmax, sigmoid)
или нормализации:
function softmax(arr) {
const max = Math.max(...arr);
const exps = arr.map(x => Math.exp(x - max));
const sum = exps.reduce((a, b) => a + b, 0);
return exps.map(v => v / sum);
}
Для батчей применяется по каждой строке тензора:
function batchSoftmax(tensor) {
const [batchSize, numClasses] = tensor.dims;
const result = new Float32Array(tensor.data.length);
for (let i = 0; i < batchSize; i++) {
const slice = tensor.data.slice(i * numClasses, (i + 1) * numClasses);
const soft = softmax(slice);
result.set(soft, i * numClasses);
}
return new ort.Tensor('float32', result, tensor.dims);
}
Работа с ort.Tensor требует аккуратного сочетания:
Применение этих методов позволяет обрабатывать любые выходные данные модели, управляя ими полностью в коде на JavaScript без зависимости от сторонних библиотек.