지노랩 /JinoLab

20. “근육량 예측 신경망”의 순전파(Forward Propagation) 과정을 C 언어로 본문

프로그래밍/C언어를 이용한 Deep Learning

20. “근육량 예측 신경망”의 순전파(Forward Propagation) 과정을 C 언어로

지노랩/JinoLab 2025. 8. 15. 09:08
  1. 데이터 정규화
  2. 가중치 초기화(예시로 임의값 사용)
  3. 첫 번째 학습 예제 순전파
  4. 중간값(z), 활성화(a), 출력(ŷ) 계산

1. 문제 정의

  • 입력 (x1,x2) = (운동시간, 휴식시간)
  • 출력 y = 근육량 증가(grams)
  • 신경망 구조
    • 입력층: 2개 노드
    • 은닉층: 3개 노드 (활성화: ReLU)
    • 출력층: 1개 노드 (활성화: Sigmoid)

2. 코드 전체

#include <stdio.h>
#include <math.h>

// --- 활성화 함수들 ---
double relu(double z) {
    return z > 0 ? z : 0;
}
double sigmoid(double z) {
    return 1.0 / (1.0 + exp(-z));
}

// --- 순전파 함수 ---
// X: (m×n) 입력 행렬, W1: (n×h) 은닉 가중치, W2: (h×1) 출력 가중치
// m=샘플 개수(여기선 3), n=피처 수(2), h=은닉 노드 수(3)
void forward(int m, int n, int h,
             double X[m][n],
             double W1[n][h],
             double W2[h][1],
             double Yhat[m])
{
    for (int i = 0; i < m; i++) {
        // 1) 은닉층 z2 = X[i] · W1  (1×n)·(n×h) → (1×h)
        double z2[ h ];
        for (int j = 0; j < h; j++) {
            z2[j] = 0;
            for (int k = 0; k < n; k++) {
                z2[j] += X[i][k] * W1[k][j];
            }
        }
        // 2) 은닉층 a2 = ReLU(z2)
        double a2[ h ];
        for (int j = 0; j < h; j++) {
            a2[j] = relu(z2[j]);
        }
        // 3) 출력층 z3 = a2 · W2  (1×h)·(h×1) → (1×1)
        double z3 = 0;
        for (int j = 0; j < h; j++) {
            z3 += a2[j] * W2[j][0];
        }
        // 4) 예측값 yhat = sigmoid(z3)
        Yhat[i] = sigmoid(z3);
    }
}

int main(void) {
    // --- 1) 원본 데이터 (m=3 샘플) ---
    // [운동시간, 휴식시간]
    double rawX[3][2] = {
        {2.0, 8.0},
        {5.0, 5.0},
        {1.0, 8.0}
    };
    // 근육량 증가(grams)
    double rawY[3] = {291.0, 920.0, 190.0};

    // --- 2) 정규화 (0~1) ---
    double maxX0 = 5.0, maxX1 = 8.0, maxY = 920.0;
    double X[3][2], Y[3];
    for (int i = 0; i < 3; i++) {
        X[i][0] = rawX[i][0] / maxX0;
        X[i][1] = rawX[i][1] / maxX1;
        Y[i]    = rawY[i]    / maxY;
    }

    // --- 3) 가중치 초기화(예시값) ---
    // 입력→은닉 (2×3)
    double W1[2][3] = {
        { 0.2, -0.1,  0.4},
        { 0.7,  0.3, -0.5}
    };
    // 은닉→출력 (3×1)
    double W2[3][1] = {
        { 1.0},
        {-1.5},
        { 0.6}
    };

    // --- 4) 순전파 실행 ---
    double Yhat[3];
    forward(3, 2, 3, X, W1, W2, Yhat);

    // --- 5) 결과 출력 (역정규화 포함) ---
    printf("샘플 | 정답(grams) | 예측(grams)\n");
    printf("------+-------------+-------------\n");
    for (int i = 0; i < 3; i++) {
        printf("  %d   |   %6.1f    |   %6.1f\n",
               i+1,
               rawY[i],
               Yhat[i] * maxY  // 역정규화
        );
    }
    return 0;
}

3. 핵심 설명

  1. 정규화(Normalization)
    • 입력·출력 범위를 0~1로 스케일링 → 학습 안정성↑
  2. 은닉층 계산
    • z2 = XW1 → ReLUa2
  3. 출력층 계산
    • z3 = a2W2 → Sigmoidy^
  4. 예측값 역정규화
    • y^ × max⁡(Y)
  5. 하나의 함수 forward 에서 반복문으로 각 샘플을 처리

이렇게 “입력 → 선형결합(z) → 비선형변환(a) → 다음 층 입력”의 흐름이 바로 순전파입니다.
다음 시간에는 역전파(Backpropagation) 를 구현해, 가중치를 학습시키는 방법을 배워봅시다!