← Home

Нормализация/стандартизация данных

By Брендель В. М.

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

Наиболее правильный способ нормализации/стандартизации.

Есть датасет из двух классов: кошечки, собачки. Fetures: вес, рост, длина хвоста, количество пятен в раскраске.

Делим датасет на тренировочную, валидационную, тестовую (если надо).

train_df, test_df = train_test_split(df, test_size=size, shuffle=shuffle_bool)

Теперь исходим из следующего — данные валидации и теста из будущего, нейросеть ничего про них знать не должна. Соотвественно нормализовать нужно данные только тренировочного датасета.

Используем scaler = sklearn.preprocessing.MinMaxScaler.

Делаем scaler.fit_transform(train_df_x), это приведет к нормализации столбцов признаков в датафрейме. При этом scaler сохранит в себе min, max для каждого столбца из тренировочного датасета.

Тут пояснение почему нормализовать нужно именно постолбечно?

В нашем случае — первый столбец — вес животного. кошечка1 = 3кг, собачка1 = 5кг, кошечка2 = 3.5кг и т.д. Второй столбец — рост — кошечка1 = 20см, собачка1 = 50см, кошечка2 = 22см и т.д. Какой смысл нормализовать данные имеющие различную физическую природу? Как нормализовать вес и рост? Ответ — никак. Нормализовать нужно попризнаково, т.е. берем все значения столбца feature1 (вес), и их нормализуем. Берем все значения столбца feature2 (рост), и их нормализуем. И так далее..

Теперь перед вызовом метода predict, нужно делать scaler.transform(new_features_vector). Тогда скейлер автоматически отнормирует новый входной вектор на основе данных из тренировочных данных и приведет новый вектор к общему знаменателю, заодно оптимизировав значения признаков под архитектуру сети.

У данного подхода есть 2 недостатка:

  1. Методы sklearn не умеют считать на GPU, если признаков много (например датасет из 1000 строк (500 собачек, 500 кошечек), с 10000 признаков (у каждой кошечки 10000 параметров)), то считаться нормированный датасет будет очень долго. Типично дольше, чем будет происходить тренировка. sklearn умеет в numpy и numpy типично быстрее GPU tensorflow, но только на задачах малого масштаба. Иногда датасет вообще не влезает в память целиком и провести нормализацию по всему тренировочному датасету без костылей невозможно.

  2. Когда модель обучится, вам в продакшен нужно будет помимо самой модели, ташить с собой объект нормализатор. Лишняя сущность — плохо.

Прочие варианты:

  1. Нормализация по диапазону.

Например у вас картинка 100х100 пикселей, один канал 8 бит. У нас есть верхняя граница диапазона, просто берем и делим значения каждого пикселя на 255.

Другой пример — wav файл 16 бит. Диапазон значений -32768 ... +32768. Делим каждый сэмпл на 32768, приводим к значениям (-1, 1).

  1. Нормализация по сэмплам.

Т.е. нормализация по строкам.

Работает если значения всех признаков находится приблизительно в одном диапазоне. И/или имеют одну природу. Например: датасет из зданий, где признаки каждого здания длина/ширина/высота (все измеряется в м, приблизительно одного порядка).

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

  1. Нормализация по батчам (batch_normalizaton)

Это некий урезанный вариант нормализации по датасету. Учится сеть батчами, оптимальный размер батча — тот, который влезет в память видеокарты, заняв как можно больше места. Т.е. учить мелкими батчами плохо — большая часть времени будет уходит на передачу данных от CPU к GPU, большими — плохо (не влезет в память GPU).

После входного full-conected слоя , делается слой batch-normalization. У него есть хитрый параметр is_training. Если True то слой берет батч, считает нормирует его, сохраняет в памяти мин, макс.

Затем берет следующий батч, определяет мин, макс в этом батче. Берет среднее с предыдущми значениями и номирует батч. Таки образом, слой реализует нормировку выходов скрытого слоя скользящим окном. И даже если нормировка первого батча сильно выбивается от остальных, то со временем скользящим окном мин и макс значения асимптотически будут подходить к истинным значениям. Тем более все это прокручивается сколько-то эпох (то есть порядка 100 прокруток тренировочного датасета полностью).

Замечу, что batch_normalization не нормирует датасет, а нормирует выходы слоев в сети. Но по сути batch_normalization избавляет от covariance_shift, что делает и нормировка датасета.

http://ruishu.io/2016/12/27/batchnorm/

https://timodenk.com/blog/tensorflow-batch-normalization/

https://towardsdatascience.com/how-to-normalize-features-in-tensorflow-5b7b0e3a4177 https://stackoverflow.com/questions/44835223/adding-a-preprocessing-layer-to-keras-model-and-setting-tensor-values