지노랩 /JinoLab

9. 은닉층(hidden layer) 본문

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

9. 은닉층(hidden layer)

지노랩/JinoLab 2025. 8. 4. 09:08

 

1. 네트워크 구조

  1. 입력층 → 은닉층
    • 입력 벡터 x∈Rnin
    • 가중치 행렬 W(1)∈Rnhid×nin
    • 은닉 활성 벡터 h∈Rnhid
           

 

  1. 은닉층 → 출력층
    • 은닉 벡터 h
    • 가중치 행렬 W(2)∈Rnout×nhid
    • 출력 예측 벡터 y∈Rnout

 

  x (n_in) ──▶ W¹ ──▶ h (n_hid) ──▶ W² ──▶ y (n_out)

이번 예제에서는 선형 활성화만 사용합니다. (추후 ReLU, Sigmoid 추가 가능)


2. C 언어 구현

2.1 헤더 파일: simple_mlp.h

#ifndef SIMPLE_MLP_H
#define SIMPLE_MLP_H

/**
 * @brief 1층 은닉층을 갖는 MLP 예측 함수 (선형 활성화)
 * @param x               입력 벡터 (크기 n_in)
 * @param W1              입력→은닉 가중치 행렬 (flatten된 배열, 크기 n_hid * n_in)
 * @param n_in            입력 개수
 * @param n_hid           은닉 유닛 개수
 * @param W2              은닉→출력 가중치 행렬 (flatten된 배열, 크기 n_out * n_hid)
 * @param n_out           출력 개수
 * @param y               결과 출력 벡터 (크기 n_out)
 */
void predict_mlp(const double *x,
                 const double *W1,
                 int n_in,
                 int n_hid,
                 const double *W2,
                 int n_out,
                 double *y);

#endif // SIMPLE_MLP_H

2.2 구현 파일: simple_mlp.c

#include "simple_mlp.h"
#include <stdlib.h>  // malloc, free

// 내부에서 사용할 벡터·행렬 곱 (flattened matrix)
static void matvec(const double *vec, const double *mat,
                   int rows, int cols, double *out) {
    for (int i = 0; i < rows; i++) {
        double sum = 0.0;
        for (int j = 0; j < cols; j++) {
            sum += mat[i * cols + j] * vec[j];
        }
        out[i] = sum;
    }
}

void predict_mlp(const double *x,
                 const double *W1,
                 int n_in,
                 int n_hid,
                 const double *W2,
                 int n_out,
                 double *y) {
    // 1) 은닉층 계산: h = W1 · x
    double *h = (double*)malloc(sizeof(double) * n_hid);
    if (!h) return;  // 메모리 할당 실패 시 종료

    matvec(x, W1, n_hid, n_in, h);

    // 2) 출력층 계산: y = W2 · h
    matvec(h, W2, n_out, n_hid, y);

    // 3) 동적 메모리 해제
    free(h);
}

2.3 메인 파일: main.c

#include <stdio.h>
#include "simple_mlp.h"

// 네트워크 크기 정의
#define N_INPUTS  3   // 온도, 습도, 공기질
#define N_HIDDEN  4   // 은닉 유닛 개수 (예시)
#define N_OUTPUTS 3   // 행복지수, 건강지수, 활동지수

int main(void) {
    // 1) 입력 벡터 x
    double x[N_INPUTS] = {30.0, 87.0, 110.0};

    // 2) 가중치 행렬 W1 (n_hid × n_in)
    double W1[N_HIDDEN * N_INPUTS] = {
        // 행 0
        0.2, 0.5, -0.3,
        // 행 1
       -0.1, 0.8,  0.4,
        // 행 2
        0.7, -0.6, 0.1,
        // 행 3
        0.3, 0.2,  0.9
    };

    // 3) 가중치 행렬 W2 (n_out × n_hid)
    double W2[N_OUTPUTS * N_HIDDEN] = {
        // y0 (행복지수) 예측 가중치
        0.5,  -0.2, 0.1, 0.3,
        // y1 (건강지수) 예측 가중치
        0.4,   0.7, 0.2, -0.5,
        // y2 (활동지수) 예측 가중치
        -0.3,  0.4, 0.6,  0.8
    };

    // 4) 예측 결과 저장 버퍼
    double y[N_OUTPUTS];

    // 5) MLP 예측 함수 호출
    predict_mlp(x, W1, N_INPUTS, N_HIDDEN, W2, N_OUTPUTS, y);

    // 6) 결과 출력
    const char *labels[N_OUTPUTS] = {
        "행복지수", "건강지수", "활동지수"
    };
    for (int i = 0; i < N_OUTPUTS; i++) {
        printf("%s: %.4f\n", labels[i], y[i]);
    }

    return 0;
}

3. 빌드 & 실행

gcc -o SimpleMLP main.c simple_mlp.c
./SimpleMLP
행복지수 :  20.7700
건강지수 :  79.9300
활동지수 :  58.4600

4. 마무리 및 확장 과제

  1. 비선형 활성화 함수 추가
    • ReLU, Sigmoid 등으로 h[i] = f(h[i]) 처리
  2. 편향(bias) 항 도입

  1. 역전파(Backpropagation) 구현해 가중치 학습
  2. 배치 처리(Batch Processing)
  3. 학습률(lr), 에포크(epoch) 등 하이퍼파라미터 튜닝

이 예제를 바탕으로, 직접 학습 알고리즘까지 구현해 보며 딥러닝의 기본 원리를 체득해 보시기 바랍니다!