100장의 사진으로 4개의 특성 로지스틱 회귀 시그모이드 함수 구현하기

기본 설정

  • 필수 모듈 불러오기
  • 그래프 출력 관련 기본 설정 지정
# 파이썬 ≥3.5 필수
import sys
assert sys.version_info >= (3, 5)

# 사이킷런 ≥0.20 필수
import sklearn
assert sklearn.__version__ >= "0.20"

# 공통 모듈 임포트
import numpy as np
import os

# 노트북 실행 결과를 동일하게 유지하기 위해
np.random.seed(42)

# 깔끔한 그래프 출력을 위해
%matplotlib inline
import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.rc('axes', labelsize=14)
mpl.rc('xtick', labelsize=12)
mpl.rc('ytick', labelsize=12)

# 그림을 저장할 위치
PROJECT_ROOT_DIR = "."
CHAPTER_ID = "training_linear_models"
IMAGES_PATH = os.path.join(PROJECT_ROOT_DIR, "images", CHAPTER_ID)
os.makedirs(IMAGES_PATH, exist_ok=True)

def save_fig(fig_id, tight_layout=True, fig_extension="png", resolution=300):
    path = os.path.join(IMAGES_PATH, fig_id + "." + fig_extension)
    print("그림 저장:", fig_id)
    if tight_layout:
        plt.tight_layout()
    plt.savefig(path, format=fig_extension, dpi=resolution)
    
# 어레이 데이터를 csv 파일로 저장하기
def save_data(fileName, arrayName, header=''):
    np.savetxt(fileName, arrayName, delimiter=',', header=header, comments='')

주요 과제

A. 사진을 낮과 밤으로 분류하는 로지스틱 회귀 모델을 구현하라.

B. 사진을 낮과 밤, 실내와 실외로 분류하는 다중 레이블 분류 모델을 두 개의 로지스틱 회귀 모델을 이용하여 구현하라.

C. 과제 1에서 구현한 자신의 알고리즘과 사이킷런에서 제공하는 LogisticRegression 모델의 성능을 비교하라.

단, 모델 구현에 필요한 사진을 직접 구해야 한다. 최소 100장 이상의 사진 활용해야 한다.

!git clone https://github.com/hyoungteak/Assignment.git
Cloning into 'Assignment'...
remote: Enumerating objects: 161, done.
remote: Counting objects: 100% (46/46), done.
remote: Compressing objects: 100% (43/43), done.
remote: Total 161 (delta 12), reused 0 (delta 0), pack-reused 115
Receiving objects: 100% (161/161), 10.28 MiB | 34.53 MiB/s, done.
Resolving deltas: 100% (13/13), done.

원래는 구글드라이브에서 나만 사용할 수 있도록 가져왔지만, 깃허브를 만들고 이미지 100개를 넣어놨었다.

깃허브에서 제대로 가져왔는지 “inside_day1.jpg”를 출력해서 확인해보자.

from IPython.display import Image
Image('/content/Assignment/Machine Learning Assignment 4/Daynight picture/inside_day1.jpg')

img_01

잘 가져온 것 같다. 하지만 사진이 너무 크니 데이터를 전처리해보자.

우선 하나만 100X100으로 만들어보자

from PIL import Image
import numpy as np
import sys
import os
import csv
import matplotlib.pyplot as plt
import cv2

fname = '/content/Assignment/Machine Learning Assignment 4/Daynight picture/inside_day1.jpg'


original = cv2.imread(fname, cv2.IMREAD_COLOR)
original = cv2.resize(original, (100, 100)).flatten()
gray = cv2.imread(fname, cv2.IMREAD_GRAYSCALE)
gray = cv2.resize(gray, (100, 100)).flatten()
np_ori = np.asarray(original)
np_gray = np.asarray(gray)


plt.figure(figsize=(10,10))
plt.subplot(1, 2, 1)
plt.imshow(np_ori.reshape(100, 100, 3))
plt.axis('off')

plt.subplot(1, 2, 2)
plt.imshow(np_gray.reshape(100, 100))
plt.axis('off')
(-0.5, 99.5, 99.5, -0.5)

img_01

from PIL import Image
import numpy as np
import sys
import os
import csv
import matplotlib.pyplot as plt

def createFileList(myDir, format='.jpg'):
  fileList = []
  print(myDir)
  for root, dirs, files in os.walk(myDir, topdown=False):
      for name in files:
          if name.endswith(format):
              fullName = os.path.join(root, name)
              fileList.append(fullName)
  return fileList

myFileList = createFileList('/content/Assignment/Machine Learning Assignment 4/Daynight picture/')
file_name = os.listdir('/content/Assignment/Machine Learning Assignment 4/Daynight picture/')

for file in myFileList:
    img_file = Image.open(file)

    img_file = img_file.resize((100,100))
    img_grey = img_file.convert('L')

    # Save Greyscale values
    value = np.asarray(img_grey.getdata(), dtype=np.int).reshape((img_grey.size[1], img_grey.size[0]))
    value = value.flatten()

    with open("img_pixels_grey.csv", 'a') as f:
        writer = csv.writer(f)
        writer.writerow(value)
/content/Assignment/Machine Learning Assignment 4/Daynight picture/

우선 사진 100개를 100X100로 만들고 csv파일로 만들었다.

그리고 0~10000개의 요소를 가지고있는데 각 픽셀의 값이 들어가 있다.

흑백 사진과 마찬가지로 RGB값을 따로 특성으로 저장하기 위해 color 값도 진행하였다.

우선 사진을 그대로 리사이즈하고 색상을 따로 뽑아오는게 안되서 폴더를 새로 만들고 새로 사진을 저장한 후 진행하였다.

밑은 폴더가 없을 경우 새로 생성해주고 있을 경우에는 그냥 넘어가는 코드이다.

def createFolder(directory):
    try:
        if not os.path.exists(directory):
            os.makedirs(directory)
    except OSError:
        print ('Error: Creating directory. ' +  directory)
 
createFolder('/content/Assignment/Machine Learning Assignment 4/resize picture/')
import glob

for f in myFileList:
    img = Image.open(f)
    img_resize = img.resize((100, 100))
    title, ext = os.path.split(f)
    img_resize.save('/content/Assignment/Machine Learning Assignment 4/resize picture/re_' + ext)

resize picture 폴더에 re_“원래 이름”.jpg로 저장하게 하는 코드이다.

myFileList = createFileList('/content/Assignment/Machine Learning Assignment 4/resize picture/')

for file in myFileList:
  img_file = plt.imread(file)
  blue,green,red = cv2.split(img_file)
  
  # Save values
  value1 = blue.reshape((10000, ))

  with open("img_pixels_blue.csv", 'a') as f:
      writer = csv.writer(f)
      writer.writerow(value1)

  value2 = green.reshape((10000, ))

  with open("img_pixels_green.csv", 'a') as f:
      writer = csv.writer(f)
      writer.writerow(value2)

  value3 = red.reshape((10000, ))

  with open("img_pixels_red.csv", 'a') as f:
      writer = csv.writer(f)
      writer.writerow(value3)
/content/Assignment/Machine Learning Assignment 4/resize picture/

흑백 사진 Daynight도 그랬듯, RGB값도 csv 파일로 생성한다.

참고로 100X100으로 되어있기 때문에 (10000, )로 reshape 해주었다.

import pandas as pd
daynight = pd.read_csv('/content/img_pixels_grey.csv', header=None)
pdred = pd.read_csv('/content/img_pixels_red.csv', header=None)
pdblue = pd.read_csv('/content/img_pixels_blue.csv', header=None)
pdgreen = pd.read_csv('/content/img_pixels_green.csv', header=None)

Daynight = pd.DataFrame([daynight.sum(axis=1)/10000])
Red = pd.DataFrame([pdred.sum(axis=1)/10000])
Blue = pd.DataFrame([pdblue.sum(axis=1)/10000])
Green = pd.DataFrame([pdgreen.sum(axis=1)/10000])

Daynight = Daynight.transpose()
Red = Red.transpose()
Blue = Blue.transpose()
Green = Green.transpose()

sum 같은 경우 100X10000의 파일을 100X1로 만들어 주며

나머지는 10000으로 나누어서 평균 값을 내서 하나의 데이터프레임으로 만들었다.

행과 열이 반대로 되어있기 때문에 transpose()도 해주었다.

Daynight
0
0 20.7369
1 97.8544
2 153.3160
3 155.7763
4 86.3276
... ...
95 121.8520
96 146.6424
97 65.1688
98 25.2492
99 109.6289

100 rows × 1 columns

Red
0
0 18.9018
1 30.7930
2 181.4984
3 121.4758
4 15.2284
... ...
95 17.0507
96 196.2541
97 146.3720
98 38.7736
99 29.9100

100 rows × 1 columns

이제 실내 실외 낮 밤 특성을 만들고 싶어서

사진의 저장된 내용중에서 숫자와 따옴표 .jpg를 제거했다.

# inside와 outdoor 구분
a = []
for x in file_name:
    a.append(x.strip("'0123456789.jpg").replace('_day', '').replace('_night', ''))
filename = pd.DataFrame([a])
# day와 night 구분
a = []
for x in file_name:
    a.append(x.strip("'0123456789.jpg").replace('inside_', '').replace('outdoor_', ''))
filename2 = pd.DataFrame([a])
filename = filename.transpose()
filename2 = filename2.transpose()
filename
0
0 outdoor
1 inside
2 inside
3 inside
4 inside
... ...
95 inside
96 inside
97 inside
98 inside
99 inside

100 rows × 1 columns

이제 저 위에 값에서 원핫인코딩을 해보자.

from sklearn.preprocessing import OneHotEncoder

cat_encoder = OneHotEncoder(sparse=False)
day_cat_1hot = cat_encoder.fit_transform(filename)
ins_cat_1hot = cat_encoder.fit_transform(filename2)
filename = pd.DataFrame((day_cat_1hot),
             columns=['day', 'night']
             )
filename2 = pd.DataFrame((ins_cat_1hot),
             columns=['inside', 'outdoor']
             )

잘 저장되었다. 이제 아까 사진을 나눈 값을 붙이자.

print(filename)
print(filename2)
    day  night
0   0.0    1.0
1   1.0    0.0
2   1.0    0.0
3   1.0    0.0
4   1.0    0.0
..  ...    ...
95  1.0    0.0
96  1.0    0.0
97  1.0    0.0
98  1.0    0.0
99  1.0    0.0

[100 rows x 2 columns]
    inside  outdoor
0      0.0      1.0
1      1.0      0.0
2      1.0      0.0
3      1.0      0.0
4      0.0      1.0
..     ...      ...
95     1.0      0.0
96     1.0      0.0
97     0.0      1.0
98     0.0      1.0
99     0.0      1.0

[100 rows x 2 columns]
result = pd.concat([filename, filename2],axis=1)
result['grey_value'] = Daynight
result['red_value'] = Red
result['blue_value'] = Blue
result['green_value'] = Green
result
day night inside outdoor grey_value red_value blue_value green_value
0 0.0 1.0 0.0 1.0 20.7369 18.9018 40.3530 28.8365
1 1.0 0.0 1.0 0.0 97.8544 30.7930 2.6830 16.0423
2 1.0 0.0 1.0 0.0 153.3160 181.4984 200.6854 192.9094
3 1.0 0.0 1.0 0.0 155.7763 121.4758 89.5200 134.7419
4 1.0 0.0 0.0 1.0 86.3276 15.2284 17.8019 16.1491
... ... ... ... ... ... ... ... ...
95 1.0 0.0 1.0 0.0 121.8520 17.0507 23.3901 20.4376
96 1.0 0.0 1.0 0.0 146.6424 196.2541 137.0802 165.0893
97 1.0 0.0 0.0 1.0 65.1688 146.3720 146.7012 146.6713
98 1.0 0.0 0.0 1.0 25.2492 38.7736 13.1340 28.0879
99 1.0 0.0 0.0 1.0 109.6289 29.9100 33.6793 32.4805

100 rows × 8 columns

데이터프레임이 완성되었다.

만들어진 특성들로 로지스틱회귀를 진행해보자.

y = result['grey_value'].copy()
X = result.drop("grey_value", axis=1)
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import randint
from sklearn.ensemble import RandomForestRegressor

param_distribs = {
        'n_estimators': randint(low=1, high=100),
        'max_features': randint(low=1, high=8),
    }

forest_reg = RandomForestRegressor(random_state=42)
rnd_search = RandomizedSearchCV(forest_reg, param_distributions=param_distribs,
                                n_iter=10, cv=5, scoring='neg_mean_squared_error', random_state=42)
rnd_search.fit(X, y)
RandomizedSearchCV(cv=5, error_score=nan,
                   estimator=RandomForestRegressor(bootstrap=True,
                                                   ccp_alpha=0.0,
                                                   criterion='mse',
                                                   max_depth=None,
                                                   max_features='auto',
                                                   max_leaf_nodes=None,
                                                   max_samples=None,
                                                   min_impurity_decrease=0.0,
                                                   min_impurity_split=None,
                                                   min_samples_leaf=1,
                                                   min_samples_split=2,
                                                   min_weight_fraction_leaf=0.0,
                                                   n_estimators=100,
                                                   n_jobs=None, oob_score=Fals...
                                                   warm_start=False),
                   iid='deprecated', n_iter=10, n_jobs=None,
                   param_distributions={'max_features': <scipy.stats._distn_infrastructure.rv_frozen object at 0x7fdc2826a790>,
                                        'n_estimators': <scipy.stats._distn_infrastructure.rv_frozen object at 0x7fdc28351050>},
                   pre_dispatch='2*n_jobs', random_state=42, refit=True,
                   return_train_score=False, scoring='neg_mean_squared_error',
                   verbose=0)
cvres = rnd_search.cv_results_
for mean_score, params in zip(cvres["mean_test_score"], cvres["params"]):
    print(np.sqrt(-mean_score), params)
27.41390642027651 {'max_features': 7, 'n_estimators': 52}
27.089818567088134 {'max_features': 5, 'n_estimators': 15}
27.000558306750957 {'max_features': 3, 'n_estimators': 72}
26.816869324837555 {'max_features': 5, 'n_estimators': 21}
27.24316319299315 {'max_features': 7, 'n_estimators': 83}
27.1275107676489 {'max_features': 7, 'n_estimators': 75}
26.993179773625222 {'max_features': 3, 'n_estimators': 88}
26.809595097343074 {'max_features': 5, 'n_estimators': 24}
26.581324816796823 {'max_features': 3, 'n_estimators': 22}
31.050528044906496 {'max_features': 5, 'n_estimators': 2}
from sklearn.metrics import mean_squared_error

final_model = rnd_search.best_estimator_
final_predictions = final_model.predict(X)

# RMSE 평가
final_mse = mean_squared_error(y, final_predictions)
final_rmse = np.sqrt(final_mse)

데이터 세트를 대상으로 한 최종 성능(RMSE)은 아래와 같다.

final_rmse
9.788665921231017

얻어진 테스트 RMSE에 대한 95% 신뢰 구간을 계산하여 확률적으로 시스템의 성능을 예측할 수 있다.

  • scipystats 모듈 활용
  • 아래 코드는 t-분포를 이용하여 신뢰구간 계산
from scipy import stats

confidence = 0.95
squared_errors = (final_predictions - y) ** 2
np.sqrt(stats.t.interval(confidence, len(squared_errors) - 1,
                         loc=squared_errors.mean(),
                         scale=stats.sem(squared_errors)))
array([ 8.24667635, 11.11882593])

수정에 수정을 거듭해서 겨우 특성 값을 만들어 낼 수 있었는데

모델 훈련 방법이 조금 시간이 걸릴 것 같다.

계속해서 수정해 나갈 것이다.