matrix weights1, weights2, weights3; // 가중치의 행렬
matrix output1, output2, result; // 신경층 출력의 행렬
input int layer1 = 200; // 첫 번째 은닉층의 크기
input int layer2 = 200; // 두 번째 은닉층의 크기
input int Epochs = 20000; // 훈련 에포크의 수
input double lr = 3e-6; // 학습률
input ENUM_ACTIVATION_FUNCTION ac_func = AF_SWISH; // 활성화 함수
//+------------------------------------------------------------------+
//| Script start function |
//+------------------------------------------------------------------+
void OnStart()
{
//---
int train = 1000; // 샘플 사이즈 훈련
int test = 10; // 샘플 사이즈 테스트
matrix m_data, m_target;
//--- 훈련 샘플 생성
if(!CreateData(m_data, m_target, train))
return;
//--- 모델 훈련
if(!Train(m_data, m_target, Epochs))
return;
//--- 테스트 샘플 생성
if(!CreateData(m_data, m_target, test))
return;
//--- 모델 테스트
Test(m_data, m_target);
}
//+------------------------------------------------------------------+
//| Sample generation method |
//+------------------------------------------------------------------+
bool CreateData(matrix &data, matrix &target, const int count)
{
//--- 초기 데이터 및 결과 행렬 초기화
if(!data.Init(count, 3) || !target.Init(count, 1))
return false;
//--- 임의의 값으로 초기 데이터 행렬 채우기
data.Random(-10, 10);
//--- 학습 샘플의 목표 값을 계산
vector X1 = MathPow(data.Col(0) + data.Col(1) + data.Col(1), 2);
vector X2 = MathPow(data.Col(0), 2) + MathPow(data.Col(1), 2) + MathPow(data.Col(2), 2);
if(!target.Col(X1 / X2, 0))
return false;
//--- 결과 반환
return true;
}
//+------------------------------------------------------------------+
//| Model training method |
//+------------------------------------------------------------------+
bool Train(matrix &data, matrix &target, const int epochs = 10000)
{
//--- 모델 생성
if(!CreateNet())
return false;
//--- 모델 훈련
for(int ep = 0; ep < epochs; ep++)
{
//--- 피드포워드 패스
if(!FeedForward(data))
return false;
PrintFormat("Epoch %d, loss %.5f", ep, result.Loss(target, LOSS_MSE));
//--- 가중치 행렬의 역전파 및 업데이트
if(!Backprop(data, target))
return false;
}
//--- 결과 반환
return true;
}
//+------------------------------------------------------------------+
//| Model creation method |
//+------------------------------------------------------------------+
bool CreateNet()
{
//--- 가중치 행렬 초기화
if(!weights1.Init(4, layer1) || !weights2.Init(layer1 + 1, layer2) || !weights3.Init(layer2 + 1, 1))
return false;
//--- 가중치 행렬을 임의의 값으로 채웁니다.
weights1.Random(-0.1, 0.1);
weights2.Random(-0.1, 0.1);
weights3.Random(-0.1, 0.1);
//--- 결과 반환
return true;
}
//+------------------------------------------------------------------+
//| Feedforward method |
//+------------------------------------------------------------------+
bool FeedForward(matrix &data)
{
//--- 초기 데이터 크기 확인
if(data.Cols() != weights1.Rows() - 1)
return false;
//--- 첫 번째 신경 계층을 계산합니다.
matrix temp = data;
if(!temp.Resize(temp.Rows(), weights1.Rows()) ||
!temp.Col(vector::Ones(temp.Rows()), weights1.Rows() - 1))
return false;
output1 = temp.MatMul(weights1);
//--- 활성화 함수 계산
if(!output1.Activation(temp, ac_func))
return false;
//--- 두 번째 신경층을 계산합니다.
if(!temp.Resize(temp.Rows(), weights2.Rows()) ||
!temp.Col(vector::Ones(temp.Rows()), weights2.Rows() - 1))
return false;
output2 = temp.MatMul(weights2);
//--- 활성화 함수 계산
if(!output2.Activation(temp, ac_func))
return false;
//--- 세 번째 신경층을 계산합니다.
if(!temp.Resize(temp.Rows(), weights3.Rows()) ||
!temp.Col(vector::Ones(temp.Rows()), weights3.Rows() - 1))
return false;
result = temp.MatMul(weights3);
//--- 결과 반환
return true;
}
//+------------------------------------------------------------------+
//| Backpropagation method |
//+------------------------------------------------------------------+
bool Backprop(matrix &data, matrix &target)
{
//--- 대상 값의 행렬 크기 확인
if(target.Rows() != result.Rows() ||
target.Cols() != result.Cols())
return false;
//--- 목표에서 계산된 값의 편차를 결정합니다.
matrix loss = (target - result) * 2;
//--- 그래디언트를 이전 레이어로 전파
matrix gradient = loss.MatMul(weights3.Transpose());
//--- 마지막 레이어의 가중 행렬 업데이트
matrix temp;
if(!output2.Activation(temp, ac_func))
return false;
if(!temp.Resize(temp.Rows(), weights3.Rows()) ||
!temp.Col(vector::Ones(temp.Rows()), weights3.Rows() - 1))
return false;
weights3 = weights3 + temp.Transpose().MatMul(loss) * lr;
//--- 활성화 함수의 도함수로 오류 기울기를 조정합니다.
if(!output2.Derivative(temp, ac_func))
return false;
if(!gradient.Resize(gradient.Rows(), gradient.Cols() - 1))
return false;
loss = gradient * temp;
//--- 기울기를 하위 레이어로 전파
gradient = loss.MatMul(weights2.Transpose());
//--- 두 번째 은닉층의 가중치 행렬 업데이트
if(!output1.Activation(temp, ac_func))
return false;
if(!temp.Resize(temp.Rows(), weights2.Rows()) ||
!temp.Col(vector::Ones(temp.Rows()), weights2.Rows() - 1))
return false;
weights2 = weights2 + temp.Transpose().MatMul(loss) * lr;
//--- 활성화 함수의 도함수로 오류 기울기를 조정합니다.
if(!output1.Derivative(temp, ac_func))
return false;
if(!gradient.Resize(gradient.Rows(), gradient.Cols() - 1))
return false;
loss = gradient * temp;
//--- 첫 번째 은닉층의 가중치 행렬을 업데이트합니다.
temp = data;
if(!temp.Resize(temp.Rows(), weights1.Rows()) ||
!temp.Col(vector::Ones(temp.Rows()), weights1.Rows() - 1))
return false;
weights1 = weights1 + temp.Transpose().MatMul(loss) * lr;
//--- 결과 반환
return true;
}
//+------------------------------------------------------------------+
//| Model testing method |
//+------------------------------------------------------------------+
bool Test(matrix &data, matrix &target)
{
//--- 테스트 데이터에서 피드포워드
if(!FeedForward(data))
return false;
//--- 모델 계산 결과 및 실제 값 기록
PrintFormat("Test loss %.5f", result.Loss(target, LOSS_MSE));
ulong total = data.Rows();
for(ulong i = 0; i < total; i++)
PrintFormat("(%.2f + %.2f + %.2f)^2 / (%.2f^2 + %.2f^2 + %.2f^2) = Net %.2f, Target %.2f", data[i, 0], data[i, 1], data[i, 2],
data[i, 0], data[i, 1], data[i, 2], result[i, 0], target[i, 0]);
//--- 결과 반환
return true;
}
//+------------------------------------------------------------------+
|