ML/머신러닝

코랩을 이용한 샴 네트워크 구현

KAU 2020. 3. 6. 22:13

출석관리팀 발표자: 김정민 

실습은 코드도 길고 첨부할 이미지도 많아서 따로 게시물을 작성하였습니다.

코드는 깃허브에서 가져왔고 중간 중간에 조금씩 수정은 하였습니다.

코랩을 이용하였기 때문에 드라이브에 마운트하여 파일을 이용하면 됩니다.

 

압축을 풀어서 드라이브에 업로드한다.

우선 깃허브에 있는 데이터셋으로 이용할 파일을 다운받아주고 코랩라이브러리에 저장해줍니다.

파일에는 각종 이미지가 담겨져 있습니다. 물론 주피터를 사용하신다면 본인 내장 드라이브 주소를 입력하시면 됩니다.

/content/drive/My Drive/Colab Notebooks/training 

위에 보이는 것이 주소입니다. 후에 언급할것입니다.

 

 

<코드>

#각종 라이브러리를 임포트 해줍시다. 

%matplotlib inline

import torchvision

import torchvision.datasets as dset

import torchvision.transforms as transforms

from torch.utils.data import DataLoader,Dataset

import matplotlib.pyplot as plt

import torchvision.utils

import numpy as np

import random

from PIL import Image

import torch

from torch.autograd import Variable

import PIL.ImageOps    

import torch.nn as nn

from torch import optim

import torch.nn.functional as F

import os

 

#각종 사용할 함수를 세팅합니다.

 

def imshow(img,text=None,should_save=False):

    npimg = img.numpy()

    plt.axis("off")

    if text:

        plt.text(758, text, style='italic',fontweight='bold',

            bbox={'facecolor':'white''alpha':0.8'pad':10})

    plt.imshow(np.transpose(npimg, (120)))

    plt.show()    

 

def show_plot(iteration,loss):

    plt.plot(iteration,loss)

    plt.show()

 

 

#Configuration Class를 정의합시다.
#구글 코랩에 '드라이브 마운트'하면 드라이브에 저장되어있는 파일을 불러서 사용가능합니다.

#참고로 코랩에 연결할 때 마다 마운트를 해줘야 불러올 수 있습니다.

드라이브 마운트를 누르면 코드가 나오는데 실행 절차를 밟으면 된다.

class Config():

    training_dir = "/content/drive/My Drive/Colab Notebooks/training"

    testing_dir = "/content/drive/My Drive/Colab Notebooks/testing"

    train_batch_size = 64

    train_number_epochs = 100

 

 

#커스텀 데이터셋을 정의합시다.

 

class SiameseNetworkDataset(Dataset):

    

    def __init__(self,imageFolderDataset,transform=None,should_invert=True):

        self.imageFolderDataset = imageFolderDataset    

        self.transform = transform

        self.should_invert = should_invert

        

    def __getitem__(self,index):

        img0_tuple = random.choice(self.imageFolderDataset.imgs)

        #we need to make sure approx 50% of images are in the same class

        should_get_same_class = random.randint(0,1

        if should_get_same_class:

            while True:

                #keep looping till the same class image is found

                img1_tuple = random.choice(self.imageFolderDataset.imgs) 

                if img0_tuple[1]==img1_tuple[1]:

                    break

        else:

            while True:

                #keep looping till a different class image is found

                

                img1_tuple = random.choice(self.imageFolderDataset.imgs) 

                if img0_tuple[1] !=img1_tuple[1]:

                    break

 

        img0 = Image.open(img0_tuple[0])

        img1 = Image.open(img1_tuple[0])

        img0 = img0.convert("L")

        img1 = img1.convert("L")

        

        if self.should_invert:

            img0 = PIL.ImageOps.invert(img0)

            img1 = PIL.ImageOps.invert(img1)

 

        if self.transform is not None:

            img0 = self.transform(img0)

            img1 = self.transform(img1)

        

        return img0, img1 , torch.from_numpy(np.array([int(img1_tuple[1]!=img0_tuple[1])],dtype=np.float32))

    

    def __len__(self):

        return len(self.imageFolderDataset.imgs)

 

#앞서서 정의한 사용자 데이터셋을 불러옵시다.

 

folder_dataset = dset.ImageFolder(root=Config.training_dir)

siamese_dataset = SiameseNetworkDataset(imageFolderDataset=folder_dataset,

                                        transform=transforms.Compose([transforms.Resize((100,100)),

                                                                      transforms.ToTensor()

                                                                      ])

                                       ,should_invert=False)

 

#데이터의 일부를 보여보도록 하겠습니다.

 

vis_dataloader = DataLoader(siamese_dataset,

                        shuffle=True,

                        num_workers=8,

                        batch_size=8)

dataiter = iter(vis_dataloader)

 

example_batch = next(dataiter)

concatenated = torch.cat((example_batch[0],example_batch[1]),0)

imshow(torchvision.utils.make_grid(concatenated))

print(example_batch[2].numpy())

데이터의 일부가 나타난다. 

 

#컨볼루션 계층을 정의합니다.

 

class SiameseNetwork(nn.Module):

    def __init__(self):

        super(SiameseNetwork, self).__init__()

        self.cnn1 = nn.Sequential(

            nn.ReflectionPad2d(1),

            nn.Conv2d(14, kernel_size=3),

            nn.ReLU(inplace=True),

            nn.BatchNorm2d(4),

            

            nn.ReflectionPad2d(1),

            nn.Conv2d(48, kernel_size=3),

            nn.ReLU(inplace=True),

            nn.BatchNorm2d(8),



            nn.ReflectionPad2d(1),

            nn.Conv2d(88, kernel_size=3),

            nn.ReLU(inplace=True),

            nn.BatchNorm2d(8),



        )

 

        self.fc1 = nn.Sequential(

            nn.Linear(8*100*100500),

            nn.ReLU(inplace=True),

 

            nn.Linear(500500),

            nn.ReLU(inplace=True),

 

            nn.Linear(5005))

 

    def forward_once(selfx):

        output = self.cnn1(x)

        output = output.view(output.size()[0], -1)

        output = self.fc1(output)

        return output

 

    def forward(selfinput1input2):

        output1 = self.forward_once(input1)

        output2 = self.forward_once(input2)

        return output1, output2

 

 

#Loss 함수를 정의합니다.

 

class ContrastiveLoss(torch.nn.Module):

 

    def __init__(selfmargin=2.0):

        super(ContrastiveLoss, self).__init__()

        self.margin = margin

 

    def forward(selfoutput1output2label):

        euclidean_distance = F.pairwise_distance(output1, output2, keepdim = True)

        loss_contrastive = torch.mean((1-label) * torch.pow(euclidean_distance, 2) +

                                      (label) * torch.pow(torch.clamp(self.margin - euclidean_distance, min=0.0), 2))



        return loss_contrastive

 

 

# 본격적으로 트레인을 시켜보도록 합시다.

#앞서서 진행했던 실습처럼 옵티마이저와 에포크등을 설정합니다.

 

train_dataloader = DataLoader(siamese_dataset,

                        shuffle=True,

                        num_workers=8,

                        batch_size=Config.train_batch_size)

net = SiameseNetwork().cuda()

criterion = ContrastiveLoss()

optimizer = optim.Adam(net.parameters(),lr = 0.0005 )

counter = []

loss_history = [] 

iteration_number= 0

for epoch in range(0,Config.train_number_epochs):

    for i, data in enumerate(train_dataloader,0):

        img0, img1 , label = data

        img0, img1 , label = img0.cuda(), img1.cuda() , label.cuda()

        optimizer.zero_grad()

        output1,output2 = net(img0,img1)

        loss_contrastive = criterion(output1,output2,label)

        loss_contrastive.backward()

        optimizer.step()

        if i %10 == 0 :

            print("Epoch number {}\n Current loss {}\n".format(epoch,loss_contrastive.item()))

            iteration_number +=10

            counter.append(iteration_number)

            loss_history.append(loss_contrastive.item())

show_plot(counter,loss_history)

# 실제 이미지와 테스트 해보겠습니다.

folder_dataset_test = dset.ImageFolder(root=Config.testing_dir)

siamese_dataset = SiameseNetworkDataset(imageFolderDataset=folder_dataset_test,

                                        transform=transforms.Compose([transforms.Resize((100,100)),

                                                                      transforms.ToTensor()

                                                                      ])

                                       ,should_invert=False)

 

test_dataloader = DataLoader(siamese_dataset,num_workers=6,batch_size=1,shuffle=True)

dataiter = iter(test_dataloader)

x0,_,_ = next(dataiter)

 

for i in range(10):

    _,x1,label2 = next(dataiter)

    concatenated = torch.cat((x0,x1),0)

    

    output1,output2 = net(Variable(x0).cuda(),Variable(x1).cuda())

    euclidean_distance = F.pairwise_distance(output1, output2)

    imshow(torchvision.utils.make_grid(concatenated),'Dissimilarity: {:.2f}'.format(euclidean_distance.item()))

 

동일 인물일 경우 거리가 작다.

 

다른 인물일 경우 거리는 크게 나온다.

 

 

참조 깃허브

https://github.com/harveyslash/Facial-Similarity-with-Siamese-Networks-in-Pytorch

 

데이터셋 파일

Facial-Similarity-with-Siamese-Networks-in-Pytorch-master.zip
4.02MB