본문 바로가기

Machine Learning Algorithm

[ML] Tensorflow 2.0을 이용한 Breast Cancer Wisconsin(유방암) 데이터 분류분석(Hparams를 이용한 하이퍼파라미터 튜닝)

데이터 출처: http://mlr.cs.umass.edu/ml/datasets/Breast+Cancer+Wisconsin+%28Diagnostic%29


 Breast Cancer Wisconsin 데이터셋은 특정 지역의 유방암 환자들의 종양 정보와 환자 정보를 이용해 해당 종양이 악성인지 양성인지를 판단하는 데이터 입니다. 본 글에서는 Breast Cancer Wisconsin 데이터셋을 활용해서 정확도 90% 이상의 분류기를 만드는 것이 목적입니다. 

import tensorflow as tf
from tensorboard.plugins.hparams import api as hp
import pandas as pd
import requests
import matplotlib.pyplot as plt
import numpy as np
import random
%load_ext tensorboard

 필요한 라이브러리를 위와 같습니다. 마지막 줄은 tensorboard를 jupyter notebook 환경에서 사용하기 위한 코드입니다.

def downloadDataOnline(url):
    r=requests.get(url, allow_redirects=True)
    open('cancer.data', 'wb').write(r.content)
downloadDataOnline('http://mlr.cs.umass.edu/ml/machine-learning-databases/breast-cancer-wisconsin/wdbc.data')

 먼저 iris 분류분석과 마찬가지로 requests 라이브러리를 이용하여 데이터를 자동적으로 다운로드 받고 저장하는 코드를 작성했습니다. 

df=pd.read_csv('cancer.data',header=None)
df.head()

 데이터의 모습을 살펴보면 (569, 32) 크기로 32개의 feature가 있다는 것을 알 수 있습니다. 데이터의 첫번째 행은 환자의 고유 번호를 의미하기 때문에 학습에 필요없는 부분이고, 두번째 행은 레이블을 나타내고 있습니다. M은 악성 종양(Malignant tumor), B는 양성 종양(Benign tumor) 을 의미합니다. 따라서 결국 학습에 이용되는 feature는 30가지라는 것을 알 수 있습니다. 

df.info()
>>>
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 569 entries, 0 to 568
Data columns (total 32 columns):
0     569 non-null int64
1     569 non-null object
2     569 non-null float64
3     569 non-null float64
...

...
30    569 non-null float64
31    569 non-null float64
dtypes: float64(30), int64(1), object(1)
memory usage: 142.3+ KB

 데이터의 각 행을 살펴보면 0, 1번 행을 제외한 모든 column은 float64 형으로 실수라는 것을 알 수 있습니다. 따라서 원 핫 인코딩과 같은 전처리를 수행하지 않아도 된다는 것을 알 수 있습니다. 

def SeperateLabel(df):
    data=df.copy()
    labels=data[1].to_numpy().reshape(-1,1)
    
    labels[labels=='M']=1
    labels[labels=='B']=0
    
    data.drop(columns=[1],inplace=True)
    data.head()
    return data,labels.astype(int)

 데이터에서 레이블을 분리하고 0, 1로 변환시켜주는 SeperateLabel 함수를 작성했습니다. 

def PreprocessData(data):
    data,Y=SeperateLabel(data)
    data.drop(columns=[0],inplace=True)
    X=data.to_numpy()
    return X,Y
X,Y=PreprocessData(df)

 데이터의 전처리를 완전하게 수행하는 함수를 작성했습니다. 이와 같은 함수를 정의하는 이유는 새로운 데이터가 들어왔을 때 전처리 과정을 위 코드를 다시 칠 필요 없이 PreprocessData(new_data)와 같이 단순화할 수 있기 때문입니다.

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.2)

 training set과 test set을 나누는 코드입니다. 

HP_NUM_UNITS_1 = hp.HParam('num_units_1', hp.Discrete([32, 64, 128, 256]))
HP_NUM_UNITS_2 = hp.HParam('num_units_2', hp.Discrete([32, 64, 128, 256]))
HP_NUM_UNITS_3 = hp.HParam('num_units_3', hp.Discrete([32, 64, 128, 256]))
HP_REGULARIZATION = hp.HParam('regularization', hp.Discrete([0.1, 0.05, 0.01, 0.005, 0.001, 0.0005, 0.0001, 0.0]))
HP_DROPOUT = hp.HParam('dropout', hp.Discrete([0.0, 0.1, 0.2, 0.3, 0.4]))

METRIC_ACCURACY = 'accuracy'

with tf.summary.create_file_writer('logs/breast_cancer_tuning').as_default():
    hp.hparams_config(
    hparams=[HP_NUM_UNITS_1, HP_NUM_UNITS_2, HP_NUM_UNITS_3, HP_DROPOUT, HP_REGULARIZATION],
    metrics=[hp.Metric(METRIC_ACCURACY, display_name='Accuracy')],
  )

 이번 모델에서는 단순히 한번 학습시키는 것이 아닌 하이퍼파라미터를 바꾸어가며 여러번 학습시키고, 이중 가장 정확한 예측을 했던 하이퍼파라미터를 선택할 것입니다. 위 코드는 Hparam 하이퍼파라미터를 정의하는 코드입니다. 4층 신경망에서 각 계층의 유닛의 갯수, l2 정규화 계수, 드롭아웃 계수 총 5개의 변수를 하이퍼파라미터로 정했습니다. 

def train_model(hparams):
    model = tf.keras.models.Sequential([
      tf.keras.layers.Dropout(hparams[HP_DROPOUT]),
      tf.keras.layers.Dense(hparams[HP_NUM_UNITS_1], activation='relu', kernel_regularizer=tf.keras.regularizers.l2(10**hparams[HP_REGULARIZATION])),
      tf.keras.layers.Dropout(hparams[HP_DROPOUT]),
      tf.keras.layers.Dense(hparams[HP_NUM_UNITS_2], activation='relu', kernel_regularizer=tf.keras.regularizers.l2(10**hparams[HP_REGULARIZATION])),
      tf.keras.layers.Dropout(hparams[HP_DROPOUT]),
      tf.keras.layers.Dense(hparams[HP_NUM_UNITS_3], activation='relu', kernel_regularizer=tf.keras.regularizers.l2(10**hparams[HP_REGULARIZATION])),
      tf.keras.layers.Dropout(hparams[HP_DROPOUT]),
      tf.keras.layers.Dense(2, activation='softmax')
    ])
    model.compile(optimizer='adam',
                  loss='sparse_categorical_crossentropy',
                  metrics=['accuracy'])
    model.fit(X_train, y_train.astype(int), epochs=200)

    _,acc=model.evaluate(X_test,  y_test, verbose=2)
    return acc
def run(directory,hparams):
    with tf.summary.create_file_writer(directory).as_default():
        hp.hparams(hparams)  # record the values used in this trial
        accuracy = train_model(hparams)
        tf.summary.scalar(METRIC_ACCURACY, accuracy, step=1)

 하이퍼파라미터가 주어졌을 때 한번의 학습을 한 세션이라고 했을 때 train_model 함수는 한 세션을 학습시키고 정확도를 반환하는 함수입니다. 그리고 run 함수는 하이퍼파라미터 값을 기록하는 등 한 세션의 전체 과정을 수행하는 함수입니다. 

session_num=0
max_session=100
for _ in range(max_session):
    hparams = {
      HP_NUM_UNITS_1: random.choice(HP_NUM_UNITS_1.domain.values),
      HP_NUM_UNITS_2: random.choice(HP_NUM_UNITS_2.domain.values),
      HP_NUM_UNITS_3: random.choice(HP_NUM_UNITS_3.domain.values),
      HP_DROPOUT: random.choice(HP_DROPOUT.domain.values),
      HP_REGULARIZATION: random.choice(HP_REGULARIZATION.domain.values),
    }
    run_name = "run-%d" % session_num
    print('--- Starting trial: %s' % run_name)
    print({h.name: hparams[h] for h in hparams})
    run('logs/breast_cancer_tuning/' + run_name, hparams)
    session_num += 1

 

 

>>>
--- Starting trial: run-0
{'num_units_1': 256, 'num_units_2': 32, 'num_units_3': 256, 'dropout': 0.2, 'regularization': 0.0001}
Train on 455 samples
Epoch 1/200
455/455 [==============================] - 0s 868us/sample - loss: 161.4963 - accuracy: 0.4725
Epoch 2/200
455/455 [==============================] - 0s 61us/sample - loss: 124.8826 - accuracy: 0.5143
...
--- Starting trial: run-1
{'num_units_1': 128, 'num_units_2': 256, 'num_units_3': 128, 'dropout': 0.4, 'regularization': 0.05}
Train on 455 samples
Epoch 1/200
455/455 [==============================] - 0s 859us/sample - loss: 463.0978 - accuracy: 0.5099
Epoch 2/200
455/455 [==============================] - 0s 68us/sample - loss: 390.3644 - accuracy: 0.5077
...

...
--- Starting trial: run-99
{'num_units_1': 128, 'num_units_2': 128, 'num_units_3': 32, 'dropout': 0.4, 'regularization': 0.0001}
Train on 455 samples
Epoch 1/200
455/455 [==============================] - 0s 875us/sample - loss: 286.4117 - accuracy: 0.5011
...
Epoch 200/200
455/455 [==============================] - 0s 66us/sample - loss: 0.6073 - accuracy: 0.7978
114/1 - 3s - loss: 0.6034 - accuracy: 0.7982

 hparams 딕셔너리에 랜덤으로 정한 하이퍼 파라미터 값을 담고 run 함수를 실행하는 과정을 max_session번 반복하는 코드입니다. 실행시 100번 랜덤한 하이퍼파라미터를 가지는 모델을 학습시키고 결과를 기록합니다. 

%tensorboard --logdir logs/breast_cancer_tuning  --host localhost

 학습 과정에서 기록한 정보는 tensorboard를 통해 확인할 수 있습니다. 위 명령어를 실행하면, jupyter notebook 환경에서 tensorboard가 실행되는데, 이때 다양한 문제가 있을 수 있기 때문에 stackoverflow에 문제를 검색하는 것을 추천 드립니다. 

성공적으로 실행이 된다면 위와 같은 그림이 보여질 것입니다. 오른쪽의 그래프는 정확도를 나타내고 있습니다. 마우슬를 올려 보면 run의 번호와 정확도 값이 표시됩니다. 만약 점이 그려져있지 않다면 왼쪽 아래의 Toggle all runs 버튼을 누르면 점이 표시됩니다. 위의 HPARAMS 버튼을 클릭하면 각 run 별로 하이퍼파라미터의 값을 확인할 수 있습니다. tensoboard에는 학습 관련된 거의 모든 정보가 나타나 있습니다. Tensorboard에 관한 정보는 tensorflow 다큐멘테이션https://www.tensorflow.org/tensorboard/get_started 를 확인해 주세요.