프로젝트 1 - 학습
- Loss
- Optimizer
- Metric
- 학습!
- 추론
파라미터를 업데이트하는 방향을 정하는 척도로, 사용하는 loss가 달라지면 파라미터를 업데이트하는 방법도 달라진다.
loss.backward()
는 오차 역전파에 해당하는 메소드로, 이걸 실행하면 모델 파라미터가 업데이트된다. (required_grad=False
설정된 경우 업데이트하지 않음)
loss도 nn.Module
을 상속받은 패밀리라서 안을 뜯어보면 forward
메소드를 가지고 있다. backward
가 실행되면 모델이 forward
를 실행해서 연쇄적으로 레이어의 forward
가 실행되는 것과 반대로, loss에서부터 거꾸로 연쇄가 일어나서 모델 파라미터가 업데이트된다.
옵티마이저에 추가로 설정할 수 있는 팁들
학습이 잘 되었는지 평가하는 지표다. 보통은 accuracy를 쓰지만 클래스별 데이터 분포가 균일하지 않은 경우에는 accuracy가 객관적 지표가 될 수 없다. 데이터 분포가 불균형할 때는 적은 데이터를 가진 클래스의 예측 성능이 떨어지는데, 데이터가 많은 클래스가 accuracy에 주는 영향이 커서 데이터가 적은 클래스 예측이 틀려도 accuracy에 별 영향을 주지 못한다. 그럴 때는 F1 score를 지표로 사용하는 것이 더 좋다.
model.train()
모델을 학습시키기 전에 model.train()
을 써서 모델을 학습 모드로 설정해야 한다.
optimizer.zero_grad()
학습할 때는 배치 단위로 데이터를 보게 된다.(forward, grdient 계산, 역전파, loss) zero_grad()
를 설정하지 않으면 이전 배치의 grdient 정보가 누적되어 남아 있게 된다. 이전 배치의 정보가 남아 있으면 새 배치의 loss 계산, 나아가 학습에 영향을 줄 수 있다. 한번에 하나의 배치에 대한 loss만 계산하기 위해서 이전 배치의 정보를 날려버리는 것이다.
loss 함수 설정
nn.Module
안에 있는 다양한 손실 함수 중 하나를 골라서 criterion
이라고 이름을 붙인 다음 해당 클래스의 객체를 만들어 loss
라고 이름붙여서 많이들 쓰는 것 같다.
criterion = torch.nn.CrossEntropyLoss()
loss = criterion(outputs, labels)
loss.backward()
와 optimizer.step()
backward
를 하면 loss
에서부터 연결된 레이어를 거꾸로 타고 올라가서 모델의 파라미터가 업데이트된다. (오차 역전파)
step
을 하면 업데이트된 파라미터를 실제 데이터데 적용시키고 다음 배치로 넘어간다.
Gradient accumulation
배치 사이즈를 크게 잡고 데이터를 한번에 많이 보고 싶은데 GPU 한계 등으로 그렇게 할 수 없을 때 쓰는 방법이다. zero_grad()
를 따로 써 주지 않으면 이전 배치의 gradient 정보가 남아 있다는 것을 이용하여 배치 사이즈를 크게 한 것 같은 효과를 얻을 수 있다.
예를 들어 배치 사이즈가 1인데 한번에 5개씩 보는 효과를 내고 싶다면 매번 zero_grad()
로 이전 배치의 정보를 날려버리는 대신 배치 5개를 볼 동안 graient를 유지한다. 이 때 gradient를 초기화시키지 않았기 때문에 loss가 누적되어 커지는데, loss가 커지는 것은 의도하지 않았기 때문에 loss를 accumulation number(예시에서는 5)로 나눠 준다. 한번에 5개 데이터를 묶어서 가상의 배치로 만들었기 때문에 optimizer.step()
도 5개 배치마다 해야 한다.
num_accum = 5
optimizer.zero_grad() # 시작하기 전에는 한번 날려주자
for epoch in range(10):
running_loss = 0.0
for i, data in enumerate(trainLoader, 0):
inputs, labels = data
outputs = net(inputs) # 이건 신경망 모델인듯?
loss = criterion(outputs, labels)/num_accum
loss.backward()
if i % num_accum == 0:
optimizer.step()
optimizer.zero_grad()
model.eval()
모델을 evaluation 모드로 설정한다. 학습 중간에 evaluation할 경우 evaluation이 끝나고 나서 다시 model.train()
을 써서 학습 모드로 돌려 놔야 한다.
with torch.no_grad():
torch.no_grad()
는 evaluation할 때 쓰느데, gradient를 업데이트하지 않겠다고 설정하는 것이다. 이걸 with
구문과 함께 쓰면 그 안에 있는 모든 작업이 grdient를 업데이트하지 않는 상태에서 실행된다.
fine tuning과 featrue extraction
fine tuning을 하려면 사전 훈련된 모델의 가중치와 바이어스 정보(pretrained=True
)를 다 가져온 상태에서 모델의 CNN도 같이 학습시켜야 한다. feature extraction을 하려면 모델의 CNN 부분을 따로 동결시켜 놔야 한다. 코드 어떻게 해야 하더라?
augmentation 할 때 데이터가 불어나는 것은 아닌 듯
데이터 양을 늘리려면 데이터로더에 똑같은 데이터를 여러 번 집어넣되 적용할 transformation을 다르게 해야 할 것 같다.
데이터의 밸런스를 맞추려고 마스크 쓴 데이터 일부를 안 갖고 와서 모델을 학습시켰더니 검증 정확도가 올랐다고 한다. 이 부분은 각자 고민해 봐야 할 문제인 듯
optimizer는 adamp가 엄청 좋다고 한다! 성능도 속도도 굿