[NLP] CS224N 22강 정리 [PyTorch Tutorial]

김성윤(Jack)·2025년 9월 5일

NLP

목록 보기
24/35

강의 예시 코드 \Rarr https://colab.research.google.com/drive/1Pz8b_h-W9zIBk1p2e6v-YFYThG1NkYeS?usp=sharing

1. PyTorch와 텐서(Tensors) 소개

PyTorch란?

  • PyTorch는 딥러닝 모델을 만들고 학습시키는 데 사용되는 강력한 오픈소스 라이브러리입니다.
  • 주요 목표는 두 가지입니다.
    1. **텐서(Tensor)**를 쉽게 생성하고 조작하며, GPU를 활용한 빠른 연산을 지원하는 것.
    2. 선형 계층(Linear Layer), 손실 함수(Loss Function) 등 미리 정의된 모듈을 제공하여 **신경망(Neural Network)**을 쉽게 구성할 수 있도록 돕는 것.
import torch
import torch.nn as nn # 신경망 모듈

텐서 (Tensors)

  • 텐서는 PyTorch에서 데이터를 표현하는 가장 기본적인 단위로, 다차원 배열입니다. NumPy의 ndarray와 매우 유사합니다.
  • 신경망의 입력, 출력, 그리고 가중치(weights) 등 모든 데이터는 텐서 형태로 표현됩니다.
  • 예시: 컬러 이미지는 (채널, 높이, 너비)의 3차원 텐서로, 이미지 배치는 (배치 크기, 채널, 높이, 너비)의 4차원 텐서로 표현될 수 있습니다.

2. 텐서의 생성과 조작

텐서 생성

  • Python의 리스트(list)를 torch.tensor() 함수에 전달하여 간단하게 텐서를 생성할 수 있습니다.
  • torch.zeros(), torch.ones(), torch.arange() 와 같은 유틸리티 함수를 사용하면 특정 형태와 값을 가진 텐서를 쉽게 만들 수 있습니다.
# 리스트로부터 텐서 생성
t1 = torch.tensor([[1, 2, 3], [4, 5, 6]])
print(t1)

# 0으로 채워진 2x3 텐서 생성
t2 = torch.zeros(2, 3)
print(t2)

# 1로 채워진 2x3 텐서 생성
t3 = torch.ones(2, 3)
print(t3)

# 0부터 9까지의 숫자를 가진 텐서 생성
t4 = torch.arange(10)
print(t4)

텐서의 연산

  • 덧셈, 곱셈과 같은 기본 연산은 **요소별(element-wise)**로 적용됩니다.
  • torch.matmul() 또는 @ 연산자를 사용하여 행렬 곱셈을 수행할 수 있습니다. 이는 신경망의 핵심 연산입니다.
  • 텐서의 모양(shape)은 .shape 또는 .size() 속성으로 확인할 수 있으며, 디버깅에 매우 중요합니다.
x = torch.tensor([[1, 2], [3, 4]])
y = torch.tensor([[5, 6], [7, 8]])

# 요소별 덧셈
print(x + y)

# 행렬 곱셈
print(torch.matmul(x, y))
# 혹은 @ 연산자 사용
print(x @ y)

# 텐서 모양 확인
print(f"Shape of x: {x.shape}")

텐서 재구성 (Reshaping)

  • .reshape() 함수를 사용하여 텐서의 모양을 원하는 대로 변경할 수 있습니다. 이는 데이터를 배치(batch) 단위로 처리하거나 계층(layer)의 입력 형식에 맞출 때 필수적입니다.
  • .item() 메소드를 사용하면 텐서에 담긴 단일 값(scalar)을 일반 Python 숫자로 추출할 수 있습니다.
# 1x15 텐서를 3x5 텐서로 재구성
a = torch.arange(15)
b = a.reshape(3, 5)
print(b)
print(b.shape)

# 단일 값 추출
scalar_tensor = torch.tensor(10)
python_number = scalar_tensor.item()
print(python_number)

텐서 인덱싱 (Indexing)

  • NumPy와 동일한 방식으로 텐서의 특정 부분에 접근하거나 값을 잘라낼(slicing) 수 있습니다.
# 2x3x4 텐서 예시
X = torch.arange(24).reshape(2, 3, 4)
print(X)

# 첫 번째 배치(2x3)의 모든 요소 선택
print(X[0, :, :])

# 두 번째 배치의 두 번째 행의 세 번째 열 요소 선택
print(X[1, 1, 2])

3. 자동 미분 (Autograd)

Autograd의 역할

  • **자동 미분(Autograd)**은 PyTorch가 신경망 학습을 위해 **기울기(gradient)**를 자동으로 계산해주는 핵심 기능입니다.
  • 텐서를 생성할 때 requires_grad=True 로 설정하면, PyTorch는 해당 텐서에 대한 모든 연산을 추적하여 계산 그래프를 만듭니다.
  • 이후 손실(loss)에 대해 .backward() 함수를 호출하면, 연쇄 법칙(chain rule)에 따라 그래프를 거슬러 올라가며 각 파라미터에 대한 기울기를 자동으로 계산합니다.

기울기 계산 예시

  • y = 3x^2 라는 수식이 있을 때, x=2에서의 기울기는 6x이므로 12가 됩니다. PyTorch는 이 과정을 자동으로 수행합니다.
# x = 2.0, requires_grad=True로 설정하여 연산 추적 시작
x = torch.tensor(2.0, requires_grad=True)

# 수식 정의
y = 3 * x**2

# y에 대한 역전파 수행 -> x의 기울기를 계산
y.backward()

# x의 기울기 확인
print(x.grad) # 결과: tensor(12.)

기울기 누적과 초기화

  • 중요: PyTorch는 .backward()가 호출될 때마다 기울기를 이전 값에 **누적(accumulate)**합니다.
  • 따라서, 매 학습 반복(iteration)마다 기울기를 새로 계산하기 전에 optimizer.zero_grad()를 호출하여 이전 기울기를 반드시 0으로 초기화해야 합니다. 그렇지 않으면 의도치 않은 학습 결과가 발생할 수 있습니다.

4. 신경망 (Neural Networks) 구성하기

torch.nn 모듈

  • torch.nn 패키지는 신경망을 구성하는 데 필요한 모든 구성 요소(레이어, 활성화 함수, 손실 함수 등)를 제공합니다.
  • nn.Linear(in_features, out_features)는 입력 데이터에 Wx + b와 같은 선형 변환을 적용하는 가장 기본적인 **선형 계층(Linear Layer)**입니다.

신경망 클래스 정의

  • 사용자 정의 신경망은 nn.Module 클래스를 상속받아 만듭니다.
  • __init__(self) (초기화 함수): 신경망에 필요한 계층들(예: nn.Linear, nn.ReLU)을 정의하고 초기화합니다.
  • forward(self, x) (순전파 함수): 입력 데이터 x__init__에서 정의된 계층들을 어떤 순서로 통과할지를 정의합니다.
# 간단한 다층 퍼셉트론(MLP) 정의
class SimpleMLP(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super().__init__()
        # 신경망 계층들을 정의
        self.layer1 = nn.Linear(input_size, hidden_size)
        self.relu = nn.ReLU()
        self.layer2 = nn.Linear(hidden_size, output_size)

    def forward(self, x):
        # 데이터의 흐름(순전파)을 정의
        x = self.layer1(x)
        x = self.relu(x)
        x = self.layer2(x)
        return x

# 모델 인스턴스 생성
input_dim = 10
hidden_dim = 20
output_dim = 5
model = SimpleMLP(input_dim, hidden_dim, output_dim)
print(model)

# 임의의 입력 데이터로 모델 테스트
test_input = torch.randn(64, input_dim) # 64개의 샘플을 가진 배치
output = model(test_input)
print(output.shape) # torch.Size([64, 5])

5. 모델 최적화 (Optimization) 및 학습 루프

학습 과정

  • 신경망을 학습시키는 과정은 **학습 루프(Training Loop)**를 통해 이루어집니다. 이 과정은 여러 에폭(epoch)에 걸쳐 반복됩니다.

표준적인 학습 루프의 5단계

  1. 기울기 초기화: optimizer.zero_grad()를 호출하여 이전 배치의 기울기를 모두 0으로 만듭니다.
  2. 순전파 (Forward Pass): model(inputs)를 호출하여 현재 배치의 입력 데이터에 대한 모델의 예측값을 얻습니다.
  3. 손실 계산 (Compute Loss): loss_fn(predictions, labels)를 호출하여 모델의 예측값과 실제 정답을 비교하여 손실(오차)을 계산합니다.
  4. 역전파 (Backward Pass): loss.backward()를 호출하여 손실에 대한 각 파라미터의 기울기를 계산합니다.
  5. 파라미터 업데이트: optimizer.step()을 호출하여 계산된 기울기를 바탕으로 모델의 파라미터를 업데이트(학습)합니다.
from torch.optim import Adam

# 모델, 손실 함수, 옵티마이저 정의
model = SimpleMLP(input_dim, hidden_dim, output_dim)
loss_fn = nn.CrossEntropyLoss() # 분류 문제용 손실 함수 예시
optimizer = Adam(model.parameters(), lr=1e-3) # Adam 옵티마이저

# --- 가상의 학습 루프 ---
# for epoch in range(num_epochs):
#   for batch in data_loader:
#       inputs, labels = batch

# 1. 기울기 초기화
optimizer.zero_grad()

# 2. 순전파 -> 예측값 계산
predictions = model(inputs)

# 3. 손실 계산
loss = loss_fn(predictions, labels)

# 4. 역전파 -> 기울기 계산
loss.backward()

# 5. 파라미터 업데이트
optimizer.step()
profile
AI 공부합니다

0개의 댓글