Нейронная сеть, предсказывающая данные реального рынка

«Сделал это просто для удовольствия, а не для получения прибыли», — написал приложение для нейронной сети, которое предсказывает вывод данных в реальном времени с биржевых рынков, связанных с биткойнами.
Теперь, чтобы уточнить, я не спрашиваю, корректен ли мой алгоритм, или моя модель сделает меня богатым — я изучаю NN и живое предсказание, поэтому, пожалуйста, прочитайте это так.

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

Я обучаю сеть, вводя исторические цены, около 2 тыс. Для каждого рынка — сеть настроена следующим образом:

ВХОД: 3 входа
СКРЫТЫЙ: ВВОД * 2 +1
ВЫХОД: 1

Обучение, пока ошибка не достигнет 0,001 фактора.

Теперь к вопросам.

1) Я храню только значения, которые меняются, поэтому я не сохраняю цену, если она не изменилась, поэтому — подходит ли этот подход? Или я должен получить цену, даже если она не изменится? Влияет ли это на прогноз? И как много? Я не хочу прогнозировать значение в 15:00. Я хочу, чтобы сеть предсказывала следующую возможную цену покупки — время здесь не имеет значения.

2) Если вы посмотрите на графики ниже, вы можете ясно увидеть, что сеть вроде «запаздывает» (особенно на втором скриншоте) и ей не нравятся «высокие пики» — что еще лучше, она даже не может предсказывать это всегда предсказывает противоположную тенденцию — это нормально, или есть какое-то объяснение этому поведению?

введите описание изображения здесь
введите описание изображения здесь

Исходный код:

#include <chrono>
#include <thread>
#include <math.h>
#include <iostream>
#include "Core/CMemTracer.h"#include "Core/CDatabase.h"#include "Core/CCalcModule.h"#include "Core/CCalcModuleNN.h"#include "Core/CNeuralNetwork.h"
CNeuralNetwork _NeuralNetwork;
CDatabase _Database;

int main(int argc, const char * argv[])
{
std::string m_strDatabaseHost;
std::string m_strDatabaseName;
std::string m_strDatabaseUsername;
std::string m_strDatabasePassword;
std::string m_strExchange;

int          m_iNumOfHistoryForTraining = 0;
int         iNeuralNetworkInputs = 5;
int         iNeuralNetworkHidden = 2 * iNeuralNetworkInputs + 1;
int         iNeuralNetworkOutputs = 1;
int         iMaximumTrainingEpoch = 10000000;
float       fMinimum = 0;
float       fMaximum = 1000;
float       fMaximumNetworkError = 0.000720;
float       fNeuralNetworkLearningRate = 0.5;
float       fNeuralNetworkMomentum = 0.1;

std::vector<float> vHistory;
std::vector<float> vNormalisedData;

m_strDatabaseHost       = "192.168.0.10";
m_strDatabaseName       = "Trader";
m_strDatabasePassword   = "password";
m_strDatabaseUsername   = "root";
m_strExchange           = "exBitMarket";

// How much data we fetch from the DB
m_iNumOfHistoryForTraining = 2000;

CLogger::Instance()->Write(XLOGEVENT_LOCATION, "Info, Connecting to Database");

// Load up Database
if(_Database.Connect(m_strDatabaseUsername, m_strDatabasePassword, m_strDatabaseHost) == false)
{
CLogger::Instance()->Write(XLOGEVENT_LOCATION, "Error, cant connect to Database");
return false;
}

CLogger::Instance()->Write(XLOGEVENT_LOCATION, "Info, Selecting Database");

// Select Database
if(_Database.SelectDatabase(m_strDatabaseName) == false)
{
CLogger::Instance()->Write(XLOGEVENT_LOCATION, "Error, cant select Database");
return false;
}

// Get x Data from Database
std::string strQuery = "SELECT * FROM (SELECT * FROM exData WHERE Exchange='"+m_strExchange+"' ORDER BY Epoch DESC LIMIT "+stringify(m_iNumOfHistoryForTraining)+")sub ORDER BY Epoch ASC";

// Query DB
CLogger::Instance()->Write(XLOGEVENT_LOCATION, "Info, Querying database");

CDatabase::tDatabaseQueryResult _QuerySelect;
if(_Database.Query(strQuery, _QuerySelect) == false)
{
//
CLogger::Instance()->Write(XLOGEVENT_LOCATION, "Error, cannot query database");

//
return false;
}

//
CLogger::Instance()->Write(XLOGEVENT_LOCATION, "Info, Got %i results", _QuerySelect.m_iRows);

// If Data available
if(_QuerySelect.m_iRows >= m_iNumOfHistoryForTraining )
{

// Push back Buy value to Historical Data Vector
for(int c = 0; c < _QuerySelect.m_vRows.size(); c++)
vHistory.push_back(atof(_QuerySelect.m_vRows[c].m_vstrColumns[3].data()));vNormalisedData = vHistory;
}
else
{
//
CLogger::Instance()->Write(XLOGEVENT_LOCATION, "Error, not enough data returned (%i of %i required)", _QuerySelect.m_iRows,m_iNumOfHistoryForTraining);

//
return false;
}

//
CLogger::Instance()->Write(XLOGEVENT_LOCATION, "Info, Normalising data for Neural network input");

// Normalise
// Find max, min values from the dataset for later normalization
std::vector<float>::iterator itMax = std::max_element(vHistory.begin(), vHistory.end(),[](const float& x, const float& y) {  return x < y; });
std::vector<float>::iterator itMin = std::min_element(vHistory.begin(), vHistory.end(),[](const float& x, const float& y) {  return x < y; });

// Store Min/Max
fMinimum = itMin[0];
fMaximum = itMax[0];

//
CLogger::Instance()->Write(XLOGEVENT_LOCATION, "Info, Normalised data <%f, %f>", fMinimum, fMaximum);

// Important - Neural Network has to be setup correctly for activation function
// both this normalization and NN has to be setup the same way.
// Log  sigmoid activation function (0,1)// logistic sigmoid function  [0, 1]
for(int a = 0; a < vHistory.size(); a++)
vNormalisedData[a] = (vHistory[a] - itMin[0]) / (itMax[0] - itMin[0]);

//
CLogger::Instance()->Write(XLOGEVENT_LOCATION, "Info, Initializing neural network with the setup %i/%i/%i Learning Rate: %f, Momentum: %f",
iNeuralNetworkInputs,
iNeuralNetworkHidden,
iNeuralNetworkOutputs,
fNeuralNetworkLearningRate,
fNeuralNetworkMomentum);// Build the network with arguments passed
_NeuralNetwork.Initialize(iNeuralNetworkInputs, iNeuralNetworkHidden, iNeuralNetworkOutputs);
_NeuralNetwork.SetLearningRate(fNeuralNetworkLearningRate);
_NeuralNetwork.SetMomentum(false, fNeuralNetworkMomentum);// Train
double  dMaxError   = 100.0;
double  dLastError  = 12345.0;
int     iEpoch      = 0;
int     iLastDump   = 0;
int     iNumberOfDataForTraining =  (vNormalisedData.size() / 2) - iNeuralNetworkInputs + iNeuralNetworkOutputs;
//
CLogger::Instance()->Write(XLOGEVENT_LOCATION, "Info, starting training with %i data out of %i", iNumberOfDataForTraining, vNormalisedData.size());

// Perform training on the training data
while ( (dMaxError > fMaximumNetworkError) && (iEpoch < iMaximumTrainingEpoch) )
{
//
dMaxError = 0;

// Now the input is normalized and ready for use perform the training
// Use 1/2 of the Normalised Data for training purposes, the rest will be used to
// Validate the network.
for(int a = 0; a < iNumberOfDataForTraining; a++)
{
// Set Inputs
for(int b = 0; b < iNeuralNetworkInputs; b++)
_NeuralNetwork.SetInput(b, vNormalisedData[a+b]);

// Set desired Output for the newest value
_NeuralNetwork.SetDesiredOutput(0, vNormalisedData[a + iNeuralNetworkInputs]);

// Feed data
_NeuralNetwork.FeedForward();

//
dMaxError += _NeuralNetwork.CalculateError();

// Backpropagate to learn
_NeuralNetwork.BackPropagate();
}

// Divide by the number of total array size to get global network error
dMaxError /= vNormalisedData.size();// Dump some stats now
if(CUtils::GetEpoch() - iLastDump > 1)
{
CLogger::Instance()->Write(XLOGEVENT_LOCATION, "Training Error Factor: %f / %f Epoch: %i", dMaxError, fMaximumNetworkError, iEpoch);
iLastDump = CUtils::GetEpoch();
}

// Increment the epoch count
iEpoch++;

// Store last error for early-stop
dLastError = dMaxError;
}
//
CLogger::Instance()->Write(XLOGEVENT_LOCATION, "Info, starting validation with %i data", vNormalisedData.size() - iNumberOfDataForTraining);

//
dMaxError = 0;

// Now check against 'Validation' Data
for(int a = iNumberOfDataForTraining; a < vNormalisedData.size(); a++)
{
// Set Inputs
for(int b = 0; b < iNeuralNetworkInputs; b++)
_NeuralNetwork.SetInput(b, vNormalisedData[a+b]);

// Set desired Output for the newest value
_NeuralNetwork.SetDesiredOutput(0, vNormalisedData[a + iNeuralNetworkInputs]);

// Feed data
_NeuralNetwork.FeedForward();

//
dMaxError += _NeuralNetwork.CalculateError();
}

// Divide by the number of total array size to get global network error
dMaxError /= vNormalisedData.size();

CLogger::Instance()->Write(XLOGEVENT_LOCATION, "%i Network Trained, Error Factor on Validation data = %f",
CUtils::GetEpoch(),
dMaxError);// Save the network to an output filer

return 0;
}

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

Обновить:
Добавлен обновленный код, который отражает обучение по данным обучения и проверку данных проверки.

0

Решение

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

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

Первый шаг: Разделите ваши данные, сделайте их 50/50 или, может быть, этого достаточно, чтобы 90% данных об обучении и 10% данных об испытаниях. Вы можете делать то, что хотите, с данными тренингов, но единственное, что вы можете использовать тестовые данные — это посмотреть, насколько хороша ваша модель. В идеале, вы делаете это только один раз.

Дальнейшие шаги: Это несколько упрощено. Вы можете использовать перекрестную проверку (т. Е. Вы используете разные разбиения). Или вы можете использовать оценочный набор, чтобы соответствовать вашим параметрам или поиграть с вещами, поэтому вы можете коснуться тестового набора только один раз. На самом деле, нет предела тому, что вы можете сделать, но основной раскол творит чудеса.

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

Данные, с которыми вы узнали модель (в вашем случае NN), испорчены. Если алгоритм обучения действительно плохой, он всегда будет подходящим. Вопрос в том, подойдут ли все остальные данные из того же дистрибутива? Для этого вы используете тестовый набор. Если модель может хорошо вписаться, значит, вы не оделись. Если это полный беспорядок, вы переоснащены (или что-то не так).

1

Другие решения

Если ваш вопрос касается переоснащения, есть определенные правила, которым вы можете следовать. Вот например.

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

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

Кроме того, вы можете использовать такие вещи, как перекрестная проверка и попытаться сохранить небольшую ошибку обобщения (или, возможно, также некоторую комбинацию ошибки обучения и обобщения).

1

По вопросам рекламы [email protected]