260312 [ Day 45 ] - ML, DL - Part 2 (4)

TaeHyun·2026년 3월 12일

TIL

목록 보기
171/185

시작하며

오늘부터 딥러닝 파트가 시작되었다. 딥러닝이 무엇인지에 대해 간단하게 배우고 딥러닝 데이터 처리를 위한 PyTorch를 배웠다.

딥러닝의 이해

머신러닝의 한계

  • 데이터에 내재된 패턴을 학습
  • 사람이 직접 특성을 설계해야 한다는 한계가 존재
    • 문제 해결에 어떤 특성이 필요한지 스스로 판단 못함
    • 특성 설계에 크게 의존

머신러닝의 도전과 해결책

  • 수학적 규칙을 기반으로 인간이 해결하기 어려운 형식적인 문제를 효과적으로 해결할 수 있도록 발전
  • 수학적 규칙으로 명확하게 서술하는 것이 어려운 문제에 한계가 있음
  • 한계 극복을 위해 개념 간의 연결 관계를 활용하여 데이터를 학습 → 스스로 개념을 학습
  • 여러 개의 층으로 구성된 구조를 형성

딥러닝의 출현

  • 딥러닝의 핵심 모델은 순방향 심층 신경망 또는 다층 퍼셉트론
    • 다층 퍼셉트론은 인간 뇌의 뉴런에서 영감을 받은 구조로 입력층과 출력층 사이에 은닉층을 포함
    • 각 층은 가중치 행렬과 편향을 사용하여 입력에 대해 선형 결합을 수행
    • 선형 결합으로 계산한 출력값은 활성화 함수를 거쳐 비선형 변환
    • 이 과정을 여러 층에 걸쳐 반복
  • 원시 데이터로부터 특징 집합을 추출 → 비정형 데이터 관련 문제를 효과적으로 해결

딥러닝의 핵심 발상

  • 사람이 특징을 정의하지 않고 모델이 스스로 특징을 학습
  • 원본 데이터로부터 시작 → 낮은 수준의 패턴에서 점점 추상적인 개념으로 나아가는 계층적 표현을 학습
    • 이미지 데이터 : 선과 모서리같은 단순한 패턴에서 점차 형태와 객체를 인식하는 구조가 만들어짐
    • 텍스트 데이터 : 문자 또는 토큰 단위의 패턴을 바탕으로 단어 의미와 문장 구조를 학습 → 문맥과 의도를 이해하는 추상적인 표현을 형성
    • 은닉층이 깊어질수록 모델은 단순한 패턴에서 복잡한 개념으로 점진적으로 조합

딥러닝 학습의 기본 원리

  • 순전파(Forward)
    • 입력과 가중치의 선형 결합
    • 활성화 함수(비선형 변환)
    • 예측값 출력(실수, 확률)
  • 역전파(Backward)
    • 손실 계산
    • 손실 함수의 기울기 계산
    • 가중치 업데이트

머신러닝과 딥러닝의 역할 분담 차이

구분머신러닝딥러닝
특징 생성사람이 직접 설계모델이 자동 학습
데이터 요구량비교적 적음매우 많음
해석 가능성상대적으로 높음매우 낮음
강점 영역정형 데이터비정형 데이터

파이토치 기초

딥러닝 프레임워크의 역할

  • 딥러닝은 선형대수 기반의 대규모 수치 연산을 반복 수행
    • 순전파 : Forward propagation
    • 손실 계산 : Loss computation
    • 역전파 : Backpropagation
    • 가중치 업데이트 : Optimization
  • 해당 과정을 효율적으로 수행하기 위해 자동 미분(역전파)과 GPU 연산을 진원하는 전용 도구 필요 → 딥러닝 프레임워크(대표적으로 TensorFlow, PyTorch)

TensorFlow

  • Google Brain 팀에서 개발한 딥러닝 프레임워크
    • 대규모 분산 학습과 산업 환경 배포를 목표로 설계
  • 초기에는 정적 계산 그래프(Static Graph) 방식을 사용
    • 연산 구조 정의 → 실제 계산 실행
  • Keras 통합 이후 즉시 실행을 기본으로 지원
    • 고수준 API를 통해 모델 구현을 단순화 등 연구 및 교육 환경에서도 사용성이 개선
  • 기본 자료형 : Tensor
    • 다차원 배열 구조를 가지며, CPU와 GPU 연산을 모두 지원
    • 대규모 서비스 환경을 고려한 생태계를 갖추고 있음

PyTorch

  • Facebook AI Research가 Lua 기반 Torch 라이브러리의 설계 철학을 계승
    • Python 환경에 맞게 개발한 딥러닝 프레임워크
  • 초기에는 NumPy와 유사
    • GPU(CUDA) 지원과 신경망 모듈이 추가되면서 딥러닝 연구에 널리 활용

TensorFlow vs PyTorch

구분TensorFlowPyTorch
개발 주체Google BrainMeta (FAIR)
초기 계산 방식정적 그래프동적 그래프
현재 실행 방식2.x부터 즉시 실행 기본 지원동적 그래프 기본
코드 흐름구조 정의 후 실행Python 실행 흐름과 동일
산업 배포Serving, Lite 등 강점점점 강화 중
연구 활용가능연구 환경에서 매우 활발
학습 곡선체계적 구조 이해 필요비교적 직관적
  • Tensor
    • 3차원 텐서의 기본 형태 : (Channel, Height, Width)
    • 4차원 텐서의 형태 : (N, C, H, W)
      • N : 이미지 장수
      • B : 배치 크기

NumPy 배열과 PyTorch 텐서의 차이점

  • 모두 다차원 수치 데이터를 표현
  • 계산 그래프 : 수치 연산의 순서와 의존 관계를 노드와 엣지로 표현한 그래프 구조를 의미
구분다차원 구조수치 연산GPU 사용자동 미분계산 그래프학습 대상
배열가능가능불가능불가능없음불가능
텐서가능가능가능가능있음가능

텐서의 자동 미분 예시

import torch
  • requires_grad : True 지정 시 연산 기록이 계산 그래프로 저장
x = torch.tensor(data=[2.0], requires_grad=True)
y = x ** 2
  • y 역전파 이후 x의 기울기 확인
y.backward()
x.grad
# tensor([4.])

NumPy 배열과 PyTorch 텐서의 주요 코드 차이

구분NumPy 배열PyTorch 텐서
차원 수ar.ndimts.ndim
형태(구조)ar.shapets.shape / ts.size()
전체 원소 개수ar.sizets.numel()
자료형 확인ar.dtypets.dtype
자료형 변환ar.astype(dtype)ts.to(dtype) / ts.float() / ts.long()
차원 추가(축)np.expand_dims(a=ar, axis=0)ts.unsqueeze(dim=0)
차원 제거np.squeeze(a=ar)ts.squeeze()
형태 변경ar.reshape()ts.reshape() / ts.view()
평탄화ar.flatten() / ar.ravel()ts.flatten() / ts.ravel()
차원 변경-ts.permute() / ts.transpose()

0차원 텐서 생성

a = 1.0
  • 실수 스칼라를 원소로 갖는 0차원 텐서 생성
ts0 = torch.tensor(a)
# tensor(1.)
  • 0차원 텐서 확인
type(ts0)
# torch.Tensor
ts0.ndim
# 0
ts0.shape
# torch.Size([])
ts0.numel()
# 1
ts0.dtype
# torch.float32

1차원 텐서 생성

li1 = [1, 2, 3]
li1 = [1, 2.0, 3]
li1 = [1, 2.0, '3']

ar1 = np.array(li1)
# array([1, 2, 3])
# array([1., 2., 3.])
# array(['1', '2.0', '3'], dtype='<U32')

ts1 = torch.tensor(li1)
# tensor([1, 2, 3])
# tensor([1., 2., 3.])
# 오류 - 파이토치 텐서는 수치형 원소만 가질 수 있음
  • 1차원 텐서 확인
type(ts1)
# torch.Tensor
ts1.ndim
# 1
ts1.shape
# torch.Size([3])
ts1.numel()
# 3
ts1.dtype
# torch.int64

PyTorch의 자료형을 지정하는 dtype의 종류

구분자료형메서드설명비고
정수형torch.int16 / torch.shortts.short()16비트 정수거의 사용 안 함 (제한된 지원)
torch.int32 / torch.intts.int()32비트 정수일부 CPU 연산에 사용 (단순 카운팅, 인덱스 계산)
torch.int64 / torch.longts.long()64비트 정수PyTorch 기본 정수형 (라벨, 인덱스에 사용)
실수형torch.float16 / torch.halfts.half()16비트 실수 (half precision)빠른 계산은 float16, 정확한 계산은 float32
torch.float32 / torch.floatts.float()32비트 실수 (single precision)PyTorch 기본 실수형 (모델의 입력, 가중치에 사용)
torch.float64 / torch.doublets.double()64비트 실수 (double precision)거의 사용 안 함 (느리고 메모리 낭비)

1차원 텐서 원소의 자료형 변환

  • float32로 변환
ts1.to(torch.float)
# tensor([1., 2., 3.])
ts1.to(torch.float).dtype
# torch.float32
ts1 = ts1.float()
ts1.dtype
# torch.float32
  • int64로 변환
ts1.to(torch.long)
# tensor([1, 2, 3])
ts1.to(torch.long).dtype
# torch.int64
ts1 = ts1.long()
ts1.dtype
# torch.int64

2차원 텐서 생성

li2 = [[1, 3, 5], [7, 9, 11]]
  • 리스트를 2차원 텐서로 변환
ts2 = torch.tensor(li2)
# tensor([[ 1,  3,  5],
#         [ 7,  9, 11]])
  • 2차원 텐서 확인
type(ts2)
# torch.Tensor
ts2.ndim
# 2
ts2.shape
# torch.Size([2, 3])
ts2.numel()
# 6
ts2.dtype
# torch.int64

텐서의 재구조화

ts1 = torch.arange(1, 12, 2)
# tensor([ 1,  3,  5,  7,  9, 11])
  • 2차원 텐서로 변환
ts2 = ts1.reshape(2, 3)
# tensor([[ 1,  3,  5],
#         [ 7,  9, 11]])
  • 1차원 텐서로 변환
    • flatten
      • 항상 새로운 텐서를 생성
      • 전체 원소를 1차원으로 평탄화 → 배치 크기를 고려하지 못함
ts2.flatten()
# tensor([ 1,  3,  5,  7,  9, 11])
  • 배치 크기를 유지한 상태로 평탄화하려면 reshape 메서드 사용
img_batch.shape
# torch.Size([128, 3, 32, 32])
img_batch.flatten().shape
# torch.Size([393216])
img_batch.reshape(128, -1).shape
# torch.Size([128, 3072])

무작위 값을 갖는 텐서 생성

  • 난수 시드 고정
torch.manual_seed(1)
  • 0~1 범위의 무작위 실수를 원소로 갖는 텐서 생성
torch.rand(3, 2)
# tensor([[0.3398, 0.5239],
#         [0.7981, 0.7718],
#         [0.0112, 0.8100]])
  • 지정한 범위의 무작위 정수를 원소로 갖는 텐서 생성
torch.randint(0, 5, (3, 2))
# tensor([[3, 1],
#         [2, 3],
#         [2, 3]])
  • 표준정규분포를 따른는 무작위 실수를 원소로 갖는 텐서 생성
torch.randn(1, 28, 28)

텐서의 브로드캐스팅

ts1 = torch.tensor([0, 1, 2])
ts1 + 1
# tensor([1, 2, 3])
ts2 = ts1.reshape(-1, 1)
ts2 * 2
# tensor([[0],
#         [2],
#         [4]])

차원 추가 및 제거

  • 0차원 축 방향으로 차원 추가
ts1.unsqueeze(dim=0)
# tensor([[0, 1, 2]])
ts1.unsqueeze(dim=0).shape
# torch.Size([1, 3])
  • 크기가 1인 차원을 제거
ts2.squeeze(dim=1)
# tensor([0, 1, 2])
ts2.squeeze(dim=1).shape
# torch.Size([3])

텐서의 결합

x1 = torch.tensor([0, 1, 2])
x2 = torch.tensor([3, 4, 5])

torch.stack(tensors=[x1, x2], dim=0)
# tensor([[0, 1, 2],
#         [3, 4, 5]])
torch.stack(tensors=[x1, x2], dim=1)
# tensor([[0, 3],
#         [1, 4],
#         [2, 5]])
gray_img_1 = torch.randn(1, 28, 28)
gray_img_2 = torch.randn(1, 28, 28)

img_batch_1 = torch.stack(tensors=[gray_img_1, gray_img_2], dim=0)
img_batch_1.shape
# torch.Size([2, 1, 28, 28])

텐서 입력 형태 변환

ts1
# tensor([0, 1, 2])
  • 0번 축 방향으로 차원을 두 번 추가
ts1 = ts1.unsqueeze(dim=0).unsqueeze(dim=0)
ts1.shape
# torch.Size([1, 1, 3])
  • 축 배치 변경 : permute
    • 차원 축을 일괄로 변경
    • np.transpose() 가 동일한 기능을 수행
ts1.permute(0, 2, 1).shape
# torch.Size([1, 3, 1])
ts1.permute(2, 0, 1).shape
# torch.Size([3, 1, 1])
  • 축 교환 : transpose
    • 2개의 차원을 서로 교환
    • np.swapaxes() 가 동일한 기능을 수행
ts1.transpose(1, 2).shape
# torch.Size([1, 3, 1])
ts1.transpose(0, 2).shape
# torch.Size([3, 1, 1])

딥러닝을 위한 기초 수학

딥러닝에서 수학의 필요성

  • 딥러닝에서는 매우 단순한 1차식(선형 결합)을 반복적으로 사용
    • 입력값에 가중치를 곱하고 편향을 더하는 산술 계산의 연속
  • 입력값을 순방향으로 전달하며 선형 결합과 활성화 함수를 통과
    • 손실을 최소화하기 위해 다시 역방향으로 미분하여 가중치를 반복적으로 최신화
    • 함수의 합성, 미분, 편미분 등 기초 수학 개념에 대한 이해가 필수적

함수의 합성

  • 딥러닝 모델은 하나의 함수가 아니라 함수의 합성으로 표현
  • f(x)=f2(f1(x))f(x) = f_2(f_1(x))
f1 = lambda x: 2 * x + 1
f2 = lambda x: 3 * x - 2

x = 3
f2(f1(x)) # 19
f1 = lambda x1, x2, x3: 2 * x1 + 1.5 * x2 + (-1.2) * x3 + 0.5
f2 = lambda x1: 3 * x1 - 1.2

x1, x2, x3 = 1, 1, 0
f2(f1(x1, x2, x3)) # 10.8

미분 / 변화율과 민감도

  • 딥러닝에서 미분은 기울기 계산 이상의 의미를 갖음
  • dydx\frac{dy}{dx} = 입력이 조금 변할 때 출력이 얼마나 변하는가
def f(x):
    return x ** 2
x, h = 3, 1e-5

(f(x + h) - f(x)) / h
# 6.000009999951316

다양한 함수의 미분

구분함수미분 공식
상수함수f(x)=cf(x) = cddxf(x)=0\frac{d}{dx} f(x) = 0
다항함수f(x)=xnf(x) = x^nddxf(x)=nxn1\frac{d}{dx} f(x) = nx^{n-1}
로그함수f(x)=lnxf(x) = \ln{x}
f(x)=logaxf(x) = \log_a{x}
ddxf(x)=1x\frac{d}{dx} f(x) = \frac{1}{x}
ddxf(x)=1xlna\frac{d}{dx} f(x) = \frac{1}{x \ln{a}}
지수함수f(x)=exf(x) = e^x
f(x)=axf(x) = a^x
ddxf(x)=ex\frac{d}{dx} f(x) = e^x
ddxf(x)=axlna\frac{d}{dx} f(x) = a^x \ln{a}

편미분의 직관적 이해

  • 편미분은 하나의 가중치만 변화시켰을 때, 출력값이 얼마나 빠르게 변하는지를 나타냄
  • z=w1x1+w2x2+bz = w_1x_1 + w_2x_2 + bzw1=x1\frac{\partial z}{\partial w_1} = x_1 , zw2=x2\frac{\partial z}{\partial w_2} = x_2
    • w1w_1을 증가시키면 출력값 zz는 그 증가량에 x1x_1을 곱한 크기만큼 증가
x1, x2 = 2.0, 3.0
w1, w2 = 0.5, -1.0

w1 * x1 + w2 * x2 # -2.0
(w1 + 0.1) * x1 + w2 * x2 # -1.8

점곱

  • 가중치와 입력값의 선형 결합은 점곱 연산의 결과
  • wx=i=1nwixiw \cdot x = \sum^{n}_{i=1} w_ix_i
    • 대응하는 원소끼리 곱셈하여 모두 더함
  • 배열의 점곱
import numpy as np

w = np.array([0.5, -1.0])
x = np.array([2.0, 3.0])

np.dot(w, x).item() # -2.0
  • 텐서의 점곱
import torch

w = torch.tensor([0.5, -1.0])
x = torch.tensor([2.0, 3.0])

torch.dot(w, x).item() # -2.0

연쇄 법칙의 필연성

  • 딥러닝 모델은 합성 함수를 사용 → 미분의 연쇄 법칙을 사용
  • dydx=dydzdzdx\frac{dy}{dx} = \frac{dy}{dz} \cdot \frac{dz}{dx}
    • 이 계산이 층을 거슬러 반복적으로 저용되는 것이 역전파
x = 2.0
z = 2 * x + 1
y = 3 * z - 2

dy_dz = 3
dz_dx = 2
dy_dz * dz_dx # 6

활성화 함수의 수학적 역할

  • 활성화 함수는 대부분의 구간에서 미분 가능하며 기울기를 전달하는 역할을 수행
def relu(x):
    return np.maximum(0, x)
    
x = np.array([-2.0, 1.0, 3.0])
relu(x) # array([0., 1., 3.])

소프트맥스

  • 다중 분류 문제에서 각 클래스별 점수(로짓)를 확률로 변환할 때 필요
  • Pik=exp(zik)j=1Kexp(zij)P_{ik} = \frac{\exp(z_{ik})}{\sum^{K}_{j=1} \exp(z_ij)}
    • PikP_{ik}는 i번째 관측값이 k번째 클래스에 속할 확률
    • zikz_{ik}는 마지막 은닉층의 출력이 출력층 가중치와 선형 결합되어 계산된 값
    • 지수 함수를 사용하면 로짓의 차이를 로그 확률 비로 표현 가능
z = np.array([2.0, 1.0, -0.1])

z = z - np.max(z)
exp_z = np.exp(z)
softmax = exp_z / np.sum(exp_z)

softmax # array([0.67098969, 0.24684331, 0.082167  ])
np.sum(softmax) # 1.0

손실 함수

  • 딥러닝에서 학습 목표는 손실 함수를 최소화하는 방향으로 가중치를 조정하는 것
  • L=1ni=1n(yiyi^)2L = \frac{1}{n} \sum^n_{i=1} (y_i - \hat{y_i})^2

크로스 엔트로피

  • 다중 분류 문제에서 손실 함수는 크로스 에늩로피를 주로 사용
  • Li=k=1Kyiklog(pik)L_i = - \sum^K_{k=1} y_{ik} \log{(p_{ik})}
y_true = np.array([1.0, 0.0, 0.0])
y_prob = np.array([0.6710, 0.2468, 0.0822])
  • 확률은 0과 1사이의 실수라 로그를 씌우면 항상 음수가 됨
np.log(y_prob) # array([-0.39898614, -1.39917699, -2.49859998])
  • 손실 함수는 값이 작을수록 좋으므로 로그 확률의 부호를 바꿔야 함
-np.dot(y_true, np.log(y_prob)) # 0.3989861420104552

마치며

수업 중에 계속해서 수식을 접하니까 이제 간단한 수식들은 보면 바로 이해가 되는 것 같다. 물론 수식을 적용해서 문제를 풀거나 하지는 못하겠지만 수식을 통해 해당 개념을 더 잘 이해할 수 있게 되는 것 같아서 도움이 되는 것 같다. 내일부터는 이미지 데이터를 다루는 작업을 시작하는데 상당히 재밌을 것 같아서 기대가 된다.

profile
Hello I'm TaeHyunAn, Currently Studying Data Analysis

0개의 댓글