Разделяй и властвуй или как спасти оперативку
- вторник, 8 февраля 2022 г. в 00:40:49
Вероятно, многие встречались с такой проблемой как нехватка оперативной памяти для решения той или иной задачи. Но порой данную проблему можно обойти, руководствуясь простому, но верному принципу: разделяй и властвуй. Данный подход может помочь не только в ML задачах, но и других проектах.
Мне была поставлена задача спрогнозировать изменение физической величины (неизвестного мне параметра на предприятии) на 240 шагов вперёд. Как вы уже поняли – это стандартная задача предсказания временного ряда.
Исходные данные представляли из себя поминутное изменение параметра:
Однако, по классике, с исходными данными как правило приходится пошаманить, чтобы привести их к виду пригодному для дальнейшей обработки и формированию обучающей выборки. В моём случае с удивительной периодичностью встречались пропуски, т.е. после 00.00.01 преспокойно могло идти 00.00.04, что немножко рушило целостность временного ряда.
Самым простым и действенным было прибегнуть к чудесной силе интерполяции из библиотеки Pandas:
Data = Data.Value.resample('s').interpolate()
Вероятно, я забыл упомянуть, что в исходных данных было около 900тыс. значений. Думаю, многие сейчас догадались, что произошло с моим ПК после того, как компилятор героически взялся за выполнение данной операции :)
После перезагрузки ПК я понял, что кусочек в 55 млн строк (учитывая, что пропуски будут заполнены ВСЕМИ недостающими секундами) - это тихий ужас для моей бедной RAM.
Вот тут-то и приходит на помощь принцип: Разделяй и властвуй.
Данный принцип активно применяется для экономии RAM во время обучения моделей в виде различных дата генераторов. Но лично для меня было не очевидно, что ещё на стадии предобработки кол-во данных может разбухнуть до неподъемных размеров для твоего железа.
В связи с этим мой алгоритм обработки выглядел следующим образом:
<> Берём кусок массива заданной нами длины
batch = data.iloc[index:index+xLen]
<> Производим всю необходимую обработку текущего куска, включая интерполяцию (в моём случае я произвожу несколько интерполяций для большего сглаживания значений, т.к. они были очень зашумлены, но суть остаётся той же)
batch = batch.Value.resample('s'
).interpolate().resample('4T'
).interpolate().resample('1T').interpolate()
<> Затем производим конкатенацию массивов на каждой итерации
batch_sum = pd.concat([batch_sum, batch])
Я произвожу сдвиг в массиве данных методом скользящего окна, но каждый может применять свой способ, т.к. важен именно принцип.
В первую очередь конвертируем строковые данные в данные типа Timestamp для возможности работы с ними как с временными данными.
Далее применяем описанный выше алгоритм, который я обернул в функцию, где первый аргумент data - это сам массив, второй xLen – размер одного батча (кол-во строк) и третий step – шаг сдвига.
########################################################
# Функция интерполяции всего массива значений по частям.
########################################################
def create_samples(data, xLen, step):
data_len = data.iloc[:].shape[0]
index = 0
count = 0
batch_sum = s_samp # берём шаблон для конкатинации Serias объектов
while (index + xLen <= data_len): # Идём по всей длине массива значений
t1 = time.time()
batch = data.iloc[index:index+xLen] # "Откусываем" пример длинной xLen
batch = batch.Value.resample('s'
).interpolate().resample('4T'
).interpolate().resample('1T').interpolate()
batch = batch.dropna()
now_shape = batch.shape[0]
count += now_shape
batch_sum = pd.concat([batch_sum, batch])
index += step # Смещаеммся вперёд на step
if index % step*10 == 0:
print(index, time.time()-t1)
print(count, 'общее кол-во значений')
return batch_sum
Как приятно видеть, что при правильном подходе задача решается всего за 36 секунд, а твой ПК не впадает в кому :)
Подведём итоги:
При решении задачи нужно оценить её размер, сложность и ресурсозатратность
Если задача трудная и объёмная, то следует разбить её на более мелкие части по принципу «Разделяй и властвуй»
Подобрать такие гиперпараметры, как размер и количество подзадач, учитывая специфику задачи
Всем большое спасибо и успехов в ML!