지노랩 /JinoLab

16. 헬스장 데이터(운동 시간·휴식 시간 → 근육량 증가) Forward Propagation 본문

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

16. 헬스장 데이터(운동 시간·휴식 시간 → 근육량 증가) Forward Propagation

지노랩/JinoLab 2025. 8. 11. 09:51

아래는 “헬스장 데이터”(운동 시간·휴식 시간 → 근육량 증가) 예제를 C 언어로 구현한 앞으로 전파(Forward Propagation) 코드입니다.

  • 데이터 전처리: 원본 데이터를 최대값으로 나눠 [0,1] 구간으로 정규화
  • 네트워크 구조:
    • 입력층: 2개 특성(운동 시간, 휴식 시간)
    • 은닉층: 3개 유닛 (ReLU 활성화)
    • 출력층: 1개 유닛 (항등 활성화)
#include <stdio.h>

// --- 활성화 함수들 ---
double relu(double x) {
    return x > 0 ? x : 0;
}

// --- 행렬·벡터 곱: z = X (m×n) · W (n×p) -->
//    result is Z (m×p)
void matmul(const double *X, int m, int n,
            const double *W, int p,
            double *Z) {
    // X row-major: X[i*n + k]
    // W row-major: W[k*p + j]
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < p; j++) {
            double sum = 0.0;
            for (int k = 0; k < n; k++) {
                sum += X[i*n + k] * W[k*p + j];
            }
            Z[i*p + j] = sum;
        }
    }
}

int main(void) {
    // --- 1) 원본 데이터 (3번의 세션) ---
    // 운동 시간(hours), 휴식 시간(hours), 근육 증가량(grams)
    double raw_X[3][2] = {
        {1.0, 2.0},   // 예: 1시간 운동, 2시간 휴식 → 300g 증가
        {2.0, 1.0},   //      2시간 운동, 1시간 휴식 → 500g 증가
        {3.0, 1.5}    //      3시간 운동, 1.5시간 휴식 → 650g 증가
    };
    double raw_y[3] = {300.0, 500.0, 650.0};

    // --- 2) 정규화 --- (각 컬럼의 최대값으로 나누기)
    double max_x0 = 3.0, max_x1 = 2.0, max_y = 650.0;
    double X[3][2], y[3];
    for (int i = 0; i < 3; i++) {
        X[i][0] = raw_X[i][0] / max_x0;
        X[i][1] = raw_X[i][1] / max_x1;
        y[i]    = raw_y[i] / max_y;  // (0~1로 스케일링)
    }

    // --- 3) 네트워크 가중치 초기값 ---
    // W1: 입력(2) → 은닉(3)
    double W1[2][3] = {
        { 0.5, -0.3,  0.8},
        { 0.2,  0.4, -0.5}
    };
    // W2: 은닉(3) → 출력(1)
    double W2[3][1] = {
        { 1.0},
        {-1.2},
        { 0.5}
    };

    // --- 4) Forward Propagation ---
    // 4-1. Z2 = X · W1    (3×2)·(2×3) = (3×3)
    double Z2[3][3];
    matmul(&X[0][0], 3, 2, &W1[0][0], 3, &Z2[0][0]);

    // 4-2. A2 = ReLU(Z2)  (3×3)
    double A2[3][3];
    for (int i = 0; i < 3; i++)
        for (int j = 0; j < 3; j++)
            A2[i][j] = relu(Z2[i][j]);

    // 4-3. Z3 = A2 · W2   (3×3)·(3×1) = (3×1)
    double Z3[3][1];
    matmul(&A2[0][0], 3, 3, &W2[0][0], 1, &Z3[0][0]);

    // 4-4. y_hat = Z3     (항등 활성화)
    double y_hat[3];
    for (int i = 0; i < 3; i++) {
        y_hat[i] = Z3[i][0];
    }

    // --- 5) 결과 출력 (역정규화 포함) ---
    printf("예측근육증가량(grams) vs 실제(grams):\n");
    for (int i = 0; i < 3; i++) {
        double pred_g = y_hat[i] * max_y;  // 역정규화
        printf("  입력(%.1fh, %.1fh) → 예측: %6.1fg, 실제: %6.1fg\n",
               raw_X[i][0], raw_X[i][1],
               pred_g, raw_y[i]);
    }

    return 0;
}

코드 설명

  1. 데이터 정규화
    • 입력 X[i][j] = raw_X[i][j] / max(raw_X[:,j])
    • 출력 y[i] = raw_y[i] / max(raw_y)
      → 모든 값이 [0,1] 구간에 있도록 스케일링
  2. 행렬 곱 함수(matmul)
    • 일반적인 Z=XW 연산을 수행하기 위한 3중 for‑loop 구현
    • X는 m×n, W는 n×p, 결과 Z는 m×p
  3. 순전파(Forward Propagation)
    1. Z₂ = X·W₁ → 은닉층 선형결합
    2. A₂ = ReLU(Z₂) → 은닉층 비선형 활성화
    3. Z₃ = A₂·W₂ → 출력층 선형결합
    4. ŷ = Z₃ → 항등(Identity) 활성화
  4. 역정규화 후 출력 비교
    • 모델이 예측한 y^를 다시 원단위(grams)로 변환하여(ŷ * max_y) 실제값과 비교

이 예제는 모델 학습 없이, 임의의 가중치로 “순전파”만 시연합니다. 이후 경사하강법(Gradient Descent)역전파(Backpropagation) 과정을 통해 W1, W2를 학습시키면, 주어진 운동·휴식 시간에 따른 근육량 예측 성능을 개선할 수 있습니다.