GPU 머신으로 앞에 전이학습에서 활용한 개미와 벌의 화상을 분류하는 모델을 학습시킨다. 학습한 네트워크의 결합 파라미터를 저장하는 방법과 저장된 파라미터를 로드하는 방법에 대해서 알아보겠다.
- 파인튜닝
파인튜닝은 출력층 등을 변경한 모델을 학습된 모델을 기반으로 구축한 후 직접 준비한 데이터로 신경망 모델의 결합 파라미터를 학습시키는 방법이다. 결합 파라미터의 초깃값은 학습된 모델의 파라미터를 사용한다.
파인튜닝은 전이학습과는 달리 출력층 및 출력층에 가까운 부분뿐만 아니라 모든 층의 파라미터를 다시 학습시킨다. 다만 일반적으로 입력층에 가까운 부분의 파라미터는 학습률을 작게 설정하고(경우에 따라서는 변화시키지 않고) 출력층에 가까운 부분의 파라미터는 학습률을 크게 설정한다.
전이학습처럼 학습된 모델을 기반으로 하는 파인튜닝은 직접 준비한 데이터가 적어도 높은 성능의 딥러닝을 실현하기 쉽다는 장점이 있다.
- 데이터셋과 데이터 로더 작성
from dataloader_image_classification import ImageTransform,make_datapath_list,HymenopteraDataset train_list=make_datapath_list(phase='train') val_list=make_datapath_list(phase='val') size=224 mean=(0.485,0.456,0.406) std=(0.229,0.224,0.225) train_dataset=HymenopteraDataset( file_list=train_list,transform=ImageTransform(size,mean,std),phase='train' ) val_dataset=HymenopteraDataset( file_list=val_list,transform=ImageTransform(size,mean,std),phase='val' ) # DataLoader 작성 batch_size=32 train_dataloader=torch.utils.data.DataLoader( train_dataset,batch_size=batch_size,shuffle=True ) val_dataloader=torch.utils.data.DataLoader( val_dataset,batch_size=batch_size,shuffle=False ) dataloaders_dict={'train':train_dataloader,'val':val_dataloader}
- 네트워크 모델 작성
# 학습된 VGG-16 모델 로드
use_pretrained=True
net=models.vgg16(pretrained=use_pretrained)
# VGG16의 마지막 출력층의 출력 유닛을 개미와 벌 두개로 변경
net.classifier[6] = nn.Linear(in_features=4096,out_features=2)
net.train()
print('네트워크 설정 완료: 학습된 가중치를 로드하고 훈련 모드로 설정했습니다.')
- 손실함수 정의
크로스 엔트로피 오차 함수를 사용하여 손실함수를 정의한다.
criterion=nn.CrossEntropyLoss()
- 최적화 방법 설정
파인튜닝은 최적화 기법의 설정이 전이학습과 다르다. 모든 층의 파라미터를 학습할 수 있도록 옵티마이저를 설정한다.
먼저 각 층의 학습률을 바꿀 수 있도록 파라미터를 설정한다. VGG-16 전반부 features 모듈의 파라미터를 update_param_names_1 변수에, 후반부 전결합 층의 classifier 모듈중 처음 두개의 전결합 층 파라미터를 update_param_names_2 변수에, 교체한 마지막 전결합 층 파라미터를 update_param_names_3 변수에 저장한다. 각각 다른 학습률을 적용할 수 있다.
params_to_update_1=[]
params_to_update_2=[]
params_to_update_3=[]
update_param_names_1=['features']
update_param_names_2=['classifier.0.weight',
'classifier.0.bias','classifier.3.weight',
'classifier.3.bias']
update_param_names_3=['classifier.6.weight','classifier.6.bias']
for name, param in net.named_parameters():
if update_param_names_1[0] in name:
param.requires_grad=True
params_to_update_1.append(param)
print('params_to_update_1에 저장:',name)
elif name in update_param_names_2:
param.requires_grad=True
params_to_update_2.append(param)
print('params_to_update_2에 저장:',name)
elif name in update_param_names_3:
param.requires_grad=True
params_to_update_3.append(param)
print('params_to_update_3에 저장', name)
else:
param.requires_grad=False
print('경사 계산 없음, 학습하지 않음:',name)
이어서 각 파라미터에 최적화 방법을 설정한다. 1.3절과 마찬가지로 모멘텀SGD를 사용한다.
# 최적화 방법 설정
optimizer=optim.SGD([
{'params':params_to_update_1,'lr':1e-4},
{'params':params_to_update_2,'lr':5e-4},
{'params':params_to_update_3,'lr':1e-3}
],momentum=0.9)
- 학습 및 검증 실시
# 모델을 학습시키는 함수 작성
def train_model(net,dataloaders_dict,criterion,optimizer,num_epochs):
device=torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
print('사용 장치:',device)
net.to(device)
# 네트워크가 어느 정도 고정되면 고속화시킨다.
torch.backends.cudnn.benchmark=True
for epoch in range(num_epochs):
print('Epoch {}/{}'.format(epoch+1,num_epochs))
print('================')
# 에폭별 훈련 및 검증 루프
for phase in ['train','val']:
if phase=='train':
net.train() # 모델을 훈련 모드로
else:
net.eval() # 모델을 검증 모드로
epoch_loss=0.0 # 에폭 손실 합
epoch_corrects=0 # 에폭 정답 수
# 학습하지 않았을 때의 검증 성능을 확인하기 위해 epoch=0의 훈련 생략
if (epoch==0) and (phase=='train'):
continue
# 데이터 로더에서 미니 배치를 꺼내 루프
for inputs,labels in tqdm(dataloaders_dict[phase]):
inputs=inputs.to(device)
labels=labels.to(device)
optimizer.zero_grad()
# 순전파 계산
with torch.set_grad_enabled(phase=='train'):
outputs=net(inputs)
loss=criterion(outputs,labels) # 손실 계산
_,preds=torch.max(outputs,1) # 라벨 예측
# 훈련 시에는 오차 역전파법
if phase=='train' :
loss.backward()
optimizer.step()
# 결과 계산
epoch_loss += loss.item() * inputs.size(0) # 손실의 합계 갱신
# 정답 수의 합계 갱신
epoch_corrects += torch.sum(preds==labels.data)
# 에폭별 손실과 정답률 표시
epoch_loss=epoch_loss / len(dataloaders_dict[phase].dataset)
epoch_acc=epoch_corrects.double() / len(dataloaders_dict[phase].dataset)
print('{} Loss:{:.4f} Acc: {:.4f}'.format(phase,epoch_loss,epoch_acc))
학습 및 검증을 실행한다
num_epochs=2
train_model(net,dataloaders_dict,criterion,optimizer,num_epochs=num_epochs)
첫 에폭0에서는 학습을 continue로 건너뛴다. 미학습의 신경망으로 분류하고 그 결과 검증데이터의 정답률 Acc는 약 44%가 된다. 처음에는 전이학습처럼 개미와 벌의 화상을 능숙하게 분류할 수 없다. 그 후 1에폭 학습으로 학습 데이터에 대한 정답률은 약 72%, 검증 데이터의 정답률은 약 95%가 된다.
- 학습한 네트워크 저장 및 로드
저장할 경우에는 네트워크 모델의 net 변수를 .state_dict()를 활용해 파라미터를 사전형 변수로 꺼낸 후 torch.save()로 저장한다. save_path 변수는 저장할 파일의 경로이다.
save_path='./weights_fine_tuning.pth'
torch.save(net.state_dict(),save_path)
로드할 경우에는 torch.load()로 사전형 객체를 로드하여 네트워크를 load_state_dict()로 저장한다. GPU 상에 저장한 파일을 CPU에 로드할 때는 map_location을 사용해야 한다.
# 파이토치 네트워크 파라미터 로드
load_path='./weights_fine_tuning.pth'
load_weights=torch.load(load_path)
net.load_state_dict(load_weights)
# GPU 상에 저장된 가중치를 CPU에 로드할 경우
load_weights=torch.load(load_path,max_location={'cuda 0': 'cpu'})
net.load_state_dict(load_weights)
파인튜닝을 활용하여 소량의 데이터로도 높은 성능의 딥러닝을 실현할 수 있다.
'Deep learning > 모델 구현' 카테고리의 다른 글
37. Optimizer 로 수행하는 매개변수 갱신 (0) | 2021.10.23 |
---|---|
36. Model 클래스 (0) | 2021.10.23 |
35. 매개변수를 모아두는 계층 (0) | 2021.10.17 |
34. 신경망 (0) | 2021.10.17 |
33. 형상 변환 함수, 합계 함수 (0) | 2021.10.16 |