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.