1. 딥러닝 핵심 4요소 (Neuron / Weight / Activation / Loss)

1.1 뉴런(Neuron): “입력들을 모아 계산해서 출력”

생물학적 뉴런이 신호를 받아 다음 뉴런으로 보내듯, 인공 뉴런도 입력을 받아 계산 후 출력한다.

  • 입력: x1,x2,x3,...x_1, x_2, x_3, ...
  • 입력: w1,w2,w3,...w_1, w_2, w_3, ...
  • 편향(bias): b

1.2. 가중치(Weight): “입력의 중요도”

같은 입력이라도 어떤 입력은 더 중요하게 반영되어야 한다.

딥러닝 학습의 본질은 결국,

좋은 예측을 만들도록 𝑤𝑤𝑏𝑏를 찾아가는 과정이다.


1.3. 활성화 함수(Activation Function): “비선형성을 넣는 장치”

활성화 함수가 없으면 층을 아무리 많이 쌓아도 결국 전체가 “선형 함수”로 귀결된다.

  • 선형만 쌓이면: 복잡한 패턴을 못 배움
  • 비선형(활성화)을 넣으면: 복잡한 경계/패턴 학습 가능

1.3.1. 활성화 함수 총정리 (Step / Sigmoid / Tanh / ReLU / Softmax)

Step function (계단 함수)

  • 퍼셉트론의 “원형”에 가까운 함수.
  • 출력: 0 또는 1
  • 장점: 개념이 직관적(문턱 넘으면 1)
  • 치명적 단점: 미분이 사실상 불가능/학습(gradient 기반 최적화)에 부적합

그래서 현대 딥러닝은 은닉층에서 step을 거의 안 쓰고, 미분 가능한 함수로 넘어간다.

Sigmoid

  • 출력이 0~1이라 확률 해석이 쉬움 → 이진 분류 출력층에서 자주 연결
  • 단점: Gradient Vanishing 문제(아래에서 자세히)

tanh

  • 출력 범위: -1 ~ 1
  • Sigmoid보다 0 중심(zero-centered) 이라 학습이 더 안정적인 경우가 있음
  • 단점: Sigmoid처럼 큰 값에서 포화 → vanishing gradient 가능

ReLU (은닉층 기본 선택)

  • ReLU(z)=max(0,z)
  • 장점: 계산 간단, 양수 구간에서 기울기 유지 → 깊은 신경망에서 학습이 잘 되는 편
  • 단점: 음수 구간에 오래 머무르면 Dead ReLU(학습이 멈춘 뉴런) 가능
  • 실전 팁: ReLU가 죽는 게 신경 쓰이면 LeakyReLU 같은 변형을 고려하지만, 기본은 ReLU부터 시작하는 게 보통이다.

Softmax (다중 분류 출력층)

  • 여러 클래스 점수를 확률분포로 변환(합이 1)
  • 사용처: “10개 질병 중 하나” 같은 다중 분류
  • softmax 함수 정의
  • 추가 실전 팁(숫자 안정성): softmax는 overflow 방지 위해 보통 max를 빼고 계산한다(확률 비율은 유지. 지수 폭발 방지에 매우 중요)

한방에 정리

함수출력 범위장점단점
Sigmoid0 ~ 1확률 해석 가능기울기 소실
ReLU0 ~ inf빠른 학습, 간단Dying ReLU
Tanh-1 ~ 10 중심기울기 소실
Leaky ReLU-inf ~ infDying ReLU 해결alpha 튜닝 필요

1.4. 손실 함수(Loss Function): “얼마나 틀렸는지 점수”

예측값과 정답의 차이를 수치화한 것이 손실이고, 학습은 손실을 줄이는 방향으로 진행된다.
즉, 손실 함수x축w(weight)이고, y축Loss(손실)인 함수이고,
우리의 목적은 Loss가 최소가 되는 w(weight)를 찾는 것이다.



2. 퍼셉트론(Perceptron): 신경망의 가장 기본 모델

퍼셉트론“입력 × 가중치 → 합 → 활성화 → 출력” 구조다.

2.1 퍼셉트론의 계산식

여기서 bias 𝑏𝑏는 “기준선(절편)”이다.
직관적으로는 “합산 점수가 이 정도는 넘어야 양성” 같은 기본 문턱값 역할.

퍼셉트론은 선형 분류기(직선/평면으로 나누는 모델)라는 점이 핵심이다.


2.2 AND / OR은 되는데 XOR은 왜 안 되나?

2.2.1 선형 분리(linearly separable)

  • AND, OR: 한 직선으로 0과 1을 나눌 수 있음 → 단일 퍼셉트론으로 가능
  • XOR: 한 직선으로는 절대 분리 불가 → 단일 퍼셉트론으로 불가능

이게 유명한 “단일 퍼셉트론의 한계”다.
XOR 같은 선형 분리 불가능 문제를 풀려면 여러 층(MLP) 이 필요하다.

결론: 층을 쌓는 이유 = 선형으로 못 푸는 문제를 비선형 변환으로 풀기 위해


2.3. 퍼셉트론 학습: “틀리면 조금 수정”

퍼셉트론 학습 규칙은 이런 형태로 요약된다:

비유로 보면,

  • 학습률이 너무 크면: 한 번에 과하게 움직여서 목표를 지나치고 흔들림
  • 너무 작으면: 맞는 방향인데 속도가 너무 느림




3. Gradient Vanishing 문제: “기울기가 사라져서 앞층이 업데이트가 안 됨”

  • Sigmoid의 도함수(기울기)는 최대가 0.25 근처
    → 깊어질수록 역전파에서 기울기가 계속 곱해지며 작아짐
    → 앞쪽 층 가중치가 거의 안 바뀜
    → 학습 정체

그래서 실전 가이드가 이렇게 정리된다:

  • 은닉층: ReLU 우선
  • 이진 분류 출력: Sigmoid
  • 다중 분류 출력: Softmax

ReLU가 도움이 되는 이유는 연산이 간단하여 속도가 빠르기도 하고,
양수 구간에서 기울기가 1로 유지되는 구간이 있어(포화가 덜해서)
깊은 층에서도 학습 신호가 비교적 잘 전달되기 때문이다.




4. PyTorch 소개: “딥러닝을 구현하는 도구 세트”

PyTorch는 Python을 위한 오픈소스 머신 러닝 라이브러리이다.
Torch를 기반으로 하며, 자연어 처리와 같은 애플리케이션을 위해 사용된다.
GPU사용이 가능하기 때문에 속도가 상당히 빠르다. 이번 챕터에서 중요한 키워드는 다음 4개였다.

  • Tensor: 딥러닝의 기본 데이터 단위(다차원 배열)
  • GPU 지원: 큰 행렬 연산을 빠르게
  • 동적 계산 그래프: 실행하면서 그래프가 만들어지는 방식(Define-by-Run)
  • Autograd(자동 미분): 기울기 계산을 자동으로

특히 Autograd는 PyTorch 학습의 심장이다.


4.1. Tensor 기초: 차원과 shape 감각 잡기

Tensor는 “다차원 배열”이고, 차원별 의미를 감각적으로 잡는 게 중요하다.

  • 0D: scalar (예: 체온 한 값)
  • 1D: vector (예: 검사 결과 3개)
  • 2D: matrix (예: 환자 여러 명의 검사표)
  • 3D/4D: 이미지/CT 배치 등(헬스케어에서 자주 등장)

PyTorch에서 Tensor 연산은 거의 NumPy처럼 진행된다(덧셈/곱셈/행렬곱/reshape 등).
관련 메서드들이 torch.Tensor에 정리되어 있다.


4.2. Tensor 생성: torch.tensor() vs torch.Tensor()

수업에서 강조된 포인트가 하나 있다.

  • torch.tensor(5) : 값 “5”를 담은 텐서를 만든다 (의도 명확)
  • torch.Tensor(5) : 종종 “값 5”가 아니라 크기 5짜리(초기화되지 않은) 텐서로 동작할 수 있음

실전에서는 torch.tensor()(소문자) 또는 torch.zeros, torch.ones, torch.randn 같은 함수를 쓰는 습관이 안전하다.

코드 예시

import torch

# -----------------------------------
방법1) 직접 생성
# -----------------------------------

# 스칼라 (0차원)
x = torch.tensor(5)    # x = torch.Tensor(5)이거랑 다름 주의!!!
print(x)  # tensor(5)

# 벡터 (1차원)
v = torch.tensor([1, 2, 3])
print(v)  # tensor([1, 2, 3])
print(v.shape)  # torch.Size([3])

# 행렬 (2차원)
m = torch.tensor([[1, 2, 3],
                  [4, 5, 6]])
print(m.shape)  # torch.Size([2, 3])

# -----------------------------------
방법2) 특수 Tensor 생성
# -----------------------------------

# 모두 0으로 채운 Tensor
zeros = torch.zeros(3, 4)  # 3x4 행렬

# 모두 1로 채운 Tensor
ones = torch.ones(2, 3)  # 2x3 행렬

# 랜덤 값으로 채운 Tensor
rand = torch.rand(3, 3)  # 0~1 사이 랜덤

# 정규분포 랜덤
randn = torch.randn(3, 3)  # 평균 0, 표준편차 1

# -----------------------------------
방법3) NumPy에서 변환
# -----------------------------------

import numpy as np

# NumPy 배열 생성
np_array = np.array([1, 2, 3])

# PyTorch Tensor로 변환
tensor = torch.from_numpy(np_array)

# 다시 NumPy로 변환
back_to_np = tensor.numpy()

4.3. 기본 연산: 원소 연산 vs 행렬 곱

코드 예시


# -----------------------------------
1) 기본 연산
# -----------------------------------

a = torch.tensor([1, 2, 3])
b = torch.tensor([4, 5, 6])

# 덧셈
print(a + b)  # tensor([5, 7, 9])

# 뺄셈
print(a - b)  # tensor([-3, -3, -3])

# 곱셈 (원소별)
print(a * b)  # tensor([4, 10, 18])

# 나눗셈
print(a / b)  # tensor([0.25, 0.4, 0.5])

# -----------------------------------
2) 행렬 곱
# -----------------------------------

# 2x3 행렬
A = torch.tensor([[1, 2, 3],
                  [4, 5, 6]])

# 3x2 행렬
B = torch.tensor([[1, 2],
                  [3, 4],
                  [5, 6]])

# 행렬 곱 (2x3) @ (3x2) = (2x2)
C = A @ B  # 또는 torch.matmul(A, B)
print(C.shape)  # torch.Size([2, 2])

# -----------------------------------
3) Shape 변경
# -----------------------------------

x = torch.tensor([[1, 2, 3],
                  [4, 5, 6]])  # shape: (2, 3)

# Reshape
y = x.reshape(3, 2)  # shape: (3, 2)
y = x.reshape(6)     # shape: (6,) - 1차원으로

# View (메모리 공유)
z = x.view(3, 2)

# 차원 추가/제거
x_unsqueeze = x.unsqueeze(0)  # shape: (1, 2, 3)    # unsqueeze(dim) : 차원 추가 (예: 배치 차원 만들기)
x_squeeze = x_unsqueeze.squeeze(0)  # shape: (2, 3)    # squeeze(dim) : 크기가 1인 차원 제거

4.4. GPU 사용: 모델/텐서를 같은 device로 보내기

핵심은 “텐서가 있는 곳에서 연산이 수행된다”는 점이다.

GPU 사용 코드

# GPU 사용 가능 확인
print([torch.cuda.is](http://torch.cuda.is)_available())  # True/False

# 디바이스 설정
device = torch.device('cuda' if [torch.cuda.is](http://torch.cuda.is)_available() else 'cpu')

# Tensor를 GPU로 이동
x = torch.tensor([1, 2, 3])
x = [x.to](http://x.to)(device)

# 모델을 GPU로 이동
model = [model.to](http://model.to)(device)

모델이 cuda에 올라갔는지 확인할 땐 next(model.parameters()).is_cuda 같은 방식도 흔히 쓴다.


4.5. Autograd: “기울기 계산을 자동으로”

딥러닝 학습은 손실을 가중치로 미분해야 하는데, PyTorch는 그걸 Autograd가 자동으로 해준다.

  • requires_grad=True : “이 텐서에 대해 기울기 추적해줘”
  • .backward() : 계산 그래프를 따라 역전파 수행
  • x.grad : d(loss)/dxd(loss)/dx 저장

Autograd가 “계산 그래프(computational graph)”를 만들고,
역전파로 gradient를 계산한다.


4.6 학습의 실제 흐름(딥러닝 루프)

실전 학습은 거의 이 패턴으로 고정된다.

  • prediction = model(input)
  • loss = loss_function(prediction, target)
  • loss.backward() ← gradient 계산
  • optimizer.step() ← 가중치 업데이트
    (+ 보통 optimizer.zero_grad() 로 기울기 초기화)

이 흐름을 기억하는게 가장 중요하다.

4.6.1. 간단 실습 예시: 선형회귀로 “y = 2x + 1” 학습하기

import torch
import torch.nn as nn

# 데이터 준비
X = torch.tensor([[1.], [2.], [3.], [4.]])
y = torch.tensor([[3.], [5.], [7.], [9.]])  # y = 2x + 1

# Step 1. 모델 정의 (Linear: y = wx + b)
model = nn.Linear(1, 1)

# Step 2.손실 함수 & 옵티마이저
criterion = nn.MSELoss()  # 평균 제곱 오차
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

# Step 3.학습 루프
for epoch in range(1000):
    # Step 3-1.예측
    pred = model(X)

    # Step 3-2.손실 계산
    loss = criterion(pred, y)

    # Step 3-3.기울기 초기화 & 계산 & Step 3-4.업데이트
    [optimizer.zero](http://optimizer.zero)_grad()
    loss.backward()
    optimizer.step()

# 학습된 가중치 확인
print(f"w: {model.weight.item():.2f}, b: {model.bias.item():.2f}")
# w: 2.00, b: 1.00 에 가까워짐!




[과제 회고]

  • perceptron()> 0일 때 1을 리턴하는 구조이고, 마진(결정 경계(z=0)와의 거리)이 0.2여서 지금은 문제가 없음.
  • perceptron()>= 0일 때 1을 리턴하는 구조라면, (1,1)에서 s가 정확히 0이 되는 값을 쓰면 애매해짐.
  • 가장 안전한 방법은 b를 “bias”가 아닌 “threshold(임계값)”로 구현 하는 것
    (ex. return 1 if (w1*x1 + w2*x2) > b else 0 의 형태)

최대값을 빼는 이유: overflow 방지(안전장치)

  • 값이 작을 땐 문제가 되지 않지만, 값이 커지면 폭발(overflow)
  • 여기에 x에 max(x)를 뺀다는건
    결국 분모, 분자 모두에 e^(-max(x))가 곱해져 있는 것이기 때문에 소거 가능
    (즉, 결과 값에는 아무 영향도 미치지 않는다는 말임)
  • x-max(x)가 가질 수 있는 최대값은 0.
    (즉, e^x의 최대값은 1이라는 소리. 최소값은 0에 매우 가까운 양수가 된다.)
profile
R&D 분야의 경험을 토대로 커리어 확장에 도전중인 개발꿈나무입니다.

0개의 댓글