Индексирование и срезы

Keras.js — это библиотека для выполнения моделей Keras прямо в браузере с использованием JavaScript. При работе с данными, полученными из модели или подаваемыми на вход, часто требуется манипулировать многомерными массивами — тензорами. Основной инструмент для этого — индексирование и срезы.


Основы тензоров

В Keras.js данные представлены в виде объектов Tensor. Тензор — это многомерный массив с фиксированной размерностью. Каждый тензор имеет атрибуты:

  • shape — массив, определяющий размеры по каждой оси.
  • data — одномерный массив, содержащий значения тензора в линейном виде.

Например, тензор формы [2, 3, 4] содержит 2 * 3 * 4 = 24 элемента в data. Фактическая организация данных одномерная, поэтому для доступа к элементу требуется индексация с расчётом смещения.


Индексация одномерного тензора

Одномерный тензор индексируется напрямую:

const tensor = new KerasJS.Tensor({data: [1, 2, 3, 4], shape: [4]});
console.log(tensor.data[2]); // 3
  • Индексация начинается с нуля.
  • Поддерживаются отрицательные индексы, которые отсчитываются с конца массива:
console.log(tensor.data[-1 + tensor.shape[0]]); // 4

Индексация многомерного тензора

Для многомерных тензоров необходимо вычислять линейный индекс:

const tensor = new KerasJS.Tensor({data: Array.from({length: 24}, (_, i) => i+1), shape: [2, 3, 4]});

Чтобы получить элемент [i, j, k], используется формула:

linear_index = i * (shape[1]*shape[2]) + j * shape[2] + k

Пример:

const i = 1, j = 2, k = 3;
const linearIndex = i * (3*4) + j * 4 + k; // 1*12 + 2*4 + 3 = 23
console.log(tensor.data[linearIndex]); // 24

Ключевой момент: Keras.js хранит данные в row-major порядке (C-style), что важно при работе с индексами.


Срезы

Срезы позволяют выделять подмассивы тензора по определённым диапазонам индексов. В Keras.js нет встроенного синтаксиса tensor[:, 1:3, :], как в Python, поэтому срезы реализуются вручную через методы:

  • sliceTensor(startIndices, endIndices) — возвращает новый тензор.

Параметры:

  • startIndices — массив начальных индексов для каждой оси.
  • endIndices — массив конечных индексов (не включая).

Пример:

const slice = tensor.sliceTensor([0,1,0],[2,3,4]);

Это соответствует Python-подобному срезу [0:2,1:3,0:4].


Продвинутые срезы: шаг и выбор осей

Для реализации шага (step) нужно вручную вычислить линейные индексы:

function sliceWithStep(tensor, axis, step) {
  const shape = tensor.shape;
  const newData = [];
  const stride = shape.slice(axis+1).reduce((a,b)=>a*b,1);
  const repeat = shape.slice(0,axis).reduce((a,b)=>a*b,1);

  for(let r=0;r<repeat;r++){
    for(let i=0;i<shape[axis];i+=step){
      const base = r * stride * shape[axis] + i * stride;
      for(let j=0;j<stride;j++){
        newData.push(tensor.data[base+j]);
      }
    }
  }
  const newShape = shape.slice();
  newShape[axis] = Math.ceil(shape[axis]/step);
  return new KerasJS.Tensor({data: newData, shape: newShape});
}

Индексирование с использованием масок

Для выборки элементов по условию создаются булевы маски. В Keras.js это делается вручную через фильтрацию:

const tensor = new KerasJS.Tensor({data: [1,2,3,4,5], shape: [5]});
const mask = tensor.data.map(v => v % 2 === 0);
const filteredData = tensor.data.filter((v,i)=>mask[i]);
const filteredTensor = new KerasJS.Tensor({data: filteredData, shape: [filteredData.length]});

Это аналог tensor[tensor % 2 === 0] в NumPy/Keras.


Изменение формы после среза

После среза или фильтрации необходимо корректно задавать shape. Пропуск этого шага приведёт к несоответствию данных и размерности:

const newTensor = new KerasJS.Tensor({data: slicedData, shape: [2,2]});
  • Размерность (shape) должна совпадать с количеством элементов.
  • Для многомерных срезов требуется пересчитать размерность по каждой оси.

Практические рекомендации

  • Всегда проверять tensor.shape перед индексированием.
  • Для многомерных тензоров удобнее создавать функции-конвертеры между [i,j,k] и линейным индексом.
  • Срезы в Keras.js требуют явного указания диапазонов — нельзя использовать Python-стиль : напрямую.
  • Булевы маски полезны для динамической фильтрации данных после вывода модели.

Индексирование и срезы — фундаментальная часть работы с тензорами в Keras.js. Грамотное использование этих методов позволяет эффективно управлять данными, подаваемыми на вход или получаемыми с выхода модели, обеспечивая корректность вычислений и оптимальное использование памяти.