Должны ли мы отказаться от LSTM для CNN?

LSTM всегда считались алгоритмом перехода для последовательных данных, а CNN всегда считались лучшим алгоритмом для обработки данных изображений. Но насколько правдивы эти настроения? Было написано много статей об использовании CNN на уровне символов, которые находятся на одном уровне или даже лучше, чем LSTM или другие повторяющиеся сети!

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

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

LSTM:

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

Такое сложное позиционирование рекуррентных нейронных сетей позволяет сети вспоминать прошлые «воспоминания» (прошлые данные). Эта возможность упрощает создание соединений между текущими точками данных и прошлыми точками данных, позволяя сети находить закономерности, которые проявляются с течением времени.

CNN:

CNN были первой инновацией многослойного персептрона, впервые появившейся в конце 20-го века. Они состоят из фильтров, которые применяются ко входным данным. Это эффективно сжимает данные до меньшего разрешения. CNN обучены преобразовывать полноразмерные изображения в меньшее разрешение без потери важной информации. Они в основном способны устранять шум во входных данных, чтобы их можно было передать через базовую нейронную сеть.

Это пространственное распознавание может работать не только в 2D-пространствах, оно также применяется в 3D-пространствах (видео, которые состоят из изображений с течением времени), а также в 1D-пространствах, где данные представляют собой просто массив значений. Именно эта универсальность делает CNN такими полезными.

Концепция:

LSTM и CNN будут реализованы на данных AAPL в течение последних 20 лет. Оценка будет производиться на основе значений потерь программы, а также на основании прогнозов сети на данных тестирования.

Код:

Шаг 1 | Подготовить данные:

import yfinance
import numpy as np
df = yfinance.download('AAPL','2000-1-1','2020-1-1')
df = df.drop(['Volume'],1).drop(['Adj Close'],1)

Этот код извлекает важную информацию из фрейма данных, который содержит котировки акций AAPL за последние 20 лет. Скорректированное закрытие и объем будут удалены, так как я обнаружил, что эти столбцы добавляют слишком много шума к набору данных.

def normalize_data(dataset):
        cols = dataset.columns.tolist()
        col_name = [0]*len(cols)
        for i in range(len(cols)):
            col_name[i] = i
        dataset.columns = col_name
        dtypes = dataset.dtypes.tolist()
        minmax = list()
        for column in dataset:
            dataset = dataset.astype({column: 'float32'})
        for i in range(len(cols)):
            col_values = dataset[col_name[i]]
            value_min = min(col_values)
            value_max = max(col_values)
            minmax.append([value_min, value_max])
        for column in dataset:
            values = dataset[column].values
            for i in range(len(values)):
                values[i] = (values[i] - minmax[column][0]) / (minmax[column][1] - minmax[column][0])
            dataset[column] = values
        dataset[column] = values
        return dataset,minmax
    
dataset,minmax = normalize_data(df)
print(df.values)
values = dataset.values

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

def split_sequences(sequence, n_steps):
    X, y = list(), list()
    for i in range(len(sequence)):
        end_ix = i + n_steps
        if end_ix > len(sequence)-1:
            break
        seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
        X.append(seq_x)
        y.append(seq_y)
    return np.array(X), np.array(y)
def data_setup(n_steps, n_seq,sequence):
    X, y = split_sequences(sequence, n_steps)
    n_features = X.shape[2]
    X = X.reshape((len(X),n_steps, n_features))
    new_y = []
    for term in y:
        new_term = term[-1]
        new_y.append(new_term)
    return X, np.array(new_y), n_features
n_steps = 10
n_seq = 10000
rel_test_len = 0.1
X,y,n_features = data_setup(n_steps,n_seq,values)
X = X[:-1]
y = y[1:]
X_test,y_test = X[:int(len(X)*rel_test_len)],y[:int(len(X)*rel_test_len)]
X_train,y_train = X[int(len(X)*rel_test_len):],y[int(len(X)*rel_test_len):]
X.shape

Этот скрипт преобразует последовательности в набор данных: значения X будут всеми другими столбцами, кроме цены закрытия, которая будет значениями y. Это означает, что компьютер будет использовать котировки акций для прогнозирования цен закрытия.

Я также разделил данные на данные для обучения и тестирования. Разделение между двумя наборами данных составляет 10–90, 90% данных используется для обучения, а 10% - для обучения.

Шаг 2 | LSTM:

from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers import Dropout
from keras.layers import LSTM
model = Sequential()
model.add(LSTM(64, activation=None, input_shape=(10,4), return_sequences = True))
model.add(LSTM(32, activation=None, return_sequences = True))
model.add(Flatten())
model.add(Dense(100, activation=None))
model.add(Dense(1, activation='sigmoid'))
model.compile(loss='mse', optimizer='adam')

Довольно понятно! Базовая сеть LSTM с убывающим числом ячеек. Используется сигмоид в последнем слое, поскольку данные нормализованы в диапазоне 0–1.

Шаг 3 | CNN:

from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers import Dropout
from keras.layers.convolutional import Conv1D
from keras.layers.convolutional import MaxPooling1D
model = Sequential()
model.add(Conv1D(filters=128, kernel_size=3, activation='relu', input_shape=(10,4)))
model.add(Conv1D(filters=64, kernel_size=3, activation='relu'))
model.add(MaxPooling1D(pool_size=2))
model.add(Flatten())
model.add(Dense(100, activation='relu'))
model.add(Dense(1, activation='sigmoid'))
model.compile(loss='mse', optimizer='adam')

Это нисходящая сверточная нейронная сеть для одномерных массивов со слоем из 100 нейронов перед выходным слоем. Relu используется для CNN, а не для LSTM, поскольку большие выходные данные функции relu могут перегрузить LSTM.

Шаг 4 | Сети поездов:

import os
from keras import callbacks
epochs = 5000
verbosity = 2
dirx = 'XXXXXXX'
os.chdir(dirx)
h5 = 'network.h5'
checkpoint = callbacks.ModelCheckpoint(h5,
                                       monitor='val_loss',
                                       verbose=0,
                                       save_best_only=True,
                                       save_weights_only=True,
                                       mode='auto',
                                       period=1)
callback = [checkpoint]
json = 'network.json'
model_json = model.to_json()
with open(json, "w") as json_file:
    json_file.write(model_json)
history = model.fit(X_train,
                    y_train,
                    epochs=epochs,
                    batch_size=len(X_train) // 4,
                    validation_data = (X_test,y_test),
                    verbose=verbosity,
                    callbacks=callback)

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

from keras.models import load_model, model_from_json
def load_keras_model(optimizer):
    dirx = 'XXXXXXX'
    os.chdir(dirx)
    json_file = open('Convnet.json', 'r')
    loaded_model_json = json_file.read()
    json_file.close()
    model = model_from_json(loaded_model_json)
    model.compile(optimizer=optimizer, loss='mse')
    model.load_weights('Convnet.h5')
    return model
model = load_keras_model('adam')

Затем загруженные веса можно загрузить из того же набора данных с помощью этой функции.

Шаг 5 | Построение результатов:

model.evaluate(X_test,y_test)

Эта одна строка кода вернет значения MSE для данных тестирования, полученных из набора данных.

from matplotlib import pyplot as plt
pred_test = model.predict(X_test)
plt.plot(pred_test,'r')
plt.plot(y_test,'g')

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

Полученные результаты:

Сравнение времени обучения сетей:

CNN: 20m 25s 
LSTM: 34m 28s

Вот значение потерь LSTM по сравнению с CNN:

CNN: 9.132824629887182e-07
LSTM: 9.205878086504526e-07

Сюжет, созданный LSTM и CNN:

Заключение:

Судя по результатам, они практически одинаковы! По качеству разница сводится к минимальному запасу. Однако с точки зрения времени обучения и вычислительной интенсивности CNN превосходит.

Хотя CNN работает немного лучше, чем LSTM, вам нужно посмотреть на набор данных и адаптировать алгоритм машинного обучения, чтобы он хорошо работал с набором данных.

Мои ссылки:

Если вы хотите увидеть больше моего контента, нажмите эту ссылку.