본문 바로가기
목차
Python

[DL] PyTorch: Autograd (Basic)

by ds31x 2024. 3. 22.
728x90
반응형

Autograd 사용하기.

Autograd는

  • reverse mode automatic differentiation을 구현하여
  • backpropagation을 자동으로 수행하는 도구임.

https://dsaint31.me/mkdocs_site/ML/ch08/back_propagation/

 

BME

Back propagation (역전파, 오차 역전파) 딥러닝 모델을 학습시키기 위한 핵심 알고리즘. Back propagation은 다음 2가지를 조합하여 ANN을 학습시킴. "Reverse-mode AutoDiff" (Reverse-mode automatic differentiation) "Gradien

dsaint31.me

 

PyTorch에서
autograd는 tensor와 함께 핵심 구성요소로,
모든 tensor 연산의 기울기 계산을
자동으로 처리하는 기반을 제공.

1. requires_gradTrue로 설정.

편미분을 구할 Tensor 인스턴스에 대해 requires_grad=True 가 되도록하여,

해당 Tensor 인스턴스에 대한 연산을 추적하고, 자동으로 gradient를 계산할 수 있도록 수행.

x = torch.tensor([2.0], requires_grad=True)

 

autograd와 관련된 attributes는 다음과 같음.

  • x.requires_grad :
    • grad 를 구하기위해 forward propagation 과정 중에
    • gradient 를 구하기 위해 필요한 연산 및 중간값을 추적하여 기록할지 여부를 나타냄.
    • autograd가 추적하는 계산 그래프에 포함 여부에 해당.
    • torch.no_grad() Context 내에서는 모든 텐서의 requires_grad 속성이 False가 설정됨.
  • x.is_leaf:
    • 계산 그래프에서 “leaf node"인지 여부를 나타냄.
    • 크게 requires_grad 가 True 인 경우와 False 인 경우로 나누어 이해하면 편함:
      • requires_grad=False 이면서 is_leaf=True 인 경우:
        • requires_grad=False 인 tensor는 autograd가 gradient 계산을 위해서 추적하지 않음.
        • 이러한 tensor는 PyTorch에서 기본적으로 is_leaf=True 로 간주함.
        • x = torch.tensor([1.0, 2.0])
      • requires_grad=True 이면서 is_leaf=True 인 경우:
        • 이경우, leaf tensor계산 그래프의 시작점에 해당하는 tensor임.
        • 다른 tensor 연산의 결과로 만들어진 중간 tensor가 아니라, 
          사용자가 직접 생성하여 계산 그래프의 시작점이 된 tensor.
        • 일반적으로 이 경우엔 grad_fn=None 임.
        • 명심할 점은, backward 처리 이후에 grad 속성에 gradient값이 누적되려면
          이 경우(requires_grad=True, is_leaf=True)에 해당해야 함.
        • x = torch.tensor([1.0, 2.0], requires_grad=True)
    • 만약 tensor 가 leaf tensor로부터 어떤 연산을 통해 생성되었거나,
      leaf tensor로부터의 연산으로 생성된 tensor들의 연산을 통해 생성된다면, is_leaf 속성은 False임.
      • 이 경우, 계산 그래프에 포함된 node임.
      • x = torch.tensor([1.0, 2.0], requires_grad=True)
        y = x * 2  # y 가 바로 non-leaf node (=intermediate node)임.
    • detach() 이후의 경우:
      • detach() 는 tensor를 기존 계산 그래프로부터 분리함.
      • 반환된 tensors는 원래 tensor와 storage를 공유할 수 있으나, 기존 그래프와 gradient 연결은 끊어짐.
      • 때문에 detach()가 반환한 tensor는 requires_grad=False 이며, 앞서 본 것처럼 is_leaf=True 임 (아래의 z).
        z = y.detach()
      • 단, 이 반환된 tensor에서 requires_grad_() 를 호출하면 이 tensor를 새로운 계산그래프의 시작점으로 만들게 됨.
        (기존 그래프와의 연결은 여전히 끊어진 상태임을 주의).
      • z = y.detach()
        z.requires_grad_()

        print(z.requires_grad)  # True
        print(z.is_leaf)        # True
    • 중요!!
      • is_leaf=True 이고 requires_grad=Trueleaf tensor 들만
      • grad 속성에 gradient값이 누적되게 됨(backward 처리 이후)
      • 단, retain_grad() 메서드를 호출하여 retain_grad 속성을 True로 한 경우엔 중간 tensor들의 grad 속성에도 gradient 값이 저장됨.
  • x.grad_fn:
    • 텐서가 다른 텐서들의 연산을 통해 생성된 경우(=leaf node가 아닌 경우),
    • grad_fn해당 연산을 나타내며,
    • 이 정보를 통해 back-propagation 에서 gradient를 계산할 때 어떤 연산을 통해 편미분을 계산하는지 알 수 있음.
    • 해당 텐서가 만들어진 마지막 연산 만을 가지나,
      해당 연산에서 앞서의 연산에 대한 정보를 가진 next_functions라는 속성을 가짐 (이를 재귀적으로 추적 가능).
  • x.grad:
    • tensor 인스턴스 x 에 대해 수행된 마지막 .backward() 연산으로부터 계산된 gradient가 누적되어 저장됨.
    • xrequires_grad = True일 때,
      • PyTorch는 x에 대한 모든 연산을 추적하며
      • .backward()가 호출될 때 자동으로 gradient를 계산함.
      • 단, is_leaf=True인  텐서에만 grad 속성에 저장됨.
    • 주의할 점은 backward 메서드를 호출하는 텐서가 scalar가 아닌 경우,
      .backward() 호출 시 gradient의 shape를 지정하는 argument를 전달해야함
      • 이는 .backward()를 호출하는 tensor와 같은 shape.
      • 각 요소로 scalar 1을 가지는 tensor 인스턴스가 주로 argument로 사용됨: torch.ones_like(output)
import torch

# 1. 직접 생성 + requires_grad=True
#    -> 계산 그래프의 시작점이므로 leaf tensor
x = torch.tensor([1.0, 2.0], requires_grad=True)

# 2. x를 이용한 연산 결과
#    -> 계산 그래프의 중간 tensor이므로 non-leaf
y = x * 2

print(x.is_leaf, x.requires_grad)  # True,  True
print(y.is_leaf, y.requires_grad)  # False, True


# 3. detach(): 기존 계산 그래프와 연결을 끊음
#    -> requires_grad=False이므로 is_leaf=True
z = y.detach()

print(z.is_leaf, z.requires_grad)  # True, False


# 4. detach된 tensor에서 gradient 계산을 다시 시작
#    -> 새로운 계산 그래프의 시작점이 됨
z.requires_grad_()

print(z.is_leaf, z.requires_grad)  # True, True


# 5. 기본적으로 leaf tensor의 .grad만 저장됨
#    y는 non-leaf이므로 gradient를 저장하려면 retain_grad() 필요
y.retain_grad()

loss1 = y.sum()
loss1.backward()

print(x.grad)  # tensor([2., 2.])
print(y.grad)  # tensor([1., 1.])  <- retain_grad() 때문에 저장됨


# 6. z는 x, y와 연결이 끊어진 새로운 leaf tensor
loss2 = (z * 3).sum()
loss2.backward()

print(z.grad)  # tensor([3., 3.])
print(x.grad)  # tensor([2., 2.])  <- z의 backward는 x에 영향을 주지 않음

2. 앞서 tensor 인스턴스에 대한 연산을 기재.

# 간단한 연산으로 구성한 예. 실제 구현에서는 매우 복잡해질 것임.
a = 2*x + 10  
b = 3*a
b.retain_grad()
c = 2*b
  • 1번 과정에서 requires_grad=True인 tensor 인스턴스에
    연결된 모든 tensor 인스턴스도 모두 다 requires_grad=True 임.
  • 맨 처음 requires_grad=True 로 지정한 tensor 인스턴스는 is_leaf는 True로 설정되고,
    이후 해당 인스턴스에 연결되는 Tensor들은 is_leaf가 False 임.
  • 이들 연결된 tensor들의 grad_fnBackward function이 할당됨

leaf node 가 아닌
다른 tensor 와 연산으로 만들어진 tensor 인스턴스에 대해
Gradient를 구하고 싶을때는
해당 tensor 인스턴스의 메서드 .retain_grad()
.backward() 호출 전에 호출하면 됨:
이를 통해 해당 tensor 인스턴스의 gradient를 "유지"하게 됨.
해당 tensor 인스턴스의 grad 속성은 gradient값을 가지게 됨.

 


3. 결과값의 tensor 부터 back-propagation 수행

backpropagation이 시작되는 loss function 의 결과 Tensor 인스턴스 ( c )에서
backward() 메서드를 수행하여 gradient를 계산하고,

computational graph의 leaf node의 attribute 인 .grad 에 계산된 gradient를 accumulate 시킴.

.backward()
Computes the sum of gradients of given tensors
with respect to graph leaves.

c.backward( torch.ones(c.shape) )
# 아니면,
# l = torch.sum(c)
# l.backward()

 

torch.autograd provides classes and functions
implementing automatic differentiation of arbitrary scalar valued functions.

때문에 .backward()가 호출될 때 넘겨지는 argument는
"호출에 사용되는 tensor 인스턴스"와 같은 shape이면서
값은 scalar 1을 가지는 tensor 인스턴스 이어야 함.
아니면 .backward() 메서드를 호출하는
tensor 인스턴스가 scalar 여야 한다.
참고로,
인자로 넘겨주는 gradient의 초기치를 이용하여
이어서 그래디언트 계산도 가능.

argument로 retain_graph=True 를 넘겨줄 경우, .backward()를 여러차례 호출이 가능해짐.

  • 해당 argument를 True로 설정하지 않으면,
  • .backward() 가 gradient를 계산하는 과정에 forward phase에서 구해진 intermediate value들을 모두 free 시켜서
  • 이후 forward phase 수행 없이 곧바로 다시 .backward()를 호출할 경우 runtime error가 발생함.
  • retain_graph=True 로 하면,  backward 과정 중 필요한 자원들을 해제를 하지 않음.

.backward()를 여러차례 호출할 경우,
leaf node의 grad에 계산된 gradient가 accumulated 되는 것을 쉽게 확인 가능함.

(모델의 파라메터를 제대로 갱신하려면, 꼭 leaf node의 grad의 초기화가 필요함.)


4. 이후, gradient를 다음으로 확인 가능.

# gradient를 출력.
print(x.grad)
  • 참고로, DL등에서 다음 backward()를 수행하기 전에
  • 꼭 구한 gradient를 초기화해야함 (아래 예제 참조).
x.grad.zeros_() # leaf node x의 grad를 0으로 초기화 하는 in-place op.
  • 이 초기화를 하지 않을 경우, grad 속성에 gradient 값이 누적이 되어
  • 제대로 된 gradient를 구하지 못하게 됨.

초기화 관련하여 좀 더 자세히

일반적으로,

Training 과정 중 "한 번의 iteration (단일 batch에 대해)"이 끝난 후에는 grad 를 0으로 초기화해야 함.

  • grad 를 초기화하지 않으면, 각 batch에 대한 grad가 이전 batch의 grad에 더해져 누적됨.
  • 이후 (gradient decent 기반의 ) optimizer를 통해 누적된 grad 를 기반으로 model의 parameters가 update되며,
  • 이는 잘못된 parameter update를 의미하고, 결국 제대로 학습이 이루어지지 못하는 결과로 이어짐.

즉, 한 epoch가 여러 batch의 iteration으로 이루어질 때,

  • 각 batch 처리(=하나의 iteration) 후에 구해진 grad 들을 0으로 초기화하지 않으면
  • 첫 번째 batch 에 대한 grad 계산이 두 번째 batch 의 grad 에 영향을 주고,
  • 이는 모든 이어지는 batch들에 마찬가지로 영향을 줌.
  • 결국 한 epoch에서의 gradient가 제대로 반영되지 못하게 된다.

이를 막기위해 대부분의 training loop 에서는

  • 적절한 위치에 x.grad.zero_() (또는 optimizer.zero_grad())를 호출하여
  • gradient를 초기화함.

다음은 이를 보여주는 예제 코드임. :

for data, target in dataset:  
    optimizer.zero_grad() # model의 모든 parameters의 grad를 0으로 초기화  
    output = model(data)  
    loss = loss_function(output, target)  
    loss.backward()   # back-propagation을 통해 gradient 계산  
    optimizer.step()  # 계산된 gradient를 통해 model의 parameters 갱신 (or 수정).

다음의 ipynb 는 PyTorch에서 AutoGrad를 쓰는 간단한 예제를 보여줌.

https://gist.github.com/dsaint31x/5642ae694a4bb1ec5887fc3642b7d91d

 

dl_tensor_pytorch_autograd.ipynb

dl_tensor_pytorch_autograd.ipynb. GitHub Gist: instantly share code, notes, and snippets.

gist.github.com


기타 참고자료.

https://teamdable.github.io/techblog/PyTorch-Autograd: 꼭 읽어볼 것.

 

PyTorch Autograd

안녕하세요. 오태호입니다.

teamdable.github.io

https://velog.io/@olxtar/PyTorch-Autograd-03-Practice02

 

[PyTorch] Autograd-03 : Practice02

retain_graph() / Stem 추가 / Leaf 추가 / Operation / .no_grad()

velog.io


같이 보면 좋은 자료들

2025.03.28 - [Python] - [PyTorch] autograd 심화: grad_fn 과 custom operation 만들기

 

[PyTorch] autograd 심화: grad_fn 과 custom operation 만들기

1. PyTorch Autograd 메커니즘 이해1-1. Tensor의 grad_fn과 연산 그래프 추적 방법PyTorch tensor의 grad_fn은 마지막 operation만 표시하는 attribute.전체 computation graph 확인을 위한 next_functions attribute의 활용.재귀적

ds31x.tistory.com

2026.06.23 - [ML] - Autograd : In-place 연산

 

Autograd : In-place 연산

In-place 연산과 Autograd의 관계backward에서 필요한 tensor를 in-place로 수정하면 안 되며,backward에서 어떤 tensor가 필요한지는 연산마다 다르다.PyTorch의 autograd는forward pass에서 계산 그래프를 만들고,backwa

ds31x.tistory.com

 

728x90