Classify Structured data with feature columns

8 minute read

Classify Structured data with feature columns

Tensorflow 2.0에 맞게 다시 Tensorflow를 살펴볼 필요가 있다고 느껴져서 Tensorflow 정식 홈페이지에 나와있는 예제부터 전반적인 Tensorflow 사용법을 먼저 익히는 Post가 된다.

필요한 Library Import

1
2
3
4
5
6
7
8
9
10
11
12
from __future__ import absolute_import, division, print_function, unicode_literals

import io
import requests
import numpy as np
import pandas as pd

import tensorflow as tf

from tensorflow import feature_column
from tensorflow.keras import layers
from sklearn.model_selection import train_test_split



Data Preprocessing

ML과 Deep Learning을 하면서 공통적으로 제일 중요하다고 생각하는 부분은 Data Preprocessing이다.
어떻게 Data를 전처리 하냐에 따라서 같은 Model을 사용하더라도 성능차이는 많이 나게 된다.
따라서 Tensorflow 2.0 Tutorial에서도 이러한 Data를 3가지의 대표적인 유형으로 나누어서 소개하게 된다.

이번 Post Classify Structured data with feature columns는 구조화된 제일 기본적인 Data를 Keras를 사용하여 전처리 하는 Post가 된다.
이러한 Post는 4가지의 목표를 가지고 있다.

  • Pandas를 사용하여 CSV 파일을 읽기
  • tf.data를 사용하여 행을 섞고 배치로 나누는 입력 파이프라인을 만들기
  • Load and preprocess Data
  • Load and preprocess Data2
  • CSV의 열을 feature_columns을 사용해 모델 훈련에 필요한 특성으로 매핑하기
  • 케라스를 사용하여 모델 구축, 훈련, 평가하기

참고
이번 Post는 위의 링크인 Load and preprocess Data1, 2와 많은 부분이 겹치게 됩니다.
복습하는 차원에서 다시 한번 정리하는 Post이므로 위의 링크를 보시고 이해가 되신 분들은 이번 Post는 넘기셔도 됩니다.


The Dataset

Cleveland Clinic Foundation for Heart Disease의 Dataset을 사용하게 됩니다.
각각의 Dataset은 다음과 같은 특성을 가지게 됩니다.
중요한점은 수치형과 범주형 열이 모두 존재한다는 것 이다.

  • 범주형(Categorical Data): 몇 개의 범주로 나누어 진 자료 ex) 남/여, 성공/실패
  • 명목형 자료: 순서와 상관 없는 자료(단순한 분류)
  • 순서형 자료: 순서와 상관 있는 자료
  • 수치형(Numerical Data)
  • 이산형 자료: 이산적인 값을 갖는 데이터
  • 연속형 자료: 연속적인 값을 갖는 데이터
Column Description Feature Type Data Type
Age Age in years Numerical integer
Sex (1 = male; 0 = female) Categorical integer
CP Chest pain type (0, 1, 2, 3, 4) Categorical integer
Trestbpd Resting blood pressure (in mm Hg on admission to the hospital) Numerical integer
Chol Serum cholestoral in mg/dl Numerical integer
FBS (fasting blood sugar > 120 mg/dl) (1 = true; 0 = false) Categorical integer
RestECG Resting electrocardiographic results (0, 1, 2) Categorical integer
Thalach Maximum heart rate achieved Numerical integer
Exang Exercise induced angina (1 = yes; 0 = no) Categorical integer
Oldpeak ST depression induced by exercise relative to rest Numerical float
Slope The slope of the peak exercise ST segment Numerical integer
CA Number of major vessels (0-3) colored by flourosopy Numerical integer
Thal 3 = normal; 6 = fixed defect; 7 = reversable defect Categorical string
Target Diagnosis of heart disease (1 = true; 0 = false) Classification integer



Use Pandas to create a dataframe

Pandas를 활용하여 위의 Dataset을 Dataframe형태로 변현한다.

참고(Request Error)
현재 Tensorflow 2.0에서 제공하는 Code를 그대로 사용하는 경우 Request Error가 발생하게 된다.
Pandas Version Error인지 Jupyter Notebook의 Ipython Error인지는 파악되지 않으나 Stack Overflow를 참조하여 Code를 아래와 같이 변경하여 해결하였다.

1
2
3
4
5
6
URL = 'https://storage.googleapis.com/applied-dl/heart.csv'
# Download and read Dataset
x = requests.get(url=URL).content 
dataframe = pd.read_csv(io.StringIO(x.decode('utf8')))
# Top 5 Data Check
dataframe.head()





Split the dataframe into train, validation, and test

위에서 선언한 Dataframe을 활용하여 Model에 필요한 Train, Validation, and Test Dataset으로서 sklearn을 활용하여 Split을 하게 된다.

1
2
3
4
5
6
7
8
# Split the dataframe into train, validation, and test
train, test = train_test_split(dataframe,test_size=0.2)
train, val = train_test_split(train,test_size=0.2)

# Check the Split Data
print(len(train),'train examples')
print(len(val),'validation examples')
print(len(test),'test examples')


193 train examples
49 validation examples
61 test examples


Create an input pipeline using tf.data

tf.data를 활용하여 Feature, Label로서 나누고 Batch처리 까지 진행하게 된다. Model의 성능을 향상시키기 위하여 Train Dataset의 경우에는 Shuffle까지 진행하게 된다.

아래 Code가 이해되지 않으면 밑의 두 링크를 참조하도록 하자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def df_to_dataset(dataframe, shuffle=True, batch_size=32):
    dataframe = dataframe.copy()
    # Pop Label
    labels = dataframe.pop('target')
    # Datafrmae to Tensor
    ds = tf.data.Dataset.from_tensor_slices((dict(dataframe),labels))
    # Shuffle Tensor
    if shuffle:
        ds = ds.shuffle(buffer_size = len(dataframe))
    # Batch Tensor
    ds = ds.batch(batch_size)
    return ds

batch_size = 5
# Train Data with Shuffle
train_ds = df_to_dataset(train,batch_size=batch_size)
# Validation Data, Test Data with no shuffle
val_ds = df_to_dataset(val,shuffle=False,batch_size=batch_size)
test_ds = df_to_dataset(test,shuffle=False,batch_size=batch_size)

# Check Tensor
for feature_batch, label_batch in train_ds.take(1):
    print('Every Feature: ',list(feature_batch.keys()))
    print('A batch of ages: ', feature_batch['age'])
    print('A batch of targets: ',label_batch)


Every Feature:  ['age', 'sex', 'cp', 'trestbps', 'chol', 'fbs', 'restecg', 'thalach', 'exang', 'oldpeak', 'slope', 'ca', 'thal']
A batch of ages:  tf.Tensor([61 64 57 41 63], shape=(5,), dtype=int32)
A batch of targets:  tf.Tensor([0 1 0 0 1], shape=(5,), dtype=int32)




Demonstrate several types of feature column

train_ds를 계속하여 Input으로 넣으나, tf.keras.layers.DenseFeatures를 사용하여 Feature Columns가 어떻게 변형되는지 살펴본다.
즉, 원하는 Dataframe의 Column을 Input으로 넣어서 어떻게 DenseFeaures로 표현되는지 살펴본다.

1
2
3
4
5
6
7
# Use this batch to demonstrate several types of feature columns
example_batch = next(iter(train_ds))[0] # Not use Label

# Create a Feature column and transform a batch of data
def demo(feature_column):
    feature_layer = layers.DenseFeatures(feature_column)
    print(feature_layer(example_batch).numpy())




Numeric columns

가장 간단한 Numeric columns이다. 기본적인 Input Data를 그대로 표현하여 나타내는 것 이다.
참조: tf.feature_column.numeric_column 사용법
참조: Option을 사용하여 Data의 수치를 바로 변경하는 것이 가능하다.(즉, 평균과 분산을 알게 되면 Normalization을 바로 적용시킬 수 있다.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Ignore Warning Change Backend to float 64
tf.keras.backend.set_floatx('float64')

# Check Numeric Column
age = feature_column.numeric_column('age')
print('Check NUmeric Column')
demo(age)
print()

# Check Option
normalizer_function = lambda x:(x-3)
age = feature_column.numeric_column('age',normalizer_fn=normalizer_function)
print('Check Option')
demo(age)


Check NUmeric Column
[[66.]
 [63.]
 [51.]
 [40.]
 [46.]]

Check Option
[[63.]
 [60.]
 [48.]
 [37.]
 [43.]]




Bucketized columns

위와 같은 Numeric한 수치를 바로 넣는 것이 아니라 일정한 버킷(Bucket)으로 나눌 수 있다.
즉, 일정한 번위안의 속하면 1, 아니면 0 으로서 One-Hot-Encoding형식으로서 나타내어 Bucketized Columns로 나타낼 수 있다.
참조: tf.feature_column.bucketized_column 사용법

1
2
age_buckets = feature_column.bucketized_column(age,boundaries=[18,25,30,40,45,50,55,60,65])
demo(age_buckets)


[[0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]
 [0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]
 [0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]]




Categorical columns

범주형 데이터(Cateforical Data)의 경우 Model의 Input으로 넣기 위하여 수치형 데이터(Numerical Data)로 변경하여야 한다.
위의 Bucketized columns와 마찬가지로서 Category에 따라서 One-Hot-Encoidng으로서 나타낸다.

참조: tf.feature_column.indicator_column: 기본적으로 Categorical Column을 Input으로 넣기 위해서는 .indicator_column가 필요하다.
참조: tf.feature_column.catrgorical_column_with_vocabulary_list 사용법(List로서 전달)
참조: tf.feature_column.catrgorical_column_with_vocabulary_file 사용법(File로서 전달)

1
2
3
4
5
6
7
8
9
10
# Check Categorical Value
thal_category = set(dataframe['thal'])
print('Thal Catrgory')
print(thal_category)
print()

# Categorical Columns -> Numeric Tensor
thal = feature_column.categorical_column_with_vocabulary_list('thal',['fixed','normal','reversible','1','2'])
thal_one_hot = feature_column.indicator_column(thal)
demo(thal_one_hot)


Thal Catrgory
{'1', 'normal', 'reversible', 'fixed', '2'}

[[0. 1. 0. 0. 0.]
 [0. 1. 0. 0. 0.]
 [0. 1. 0. 0. 0.]
 [0. 0. 1. 0. 0.]
 [0. 1. 0. 0. 0.]]




Embedding columns

Category의 수가 매우 많아지게 되었을 경우 One-Hot-Encoding으로서 나타내는 것은 Resource를 많이 낭비하게 되고, Computing Power가 버티지 못할 확률이 매우 높아진다.
따라서 Embedding Columns로서 나타내는 것이 필요하다.
개인적인 경험으로는 이러한 Embedding Layer는 자연어 분야에서 Vocab File의 전처리를 위하여 많이 사용되는 것으로 알 고 있다.

참조: Embedding 자세한 내용
참조: tf.feature_column.embedding_column 사용법

1
2
thal_embedding = feature_column.embedding_column(thal,dimension=3)
demo(thal_embedding)


[[-0.04294701  0.6549929   0.1659287 ]
 [-0.04294701  0.6549929   0.1659287 ]
 [-0.04294701  0.6549929   0.1659287 ]
 [ 0.6932911   0.52152395 -0.26704228]
 [-0.04294701  0.6549929   0.1659287 ]]




Hashed feature columns

위와 같이 많은 Category의 수를 줄이기 위한 방법으로서 Hash Feature Columns를 사용하는 방법이 있다.
하지만, 어떻게 Hash를 할당하는 지는 자세히 모르겠으며, 다른 문자열이 같은 Bucket에 할당 될 수 있는 매우 큰 단점이 존재하나, 일부 데이터셋 에서 잘 작동한다고 한다.
참조: tf.feature_column.categorical_column_with_hash_bucket 사용법

1
2
3
thal_hashed = feature_column.categorical_column_with_hash_bucket(
      'thal', hash_bucket_size=2)
demo(feature_column.indicator_column(thal_hashed))


[[0. 1.]
 [0. 1.]
 [0. 1.]
 [1. 0.]




Crossed Feature columns

여러 특성을 연결하여 하나의 특성으로 만드는 것을 feature cross라고 한다.
모델이 특성의 조합에 대한 가중치를 학습할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
# Check Thal's Categorical Value
thal_category = set(dataframe['thal'])
print('Thal Catrgory')
print(thal_category)
print()

# Check Age bucket
print("Age bucket")
print(age_buckets.boundaries)
print()

crossed_feature = feature_column.crossed_column([age_buckets,thal],hash_bucket_size=10)
demo(feature_column.indicator_column(crossed_feature))


Thal Catrgory
{'1', 'normal', 'reversible', 'fixed', '2'}

Age bucket
(18, 25, 30, 35, 40, 45, 50, 55, 60, 65)

[[0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]
 [0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]]




Choose which columns to use

실제 Dataframe에서 사용할 Columns를 선택하여 Input Tensor로서 변형하기 위한 과정이다.
Numeric Data, Bucketized Column(Age), Categorical Column(Thal)을 위에서 설명한 전처리 과정으로서 Data Preprocessing Layer를 추가하여 실제 Model의 Input으로서 사용하기 위한 Layer를 선언하는 방법이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
feature_columns = []

# numeric cols
for header in ['age', 'trestbps', 'chol', 'thalach', 'oldpeak', 'slope', 'ca']:
    feature_columns.append(feature_column.numeric_column(header))

# bucketized cols
age_buckets = feature_column.bucketized_column(age, boundaries=[18, 25, 30, 35, 40, 45, 50, 55, 60, 65])
feature_columns.append(age_buckets)

# indicator cols
thal = feature_column.categorical_column_with_vocabulary_list(
      'thal', ['fixed', 'normal', 'reversible','1','2'])
thal_one_hot = feature_column.indicator_column(thal)
feature_columns.append(thal_one_hot)

# Create Feature Layer
feature_layer = tf.keras.layers.DenseFeatures(feature_columns)




Create Model & Train & Test

위에서 선언한 Data Preprocessing Layer 를 사용하여 실제 Model에 넣고 정확도까지 확인하는 과정 이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
model = tf.keras.Sequential([
    feature_layer,
    layers.Dense(128,activation='relu'),
    layers.Dense(128,activation='relu'),
    layers.Dense(1)
])

model.compile(optimizer='adam',
             loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
             metrics=['accuracy'])

model.fit(train_ds,
         validation_data = val_ds,
         epochs = 10)

loss,accuracy = model.evaluate(test_ds)
print('Accuracy',accuracy)


Train for 39 steps, validate for 10 steps
Epoch 1/10
39/39 [==============================] - 1s 14ms/step - loss: 6.0396 - accuracy: 0.6218 - val_loss: 1.2359 - val_accuracy: 0.7755
Epoch 2/10
39/39 [==============================] - 0s 3ms/step - loss: 1.4230 - accuracy: 0.6891 - val_loss: 1.2503 - val_accuracy: 0.6735
Epoch 3/10
39/39 [==============================] - 0s 2ms/step - loss: 0.7141 - accuracy: 0.7513 - val_loss: 1.5309 - val_accuracy: 0.5714
Epoch 4/10
39/39 [==============================] - 0s 2ms/step - loss: 1.4310 - accuracy: 0.6736 - val_loss: 0.8234 - val_accuracy: 0.7143
Epoch 5/10
39/39 [==============================] - 0s 3ms/step - loss: 0.6450 - accuracy: 0.7720 - val_loss: 0.6224 - val_accuracy: 0.7551
Epoch 6/10
39/39 [==============================] - 0s 3ms/step - loss: 0.6200 - accuracy: 0.7513 - val_loss: 0.5520 - val_accuracy: 0.7755
Epoch 7/10
39/39 [==============================] - 0s 3ms/step - loss: 0.6343 - accuracy: 0.7202 - val_loss: 0.7297 - val_accuracy: 0.7959
Epoch 8/10
39/39 [==============================] - 0s 3ms/step - loss: 0.8828 - accuracy: 0.6891 - val_loss: 0.8319 - val_accuracy: 0.7347
Epoch 9/10
39/39 [==============================] - 0s 3ms/step - loss: 0.6719 - accuracy: 0.7720 - val_loss: 1.8141 - val_accuracy: 0.4694
Epoch 10/10
39/39 [==============================] - 0s 3ms/step - loss: 0.7413 - accuracy: 0.7306 - val_loss: 0.5578 - val_accuracy: 0.7755
13/13 [==============================] - 0s 1ms/step - loss: 0.8657 - accuracy: 0.7541
Accuracy 0.75409836



참조: 원본코드
참조: Classify structured data with feature columns
코드에 문제가 있거나 궁금한 점이 있으면 wjddyd66@naver.com으로 Mail을 남겨주세요.

Leave a comment