Galaxy Zoo classification
- 목적
Galaxy Zoo data를 이용하여 머신러닝으로 은하의 morphology를 구별의 정확도를 확인 하고자 한다.
CAM을 통해서 은하를 분류하는데 있어 머신러닝 모델이 가장 중요하게 생각하는 structure를 확인 하고자 한다.
자료 정리
Galaxy Zoo 2 : Galaxy Zoo 2: detailed morphological classifications for 304 122 galaxies from the Sloan Digital Sky Survey
Galaxy Zoo "Class 1"
Class 1 - 1 : Smooth --> move to Class 7
Class1 - 2 : Features or Disk --> move to Class 2
Class 1 - 3 : star or Artifact --> move to END
준비작업
*** 각 Class별 폴더를 만들고 해당 하는 이미지를 분류해서 넣어주자. ****
1-1. 학습을 위한 이미지 "transforms"
from torchvision import transforms
input_size=224
rotation=90
full_transforms = transforms.Compose(
[transforms.Resize((input_size,input_size)),
transforms.RandomRotation(rotation),
transforms.ToTensor(),
transforms.Normalize([0.5,0.5,0.5],[1,1,1])])
transforms.Resize : 학습할 이미지 size를 통일 하기 위해 이미지 사이즈를 재정의한다.
RandomRotation() : 학습할 이미지를 Random하게 회전시킨다.
Normalize([],[]) : image 자료가 jpg, png처럼 color image라면 r,g,b 3개의 레이어를 가지고 있을 것이다. 각각의 레이어에 Normalize한다고 생각하면 된다.
*** transforms.Compose의 다양한 함수들을 알아둘 필요가 있다.!!
“transforms” 함수는 학습할 이미지를 어떻게 변화시킬것인지에 대한 함수를 정의하는 것이다. 따라서 우리는 이미지를 불러올때 이 함수를 적용하여 이미지를 변환한 후 학습 이미지로 사용할 것이다.
1-2 이미지 불러오기
*** 참고자료***
https://honeyjamtech.tistory.com/38
https://github.com/ufoym/imbalanced-dataset-sampler?fbclid=IwAR1IYT6RzMTjk3CBwRlK99w-npjURktHKiUC2AJ3Qs2blaTd2WfLoKilUS0
from torchvision import datasets
full_dataset=datasets.ImageFolder(train_dir,transform=full_transforms)
train_size = int(0.8 * len(full_dataset)) # 전체 자료의 80% size를 계산한다.
valid_size = len(full_dataset) - train_size # 나머지 20% size를 계산한다.
train_dataset, valid_dataset = torch.utils.data.random_split(full_dataset, [train_size, valid_size]) # 전체 자료를 train data와 validation data로 나누는데 "torch.utils.data.random_split"을 이용하여 random하게 8:2로 자료를 나눠준다.
datasets.ImageFolder 를 사용하기 위해서는 이미지의 label별로 Folder 형태로 정리되어 있어야 한다. 예로들이 GalImage/elliptical, GalImage/spiral ... 이런식으로 폴더가 정리 되어 있다고 한다면, train_dir은 ./GalImage/가 되어야 한다. 즉, datasets.Folder는 GalImage 하위 폴터를 탐색하여 image를 불러드린다는 것을 의미한다.
학습할 이미지를 가져오기전에 우리는 학습자료를 두 그룹으로 나눌 것이다. 학습자료(Train data) + 모델확인하기 위한 자료(validation data)
import torch
_batch_size=64
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=_batch_size, shuffle=True)
valid_loader = torch.utils.data.DataLoader(valid_dataset, batch_size=_batch_size, shuffle=True)
train_loader = torch.utils.data.DataLoader(train_dataset, sampler=ImbalancedDatasetSampler(train_dataset), batch_size=_batch_size)
"torch.utils.data.DataLoader" 는 불러드린 image를 학습하기위한 적당한 형태의 구조로 묶는 역확을 한다다. 예로 들어 batch_size = 64는 64개의 이미지를 1개의 set로 묶는 것을 의미하는데, training_dataset에서 "datasets.ImageFolder"를 이용한 dataset을 만들었기 때문에 각 이미지 당 해당하는 larbel를 같이 가지고 다닌다는 점을 기억하자!!
ImbalancedDatasetSampler : train sample 중 분류할 class가 동일 수로 분포해 있어야 옳은 학습을 할 수 있다. 이렇게 동일한 수로 맞추기 위한 함수
모델 (Model)
2.1 vgg16 model
이번에 사용한 model은 VGG16이라는 모델이다. 모델에 자세한 설명은 다음 링크에서 확인하시면 된다.
Transfer Learing을 사용할 것이다.
https://neurohive.io/en/popular-networks/vgg16/
https://bskyvision.com/504
Transfoer Learing에 대한 정보
http://incredible.ai/artificial-intelligence/2017/05/13/Transfer-Learning/
https://jeinalog.tistory.com/13
https://towardsdatascience.com/transfer-learning-from-pre-trained-models-f2393f124751
from torchvision import models
Net = models.vgg16(pretrained=True) : # vgg16을 다운 받는다. 기본적으로 'torchvision'에 'models' 함수에 링크가 걸려있는것 같다.
display(Net) # model의 구조를 확인한다
# 사전 훈련 된 모델에서 가중치를 업데이트하지 않도록합니다.
for param in Net.parameters():
param.requires_grad = False
in_features = 25088 # 사전에 학습된 모델을 통해서 알 수 있다. vgg16을 받아서 사용한다면, display(model)을 통해서 확인하는 값을 넣으면 된다.
out_categories = 3 # 분류하고자 하는 label의 개수이다. 앞서 이미지를 load할때 "datasets.ImageFolder" 를 사용했다면, sub-class의 폴더 개수와 값아야 한다.
layer_1 = 512
classifier = nn.Sequential(OrderedDict([
('dropout1', nn.Dropout(0.5)),
('fc1', nn.Linear(in_features, layer_1)),
('relu', nn.ReLU()),
('dropout2', nn.Dropout(0.5)),
('fc2', nn.Linear(layer_1, out_categories)),
('output', nn.LogSoftmax(dim=1))
]))
Net.classifier = classifier
2.1 resnet18 model
만약에 resnet18 model을 사용하고 싶다면...
다음과 같습니다.
Num_class 는 분류하고자 하는 종류의 수 입니다.
Num_class 는 model의 최종 out_feature를 바꿔주는 역활을 합니다. 물론 구별하고자 하는 수보다 높게 들어가 있어도 정상 작동을 하지만, 그렇게 되면 out_feature에 필요없는 자료가 쌓이게 됩니다. 따라서 구별하고자 하는 class 숫자를 맞춰주는 편이 좋습니다.
#resnet model loader
device = torch.device("cuda:0" if (torch.cuda.is_available() and ngpu > 0) else "cpu")
num_class=2
Net = models.resnet18(pretrained=True)
num_ftrs = Net.fc.in_features
Net.fc = nn.Linear(num_ftrs,num_class)
Net = Net.to(device)
display(Net)
Machine Learning
3.1 setting
# Instantiate loss function
loss_function = nn.NLLLoss()
# Instantiate optimization algorithm
learning_rate = 0.0005
optimizer = optim.Adam(Net.classifier.parameters(), lr=learning_rate)
# Enable CUDA: use GPUs for model computation
if torch.cuda.is_available():
Net.to('cuda')
param = list(Net.parameters())
print(len(param))
for i in param:
print(i.shape)
3.2 Run
#using Multi gpu setting
Net = nn.DataParallel(Net)
Net.cuda()
# Epochs: number of iterations over the entire training dataset
epochs = 3
# Number of iterations between printing loss and accuracy
print_steps = 30
# Initialize steps
step = 0
# Iterate over number of epochs
for e in range(epochs):
running_loss = 0
# Iterate over the entire training dataset
# one batch per iteration
for i, data in enumerate(train_loader, 0):
# get the inputs
inputs, labels = data
# Enable CUDA: use GPUs for model computation
if torch.cuda.is_available():
inputs, labels = inputs.to('cuda'), labels.to('cuda')
# Clear the gradients of all optimized tensors
optimizer.zero_grad()
# Forward pass
outputs = Net.forward(inputs)
loss = loss_function(outputs, labels)
# Backward pass
loss.backward()
optimizer.step()
# Calculate and print running training loss
running_loss += loss.item()
if step % print_steps == 0:
print("Epoch: {}/{}... ".format(e+1, epochs),
"Loss: {:.4f}".format(running_loss/print_steps))
running_loss = 0
print('Finished Training')
3.3 validation data를 통해 정확도 확인하기
validation_correct = 0
validation_total = 0
# no_grad() prevents tracking history (and using memory)
with torch.no_grad():
# Iterate over the entire validation dataset
for input_images, labels in valid_loader:
# Enable CUDA: use GPUs for model computation
if torch.cuda.is_available():
input_images, labels = input_images.to('cuda'), labels.to('cuda')
# Make predictions
outputs = Net(input_images)
_, predicted = torch.max(outputs.data, 1)
# Count total and correct predictions
validation_total += labels.size(0)
validation_correct += (predicted == labels).sum().item()
# Print validation accuracy
print('Validation accuracy ({0:d} validation images): {1:.1%}'
.format(validation_total, validation_correct / validation_total))
3.3 model save
save_path="/home/virgo/ML_TUTORIAL/PyTorch/Classification/GalaxyZoo/vgg/resnet18.pth"
torch.save(Net,save_path)
model=torch.load(save_path)
3.4 결과 확인
softmax : https://pytorch.org/docs/stable/generated/torch.nn.Softmax.html
softmax는 classification 의 결정하는 output value 들의 합이 1이 되게 normalized 해주는 function이다. 따라서 어떤 분류 결과값에 max 값을 가지는 위치로 classification 하게 된다. 이때 softmax 를 사용하게 된다면, 각 분류 목록에 해당되는 classification fraction 을 확인할 수 있다.
np.set_printoptions(precision=2,suppress=True)
images, labels = next(iter(valid_loader))
outputs = Net(images)
_, predicted = torch.max(outputs.data, 1)
m=nn.Softmax(dim=1)
frac=m(outputs)
frac=frac.cpu()
frac=frac.detach().numpy()
print((frac[1]*100))
print(frac[1][1]*100)
print((predicted[0]))
Nr =4
Nc = np.int(images.shape[0]/Nr)
ImSize=3
fig=plt.figure(figsize=(Nc*ImSize,Nr*ImSize),dpi=80)
ax = fig.subplots(Nr,Nc)
plt.subplots_adjust(wspace=0.05, hspace=0.1)
for i in range(Nr):
for j in range(Nc):
data=images[j+Nc*i]
if np.array(labels[j+Nc*i]) == 0:
label='E'
if np.array(labels[j+Nc*i]) == 1:
label='L'
if predicted[j+Nc*i] == 0:
Mlabel='E'
if predicted[j+Nc*i] == 1:
Mlabel='L'
ax[i,j].imshow(np.transpose(vutils.make_grid(data.to(device), padding=2, normalize=True).cpu()))
ax[i,j].text(30,70,label,color='white',fontsize=15)
ax[i,j].text(430,70,Mlabel,color='red',fontsize=15)
ax[i,j].text(30,460,frac[j+Nc*i]*100,color='white',fontsize=15)
ax[i,j].axis('off')