첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
728x90
반응형
728x170

TestProject.zip
다운로드

▶ NeuralNetwork.cs

using System;

namespace TestProject
{
    /// <summary>
    /// 신경망
    /// </summary>
    public class NeuralNetwork
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 난수 발생기
        /// </summary>
        private static Random _random;

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Instance
        //////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 입력 노드 카운트
        /// </summary>
        private int inputNodeCount;

        /// <summary>
        /// 은닉 노드 카운트
        /// </summary>
        private int hiddenNodeCount;

        /// <summary>
        /// 출력 노드 카운트
        /// </summary>
        private int outputNodeCount;


        /// <summary>
        /// 입력 노드 값 배열
        /// </summary>
        private double[] inputNodeValueArray;

        /// <summary>
        /// 입력-은닉 가중치 배열
        /// </summary>
        private double[][] inputHiddenWeightArray;

        /// <summary>
        /// 은닉 노드 바이어스 배열
        /// </summary>
        private double[] hiddenNodeBiasArray;

        /// <summary>
        /// 은닉 노드 값 배열
        /// </summary>
        private double[] hiddenNodeValueArray;

        /// <summary>
        /// 은닉-출력 가중치 배열
        /// </summary>
        private double[][] hiddenOutputWeightArray;

        /// <summary>
        /// 출력 노드 바이어스 배열
        /// </summary>
        private double[] outputNodeBiasArray;

        /// <summary>
        /// 출력 노드 값 배열
        /// </summary>
        private double[] outputNodeValueArray;


        /// <summary>
        /// 은닉 노드 그라디언트 배열
        /// </summary>
        private double[] hiddenNodeGradientArray;

        /// <summary>
        /// 출력 노드 그라디언트 배열
        /// </summary>
        private double[] outputNodeGradientArray;


        /// <summary>
        /// 입력-은닉 이전 가중치 델타 배열
        /// </summary>
        private double[][] inputHiddenPreviousWeightDeltaArray;

        /// <summary>
        /// 은닉 노드 이전 바이어스 델타 배열
        /// </summary>
        private double[] hiddenNodePreviousBiasDeltaArray;

        /// <summary>
        /// 은닉-출력 이전 가중치 델타 배열
        /// </summary>
        private double[][] hiddenOutputPreviousWeightDeltaArray;

        /// <summary>
        /// 출력 노드 이전 바이어스 델타 배열
        /// </summary>
        private double[] outputNodePreviousBiasDeltaArray;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - NeuralNetwork(inputNodeCount, hiddenNodeCount, outputNodeCount)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="inputNodeCount">입력 노드 카운트</param>
        /// <param name="hiddenNodeCount">은닉 노드 카운트</param>
        /// <param name="outputNodeCount">출력 노드 카운트</param>
        public NeuralNetwork(int inputNodeCount, int hiddenNodeCount, int outputNodeCount)
        {
            _random = new Random(0);
 
            this.inputNodeCount  = inputNodeCount;
            this.hiddenNodeCount = hiddenNodeCount;
            this.outputNodeCount = outputNodeCount;
 
            this.inputNodeValueArray     = new double[inputNodeCount];
            this.inputHiddenWeightArray  = GetMatrixArray(inputNodeCount, hiddenNodeCount);
            this.hiddenNodeBiasArray     = new double[hiddenNodeCount];
            this.hiddenNodeValueArray    = new double[hiddenNodeCount];
            this.hiddenOutputWeightArray = GetMatrixArray(hiddenNodeCount, outputNodeCount);
            this.outputNodeBiasArray     = new double[outputNodeCount];
            this.outputNodeValueArray    = new double[outputNodeCount];
 
            this.InitializeWeightArray();
 
            this.hiddenNodeGradientArray = new double[hiddenNodeCount];
            this.outputNodeGradientArray = new double[outputNodeCount];
 
            this.inputHiddenPreviousWeightDeltaArray  = GetMatrixArray(inputNodeCount, hiddenNodeCount);
            this.hiddenNodePreviousBiasDeltaArray     = new double[hiddenNodeCount];
            this.hiddenOutputPreviousWeightDeltaArray = GetMatrixArray(hiddenNodeCount, outputNodeCount);
            this.outputNodePreviousBiasDeltaArray     = new double[outputNodeCount];
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 가중치 배열 구하기 - GetWeightArray()

        /// <summary>
        /// 가중치 배열 구하기
        /// </summary>
        /// <returns>가중치 배열</returns>
        public double[] GetWeightArray()
        {
            int totalWeightCount = (this.inputNodeCount  * this.hiddenNodeCount) +
                                   (this.hiddenNodeCount * this.outputNodeCount) +
                                    this.hiddenNodeCount + this.outputNodeCount;
 
            double[] targetWeightArray = new double[totalWeightCount];

            int k = 0;

            for(int i = 0; i < this.inputNodeCount; i++)
            {
                for(int j = 0; j < this.hiddenNodeCount; j++)
                {
                    targetWeightArray[k++] = this.inputHiddenWeightArray[i][j];
                }
            }
 
            for(int i = 0; i < this.hiddenNodeCount; i++)
            {
                targetWeightArray[k++] = this.hiddenNodeBiasArray[i];
            }

            for(int i = 0; i < this.hiddenNodeCount; i++)
            {
                for(int j = 0; j < this.outputNodeCount; j++)
                {
                    targetWeightArray[k++] = this.hiddenOutputWeightArray[i][j];
                }
            }
 
            for(int i = 0; i < this.outputNodeCount; i++)
            {
                targetWeightArray[k++] = this.outputNodeBiasArray[i];
            }

            return targetWeightArray;
        }

        #endregion
        #region 가중치 배열 설정하기 - SetWeightArray(sourceWeightArray)

        /// <summary>
        /// 가중치 배열 설정하기
        /// </summary>
        /// <param name="sourceWeightArray">소스 가중치 배열</param>
        public void SetWeightArray(double[] sourceWeightArray)
        {
            int totalWeightCount = (this.inputNodeCount  * this.hiddenNodeCount) +
                                   (this.hiddenNodeCount * this.outputNodeCount) +
                                    this.hiddenNodeCount + this.outputNodeCount;
 
            if(sourceWeightArray.Length != totalWeightCount)
            {
                throw new Exception("잘못된 소스 가중치 배열 길이 입니다.");
            }
 
            int k = 0;

            for(int i = 0; i < this.inputNodeCount; i++)
            {
                for(int j = 0; j < this.hiddenNodeCount; j++)
                {
                    this.inputHiddenWeightArray[i][j] = sourceWeightArray[k++];
                }
            }

            for(int i = 0; i < this.hiddenNodeCount; i++)
            {
                this.hiddenNodeBiasArray[i] = sourceWeightArray[k++];
            }

            for(int i = 0; i < this.hiddenNodeCount; i++)
            {
                for(int j = 0; j < this.outputNodeCount; j++)
                {
                    this.hiddenOutputWeightArray[i][j] = sourceWeightArray[k++];
                }
            }

            for(int i = 0; i < this.outputNodeCount; i++)
            {
                this.outputNodeBiasArray[i] = sourceWeightArray[k++];
            }
        }

        #endregion
        #region 학습하기 - Train(trainingValueArray, maximumEpochCount, learningRate, momentum)

        /// <summary>
        /// 학습하기
        /// </summary>
        /// <param name="trainingValueArray">학습 값 배열</param>
        /// <param name="maximumEpochCount">최대 시대 카운트</param>
        /// <param name="learningRate">학습률</param>
        /// <param name="momentum">모멘텀</param>
        public void Train(double[][] trainingValueArray, int maximumEpochCount, double learningRate, double momentum)
        {
            int      epochCount          = 0;
            double[] xValueArray         = new double[this.inputNodeCount ];
            double[] targetValueArray    = new double[this.outputNodeCount];
            int      traingingValueCount = trainingValueArray.Length;
            int[]    sequenceArray       = new int[traingingValueCount];
            int      sequenceCount       = sequenceArray.Length;

            for(int i = 0; i < sequenceCount; ++i)
            {
                sequenceArray[i] = i;
            }
 
            while(epochCount < maximumEpochCount)
            {
                double mse = GetMeanSquaredError(trainingValueArray);
 
                if(mse < 0.04d)
                {
                    break;
                }
 
                Shuffle(sequenceArray);
 
                for(int i = 0; i < traingingValueCount; i++)
                {
                    int sequenceIndex = sequenceArray[i];

                    Array.Copy(trainingValueArray[sequenceIndex], xValueArray, this.inputNodeCount);

                    Array.Copy(trainingValueArray[sequenceIndex], this.inputNodeCount, targetValueArray, 0, this.outputNodeCount);

                    ComputeOutputNodeValueArray(xValueArray);

                    UpdateWeightArray(targetValueArray, learningRate, momentum);
                }
 
                epochCount++;
            }
        }

        #endregion
        #region 출력 노드 배열 계산하기 - ComputeOutputNodeValueArray(sourceInputNodeValueArray)

        /// <summary>
        /// 출력 노드 배열 계산하기
        /// </summary>
        /// <param name="sourceInputNodeValueArray">소스 입력 노드 값 배열</param>
        /// <returns>출력 노드 값 배열</returns>
        public double[] ComputeOutputNodeValueArray(double[] sourceInputNodeValueArray)
        {
            if(sourceInputNodeValueArray.Length != this.inputNodeCount)
            {
                throw new Exception("잘못된 소스 입력 노드 값 배열 길이 입니다.");
            }

            double[] hiddenNodeSummaryArray = new double[this.hiddenNodeCount];
            double[] outputNodeSummaryArray = new double[this.outputNodeCount];

            int sourceInputNodeValueCount = sourceInputNodeValueArray.Length;

            for(int i = 0; i < sourceInputNodeValueCount; i++)
            {
                this.inputNodeValueArray[i] = sourceInputNodeValueArray[i];
            }

            for(int i = 0; i < this.hiddenNodeCount; i++)
            {
                for(int j = 0; j < this.inputNodeCount; j++)
                {
                    hiddenNodeSummaryArray[i] += this.inputNodeValueArray[j] * this.inputHiddenWeightArray[j][i];
                }
            }
 
            for(int i = 0; i < this.hiddenNodeCount; i++)
            {
                hiddenNodeSummaryArray[i] += this.hiddenNodeBiasArray[i];
            }

            for(int i = 0; i < this.hiddenNodeCount; i++)
            {
                this.hiddenNodeValueArray[i] = GetHyperTangentValue(hiddenNodeSummaryArray[i]);
            }

            for(int i = 0; i < this.outputNodeCount; i++)
            {
                for(int j = 0; j < this.hiddenNodeCount; j++)
                {
                    outputNodeSummaryArray[i] += this.hiddenNodeValueArray[j] * this.hiddenOutputWeightArray[j][i];
                }
            }

            for(int i = 0; i < this.outputNodeCount; i++)
            {
                outputNodeSummaryArray[i] += this.outputNodeBiasArray[i];
            }

            double[] softMaximumArray = GetSoftMaximumArray(outputNodeSummaryArray);

            Array.Copy(softMaximumArray, this.outputNodeValueArray, softMaximumArray.Length);

            double[] targetOutputNodeValueArray = new double[this.outputNodeCount];

            Array.Copy(this.outputNodeValueArray, targetOutputNodeValueArray, targetOutputNodeValueArray.Length);

            return targetOutputNodeValueArray;
        }

        #endregion
        #region 정확도 구하기 - GetAccuracy(trainingValueArray)

        /// <summary>
        /// 정확도 구하기
        /// </summary>
        /// <param name="trainingValueArray">훈련 값 배열</param>
        /// <returns>정확도</returns>
        public double GetAccuracy(double[][] trainingValueArray)
        {
            int      correctCount       = 0;
            int      wrongCount         = 0;
            double[] xValueArray        = new double[this.inputNodeCount ];
            double[] targetValueArray   = new double[this.outputNodeCount];
            double[] yValueArray;
            int      trainingValueCount = trainingValueArray.Length;
 
            for(int i = 0; i < trainingValueCount; i++)
            {
                Array.Copy(trainingValueArray[i], xValueArray, this.inputNodeCount);

                Array.Copy(trainingValueArray[i], this.inputNodeCount, targetValueArray, 0, this.outputNodeCount);

                yValueArray = ComputeOutputNodeValueArray(xValueArray);

                int maximumIndex = GetMaximumIndex(yValueArray);

                if(targetValueArray[maximumIndex] == 1d)
                {
                    correctCount++;
                }
                else
                {
                    wrongCount++;
                }
            }

            return (correctCount * 1d) / (correctCount + wrongCount);
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region 매트릭스 배열 구하기 - GetMatrixArray(rowCount, columnCount)

        /// <summary>
        /// 매트릭스 배열 구하기
        /// </summary>
        /// <param name="rowCount">행 카운트</param>
        /// <param name="columnCount">컬럼 카운트</param>
        /// <returns>매트릭스 배열</returns>
        private static double[][] GetMatrixArray(int rowCount, int columnCount)
        {
            double[][] matrixArray = new double[rowCount][];

            int count = matrixArray.Length;

            for(int i = 0; i < count; i++)
            {
                matrixArray[i] = new double[columnCount];
            }

            return matrixArray;
        }

        #endregion
        #region 가중치 배열 초기화 하기 - InitializeWeightArray()

        /// <summary>
        /// 가중치 배열 초기화 하기
        /// </summary>
        private void InitializeWeightArray()
        {
            int totalWeightCount = (this.inputNodeCount  * this.hiddenNodeCount) +
                                   (this.hiddenNodeCount * this.outputNodeCount) +
                                    this.hiddenNodeCount + this.outputNodeCount;
 
            double[] initialWeightArray = new double[totalWeightCount];
            double   low                = -0.01d;
            double   high               =  0.01d;
            int      initialWeightCount = initialWeightArray.Length;

            for(int i = 0; i < initialWeightCount; i++)
            {
                initialWeightArray[i] = (high - low) * _random.NextDouble() + low;
            }
 
            SetWeightArray(initialWeightArray);
        }

        #endregion
        #region 하이퍼 탄젠트 값 구하기 - GetHyperTangentValue(x)

        /// <summary>
        /// 하이퍼 탄젠트 값 구하기
        /// </summary>
        /// <param name="x">X</param>
        /// <returns>하이퍼 탄젠트 값</returns>
        private static double GetHyperTangentValue(double x)
        {
            if(x < -20d)
            {
                return -1d;
            }
            else if(x > 20d)
            {
                return 1d;
            }
            else
            {
                return Math.Tanh(x);
            }
        }

        #endregion
        #region SOFT-MAX 배열 구하기 - GetSoftMaximumArray(sourceOutputNodeSummaryArray)

        /// <summary>
        /// SOFT-MAX 배열 구하기
        /// </summary>
        /// <param name="sourceOutputNodeSummaryArray">소스 출력 노드 합산 배열</param>
        /// <returns>SOFT-MAX 배열</returns>
        private static double[] GetSoftMaximumArray(double[] sourceOutputNodeSummaryArray)
        {
            int sourceOutputNodeSummaryCount = sourceOutputNodeSummaryArray.Length;

            double maximum = sourceOutputNodeSummaryArray[0];

            for(int i = 0; i < sourceOutputNodeSummaryCount; i++)
            {
                if(sourceOutputNodeSummaryArray[i] > maximum)
                {
                    maximum = sourceOutputNodeSummaryArray[i];
                }
            }
 
            double scale = 0d;
 
            for(int i = 0; i < sourceOutputNodeSummaryCount; ++i)
            {
                scale += Math.Exp(sourceOutputNodeSummaryArray[i] - maximum);
            }
 
            double[] targetArray = new double[sourceOutputNodeSummaryCount];
 
            for(int i = 0; i < sourceOutputNodeSummaryCount; ++i)
            {
                targetArray[i] = Math.Exp(sourceOutputNodeSummaryArray[i] - maximum) / scale;
            }
 
            return targetArray;
        }

        #endregion
        #region 가중치 배열 갱신하기 - UpdateWeightArray(targetValueArray, learningRate, momentum)

        /// <summary>
        /// 가중치 배열 갱신하기
        /// </summary>
        /// <param name="targetValueArray">타겟 값 배열</param>
        /// <param name="learningRate">학습률</param>
        /// <param name="momentum">모멘텀</param>
        private void UpdateWeightArray(double[] targetValueArray, double learningRate, double momentum)
        {
            if(targetValueArray.Length != this.outputNodeCount)
            {
                throw new Exception("타겟 값 배열 길이가 출력 노드 값 배열과 같지 않습니다.");
            }
 

            for(int i = 0; i < this.outputNodeCount; i++)
            {
                double derivative = (1 - this.outputNodeValueArray[i]) * this.outputNodeValueArray[i];
 
                this.outputNodeGradientArray[i] = derivative * (targetValueArray[i] - this.outputNodeValueArray[i]);
            }
 

            for(int i = 0; i < hiddenNodeCount; i++)
            {
                double derivative = (1 - this.hiddenNodeValueArray[i]) * (1 + this.hiddenNodeValueArray[i]);
                double summary    = 0d;
 
                for(int j = 0; j < this.outputNodeCount; j++)
                {
                    double x = this.outputNodeGradientArray[j] * this.hiddenOutputWeightArray[i][j];

                    summary += x;
                }

                this.hiddenNodeGradientArray[i] = derivative * summary;
            }
 

            for(int i = 0; i < this.inputNodeCount; i++)
            {
                for(int j = 0; j < this.hiddenNodeCount; j++)
                {
                    double delta = learningRate * this.hiddenNodeGradientArray[j] * this.inputNodeValueArray[i];

                    this.inputHiddenWeightArray[i][j] += delta;
 
                    this.inputHiddenWeightArray[i][j] += momentum * this.inputHiddenPreviousWeightDeltaArray[i][j];

                    this.inputHiddenPreviousWeightDeltaArray[i][j] = delta;
                }
            }
 

            for(int i = 0; i < this.hiddenNodeCount; i++)
            {
                double delta = learningRate * this.hiddenNodeGradientArray[i];

                this.hiddenNodeBiasArray[i] += delta;
                this.hiddenNodeBiasArray[i] += momentum * this.hiddenNodePreviousBiasDeltaArray[i];

                this.hiddenNodePreviousBiasDeltaArray[i] = delta;
            }


            for(int i = 0; i < this.hiddenNodeCount; i++)
            {
                for(int j = 0; j < this.outputNodeCount; j++)
                {
                    double delta = learningRate * this.outputNodeGradientArray[j] * this.hiddenNodeValueArray[i];

                    this.hiddenOutputWeightArray[i][j] += delta;
                    this.hiddenOutputWeightArray[i][j] += momentum * this.hiddenOutputPreviousWeightDeltaArray[i][j];

                    this.hiddenOutputPreviousWeightDeltaArray[i][j] = delta;
                }
            }
 

            for(int i = 0; i < this.outputNodeCount; i++)
            {
                double delta = learningRate * this.outputNodeGradientArray[i] * 1d;

                this.outputNodeBiasArray[i] += delta;
                this.outputNodeBiasArray[i] += momentum * this.outputNodePreviousBiasDeltaArray[i];

                this.outputNodePreviousBiasDeltaArray[i] = delta;
            } 
        }

        #endregion
        #region 섞기 - Shuffle(sequenceArray)

        /// <summary>
        /// 섞기
        /// </summary>
        /// <param name="sequenceArray">시퀀스 배열</param>
        private static void Shuffle(int[] sequenceArray)
        {
            for(int i = 0; i < sequenceArray.Length; i++)
            {
                int randomIndex = _random.Next(i, sequenceArray.Length);
                int sequence    = sequenceArray[randomIndex];
 
                sequenceArray[randomIndex] = sequenceArray[i];

                sequenceArray[i] = sequence;
            }
        }

        #endregion
        #region 평균 제곱 에러 구하기 - GetMeanSquaredError(trainingValueArray)

        /// <summary>
        /// 평균 제곱 에러 구하기
        /// </summary>
        /// <param name="trainingValueArray">훈련 값 배열</param>
        /// <returns>평균 제곱 에러</returns>
        private double GetMeanSquaredError(double[][] trainingValueArray)
        {
            double   summarySquaredError = 0d;
            double[] xValueArray         = new double[this.inputNodeCount ];
            double[] targetValueArray    = new double[this.outputNodeCount];
            int      trainingValueCount  = trainingValueArray.Length;

            for(int i = 0; i < trainingValueCount; i++)
            {
                Array.Copy(trainingValueArray[i], xValueArray, this.inputNodeCount);

                Array.Copy(trainingValueArray[i], this.inputNodeCount, targetValueArray, 0, this.outputNodeCount);

                double[] yValueArray = ComputeOutputNodeValueArray(xValueArray);

                for(int j = 0; j < this.outputNodeCount; j++)
                {
                    double error = targetValueArray[j] - yValueArray[j];

                    summarySquaredError += error * error;
                }
            }

            return summarySquaredError / trainingValueCount;
        }

        #endregion
        #region 최대 인덱스 구하기 - GetMaximumIndex(vectorArray)

        /// <summary>
        /// 최대 인덱스 구하기
        /// </summary>
        /// <param name="vectorArray">벡터 배열</param>
        /// <returns>최대 인덱스</returns>
        private static int GetMaximumIndex(double[] vectorArray)
        {
            int    maximumIndex = 0;
            double maximum      = vectorArray[0];
            int    vectorCount  = vectorArray.Length;
 
            for(int i = 0; i < vectorCount; i++)
            {
                if(vectorArray[i] > maximum)
                {
                    maximum      = vectorArray[i];
                    maximumIndex = i;
                }
            }
 
            return maximumIndex;
        }

        #endregion
    }
}

 

728x90

 

▶ Program.cs

using System;
 
namespace TestProject
{
    class Program
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Private

        #region 프로그램 시작하기 - Main()

        /// <summary>
        /// 프로그램 시작하기
        /// </summary>
        private static void Main()
        {
            Console.Title = "신경망 역전파 알고리즘 사용하기";

            Console.WriteLine();
            Console.WriteLine("신경망 훈련 데모를 시작한다.");
            Console.WriteLine();
            Console.WriteLine("데이터는 유명한 아이리스 꽃 세트이다.");
            Console.WriteLine("꽃받침 길이/너비, 꽃잎 길이/너비에서 종을 예측한다.");
            Console.WriteLine("Iris setosa = 0 0 1, versicolor = 0 1 0, virginica = 1 0 0");
            Console.WriteLine();
            Console.WriteLine("실제 데이터와 유사하다 :");
            Console.WriteLine();
            Console.WriteLine(" 5.1, 3.5, 1.4, 0.2, Iris setosa"    );
            Console.WriteLine(" 7.0, 3.2, 4.7, 1.4, Iris versicolor");
            Console.WriteLine(" 6.3, 3.3, 6.0, 2.5, Iris versinica" );
            Console.WriteLine(" ......");
            Console.WriteLine();
           
            double[][] sourceValueArray = new double[150][];

            #region 소스 값 배열 데이터를 설정한다.

            sourceValueArray[0] = new double[] { 5.1, 3.5, 1.4, 0.2, 0, 0, 1 };
            sourceValueArray[1] = new double[] { 4.9, 3.0, 1.4, 0.2, 0, 0, 1 }; // Iris setosa = 0 0 1
            sourceValueArray[2] = new double[] { 4.7, 3.2, 1.3, 0.2, 0, 0, 1 }; // Iris versicolor = 0 1 0
            sourceValueArray[3] = new double[] { 4.6, 3.1, 1.5, 0.2, 0, 0, 1 }; // Iris verginica = 1 0 0 
            sourceValueArray[4] = new double[] { 5.0, 3.6, 1.4, 0.2, 0, 0, 1 };
            sourceValueArray[5] = new double[] { 5.4, 3.9, 1.7, 0.4, 0, 0, 1 };
            sourceValueArray[6] = new double[] { 4.6, 3.4, 1.4, 0.3, 0, 0, 1 };
            sourceValueArray[7] = new double[] { 5.0, 3.4, 1.5, 0.2, 0, 0, 1 };
            sourceValueArray[8] = new double[] { 4.4, 2.9, 1.4, 0.2, 0, 0, 1 };
            sourceValueArray[9] = new double[] { 4.9, 3.1, 1.5, 0.1, 0, 0, 1 };
 
            sourceValueArray[10] = new double[] { 5.4, 3.7, 1.5, 0.2, 0, 0, 1 };
            sourceValueArray[11] = new double[] { 4.8, 3.4, 1.6, 0.2, 0, 0, 1 };
            sourceValueArray[12] = new double[] { 4.8, 3.0, 1.4, 0.1, 0, 0, 1 };
            sourceValueArray[13] = new double[] { 4.3, 3.0, 1.1, 0.1, 0, 0, 1 };
            sourceValueArray[14] = new double[] { 5.8, 4.0, 1.2, 0.2, 0, 0, 1 };
            sourceValueArray[15] = new double[] { 5.7, 4.4, 1.5, 0.4, 0, 0, 1 };
            sourceValueArray[16] = new double[] { 5.4, 3.9, 1.3, 0.4, 0, 0, 1 };
            sourceValueArray[17] = new double[] { 5.1, 3.5, 1.4, 0.3, 0, 0, 1 };
            sourceValueArray[18] = new double[] { 5.7, 3.8, 1.7, 0.3, 0, 0, 1 };
            sourceValueArray[19] = new double[] { 5.1, 3.8, 1.5, 0.3, 0, 0, 1 };
 
            sourceValueArray[20] = new double[] { 5.4, 3.4, 1.7, 0.2, 0, 0, 1 };
            sourceValueArray[21] = new double[] { 5.1, 3.7, 1.5, 0.4, 0, 0, 1 };
            sourceValueArray[22] = new double[] { 4.6, 3.6, 1.0, 0.2, 0, 0, 1 };
            sourceValueArray[23] = new double[] { 5.1, 3.3, 1.7, 0.5, 0, 0, 1 };
            sourceValueArray[24] = new double[] { 4.8, 3.4, 1.9, 0.2, 0, 0, 1 };
            sourceValueArray[25] = new double[] { 5.0, 3.0, 1.6, 0.2, 0, 0, 1 };
            sourceValueArray[26] = new double[] { 5.0, 3.4, 1.6, 0.4, 0, 0, 1 };
            sourceValueArray[27] = new double[] { 5.2, 3.5, 1.5, 0.2, 0, 0, 1 };
            sourceValueArray[28] = new double[] { 5.2, 3.4, 1.4, 0.2, 0, 0, 1 };
            sourceValueArray[29] = new double[] { 4.7, 3.2, 1.6, 0.2, 0, 0, 1 };
            sourceValueArray[30] = new double[] { 4.8, 3.1, 1.6, 0.2, 0, 0, 1 };
 
            sourceValueArray[31] = new double[] { 5.4, 3.4, 1.5, 0.4, 0, 0, 1 };
            sourceValueArray[32] = new double[] { 5.2, 4.1, 1.5, 0.1, 0, 0, 1 };
            sourceValueArray[33] = new double[] { 5.5, 4.2, 1.4, 0.2, 0, 0, 1 };
            sourceValueArray[34] = new double[] { 4.9, 3.1, 1.5, 0.1, 0, 0, 1 };
            sourceValueArray[35] = new double[] { 5.0, 3.2, 1.2, 0.2, 0, 0, 1 };
            sourceValueArray[36] = new double[] { 5.5, 3.5, 1.3, 0.2, 0, 0, 1 };
            sourceValueArray[37] = new double[] { 4.9, 3.1, 1.5, 0.1, 0, 0, 1 };
            sourceValueArray[38] = new double[] { 4.4, 3.0, 1.3, 0.2, 0, 0, 1 };
            sourceValueArray[39] = new double[] { 5.1, 3.4, 1.5, 0.2, 0, 0, 1 };
 
            sourceValueArray[40] = new double[] { 5.0, 3.5, 1.3, 0.3, 0, 0, 1 };
            sourceValueArray[41] = new double[] { 4.5, 2.3, 1.3, 0.3, 0, 0, 1 };
            sourceValueArray[42] = new double[] { 4.4, 3.2, 1.3, 0.2, 0, 0, 1 };
            sourceValueArray[43] = new double[] { 5.0, 3.5, 1.6, 0.6, 0, 0, 1 };
            sourceValueArray[44] = new double[] { 5.1, 3.8, 1.9, 0.4, 0, 0, 1 };
            sourceValueArray[45] = new double[] { 4.8, 3.0, 1.4, 0.3, 0, 0, 1 };
            sourceValueArray[46] = new double[] { 5.1, 3.8, 1.6, 0.2, 0, 0, 1 };
            sourceValueArray[47] = new double[] { 4.6, 3.2, 1.4, 0.2, 0, 0, 1 };
            sourceValueArray[48] = new double[] { 5.3, 3.7, 1.5, 0.2, 0, 0, 1 };
            sourceValueArray[49] = new double[] { 5.0, 3.3, 1.4, 0.2, 0, 0, 1 };
 
            sourceValueArray[50] = new double[] { 7.0, 3.2, 4.7, 1.4, 0, 1, 0 };
            sourceValueArray[51] = new double[] { 6.4, 3.2, 4.5, 1.5, 0, 1, 0 };
            sourceValueArray[52] = new double[] { 6.9, 3.1, 4.9, 1.5, 0, 1, 0 };
            sourceValueArray[53] = new double[] { 5.5, 2.3, 4.0, 1.3, 0, 1, 0 };
            sourceValueArray[54] = new double[] { 6.5, 2.8, 4.6, 1.5, 0, 1, 0 };
            sourceValueArray[55] = new double[] { 5.7, 2.8, 4.5, 1.3, 0, 1, 0 };
            sourceValueArray[56] = new double[] { 6.3, 3.3, 4.7, 1.6, 0, 1, 0 };
            sourceValueArray[57] = new double[] { 4.9, 2.4, 3.3, 1.0, 0, 1, 0 };
            sourceValueArray[58] = new double[] { 6.6, 2.9, 4.6, 1.3, 0, 1, 0 };
            sourceValueArray[59] = new double[] { 5.2, 2.7, 3.9, 1.4, 0, 1, 0 };
 
            sourceValueArray[60] = new double[] { 5.0, 2.0, 3.5, 1.0, 0, 1, 0 };
            sourceValueArray[61] = new double[] { 5.9, 3.0, 4.2, 1.5, 0, 1, 0 };
            sourceValueArray[62] = new double[] { 6.0, 2.2, 4.0, 1.0, 0, 1, 0 };
            sourceValueArray[63] = new double[] { 6.1, 2.9, 4.7, 1.4, 0, 1, 0 };
            sourceValueArray[64] = new double[] { 5.6, 2.9, 3.6, 1.3, 0, 1, 0 };
            sourceValueArray[65] = new double[] { 6.7, 3.1, 4.4, 1.4, 0, 1, 0 };
            sourceValueArray[66] = new double[] { 5.6, 3.0, 4.5, 1.5, 0, 1, 0 };
            sourceValueArray[67] = new double[] { 5.8, 2.7, 4.1, 1.0, 0, 1, 0 };
            sourceValueArray[68] = new double[] { 6.2, 2.2, 4.5, 1.5, 0, 1, 0 };
            sourceValueArray[69] = new double[] { 5.6, 2.5, 3.9, 1.1, 0, 1, 0 };
 
            sourceValueArray[70] = new double[] { 5.9, 3.2, 4.8, 1.8, 0, 1, 0 };
            sourceValueArray[71] = new double[] { 6.1, 2.8, 4.0, 1.3, 0, 1, 0 };
            sourceValueArray[72] = new double[] { 6.3, 2.5, 4.9, 1.5, 0, 1, 0 };
            sourceValueArray[73] = new double[] { 6.1, 2.8, 4.7, 1.2, 0, 1, 0 };
            sourceValueArray[74] = new double[] { 6.4, 2.9, 4.3, 1.3, 0, 1, 0 };
            sourceValueArray[75] = new double[] { 6.6, 3.0, 4.4, 1.4, 0, 1, 0 };
            sourceValueArray[76] = new double[] { 6.8, 2.8, 4.8, 1.4, 0, 1, 0 };
            sourceValueArray[77] = new double[] { 6.7, 3.0, 5.0, 1.7, 0, 1, 0 };
            sourceValueArray[78] = new double[] { 6.0, 2.9, 4.5, 1.5, 0, 1, 0 };
            sourceValueArray[79] = new double[] { 5.7, 2.6, 3.5, 1.0, 0, 1, 0 };
 
            sourceValueArray[80] = new double[] { 5.5, 2.4, 3.8, 1.1, 0, 1, 0 };
            sourceValueArray[81] = new double[] { 5.5, 2.4, 3.7, 1.0, 0, 1, 0 };
            sourceValueArray[82] = new double[] { 5.8, 2.7, 3.9, 1.2, 0, 1, 0 };
            sourceValueArray[83] = new double[] { 6.0, 2.7, 5.1, 1.6, 0, 1, 0 };
            sourceValueArray[84] = new double[] { 5.4, 3.0, 4.5, 1.5, 0, 1, 0 };
            sourceValueArray[85] = new double[] { 6.0, 3.4, 4.5, 1.6, 0, 1, 0 };
            sourceValueArray[86] = new double[] { 6.7, 3.1, 4.7, 1.5, 0, 1, 0 };
            sourceValueArray[87] = new double[] { 6.3, 2.3, 4.4, 1.3, 0, 1, 0 };
            sourceValueArray[88] = new double[] { 5.6, 3.0, 4.1, 1.3, 0, 1, 0 };
            sourceValueArray[89] = new double[] { 5.5, 2.5, 4.0, 1.3, 0, 1, 0 };
 
            sourceValueArray[90] = new double[] { 5.5, 2.6, 4.4, 1.2, 0, 1, 0 };
            sourceValueArray[91] = new double[] { 6.1, 3.0, 4.6, 1.4, 0, 1, 0 };
            sourceValueArray[92] = new double[] { 5.8, 2.6, 4.0, 1.2, 0, 1, 0 };
            sourceValueArray[93] = new double[] { 5.0, 2.3, 3.3, 1.0, 0, 1, 0 };
            sourceValueArray[94] = new double[] { 5.6, 2.7, 4.2, 1.3, 0, 1, 0 };
            sourceValueArray[95] = new double[] { 5.7, 3.0, 4.2, 1.2, 0, 1, 0 };
            sourceValueArray[96] = new double[] { 5.7, 2.9, 4.2, 1.3, 0, 1, 0 };
            sourceValueArray[97] = new double[] { 6.2, 2.9, 4.3, 1.3, 0, 1, 0 };
            sourceValueArray[98] = new double[] { 5.1, 2.5, 3.0, 1.1, 0, 1, 0 };
            sourceValueArray[99] = new double[] { 5.7, 2.8, 4.1, 1.3, 0, 1, 0 };
 
            sourceValueArray[100] = new double[] { 6.3, 3.3, 6.0, 2.5, 1, 0, 0 };
            sourceValueArray[101] = new double[] { 5.8, 2.7, 5.1, 1.9, 1, 0, 0 };
            sourceValueArray[102] = new double[] { 7.1, 3.0, 5.9, 2.1, 1, 0, 0 };
            sourceValueArray[103] = new double[] { 6.3, 2.9, 5.6, 1.8, 1, 0, 0 };
            sourceValueArray[104] = new double[] { 6.5, 3.0, 5.8, 2.2, 1, 0, 0 };
            sourceValueArray[105] = new double[] { 7.6, 3.0, 6.6, 2.1, 1, 0, 0 };
            sourceValueArray[106] = new double[] { 4.9, 2.5, 4.5, 1.7, 1, 0, 0 };
            sourceValueArray[107] = new double[] { 7.3, 2.9, 6.3, 1.8, 1, 0, 0 };
            sourceValueArray[108] = new double[] { 6.7, 2.5, 5.8, 1.8, 1, 0, 0 };
            sourceValueArray[109] = new double[] { 7.2, 3.6, 6.1, 2.5, 1, 0, 0 };
 
            sourceValueArray[110] = new double[] { 6.5, 3.2, 5.1, 2.0, 1, 0, 0 };
            sourceValueArray[111] = new double[] { 6.4, 2.7, 5.3, 1.9, 1, 0, 0 };
            sourceValueArray[112] = new double[] { 6.8, 3.0, 5.5, 2.1, 1, 0, 0 };
            sourceValueArray[113] = new double[] { 5.7, 2.5, 5.0, 2.0, 1, 0, 0 };
            sourceValueArray[114] = new double[] { 5.8, 2.8, 5.1, 2.4, 1, 0, 0 };
            sourceValueArray[115] = new double[] { 6.4, 3.2, 5.3, 2.3, 1, 0, 0 };
            sourceValueArray[116] = new double[] { 6.5, 3.0, 5.5, 1.8, 1, 0, 0 };
            sourceValueArray[117] = new double[] { 7.7, 3.8, 6.7, 2.2, 1, 0, 0 };
            sourceValueArray[118] = new double[] { 7.7, 2.6, 6.9, 2.3, 1, 0, 0 };
            sourceValueArray[119] = new double[] { 6.0, 2.2, 5.0, 1.5, 1, 0, 0 };
 
            sourceValueArray[120] = new double[] { 6.9, 3.2, 5.7, 2.3, 1, 0, 0 };
            sourceValueArray[121] = new double[] { 5.6, 2.8, 4.9, 2.0, 1, 0, 0 };
            sourceValueArray[122] = new double[] { 7.7, 2.8, 6.7, 2.0, 1, 0, 0 };
            sourceValueArray[123] = new double[] { 6.3, 2.7, 4.9, 1.8, 1, 0, 0 };
            sourceValueArray[124] = new double[] { 6.7, 3.3, 5.7, 2.1, 1, 0, 0 };
            sourceValueArray[125] = new double[] { 7.2, 3.2, 6.0, 1.8, 1, 0, 0 };
            sourceValueArray[126] = new double[] { 6.2, 2.8, 4.8, 1.8, 1, 0, 0 };
            sourceValueArray[127] = new double[] { 6.1, 3.0, 4.9, 1.8, 1, 0, 0 };
            sourceValueArray[128] = new double[] { 6.4, 2.8, 5.6, 2.1, 1, 0, 0 };
            sourceValueArray[129] = new double[] { 7.2, 3.0, 5.8, 1.6, 1, 0, 0 };
 
            sourceValueArray[130] = new double[] { 7.4, 2.8, 6.1, 1.9, 1, 0, 0 };
            sourceValueArray[131] = new double[] { 7.9, 3.8, 6.4, 2.0, 1, 0, 0 };
            sourceValueArray[132] = new double[] { 6.4, 2.8, 5.6, 2.2, 1, 0, 0 };
            sourceValueArray[133] = new double[] { 6.3, 2.8, 5.1, 1.5, 1, 0, 0 };
            sourceValueArray[134] = new double[] { 6.1, 2.6, 5.6, 1.4, 1, 0, 0 };
            sourceValueArray[135] = new double[] { 7.7, 3.0, 6.1, 2.3, 1, 0, 0 };
            sourceValueArray[136] = new double[] { 6.3, 3.4, 5.6, 2.4, 1, 0, 0 };
            sourceValueArray[137] = new double[] { 6.4, 3.1, 5.5, 1.8, 1, 0, 0 };
            sourceValueArray[138] = new double[] { 6.0, 3.0, 4.8, 1.8, 1, 0, 0 };
            sourceValueArray[139] = new double[] { 6.9, 3.1, 5.4, 2.1, 1, 0, 0 };
 
            sourceValueArray[140] = new double[] { 6.7, 3.1, 5.6, 2.4, 1, 0, 0 };
            sourceValueArray[141] = new double[] { 6.9, 3.1, 5.1, 2.3, 1, 0, 0 };
            sourceValueArray[142] = new double[] { 5.8, 2.7, 5.1, 1.9, 1, 0, 0 };
            sourceValueArray[143] = new double[] { 6.8, 3.2, 5.9, 2.3, 1, 0, 0 };
            sourceValueArray[144] = new double[] { 6.7, 3.3, 5.7, 2.5, 1, 0, 0 };
            sourceValueArray[145] = new double[] { 6.7, 3.0, 5.2, 2.3, 1, 0, 0 };
            sourceValueArray[146] = new double[] { 6.3, 2.5, 5.0, 1.9, 1, 0, 0 };
            sourceValueArray[147] = new double[] { 6.5, 3.0, 5.2, 2.0, 1, 0, 0 };
            sourceValueArray[148] = new double[] { 6.2, 3.4, 5.4, 2.3, 1, 0, 0 };
            sourceValueArray[149] = new double[] { 5.9, 3.0, 5.1, 1.8, 1, 0, 0 };

            #endregion

            Console.WriteLine("150개 항목 데이터 세트의 첫번째 6개 행들 :");
            Console.WriteLine();

            DisplayMatrixArray(sourceValueArray, 6, 1, true);
 
            Console.WriteLine("80% 훈련 데이터 매트릭스와 20% 테스트 데이터 매트릭스 생성");

            double[][] traingingValueArray = null;
            double[][] testValueArray      = null;

            PrepareData(sourceValueArray, 72, out traingingValueArray, out testValueArray);

            Console.WriteLine();
            Console.WriteLine("훈련 데이터의 첫번째 3개 행들 :");
            Console.WriteLine();

            DisplayMatrixArray(traingingValueArray, 3, 1, true);
 
            Console.WriteLine("테스트 데이터의 첫번째 3개 행들 :");
            Console.WriteLine();

            DisplayMatrixArray(testValueArray, 3, 1, true);
 
            Console.WriteLine("4개 입력 노드, 7개 은닉 노드, 3개 출력 노드를 갖는 신경망 생성");
            Console.WriteLine("입력-은닉층을 위한 하드 코딩된 하이퍼 탄젠트 함수와 은닉-출력층 활성화를 위한 소프트 맥스 함수");

            int inputNodeCount  = 4;
            int hiddenModeCount = 7;
            int outputNodeCount = 3;

            NeuralNetwork neuralNetwork = new NeuralNetwork(inputNodeCount, hiddenModeCount, outputNodeCount);

            int    maximumEpochCount = 1000;
            double learningRate      = 0.05d;
            double momentum          = 0.01d;
 
            Console.WriteLine("최대 시대 카운트 = " + maximumEpochCount + ", 학습률 = " + learningRate + ", 모멘텀 = " + momentum);

            Console.WriteLine("훈련은 하드 코딩된 평균 제곱 오류가 0.040보다 작은 경우 중단하는 조건을 갖는다.");

            Console.WriteLine();
            Console.WriteLine("증분 역전파를 사용해 훈련 시작하기");
            Console.WriteLine();

            neuralNetwork.Train(traingingValueArray, maximumEpochCount, learningRate, momentum);

            Console.WriteLine("훈련 완료");
            Console.WriteLine();
 
            double[] weightArray = neuralNetwork.GetWeightArray();

            Console.WriteLine("최종 신경망 가중치와 바이어스 값들 : ");

            DisplayVectorArray(weightArray, 10, 3, true);
 
            double trainingAccuracy = neuralNetwork.GetAccuracy(traingingValueArray);
            double testAccuracy     = neuralNetwork.GetAccuracy(testValueArray     );

            Console.WriteLine();
            Console.WriteLine("훈련 데이터 정확도   = " + trainingAccuracy.ToString("F4"));
            Console.WriteLine("테스트 데이터 정확도 = " + testAccuracy.ToString("F4"));
            Console.WriteLine();

            Console.WriteLine("입력 데이터 : ");

            double[] inputArray = new double[] { 5.1, 3.5, 1.4, 0.2 };

            DisplayVectorArray(inputArray, 4, 3, true);

            double[] resultArray = neuralNetwork.ComputeOutputNodeValueArray(inputArray);

            Console.WriteLine();
            Console.WriteLine("출력 데이터 : ");

            DisplayVectorArray(resultArray, 3, 3, true);

            Console.WriteLine();
            Console.WriteLine("신경망 훈련 데모 종료");

            Console.ReadLine();
        }

        #endregion

        #region 데이터 준비하기 - PrepareData(sourceValueArray, seed, trainingValueArray, testValueArray)

        /// <summary>
        /// 데이터 준비하기
        /// </summary>
        /// <param name="sourceValueArray">소스 값 배열</param>
        /// <param name="seed">시드</param>
        /// <param name="trainingValueArray">훈련 값 배열</param>
        /// <param name="testValueArray">테스트 값 배열</param>
        private static void PrepareData
        (
            double[][]     sourceValueArray,
            int            seed,
            out double[][] trainingValueArray,
            out double[][] testValueArray
        )
        {
            Random random = new Random(seed);

            int rowCount         = sourceValueArray.Length;
            int columnCount      = sourceValueArray[0].Length;
            int trainingRowCount = (int)(rowCount * 0.80);
            int testRowCount     = rowCount - trainingRowCount;
 
            trainingValueArray = new double[trainingRowCount][];
            testValueArray     = new double[testRowCount][];
 
            double[][] copyValueArray = new double[sourceValueArray.Length][];

            for(int i = 0; i < copyValueArray.Length; i++)
            {
                copyValueArray[i] = sourceValueArray[i];
            }
 
            for(int i = 0; i < copyValueArray.Length; i++)
            {
                int randomIndex = random.Next(i, copyValueArray.Length);

                double[] temporaryArray = copyValueArray[randomIndex];

                copyValueArray[randomIndex] = copyValueArray[i];
                copyValueArray[i          ] = temporaryArray;
            }
 
            for(int i = 0; i < trainingRowCount; i++)
            {
                trainingValueArray[i] = new double[columnCount];

                for(int j = 0; j < columnCount; j++)
                {
                    trainingValueArray[i][j] = copyValueArray[i][j];
                }
            }
 
            for(int i = 0; i < testRowCount; i++)
            {
                testValueArray[i] = new double[columnCount];

                for(int j = 0; j < columnCount; j++)
                {
                    testValueArray[i][j] = copyValueArray[i + trainingRowCount][j];
                }
            }
        }

        #endregion
        #region 벡터 배열 출력하기 - DisplayVectorArray(sourceVectorArray, valueCountPerRow, decimalCount, newLine)

        /// <summary>
        /// 벡터 배열 출력하기
        /// </summary>
        /// <param name="sourceVectorArray">소스 벡터 배열</param>
        /// <param name="valueCountPerRow">행당 값 카운트</param>
        /// <param name="decimalCount">소수점 카운트</param>
        /// <param name="newLine">개행 여부</param>
        private static void DisplayVectorArray(double[] sourceVectorArray, int valueCountPerRow, int decimalCount, bool newLine)
        {
            int sourceVectorCount = sourceVectorArray.Length;

            for(int i = 0; i < sourceVectorCount; i++)
            {
                if(i % valueCountPerRow == 0)
                {
                    Console.WriteLine("");
                }

                Console.Write(sourceVectorArray[i].ToString("F" + decimalCount).PadLeft(decimalCount + 4) + " ");
            }
 
            if (newLine == true)
            {
                Console.WriteLine("");
            }
        }

        #endregion
        #region 매트릭스 배열 출력하기 - DisplayMatrixArray(sourceMatrixArray, rowCount, decimalCount, newLine)

        /// <summary>
        /// 매트릭스 배열 출력하기
        /// </summary>
        /// <param name="sourceMatrixArray">소스 매트릭스 배열</param>
        /// <param name="rowCount">행 카운트</param>
        /// <param name="decimalCount">소수점 카운트</param>
        /// <param name="newLine">개행 여부</param>
        private static void DisplayMatrixArray(double[][] sourceMatrixArray, int rowCount, int decimalCount, bool newLine)
        {
            for(int i = 0; i < rowCount; i++)
            {
                Console.Write(i.ToString().PadLeft(3) + " : ");
                
                for(int j = 0; j < sourceMatrixArray[i].Length; j++)
                {
                    if(sourceMatrixArray[i][j] >= 0d)
                    {
                        Console.Write(" ");
                    }
                    else
                    {
                        Console.Write("-");
                    }

                    Console.Write(Math.Abs(sourceMatrixArray[i][j]).ToString("F" + decimalCount) + " ");
                }

                Console.WriteLine("");
            }

            if(newLine == true)
            {
                Console.WriteLine("");
            }
        }

        #endregion 
    }
}
728x90
반응형
그리드형(광고전용)
Posted by icodebroker

댓글을 달아 주세요