독학 연구소
공부한 내용을 정리합니다.
cross validation (1)
교차 검증(Cross Validation)

 

2020/11/06 - [머신러닝/딥러닝] - 로지스틱 회귀(Logistic Regression)

 

검증 데이터(Validation Data)

데이터는 학습 데이터와 테스트 나누어지는데, 학습 데이터로 학습을 하고 테스트 데이터를 이용해 범용 성능을 평가합니다. 여기서 문제는 과대적합(Overfitting)이 나타날 수 있다는 것인데, 이는 학습 데이터에만 적합한 모델이 될 수 있다는 것입니다. 따라서 학습 데이터의 일부를 검증 데이터로 분할하여 튜닝에 사용할 수 있습니다.

 

 

튜닝한다는 것은 모델의 하이퍼파라미터를 조정하는 것으로 성능 지표가 필요합니다. 이에 대해 테스트 데이터를 사용하여 튜닝하면 테스트 데이터에만 적합하도록 조정되어 과대적합이 발생하며 테스트 데이터의 정보가 알려져 범용 성능이 떨어지게 됩니다.

 

 

Holdout Cross Validation

학습 데이터의 일부를 고정적으로 분할하는 방식으로 전체 데이터가 많을 경우에 사용할 수 있는 방법입니다.

 

전체 데이터를 8:2 비율로 학습 데이터와 테스트 데이터를 분할하고 학습 데이터를 또다시 8:2 비율로 학습 데이터와 검증 데이터로 분할합니다.

from sklearn.model_selection import train_test_split

x_train, x_test, y_train, y_test = train_test_split(data, target, test_size=0.2)
x_train, x_val, y_train, y_val = train_test_split(x_train, y_train, test_size=0.2)

 

주의해야할 점은 전체 데이터에서 6:2:2과 같이 한 번에 나누는게 아니라는 것입니다.

 

 

K-Fold Cross Validation

전체 데이터가 적을 경우에 사용할 수 있는 방법입니다.

 

k는 학습 데이터를 분할하는 수를 의미하며 분할된 데이터를 폴드(fold)라고 합니다. 모든 폴드는 1개의 검증 폴드와 나머지 학습 폴드로 구성되는데, 모든 폴드가 최소 1번의 검증 폴드가 되도록 반복합니다. 이 과정에서 검증 폴드를 이용하여 성능 평가를 수행하며, 이에 대한 평균을 지표로 이용하는 것입니다.

 

사이킷런에서 제공하는 KFold 를 이용할 수 있습니다.

from sklearn.model_selection import KFold

data = np.arange(20).reshape(10, 2)
print(data)

kf = KFold(n_splits=4)

for train_idx, val_idx in kf.split(data):
    print('\ntrain index:', train_idx)
    print('val index:', val_idx)
    print('val data')
    print(data[val_idx])
[[ 0  1]
 [ 2  3]
 [ 4  5]
 [ 6  7]
 [ 8  9]
 [10 11]
 [12 13]
 [14 15]
 [16 17]
 [18 19]]

train index: [3 4 5 6 7 8 9]
val index: [0 1 2]
val data
[[0 1]
 [2 3]
 [4 5]]

train index: [0 1 2 6 7 8 9]
val index: [3 4 5]
val data
[[ 6  7]
 [ 8  9]
 [10 11]]

train index: [0 1 2 3 4 5 8 9]
val index: [6 7]
val data
[[12 13]
 [14 15]]

train index: [0 1 2 3 4 5 6 7]
val index: [8 9]
val data
[[16 17]
 [18 19]]

 

입력 데이터에 대해 k번 반복하여 학습 인덱스와 검증 인덱스를 반환합니다.

 

 

구현

K폴드 교차 검증을 하는 로지스틱 회귀 모델을 구현합니다.

 

유방암 데이터셋을 불러옵니다.

from sklearn.datasets import load_breast_cancer

breast_cancer = load_breast_cancer()

data = breast_cancer.data
target = breast_cancer.target
feature_names = breast_cancer.feature_names

print('data shape:', data.shape)
print('data sample:', data[0])
print('feature_names:', feature_names)

print('target shape:', target.shape)
print('target sample:', target[0], target[1], target[2])

 

타겟 형태를 변형합니다.

target = target.reshape(-1, 1)
target.shape

 

8:2 비율로 학습 데이터와 테스트 데이터를 분할합니다.

from sklearn.model_selection import train_test_split

x_train, x_test, y_train, y_test = train_test_split(data, target, test_size=0.2)

 

테스트 데이터를 전처리합니다.

x_test = (x_test - x_train.mean(axis=0)) / x_train.std(axis=0)

 

신경망을 정의합니다.

import numpy as np
import tensorflow as tf
from sklearn.model_selection import KFold

class Model:
    def __init__(self, lr=1e-3):
        with tf.name_scope('input'):
            self.x = tf.placeholder(tf.float32, [None, 30])
            self.y = tf.placeholder(tf.float32, [None, 1])

        with tf.name_scope('layer'):
            fc = tf.layers.dense(self.x, 64, tf.nn.relu)
            fc2 = tf.layers.dense(fc, 64, tf.nn.relu)
            fc3 = tf.layers.dense(fc2, 1)
            
        with tf.name_scope('output'):
            self.output = tf.nn.sigmoid(fc3)

        with tf.name_scope('accuracy'):
            self.predict = tf.cast(tf.greater(self.output, tf.constant([0.5])), dtype=tf.float32)
            self.accuracy = tf.reduce_mean(tf.cast(tf.equal(self.y, self.predict), dtype=tf.float32))    
        
        with tf.name_scope('loss'):
            self.loss = -tf.reduce_mean(self.y * tf.log(self.output) + (1-self.y) * tf.log(1-self.output))

        with tf.name_scope('optimizer'):
            self.train_op = tf.train.GradientDescentOptimizer(lr).minimize(self.loss)
      
        self.sess = tf.Session()
        self.sess.run(tf.global_variables_initializer())
   
    def train(self, x_train, y_train, epochs=100):
        train_loss, train_acc = [], []
        for e in range(epochs):
            loss, acc, _ = self.sess.run([self.loss, self.accuracy, self.train_op], {self.x: x_train, self.y: y_train})
            train_loss.append(loss)
            train_acc.append(acc)
        print('train loss:', np.mean(train_loss), '/ train accuracy:', np.mean(train_acc))
    
    def score(self, x, y):
        return self.sess.run(self.accuracy, {self.x: x, self.y: y})

 

배치 경사 하강법으로 학습을 수행합니다.

def train(self, x_train, y_train, epochs=100):
    train_loss, train_acc = [], []
    for e in range(epochs):
        loss, acc, _ = self.sess.run([self.loss, self.accuracy, self.train_op], {self.x: x_train, self.y: y_train})
        train_loss.append(loss)
        train_acc.append(acc)
    print('train loss:', np.mean(train_loss), '/ train accuracy:', np.mean(train_acc))

 

K폴드 교차 검증을 위한 반복을 수행합니다.

model = Model()

k = 5
kf = KFold(n_splits=k, shuffle=True)

val_scores = []

for train_idx, val_idx in kf.split(x_train):
    train_fold, train_target = x_train[train_idx], y_train[train_idx]
    val_fold, val_target = x_train[val_idx], y_train[val_idx]
    
    train_mean = train_fold.mean()
    train_std = train_fold.std()
    
    train_fold = (train_fold - train_mean) / train_std
    val_fold = (val_fold - train_mean) / train_std
    
    model.train(train_fold, train_target, epochs=500)
    acc = model.score(val_fold, val_target)
    val_scores.append(acc)

print('\nk-fold validation accuracy:', np.mean(val_scores))

print('\ntest accuracy:', model.score(x_test, y_test))
train loss: 0.615141 / train accuracy: 0.6809341
train loss: 0.5062132 / train accuracy: 0.91860986
train loss: 0.43708414 / train accuracy: 0.91754395
train loss: 0.37843737 / train accuracy: 0.92094517
train loss: 0.33940402 / train accuracy: 0.9114397

k-fold validation accuracy: 0.9076923

test accuracy: 0.85087717

 

전체 학습 데이터를 5분할합니다.

k = 5
kf = KFold(n_splits=k, shuffle=True)

 

각 반복의 학습 폴드를 이용하여 검증 폴드를 전처리합니다.

_x_train, _y_train = x_train[train_idx], y_train[train_idx]
x_val, y_val = x_train[val_idx], y_train[val_idx]

train_mean = _x_train.mean()
train_std = _x_train.std()

_x_train = (_x_train - train_mean) / train_std
x_val = (x_val - train_mean) / train_std

 

각 반복의 검증 폴드를 이용한 성능 평가에 대한 평균을 구합니다.

val_scores = []

...

acc = model.score(x_val, y_val)
val_scores.append(acc)

...

print('\nk-fold validation accuracy:', np.mean(val_scores))

 

 

  Comments,     Trackbacks