Transfer learning (전이학습)
- 사전에 학습된 신경망의 구조와 파라미터를 재사용해서 새로운 모델(우리가 만드는 모델)의 시작점으로 삼고 해결하려는 문제를 위해 다시 학습시킨다.
- 전이 학습을 통해 다음을 해결할 수 있다.
1. 데이터 부족문제
- 딥러닝은 대용량의 학습데이터가 필요하다.
- 충분한 데이터를 수집하는 것은 항상 어렵다.
2. 과다한 계산량
- 신경망 학습에는 엄청난 양의 계산 자원이 필요하다. - 미리 학습된(pre-trained) Model을 이용하여 모델을 구성한 뒤 현재 하려는 예측 문제를 해결한다.
- 보통 Pretrained Model에서 Feature Extraction 부분을 사용한다.
- Computer Vision 문제의 경우 Bottom 쪽의 Convolution Layer(Feature Extractor)들은 이미지에 나타나는 일반적인 특성을 추출하므로 다른 대상을 가지고 학습했다고 하더라도 재사용할 수 있다.
- Top 부분 Layer 부분은 특히 출력 Layer의 경우 대상 데이터셋의 목적에 맞게 변경 해야 하므로 재사용할 수 없다.

Feature extraction 재사용
- Pretrained Model에서 Feature Extractor 만 가져오고 추론기(Fully connected layer)만 새로 정의한 뒤 그 둘을 합쳐서 모델을 만든다.
- 학습시 직접 구성한 추론기만 학습되도록 한다.
- Feature Extractor는 추론을 위한 Feature 추출을 하는 역할만 하고 그 parameter(weight)가 학습되지 않도록한다. - 모델/레이어의 parameter trainable 여부 속성 변경
- model/layer 의 parameters() 메소드를 이용해 weight와 bias를 조회한 뒤 requires_grad 속성을 False로 변경한다.
Backbone, Base network
- 전체 네트워크에서 Feature Extraction의 역할을 담당하는 부분을 backbone/base network라고 한다.
Fine-tuning(미세조정)
- Transfer Learning을 위한 Pretrained 모델을 내가 학습시켜야 하는 데이터셋(Custom Dataset)으로 재학습시키는 것을 fine tunning 이라고 한다.
- 주어진 문제에 더 적합하도록 Feature Extractor의 가중치들도 조정 한다.


3번 전략 코드
import
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import models, datasets, transforms
from torchinfo import summary
from module.train import fit
from module.utils import plot_fit_result
import os
from zipfile import ZipFile
!pip install gdown -U
import gdown
device = 'cuda' if torch.cuda.is_available() else "cpu"
device
data, datasets 디렉토리 생성
os.makedirs("data", exist_ok=True)
os.makedirs("datasets", exist_ok=True)
download
# download
url = 'https://drive.google.com/uc?id=1YIxDL0XJhhAMdScdRUfDgccAqyCw5-ZV'
path = r'data/cats_and_dogs_small.zip'
gdown.download(url, path, quiet=False)
downlad한 파일 압축풀기
# 압축 풀기 - zip
image_path = os.path.join("datasets", "cats_and_dogs_small") # 압축 풀 경로
with ZipFile(path) as zfile: # ZipFile(압축파일경로)
zfile.extractall(image_path) # extractall(압축풀 디렉토리 경로), 경로생략 -> 현재 directory에 푼다
Dataset, DataLoader를 생성
# transform 정의
# Trainset: Image augmentation + Resize + ToTensor
train_transform = transforms.Compose([
transforms.Resize((224, 224)) # Image Net 학습 모델 -> input size: 224, 224
###############################
# Image augmentation
,transforms.ColorJitter(brightness=0.3, contrast=0.3, saturation=0.5, hue=0.15) # 상수 -> 1-상수 ~ 1+상수 범위에서 변환
,transforms.RandomHorizontalFlip() # p = 0.5(default)
,transforms.RandomVerticalFlip()
,transforms.RandomRotation(degrees=90) # -90 ~ +90 범위에서 회전
################################
,transforms.ToTensor()
,transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))
# 채널별: (평균), (표준편차) # (각픽셀값=평균)/표준편차
])
# validation/test set -> Image augmentation은 정의하지 않는다.
# Resize, ToTensor, Normalize만 한다.
test_transform = transforms.Compose([
transforms.Resize((224, 224))
,transforms.ToTensor()
,transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))
])
Dataset 생성
train_set = datasets.ImageFolder(os.path.join(image_path, "train"), transform=train_transform)
valid_set = datasets.ImageFolder(os.path.join(image_path, "validation"), transform=test_transform)
test_set = datasets.ImageFolder(os.path.join(image_path, "test"), transform=test_transform)
Dataloader 생성
# DataLoader
# num_workers=os.cpu_count() -> cpu 프로세서 개수
train_loader = DataLoader(train_set, batch_size=BATCH_SIZE, drop_last=True, shuffle=True, num_workers=os.cpu_count()) # Data를 load할 때 병렬처리 -> 한번에 몇개씩 동시에 loading할지 설정
valid_loader = DataLoader(valid_set, batch_size=BATCH_SIZE, num_workers=os.cpu_count())
test_loader = DataLoader(test_set, batch_size=BATCH_SIZE, num_workers=os.cpu_count())
사전학습된 VGG19 모델 -> model 생성
# pretrained VGG19 모델
model = models.vgg19(weights=models.VGG19_Weights.DEFAULT)
파라미터 확인
- 파라미터(Tensor)의 train가능 여부 -> Tensor객체.requires_grad: True - Trainable, False: Non-Trainable(Frozen)
# 파라미터 확인 - trainable 상태를 확인
for param in model.parameters():
print(param.shape, param.requires_grad)
break
모델의 parameter들을 frozen -> 학습이 안되도록 만든다.
# VGG19의 parameter들을 frozen (학습이 안되도록 처리 -> optimizer.step() 시 업데이트 안되도록 한다.
# requires_grad=False
for param in model.parameters():
param.requires_grad = False
summary를 실행
- Trainable params: 0 확인 -> 학습 가능한 파라미터가 없다는 의미
summary(model, (1, 3, 224, 224))
새로운 분류기를 정의하여 model에 추가한다.
# classifier를 정의해서 model에 추가.
# VGG19의 attribute 변수 classifier에 추론기가 정의설정되 있는 상태
# => 새로운 추론기(개/고양이 분류)를 정의한 뒤 classifier attribute 변수에 할당
model.classifier = nn.Sequential(
nn.Linear(512 * 7 * 7, 4096)
,nn.ReLU()
,nn.Dropout(p=0.3)
,nn.Linear(4096, 4096)
,nn.ReLU()
,nn.Dropout(p=0.3)
,nn.Linear(4096, 2) # 개/고양이 두개 class를 분류 => out: 2
)
summary를 실행
- output layer의 output shape: [1, 2]를 확인 -> 원래 모델은 [1, 1000]이였고, 분류 class가 1000개에서 2개로 줄였다는 것을 의미한다.
summary(model, (1, 3, 224, 224))
새로운 분류기를 넣은 모델을 학습 시킨다.
# 학습
model_save_path = "/content/drive/MyDrive/pytorch/models/cat_dog_model1.pth"
model = model.to(device)
loss_fn = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=LR)
result = fit(train_loader, valid_loader, model, loss_fn, optimizer, N_EPOCH, save_best_model=True, device=device, mode="multi", save_model_path=model_save_path)
테스트셋으로 검증
# 테스트셋으로 검증
from module.train import test_multi_classification
best_model = torch.load(model_save_path)
loss, acc = test_multi_classification(test_loader
,best_model
,loss_fn
,device=device
)
loss, acc 확인
print(loss, acc)
이미지를 읽고 추론하는 함수 predict 정의
from PIL import Image
def predict(image_path, model, transform, device):
img = Image.open(image_path) # 이미지 읽기 -> PIL.Image
input_data = transform(img) # transform을 이용해서 전처리
input_data = input_data.unsqueeze(dim=0) # (c, h, w) -> (1, c, h, w)
input_data = input_data.to(device)
model.eval()
with torch.no_grad():
pred = model(input_data) # 모델을 이용해서 추론
pred_prob = nn.Softmax(dim=-1)(pred) # 추론결과를 확률값으로 계산
max_v = pred_prob.max(dim=-1)
label = max_v.indices.item() # label 조회
label_name = "cat" if label==0 else "dog"# label name
prob = max_v.values.item() # 확률값 조회
return label, label_name, prob
결과 확인
- 예측결과(dog 또는 cat)와 정확도를 확인한다.
predict("/content/sample_data/dog.jpg", best_model, test_transform, device)
predict("/content/sample_data/cat.jpg", best_model, test_transform, device)
2번 전략 코드
- 3번 전략코드의 모델 정의 이전까지는 코드가 같으므로 생략
모델 정의
- 사전학습된 VGG19 모델 model2를 정의
# 모델 정의
# VGG19 다운로드
model2 = models.vgg19(weights=models.VGG19_Weights.DEFAULT)
layer의 일부만 freezing시킨다. -> 일부는 학습되게 함.
# conv base(backbone) layer를 조회 -> 0 ~ 36 중에서 0 ~ 32: frozen, 33 ~ 36: trainable
for idx, layer in enumerate(model2.features):
# print(idx, type(layer))
if idx < 33: # 0 ~ 32 -> frozen
for param in layer.parameters(): # model.parameters(), layer.parameters() -> 구성 weight, bias를 반환하는 generator
# print(param.shape)
param.requires_grad = False
새로운 분류기로 변경
# classifier를 변경
model2.classifier = nn.Sequential(
nn.Linear(512 * 7 * 7, 4096)
,nn.ReLU()
,nn.Dropout(p=0.3)
,nn.Linear(4096, 4096)
,nn.ReLU()
,nn.Dropout(p=0.3)
,nn.Linear(4096, 2)
)
학습
# 학습
model_save_path = "/content/drive/MyDrive/pytorch/models/cat_dog_model2.pth"
model2 = model2.to(device)
loss_fn = nn.CrossEntropyLoss()
optimizer = optim.Adam(model2.parameters(), lr=LR)
result = fit(train_loader, valid_loader, model2, loss_fn, optimizer, N_EPOCH, save_best_model=True, device=device, mode="multi", save_model_path=model_save_path)
이후 3번 전략 코드의 검증 및 추론 코드대로 하면 완료.
'Data_Analysis_Track_33 > Python' 카테고리의 다른 글
| Python_Deeplearning_pytorch_11(LSTM을 활용한 주가예측) (1) | 2023.11.03 |
|---|---|
| Python_Deeplearning_pytorch_10(RNN, LSTM) (1) | 2023.11.03 |
| Python_Deeplearning_pytorch_09(VGGNet Pretrained 모델을 이용해 이미지 분류) (0) | 2023.10.31 |
| Python_Deeplearning_pytorch_08(Image Augmentation) (0) | 2023.10.30 |
| Python_Deeplearning_pytorch_07(CNN_Model 정의) (0) | 2023.10.30 |