После использования вейвлетов для классификации, я конечно сразу попробовал оценить ценность вейвлет для прогнозирования, но тестирование мягко говоря затянулось. Эпох я прогонял много, 600, а одна эпоха это минута. И оставляя нейросеть на ночь я забывал, то одно, то другое. Затем я стал пробовать разные learning rate, датасеты, архитектуры и обнаружилось что тот самый позитивный первоначальный результат (в виде 55% accuracy на валидации и over +75% на тесте) исчез, сеть не могла зацепить ничего, даже на тесте. В пятницу я решил в последний раз прогнать сеть и уже с 50 эпохи сеть начала уверенно обучаться. Обьяснение тому что на том же наборе данных, одна и та же сеть, то обучается, то необучается, у меня одно — каждый раз нейросеть начинает блуждать по гиперплоскости функции ошибки с разных мест, и при одном наборе весов застревает в зоне локальных минимумов, которые чуть-чуть лучше 50%, не в силах перескочить в область более низких минимумов.
Данные все те же — голубые, отечественные. Тестирую на недельках, прогноз на недельку вперед.
Помимо вейвлетов и CNN занялся вопросом выбора оптимального набора весов, благо у меня выбор широк — более 600 эпох. Вот есть трейн выборка на которой нейросеть учится, и где то к 300-400 эпохам уверенно подбирается к 75% угадайки. Замечательно. Есть валидация на которой есть пик в 57% на 200 эпохе (±20), после чего она снижается и болтается в диапазоне 52-55%. Вот интересно, тесты будут лучше коррелировать с трейном или валидацией? Теория учит нас использовать валидацию, но если подумать то и в валидации тоже есть момент подгонки, да и 1 набор валидации это все такая же игра в кости.
Приступим. У нас 600 эпох и наша задача придумать критерий для выбора той эпохи, где набор весов оптимальный для теста (out-sample). Насколько это важно? Ну вот например так выглядят средние профиты по эпохам на тесте:
Скачки велики, от -0,2 на 1 эпохе до 0,5. Так что правильно выбрать эпоху важно. А то вместо профита будем подсчитывать убытки и плакать.
Что скажет купечество?! То бишь теория? Теория говорит что надо опираться на валидацию, мол валидация даст нам знак на какой эпохе самые правильные веса, чтобы не попасть в оверфит.
Ну вот я наложил средние профиты на трeйне, валидации и тесте (голубой, красный, зеленый):
Ну понятно, трейн улетает в небо, сбивая масштаб, но если посмотреть результаты на тесте и валидации отдельно
то разве кто то скажет что тут все четко, и по валидации можно вытащить хороший набор весов для теста?
Я посчитал корреляцию по среднему профиту, и между тестом и валидацией он равен +0,28, а тестом и трейном +0,14. Если взять по точности, то корреляция трейна с валидацией выше: +0,49, но с тестом она еще выше +0,57. О как. То есть ориентироваться на валидацию дело довольно сомнительное.
Далее я пробовал разные способы, подбора по результатам на трейне и валидации найти оптимальный набор для теста, даже думал еще одну нейросеть прикрутить, но в общем ничего интересного не получил, поэтому описывать не буду.
Ну и наконец ради чего все это затевалось — можно ли на основе вейвлет преобразования на неделе, сделать какой то вразумительный прогноз на следующую неделю?! Взял эпоху по лучшей валидации. И у меня получилось что да, можно, мягко говоря не озолотитесь, но после того как мои прошлые нейросети не могли зацепить ничего от слова совсем, уступая в одну калитку градиентному бустингу /случайному лесу, то для меня это уже результ.
Результаты прогнозирования, count понятно что, mean — средняя профитность сделки:
Спрогнозировали 93 недели (каждый год должен быть по 40-42 недели, но часть 2018 года ушла под валидацию, а 2020 все никак не закончится). Данные представил с разной разбивкой-по акциям (стабильно по всем акциям, только Роснефть что то химичит), по лонг/шорт (видим что в половине случаев сеть спрогнозировала шорт, в половине лонг, что приятно. и там и там профиты). Для тех кто не понял почему суммы по count не совпадают:
Недель 93, но каждая неделя это 6 фишек: 6*93 сделок. 93 лонгов означает что каждую недели были какие то лонговые сделки, средняя их равна +0,5%. Шортовых 92 — это значит что из 93 недель, в одну неделю ни одна акция не показала в шорт.
Кстати если бы мы каждую неделю просто покупали бы все эти фишки, а в конце продавали, то средняя профитность составила бы +0,27% за неделю. Использование системы позволяет увеличить доходность по сделкам «только в лонг» до +0,5%.
Я когда пробовал нейросети первый раз — ещё мало что понимал, у меня получилось что что чуть ли не с третьей эпохи результат начинает ухудшаться на отложенной выборке. Щас я конечно по-другому бы все делал — может и результаты были бы другие. Здесь на графиках вижу, что после 50-й эпохи обучаться дальше нет смысле и выбирать «правильную» точку на участке 50+ — даже не переподгонка в стандартном смысле слова — просто самообман. На первых (единицы эпох) эпохах нейросеть вычленяет основную суть, дальше еще несколько десятков эпох что-то довысасывает из данных, дальше белый шум. Но из-за зашумленности данных (и выбранной архитектуры тоже, видимо) модель не переобучается в привычном смысле слова, результаты на отложенной выборке не падают.
Я буду копать в сторону более интересных данных на входе. Ещё хочу попробовать по-максимуму отнормировать данные и слить данные по разным тикерам в один датасет чтобы нейросеть получила больше данных на вход и выявила наиболее общие закономерности. Кстати, как вариант, можно обучить нейросеть на слитых данных, потом, как вариант, заморозить часть слоев (или нет) и дообучить на конкретном инструменте.
model = Sequential()
model.add(Conv2D(32, kernel_size=(3, 3), strides=(1, 1), activation='relu', input_shape=input_shape))
model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
model.add(Conv2D(64, (5, 5), activation='relu'))
model.add(Dropout(0.3))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.3))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.3))
model.add(Dense(num_classes, activation='softmax'))
opt = keras.optimizers.Adam(learning_rate=0.001)
model.compile(loss=keras.losses.categorical_crossentropy,
С архитектурой особо не заморачивался, когда хотел усложнить — ресурсы у компа кончилисьoptimizer=keras.optimizers.Adam(),
metrics=['accuracy'])