🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

A

AI Generated

2025. 12. 4. · 49 Views

신경망 기초 완벽 가이드

인공지능의 핵심인 신경망을 처음부터 차근차근 배워봅니다. 퍼셉트론부터 딥러닝 아키텍처까지, 초급 개발자도 이해할 수 있도록 쉽게 설명합니다.


목차

  1. 신경망의_역사적_배경
  2. AI_겨울과_AI_봄
  3. 퍼셉트론_이해하기
  4. 신경망_직관적_이해
  5. 딥러닝의_층_구조
  6. 입력층_은닉층_출력층
  7. 신경망_아키텍처_설계

1. 신경망의 역사적 배경

김개발 씨는 회사에서 AI 프로젝트에 투입되었습니다. 팀장님이 "신경망 기반으로 개발해주세요"라고 했는데, 신경망이 도대체 뭔지 감이 잡히지 않았습니다.

도대체 신경망은 어디서 시작된 것일까요?

신경망은 인간의 뇌 구조에서 영감을 받아 만들어진 계산 모델입니다. 마치 뇌 속의 뉴런들이 서로 연결되어 정보를 처리하는 것처럼, 인공 신경망도 노드들이 연결되어 데이터를 처리합니다.

1943년 맥컬록과 피츠가 처음 수학적 모델을 제안한 이후, 80년이 넘는 역사를 가지고 있습니다.

다음 코드를 살펴봅시다.

# 1943년 맥컬록-피츠 뉴런 모델의 현대적 구현
def mcculloch_pitts_neuron(inputs, weights, threshold):
    # 입력값과 가중치를 곱해서 합산
    weighted_sum = sum(x * w for x, w in zip(inputs, weights))

    # 임계값을 넘으면 1, 아니면 0 출력 (단순한 이진 판단)
    if weighted_sum >= threshold:
        return 1  # 뉴런이 활성화됨
    else:
        return 0  # 뉴런이 비활성화됨

# 간단한 AND 게이트 구현
inputs = [1, 1]
weights = [0.5, 0.5]
result = mcculloch_pitts_neuron(inputs, weights, threshold=1.0)
print(f"AND 연산 결과: {result}")  # 출력: 1

김개발 씨는 입사 6개월 차 주니어 개발자입니다. 어느 날 팀장님이 다가와 말했습니다.

"김 개발자, 이번 프로젝트는 신경망 기반으로 진행할 거예요. 미리 공부해두세요." 신경망이라는 단어는 많이 들어봤지만, 정확히 뭔지는 몰랐습니다.

김개발 씨는 옆자리 선배 박시니어 씨에게 조심스럽게 물었습니다. "선배님, 신경망이 정확히 뭔가요?" 박시니어 씨가 커피를 한 모금 마시며 대답했습니다.

"신경망의 역사를 알면 이해가 쉬워져요. 잠깐 설명해줄게요." 이야기는 1943년으로 거슬러 올라갑니다.

신경과학자 워렌 맥컬록과 수학자 월터 피츠는 인간의 뇌가 어떻게 계산을 수행하는지 궁금했습니다. 그들은 뇌 속의 뉴런이 전기 신호를 주고받는 모습을 관찰했습니다.

마치 전구에 불이 들어오고 꺼지는 것처럼, 뉴런도 활성화되거나 비활성화됩니다. 이 단순한 관찰이 모든 것의 시작이었습니다.

맥컬록과 피츠는 이것을 수학적으로 모델링했습니다. 그들이 만든 모델은 단순했습니다.

여러 입력을 받아서, 각 입력에 가중치를 곱하고, 합산한 값이 임계값을 넘으면 1을 출력하고, 그렇지 않으면 0을 출력합니다. 이것이 바로 인공 뉴런의 기본 원리입니다.

위의 코드를 보면 이 원리가 그대로 구현되어 있습니다. inputs는 뉴런에 들어오는 신호들이고, weights는 각 신호의 중요도입니다.

이 둘을 곱해서 더한 값이 threshold를 넘으면 뉴런이 활성화됩니다. 재미있는 것은 이 단순한 모델로 AND, OR 같은 논리 연산을 구현할 수 있다는 점입니다.

코드에서 보듯이, 입력이 둘 다 1일 때만 결과가 1이 되도록 가중치와 임계값을 설정하면 AND 게이트가 됩니다. 1958년에는 프랭크 로젠블랫퍼셉트론을 발명했습니다.

맥컬록-피츠 모델을 발전시켜, 스스로 학습할 수 있는 알고리즘을 추가한 것입니다. 이때 사람들은 "드디어 생각하는 기계가 탄생했다!"고 열광했습니다.

하지만 모든 것이 순탄하지는 않았습니다. 신경망 연구는 여러 번의 겨울을 맞이하게 됩니다.

그 이야기는 다음 장에서 계속됩니다. 박시니어 씨의 설명을 들은 김개발 씨는 고개를 끄덕였습니다.

"아, 신경망이 갑자기 나온 게 아니라 80년이나 된 역사가 있군요!"

실전 팁

💡 - 신경망의 기본 원리는 입력, 가중치, 임계값 세 가지로 이해할 수 있습니다

  • 역사를 알면 현재 기술이 왜 이렇게 발전했는지 맥락을 파악하기 쉽습니다

2. AI 겨울과 AI 봄

김개발 씨는 신경망이 80년 역사를 가졌다는 것을 알게 되었습니다. 그런데 한 가지 의문이 들었습니다.

"80년이나 됐는데 왜 최근에야 이렇게 주목받는 거죠?" 박시니어 씨가 의미심장한 미소를 지었습니다.

AI 겨울은 인공지능 연구에 대한 기대가 꺾이고 투자가 급감했던 시기를 말합니다. 마치 긴 겨울처럼 연구가 얼어붙었던 시기가 두 번이나 있었습니다.

하지만 2012년 이후 딥러닝의 성공으로 AI 봄이 찾아왔고, 지금 우리가 누리는 AI 혁명이 시작되었습니다.

다음 코드를 살펴봅시다.

# AI 역사의 주요 이정표를 시뮬레이션
ai_timeline = {
    1958: {"event": "퍼셉트론 발명", "mood": "열광", "funding": "high"},
    1969: {"event": "XOR 문제 발견", "mood": "좌절", "funding": "low"},
    1986: {"event": "역전파 알고리즘", "mood": "희망", "funding": "medium"},
    1995: {"event": "SVM의 등장", "mood": "신경망 외면", "funding": "low"},
    2012: {"event": "AlexNet 우승", "mood": "부활", "funding": "very_high"},
    2023: {"event": "ChatGPT 혁명", "mood": "열광", "funding": "unprecedented"}
}

# AI 봄이 온 2012년 이후의 변화
def check_ai_spring(year):
    if year >= 2012:
        return "AI 봄: 딥러닝이 세상을 바꾸고 있습니다"
    elif year >= 1986:
        return "두 번째 겨울을 향해: 기대와 현실의 괴리"
    else:
        return "첫 번째 AI 겨울: 퍼셉트론의 한계 발견"

print(check_ai_spring(2024))

"신경망 연구에는 AI 겨울이라는 암흑기가 있었어요." 박시니어 씨가 말을 이었습니다. 1958년 퍼셉트론이 발명되었을 때, 세상은 열광했습니다.

신문에는 "생각하는 기계 탄생!"이라는 기사가 실렸고, 연구 자금이 쏟아져 들어왔습니다. 사람들은 곧 인간 수준의 AI가 등장할 거라고 믿었습니다.

하지만 현실은 달랐습니다. 1969년, 마빈 민스키시모어 페이퍼트가 퍼셉트론의 치명적인 한계를 발견했습니다.

퍼셉트론은 XOR 문제를 풀 수 없었던 것입니다. XOR은 두 입력이 다를 때만 1을 출력하는 간단한 논리 연산인데, 단층 퍼셉트론으로는 이것조차 학습할 수 없었습니다.

마치 날씨가 갑자기 추워지듯, 연구 자금이 급격히 줄어들었습니다. 이것이 첫 번째 AI 겨울입니다.

많은 연구자들이 다른 분야로 떠났고, 신경망 연구는 외면받았습니다. 1986년, 희망의 빛이 보였습니다.

역전파 알고리즘이 재발견된 것입니다. 여러 층을 쌓은 신경망도 학습시킬 수 있는 방법이었습니다.

"드디어 XOR 문제를 풀었다!"고 연구자들은 환호했습니다. 하지만 기쁨은 오래가지 않았습니다.

컴퓨터 성능이 충분하지 않았고, 데이터도 부족했습니다. 신경망보다 SVM(서포트 벡터 머신) 같은 다른 기법이 더 좋은 성능을 보여주면서, 신경망은 다시 외면받았습니다.

이것이 두 번째 AI 겨울입니다. 그리고 2012년, 역사적인 일이 일어났습니다.

제프리 힌튼 교수팀의 AlexNet이 이미지 인식 대회에서 압도적인 성능으로 우승한 것입니다. 기존 방법보다 오류율을 10% 이상 낮췄습니다.

사람들은 깜짝 놀랐습니다. 무엇이 달라졌을까요?

세 가지가 바뀌었습니다. 첫째, GPU의 발전으로 연산 속도가 획기적으로 빨라졌습니다.

둘째, 인터넷 덕분에 빅데이터가 쏟아졌습니다. 셋째, 드롭아웃, ReLU 같은 새로운 기법들이 등장했습니다.

이후로는 말 그대로 AI 봄입니다. 2016년 알파고가 이세돌을 이겼고, 2023년 ChatGPT가 세상을 놀라게 했습니다.

신경망은 이제 일상 곳곳에서 사용되고 있습니다. 김개발 씨가 감탄했습니다.

"두 번이나 겨울을 견디고 이렇게 발전했군요!" 박시니어 씨가 고개를 끄덕였습니다. "맞아요.

포기하지 않은 연구자들 덕분이죠."

실전 팁

💡 - AI 겨울을 알면 현재 기술의 소중함을 이해할 수 있습니다

  • 2012년 AlexNet은 딥러닝 시대를 연 역사적 전환점입니다

3. 퍼셉트론 이해하기

이제 김개발 씨는 신경망의 역사를 알게 되었습니다. 박시니어 씨가 말했습니다.

"역사를 알았으니 이제 기초부터 배워봅시다. 신경망의 가장 기본 단위인 퍼셉트론부터 시작해요."

퍼셉트론은 신경망의 가장 기본적인 구성 요소입니다. 마치 레고 블록 하나하나가 모여 거대한 성을 만들듯, 퍼셉트론이 모여 복잡한 신경망을 구성합니다.

입력값에 가중치를 곱하고, 편향을 더한 뒤, 활성화 함수를 통과시켜 출력을 만들어냅니다.

다음 코드를 살펴봅시다.

import numpy as np

class Perceptron:
    def __init__(self, input_size, learning_rate=0.1):
        # 가중치를 랜덤하게 초기화
        self.weights = np.random.randn(input_size)
        self.bias = 0  # 편향
        self.lr = learning_rate

    def predict(self, x):
        # 가중합 계산: 입력 * 가중치 + 편향
        weighted_sum = np.dot(x, self.weights) + self.bias
        # 계단 함수: 0보다 크면 1, 아니면 0
        return 1 if weighted_sum > 0 else 0

    def train(self, x, target):
        prediction = self.predict(x)
        error = target - prediction
        # 오차에 따라 가중치와 편향 업데이트
        self.weights += self.lr * error * x
        self.bias += self.lr * error

# AND 게이트 학습 예제
perceptron = Perceptron(input_size=2)

박시니어 씨가 화이트보드에 동그라미 하나를 그렸습니다. "이게 퍼셉트론이에요.

신경망의 가장 작은 단위죠." 퍼셉트론을 이해하려면 먼저 우리 뇌 속의 뉴런을 생각해보면 됩니다. 뉴런은 다른 뉴런들로부터 신호를 받아서, 그 신호들을 종합한 다음, 일정 수준을 넘으면 다음 뉴런에게 신호를 보냅니다.

퍼셉트론도 똑같습니다. 여러 입력을 받아서, 각 입력에 가중치를 곱합니다.

가중치는 각 입력이 얼마나 중요한지를 나타냅니다. 마치 시험에서 과목마다 배점이 다른 것과 같습니다.

김개발 씨가 물었습니다. "가중치를 곱한 다음에는요?" "다 더해요.

그리고 편향이라는 것도 더합니다." 편향은 마치 문턱 높이를 조절하는 것과 같습니다. 편향이 높으면 쉽게 활성화되고, 낮으면 활성화되기 어렵습니다.

그 다음이 중요합니다. 이 합산된 값을 활성화 함수에 통과시킵니다.

가장 단순한 활성화 함수는 계단 함수입니다. 값이 0보다 크면 1을 출력하고, 그렇지 않으면 0을 출력합니다.

"발사할까 말까"를 결정하는 것입니다. 위의 코드를 보면 이 과정이 predict 메서드에 구현되어 있습니다.

np.dot(x, self.weights)가 입력과 가중치를 곱해서 더하는 부분이고, self.bias를 더해서 최종 가중합을 계산합니다. 더 놀라운 것은 퍼셉트론이 스스로 학습할 수 있다는 점입니다.

train 메서드를 보세요. 예측값과 실제값의 차이인 error를 계산하고, 이 오차를 이용해서 가중치와 편향을 조금씩 수정합니다.

마치 양궁 선수가 과녁을 맞히지 못했을 때 조준점을 조금씩 수정하는 것과 같습니다. 왼쪽으로 빗나갔으면 오른쪽으로 조금 수정하고, 위로 빗나갔으면 아래로 조금 수정합니다.

이것을 반복하면 점점 과녁 중심에 가까워집니다. 퍼셉트론도 마찬가지입니다.

틀릴 때마다 가중치를 조금씩 수정하면, 결국 올바른 답을 내놓는 가중치를 찾게 됩니다. 이것이 바로 학습입니다.

김개발 씨의 눈이 반짝였습니다. "아, 그래서 AI가 학습한다고 하는 거군요!

가중치를 조금씩 수정하는 거였어요!"

실전 팁

💡 - 퍼셉트론의 핵심 공식: 출력 = 활성화함수(입력 * 가중치 + 편향)

  • 학습은 오차를 줄이는 방향으로 가중치를 수정하는 과정입니다

4. 신경망 직관적 이해

김개발 씨는 퍼셉트론을 이해했습니다. 하지만 한 가지 의문이 남았습니다.

"그런데 퍼셉트론 하나로는 XOR 문제를 못 푼다고 했잖아요? 그럼 어떻게 해야 하나요?"

신경망은 여러 개의 퍼셉트론을 층으로 쌓아 만든 구조입니다. 마치 공장에서 여러 단계의 조립 라인을 거쳐 복잡한 제품이 만들어지듯, 신경망도 여러 층을 거치면서 단순한 입력을 복잡한 판단으로 바꿔냅니다.

층을 쌓으면 단일 퍼셉트론으로는 불가능했던 문제도 해결할 수 있습니다.

다음 코드를 살펴봅시다.

import numpy as np

def sigmoid(x):
    # 시그모이드: 부드러운 활성화 함수
    return 1 / (1 + np.exp(-x))

# 간단한 2층 신경망으로 XOR 문제 해결
class SimpleNeuralNetwork:
    def __init__(self):
        # 은닉층 가중치 (2개 입력 -> 2개 뉴런)
        self.hidden_weights = np.array([[0.5, 0.5], [0.5, 0.5]])
        self.hidden_bias = np.array([-0.7, -0.2])
        # 출력층 가중치 (2개 뉴런 -> 1개 출력)
        self.output_weights = np.array([1.0, -1.0])
        self.output_bias = -0.5

    def forward(self, x):
        # 1단계: 은닉층 통과
        hidden = sigmoid(np.dot(x, self.hidden_weights) + self.hidden_bias)
        # 2단계: 출력층 통과
        output = sigmoid(np.dot(hidden, self.output_weights) + self.output_bias)
        return output

nn = SimpleNeuralNetwork()
print(f"XOR(0,0) = {nn.forward([0,0]):.2f}")  # 약 0
print(f"XOR(1,1) = {nn.forward([1,1]):.2f}")  # 약 0

박시니어 씨가 화이트보드에 동그라미를 여러 개 그리기 시작했습니다. 세로로 줄을 지어 그리고, 각 층의 동그라미들을 선으로 연결했습니다.

"단일 퍼셉트론으로 XOR을 못 푸는 이유를 알려줄게요." 박시니어 씨가 좌표 평면을 그렸습니다. (0,0)과 (1,1)에는 0, (0,1)과 (1,0)에는 1을 표시했습니다.

"이걸 직선 하나로 분류할 수 있어요?" 김개발 씨가 잠시 생각했습니다. 아무리 해도 직선 하나로는 0과 1을 완벽하게 나눌 수 없었습니다.

"안 되네요..." "맞아요. 단일 퍼셉트론은 직선 하나만 그을 수 있어요.

이걸 선형 분리라고 하는데, XOR은 선형 분리가 불가능해요." 그래서 해결책이 뭘까요? 바로 층을 쌓는 것입니다.

박시니어 씨가 설명을 이어갔습니다. "퍼셉트론을 층으로 쌓으면 직선을 여러 개 그을 수 있어요.

직선 두 개를 조합하면 XOR도 분류할 수 있게 됩니다." 이것을 공장 조립 라인에 비유하면 이해가 쉽습니다. 자동차 공장을 생각해보세요.

첫 번째 라인에서는 프레임을 만들고, 두 번째 라인에서는 엔진을 장착하고, 세 번째 라인에서는 도색을 합니다. 각 단계가 단순한 작업을 하지만, 전체를 거치면 복잡한 자동차가 완성됩니다.

신경망도 마찬가지입니다. 첫 번째 층에서는 단순한 특징을 추출하고, 다음 층에서는 이를 조합해서 더 복잡한 특징을 만들고, 최종 층에서 판단을 내립니다.

위의 코드에서 forward 메서드를 보세요. 입력이 먼저 은닉층을 통과합니다.

은닉층의 두 뉴런이 각각 다른 직선을 긋습니다. 그 다음 출력층에서 이 두 결과를 조합해서 최종 답을 냅니다.

여기서 시그모이드 함수가 등장합니다. 계단 함수 대신 시그모이드를 쓰면 출력이 0과 1 사이의 부드러운 값이 됩니다.

이게 왜 중요할까요? 나중에 역전파로 학습할 때 미분이 가능해야 하기 때문입니다.

김개발 씨가 감탄했습니다. "층을 쌓으니까 XOR이 풀리네요!" 박시니어 씨가 미소 지었습니다.

"이게 바로 신경망의 힘이에요. 층을 더 쌓으면 더 복잡한 문제도 풀 수 있습니다."

실전 팁

💡 - 단일 퍼셉트론은 직선 하나, 다층 신경망은 직선 여러 개로 분류합니다

  • 층을 쌓으면 표현력이 기하급수적으로 증가합니다

5. 딥러닝의 층 구조

김개발 씨는 2층 신경망으로 XOR 문제가 풀린다는 것을 알게 되었습니다. 그런데 요즘 유행하는 딥러닝은 훨씬 더 많은 층을 사용한다고 합니다.

"도대체 몇 층까지 쌓는 거예요?"

딥러닝은 말 그대로 깊은(deep) 학습입니다. 신경망의 층을 깊이 쌓으면 더 복잡하고 추상적인 특징을 학습할 수 있습니다.

이미지 인식에서 첫 번째 층은 선과 가장자리를, 중간 층은 모양과 질감을, 깊은 층은 얼굴이나 물체 전체를 인식합니다. 이처럼 층이 깊어질수록 더 고차원적인 개념을 학습합니다.

다음 코드를 살펴봅시다.

import numpy as np

# 딥러닝의 층별 특징 추출을 시뮬레이션
class DeepFeatureExtractor:
    def __init__(self):
        self.layer_descriptions = {
            1: "가장자리, 색상 검출",
            2: "질감, 패턴 인식",
            3: "부분 형태 (눈, 코, 바퀴 등)",
            4: "객체 부분 조합",
            5: "전체 객체 인식 (얼굴, 자동차)"
        }

    def describe_layer(self, layer_num):
        return self.layer_descriptions.get(layer_num, "추상적 고수준 특징")

    def forward_visualization(self, num_layers):
        print(f"=== {num_layers}층 딥러닝 네트워크 ===")
        for i in range(1, num_layers + 1):
            feature = self.describe_layer(i)
            print(f"Layer {i}: {feature}")
        print("최종 출력: 분류 결과")

extractor = DeepFeatureExtractor()
extractor.forward_visualization(5)

"딥러닝의 'deep'이 뭘 의미하는지 아세요?" 박시니어 씨가 물었습니다. 김개발 씨가 대답했습니다.

"층이 많다는 뜻 아닌가요?" "맞아요. 근데 왜 층을 많이 쌓는지가 중요해요." 박시니어 씨가 사진 한 장을 예로 들었습니다.

이 사진이 고양이인지 개인지 어떻게 판단할까요? 사람의 뇌는 여러 단계를 거칩니다.

먼저 선과 윤곽을 인식합니다. 그 다음 질감과 패턴을 파악합니다.

털이 있는지, 매끄러운지. 그 다음 부분들을 인식합니다.

귀 모양, 눈 모양, 코 모양. 마지막으로 이 부분들을 조합해서 "고양이다!"라고 판단합니다.

딥러닝도 비슷합니다. 낮은 층에서는 저수준 특징을 학습합니다.

가장자리, 색상 변화, 간단한 패턴 같은 것들입니다. 이건 어떤 이미지든 공통적으로 필요한 기본 요소입니다.

중간 층에서는 이 기본 요소들을 조합해서 중수준 특징을 만듭니다. 눈처럼 생긴 패턴, 코처럼 생긴 패턴, 귀처럼 생긴 패턴 등입니다.

깊은 층에서는 고수준 특징을 인식합니다. "이건 고양이 얼굴이다", "이건 자동차 바퀴다"처럼 전체적인 개념을 파악합니다.

위의 코드는 이 과정을 시뮬레이션합니다. 5층 네트워크가 각 층에서 어떤 특징을 추출하는지 보여줍니다.

실제 딥러닝 모델은 수십, 수백 층까지 쌓습니다. 재미있는 사실이 있습니다.

낮은 층에서 학습한 특징은 재사용이 가능합니다. 고양이를 학습한 모델의 낮은 층은 가장자리와 질감을 검출하는데, 이건 자동차를 인식할 때도 유용합니다.

이게 바로 전이 학습의 기초입니다. "그래서 요즘 GPT나 이미지 인식 모델들이 수십억 개의 파라미터를 가지는 거예요." 박시니어 씨가 말했습니다.

"층을 깊이 쌓을수록 더 복잡한 패턴을 학습할 수 있거든요." 김개발 씨가 고개를 끄덕였습니다. "층이 깊어질수록 더 추상적인 개념을 배우는 거군요!"

실전 팁

💡 - 일반적으로 층이 깊을수록 더 복잡한 문제를 풀 수 있지만, 학습도 어려워집니다

  • 전이 학습을 활용하면 적은 데이터로도 좋은 성능을 얻을 수 있습니다

6. 입력층 은닉층 출력층

딥러닝이 왜 깊은지 이해한 김개발 씨. 이제 실제로 신경망을 설계하려고 합니다.

"그런데 선배님, 층의 종류가 있다고 들었는데요. 입력층, 은닉층, 출력층이 뭔가요?"

신경망은 크게 세 종류의 층으로 구성됩니다. 입력층은 데이터를 받아들이는 창구이고, 은닉층은 실제 계산과 학습이 일어나는 두뇌이며, 출력층은 최종 결과를 내보내는 출구입니다.

마치 식당에서 주문을 받고, 요리하고, 음식을 내어주는 과정과 같습니다.

다음 코드를 살펴봅시다.

import numpy as np

class NeuralNetworkLayers:
    def __init__(self, input_size, hidden_sizes, output_size):
        self.layers = []

        # 입력층 -> 첫 번째 은닉층
        prev_size = input_size
        for hidden_size in hidden_sizes:
            # 각 층의 가중치와 편향 초기화
            layer = {
                'weights': np.random.randn(prev_size, hidden_size) * 0.1,
                'bias': np.zeros(hidden_size)
            }
            self.layers.append(layer)
            prev_size = hidden_size

        # 마지막 은닉층 -> 출력층
        self.output_layer = {
            'weights': np.random.randn(prev_size, output_size) * 0.1,
            'bias': np.zeros(output_size)
        }

    def describe_architecture(self):
        print("=== 신경망 구조 ===")
        print(f"입력층: 데이터 수신")
        for i, layer in enumerate(self.layers):
            print(f"은닉층 {i+1}: 특징 추출 ({layer['weights'].shape[1]} 뉴런)")
        print(f"출력층: 최종 결과 ({self.output_layer['weights'].shape[1]} 뉴런)")

# 784(이미지) -> 256 -> 128 -> 10(숫자 분류) 구조
nn = NeuralNetworkLayers(784, [256, 128], 10)
nn.describe_architecture()

박시니어 씨가 신경망 구조 그림을 보여주며 설명을 시작했습니다. "신경망을 식당에 비유해볼게요." 박시니어 씨가 말했습니다.

입력층은 식당의 접수 카운터와 같습니다. 손님이 주문을 하면 접수 직원이 주문을 받아 기록합니다.

주문 내용을 변경하거나 처리하지 않고, 그대로 주방에 전달합니다. 입력층도 마찬가지입니다.

이미지 픽셀 값, 텍스트 데이터, 숫자 등을 받아서 다음 층에 전달합니다. 은닉층주방입니다.

여러 명의 요리사가 각자 맡은 작업을 합니다. 누군가는 재료를 손질하고, 누군가는 볶고, 누군가는 굽습니다.

각 요리사의 작업이 조합되어 맛있는 요리가 완성됩니다. 은닉층도 여러 뉴런이 각자 특징을 추출하고, 이것들이 조합되어 복잡한 패턴을 인식합니다.

왜 '은닉'층이라고 부를까요? 우리가 직접 볼 수 없기 때문입니다.

입력과 출력은 눈에 보이지만, 중간 과정은 숨겨져 있습니다. 마치 주방 안에서 무슨 일이 일어나는지 손님이 모르는 것처럼요.

출력층서빙입니다. 완성된 요리를 손님에게 내어줍니다.

분류 문제라면 "이 이미지는 고양이입니다"라는 결과를 내놓고, 회귀 문제라면 "예측 가격은 50,000원입니다"라는 숫자를 출력합니다. 위의 코드를 보면 실제 구조를 확인할 수 있습니다.

784개의 입력(28x28 이미지)을 받아서, 256개 뉴런의 첫 번째 은닉층, 128개 뉴런의 두 번째 은닉층을 거쳐, 10개의 출력(0-9 숫자 분류)을 내보냅니다. 각 층 사이에는 가중치편향이 있습니다.

이것들이 학습 과정에서 조정되는 파라미터입니다. 784 -> 256 연결에는 784 * 256 = 200,704개의 가중치가 필요합니다.

딥러닝 모델의 파라미터 수가 엄청나게 많은 이유가 여기 있습니다. "입력층 크기는 데이터에 따라 정해지고, 출력층 크기는 문제에 따라 정해져요." 박시니어 씨가 정리했습니다.

"우리가 설계해야 하는 건 은닉층의 수와 각 층의 뉴런 수입니다." 김개발 씨가 물었습니다. "은닉층을 어떻게 설계해야 할까요?"

실전 팁

💡 - 입력층 크기는 데이터의 특성 수와 같아야 합니다 (이미지면 픽셀 수)

  • 출력층 크기는 분류할 클래스 수 또는 예측할 값의 수와 같습니다

7. 신경망 아키텍처 설계

김개발 씨는 드디어 직접 신경망을 설계해볼 차례입니다. 하지만 막상 시작하려니 막막했습니다.

"은닉층은 몇 개로 해야 하지? 뉴런은 몇 개가 적당하지?" 박시니어 씨가 실전 노하우를 전수해주기로 했습니다.

신경망 아키텍처 설계는 층의 수, 각 층의 뉴런 수, 활성화 함수 등을 결정하는 과정입니다. 마치 건물을 설계할 때 층수, 각 층의 크기, 구조를 정하는 것과 같습니다.

정해진 정답은 없지만, 실무에서 검증된 가이드라인과 실험을 통해 최적의 구조를 찾아나갑니다.

다음 코드를 살펴봅시다.

import numpy as np

def relu(x):
    return np.maximum(0, x)

def softmax(x):
    exp_x = np.exp(x - np.max(x))
    return exp_x / exp_x.sum()

class MNISTClassifier:
    """손글씨 숫자 인식을 위한 신경망 아키텍처"""

    def __init__(self):
        # 입력: 28x28 = 784 픽셀
        # 은닉층 1: 512 뉴런 (충분한 표현력)
        # 은닉층 2: 256 뉴런 (점점 줄어드는 피라미드 구조)
        # 출력: 10 (0-9 숫자)

        self.w1 = np.random.randn(784, 512) * 0.01
        self.b1 = np.zeros(512)
        self.w2 = np.random.randn(512, 256) * 0.01
        self.b2 = np.zeros(256)
        self.w3 = np.random.randn(256, 10) * 0.01
        self.b3 = np.zeros(10)

    def forward(self, x):
        # 은닉층 1: ReLU 활성화
        self.z1 = np.dot(x, self.w1) + self.b1
        self.a1 = relu(self.z1)
        # 은닉층 2: ReLU 활성화
        self.z2 = np.dot(self.a1, self.w2) + self.b2
        self.a2 = relu(self.z2)
        # 출력층: Softmax (확률 분포)
        self.z3 = np.dot(self.a2, self.w3) + self.b3
        return softmax(self.z3)

model = MNISTClassifier()
print("MNIST 분류 신경망 아키텍처: 784 -> 512 -> 256 -> 10")

"신경망 설계에는 왕도가 없어요." 박시니어 씨가 솔직하게 말했습니다. "하지만 수년간 쌓인 경험칙이 있습니다." 첫 번째 원칙은 피라미드 구조입니다.

입력층에서 출력층으로 갈수록 뉴런 수를 점점 줄이는 것이 일반적입니다. 위의 코드처럼 784 -> 512 -> 256 -> 10으로 줄어드는 구조입니다.

마치 정보를 점점 압축하면서 핵심만 추출하는 것과 같습니다. 두 번째는 활성화 함수 선택입니다.

은닉층에서는 주로 ReLU를 사용합니다. ReLU는 음수는 0으로 만들고 양수는 그대로 통과시킵니다.

계산이 간단하고 학습도 잘 됩니다. 출력층에서는 문제 유형에 따라 다릅니다.

분류 문제면 Softmax, 회귀 문제면 선형 활성화를 씁니다. 세 번째는 층의 수입니다.

간단한 문제는 은닉층 1-2개면 충분합니다. 복잡한 이미지 인식은 수십 층, 언어 모델은 수백 층까지 쌓습니다.

하지만 층이 많다고 무조건 좋은 것은 아닙니다. 과적합 위험이 있고, 학습도 어려워집니다.

박시니어 씨가 실전 팁을 알려주었습니다. "처음에는 작게 시작하세요.

은닉층 하나에 64개 뉴런으로 시작해서, 성능이 부족하면 점점 키우는 거예요." 코드를 보면 MNIST 손글씨 분류를 위한 전형적인 구조가 구현되어 있습니다. 28x28 이미지를 784개의 입력으로 펼치고, 두 개의 은닉층을 거쳐 10개의 숫자로 분류합니다.

forward 메서드에서 데이터가 어떻게 흘러가는지 주목하세요. 각 층에서 가중치를 곱하고 편향을 더한 다음, 활성화 함수를 통과시킵니다.

이 과정이 순전파입니다. 네 번째는 드롭아웃입니다.

학습할 때 일부 뉴런을 랜덤하게 끄는 기법입니다. 과적합을 방지하는 데 매우 효과적입니다.

실무에서는 거의 필수로 사용합니다. 다섯 번째는 배치 정규화입니다.

각 층의 출력을 정규화해서 학습을 안정시킵니다. 특히 깊은 네트워크에서 효과가 큽니다.

김개발 씨가 정리했습니다. "작게 시작해서 점점 키우고, ReLU 쓰고, 드롭아웃으로 과적합 방지하면 되는 거군요!" 박시니어 씨가 웃으며 대답했습니다.

"네, 그리고 가장 중요한 건 직접 실험해보는 것이에요. 아무리 이론을 알아도 데이터마다 최적의 구조가 다르니까요."

실전 팁

💡 - 시작은 작게: 은닉층 1-2개, 뉴런 64-128개로 시작하세요

  • ReLU + Dropout + BatchNorm 조합은 현대 딥러닝의 기본입니다
  • 하이퍼파라미터 튜닝은 실험이 답입니다

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#Python#NeuralNetwork#DeepLearning#Perceptron#AI#Python,DL,NeuralNetwork

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.

함께 보면 좋은 카드 뉴스