import torch
import torch.nn as nn
import torch.optim as optim
# 데이터셋 정의 (OR 게이트)
X = torch.tensor([[0, 0], [0, 1], [1, 0], [1, 1]], dtype=torch.float32)
Y = torch.tensor([[0], [1], [1], [1]], dtype=torch.float32)
# 퍼셉트론 클래스 정의
class Perceptron(nn.Module):
def __init__(self):
super(Perceptron, self).__init__()
# 가중치와 바이어스를 수동으로 정의
self.weights = torch.randn((2, 1), dtype=torch.float32, requires_grad=True)
self.bias = torch.randn(1, dtype=torch.float32, requires_grad=True)
def forward(self, x):
# 선형 변환 (Wx + b)
x = torch.matmul(x, self.weights) + self.bias
# 활성화 함수 (시그모이드)
x = torch.sigmoid(x)
return x
# 모델, 손실 함수, 옵티마이저 정의
model = Perceptron()
criterion = nn.BCELoss()
optimizer = optim.SGD([model.weights, model.bias], lr=0.1)
# 학습
num_epochs = 1000
for epoch in range(num_epochs):
# 순전파
outputs = model(X)
loss = criterion(outputs, Y)
# 역전파 및 최적화
optimizer.zero_grad()
loss.backward()
optimizer.step()
if (epoch + 1) % 100 == 0:
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')
# 학습 후 테스트
with torch.no_grad():
outputs = model(X)
predicted = (outputs > 0.5).float()
accuracy = (predicted == Y).sum() / Y.shape[0]
print(f'Predicted:\n{predicted}\nAccuracy: {accuracy.item() * 100:.2f}%')
이런식으로 모델을 만들어 볼 수 있는데 forward는 이해가 되는데 학습할때 loss계산하고 backward하고 step으로 수정하는게 어떤 식으로 되는지 모르겠어서 찾아보았다
torch.randn((2, 1), dtype=torch.float32, requires_grad=True)
이건 2x1 랜덤값이 들어있는 행렬을 만드는건데 dtype은 데이터 타입이 32bit float형이라는 뜻이고 requires_grad는 이게 중요한 것이었는데 autograd라는걸 true로 하게한다는 것이다
https://velog.io/@ss-hj/PyTorch%EC%97%90%EC%84%9C-autograd-%EB%8F%99%EC%9E%91-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0
위 글들을 읽고 어느정도 이해했지만 행렬의 이해가 아직 부족해서 야코비 행렬을 아직 잘 모르겠다.
이어가자면 requires_grad가 true인 경우 그 행렬으로부터 계산이 될 때마다 그 계산을 그래프 형태로 기록해서 미분할 수 있게 하는 것이다.
forward에서 계산이 이루어지면 그 계산까지 다 기록을 해두고
loss = criterion(outputs, Y)와 같이 계산해도 기록이 되고
loss.backward()할때 처음부터 loss를 계산한 순간까지의 계산들에 대해서 chain rule을 적용해서 미분을 해서 loss 계산에 이용된 requires_grad가 true인 변수들이 grad 값이 추가된다.
backward()는 스칼라값일때만 된다 입력이 없을경우
loss.backward()할때마다 누적되니까 optimizer.zero_grad()를 통해 grad를 0으로 만들어주고 backward한다 optimizer.step()는 optimizer 생성할때 requires_grad가 true인 class들을 넣어주면 그 grad에 대해 optimizer의 계산식대로 그 행렬을 변화시킨다.
이번에 공부를 통해 파이토치의 텐서가 그저 행렬이 라고 생각해서 최적화 과정이 이해되지 않았다는걸 알았다.
텐서에 대해 더 알아 보고 야코비 행렬, 행렬의 미분을 좀 공부해야겠다고 생각했다.