Skip to content

Files

Latest commit

 

History

History
279 lines (200 loc) Β· 12.2 KB

torch-script-parallelism.rst

File metadata and controls

279 lines (200 loc) Β· 12.2 KB

TorchScript의 동적 병렬 처리(Dynamic Parallelism)

이 νŠœν† λ¦¬μ–Όμ—μ„œλŠ”, TorchScriptμ—μ„œ 동적 inter-op λ³‘λ ¬μ²˜λ¦¬ λ₯Ό ν•˜λŠ” ꡬ문(syntax)을 μ†Œκ°œν•©λ‹ˆλ‹€. 이 λ³‘λ ¬μ²˜λ¦¬μ—λŠ” λ‹€μŒκ³Ό 같은 속성이 μžˆμŠ΅λ‹ˆλ‹€:

  • 동적(dynamic) - μƒμ„±λœ 병렬 μž‘μ—…μ˜ μˆ˜μ™€ μž‘μ—… λΆ€ν•˜λŠ” ν”„λ‘œκ·Έλž¨μ˜ μ œμ–΄ 흐름에 따라 λ‹¬λΌμ§ˆ 수 μžˆμŠ΅λ‹ˆλ‹€.
  • inter-op - 병렬 μ²˜λ¦¬λŠ” TorchScript ν”„λ‘œκ·Έλž¨ 쑰각을 λ³‘λ ¬λ‘œ μ‹€ν–‰ν•˜λŠ” 것과 관련이 μžˆμŠ΅λ‹ˆλ‹€. μ΄λŠ” κ°œλ³„ μ—°μ‚°μžλ₯Ό λΆ„ν• ν•˜κ³  μ—°μ‚°μž μž‘μ—…μ˜ ν•˜μœ„ 집합을 λ³‘λ ¬λ‘œ μ‹€ν–‰ν•˜λŠ” 방식인 intra-op parallelism μ™€λŠ” κ΅¬λ³„λ©λ‹ˆλ‹€.

기본 ꡬ문

동적 병렬 처리λ₯Ό μœ„ν•œ 두 가지 μ€‘μš”ν•œ APIλŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€:

  • torch.jit.fork(fn : Callable[..., T], *args, **kwargs) -> torch.jit.Future[T]
  • torch.jit.wait(fut : torch.jit.Future[T]) -> T

μ΄λŸ¬ν•œ μž‘λ™ 방식은 λ‹€μŒ μ˜ˆμ œμ—μ„œ 잘 이해할 수 μžˆμŠ΅λ‹ˆλ‹€:

import torch

def foo(x):
    return torch.neg(x)

@torch.jit.script
def example(x):
    # λ³‘λ ¬μ μœΌλ‘œ `foo` λ₯Ό ν˜ΈμΆœν•©λ‹ˆλ‹€.
    # λ¨Όμ €, μž‘μ—…μ„ "fork" ν•©λ‹ˆλ‹€. 이 μž‘μ—…μ€ `x` 인자(argument)와 ν•¨κ»˜ `foo` λ₯Ό μ‹€ν–‰ν•©λ‹ˆλ‹€.
    future = torch.jit.fork(foo, x)

    # 일반적으둜 `foo`λ₯Ό ν˜ΈμΆœν•©λ‹ˆλ‹€.
    x_normal = foo(x)

    # λ‘˜μ§Έ, μž‘μ—…μ„ "κΈ°λ‹€λ¦½λ‹ˆλ‹€".
    # μž‘μ—…μ΄ λ³‘λ ¬λ‘œ μ‹€ν–‰ 쀑일 수 μžˆμœΌλ―€λ‘œ κ²°κ³Όλ₯Ό μ‚¬μš©ν•  수 μžˆμ„ λ•ŒκΉŒμ§€ "λŒ€κΈ°" ν•΄μ•Όν•©λ‹ˆλ‹€.
    # 계산을 λ³‘λ ¬λ‘œ μˆ˜ν–‰ν•˜κΈ° μœ„ν•΄μ„œ
    # "fork()" 와 "wait()" μ‚¬μ΄μ—μ„œ
    # Futureλ₯Ό ν˜ΈμΆœν•˜λŠ” 점에 μœ μ˜ν•˜μ„Έμš”.
    x_parallel = torch.jit.wait(future)

    return x_normal, x_parallel

print(example(torch.ones(1))) # (-1., -1.)

fork() λŠ” 호좜 κ°€λŠ₯ν•œ(callable) fn ,그에 λŒ€ν•œ 호좜 κ°€λŠ₯ν•œ 인자 args 및 kwargs λ₯Ό μ·¨ν•˜κ³  fn 싀행을 μœ„ν•œ 비동기(asynchronous) μž‘μ—…μ„ μƒμ„±ν•©λ‹ˆλ‹€. fn 은 ν•¨μˆ˜, λ©”μ†Œλ“œ, λ˜λŠ” λͺ¨λ“ˆ μΈμŠ€ν„΄μŠ€μΌ 수 μžˆμŠ΅λ‹ˆλ‹€. fork() λŠ” Future 라고 λΆˆλ¦¬λŠ” 이 μ‹€ν–‰ 결과의 값에 λŒ€ν•œ μ°Έμ‘°(reference)λ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€. fork λŠ” 비동기 μž‘μ—…μ„ μƒμ„±ν•œ 직후에 λ°˜ν™˜λ˜κΈ° λ•Œλ¬Έμ—, fork() 호좜 ν›„ μ½”λ“œ 라인이 싀행될 λ•ŒκΉŒμ§€ fn 이 μ‹€ν–‰λ˜μ§€ μ•Šμ„ 수 μžˆμŠ΅λ‹ˆλ‹€. λ”°λΌμ„œ, wait() 은 비동기 μž‘μ—…μ΄ μ™„λ£Œ λ λ•ŒκΉŒμ§€ λŒ€κΈ°ν•˜κ³  값을 λ°˜ν™˜ν•˜λŠ”λ° μ‚¬μš©λ©λ‹ˆλ‹€.

μ΄λŸ¬ν•œ κ΅¬μ‘°λŠ” ν•¨μˆ˜ λ‚΄μ—μ„œ λͺ…λ Ήλ¬Έ 싀행을 μ€‘μ²©ν•˜κ±°λ‚˜ μž‘μ—…λœ (예제 μ„Ήμ…˜μ— ν‘œμ‹œλ¨) 루프와 같은 λ‹€λ₯Έ μ–Έμ–΄ ꡬ쑰둜 ꡬ성 될 수 μžˆμŠ΅λ‹ˆλ‹€:

import torch
from typing import List

def foo(x):
    return torch.neg(x)

@torch.jit.script
def example(x):
    futures : List[torch.jit.Future[torch.Tensor]] = []
    for _ in range(100):
        futures.append(torch.jit.fork(foo, x))

    results = []
    for future in futures:
        results.append(torch.jit.wait(future))

    return torch.sum(torch.stack(results))

print(example(torch.ones([])))

Note

Future의 빈 리슀트(list)λ₯Ό μ΄ˆκΈ°ν™”ν• λ•Œ, λͺ…μ‹œμ μΈ μœ ν˜• 주석을 futures 에 μΆ”κ°€ν•΄μ•Ό ν–ˆμŠ΅λ‹ˆλ‹€. TorchScriptμ—μ„œ 빈 μ»¨ν…Œμ΄λ„ˆ(container)λŠ” 기본적으둜 tensor 값을 ν¬ν•¨ν•œλ‹€κ³  κ°€μ •ν•˜λ―€λ‘œ 리슀트 μƒμ„±μž(constructor) #에 List[torch.jit.Future[torch.Tensor]] μœ ν˜•μ˜ 주석을 λ‹¬μ•˜μŠ΅λ‹ˆλ‹€.

이 μ˜ˆμ œλŠ” fork() λ₯Ό μ‚¬μš©ν•˜μ—¬ ν•¨μˆ˜ foo 의 μΈμŠ€ν„΄μŠ€ 100개λ₯Ό μ‹œμž‘ν•˜κ³ , 100개의 μž‘μ—…μ΄ μ™„λ£Œ λ λ•ŒκΉŒμ§€ λŒ€κΈ°ν•œ λ‹€μŒ, κ²°κ³Όλ₯Ό ν•©μ‚°ν•˜μ—¬ -100.0 을 λ°˜ν™˜ν•©λ‹ˆλ‹€.

적용된 μ˜ˆμ‹œ: μ–‘λ°©ν–₯(bidirectional) LSTMs의 앙상블(Ensemble)

보닀 ν˜„μ‹€μ μΈ μ˜ˆμ‹œμ— 병렬화λ₯Ό μ μš©ν•˜κ³  μ–΄λ–€ μ„±λŠ₯을 얻을 수 μžˆλŠ”μ§€ μ‚΄νŽ΄λ΄…μ‹œλ‹€. λ¨Όμ €, μ–‘λ°©ν–₯ LSTM κ³„μΈ΅μ˜ 앙상블인 κΈ°μ€€ λͺ¨λΈμ„ μ •μ˜ν•©μ‹œλ‹€.

import torch, time

# RNN μš©μ–΄μ—μ„œλŠ” μš°λ¦¬κ°€ 관심 κ°–λŠ” 차원듀을 μ•„λž˜μ™€ 같이 λΆ€λ¦…λ‹ˆλ‹€:
# λ‹¨μœ„μ‹œκ°„μ˜ 갯수 (T)
# 배치 크기 (B)
# "channels"의 μˆ¨κ²¨μ§„ 크기/숫자 (C)
T, B, C = 50, 50, 1024

# 단일 "μ–‘λ°©ν–₯ LSTM"을 μ •μ˜ν•˜λŠ” λͺ¨λ“ˆμž…λ‹ˆλ‹€.
# μ΄λŠ” λ‹¨μˆœνžˆ λ™μΌν•œ μ‹œν€€μŠ€μ— 적용된 두 개의 LSTMμ΄μ§€λ§Œ ν•˜λ‚˜λŠ” λ°˜λŒ€λ‘œ μ μš©λ©λ‹ˆλ‹€.
class BidirectionalRecurrentLSTM(torch.nn.Module):
    def __init__(self):
        super().__init__()
        self.cell_f = torch.nn.LSTM(input_size=C, hidden_size=C)
        self.cell_b = torch.nn.LSTM(input_size=C, hidden_size=C)

    def forward(self, x : torch.Tensor) -> torch.Tensor:
        # Forward 계측
        output_f, _ = self.cell_f(x)

        # Backward 계측. μ‹œκ°„ 차원(time dimension)(dim 0)μ—μ„œ μž…λ ₯을 flip (dim 0),
        # 계측 μ μš©ν•˜κ³ , μ‹œκ°„ μ°¨μ›μ—μ„œ 좜λ ₯을 flip ν•©λ‹ˆλ‹€.
        x_rev = torch.flip(x, dims=[0])
        output_b, _ = self.cell_b(torch.flip(x, dims=[0]))
        output_b_rev = torch.flip(output_b, dims=[0])

        return torch.cat((output_f, output_b_rev), dim=2)


# `BidirectionalRecurrentLSTM` λͺ¨λ“ˆμ˜ "ensemble"μž…λ‹ˆλ‹€.
# μ•™μƒλΈ”μ˜ λͺ¨λ“ˆμ€ 같은 μž…λ ₯으둜 ν•˜λ‚˜ν•˜λ‚˜μ”© μ‹€ν–‰λ˜κ³ ,
# λˆ„μ λ˜κ³  ν•©μ‚°λœ κ²°κ³Όλ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.
class LSTMEnsemble(torch.nn.Module):
    def __init__(self, n_models):
        super().__init__()
        self.n_models = n_models
        self.models = torch.nn.ModuleList([
            BidirectionalRecurrentLSTM() for _ in range(self.n_models)])

    def forward(self, x : torch.Tensor) -> torch.Tensor:
        results = []
        for model in self.models:
            results.append(model(x))
        return torch.stack(results).sum(dim=0)

# fork/wait으둜 μ‹€ν–‰ν•  κ²ƒλ“€μ˜ 직접 비ꡐλ₯Ό μœ„ν•΄
# λͺ¨λ“ˆμ„ μΈμŠ€ν„΄μŠ€ν™”ν•˜κ³  TorchScriptλ₯Ό 톡해 μ»΄νŒŒμΌν•΄ λ΄…μ‹œλ‹€.
ens = torch.jit.script(LSTMEnsemble(n_models=4))

# 일반적으둜 μž„λ² λ”© ν…Œμ΄λΈ”(embedding table)μ—μ„œ μž…λ ₯을 κ°€μ Έμ˜€μ§€λ§Œ,
# 데λͺ¨λ₯Ό μœ„ν•΄ μ—¬κΈ°μ„œλŠ” λ¬΄μž‘μœ„ 데이터λ₯Ό μ‚¬μš©ν•˜κ² μŠ΅λ‹ˆλ‹€.
x = torch.rand(T, B, C)

# λ©”λͺ¨λ¦¬ ν• λ‹Ήμž(memory allocator) 등을 μ€€λΉ„μ‹œν‚€κΈ° μœ„ν•΄ λͺ¨λΈμ„ λ¨Όμ € ν•œλ²ˆ μ‹€ν–‰ν•©λ‹ˆλ‹€.
ens(x)

x = torch.rand(T, B, C)

# μ–Όλ§ˆλ‚˜ λΉ λ₯΄κ²Œ μ‹€ν–‰λ˜λŠ”μ§€ λ΄…μ‹œλ‹€!
s = time.time()
ens(x)
print('Inference took', time.time() - s, ' seconds')

제 μ»΄ν“¨ν„°μ—μ„œλŠ” λ„€νŠΈμ›Œν¬κ°€ 2.05 초 λ§Œμ— μ‹€ν–‰λ˜μ—ˆμŠ΅λ‹ˆλ‹€. 훨씬 더 λΉ λ₯΄κ²Œ ν•  수 μžˆμŠ΅λ‹ˆλ‹€!

Forward, Backward 계측 병렬화

κ°„λ‹¨ν•˜κ²Œ ν•  수 μžˆλŠ” μΌλ‘œλŠ” BidirectionalRecurrentLSTM λ‚΄μ—μ„œ forward, backward 계측듀을 λ³‘λ ¬ν™”ν•˜λŠ” 것이 μžˆμŠ΅λ‹ˆλ‹€. 이 λ•Œ, 계산 κ΅¬μ‘°λŠ” κ³ μ •λ˜μ–΄ μžˆμœΌλ―€λ‘œ μš°λ¦¬λŠ” μ–΄λ–€ 루프도 ν•„μš”λ‘œ ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. BidirectionalRecurrentLSTM 의 forward λ©”μ†Œλ“œλ₯Ό λ‹€μŒκ³Ό 같이 μž¬μž‘μ„±ν•΄λ΄…μ‹œλ‹€:

def forward(self, x : torch.Tensor) -> torch.Tensor:

    # Backward 계측과 λ³‘λ ¬λ‘œ μ‹€ν–‰μ‹œν‚€κΈ° μœ„ν•΄ forward layerλ₯Ό fork()λ₯Ό ν•œλ‹€.
    future_f = torch.jit.fork(self.cell_f, x)

    # Backward 계측. μ‹œκ°„ 차원(time dimension)(dim 0)μ—μ„œ μž…λ ₯을 flip (dim 0),
    # 계측을 μ μš©ν•˜κ³ , 그리고 μ‹œκ°„ μ°¨μ›μ—μ„œ 좜λ ₯을 flip ν•©λ‹ˆλ‹€.
    x_rev = torch.flip(x, dims=[0])
    output_b, _ = self.cell_b(torch.flip(x, dims=[0]))
    output_b_rev = torch.flip(output_b, dims=[0])

    # Forward κ³„μΈ΅μ—μ„œ 좜λ ₯을 λ°›μ•„μ˜΅λ‹ˆλ‹€.
    # μ΄λŠ” μš°λ¦¬κ°€ λ³‘λ ¬ν™”ν•˜λ €λŠ” μž‘μ—… *이후*에 μΌμ–΄λ‚˜μ•Ό 함을 μ£Όμ˜ν•΄μ•Ό ν•©λ‹ˆλ‹€.
    output_f, _ = torch.jit.wait(future_f)

    return torch.cat((output_f, output_b_rev), dim=2)

이 μ˜ˆμ‹œμ—μ„œ, forward() λŠ” cell_b 의 싀행을 κ³„μ†ν•˜λŠ” λ™μ•ˆ cell_f λ₯Ό λ‹€λ₯Έ μŠ€λ ˆλ“œλ‘œ μœ„μž„ν•©λ‹ˆλ‹€. 이둜 인해 두 μ…€μ˜ 싀행이 μ„œλ‘œ κ²ΉμΉ©λ‹ˆλ‹€.

이 κ°„λ‹¨ν•œ μˆ˜μ • 후에 슀크립트λ₯Ό λ‹€μ‹œ μ‹€ν–‰ν•˜λ©΄ 17% ν–₯μƒλœ 1.71 초의 λŸ°νƒ€μž„μ΄ λ‚˜μ˜΅λ‹ˆλ‹€!

Aside: 병렬화 μ‹œκ°ν™” (Visualizing Parallelism)

아직 λͺ¨λΈ μ΅œμ ν™”κ°€ λλ‚˜μ§€ μ•Šμ•˜μ§€λ§Œ μ΄μ―€μ—μ„œ μ„±λŠ₯ μ‹œκ°ν™”λ₯Ό μœ„ν•œ 도ꡬλ₯Ό λ„μž…ν•΄λ΄…μ‹œλ‹€. ν•œ 가지 μ€‘μš”ν•œ λ„κ΅¬λŠ” PyTorch ν”„λ‘œνŒŒμΌλŸ¬(profiler) μž…λ‹ˆλ‹€.

Chrome의 좔적 내보내기 κΈ°λŠ₯(trace export functionality)κ³Ό ν•¨κ»˜ ν”„λ‘œνŒŒμΌλŸ¬λ₯Ό μ‚¬μš©ν•΄ λ³‘λ ¬ν™”λœ λͺ¨λΈμ˜ μ„±λŠ₯을 μ‹œκ°ν™”ν•΄λ΄…μ‹œλ‹€:

with torch.autograd.profiler.profile() as prof:
    ens(x)
prof.export_chrome_trace('parallel.json')

이 μž‘μ€ μ½”λ“œ 쑰각은 parallel.json νŒŒμΌμ„ μž‘μ„±ν•©λ‹ˆλ‹€. Google Chromeμ—μ„œ chrome://tracing 으둜 μ΄λ™ν•˜μ—¬ Load λ²„νŠΌμ„ ν΄λ¦­ν•˜κ³  JSON νŒŒμΌμ„ λ‘œλ“œν•˜λ©΄ λ‹€μŒκ³Ό 같은 νƒ€μž„λΌμΈμ„ 보게 될 κ²λ‹ˆλ‹€:

https://i.imgur.com/rm5hdG9.png

νƒ€μž„λΌμΈμ˜ κ°€λ‘œμΆ•μ€ μ‹œκ°„μ„, μ„Έλ‘œμΆ•μ€ μ‹€ν–‰ μŠ€λ ˆλ“œλ₯Ό λ‚˜νƒ€λƒ…λ‹ˆλ‹€. λ³΄λ‹€μ‹œν”Ό ν•œ λ²ˆμ— 두 개의 lstm 을 μ‹€ν–‰ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. 이것은 μ–‘λ°©ν–₯(forward, backward) 계측을 λ³‘λ ¬ν™”ν•˜κΈ° μœ„ν•΄ λ…Έλ ₯ν•œ κ²°κ³Όμž…λ‹ˆλ‹€!

μ•™μƒλΈ”μ—μ„œμ˜ 병렬화 λͺ¨λΈ

이 μ½”λ“œμ— 더 λ§Žμ€ 병렬화 κΈ°νšŒκ°€ μžˆλ‹€λŠ” 것을 λˆˆμΉ˜μ±˜μ„μ§€λ„ λͺ¨λ¦…λ‹ˆλ‹€: LSTMEnsemble 에 ν¬ν•¨λœ λͺ¨λΈλ“€μ„ μ„œλ‘œ λ³‘λ ¬λ‘œ μ‹€ν–‰ν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€. μ΄λ ‡κ²Œ ν•˜κΈ° μœ„ν•œ 방법은 μ•„μ£Ό κ°„λ‹¨ν•©λ‹ˆλ‹€. λ°”λ‘œ LSTMEnsemble 의 forward λ©”μ†Œλ“œλ₯Ό λ³€κ²½ν•˜λŠ” λ°©λ²•μž…λ‹ˆλ‹€:

def forward(self, x : torch.Tensor) -> torch.Tensor:
    # 각 λͺ¨λΈμ„ μœ„ν•œ μž‘μ—… μ‹€ν–‰ν•©λ‹ˆλ‹€.
    futures : List[torch.jit.Future[torch.Tensor]] = []
    for model in self.models:
        futures.append(torch.jit.fork(model, x))

    # μ‹€ν–‰λœ μž‘μ—…λ“€μ—μ„œ κ²°κ³Ό μˆ˜μ§‘ν•©λ‹ˆλ‹€.
    results : List[torch.Tensor] = []
    for future in futures:
        results.append(torch.jit.wait(future))

    return torch.stack(results).sum(dim=0)

λ˜λŠ”, λ§Œμ•½ 간결함을 μ€‘μš”ν•˜κ²Œ μƒκ°ν•œλ‹€λ©΄ 리슀트 μ»΄ν”„λ¦¬ν—¨μ…˜(list comprehension)을 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

def forward(self, x : torch.Tensor) -> torch.Tensor:
    futures = [torch.jit.fork(model, x) for model in self.models]
    results = [torch.jit.wait(fut) for fut in futures]
    return torch.stack(results).sum(dim=0)

μ„œλ‘μ—μ„œ μ„€λͺ…ν–ˆλ“―이, μš°λ¦¬λŠ” 루프λ₯Ό μ‚¬μš©ν•΄ μ•™μƒλΈ”μ˜ 각 λͺ¨λΈλ“€μ— λŒ€ν•œ μž‘μ—…μ„ λ‚˜λˆ΄μŠ΅λ‹ˆλ‹€. 그리고 λͺ¨λ“  μž‘μ—…μ΄ μ™„λ£Œλ  λ•ŒκΉŒμ§€ 기닀릴 λ‹€λ₯Έ 루프λ₯Ό μ‚¬μš©ν–ˆμŠ΅λ‹ˆλ‹€. μ΄λŠ” 더 λ§Žμ€ κ³„μ‚°μ˜ μ˜€λ²„λž©μ„ μ œκ³΅ν•©λ‹ˆλ‹€.

이 μž‘μ€ μ—…λ°μ΄νŠΈλ‘œ μŠ€ν¬λ¦½νŠΈλŠ” 1.4 μ΄ˆμ— μ‹€ν–‰λ˜μ–΄ 총 32% 만큼 속도가 ν–₯μƒλ˜μ—ˆμŠ΅λ‹ˆλ‹€! 단 두 μ€„λ§Œμ— 쒋은 효과λ₯Ό λ³΄μ˜€μŠ΅λ‹ˆλ‹€.

λ˜ν•œ Chrome 좔적기(tracer)λ₯Ό λ‹€μ‹œ μ‚¬μš©ν•΄ 진행 상황을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€:

https://i.imgur.com/kA0gyQm.png

이제 λͺ¨λ“  LSTM μΈμŠ€ν„΄μŠ€κ°€ μ™„μ „νžˆ λ³‘λ ¬λ‘œ μ‹€ν–‰λ˜λŠ” 것을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

κ²°λ‘ 

이 νŠœν† λ¦¬μ–Όμ—μ„œ μš°λ¦¬λŠ” TorchScriptμ—μ„œ 동적(dynamic), inter-op 병렬 처리λ₯Ό μˆ˜ν–‰ν•˜κΈ° μœ„ν•œ κΈ°λ³Έ API인 fork() 와 wait() 에 λŒ€ν•΄ λ°°μ› μŠ΅λ‹ˆλ‹€. μ΄λŸ¬ν•œ ν•¨μˆ˜λ“€μ„ μ‚¬μš©ν•΄ TorchScript μ½”λ“œμ—μ„œ ν•¨μˆ˜, λ©”μ†Œλ“œ, λ˜λŠ” Modules 의 싀행을 λ³‘λ ¬ν™”ν•˜λŠ” λͺ‡ 가지 일반적인 μ‚¬μš© νŒ¨ν„΄λ„ λ³΄μ•˜μŠ΅λ‹ˆλ‹€. λ§ˆμ§€λ§‰μœΌλ‘œ, 이 κΈ°μˆ μ„ μ‚¬μš©ν•΄ λͺ¨λΈμ„ μ΅œμ ν™”ν•˜λŠ” 예λ₯Ό 훑어보고, PyTorchμ—μ„œ μ‚¬μš© κ°€λŠ₯ν•œ μ„±λŠ₯ μΈ‘μ • 및 μ‹œκ°ν™” 도ꡬλ₯Ό μ‚΄νŽ΄λ³΄μ•˜μŠ΅λ‹ˆλ‹€.