본문 바로가기
카테고리 없음

[MachineLearning] 선형분류(LogisticRegression, SVM) 손글씨 숫자데이터 분류

by YoungD 2023. 9. 20.

문제정의

  • 손글씨 숫자(0~9)를 분류하는 모델을 만들어보자
  • 이미지 데이터의 형태를 이해해보자

 

데이터 수집

In [1]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
In [2]:
# 데이터 가져오기 
data = pd.read_csv('./data/digit_train.csv')
In [3]:
# 데이터 크기 확인
data.shape
Out[3]:
(42000, 785)
In [4]:
data.head()
# label : 정답
# pixel0 ~ pixel783 : 문제 (28*28 이미지 데이터) 
Out[4]:
In [5]:
# 데이터 시각화 --> 이미지 데이터로 첫번째 행의 label을 제외한 모든 컬럼 가져오기
img0 = data.iloc[0, 1:]
img0.shape
Out[5]:
(784,)
In [6]:
img0.values # rgb(0 ~ 255)
Out[6]:
In [7]:
# 리스트 자료형을 28*28의 2차원으로 변환
img0_reshape = img0.values.reshape(28,28)
In [8]:
# img0 이미지 하나를 그려보기
plt.imshow(img0_reshape, cmap='gray')
Out[8]:
<matplotlib.image.AxesImage at 0x1c408394970>
In [9]:
# 7777행의 데이터 꺼내와서 - 값 확인 - 2차원 변환 - 이미지 그려보기 - 어떤 숫자인지 확인
img7777 = data.iloc[7777, 1:]
img7777_reshape = img7777.values.reshape(28,28)
plt.imshow(img7777_reshape, cmap='gray')
# plt.show() # 불필요한 text 정보를 출력하지 않는 코드
Out[9]:
<matplotlib.image.AxesImage at 0x1c4084652d0>

 

학습을 위한 데이터 분리

  • 학습 7 : 테스트 3
  • random_state : 7
In [10]:
data
Out[10]:
In [11]:
X = data.iloc[:, 1 : ]   # 문제 - 2차원 데이터프레임 형태 [[ ]]
y = data.iloc[:, 0]   # 정답 - 1차원 시리즈 형태
In [12]:
# 크기 확인
X.shape, y.shape
Out[12]:
((42000, 784), (42000,))
In [13]:
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=7)
In [14]:
print(X_train.shape)
print(X_test.shape)
print(y_train.shape)
print(y_test.shape)
(29400, 784)
(12600, 784)
(29400,)
(12600,)

 

모델 선택

  • KNN
  • Logistic Regression
  • SVM
In [15]:
from sklearn.neighbors import KNeighborsClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import LinearSVC

# 모델 생성
knn_model = KNeighborsClassifier()
logi_model = LogisticRegression()
svm_model = LinearSVC() 

 

학습 및 평가

In [16]:
# 학습
knn_model.fit(X_train, y_train)
logi_model.fit(X_train, y_train)
svm_model.fit(X_train, y_train)

# 평가
print('KNN : ', knn_model.score(X_test, y_test))
print('Logistic : ', logi_model.score(X_test, y_test))
print('SVM : ', svm_model.score(X_test, y_test))
 
KNN :  0.9645238095238096
Logistic :  0.9138095238095238
SVM :  0.8468253968253968

 

데이터 스케일링 적용

  • 이미지 픽셀정보 : 0 ~ 255로 최소/최대가 고정되어 있는 데이터
  • MinMaxScaler를 사용하여 0 ~ 1 사이의 값으로 모든 픽셀을 조정
  • 스케일링의 효과 확인하기
    • 학습(연산) 속도 개선
    • 거리기반으로 예측하는 KNN 모델 --> 거의 변화가 없었음.
  • KNN 모델 : 모든 데이터의 대한 거리를 계산, 데이터가 많을수록 속도가 느려짐
In [17]:
from sklearn.preprocessing import MinMaxScaler
In [18]:
scaler = MinMaxScaler() # 0 ~ 1 사이로 변환
In [19]:
scaler.fit(X_train)  # 현재 데이터 분포의 최소/최대 값 확인
                     # 변환하는 기준 값을 X_train으로 추출 
Out[19]:
In [20]:
X_train
Out[20]:
In [21]:
# 문제데이터에 스케일링 적용
X_train_scale = scaler.transform(X_train)
X_test_scale = scaler.transform(X_test)
In [22]:
# 스케일링이 적용된 데이터로 다시 학습
knn_model.fit(X_train_scale, y_train)
logi_model.fit(X_train_scale, y_train)
svm_model.fit(X_train_scale, y_train)

# 스케일링이 적용된 데이터로 변화값 확인 
print('KNN : ', knn_model.score(X_test_scale, y_test))
print('Logistic : ', logi_model.score(X_test_scale, y_test))
print('SVM : ', svm_model.score(X_test_scale, y_test))
 
KNN :  0.9644444444444444
Logistic :  0.9169047619047619
SVM :  0.9093650793650794
In [23]:
# 데이터 스케일링전 수치
# KNN :  0.9645238095238096
# Logistic :  0.9138095238095238
# SVM :  0.8741269841269841

평가지표

  • 회귀 : 오차를 기반으로 하는 평가지표 사용(MSE, R2score)
  • 분류 : 맞춘 개수를 기반으로 평가지표를 사용
    1. 정확도(Accuracy) : 전체 데이터중에 정확히 맞춘 비율
    2. 재현율(Recall) : 실제 양성중에서 정확히 맞춘 비율
    3. 정밀도(Precision) : 예측 양성중에서 정확히 맞춘 비율
    4. F1-score : 정밀도와 재현율의 조화평균
  • SVM : 초평면(결정경계)을 기준으로 소프트벡터의 마진(거리)이 클수록 좋은 결정경계
In [24]:
# 평가지표 라이브러리 불러오기
from sklearn.metrics import confusion_matrix
In [25]:
# 스케일링이 적용된 테스트데이터로 예측값 만들기
# 로지스틱분류 사용
pre = logi_model.predict(X_test_scale)
In [26]:
# confusion_matrix(실제값, 예측값)
test_conf = confusion_matrix(y_test, pre)
test_conf
Out[26]:
array([[1207,    0,    4,    4,    3,   13,    6,    3,    7,    2],
       [   0, 1406,    7,    7,    1,    4,    1,    2,    8,    4],
       [   7,   11, 1112,   23,   15,    3,   11,   19,   22,    8],
       [   7,    2,   39, 1152,    1,   46,    1,   11,   28,   12],
       [   3,   18,   10,    2, 1140,    1,   13,   10,    9,   44],
       [  11,    7,   16,   41,   15,  936,   17,    7,   29,    9],
       [  13,    4,    8,    1,   16,   16, 1157,    1,    7,    2],
       [   1,   11,   13,    4,    7,    1,    2, 1216,    3,   48],
       [   9,   38,    8,   36,    9,   27,    6,    1, 1089,   24],
       [   8,    8,    2,   10,   36,    8,    0,   44,   11, 1138]],
      dtype=int64)
In [27]:
from sklearn.metrics import classification_report
# classification_report - 분류평가지표
In [28]:
# classification_report(실제값, 예측값)
print(classification_report(y_test, pre))
              precision    recall  f1-score   support

           0       0.95      0.97      0.96      1249
           1       0.93      0.98      0.95      1440
           2       0.91      0.90      0.91      1231
           3       0.90      0.89      0.89      1299
           4       0.92      0.91      0.91      1250
           5       0.89      0.86      0.87      1088
           6       0.95      0.94      0.95      1225
           7       0.93      0.93      0.93      1306
           8       0.90      0.87      0.89      1247
           9       0.88      0.90      0.89      1265

    accuracy                           0.92     12600
   macro avg       0.92      0.92      0.92     12600
weighted avg       0.92      0.92      0.92     12600

  • support : 데이서의 갯수
  • accuracy : 로직스틱회귀의 예측성능 92%이지만, 각 숫자(클래스)가 나온 확률의 수치값은 다름
  • f1-score : 정밀도와 재현율의 조화평균 --> 정밀도와 재현율을 고려한 수치값 --> 수치가 높은게 두 분류지표(정밀도, 재현율)도 높음을 알 수 있음
  • macro avg : 정밀도, 재현율, f1-score를 구해서 각각 평균을 낸 것 --> 분류모델이 각 클래스에 대해 얼마나 평균적으로 잘 동작하는지 알고싶을 때 사용
  • weighted avg(가중평균) : 각 클래스의 중요도, 영향도(빈도) 등에 따라서 가중치를 곱해서 구해지는 평균

 

예측

In [29]:
# index 7번째 데이터 가져오기
img7 = X_test_scale[7]
In [30]:
logi_model.predict([img7])
Out[30]:
array([1], dtype=int64)
In [31]:
# 실제 정답 확인
y_test.iloc[7]
Out[31]:
1
In [32]:
svm_model.predict([img7])
Out[32]:
array([1], dtype=int64)
In [33]:
logi_model.predict_proba(X_test_scale[7:8]) # 7번째 값(0 ~ 9) 숫자가 1일 확률
# svm은 predict_proba 없음
Out[33]:
array([[3.73323833e-10, 9.88049149e-01, 2.75926406e-04, 3.35151433e-03,
        2.15892443e-06, 7.28083512e-05, 1.06545842e-05, 6.20899060e-03,
        1.35662821e-04, 1.89313425e-03]])
In [34]:
img7 = X_test.iloc[7]
plt.imshow(img7.values.reshape(28,28), cmap='gray')
Out[34]:
<matplotlib.image.AxesImage at 0x1c40a7ed000>

 

  • 낮은 재현율 높은 정밀도 : 모델이 한 번 예측할 때마다 비용이 많이 들 경우 적합 ==> 의약실험, 모델이 예측의 리스크/비용이 큰 경우, 아이에게 유익한 영상만 보여주고 싶을 때
  • 높은 재현율 낮은 정밀도 : 실제 사건(상황)에 대한 리스크가 큰 경우 적합 ==> 감시카메라를 이용해 좀도둑을 걸러내는 상황, 암환자 판정, 스팸문자
  • 같은 정확도일 때 애매한 것들을 사용하지 않겠다 ==> 높은 정밀도
  • 같은 정확도일 때 애매한 것들을 사용하겠다 ==> 높은 재현율
  • 상황에 맞게 평가지표를 적용
  • best : 높은 재현율, 높은 정밀도
  • ROC 곡선
    • 임계값을 0 ~ 1 까지 변화시켜가면서
    • x축 : 가짜 양성 비율(FPR), y축 : 진짜 양성 비율(TPR)을 표시한 곡선
    • 임계값 변화에 따라 양성/음성 분류가 달라짐, ROC 곡선도 달라짐
    • ROC 곡선 아래 면적 - AUC(Area Under Curve)
    • AUC의 값이 1에 가까울수록 분류모델의 성능이 좋다고 평가
    • 0.5를 기준으로 위쪽으로 갈수록 좋음.