Posts 강아지와 고양이 분류기 on PCA
Post
Cancel

강아지와 고양이 분류기 on PCA

1. Dosg and Cats data


1.1 Data donwload

  • kaggle : https://www.kaggle.com/c/dogs-vs-cats/data
  • 위의 링크에서 Donwload all 버튼을 눌러 dogs-vs-cats를 받으면 된다.
  • 압축을 2번 풀어 train과 test1 폴더를 구한다.
  • 용량이 약 900메가정도되는 개와 고양이 이미지 데이터이다.
  • trian에는 강아지 사진 12500장, 고양이 사진 12500장이 있으며, test1데이터에는 고양이와 강아지 사진이 12500장 있다.


2. Data Load and Preprocessing


2.1 Data Load

1
2
3
4
import os

path = './data/dogs-vs-cats/train/' # 본인의 경로에 맞게 수정해야함
os.listdir(path)
1
2
3
4
5
6
7
8
9
10
['dog.8011.jpg',
 'cat.5077.jpg',
 'dog.7322.jpg',
 'cat.2718.jpg',
  ...
 'dog.9939.jpg',
 'cat.402.jpg',
 'cat.2490.jpg',
 'cat.364.jpg',
 ...]
  • os 패키지의 listdir을 사용하여 train 경로에 있는 파일명을 읽어왔다.
  • 파일을 자새히 보면 label.file_id.jpg로 되어있음을 알수 있다


2.2 파일명에서 label 및 file_id 추출하기

1
os.listdir(path)[0].split('.')
1
['dog', '8011', 'jpg']
  • 파일명이 ‘.’ 으로 구분지을수 있으므로, split 메서드를 사용하여 파일명을 구분한다.


1
2
3
full_names = os.listdir(path)
labels = [each.split('.')[0] for each in full_names]
file_id = [each.split('.')[1] for each in full_names]
1
len(labels), len(file_id)
1
(25000, 25000)
  • 리스트 컴프리행션을 사용하여 label와 파일id를 구분하여 저장하였다.


2.3 분포 확인

1
2
3
4
from collections import Counter

print(Counter(labels).keys())
print(Counter(labels).values())
1
2
dict_keys(['dog', 'cat'])
dict_values([12500, 12500])
  • label 데이터의 갯수를 확인하였다.
  • 위에서 이야기한대로 강아지(dog) 125000장, 고양이(cat) 125000장 이다.


2.4 데이터 시각화

1
2
3
4
5
6
7
8
9
10
11
import random
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
%matplotlib inline

sample = random.choice(full_names)
image = mpimg.imread(path + sample)

plt.title(image.shape)
plt.imshow(image)
plt.show()

  • 고양이나 강아지 사진이 랜덤하게 나오게 된다.
  • 여러번 해보면 알겠지만 정확히 고양이 강아지 사진이 아니라 사람도 섞여있고 한다.
  • 또한 title로 잡은 이미지의 크기도 다 다르게 나온다. 한마디로 사진마다 사이즈가 다르다


2.5 이미지 크기를 동일하게 작업

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from skimage.transform import resize
resize = resize(image, (100, 100, 3))

fig, axes = plt.subplots(1, 2, figsize = (8 ,4))
ax = axes.ravel()

ax[0].imshow(image, cmap = plt.cm.gray)
ax[0].set_title('original image')

ax[1].imshow(resize, cmap = plt.cm.gray)
ax[1].set_title('resized')

fig.tight_layout()
plt.show()

  • 머신러닝의 작업을 위해서는 resize를 통해 모든 이미지가 동일한 size를 가져야 한다
  • 실제로 (100, 100, 3) size로 바꾼결과 모양이 조금 이상해 진다.


2.6 RGB 데이터를 flat하게 만들기

1
resize.shape
1
(100, 100, 3)
  • 100, 100, 3 이미지를 1개의 차원으로 바꾸는(평평하게, flat) 전처리를 하여야 한다.


1
resize.reshape(100 * 100 * 3,).shape
1
(30000,)
  • reshape를 사용하여 100 , 100, 3의 이미지를 30000 이미지로 flat하게 만들었다
  • 만일 위에서 resized를 할때 100 대신 다른 크기를 했다면, 위의 reshape를 해당 숫자에 맞게 해야 한다.


1
2
3
4
5
6
from skimage.transform import resize
images = []
for file in full_names[:3]:
    image = mpimg.imread(path + file)
    resized = resize(image, (100, 100, 3))
    images.append(resized.reshape(100 * 100 * 3,))
1
2
import numpy as np
np.array(images).shape
1
(3, 30000)
  • 위에서 했던 이미지를 불러와서 100 , 100, 3으로 resize하고 flat하게 reshape를 한번에 하였다.
  • 3개만 진행해보았을때 코드 작동이 잘되는것으로 확인됨
  • (3, 30000)은 3개의 데이터가 각 30000의 열을 가진다고 보면 된다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from skimage.transform import resize
import numpy as np
import time

start_time = time.time()

images = []

for file in full_names:
    image = mpimg.imread(path + file)
    resized = resize(image, (100, 100, 3))
    images.append(resized.reshape(100 * 100 * 3,))
    
images = np.array(images)

print(f'time : {time.time() - start_time}')
1
time : -358.03722286224365
  • 약 5분 넘게 걸린다.


1
2
3
4
5
print(images.shape)
print()
print(labels[:3])
print()
print(images[:3])
1
2
3
4
5
6
7
(25000, 30000)

['dog', 'cat', 'dog']

[[0.07137255 0.10588235 0.16862745 ... 0.14509804 0.21960784 0.31764706]
 [0.19223529 0.45101961 0.60590196 ... 0.02356863 0.2177451  0.34913725]
 [0.36177059 0.19061373 0.23028039 ... 0.22014314 0.28916275 0.4458902 ]]
  • 결과를 확인해보았는데 큰 문제는 없어보인다.


2.7 Label Encoding

1
2
3
4
5
6
from sklearn.preprocessing import LabelEncoder

encoder = LabelEncoder()
encoder.fit(labels)
labels_encoded = encoder.transform(labels)
labels_encoded[:3], encoder.classes_
1
(array([1, 0, 1]), array(['cat', 'dog'], dtype='<U3'))
  • 컴퓨터는 dog, cat 혹은 강아지 고양이라는 단어를 알지 못한다.
  • 그래서 dog느 0, cat은 1로 전처리하는것을 labelencoding이라고 한다.
  • sklearn에 labelencoder 모듈을 사용하여 바꾼다
  • labelencoder 객체를 만들고, fit 후 transform하면 된다.


2.8 Train, Test 분리

1
2
3
4
5
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(images, labels_encoded,
                                                   test_size = 0.2, random_state = 13,
                                                   stratify = labels_encoded)
1
X_train.shape, X_test.shape
1
((20000, 30000), (5000, 30000))
  • train과 test로 데이터를 분리
  • train에는 20000개, test에는 5000개로 분리하였다
  • stratify 옵션을 주어 고양이와 강아지의 라벨의 분포를 똑같게 만들었음

2.9 Train 데이터 확인

1
2
3
4
5
6
7
8
9
10
samples = random.choices(population= range(0, 20000), k = 8)

plt.figure(figsize=(14, 12))
for idx, n in enumerate(samples):
    plt.subplot(2, 4, idx + 1)
    plt.imshow(X_train[n].reshape(100, 100, 3), cmap = 'Greys', interpolation='nearest')
    plt.title(y_train[n])
        
plt.tight_layout()
plt.show()

  • 해당 그림들은 한장당 30000개의 특성으로 이루어져 있다.


3. PCA 적용


3.1 PCA 함수 생성

1
2
3
4
5
6
7
from sklearn.decomposition import PCA

def get_pca_data(ss_data, n_components = 2):
    pca = PCA(n_components = n_components)
    pca.fit(ss_data)
    
    return pca.transform(ss_data), pca
  • 원본 data를 pca하는 함수 작성


1
2
3
def get_pd_from_pca(pca_data, col_num):
    cols = ['pca_'+str(n) for n in range(col_num)]
    return pd.DataFrame(pca_data, columns = cols)
  • pca한 데이터를 데이터프레임으로 생성하는 함수


1
2
3
4
def print_variance_ratio(pca, only_sum = False):
    if only_sum == False:
        print('variance_ratio :', pca.explained_variance_ratio_)
    print('sum of variance_ratio: ', np.sum(pca.explained_variance_ratio_))
  • pca한 데이터의 정보를 출력하는 함수


3.2 30000개의 특성 중 100개만 사용한 PCA

1
2
3
4
5
6
import time

start_time = time.time()
pca_data, pca = get_pca_data(X_train, n_components=100)
print_variance_ratio(pca, only_sum=True)
print(f'time : {time.time() - start_time}')
1
2
sum of variance_ratio:  0.8377006227623456
time : 17.498114109039307
  • 30000개의 특성을 100개로 줄여도 전체 데이터의 83.8%정도를 설명한다


3.3 PCA 데이터 시각화

1
2
3
4
5
6
7
8
9
10
11
12
13
n = 100

fig, axes = plt.subplots(1, 2, figsize=(8, 4))
ax = axes.ravel()

ax[0].imshow(X_train[n].reshape(100, 100, 3))
ax[0].set_title('Before PCA')

ax[1].imshow(pca.inverse_transform(pca_data[n]).reshape(100, 100, 3))
ax[1].set_title('After PCA')

fig.tight_layout()
plt.show()
1
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).

  • PCA한 데이터를 확인해보니 흐릿하게 보인다.


4. Machine Learning 적용


4.1 LogisticRegression 적용

1
2
3
4
from sklearn.linear_model import LogisticRegression

lr_clf = LogisticRegression(random_state= 13, solver='liblinear')
lr_clf.fit(pca_data, y_train)
1
LogisticRegression(random_state=13, solver='liblinear')
1
2
3
4
from sklearn.metrics import accuracy_score

pred = lr_clf.predict(pca.transform(X_test))
accuracy_score(y_test, pred)
1
0.5802
  • 성능은 그냥 그렇다..


4.2 RandomForest 적용

1
2
3
4
5
6
7
from sklearn.ensemble import RandomForestClassifier

rf_clf = RandomForestClassifier(random_state= 13, n_jobs= -1, n_estimators= 100)
rf_clf.fit(pca_data, y_train)

pred = rf_clf.predict(pca.transform(X_test))
accuracy_score(y_test, pred)
1
0.6286
  • LogisticRegression 보다는 성능이 좋지만. 그래도 그냥 그렇다.
  • 아무래도 사진뒤에 이것저것 많은 노이즈가 껴있어서 그런듯 하다.


5. 회고


5.1 회고

  • PCA는 여러방면에서 쓰인다. 이번엔 이미지 관련한 PCA였다.
  • 사실 kaggle에서 받은 test1 데이터도 쓰고싶었지만, kaggle에 제출해서 성적을 확인 할수도 없고
  • 실제 test1 데이터에 라벨이 있지 않기에 활용을 할수 없는게 아쉬웠다.
This post is licensed under CC BY 4.0 by the author.

MNIST로 해보는 PCA와 kNN

타이타닉 데이터로 해보는 PCA와 kNN

Comments powered by Disqus.