[Deep Learning 2] Chapter02_PyTorch_Background Coding
Version
#버전표시
import torch
print(torch.__version__)
1.6.0+cu101
PyTorch: Tensors
# -*- coding: utf-8 -*-
import torch
#텐서의 데이터 타입
dtype = torch.float
device = torch.device("cpu")
# device = torch.device("cuda:0") # GPU를 이용해서 실행하려면 이 주석을 제거해주세요.
# N은 배치 크기, D_in은 입력값으로 이용되는 벡터의 크기입니다. H는 은닉층의 노드 개수를 의미하며, D_out은 출력값 벡터의 크기입니다.
N, D_in, H, D_out = 64, 1000, 100, 10
# 무작위로 입력값과 출력값을 생성합니다.
### device=device는 해당 디바이스에 할당한다는 의미
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)
# 가중치값을 무작위로 초기화합니다.
w1 = torch.randn(D_in, H, device=device, dtype=dtype)
w2 = torch.randn(H, D_out, device=device, dtype=dtype)
#학습률은 가중치를 얼마나 크게 업데이트할지 결정하는 값
learning_rate = 1e-6
# 순전파 단계 : 모델의 출력값을 계산합니다.일련의 과정
#총 500번 의 학습을 반복
for t in range(500):
## x와 w1의 행렬곱을 계산합니다.
h = x.mm(w1)
# ReLU 활성화 함수를 적용합니다
h_relu = h.clamp(min=0)
# h_relu와 w2의 행렬곱을 계산합니다.
y_pred = h_relu.mm(w2)
# 오차값을 계산하고 출력합니다.
loss = (y_pred - y).pow(2).sum().item()
# 100번째 반복마다 오차값을 출력합니다.
if t % 100 == 99:
print(t, loss)
# 오차값에 대한 기울기를 계산하여 역전파를 진행합니다.
#역전파 : 경망이 예측값을 내고 난 후, 실제 값과 얼마나 차이가 나는지를 측정한 다음, 그 오차를 줄이기 위해 신경망의 가중치를 조정하는 과정
#기울기 계산 이유 : 기울기를 계산하면 오차를 얼마나 줄여야 하는지를 알수있다.
# 오차값에 대한 기울기를 계산합니다.
grad_y_pred = 2.0 * (y_pred - y)
# w2에 대한 기울기를 계산합니다.
grad_w2 = h_relu.t().mm(grad_y_pred)
# ReLU 활성화 함수에 대한 기울기를 계산합니다.
grad_h_relu = grad_y_pred.mm(w2.t())
# ReLU 함수를 적용하기 전의 기울기를 복사
grad_h = grad_h_relu.clone()
grad_h[h < 0] = 0
#첫 번째 가중치 w1에 대한 기울기를 계산
grad_w1 = x.t().mm(grad_h)
# 경사하강법을 통해 가중치를 업데이트합니다.
w1 -= learning_rate * grad_w1
w2 -= learning_rate * grad_w2
99 911.7958374023438
199 13.303966522216797
299 0.2643859088420868
399 0.005879917647689581
499 0.00033520121360197663
->PyTorch의 기본적인 텐서 연산만을 사용하여 신경망을 구현
PyTorch: Tensors and autograd
# -*- coding: utf-8 -*-
import torch
dtype = torch.float
device = torch.device("cpu")
# device = torch.device("cuda:0") # GPU를 이용해서 실행하려면 이 주석을 제거
# N은 배치 크기, D_in은 입력값으로 이용되는 벡터의 크기입니다.H는 은닉층의 노드 개수를 의미하며, D_out은 출력값 벡터의 크기입니다.
N, D_in, H, D_out = 64, 1000, 100, 10
# 무작위로 입력값과 출력값을 생성합니다.
# requires_grad=False 로 설정하여 역전파가 진행하는 과정에 x, y값에 대한 변화값을 계산할 필요가 없다고 설정합니다.
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)
# 가중치값을 무작위로 초기화합니다.
# requires_grad=True 로 설정하여 역전파가 진행하는 과정에 w1, w2값에 대한 변화값을 계산할 필요가 있다고 설정합니다.
w1 = torch.randn(D_in, H, device=device, dtype=dtype, requires_grad=True)
w2 = torch.randn(H, D_out, device=device, dtype=dtype, requires_grad=True)
learning_rate = 1e-6
for t in range(500):
# 순전파 단계 : Tensor 연산을 통해 모델의 출력값을 계산합니다.
# 이 단계는 Tensor를 이용하여 순전파 단계를 통해 계산되는 과정과 동일하지만,
# 역전파 단계를 구현하지 않아도 되기 때문에 중간 과정들을 생략해도 됩니다.
y_pred = x.mm(w1).clamp(min=0).mm(w2)
# 오차값을 계산하고 출력합니다.
# 계산한 결과값은 (1,) 크기의 Tensor 값이므로
# loss.item()을 통해 Scalar 값으로 출력합니다.
loss = (y_pred - Sy).pow(2).sum()
if t % 100 == 99:
print(t, loss.item())
# autograd를 사용하여 역전파 단계를 계산합니다. 이는 requires_grad=True를
# 갖는 모든 Tensor에 대해 손실의 변화도를 계산합니다. 이후 w1.grad와 w2.grad는
# w1과 w2 각각에 대한 손실의 변화도를 갖는 Tensor가 됩니다.
loss.backward()
#손실 함수에 대한 가중치들의 기울기를 자동으로 계산
# 경사하강법(gradient descent)을 사용하여 가중치를 수동으로 업데이트합니다.
# torch.no_grad()로 감싸는 이유는 가중치들이 requires_grad=True이지만
# autograd에서는 이를 추적할 필요가 없기 때문입니다.
# 다른 방법은 weight.data 및 weight.grad.data를 설정하는 방법입니다.
# tensor.data가 tensor의 저장공간을 공유하기는 하지만, 기록을
# 추적하지 않는다는 것을 주의하세요.
# 또한, 이를 위해 torch.optim.SGD 를 사용할 수도 있습니다.
with torch.no_grad():
w1 -= learning_rate * w1.grad
w2 -= learning_rate * w2.grad
# 가중치 업데이트 이후에는 수동으로 변화량을 0으로 설정합니다.
w1.grad.zero_()
w2.grad.zero_()
#torch.no_grad()는 가중치 업데이트 시 기울기 계산을 비활성화
99 452.27435302734375
199 3.4509215354919434
299 0.0448567196726799
399 0.0009776445804163814
499 0.00010081531945616007
N은 배치 크기, D_in은 입력값으로 이용되는 벡터의 크기입니다.H는 은닉층의 노드 개수를 의미하며, D_out은 출력값 벡터의 크기입니다.
무작위로 입력값과 출력값을 생성
가중치값을 무작위로 초기화
순전파 단계 : Tensor 연산을 통해 모델의 출력값을 계산
오차값을 계산하고 출력
autograd를 사용하여 역전파 단계를 계산
손실 함수에 대한 가중치들의 기울기를 자동으로 계산
경사하강법(gradient descent)을 사용하여 가중치를 수동으로 업데이트
가중치 업데이트 이후에는 수동으로 변화량을 0으로 설정
->PyTorch의 자동 미분 기능을 활용하여 가중치의 기울기를 계산하고, 경사하강법을 적용하여 가중치를 업데이트
PyTorch: Defining new autograd functions
# -*- coding: utf-8 -*-
import torch
#PyTorch의 torch.autograd.Function을 상속받아 사용자 정의 autograd 함수인 MyReLU를 정의
class MyReLU(torch.autograd.Function):
"""
torch.autograd.Function을 상속받아 사용자 정의 autograd Function을 구현하고,
Tensor 연산을 하는 순전파와 역전파 단계를 구현하겠습니다.
"""
#순전파 단계를 정의하는 메소드. ctx는 컨텍스트 객체로, 역전파 단계에서 사용할 정보를 저장하는 데 사용
@staticmethod
def forward(ctx, input):
"""
순전파 단계에서는 입력을 갖는 Tensor를 받아 출력을 갖는 Tensor를 반환합니다.
ctx는 컨텍스트 객체(context object)로 역전파 연산을 위한 정보 저장에
사용합니다. ctx.save_for_backward method를 사용하여 역전파 단계에서 사용할 어떠한
객체도 저장(cache)해 둘 수 있습니다.
"""
#역전파 단계에서 사용할 입력값 input을 저장
ctx.save_for_backward(input)
return input.clamp(min=0)
#역전파 단계를 정의하는 메소드
@staticmethod
def backward(ctx, grad_output):
"""
역전파 단계에서는 출력에 대한 손실의 변화도를 갖는 Tensor를 받고, 입력에
대한 손실의 변화도를 계산합니다.
"""
#순전파 단계에서 저장된 입력값을 불러옵니다.
input, = ctx.saved_tensors
#grad_output은 손실 함수로부터 전달받은 출력값에 대한 변화도(기울기)
grad_input = grad_output.clone()
#입력값이 0보다 작은 경우 변화도를 0으로 설정
grad_input[input < 0] = 0
return grad_input
dtype = torch.float
device = torch.device("cpu")
N, D_in, H, D_out = 64, 1000, 100, 10
#x: 모델의 입력으로 사용될 무작위 데이터 텐서입니다. 크기는 N x D_in입니다.
#y: x에 대한 타겟(목표) 출력값을 나타내는 무작위 데이터 텐서
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)
#w1은 첫 번째 층의 가중치로 D_in x H 차원을 가지며, w2는 두 번째 층(출력층)의 가중치로 H x D_out 차원
#requires_grad=True: 이 텐서들에 대한 연산을 추적하여 역전파 시 자동으로 기울기를 계산
w1 = torch.randn(D_in, H, device=device, dtype=dtype, requires_grad=True)
w2 = torch.randn(H, D_out, device=device, dtype=dtype, requires_grad=True)
learning_rate = 1e-6
#총 500회의 반복(에폭)으로 학습 과정을 수행
for t in range(500):
#사용자 정의 ReLU 함수를 relu라는 이름
relu = MyReLU.apply
#순전파 단계에서 신경망을 통해 예측값 y_pred를 계산
y_pred = relu(x.mm(w1)).mm(w2)
#예측값과 실제값 사이의 제곱 오차의 합으로 손실을 계산
loss = (y_pred - y).pow(2).sum()
if t % 100 == 99:
print(t, loss.item())
#손실 함수에 대한 역전파를 수행하여 w1과 w2의 기울기를 계산
loss.backward()
#가중치 업데이트 시 기울기 계산을 비활성화
with torch.no_grad():
#경사하강법을 통해 첫 번째 및 두 번째 층의 가중치를 업데이트
w1 -= learning_rate * w1.grad
w2 -= learning_rate * w2.grad
#가중치 업데이트 후 기울기를 초기화
w1.grad.zero_()
w2.grad.zero_()
99 1162.515869140625
199 22.481172561645508
299 0.7321600914001465
399 0.027399495244026184
499 0.001426524599082768
PyTorch의 torch.autograd.Function을 상속받아 사용자 정의 autograd 함수인 MyReLU를 정의
순전파 단계를 정의하는 메소드. ctx는 컨텍스트 객체로, 역전파 단계에서 사용할 정보를 저장하는 데 사용
역전파 단계를 정의하는 메소드
역전파 단계에서 사용할 입력값 input을 저장
순전파 단계에서 저장된 입력값을 불러옵니다.
grad_output은 손실 함수로부터 전달받은 출력값에 대한 변화도(기울기)
입력값이 0보다 작은 경우 변화도를 0으로 설정
x: 모델의 입력으로 사용될 무작위 데이터 텐서입니다. 크기는 N x D_in입니다.
y: x에 대한 타겟(목표) 출력값을 나타내는 무작위 데이터 텐서
w1은 첫 번째 층의 가중치로 D_in x H 차원을 가지며, w2는 두 번째 층(출력층)의 가중치로 H x D_out 차원
requires_grad=True: 이 텐서들에 대한 연산을 추적하여 역전파 시 자동으로 기울기를 계산
총 500회의 반복(에폭)으로 학습 과정을 수행
사용자 정의 ReLU 함수를 relu라는 이름
순전파 단계에서 신경망을 통해 예측값 y_pred를 계산
예측값과 실제값 사이의 제곱 오차의 합으로 손실을 계산
손실 함수에 대한 역전파를 수행하여 w1과 w2의 기울기를 계산
가중치 업데이트 시 기울기 계산을 비활성화
경사하강법을 통해 첫 번째 및 두 번째 층의 가중치를 업데이트
가중치 업데이트 후 기울기를 초기화
->PyTorch의 사용자 정의 autograd 함수인 MyReLU를 구현하여 2층 신경망을 통한 순전파와 역전파 과정을 수행하고, 손실을 계산한 뒤 경사하강법으로 가중치를 업데이트하는 학습 과정
PyTorch: nn
# -*- coding: utf-8 -*-
import torch
#신경망의 구조에 필요한 차원을 정의
N, D_in, H, D_out = 64, 1000, 100, 10
-------------------------------------------
N,D_in,H,D_out=64,1000,100,10
-------------------------------------------
# 무작위로 입력값과 출력값을 생성합니다.
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)
-------------------------------------------
x=torch.randn(N,D_in)
y=torch.randn(N,D_out)
-------------------------------------------
#세 개의 층을 갖는 순차 모델을 정의. 첫 번째 층은 D_in에서 H로 가는 선형 변환, 두 번째 층은 ReLU 활성화 함수, 세 번째 층은 H에서 D_out로 가는 선형 변환
model = torch.nn.Sequential(
torch.nn.Linear(D_in, H),
torch.nn.ReLU(),
torch.nn.Linear(H, D_out),
)
#손실 함수로 평균 제곱 오차(MSE)를 사용합니다. 여기서 reduction='sum'은 손실 값들의 합을 계산하라는 의미
loss_fn = torch.nn.MSELoss(reduction='sum')
#학습률을 정의
learning_rate = 1e-4
#총 500번의 학습 반복(에폭)을 수행
for t in range(500):
#현재 모델을 사용하여 입력 x에 대한 예측 y_pred를 계산
y_pred = model(x)
#예측된 출력 y_pred와 실제 목표값 y 사이의 손실을 계산
loss = loss_fn(y_pred, y)
#100번의 반복마다 현재의 손실 값을 출력
if t % 100 == 99:
print(t, loss.item())
#모델의 모든 파라미터의 기울기를 0으로 초기화
model.zero_grad()
loss.backward()
with torch.no_grad():
for param in model.parameters():
param -= learning_rate * param.grad
99 1.9351273775100708
199 0.07569249719381332
299 0.009513352997601032
399 0.0013505233218893409
499 0.00018912901578005403
신경망의 구조에 필요한 차원을 정의
무작위로 입력값과 출력값을 생성합니다. 세 개의 층을 갖는 순차 모델을 정의. 첫 번째 층은 D_in에서 H로 가는 선형 변환, 두 번째 층은 ReLU 활성화 함수, 세 번째 층은 H에서 D_out로 가는 선형 변환
손실 함수로 평균 제곱 오차(MSE)를 사용합니다. 여기서 reduction='sum'은 손실 값들의 합을 계산하라는 의미
학습률을 정의
총 500번의 학습 반복(에폭)을 수행
현재 모델을 사용하여 입력 x에 대한 예측 y_pred를 계산
예측된 출력 y_pred와 실제 목표값 y 사이의 손실을 계산
100번의 반복마다 현재의 손실 값을 출력
모델의 모든 파라미터의 기울기를 0으로 초기화
loss.backward()
with torch.no_grad():
-> nn 모듈을 사용하여 고차원의 신경망 모델 구성
PyTorch: optim
# -*- coding: utf-8 -*-
import torch
# N은 배치 크기, D_in은 입력값으로 이용되는 벡터의 크기입니다.
# H는 은닉층의 노드 개수를 의미하며, D_out은 출력값 벡터의 크기입니다.
N, D_in, H, D_out = 64, 1000, 100, 10
# 무작위로 입력값과 출력값을 생성합니다.
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)
# nn 패키지를 사용하여 모델과 손실 함수를 정의합니다.
model = torch.nn.Sequential(
torch.nn.Linear(D_in, H),
torch.nn.ReLU(),
torch.nn.Linear(H, D_out),
)
loss_fn = torch.nn.MSELoss(reduction='sum')
# optim 패키지를 사용하여 모델의 가중치를 갱신할 Optimizer를 정의합니다.
# 여기서는 Adam을 사용하겠습니다; optim 패키지는 다른 다양한 최적화 알고리즘을
# 포함하고 있습니다. Adam 생성자의 첫번째 인자는 어떤 Tensor가 갱신되어야 하는지
# 알려줍니다.
learning_rate = 1e-4
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
for t in range(500):
# 순전파 단계: 모델에 x를 전달하여 예상되는 y 값을 계산합니다.
y_pred = model(x)
# 손실을 계산하고 출력합니다.
loss = loss_fn(y_pred, y)
if t % 100 == 99:
print(t, loss.item())
# 역전파 단계 전에, Optimizer 객체를 사용하여 (모델의 학습 가능한 가중치인)
# 갱신할 변수들에 대한 모든 변화도를 0으로 만듭니다. 이렇게 하는 이유는
# 기본적으로 .backward()를 호출할 때마다 변화도가 버퍼(buffer)에 (덮어쓰지 않고)
# 누적되기 때문입니다. 더 자세한 내용은 torch.autograd.backward에 대한 문서를
# 참조하세요.
optimizer.zero_grad()
# 역전파 단계: 모델의 매개변수에 대한 손실의 변화도를 계산합니다.
loss.backward()
# Optimizer의 step 함수를 호출하면 매개변수가 갱신됩니다.
optimizer.step()
99 49.201316833496094
199 0.6050420999526978
299 0.004646726418286562
399 2.7959904400631785e-05
499 6.055332590904072e-08
PyTorch: Custom nn Modules
# -*- coding: utf-8 -*-
import torch
class TwoLayerNet(torch.nn.Module):
def __init__(self, D_in, H, D_out):
"""
In the constructor we instantiate two nn.Linear modules and assign them as
member variables.
"""
super(TwoLayerNet, self).__init__()
self.linear1 = torch.nn.Linear(D_in, H)
self.linear2 = torch.nn.Linear(H, D_out)
def forward(self, x):
"""
In the forward function we accept a Tensor of input data and we must return
a Tensor of output data. We can use Modules defined in the constructor as
well as arbitrary operators on Tensors.
"""
h_relu = self.linear1(x).clamp(min=0)
y_pred = self.linear2(h_relu)
return y_pred
# N은 배치 크기, D_in은 입력값으로 이용되는 벡터의 크기입니다.
# H는 은닉층의 노드 개수를 의미하며, D_out은 출력값 벡터의 크기입니다.
N, D_in, H, D_out = 64, 1000, 100, 10
# 무작위로 입력값과 출력값을 생성합니다.
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)
# 앞서 정의한 클래스를 생성(instantiating)하여 모델을 구성합니다.
model = TwoLayerNet(D_in, H, D_out)
# 손실 함수와 Optimizer를 만듭니다. SGD 생성자에 model.parameters()를 호출하면
# 모델의 멤버인 2개의 nn.Linear 모듈의 학습 가능한 매개변수들이 포함됩니다.
criterion = torch.nn.MSELoss(reduction='sum')
optimizer = torch.optim.SGD(model.parameters(), lr=1e-4)
for t in range(500):
# 순전파 단계: 모델에 x를 전달하여 예상되는 y 값을 계산합니다.
y_pred = model(x)
# 손실을 계산하고 출력합니다.
loss = criterion(y_pred, y)
if t % 100 == 99:
print(t, loss.item())
# 변화도를 0으로 만들고, 역전파 단계를 수행하고, 가중치를 갱신합니다.
optimizer.zero_grad()
loss.backward()
optimizer.step()
99 2.6760733127593994
199 0.04300420731306076
299 0.002363199135288596
399 0.0002131618239218369
499 2.1617370293824933e-05
PyTorch: Control Flow + Weight Sharing
# -*- coding: utf-8 -*-
import random
import torch
class DynamicNet(torch.nn.Module):
def __init__(self, D_in, H, D_out):
"""
생성자에서 순전파 단계에서 사용할 3개의 nn.Linear 인스턴스를 생성합니다.
"""
super(DynamicNet, self).__init__()
self.input_linear = torch.nn.Linear(D_in, H)
self.middle_linear = torch.nn.Linear(H, H)
self.output_linear = torch.nn.Linear(H, D_out)
def forward(self, x):
"""
모델의 순전파 단계에서, 무작위로 0, 1, 2 또는 3 중에 하나를 선택하고
은닉층을 계산하기 위해 여러번 사용한 middle_linear Module을 재사용합니다.
각 순전파 단계는 동적 연산 그래프를 구성하기 때문에, 모델의 순전파 단계를
정의할 때 반복문이나 조건문과 같은 일반적인 Python 제어 흐름 연산자를 사용할
수 있습니다.
여기에서 연산 그래프를 정의할 때 동일 Module을 여러번 재사용하는 것이
완벽히 안전하다는 것을 알 수 있습니다. 이것이 각 Module을 한 번씩만 사용할
수 있었던 Lua Torch보다 크게 개선된 부분입니다.
"""
h_relu = self.input_linear(x).clamp(min=0)
for _ in range(random.randint(0, 3)):
h_relu = self.middle_linear(h_relu).clamp(min=0)
y_pred = self.output_linear(h_relu)
return y_pred
# N은 배치 크기, D_in은 입력값으로 이용되는 벡터의 크기입니다.
# H는 은닉층의 노드 개수를 의미하며, D_out은 출력값 벡터의 크기입니다.
N, D_in, H, D_out = 64, 1000, 100, 10
# 무작위로 입력값과 출력값을 생성합니다.
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)
# 앞서 정의한 클래스를 생성(instantiating)하여 모델을 구성합니다.
model = DynamicNet(D_in, H, D_out)
# 손실함수와 Optimizer를 만듭니다. 이 이상한 모델을 순수한 확률적 경사 하강법
# (stochastic gradient decent)으로 학습하는 것은 어려우므로, 모멘텀(momentum)을 사용합니다.
criterion = torch.nn.MSELoss(reduction='sum')
optimizer = torch.optim.SGD(model.parameters(), lr=1e-4, momentum=0.9)
for t in range(500):
# 순전파 단계: 모델에 x를 전달하여 예상되는 y 값을 계산합니다.
y_pred = model(x)
# 손실을 계산하고 출력합니다.
loss = criterion(y_pred, y)
if t % 100 == 99:
print(t, loss.item())
# 변화도를 0으로 만들고, 역전파 단계를 수행하고, 가중치를 갱신합니다.
optimizer.zero_grad()
loss.backward()
optimizer.step()
99 41.61725616455078
199 0.8758949637413025
299 2.5072014331817627
399 1.1256434917449951
499 1.448188304901123