신경망을 이용해 뭘할 수 있을까? 고민이 많을것 같은데요.. 요즈음 deep learning이나 이미지 인식쪽에 주제가 많은 것 같습니다.
codeproject를 살피다 보니 흥미로운 주제를 발견하였습니다.
이름하여 경제(주식)예측 입니다.
http://www.codeproject.com/Articles/175777/Financial-predictor-via-neural-network
그전에 해당글의 저자가 아래와 같이 경고를 하고 있습니다. 해당 글은 교육 목적으로 쓰여졌으며 설명된 어플은 실제 시나리오에서는 사용되어질 수 없다고 합니다.
I would like to warn you that the entire article is written for educational purposes, thus the described application cannot be used in real-world scenario.
어떤한 방법으로 예측을 구현했는지 소스를 분석하도록 하겠습니다. 소스는 c#으로 되어 있습니다. java와비슷하면서도 c++과 비슷해서 어떻게 동작하는지 확인하는건 어렵지 않을것 같습니다.
The data that will be feed to neural network at the input, represents historical data of the S&P500, DOW, NASDAQ Composite and Prime Interest Rate. In general terms, these are leading indicators of stock market activity, which have a common fluctuation pattern.
입력에서 신경 네트워크에 입력으로 사용될 데이터는 S&P500, 다우, 나스닥, 프라임 금리의 과거 데이터를 사용합니다. 이러한 공통의 변동 패턴이 주식 시장 활동의 지표를 선도하고 있습니다.
입력 데이터로는 4개의 지표의 과거 10개의 데이터를 사용합니다. 결국 4*10=40개의 입력 데이터 x를 넣고 11번째의 데이터를 y에 넣어서 학습을 합니다.
Pairs used in prediction:
# | 1 | 2 | ... | 10 | 11 |
NASDAQ | 2288.55 | 2301.66 | ... | 2231.65 | ? |
DOW | 12376.72 | 12319.73 | ... | 12350.61 | ? |
S&P500 | 1110.88 | 1112.92 | ... | 1099.5 | ? |
PIR | 3.25 | 3.25 | ... | 3.25 | ? |
그러면 입력 데이터는 어떻게 되어있는지 살펴보도록 하겠습니다.
dow.csv
Date,Open,High,Low,Close,Volume,Adj Close
2011-04-01,12321.02,12454.52,12301.11,12376.72,4223740000,12376.72
2011-03-31,12350.84,12422.96,12277.05,12319.73,3566270000,12319.73
...
nasdaq.csv
Date,Open,High,Low,Close,Volume,Adj Close
2011-04-01,2796.67,2802.63,2779.71,2789.60,2090120000,2789.60
2011-03-31,2774.23,2783.98,2769.52,2781.07,1896420000,2781.07
...
rates.csv
date,prime
1970-01,8.50
1970-02,8.50
...
SP500.csv
Date,Open,High,Low,Close,Volume,Adj Close
2011-04-01,1329.48,1337.85,1328.89,1332.41,4223740000,1332.41
2011-03-31,1327.44,1329.77,1325.03,1325.83,3566270000,1325.83
...
csv 파일을 읽어들이는 파일은 FinancialMarketPredictor\Entities\FinancialPredictorManager.cs 입니다.
dow의 경우 많은 항목 중 double amount = csv.GetDouble(ADJ_CLOSE_HEADER); Adj Close 값을 읽어들어입니다. 값을 봐서는 Close(종가) 값과 동일한데 Adj의 의미는 조정된 의미니까 조정된 종가 의미라고 받아들이면 되는데요. dow값이 Close, Adj Close값이 항상 같습니다.
nasdarq의 경우도 double amount = csv.GetDouble(ADJ_CLOSE_HEADER); adj close값을 읽어들입니다.
s&p500도 마찬가지로 adj close값을읽고 rates의경우는 하나의 값 밖에는 없습니다.
모두 읽었으면 데이터를 정규화 합니다. NormalizeData() 함수 이용, 즉 최대 최소값을 읽어서 해당범위의 값으로 바꾸면 모든 수치가 0~1 사이의 범위에 있게 만들어 지는데 이 과정을 정규화라고 합니다.
이 모든 과정을 FinancialPredictorManager.cs Load()라는 함수를 통해서 이루어 집니다.
public void Load(String sp500Filename, String primeFilename, String pathToDow, String pathToNasdaq)
{
if (!File.Exists(sp500Filename))
throw new ArgumentException("sp500Filename targets an invalid file");
if (!File.Exists(primeFilename))
throw new ArgumentException("primeFilename targets an invalid file");
if (!File.Exists(pathToDow))
throw new ArgumentException("pathToDow targets an invalid file");
if (!File.Exists(pathToNasdaq))
throw new ArgumentException("pathToNasdaq targets an invalid file");
try
{
LoadSp500(sp500Filename);
}
catch
{
throw new NotSupportedException("Loading SP500 file failed. Not supported file format. Make sure \"date\" and \"adj close\" column headers are written in the file");
}
try
{
LoadPrimeInterestRates(primeFilename);
}
catch
{
throw new NotSupportedException("Loading Prime Interest Rate file failed. Not supported file format. Make sure \"date\" and \"prime\" column headers are written in the file");
}
try
{
LoadDowIndexes(pathToDow);
}
catch
{
throw new NotSupportedException("Loading Dow indexes file failed. Not supported file format. Make sure \"date\" and \"adj close\" column headers are written in the file");
}
try
{
LoadNasdaqIndexes(pathToNasdaq);
}
catch
{
throw new NotSupportedException("Loading Nasdaq indexes file failed. Not supported file format. Make sure \"date\" and \"adj close\" column headers are written in the file");
}
MaxDate = MaxDate.Subtract(new TimeSpan(_inputSize, 0, 0, 0)); /*Subtract 10 last days*/
StitchFinancialIndexes();
_samples.Sort(); /*Sort by date*/
NormalizeData();
}
사용자가 train 버튼을 누르게 되면 FinancialMarketPredictor.cs BtnStartTrainingClick()를 거쳐서 _predictor.TrainNetworkAsync(trainFrom, trainTo, callback); -> TrainNetwork()를 호출하게 됩니다.
기본적으로 data를 설정하고 train후 에러를 검사하고 반복하는 형태입니다.
private void TrainNetwork(DateTime trainFrom, DateTime trainTo, TrainingStatus status)
{
......
try
{
var trainSet = new BasicNeuralDataSet(_input, _ideal);
train = new ResilientPropagation(_network, trainSet);
double error;
do
{
train.Iteration();
error = train.Error;
if (status != null)
status.Invoke(epoch, error, TrainingAlgorithm.Resilient);
epoch++;
} while (error > MAX_ERROR);
}
검색해봤는데 ResilientPropagation 함수를 아무리 찾아봐도 없었습니다.
구글 검색 해보니 NN엔진을 사용한것 같습니다.
Encog Machine Learning Framework
상당히 잘되어있는 라이브러리처럼 보입니다. 와우... GPU도 지원한다고 합니다.
Encog can also make use of a GPU to further speed processing time.
http://www.heatonresearch.com/encog
다시 소스로 돌아가서 input data를 찾아보도록 하겠습니다.
public void CreateTrainingSets(DateTime trainFrom, DateTime trainTo)
{
// find where we are starting from
int startIndex = -1;
int endIndex = -1;
foreach (FinancialIndexes sample in _manager.Samples)
{
if (sample.Date.CompareTo(trainFrom) < 0)
startIndex++;
if (sample.Date.CompareTo(trainTo) < 0)
endIndex++;
}
// create a sample factor across the training area
_trainingSize = endIndex - startIndex;
_input = new double[_trainingSize][];
_ideal = new double[_trainingSize][];
// grab the actual training data from that point
for (int i = startIndex; i < endIndex; i++)
{
_input[i - startIndex] = new double[INPUT_TUPLES * INDEXES_TO_CONSIDER];
_ideal[i - startIndex] = new double[OUTPUT_SIZE];
_manager.GetInputData(i, _input[i - startIndex]);
_manager.GetOutputData(i, _ideal[i - startIndex]);
}
CreateTrainingSets()함수에서 _input 함수에 입력 데이터가 들어가며 _ideal에 훈련을 위한 출력 데이터가 들어갑니다. 갯수는 INPUT_TUPLES = 10, INDEXES_TO_CONSIDER = 4, OUTPUT_SIZE = 4 입니다.
cvs 파일에서 읽어들인 normalize된 변수들을 GetInputData를 통해서 40개의 입력을 받아들이는데 startIndex~endIndex를 사용자가 날짜로 선택하게 되는데 GetInputData에 첫번째 인자에 의해서 4개씩 4개씩 모두 40개가 데이터에 들어옵니다.
public void GetInputData(int offset, double[] input)
{
// get SP500, prime data, NASDAQ, Dow
for (int i = 0; i < _inputSize; i++)
{
FinancialIndexes sample = _samples[offset + i];
input[i*4] = sample.Sp;
input[i*4 + 1] = sample.PrimeInterestRate;
input[i*4 + 2] = sample.DowIndex;
input[i*4 + 3] = sample.NasdaqIndex;
}
}
출력 데이터는 현재 offset+_inputSize 즉 10을 더한 위치의 값이 됩니다.
public void GetOutputData(int offset, double[] output)
{
FinancialIndexes sample = _samples[offset + _inputSize];
output[0] = sample.Sp;
output[1] = sample.PrimeInterestRate;
output[2] = sample.DowIndex;
output[3] = sample.NasdaqIndex;
}
정리하자면 input의 경우 [idx+0],[idx+1],[idx+2],[idx+3],~~,[idx+9]:10개씩 * 각가의 데이터 4개, 출력의 경우 [idx+10]:1개를 각각의 데이터 4개가 됩니다.
입력과 출력이 어떻게 만들어 졌는지 알면 신경망에서는 모든 분석이 완료되었다고 생각이 되네요.
예측시에는 input의 경우 [idx+0],[idx+1],[idx+2],[idx+3],~~,[idx+9]:10개씩 * 각가의 데이터 4개 모두 40개를 입력에 넣고 나오는 출력값을 보면 알게 될겁니다.
소스는 다음과 같습니다.
public List<PredictionResults> Predict(DateTime predictFrom, DateTime predictTo)
{
List<PredictionResults> results = new List<PredictionResults>();
double[] present = new double[INPUT_TUPLES * INDEXES_TO_CONSIDER];
double[] actualOutput = new double[OUTPUT_SIZE];
int index = 0;
foreach (var sample in _manager.Samples)
{
if (sample.Date.CompareTo(predictFrom) > 0 && sample.Date.CompareTo(predictTo) < 0)
{
PredictionResults result = new PredictionResults();
_manager.GetInputData(index - INPUT_TUPLES, present);
_manager.GetOutputData(index - INPUT_TUPLES, actualOutput);
var data = new BasicNeuralData(present);
var predict = _network.Compute(data);
result.ActualSp = actualOutput[0] * (_manager.MaxSp500 - _manager.MinSp500) + _manager.MinSp500;
result.PredictedSp = predict[0] * (_manager.MaxSp500 - _manager.MinSp500) + _manager.MinSp500;
result.ActualPir = actualOutput[1] * (_manager.MaxPrimeRate - _manager.MinPrimeRate) + _manager.MinPrimeRate;
result.PredictedPir = predict[1] * (_manager.MaxPrimeRate - _manager.MinPrimeRate) + _manager.MinPrimeRate;
result.ActualDow = actualOutput[2] * (_manager.MaxDow - _manager.MinDow) + _manager.MinDow;
result.PredictedDow = predict[2] * (_manager.MaxDow - _manager.MinDow) + _manager.MinDow;
result.ActualNasdaq = actualOutput[3] * (_manager.MaxNasdaq - _manager.MinNasdaq) + _manager.MinNasdaq;
result.PredictedNasdaq = predict[3] * (_manager.MaxNasdaq - _manager.MinNasdaq) + _manager.MinNasdaq;
result.Date = sample.Date;
ErrorCalculation error = new ErrorCalculation();
error.UpdateError(actualOutput, predict.Data);
result.Error = error.CalculateRMS();
results.Add(result);
}
index++;
}
return results;
}
이것도 참고하세요.
http://swlock.blogspot.kr/2015/04/blog-post.html