딥러닝 포스트 04까지 이해하는 것을 권장한다.


 이진분류(Binary Classification)를 목적으로 한 단층퍼셉트론(Single Layer Perceptron)을 만들어보자. 데이터셋은 Kaggle의 cat vs dog Conpetition에서 제공하는 데이터셋을 사용했다. 링크

 

Cat images

     Dog images


 모델을 설계하기에 앞서 영상의 구성요소는 가로(width), 세로(height), 색깔(RGB)이 있다. 데이터셋에 있는 영상이 각각 다른 크기를 가지고 있어 가로, 세로를 ($100 \times 100$)으로 통일시켜주었다. 개와 고양이 영상이 각각 $12,500$장씩 있는데 우리는 간단한 모델을 학습하니 개와 고양이 영상을 각각 $300$장으로 샘플링하여 학습데이터로 삼고, 개와 고양이 각각 $100$장씩을 테스트데이터로 하겠다. 데이터는 CSV 파일로 관리하는 것이 좋은데 다음 포스트를 참고하길 바란다. ---> 링크(작성중..)


 이번 실습은 맛보기용이므로 간단하게 모델을 설계하겠다. ($100 \times 100 \times 3$)의 영상을 ($30,000 \times 1$)의 벡터로 펼쳐 입력층으로 만들어준다. 그러면 아래와 같은 단일퍼셉트론 모델을 설계할 수 있다. 수식은 04 포스팅과 동일하게 사용하겠다.



 여기서 대문자로 표기한 것들($W, X, B$)은 행렬식이며 각각의 크기는 아래와 같다. ($m$은 학습 데이터 영상의 개수)


$$ W, dW: (30,000 \times 1) $$

$$ X: (30,000 \times m) $$

$$ Y, \hat{Y}: (1 \times m) $$

$$ B, dB: 1 $$


 이제 본격적인 코딩을 시작하겠다. 필요한 Python Library는 (numpy, pandas, matplotlib.pyplot, PIL) 이다. 적당히 구글링해서 까시고.. 필요한 라이브러리를 importing하자.


import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from PIL import Image



Training dataset( cat0.png ~ cat299.png $+$ dog0.png ~ dog299.png )

Test dataset( cat300.png ~ cat399.png $+$ dog300.png ~ dog399.png )

 위의 데이터 정보가 임의로 섞여있는 csv 파일을 읽어들이고 parsing한다.



train.csv

data=pd.read_csv('train.csv', parse_dates=['file_name', 'label', 'str' ])
data_test=pd.read_csv('test.csv', parse_dates=['file_name', 'label', 'str' ])

 csv파일이 성공적으로 읽혔는지 확인해보자. 
 np.shape(data) 함수는 data의 크기를 반환해주는 함수다. 학습데이터는 ($600, 3$)을 반환하겠고 테스트데이터는 ($200, 3$)을 반환한다. 
 data.head() 함수는 data의 제일 위에 있는 몇개의 원소를 출력창에 보여주는 함수이다.

num_train = np.shape(data)[0]
num_test = np.shape(data_test)[0]
print('training dataset: '+str(num_train))
print('test dataset: '+str(num_test))
data.head()


 설계한 모델로 학습을 진행하기 위해 모든 영상을 ($100 \times 100$) 크기로 만들어줘야 한다. 이번 실습에서 사용할 영상들을 resize() 함수로 크기를 통일시켜주자. 나는 원본영상과 resizing한 영상을 동일한 폴더에 넣었으므로 확장자(.png)로 구분했다.


# resize training dataset
for i in range(0, num_train): 
    im = Image.open(data.file_name[i]+".jpg")
    im = im.resize((100, 100))
    im.save(data.file_name[i]+".png")

# resize test dataset for i in range(0, num_test):      im = Image.open(data_test.file_name[i]+".jpg")     im = im.resize((100, 100))     im.save(data_test.file_name[i]+".png")
print('Resizing done')

  Resizing이 잘 되었는지 확인해보자. PLT 라이브러리의 imshow() 함수를 사용하면 좌표평면에 영상을 출력할 수 있다.

index = 6
im = Image.open(data.file_name[index]+".png")
plt.imshow(im)
num_px = np.shape(im)[0]*np.shape(im)[1]*np.shape(im)[2]



 위에 정리한 행렬 크기에 맞게 $X$와 $Y$를 선언해주고, 모든 영상을 펼쳐 $X$에 넣고 정답 Label을 $Y$에 넣는다.

 ($100 \times 100 \times 3$)영상에 reshape(-1) 함수를 적용시키면 모든 픽셀 정보가 일렬로 펼쳐지는 $30000$개의 배열을 반환한다. 

 X[:, i]=im은 i번째 Column에 배열 im을 할당하는 것을 의미한다.

 RGB 3개의 채널이 각각 8bit로 표현이 되며 $0$~$255(2^{8})$ 사이의 값을 가진다. 입력을 0~1 사이로 정규화 하기 위해 행렬 $X$의 모든 원소를 255로 나누어준다.


# for train
X=np.zeros((num_px, num_train))
Y=np.zeros((1, num_train))

for i in range(0, num_train): 
    im = Image.open(data.file_name[i]+".png")    
    im = np.asarray(im)
    im = im.reshape(-1)
    X[:, i] = im
    Y[:, i] = data.label[i]
X=X/255.

# for test
X_test=np.zeros((num_px, num_test))
Y_test=np.zeros((1, num_test))

for i in range(0, num_test): 
    im = Image.open(data_test.file_name[i]+".png")    
    im = np.asarray(im)
    im = im.reshape(-1)
    X_test[:, i] = im
    Y_test[:, i] = data_test.label[i]
X_test=X_test/255.

print('Done!')

 앞으로 자주 쓰게 될 Sigmoid 함수를 외부함수로 정의해주자.

def sigmoid(a):
    y = 1/(1+np.exp(-a))
    return y

 이제 학습을 시작할텐데 딥러닝 포스트04에서 도출해낸 아래의 수식대로 코드를 짜서 경사하강법대로 파라미터를 업데이트 한다. predict 변수는 학습데이터의 최종학습 결과


W=np.zeros((num_px, 1))
B=0
Y_hat=np.zeros((1, num_train))
predict=np.zeros((1, num_train))

# training start
iter = 2000
for i in range(0, iter):
    # forward
    Y_hat = sigmoid(np.dot(W.T, X)+B)      
        
    # backward
    dW = 1/num_train*np.dot(X, (Y_hat-Y).T)
    dB = 1/num_train*np.sum(Y_hat-Y, axis=1)
    
    # update parameters
    learning_rate = 0.005
    W = W-learning_rate*dW
    B = B-learning_rate*dB

    # print cose
    if i % (iter/5) == 0:
        cost = -np.sum(Y*np.log(Y_hat)+(1-Y)*np.log(1-Y_hat))/num_train
        print('cost: ' + str(cost)+ '\t-> ('+ str(i) +' iter)')
cost = -np.sum(Y*np.log(Y_hat)+(1-Y)*np.log(1-Y_hat))/num_train
print('cost: ' + str(cost)+ '\t-> ('+ str(i) +' iter)')


 학습이 완료되었다. 우선 학습데이터가 얼마나 잘 학습되었는지 학습데이터의 정답($Y$)과 최종적으로 도출된 결과($\hat{Y}$)를 threshold(0.5) 하여 정확도를 구해보자. 

for i in range(0, num_train):
    if Y_hat[0, i] > 0.5:
        predict[0, i] = 1
    else:
        predict[0, j] = 0  
print("train accuracy: {} %".format(100 - np.mean(np.abs(predict - Y)) * 100))


 약 94%로 나름 기대할만한 결과가 나왔다. 다음으로 앞서 따로 구분했던 테스트 데이터를 퍼셉트론 모델에 통과시켜서 정확도를 계산해보자.

predict_test=np.zeros((1, num_test))
Y_hat_test = sigmoid(np.dot(W.T, X_test)+B)
for i in range(0, num_test):
    if Y_hat_test[0, i] > 0.5:
        predict_test[0, i] = 1
    else:
        predict_test[0, i] = 0

print("test accuracy: {} %".format(100 - np.mean(np.abs(predict_test - Y_test)) * 100))


 쓰레기 같은 결과가 나왔다. 단일퍼셉트론의 한계이다. 테스트 결과를 시각적으로 확인하기 위해 아래 코드의 index값을 임의로 정하여 돌려볼 수 있다.

index = 149
A = getim(index)
label = labeling(A)
print('predict: '+str(label) + '\nAnswer: '+str(data_test.str[index]))

 

 딥러닝의 조상격인 Perceptron에 대한 이해를 충분히 했으리라 믿고 다음 실습에서는 Multi-Layer Perceptron을 구현해보도록 하겠다.


+ Recent posts