신경망 셈플 프로그램입니다.
#include <iostream>
#include <string>
#include <cmath>
#include <ctime>
#include <cstdlib>
#include <fstream>
using namespace std;
// 현재 네트워크의 학습률은 2의 제곱근
float learningRate = 1.414213562;
// 운동량
float momentum = 0.25;
// 바이어스(경향, 성향)
int bias = 1;
// 신경망을 통해 전달되는 학습 데이터
double arrTrainingData[4][2] = {
{ 1, 0 }, // target : 1
{ 1, 1 }, // target : 0
{ 0, 1 }, // target : 1
{ 0, 0 } // target : 0
};
// 신경망 학습 데이터에 대한 값
int arrTargetData[4] = {
1,
0,
1,
0
};
// 신경망 가중치 값들
float arrWeight[9] = {};
// 신규 업데이트 가중치 값
float arrUpdateWeight[9];
// 이전 업데이트 가중치 값
float arrUpdatePrevWeight[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
// Hidden Layer
float sumHiddenLayer1;
float sumHiddenLayer2;
// 편미분(도함수) 값
float derivativeOutput;
float derivativeHiddenLayer1;
float derivativeHiddenLayer2;
float sumOutput;
// 기울기(미분 값)
float gradients[9];
// 최종 결과 값
float outputNeuron;
// 현재 학습중인 세대
int epoch = 0;
// 에러
float arrError[4];
float arrRMSE[20000];
////////Prototyping////////
float sigmoid(float x);
void calcHiddenLayers(double input1, double input2);
void calcOutputNeuron();
void calcError(int x);
void calcDerivatives();
void backwardPropagation(double input1, double input2, float error);
void calcUpdates();
void update_new_arrWeight();
float calcRMSE();
void generateWeight();
void trainingNeuralNetwork();
void testInput();
void saveData();
////////Prototyping////////
int main(int argc, const char * argv[]) {
/*
input neurons : 2
hidden layers : 2
output neuron : 1
The learning algorithm i use is the backpropagation algorithm.
The network has 2 input neurons, 2 hidden layers, and 1 output neuron.
also the hidden layer and the output layer is supported by a additional bias neuron(a neuron with a constant value of 1)
*/
// 가중치 값 생성
generateWeight();
// 신경망 학습
trainingNeuralNetwork();
// 저장파일 생성
saveData();
// 입력 시작
testInput();
system("pause");
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////
// 가중치 값 랜덤 생성
void generateWeight()
{
srand((unsigned)time(NULL));
for (int i = 0; i < 9; i++)
{
if (1 == rand() % 2) {
// generate number between -1.0 and 0.0
arrWeight[i] = -(double(rand()) / (double(RAND_MAX) + 1.0));
} else {
// generate number between 1.0 and 0.0
arrWeight[i] = double(rand()) / (double(RAND_MAX) + 1.0);
}
cout << "weight " << i << " = " << arrWeight[i] << endl;
}
cout << "" << endl;
}
// 신경망 학습
void trainingNeuralNetwork()
{
// 20000 세대만큼 반복
while (epoch < 20000)
{
for (int i = 0; i < 4; i++)
{
double input1 = arrTrainingData[i][0];
double input2 = arrTrainingData[i][1];
calcHiddenLayers(input1, input2); // input
calcOutputNeuron(); // output
calcError(i); // input!!
calcDerivatives(); // input!!
float error = arrError[i];
backwardPropagation(input1, input2, error); // input!!
calcUpdates();
update_new_arrWeight();
}
// 제곱근 평균 제곱 오차(RMSE;오차(잔차)의 제곱에 대해 평균을 취하고 이를 제곱근한 것)
// 작을수록 추정의 정확성이 높다.
// 추정값들이 중심으로부터 얼마나 멀리 떨어져 있는지를 나타낼때 많이 쓰인다.
float rmse = calcRMSE();
arrRMSE[epoch] = rmse;
cout << "epoch: " << epoch << endl;
// 세대수 증가
epoch = epoch + 1;
// Adding some motivation so if the neural network is not converging after 4000 epochs it will start over again until it converges
// 신경망이 4000세대 이후까지 RMSE 에러율이 0.5이하가 안된다면 처음부터 다시 시도한다.(가중치 재설정)
if (epoch > 4000 && rmse > 0.5)
{
for (int i = 0; i < 9; i++)
{
arrUpdatePrevWeight[i] = 0;
arrUpdateWeight[i] = 0;
gradients[i] = 0;
}
for (int i = 0; i < 4; i++) {
arrError[i] = 0;
}
for (int i = 0; i < epoch; i++) {
arrRMSE[i] = 0;
}
epoch = 0;
generateWeight();
}
}
}
// 저장 파일 생성
void saveData()
{
// 각 세대별 RMSE값 저장
ofstream dataER;
dataER.open("errorData1.txt");
for (int i = 0; i < epoch; i++)
{
dataER << i << " " << arrRMSE[i] << endl;
}
dataER.close();
// 최종 가중치 값 저장
ofstream dataER1;
dataER1.open("weight_data1.txt");
for (int i = 0; i < 9; i++)
{
dataER1 << i << " " << arrWeight[i] << endl;
}
dataER1.close();
}
// 학습된 신경망 테스트
void testInput()
{
char choise = 'Y';
do
{
if (choise == 'Y' || choise == 'y')
{
float input1;
float input2;
cout << "user input 1: "; cin >> input1; cout << endl;
cout << "user input 2: "; cin >> input2; cout << endl;
calcHiddenLayers(input1, input2); // input
calcOutputNeuron(); // output
// 반올림 처리로 최종 값을 결정 함
cout << "output = " << outputNeuron << " => " << floor(outputNeuron+0.5) << endl;
cout << "Again(y/n)? "; cin >> choise;
}
else
{
break;
}
} while ((choise == 'Y' || 'y') && (choise != 'n' || 'N'));
}
////////////////////////////////////////////////////////////////////////////////////////////////
/*
bias(1) bias(1)
| \ |
| w4 w8
| \ |
input1 --w0 --- h1 |
\ | / \ |
\ \/ w6 |
w1 /\ \ |
\/ \ outputNeuron
/\ \ /
w2 \ w5 w7
/ \ | /
/ \ |/
input2 --w3----- h2
*/
// Hidden Layer 계산
void calcHiddenLayers(double input1, double input2)
{
sumHiddenLayer1 = (input1 * arrWeight[0]) + (input2 * arrWeight[2]) + (bias * arrWeight[4]);
sumHiddenLayer2 = (input1 * arrWeight[1]) + (input2 * arrWeight[3]) + (bias * arrWeight[5]);
}
// Output Layer 계산
void calcOutputNeuron()
{
sumOutput = (sigmoid(sumHiddenLayer1) * arrWeight[6]) + (sigmoid(sumHiddenLayer2) * arrWeight[7]) + (bias * arrWeight[8]);
outputNeuron = sigmoid(sumOutput);
}
// sigmoid activation function
// 계단형식의 함수를 미분이 가능하도록 곡선화를 해주는 함수이다.
// 주어진 값(x)에 대하여 0 ~ 1사이의 값으로 변환하여 반환해 준다.
// 이 함수를 사용하는 이유는 미분이 용이하기 때문이다. 이 함수의 미분 공식은 sigmoid(x)(1-sigmoid(x)) 이다.
float sigmoid(float x)
{
float sigmoid = 1.0 / (1.0 + exp(-x));
return sigmoid;
}
// 타겟값과 얼마나 차이가 나는지 배열에 저장
void calcError(int x)
{
arrError[x] = outputNeuron - arrTargetData[x];
}
// sigmoid 미분
void calcDerivatives()
{
/*
derivativeOutput = (exp(sumOutput) / pow((1 + exp(sumOutput)), 2)) * -arrError[x];
derivativeHiddenLayer1 = (exp(sumHiddenLayer1) / pow((1 + exp(sumHiddenLayer1)), 2)) * arrWeight[6] * derivativeOutput;
derivativeHiddenLayer2 = (exp(sumHiddenLayer2) / pow((1 + exp(sumHiddenLayer2)), 2)) * arrWeight[7] * derivativeOutput;
*/
// 각 출력값에대한 미분값을 구한다.
derivativeOutput = sigmoid(sumOutput) * (1 - sigmoid(sumOutput));
derivativeHiddenLayer1 = sigmoid(sumHiddenLayer1) * (1 - sigmoid(sumHiddenLayer1));
derivativeHiddenLayer2 = sigmoid(sumHiddenLayer2) * (1 - sigmoid(sumHiddenLayer2));
}
// 기울기 계산
// Backward Propagation (역전파)
void backwardPropagation(double input1, double input2, float error)
{
/*
gradients[0] = sigmoid(input1) * derivativeHiddenLayer1;
gradients[1] = sigmoid(input1) * derivativeHiddenLayer2;
gradients[2] = sigmoid(input2) * derivativeHiddenLayer1;
gradients[3] = sigmoid(input2) * derivativeHiddenLayer2;
gradients[4] = sigmoid(bias) * derivativeHiddenLayer1;
gradients[5] = sigmoid(bias) * derivativeHiddenLayer2;
gradients[6] = sigmoid(sumHiddenLayer1) * derivativeOutput;
gradients[7] = sigmoid(sumHiddenLayer2) * derivativeOutput;
gradients[8] = sigmoid(bias) * derivativeOutput;
*/
// Backward Propagation(역전파)란 Error(오차)가 본래 진행방향과 반대방향으로 전파 된다고 하여 붙여진 이름이다.
// 역전파 알고리즘은 Supervised Learning(input과 output을 알고있는 상태)에서 신경망을 학습시키는 방법이다.
double bpOutput = derivativeOutput * -error;
double bpLayer2 = derivativeHiddenLayer2 * arrWeight[7] * bpOutput;
double bpLayer1 = derivativeHiddenLayer1 * arrWeight[6] * bpOutput;
// w8,w7,w6
gradients[8] = sigmoid(bias) * bpOutput;
gradients[7] = sigmoid(sumHiddenLayer2) * bpOutput;
gradients[6] = sigmoid(sumHiddenLayer1) * bpOutput;
// w5, w4
gradients[5] = sigmoid(bias) * bpLayer2;
gradients[4] = sigmoid(bias) * bpLayer1;
// w3, w2
gradients[3] = sigmoid(input2) * bpLayer2;
gradients[2] = sigmoid(input2) * bpLayer1;
// w1, w0
gradients[1] = sigmoid(input1) * bpLayer2;
gradients[0] = sigmoid(input1) * bpLayer1;
}
void calcUpdates()
{
for (int i = 0; i < 9; i++)
{
//arrUpdateWeight[i] = gradients[i];
arrUpdateWeight[i] = (learningRate * gradients[i]) + (momentum * arrUpdatePrevWeight[i]);
arrUpdatePrevWeight[i] = arrUpdateWeight[i];
}
}
void update_new_arrWeight()
{
for (int i = 0; i < 9; i++)
{
arrWeight[i] = arrWeight[i] + arrUpdateWeight[i];
}
}
float calcRMSE()
{
float rmse = sqrt((pow(arrError[0], 2) + pow(arrError[1], 2) + pow(arrError[2], 2) + pow(arrError[3], 2) / 4));
cout << "RMSE : " << rmse << endl;
cout << "" << endl;
return rmse;
}
2020/05/19 - [AI/Algorithm] - minimax full search example
2020/05/19 - [AI/Algorithm] - minimax, alpha-beta pruning
2020/05/19 - [iOS/Tips] - Bitbucket Carthage 사용
2020/05/19 - [iOS/Jailbreak] - Fridump 사용법 (3/3) - 메모리 덤프
2020/05/19 - [iOS/Jailbreak] - Fridump 사용법 (2/3) - Mac OS X 환경 구축
2020/05/19 - [iOS/Jailbreak] - Fridump 사용법 (1/3) - iOS디바이스 환경 구축
2020/05/19 - [iOS/Jailbreak] - Fridump, Tcpdump, OpenSSL Quick Guide
2020/05/19 - [OS/Mac OS X] - gdb 사용
2020/05/19 - [iOS/Jailbreak] - Frida 설치 및 사용법
2020/05/19 - [OS/Mac OS X] - gdb 설치
2020/05/19 - [OS/Mac OS X] - Mac에서 Node.js 설치
2020/05/19 - [iOS/Jailbreak] - Tcpdump 사용법
2020/05/19 - [개발노트] - UUID의 구성 요소
2020/05/18 - [iOS/Tips] - APNs
'개발 > AI,ML,ALGORITHM' 카테고리의 다른 글
Neural Network (XOR) (0) | 2022.11.18 |
---|---|
2D 충돌처리 (0) | 2020.12.12 |
Generic algorithm (0) | 2020.05.19 |
minimax full search example (0) | 2020.05.19 |
minimax, alpha-beta pruning (0) | 2020.05.19 |