지노랩 /JinoLab

8. 다중 입력·다중 출력(Multiple Input, Multiple Output, MIMO) 퍼셉트론 본문

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

8. 다중 입력·다중 출력(Multiple Input, Multiple Output, MIMO) 퍼셉트론

지노랩/JinoLab 2025. 8. 3. 12:57

이번 글에서는 다중 입력·다중 출력(Multiple Input, Multiple Output, MIMO) 퍼셉트론을 C 언어로 구현해 보겠습니다. 앞에서 구현한 “다중 입력·단일 출력”을 여러 개의 출력으로 확장한 형태입니다. 컴퓨터공학과 전공생 여러분이 쉽게 따라 할 수 있도록 이론과 코드 예제를 단계별로 설명합니다.


1. 네트워크 아키텍처

flowchart TB
    subgraph Features
        x0[온도 x₀]  
        x1[습도 x₁]  
        x2[공기질 x₂]
    end
    subgraph WeightsMatrix [가중치 행렬 W (3×3)]
        direction TB
        row0[/w₀₀ w₀₁ w₀₂\]  
        row1[/w₁₀ w₁₁ w₁₂\]  
        row2[/w₂₀ w₂₁ w₂₂\]
    end
    subgraph Outputs
        y0[행복지수 y₀]  
        y1[건강지수 y₁]  
        y2[활동지수 y₂]
    end

    %% 벡터·행렬 곱 연산
    Features -->|벡터·행렬 곱| Outputs
  • 입력(features): [x0,x1,x2][x_0, x_1, x_2] = [온도, 습도, 공기질]
  • 가중치 행렬(W): 크기 3×3

    • 행(row) iii번째 출력을 예측할 때 쓰는 가중치 벡터
    • 열(column) jjj번째 입력에 대응하는 가중치
  • 출력(outputs):


2. 의사코드(Pseudocode)

function predict_mimo(inputs[3], W[3][3], outputs[3]):
    for i from 0 to 2:          # 출력 인덱스
        sum = 0
        for j from 0 to 2:      # 입력 인덱스
            sum += W[i][j] * inputs[j]
        outputs[i] = sum
    return outputs

3. C 언어 구현

헤더 파일: simple_nn.h

#ifndef SIMPLE_NN_H
#define SIMPLE_NN_H

/**
 * @brief 다중 입력·다중 출력 퍼셉트론 (벡터·행렬 곱)
 * @param inputs    입력 벡터 (크기 n_inputs)
 * @param W         가중치 행렬 (크기 n_outputs × n_inputs)
 * @param outputs   출력 벡터 저장 (크기 n_outputs)
 * @param n_inputs  입력 개수
 * @param n_outputs 출력 개수
 */
void predict_mimo(const double *inputs,
                  const double **W,
                  double *outputs,
                  int n_inputs,
                  int n_outputs);

#endif // SIMPLE_NN_H

Tip: 2D 배열을 넘길 때 const double **W 형태로 선언했지만, 실제 메모리에서는 1차원 배열을 인덱스 산술로 처리해도 무방합니다.


구현 파일: simple_nn.c

#include "simple_nn.h"

void predict_mimo(const double *inputs,
                  const double **W,
                  double *outputs,
                  int n_inputs,
                  int n_outputs) {
    for (int i = 0; i < n_outputs; i++) {
        double sum = 0.0;
        for (int j = 0; j < n_inputs; j++) {
            sum += W[i][j] * inputs[j];
        }
        outputs[i] = sum;
    }
}

메인 파일: main.c

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

// 특성 개수와 출력 개수
#define N_INPUTS   3
#define N_OUTPUTS  3

int main(void) {
    // 입력 벡터: 온도, 습도, 공기질
    double inputs[N_INPUTS]  = {30.0, 70.0,  60.0};

    // 가중치 행렬 W: [출력][입력]
    static const double W_data[N_OUTPUTS][N_INPUTS] = {
        /* 행 0: 행복지수 예측→ 온도, 습도, 공기질 가중치 */
        { 0.5, 0.1, 0.3 },
        /* 행 1: 건강지수 예측 */
        { 0.2, 0.7, 0.1 },
        /* 행 2: 활동지수 예측 */
        { 0.4, 0.2, 0.6 }
    };
    // 2D 배열 포인터로 변환
    const double *W_ptrs[N_OUTPUTS];
    for (int i = 0; i < N_OUTPUTS; i++) {
        W_ptrs[i] = W_data[i];
    }

    double outputs[N_OUTPUTS];

    // 다중 입력·다중 출력 예측 수행
    predict_mimo(inputs, W_ptrs, outputs, N_INPUTS, N_OUTPUTS);

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

    return 0;
}

4. 빌드 & 실행

gcc -o SimpleNN main.c simple_nn.c
./SimpleNN
행복지수: 27.00
건강지수: 56.00
활동지수: 34.00

5. 요약 및 확장 아이디어

  • 핵심 연산: 벡터·행렬 곱

  • 확장 과제:
    1. **편향(bias)**를 각 출력에 추가

    1. 활성화 함수 적용 → ReLU, Sigmoid 등
    2. 다층 퍼셉트론(MLP) 구조로 확장하고 역전파(backpropagation) 구현
    3. 배치 처리: 다수 샘플을 한 번에 연산하는 행렬 곱 적용

이번 실습을 통해 신경망의 기본 빌딩 블록인 벡터·행렬 연산을 C 언어로 직접 구현해 보았습니다. 다음 포스팅에서는 역전파 알고리즘경사하강법(Gradient Descent) 을 사용해 가중치 학습 과정을 다룹니다. 많은 관심 부탁드립니다!