Работа с пропущенными значениями

При построении нейронных сетей на JavaScript с использованием библиотеки Brain.js часто возникает ситуация, когда входные данные содержат пропущенные значения. Правильная обработка таких данных критически важна для стабильной работы сети и получения корректных результатов.


Особенности работы с пропущенными значениями

Brain.js не поддерживает работу с undefined, null или пустыми строками в качестве входных данных напрямую. Любое значение, которое не является числом или массивом чисел, может вызвать исключение или некорректное поведение сети. Следовательно, требуется предварительная обработка данных перед подачей их в нейронную сеть.

Основные подходы к обработке пропусков:

  1. Заполнение значений (Imputation)

    • Средним/медианой: для числовых признаков часто используют среднее или медиану по колонке.
    • Значением по умолчанию: например, ноль или минимальное значение в диапазоне признака.
    • Метод ближайших соседей: использование значения ближайших похожих записей. Этот метод эффективен для небольших наборов данных, где структура признаков позволяет выявлять сходство между объектами.
  2. Исключение записей с пропусками

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

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

Подготовка данных для Brain.js

Brain.js принимает входные данные в формате массивов чисел или объектов с числовыми значениями. Пропуски необходимо преобразовать заранее:

const trainingData = [
  { input: { age: 25, income: 50000 }, output: { buy: 1 } },
  { input: { age: null, income: 40000 }, output: { buy: 0 } },
];

// Заполнение пропусков средним значением
const filledData = trainingData.map(item => {
  return {
    input: {
      age: item.input.age !== null ? item.input.age : 30, // среднее возраст
      income: item.input.income,
    },
    output: item.output
  };
});

При работе с массивами:

const data = [
  [0.2, 0.5],
  [0.1, null],
];

const filled = data.map(row => row.map(value => value !== null ? value : 0));

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

После заполнения пропусков важно привести данные к диапазону, подходящему для работы сети. В Brain.js рекомендуется использовать значения в диапазоне [0,1]:

const normalize = (value, min, max) => (value - min) / (max - min);

const normalizedData = filledData.map(item => ({
  input: {
    age: normalize(item.input.age, 18, 65),
    income: normalize(item.input.income, 20000, 100000)
  },
  output: item.output
}));

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


Обработка категориальных признаков с пропусками

Если данные содержат категориальные признаки, пропуски обрабатываются отдельно:

  1. One-hot кодирование с отдельной категорией для пропуска: Например, если признак color принимает значения red, green, blue, а у некоторых записей отсутствует значение, добавляется категория unknown.
const colors = ['red', 'green', 'blue', 'unknown'];

function oneHotEncodeColor(color) {
  return colors.map(c => (c === (color || 'unknown') ? 1 : 0));
}
  1. Заполнение наиболее частой категорией: Подходит, если пропусков мало, и их значение, вероятно, совпадает с преобладающим классом.

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

  • Всегда проверять данные на наличие пропусков перед обучением сети.
  • Сохранять способ обработки пропусков для тестовых данных, чтобы избежать рассогласования при предсказании.
  • Для динамических данных (например, потоковых) использовать постоянный метод заполнения пропусков, а не вычислять среднее заново на каждом батче.
  • Сочетание нескольких методов часто дает наилучший результат: например, заполнение средним для числовых признаков и добавление отдельного бинарного признака, указывающего на пропуск.

Пример полной подготовки данных

const rawData = [
  { age: 25, income: 50000, color: 'red', buy: 1 },
  { age: null, income: 40000, color: null, buy: 0 },
];

// Заполнение пропусков и нормализация
const preparedData = rawData.map(item => ({
  input: {
    age: normalize(item.age !== null ? item.age : 30, 18, 65),
    income: normalize(item.income, 20000, 100000),
    colorRed: item.color === 'red' ? 1 : 0,
    colorGreen: item.color === 'green' ? 1 : 0,
    colorBlue: item.color === 'blue' ? 1 : 0,
    colorUnknown: item.color === null ? 1 : 0
  },
  output: { buy: item.buy }
}));

const net = new brain.NeuralNetwork();
net.train(preparedData);

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