import torch
import torch.nn as nn
import torch.optim as optim
class SimpleMultiClassModel(nn.Module): # step1. 클래스는 pytorch의 nn.Module을 상속받는다
def __init__(self):
super(SimpleMultiClassModel, self).__init__()
self.fc = nn.Linear(5, 3)
def forward(self, x): # 순전파 메서드
return self.fc(x) # step2. fc(x)는 선형 변환을 통해 입력을 3개의 출력 클래스로 변환한다
model = SimpleMultiClassModel()
criterion = nn.CrossEntropyLoss() # 교차 엔트로피 손실 함수 이용 (주로 다중클래스 분류에서 사용)
optimizer = optim.Adam(model.parameters(), lr=0.01) # adam 옵티마이저를 사용한 파라미터 업데이트
# step 3. 데이터 생성
inputs = torch.randn(4, 5)
labels = torch.tensor([0, 2, 1, 0])
for _ in range(10):
preds = model(inputs) # step4. 순전파에 입력값을 넣고 예측값 계산
loss = criterion(preds, labels) # step 5. 손실 계산
print(loss.item())
optimizer.zero_grad() # step 6. 이전 단계에서 계산된 기울기를 0으로 초기화 (기울기 계속 증가를 막음)
loss.backward() # step 7. 손실에 대한 역전파 수행 (파라미터에 대한 기울기 계산)
optimizer.step() # step 8. 계산된 기울기를 사용하여 옵티마이저가 모델의 파라미터 업데이트
ex) x 이외에도 y, z 등 여러 변수들이 있을 수 있는데, 이 중에서 x에 대해서만 미분한다면 y, z 등 다른 변수들은 고정한 채 x만을 변화시켰을 때 함수값이 얼마나 변하는지를 측정한 값
수치미분의 단점을 보완한다
=> 다변수 함수 f(x, y) 에 대해 편미분을 벡터 형태로 나타낸 것을 기울기 벡터(Gradient Vector)라고 한다.
7. 기울기
01. sigmoid의 기울기 소실 문제
import numpy as np
import matplotlib.pyplot as plt
def sigmoid(x):
return 1 / (1 + np.exp(-x))
np.random.seed(0)
weight = np.random.randn(10, 10) * 0.01
x = np.random.randn(10,1)
for i in range(1, 11):
x = sigmoid(np.dot(weight, x)) # x값과 가중치 내적
print(f'{i}번째 층 출력 평균 : {np.mean(x)}')
[출력]
1번째 층 출력 평균 : 0.4923981391662232
2번째 층 출력 평균 : 0.5007433168636295
3번째 층 출력 평균 : 0.5007308165030734
4번째 층 출력 평균 : 0.5007305713032845
5번째 층 출력 평균 : 0.5007305725302615
6번째 층 출력 평균 : 0.5007305725406197
7번째 층 출력 평균 : 0.5007305725405601
8번째 층 출력 평균 : 0.5007305725405591
9번째 층 출력 평균 : 0.5007305725405591
10번째 층 출력 평균 : 0.5007305725405591
sigmoid 함수를 여러번 적용하면 0또는 1에 가까운 값으로 수렴하게 되어 변화가 일어나지 않는다.
이러한 문제를 기울기 소실(vanishing gradient) 이라고 한다.
따라서 다양한 활성화 함수를 활용해야 한다.
02. 잔차연결 효과
잔차연결(skip connection)
일반적인 신경망 모델 학습 시에는 모델 층이 깊어질수록 학습 결과가 좋다.
하지만 층이 너무 깊거나 노드 수가 너무 크면 이전 층에 대한 정보 손실이 발생하고 이는 가중치가 잘못 갱신되는 문제를 초래한다.
따라서 이전 층의 정보를 이용하여 정보를 연결하는 잔차 연결을 적용한다.
즉, 네트워크의 입력과 출력이 더해진 것이 다음 층의 입력으로 사용되는 것이다
# 두 개의 선형 레이어를 가진 신경망
class SimpleNN(nn.Module):
def __init__(self):
super(SimpleNN, self).__init__()
self.layer1 = nn.Linear(10,10)
self.layer2 = nn.Linear(10, 10)
def forward(self, x):
return self.layer2(torch.relu(self.layer1(x)))
# 단일 선형 레이어를 가진 신경망
class ResidualNN(nn.Module):
def __init__(self):
super(ResidualNN, self).__init__()
self.layer = nn.Linear(10,10)
def forward(self, x):
return torch.relu(self.layer(x))
# 잔차 연결을 포함하는 3개의 레이어를 가진 신경망
class DeeperResidualNN(nn.Module):
def __init__(self):
super(DeeperResidualNN, self).__init__()
self.layer1 = nn.Linear(10,10)
self.layer2 = nn.Linear(10,10)
self.layer3 = nn.Linear(10,10)
def forward(self, x):
residual = x
x = torch.relu(self.layer1(x)) + residual
residual = x
x = torch.relu(self.layer2(x)) + residual
x = self.layer3(x) + residual
return x
신경망을 변경하고 손실함수를 적용하여 학습시킨 결과, 잔차 연결 신경망의 기울기 크기가 가장 작았다.
물론 항상 잔차 연결이 최적의 손실값을 가지는 것은 아니므로 노드 수나 은닉에 따라 조정해야 한다.
💡 Keep
머신러닝 프로젝트와 학습을 병행하는 일이 생각보다 쉽지 않은데 그래도 할거는 꿋꿋하게 해나가고 있다는 점 칭찬...⭐
프로젝트랑 학습이랑 병행하니까 힘든 점도 있지만 오히려 좋은 점은 욕심이 생긴다는 점? 주제를 찾고 좋은 팀원들이랑 의견을 맞춰나가는 것 자체도 즐겁고, 평소에 관심 있던 이슈를 구현해본다는 점이 재밌는 일인것 같다 !
⚠️ Problem
일단 딥러닝 강의를 들어오면서부터 개념적으로 헷갈리는게 너무 많다.... 특히 편미분이랑 수치미분 계산식 적용 이런 수학적인 것들.. 개강 초반에 예복습 스터디에 안들었는데 프로젝트하랴 코테하랴 복습하랴 정신이 없어지고 있다 ㅜㅜㅜ그래도 한달 반 조금 넘는 시간동안 기대 이상으로 지식을 많이 얻었다는 느낌이다. 학교때 배웠던 개념도 종강과 동시에 머릿속에서 없어지는 편이었는데 지금은 반강제적으로(?) 꽉 잡고 있을 수 있는 환경이 좋다ㅋㅋㅋ 제때제때 복습하고 체력관리 잘하도록 하자