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 데이터에 라벨이 있지 않기에 활용을 할수 없는게 아쉬웠다.