딥러닝(6) - 전이 학습(1)
# 전이 학습
이미지넷(ImageNet)처럼 아주 큰 데이터셋을 써서 훈련된 모델의 가중치를 가져와 우리가 해결하려는 과제에 맞게 보정해서 사용하는 것
**네트워크(=사전 훈련 모델)
아주 큰 데이터셋을 사용하여 훈련된 모델
비교적 적은 수의 데이터를 가지고도 우리가 원하는 과제를 해결하는 것이 가능해진다.
전이학습 사용은 어디서 하나요?
1. 특성 추출 기법
- ImageNet 데이터셋으로 사전 훈련된 모델을 가져온 후 마지막에 완전연결층 부분만 새로 만드는 것
- 학습할 때는 마지막 완전연결층(이미지의 카테고리를 결정하는 부분)만 학습하고 나머지 계층들은 학습되지 않게 한다.
특성 추출을 이루는 부분
합성곱층( 합성곱층과 풀링층으로 구성) + 데이터 분류기(=완전연결층, 추출된 특성을 입력받아 최종적으로 이미지에 대한 클래스를 분류하는 부분)
사전 훈련된 네트워크의 합성곱층에 새로운 데이터를 통과시키고, 데이터를 통과시킨 것에 대한 출력을 데이터 분류기에서 훈련시킴
🐍 특성 추출 사용 가능한 이미지 분류 모델
• Xception
• Inception V3
• ResNet50
• VGG16
• VGG19
• MobileNet
🐍 특성 추출 기법
👨💻 특성추출 진행하기
✍라이브러리 설치
!pip install opencv-python
import os
import time
import copy
import glob
import cv2 # OpenCV 라이브러리
import shutil
import torch
import torchvision # 컴퓨터 비전(computer vision) 용도의 패키지
import torchvision.transforms as transforms #torchvision.transforms : 데이터 전처리를 위해 사용되는 패키지
import torchvision.models as models #torchvision.models : 다양한 파이토치 네트워크를 사용할 수 있도록 도와주는 패키지
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
✍이미지 전처리 경로(routine) 지정하기(이미지 데이터 불러오기)
data_path = '../train' #이미지 데이터 불러오기
#transforms.Compose([])
transform = transforms.Compose(
[
transforms.Resize([256, 256]),
transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.ToTensor()
]) #torchvision.transform
train_dataset = torchvision.datasets.ImageFolder(
data_path,
transform=transform
) #torchvision.datasets.ImageFolder
train_loader = torch.utils.data.DataLoader(
train_dataset,
batch_size=32,
num_workers=8,
shuffle=True
)
print(len(train_dataset))
🐍 transforms.Compose()
- 이미지 데이터를 변환하여 이미 훈련된 모델(네트워크)에 대한 입력으로 사용할 수 있도록 형태 변화
- 입력값을 준비해주는 과정
⭐ torchvision.transform 파라미터
ⓐ transforms.Resize([ , ])
이미지의 크기를 조정. 여기서는 256×256 크기로 이미지 데이터를 조정한다.
ⓑ transforms.RandomResizedCrop( )
이미지를 랜덤한 크기 및 비율로 자른다.
📌📌transoforms.Resize([ , ])와 transforms.RandomResizedCrop()의 차이점은 무엇인가요?
Resize와 RandomResizedCrop 모두 이미지를 자르는 데 사용하지만 그 용도는 다르다. Resize가 합성곱층을 통과하기 위해 이미지 크기를 조정하는 전처리 과정이라면, RandomResizedCrop은 데이터 확장 용도로 사용된다.
RandomResizedCrop은 이미지를 랜덤한 비율로 자른 후 데이터 크기를 조정한다.
ⓒ RandomHorizontalFlip
이미지를 랜덤하게 수평으로 뒤집는다.
ⓓ ToTensor
이미지 데이터를 텐서로 변환한다.
🐍 datasets.ImageFolder : 이미지 전처리 허용 여부, 대상선택
- 데이터로더가 데이터를 불러올 대상(혹은 경로)과 방법(transform)(혹은 전처리)을 정의한다.
ⓐ data_path
불러올 데이터가 위치한 경로(위에서 정의해놓은 변수를 그대로 가져와서 정의한다.)
ⓑ transform
이미지 데이터에 대한 전처리 허용 여부
🐍 torch.utils.data.Dataloader : 미니배치양 지정
- 한 번에 불러올 데이터양(미니배치양)을 결정하는 batch_size를 지정해준다.
- 추가적으로 데이터를 무작위로 섞을(shuffle) 것인지도 설정함
ⓐ train_dataset
데이터셋 지정
ⓑ batch_size
한 번에 불러올 데이터양을 결정하는 배치 크기
ⓒ num_workers
데이터를 불러올 때 하위 프로세스를 몇 개 사용할지 설정하는데, 이때 너무 많은 하위 프로세스를 설정하게 되면 오류가 발생하거나 메모리 부족 현상이 발생할 수 있다.
ⓓ shuffle: 데이터를 무작위로 섞을지를 지정한다. shuffle=True로 설정하면 데이터를 무작위로 섞어서 랜덤으로 불러옴
✍학습에 사용될 이미지 출력하기
samples, labels = iter(train_loader).next()
classes = {0:'cat', 1:'dog'} # 개와 고양이에 대한 클래스로 구성된 것을 각각 0 클러스터와 1클러스터로 묶는다. (각각의 클러스터를 클래스로 만들어 이름 지정)
fig = plt.figure(figsize=(16,24)) #fig = plt.figure(figsize=(가로 길이, 세로 길이))
for i in range(24): #24개의 이미지 데이터 출력(이미지 데이터를 range라고 해주기)
a = fig.add_subplot(4,6,i+1)
a.set_title(classes[labels[i].item()]) #레이블 정보(클래스)를 함께 출력
a.axis('off')
a.imshow(np.transpose(samples[i].numpy(), (1,2,0)))
plt.subplots_adjust(bottom=0.2, top=0.6, hspace=0)
🐍 iter(전달된 데이터).next()
- iter().next() 형태로 같이 묶여서 쓰인다.
- iter()로 반복자를 구하고 그 반복자를 next()에 전달하여 차례대로 꺼낸다.
samples, labels = iter(train_loader).next()
에서 반복자는 train_loader가 되기 때문에 train_loader에서 samples와 labels의 값을 순차적으로 꺼내서 저장한다는 것이다.
** np.transpose()
A와 B의 행과 열의 수가 달라 내적이 불가능할 때는 np.transpose()나 np.reshape() 등으로 차원을 조정해야 한다.
exam = np.arange(24).reshape(2,3,4)
exam
0~24까지의 원소들을 (2,3,4)로 배열해준다.
np.transpose(exam, (2,1,0))
✍사전 훈련된 ResNet18 모델 불러오기(모델 설계 시작)
resnet18 = models.resnet18(pretrained =True) #------ pretrained=True는 사전 학습된 가중치를 사용하겠다는 의미
✍불러온 ResNet18의 합성곱층을 사용하되 파라미터에 대해서는 학습을 하지 않도록 고정함(사전 훈련된 모델의 파라미터 학습 유무 지정하기)
def set_parameter_requires_grad(model, feature_extracting=True):
if feature_extracting:
for param in model.parameters():
param.requires_grad = False
set_parameter_requires_grad(resnet18)
🐍 param.requires_grad = False
- 역전파 중 파라미터들에 대한 변화를 계산할 필요가 없음
- 모델의 합성곱층(convolutional layer)과 풀링층(pooling)을 바꿀 필요 없도록 한다.False (합성곱층과 풀링층이 파라미터임)
for param in model.parameters():
param.requires_grad = False
-위와 같이 조건문 형태로 고정하게 된다.
모델 설계 단계에서 지금까지 합성곱층 모델을 그려보았다. 이제 완전연결층(개와 고양이 클래스를 직접 분류하는 용도)을 추가해주면 된다.
✍ResNet18에 완전연결층 모델 설계해주기
resnet18.fc = nn.Linear(512, 2) #2는 클래스가 두 개라는 의미(output_dim = 2, 입력데이터 개수 =512(input_dim=2))
✍모델의 파라미터 값들 확인해보기
for name, param in resnet18.named_parameters(): # model.named_parameters()는 모델에 접근하여 파라미터 값들을 가져올 때 사용
if param.requires_grad: #parameter 값들이 존재한다면, parameter의 이름과 data값들을 출력해주기(모델층, 풀링층)
print(name, param.data)
파라미터는 weight(가중치 = 필터, 커널), 편향(bias)를 사용하고 있음을 확인 가능
✍모델 객체 생성 및 최적화, 손실함수 정의하기 (네트워크 생성)
model = models.resnet18(pretrained=True) #------ 모델의 객체 생성
for param in model.parameters(): #------ 모델의 합성곱층을 가중치 고정. 객체로 생성하였기 떄문에 다시한번 가중치를 고정해줄 필요가 있다. (일반합성곱층은 고정해주고 훈련시켜주지 않는다.)
param.requires_grad = False
model.fc = torch.nn.Linear(512, 2)
for param in model.fc.parameters(): #------ 완전연결층은 학습
param.requires_grad = True
#모델 설계 부가조건들 : optimizer, 손실함수
optimizer = torch.optim.Adam(model.fc.parameters()) # 가중치의 기울기 찾기 = 최적화 알고리즘으로 Adam을 사용함
cost = torch.nn.CrossEntropyLoss() #------ 손실 함수 정의하기
print(model) #모델 출력
✍모델 객체 생성 및 최적화, 손실함수 정의하기 (네트워크 생성)