레이블이 AI인 게시물을 표시합니다. 모든 게시물 표시
레이블이 AI인 게시물을 표시합니다. 모든 게시물 표시

2017년 11월 5일 일요일

windows에서 tensorflow 설치 하기 와 처음시작하는 예제


python 설치


anaconda 배포판 설치
3.x 버전과 2.x 버전이 있는데 두개 호환성이 없습니다. 자신이 배우려고 하는 예제에 어떤 버전을 사용하는지 확인해보고 설치합니다. 여기에서 사용한 예제는 3.x 예제이므로 3.x 버전을 선택합니다.

https://www.anaconda.com/

위치: download > 선택 > 윈도우용 설치 32/64 bit 자신의 window 환경에 맞게 선택, Python 3.x 버전 선택

tensorflow 설치


anaconda를 설치하고나면 anaconda prompt 라는게 있습니다.



prompt에서 아래 명령어로 설치합니다.

pip install tensorflow
(설치된경로명이나옴\Anaconda3) 작업폴더anaconda>pip install tensorflow
이미 설치해서 최신 버전이라면 아래와 같이 나옵니다.


tensorflow example 코드 실행

설치했다면 제대로 실행해야하는지 동작 여부를 봐야할것입니다. 여기에서는 간단하게 두개의 변수를 곱해서 결과를 출력하는 코드입니다.

파일명 1.py 로 저장함
import tensorflow as tf

a = tf.placeholder("float")
b = tf.placeholder("float")

y = tf.multiply(a,b)

sess = tf.Session()

print (sess.run(y, feed_dict={a:3,b:4}))

실행화면
(E:\Users\darts\Anaconda3) E:\work\ai\anaconda>python 1.py
2017-11-05 21:25:15.241264: I C:\tf_jenkins\home\workspace\rel-win\M\windows\PY\36\tensorflow\core\platform\cpu_feature_guard.cc:137] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX AVX2
12.0

설명

플레이스 홀더 : placeholder (러닝)실행중 값을 변경할 수 있도록 하는 변수
텐서 : 동적크기를 갖는 배열(1차원 벡터일수도 있고 2차원 행렬 이거나, 다차원 배열 일 수 있듬)
tf.multiply() 는 텐서(tensor)를 조작하기위한 텐서플로의 수학 함수 중 하나
지원하는 수학 함수 목록
https://www.tensorflow.org/versions/master/api_guides/python/math_ops
Session() 세션을 생성해야만 텐서플로 라이브러리와 상호 작용가능
Session.run()을 통해 텐서플로를 호출함, 이때 feed_dict에 인수로 값을 넘겨줌










2015년 4월 26일 일요일

신경망을 이용한 경제(주식) 예측 코드 한번해보기

앞서 http://swlock.blogspot.kr/2014/11/blog-post.html 여기에서 http://www.codeproject.com/Articles/175777/Financial-predictor-via-neural-network 를 이용하여 코드를 분석하였습니다.
실제는 어떤지 한번 우리나라 데이터를 넣어서 테스트 한번 해보도록 하겠습니다.

기억이 날지 모르겠지만,
앞에서는 아래표에서 처럼 40개의 입력과 4개의 출력을 가지도록 되어 있습니다.
#12...1011
NASDAQ2288.552301.66...2231.65?
DOW12376.7212319.73...12350.61?
S&P5001110.881112.92...1099.5?
PIR3.253.25...3.25?

비슷하게 하기 위해서는 주식 데이터와 금리 데이터가 필요합니다.
그런데 자료를 구하기 힘들기 때문에 HTS의 저장기능을 이용해서 자료를 준비하였습니다.
HTS에서 일반적으로 엑셀로 저장하는 기능이 있습니다.


구한 자료는 KODEX 레버리지, KODEX 인버스, KOSPI 3개로 하였습니다.
엑셀 프로그램이 없으면 아래와 같이 오픈 office를 설치하여 자료를 변환 하였습니다.


자료는 일반적으로 tab이나 콤마로 구분되는데 그런 부분은 hts에 따라서 다릅니다.
엑셀같은 프로그램에서 해당 데이터를 가져오면 조작을 다시한번 해야합니다.

아래는 코스피 데이터를 가져왔습니다. 이중에 종가 기준으로 데이터를 넣는다고 했을때 종가만 나두고 나머지데이터는 삭제해야 합니다.

그래서 코스피외 나머지 자료도 모아서 하나의 엑셀에 담았습니다.

기간도 2010/05 부터 2014년 까지 자료를 train으로 하고 2015데이터를 test 데이터로 만들어서 진행하였습니다.

데이터를 위와같이 모았으면, MBP 툴에서 사용할 수 있도록 데이터를 만듭니다.
지표를 3개로 하고 10개씩 입력하면 3*10=30 X의 갯수는 30개가 되고 Y는 각각의 출력을 한다고 봤을때 3개를 두면 됩니다. 물론 1개여도 관계는 없지만, 위의 예를 참고로 만드는 것이니 비슷하게 하도록 하겠습니다.
입력 데이터는 아래 내용중 오른쪽에 33개 데이터를 다시 CVS형태로 변환 합니다.


train용 입력데이터는 아래와 같습니다.
X1,X2,X3,X4,X5,X6,X7,X8,X9,X10,X11,X12 ...
9565,9635,9120,9250,9570,9800,9950,9890,10340,10360,...
9635,9120,9250,9570,9800,9950,9890,10340,10360,10005,9955...
...

node는 30-30-15-8-3으로 하였습니다.

MBP 프로그램을 이용하여 train중인 화면입니다. train 데이터를 입력하였습니다. 툴사용하는 방법에 대해서는 이미 이전에 작성한 내용을 참고하시기 바랍니다.


시간이 많이 흐를수록 에러수치가 떨어지게 됩니다. configuration을 어떻게 하느냐에 따라 오래걸릴 수도 있고 적당한 시점에 stop버튼을 눌러도 됩니다. (어차피 테스트용이니...)

결과를 넣은 데이터 입니다.
보는 방법은 아래의 x축은 test  데이터의 순번을 의미합니다 즉 빨간색과 검은색 수치의 차이가 오차를 의미합니다.

앗 생각해보니 정규화를 안했네요.
최대 최소값을 읽어서 해당범위의 값으로 바꾸면 모든 수치가 0~1 사이의 범위에 있게 만들어 지는데 이 과정을 정규화라고 합니다.
(X-최소)/(최대-최소) = 0~1 범위의 숫자가 나옵니다.
정규화 데이터를 이용해서 새로 만들겠습니다.



이걸 결과를 돌리면 아래와 같이 나옵니다. 시간이 오래걸려서 중단시킨상태에서 test를 하였습니다.
정규화된 데이터라서 다시 최대 최소 값을 이용해서 원본값을 찾아내야 합니다.






2014년 11월 9일 일요일

신경망을 이용한 경제(주식) 예측 코드 분석

신경망을 이용해 뭘할 수 있을까? 고민이 많을것 같은데요.. 요즈음 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, 다우, 나스닥, 프라임 금리의 과거 데이터를 사용합니다. 이러한 공통의 변동 패턴이 주식 시장 활동의 지표를 선도하고 있습니다.

Input

입력 데이터로는 4개의 지표의 과거 10개의 데이터를 사용합니다. 결국 4*10=40개의 입력 데이터 x를 넣고 11번째의 데이터를 y에 넣어서 학습을 합니다.
Pairs used in prediction:
#12...1011
NASDAQ2288.552301.66...2231.65?
DOW12376.7212319.73...12350.61?
S&P5001110.881112.92...1099.5?
PIR3.253.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


2014년 11월 8일 토요일

MBP 사용 XOR 학습하기

앞에서 다른 툴을 사용할때 대부분 XOR예제를 들었습니다.
이번에도 XOR로 예제를 할껀데요, Multi-layer이므로 hidden을 2개 2개 연결하는 전체 layer를 4개가 되도록 만들겠습니다.
이미 다른 툴에서도 같은 방식으로 테스트 하였습니다.




빨간색으로 표시된 부분에 node형태를 기록합니다. 2-2-2-1 을 적어주면 자동으로 아래와 같은 신경망이 생기게 됩니다.

학습 데이터는 CSV 파일로 입력하면 되는데 ","로 구별되게 만들면 됩니다. excel 로 만들거나 open office 또는 직접만들면 됩니다.

x1,x2,y
0,0,0
1,0,1
0,1,1
1,1,0

train data를 선택하고 가중치 값을 randomize 설정합니다. 일반적으로 -0.5~0.5 구간으로 설정하고 train 버튼을 누릅니다.


해당 결과를 C언어로 생성하면 다음과 같다

/**
 Generated by Multiple Back-Propagation Version 2.2.4
 Multiple Back-Propagation can be freely obtained at http://dit.ipg.pt/MBP
*/

#include <math.h>
/**
 inputs  - should be an array of 2 element(s), containing the network input(s).
 outputs - should be an array of 1 element(s), that will contain the network output(s).
 Note : The array inputs will also be changed.Its values will be rescaled between -1 and 1.
*/
void test1(double * inputs, double * outputs) {
 double mainWeights[] = {-3.432557344436646, 3.249020814895630, 3.257052659988403, 2.534264326095581, 2.839271068572998, 2.841655492782593, -2.028863191604614, -5.165799140930176, 4.703624248504639, 0.984100341796875, 1.948586821556091, -2.196990251541138, -2.397869348526001, 8.108511924743652, -3.533092021942139};
 double * mw = mainWeights;
 double hiddenLayer1outputs[2];
 double hiddenLayer2outputs[2];
 int c;

 inputs[0] = -1.0 + (inputs[0] - 0.000000000000000) / 0.500000000000000;
 inputs[1] = -1.0 + (inputs[1] - 0.000000000000000) / 0.500000000000000;
 hiddenLayer1outputs[0] = *mw++;
 for(c = 0; c < 2; c++) hiddenLayer1outputs[0] += *mw++ * inputs[c];
 hiddenLayer1outputs[0] = 1.0 / (1.0 + exp(-hiddenLayer1outputs[0]));
 hiddenLayer1outputs[1] = *mw++;
 for(c = 0; c < 2; c++) hiddenLayer1outputs[1] += *mw++ * inputs[c];
 hiddenLayer1outputs[1] = 1.0 / (1.0 + exp(-hiddenLayer1outputs[1]));
 hiddenLayer2outputs[0] = *mw++;
 for(c = 0; c < 2; c++) hiddenLayer2outputs[0] += *mw++ * hiddenLayer1outputs[c];
 hiddenLayer2outputs[0] = 1.0 / (1.0 + exp(-hiddenLayer2outputs[0]));
 hiddenLayer2outputs[1] = *mw++;
 for(c = 0; c < 2; c++) hiddenLayer2outputs[1] += *mw++ * hiddenLayer1outputs[c];
 hiddenLayer2outputs[1] = 1.0 / (1.0 + exp(-hiddenLayer2outputs[1]));
 outputs[0] = *mw++;
 for(c = 0; c < 2; c++) outputs[0] += *mw++ * hiddenLayer2outputs[c];
 outputs[0] = 1.0 / (1.0 + exp(-outputs[0]));
 outputs[0] = 0.000000000000000 + (outputs[0] - 0.000000) * 1.000000000000000;
}

위 소스를 java로 변경하여 테스트 해보았습니다.
public class MBPtest {

 /**
  * @param args
  */
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  
  double []inputs = new double [2];
  double []outputs = new double [1];
  inputs[0]=0d;
  inputs[1]=0d;
  test1(inputs,outputs);
  System.out.println("test1 : 0 0 "+outputs[0]);

  inputs[0]=0d;
  inputs[1]=1d;
  test1(inputs,outputs);
  System.out.println("test1 : 0 1 "+outputs[0]);

  inputs[0]=1d;
  inputs[1]=0d;
  test1(inputs,outputs);
  System.out.println("test1 : 1 0 "+outputs[0]);

  inputs[0]=1d;
  inputs[1]=1d;
  test1(inputs,outputs);
  System.out.println("test1 : 1 1 "+outputs[0]);
 }
 public static void test1(double []inputs, double []outputs) {
  double mainWeights[] = {-3.432557344436646, 3.249020814895630, 3.257052659988403, 2.534264326095581, 2.839271068572998, 2.841655492782593, -2.028863191604614, -5.165799140930176, 4.703624248504639, 0.984100341796875, 1.948586821556091, -2.196990251541138, -2.397869348526001, 8.108511924743652, -3.533092021942139};
  double [] mw = mainWeights;
  int mwindex = 0;
  double []hiddenLayer1outputs = new double[2];
  double []hiddenLayer2outputs = new double[2];
  int c;

  inputs[0] = -1.0 + (inputs[0] - 0.000000000000000) / 0.500000000000000;
  inputs[1] = -1.0 + (inputs[1] - 0.000000000000000) / 0.500000000000000;
  hiddenLayer1outputs[0] = mw[mwindex++];
  for(c = 0; c < 2; c++) hiddenLayer1outputs[0] += mw[mwindex++] * inputs[c];
  hiddenLayer1outputs[0] = 1.0 / (1.0 + Math.exp(-hiddenLayer1outputs[0]));
  hiddenLayer1outputs[1] = mw[mwindex++];
  for(c = 0; c < 2; c++) hiddenLayer1outputs[1] += mw[mwindex++] * inputs[c];
  hiddenLayer1outputs[1] = 1.0 / (1.0 + Math.exp(-hiddenLayer1outputs[1]));
  hiddenLayer2outputs[0] = mw[mwindex++];
  for(c = 0; c < 2; c++) hiddenLayer2outputs[0] += mw[mwindex++] * hiddenLayer1outputs[c];
  hiddenLayer2outputs[0] = 1.0 / (1.0 + Math.exp(-hiddenLayer2outputs[0]));
  hiddenLayer2outputs[1] = mw[mwindex++];
  for(c = 0; c < 2; c++) hiddenLayer2outputs[1] += mw[mwindex++] * hiddenLayer1outputs[c];
  hiddenLayer2outputs[1] = 1.0 / (1.0 + Math.exp(-hiddenLayer2outputs[1]));
  outputs[0] = mw[mwindex++];
  for(c = 0; c < 2; c++) outputs[0] += mw[mwindex++] * hiddenLayer2outputs[c];
  outputs[0] = 1.0 / (1.0 + Math.exp(-outputs[0]));
  outputs[0] = 0.000000000000000 + (outputs[0] - 0.000000) * 1.000000000000000;
 }
}


결과는 아래와 같이 나왔습니다.

test1 : 0 0 0.022113442375209617
test1 : 0 1 0.9805543032118464
test1 : 1 0 0.980571510242402
test1 : 1 1 0.018783890881570123
굉장히 편한 도구입니다. 대부분의 학습은 속도가 느리기 때문에 해당 툴을 CUDA를 이용해서 학습 후 결과만 받아서, 사용하는 곳에서는 그 결과를 이용해서 구현하면 되기 때문에 모바일 환경에서도 쉽게 구현할 수 있을것 같습니다.



MBP tool 소개 (CUDA 지원)

헐~~ 이미 CUDA가 지원되는 MBP 툴이 존재하고 있었습니다.
어렵게 코드를 만들 필요가 없고 학습 후 결과만 사용해도 유용한 도구가 될 것 같네요.

툴이름은 MBP 이고 홈페이지는 http://mbp.sourceforge.net/ 입니다.

CUDA를 지원하는게 매력적입니다.


tutorial 이 있어서 참고하면 되긴한데, 설명하는 버전과 툴의 버전이 안 맞아서 다른 부분도 꽤 있고, 도움말 문서가 없고 영상 밖에 없어서 조금 불편한 점이 있습니다. 하지만 학습된 결과를 c 소스 생성하는 기능도 있고 굉장히 유용하리라 생각됩니다.

2014년 11월 6일 목요일

Java로 구현한 Neural Network BP(Backpropagation)

기존에 작업한 NNBP 클래스를 좀 더 다듬어 보도록 하겠습니다.

learn 함수를 호출하는 wile 문이 너무 길어져서 지저분 해보여서, pattern을 ArrayList로 미리 넣어두고 학습을 하도록 변경하였습니다.
그리고 double보다 속도를 높이기 위해서 전체적으로 float로 변경하였습니다.

기존 코드
while( true ) {
   loop_count++;
   E_sum = 0;

   //Step 3 : For each training pattern pair
   //do Step 4-10 until k = p

   // k=1
   x[0] = 0.0f;
   x[1] = 0.0f;
   y[0] = 0.0f;
   E_sum += nnbp.learn(x,y);

   // k=2
   x[0] = 0.0f;
   x[1] = 1.0f;
   y[0] = 1.0f;
   E_sum += nnbp.learn(x,y);

   // k=3
   x[0] = 1.0f;
   x[1] = 0.0f;
   y[0] = 1.0f;
   E_sum += nnbp.learn(x,y);

   // k=4
   x[0] = 1.0f;
   x[1] = 1.0f;
   y[0] = 0.0f;
   E_sum += nnbp.learn(x,y);

   // Step 11 :   Test stop condition
   if( E_sum < E_MAX ) break;
  }

개선 코드

wile 문 내부가 간단해 졌습니다.
  while( true ) {
   loop_count++;
   E_sum = nnbpv2.epoch(xPatterns,yPatterns);
   if( E_sum < E_MAX ) break;
  }

epoch 함수 내에서 learn 함수를 호출 하도록 변경하였습니다.
 public float epoch(ArrayList<float[]> xPatterns, ArrayList<float[]> yPatterns) {
  int patternCount = xPatterns.size();
  float err = 0f;
  for( int i = 0 ; i < patternCount ; i++ ){
   float x[] = xPatterns.get(i);
   float y[] = yPatterns.get(i);
   err += learn(x,y);
  }
  return err;
 }

소스


2014년 11월 3일 월요일

Neural Network BP(Backpropagation)를 이용한 XOR 학습 in Java


소스는 http://martinblog.tistory.com/888 여기 cpp 소스를 참고하였습니다. 하지만 일부 구현이 이상하다고 판단되는 부분은 다른 ppt를 참고하였습니다.
http://ai-times.tistory.com/272 여기 1211978906_09. BP알고리즘.ppt 첨부 문서를 참고하였습니다만, 여기에서도 일부 잘못된 내용들이 있었습니다.
p.14 Step 8 부분인데 빨간색으로 표시된 부분이 잘못표현 되어있었습니다.

Class이름을 NNBP라고 이름 지었습니다.
NNBP는 생성자를 호출할때 입출력,히든 노드의 개수와 학습률, 출력 최대 오차를 정의 합니다.
그리고 학습하는 함수는 learn이란 이름으로 하고 인자로는 입력 값과 출력을 넘겨 주면 오차를 넘겨줍니다. 이것을 가지고 전반적인 top down식 설계를 하여, xor을 학습 시키는 예제를 만들도록 하겠습니다.

  final int INPUT_NODE_COUNT = 2;
  final int HIDDEN_NODE_COUNT = 4;
  final int OUTPUT_NODE_COUNT = 1;

  //Step 2 : Set learning rate and Emax
  final double ALPHA = 1.0d;//학습률
  final double E_MAX = 0.01d;//최대 출력 오차
  double E_sum = 1d;

  NNBP nnbp= new NNBP(
    INPUT_NODE_COUNT,
    HIDDEN_NODE_COUNT,
    OUTPUT_NODE_COUNT,
    ALPHA,
    E_MAX
    );

  double x[] = new double [INPUT_NODE_COUNT];
  double y[] = new double [OUTPUT_NODE_COUNT];
  int loop_count = 0;

  while( true ) {
   loop_count++;
   E_sum = 0;

   //Step 3 : For each training pattern pair
   //do Step 4-10 until k = p

   // k=1
   x[0] = 0.0d;
   x[1] = 0.0d;
   y[0] = 0.0d;
   E_sum += nnbp.learn(x,y);

   // k=2
   x[0] = 0.0d;
   x[1] = 1.0d;
   y[0] = 1.0d;
   E_sum += nnbp.learn(x,y);

   // k=3
   x[0] = 1.0d;
   x[1] = 0.0d;
   y[0] = 1.0d;
   E_sum += nnbp.learn(x,y);

   // k=4
   x[0] = 1.0d;
   x[1] = 1.0d;
   y[0] = 0.0d;
   E_sum += nnbp.learn(x,y);

   // Step 11 :   Test stop condition
   if( E_sum < E_MAX ) break;
  }

  System.out.printf("loop_count:%d, error:%f\n",loop_count,E_sum);


이제 학습이 끝나면 테스트가 필요하므로 테스트시 필요한 소스를 제작해 보았습니다.

  // test
  x[0] = 0.0d;
  x[1] = 0.0d;
  y = nnbp.doTest(x);
  System.out.printf("test : %f %f %f \n",x[0],x[1],y[0]);
  x[0] = 1.0d;
  x[1] = 0.0d;
  y = nnbp.doTest(x);
  System.out.printf("test : %f %f %f \n",x[0],x[1],y[0]);
  x[0] = 0.0d;
  x[1] = 1.0d;
  y = nnbp.doTest(x);
  System.out.printf("test : %f %f %f \n",x[0],x[1],y[0]);
  x[0] = 1.0d;
  x[1] = 1.0d;
  y = nnbp.doTest(x);
  System.out.printf("test : %f %f %f \n",x[0],x[1],y[0]);

클래스 생성자에서는 변수 초기화를 하는데 BP에서는 가중치값이 -0.5~+0.5가 되도록 설정합니다. (random함수 이용)

 public NNBP(int input, int hidden, int output, double aLPHA, double e_MAX) {
  inputNodeCount = input;
  hiddenNodeCount = hidden;
  outputNodeCount = output;
  alpha = aLPHA;
  e_max = e_MAX;
  v = new double [input][hidden];
  z = new double [hidden];
  w = new double [hidden][output];
  y = new double [output];
  
  for (int i = 0; i < inputNodeCount; i++) {
   for (int j = 0; j < hiddenNodeCount; j++) {
    v[i][j] = (double)Math.random()-0.5d;
   }
  }
  for (int i = 0; i < hiddenNodeCount; i++) {
   for (int j = 0; j < outputNodeCount; j++) {
    w[i][j] = (double)Math.random()-0.5d;
   }
  }
 }

소스내의 변수들은 아래의 이미지를 참고하여 구현하였습니다. x는 입력, v는 입력층과 은닉층의 연결강도, z는 은닉층, w는 은닉층과 출력층의 연결강도, y는 출력층을 의미합니다. 그리고 ppt문서와는 다르게 상수로 1로 표현되고 있는 bias 부분은 적용하지 않았습니다.(빨간색 X표로 표시해 두었습니다.)

learn함수는 4~9단계로 이루어지며, 4,5단계에서는 출력을 계산합니다.

  //Step 4 : Compute output of hidden layer 
  for (int i = 0; i < hiddenNodeCount; i++)
  {
   double NETz = 0.0d;
   for (int j = 0; j < inputNodeCount; j++) {
    NETz += x[j] * this.v[j][i];
   }
   this.z[i] = sigmoid(NETz);
  }

  //Step 5 : Compute output
  for (int k = 0; k < outputNodeCount; k++)
  {
   double NETy = 0.0d;
   for (int i = 0; i < hiddenNodeCount; i++) {
    NETy += z[i] * this.w[i][k];
   }
   this.y[k] = sigmoid(NETy);
  }

6단계에서는 출력 오차를 계산하고 마지막에 오차를 리턴할때 사용합니다.

  //Step 6 : Compute output error
  double Err = 0.0d;
  for (int k = 0; k < outputNodeCount; k++) {
   Err = (float)(Err + ((t[k] - this.y[k])*(t[k] - this.y[k])) / 2.0D);
  }

7,8,9단에서는 오차 기울기를 구하고 가중치를 변경합니다.

  //Step 7 : Compute error signal of output layer
  // dy = (d-y)*y*(1-y) :: y*(1-y)시그모이드 함수의 미분  * (d-y)출력 오차값  => 오차기울기
  double dy[] = new double [ outputNodeCount ];
  for( int k=0; k < outputNodeCount; k++ ) 
  {
   dy[k] = (t[k] - y[k]) * y[k] * (1 - y[k]);
  }

  //Step 8 : Compute error signal of hidden layer
  // dz = z*(1-z) * sigma[i=1]to[m]( dy * w )
  double dz[] = new double [ hiddenNodeCount ];
  for (int i = 0; i < hiddenNodeCount; i++)
  {
   double Sum = 0.0d;
   for (int k = 0; k < outputNodeCount; k++) {
    Sum += dy[k] * w[i][k];
   }
   dz[i] = (z[i] * (1.0d - z[i]) * Sum);
  }

  //Step 9 : Update weights
  for (int i = 0; i < hiddenNodeCount; i++) {
   for (int j = 0; j < outputNodeCount; j++)
   {
    w[i][j] += (alpha * dy[j] * z[i]);
   }
  }
  for (int i = 0; i < inputNodeCount; i++) {
   for (int j = 0; j < hiddenNodeCount; j++)
   {
    v[i][j] += (alpha * dz[j] * x[i]);
   }
  }

테스트 하는 함수는 4,5단계에 사용하는 z,y의 변수를 class 멤버변수로 사용하지 않고 로컬 변수로 사용하며 최종결과 값인 y값을 리턴해주면 됩니다.
public double[] doTest(double[] testx) {
  double testz[];
  double testy[];
  testz = new double [hiddenNodeCount];
  testy = new double [outputNodeCount];
  // Compute output of hidden layer 
  for (int i = 0; i < hiddenNodeCount; i++)
  {
   double NETz = 0.0d;
   for (int j = 0; j < inputNodeCount; j++) {
    NETz += testx[j] * this.v[j][i];
   }
   testz[i] = sigmoid(NETz);
  }

  // Compute output
  for (int k = 0; k < outputNodeCount; k++)
  {
   double NETy = 0.0d;
   for (int i = 0; i < hiddenNodeCount; i++) {
    NETy += testz[i] * this.w[i][k];
   }
   testy[k] = sigmoid(NETy);
  }
  return testy;
 }
테스트시 아래와 같은 결과가 나옵니다.
loop_count:1827, error:0.009997
test : 0.000000 0.000000 0.077762 
test : 1.000000 0.000000 0.932610 
test : 0.000000 1.000000 0.933328 
test : 1.000000 1.000000 0.069122 
각 출력의 의미하는 바는 다음과 같습니다.
loop_count는 학습한 횟수를 의미합니다. error는 학습이 끝났을때 오차를 의미하고, test 는 각각의 test case에서 결과값이 어떤지를 보여줍니다.
개인적으로 완벽하고 이해하기 쉽게 구현했다고 생각되는 java 구현 소스를 공개합니다.


2014년 11월 2일 일요일

도함수와 역함수 sigmoid 인공지능


신경망 활성함수에 sigmoid 함수를 종종 사용한다. 그래서 여기에서는 sigmoid함수와 도함수와 역함수를 살펴 보도록 하였습니다.

시그모이드 함수는 아래와 같이 표시됩니다.

S(t) = \frac{1}{1 + e^{-t}}.

SigmoidGraph.png


* 역함수 (Inverse Function)
  - y가 x의 함수일 때, 그 역으로 x를 y의 함수로 본 것

* 도함수 (derivative) http://en.wiktionary.org/wiki/derivative
  - 곡선에 그은 접선의 기울기를 나타내는 함수입니다.

신경망 관련 소스코드를 보면 가끔 sigmoid 함수의 함수명을 inverse sigmoid 로 작명해놓은 소스가 있었습니다.

표기법이 비슷해서 혼동한것 같습니다.
역함수 : f^{-1}
도함수 : \, f'(x)
그걸 보고 역함수로 이해하고 작명을 한게 아닌가 싶네요. 퍼셉트론이나 신경망에 나오는것은 도함수입니다. sigmoid 함수의 도함수는 간단하게 y*(1-y) 로 표현이 가능합니다.
http://roboticist.tistory.com/494 참고하세요.


도함수는 곡선에 그은 접선의 기울기를 나타내는 함수입니다.
따라서 '도함수를 알고 있다.'는 것은 곡선 위의 임의의 점에서의 '접선의 기울기를 알고 있다.'는 것과 같습니다.

수학적으로 다음과 같이 표현합니다.
f'(x) = \lim_{\Delta x \to 0} \frac{f(x + \Delta x) - f(x)}{\Delta x}


미분 계수 = 한 지점에서의 순간변화율

Lim-secant.svg왼쪽의 그래프와 같이 변화량 \Delta x 가 0 으로 수렴하는 극한을 취하면 결국 미분계수는 접선의 기울기가 된다.



결론 : 신경망에 나오는 함수는 sigmoid, sigmoid의 도함수를 사용한다.

2014년 10월 26일 일요일

JustNN

NN쪽 어플들 찾다 보니 아래와 같은 툴이 있습니다.
JavaNNS 보다는 좀 더 쉬워 보입니다. 3가지 어플이 있는데 그 중에 기능이 제한된 무료 버전을 http://www.justnn.com/ 여기에서 받을 수 있습니다.

특징이라면, JavaNNS에서도 학습하기 위한 데이터 pat파일(패턴 데이터) 를 만들기 힘들었는데, txt,cvs,xls,bmp,binary 를 이용해서 할 수 있다고 합니다.
그리고 학습한 후에 test할 수 있는 query 기능도 편하게 되어 있습니다.

샘플은 있지만, 튜토리얼 없어서 한참 연구했습니다.


File>New 를 선택한 후, Grid 형태의 UI가 나오게 됩니다. 여기에서 Input/Output Column을 만들어 줍니다. 그리고 Training data도 넣습니다. Training 는 row로 증가되어 T:0, T:1 ... 이런식으로 나타나고 Output의 경우 O:0, O:1 이런식으로 나타납니다.
이번에도 XOR을 만들기 때문에 Input 2개 Output 1개 training data 4개가 되도록 만듭니다.
삭제할때가 어려운데 그리드를 더블클릭하면 빨간색으로 변하는데 Edit>Delete를 하거나 delete를 키를 누릅니다.


이제 네트워크를 만들 차례입니다.

JavaNNS때와 같이 hidden layer를 2개 만들고 node도 아래와 같이 각각 2개씩 만듭니다.
툴에서 Create New Network을 눌러서 만듭니다. 만든후에는 View Network 툴을 이용해서 제대로 만들어 졌는지 확인합니다.





JavaNNS와 다르게 네트워크를 만들면서 학습이 동시에 이루어집니다. 메뉴상에 추가로 학습할 수 있는 메뉴가 있는것 같습니다.
그리고 제대로 학습 되었는지 에러 그래프로 에러를 확인해 줍니다.

결과를 보니 아주 훌륭하네요. ㅋㅋ
그럼 이제 훈련을 했으니 임의의 입력을  넣어서 출력이 어떻게 나오는지 보도록 하죠.
View Grid를 눌러서 Grid 상태에서 메뉴의 Query>Add Query 버튼을 눌러도 되고 툴바의 +Q버튼을 눌러도 됩니다.



원하는 값을 입력하면 출력에 결과가 나옵니다.





Neural Network 과 javaNNS 소개

Neural Network에 관하여 공부하다가 좀 더 이해하기 쉽게 공부할 수 있는 방법이 없을까 찾아보다가 JavaNNS를 찾았습니다.
이 글은 JavaNNS 자세한 설명서가 없어서 이것저것 시도한 글입니다.
JavaNNS란 Java Neural Network Simulator입니다. SNN(Simulation of Neural Networks)가 이미 존재하는건데 Java로 만든겁니다.
다운로드는 http://www.ra.cs.uni-tuebingen.de/downloads/JavaNNS/ 여기에서 할 수 있습니다.
그리고 자바 버전은 jre7 을 설치해 줍니다. 혹시 java8 버전을 이용하시는 분이 안되시면 java7로 설치해보시기 바랍니다.
JavaNNS를 설치하면 manual.pdf가 존재하는데 그것은 메뉴와 기능에 대한 설명입니다.
NN에서 자주 등장하게 나오는 XOR을 만드는 설명을 해보도록 하겠습니다.

NN에 관해 공부를 하셨다면, XOR이 Hidden layer 없이 퍼셉트론으로 분류가 안된다는 사실을 아실겁니다.
그래서 Hidden layer를 입력한 예제를 준비하였습니다.(example 폴더에서 구할 수 있습니다.)

참고 :
http://baibook.epfl.ch/exercises/supervisedLearningNN/NN_Exercise.pdf
http://www.cs.bham.ac.uk/~jxb/NN/javaNNS/javaNNSguide.html

처음 시작하면 아래와 같은 화면이 나온다. 처음 보이는 화면이 View>Network 화면이 기본으로 보이는 윈도우가 됩니다.


Network의 노드를 만들기 위해서는 Tool>Create>Layers를 선택합니다.
그리고 노드를 만들때 주의할 점은 Unit type과 Layer number가 됩니다.


일반적으로 Input이 하나의 Layer를 가지고 Hidden 과 Output Layer를 지닐때 각각 Layer 번호를 다르게 입력하도록 합니다.



XOR 을 예를 들자면 아래와 같습니다.
입력 출력
0 0   0
1 0   1
0 1   1
0 0   0
입력이 2개이고 출력이 하나가 되도록 합니다.
그리고 hidden이 몇 개 있을지는 맘대로 해도 됩니다. hidden을 0,1,2개 등등 시뮬레이션 해볼 수 있는 툴이 JavaNNS가 되겠습니다. hidden Layer를 더많이 넣어 볼 수도 있습니다.

여기에서는 hidden node를 2개 hidden layer 2개를 만들어 보겠습니다.
그리고 Tools>Create connections 선택해서 Connect feed-forward 선택하면 됩니다.
그려면 화살표가 자동으로 나오게 되며 하나씩 움직일 수 있습니다.


이제 훈련시키는일이 남았습니다.
훈련시키기 위해서는 패턴 데이터가 필요합니다. 구글 검색을 해봤는데 같이 첨부된 패턴 데이터를 참고하라고 합니다.
JavaNNS 예제 폴더에 보면 xor.pat 파일을 찾을 수 있습니다.
이것을 File>Open 해서 파일을 xor.pat파일을 선택합니다.

아래는 xor.pat파일 내용입니다. 패턴은 4가지이고 입력은 2개 출력은 하나를 가지고 있는 데이터 입니다.
SNNS pattern definition file V3.2
generated at Mon Apr 25 15:58:23 1994

No. of patterns : 4
No. of input units : 2
No. of output units : 1
# Input pattern 1:
0 0
# Output pattern 1:
0
# Input pattern 2:
0 1
# Output pattern 2:
1
# Input pattern 3:
1 0
# Output pattern 3:
1
# Input pattern 4:
1 1
# Output pattern 4:
일단 데이터를 초기화 하도록 합니다. Tool>Control Panel : Initializing 탭에서 Init을 누릅니다. 그리고 훈련 시키기 위해서는 Tool>Control Panel : Learning 탭을 사용하고 훈련과정을 보기위해서 Error graph를 열어서 보도록 합니다.
Tool>Control Panel : Learning : Learn All 버튼을 누릅니다.

확인해야 할 부분은 Error graph에서 Learn All을 많이 했을때 값이 떨어지는지 확인하는것입니다. 그리고 Log에서 SSE값이 얼마가 나오는지 보면 됩니다. 0에 가까울 수록 오류가 작다는것을 의미합니다. 위 데이터에서는 11000 회만에 오차가 0이 되었습니다.