728x90
반응형

 이전 포스트에서 다뤘던 선형 회귀 모형은 단일 입력층과 단일 출력층을 갖는 형태였다. 그러나, Keras를 사용하는 Tensorflow의 가장 큰 장점인 함수형 API모델을 사용하면, 다중 입력과 다중 출력을 갖는 모델을 만들 수 있다.

이번 포스트에서는 Keras의 장점인 함수형 API로 대규모 회귀 및 분류 모델에서 주로 사용되는 Wide & Deep Learning 모델을 만들어보자.

 

 

와이드 & 딥(Wide & Deep) 신경망

  • 2016년 헝쯔 청(Heng-Tze Cheng et al.)의 논문에 소개된 신경망이다.
    Heng-Tze Cheng et al., "Wide & Deep Learning for Recommender Systems." Proceedings of the First Workshop on Deep Learning for Recommender Systems (2016): 7 - 10. http://homl.info/widedeep
  • 인간은 일반화와 암기를 통해 학습을 한다.

  • 일반화(Deep Models): 인간은 "참새는 날 수 있다.", "비둘기는 날 수 있다."를 통해 "날개를 가진 동물은 날 수 있다"라 일반화를 시킨다.
  • 암기(Wide Models): 반면에 "펭귄은 날 수 없다.", "타조는 날 수 없다." 등과 같은 예외 사항을 암기하여, 일반화된 규칙을 더욱 세분화시킨다.
  • Wide & Deep Learning은 이러한 인간의 일반화와 암기를 결합하여 학습하는 점에 착안하여, 만들어진 기계 학습 방법이다.
  • 앞서 우리가 학습해왔던 심층 신경망(Deep Network)은 대상으로부터 공통된 패턴을 찾아내어 일반화시키지만, 그 패턴을 가지고 있으나, 이에 해당하지 않는 많은 반례들이 존재한다.
    (일반적으로 딥 러닝에서는 이를 감안한 데이터 셋을 준비해 학습한다.)
  • Wide & Deep Learning은 여기서 더 나아가 넓은 선형 모델을 공동으로 학습시켜, 일반화(Deep Learning)와 암기(Wide Linear model)의 장점을 얻는다.
  • Wide & Deep Learning은 추천 시스템, 검색 및 순위 문제 같은 많은 양의 범주형 특징(Categorical Feature)이 있는 데이터를 사용하는 대규모 회귀, 분류 모델에서 유용하게 사용된다.
  • Wide & Deep Learning은 위 그림처럼 앞서 학습했던 다층 퍼셉트론(MLP - Deep way)에 입력 데이터의 일부 또는 전체 데이터가 출력층에 바로 연결(Wide way)되는 부분이 추가된 것이다.
  • 이로 인해, Wide & Deep Learning은 복잡한 패턴과 간단한 규칙을 모두 학습할 수 있다.
    (MLP에서는 간단한 패턴이 연속된 변환으로 인해 왜곡될 수 있다.)

 

 

 

 

1. Tensorflow의 함수형 API를 사용하여 Wide & Deep Learning을 구현해보자.

  • sklearn에서 제공하는 캘리포니아 주택 가격 데이터 셋을 사용해보자.
  • sklearn에서 제공하는 기초 함수로 데이터 셋을 분리해보자.
  • sklearn에서 제공하는 기초 함수로 데이터 셋의 스케일 조정을 실시해보자.

  • Wide model과 Deep model이 같은 데이터를 바라보는 Input이 1개인 Wide & Deep Learning Model을 만들어보자.

1.1 데이터셋 준비

# import Module
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import pandas as pd
# 캘리포니아 주택 가격 데이터 셋 확인
Rawdict = fetch_california_housing()

Cal_DF = pd.DataFrame(Rawdict.data, columns=Rawdict.feature_names)
Cal_DF

# Data의 모든 컬럼들의 data type을 확인한다.
>>> Cal_DF.dtypes
MedInc        float64
HouseAge      float64
AveRooms      float64
AveBedrms     float64
Population    float64
AveOccup      float64
Latitude      float64
Longitude     float64
dtype: object


# Data의 모든 컬럼들의 결측값 개수를 확인한다.
>>> Cal_DF.isnull().sum()
MedInc        0
HouseAge      0
AveRooms      0
AveBedrms     0
Population    0
AveOccup      0
Latitude      0
Longitude     0
dtype: int64
  • 데이터 셋은 모두 소수(float64)로 구성되어 있다.
  • 결측 값은 모든 칼럼에 존재하지 않는다.

1.2 데이터셋 분리

>>> X_train_all, X_test, y_train_all, y_test = train_test_split(Rawdict.data, Rawdict.target, test_size = 0.3)
>>> X_train, X_valid, y_train, y_valid = train_test_split(X_train_all, y_train_all, test_size = 0.2)
>>> print("Train set shape:", X_train.shape)
>>> print("Validation set shape:", X_valid.shape)
>>> print("Test set shape:", X_test.shape)

Train set shape: (11558, 8)
Validation set shape: (2890, 8)
Test set shape: (6192, 8)
  • sklearn.model_selection.train_test_split(array, test_size, shuffle): dataset을 쉽게 나눌 수 있는 sklearn 함수로 일반적으로 dataset, label 이 두 array를 동시에 넣고 사용한다.
  • test_size: test Dataset의 비율(float)이나 총 숫자(int)로 나눠준다.
  • shuffle: Dataset을 쪼개기 전에 섞을지 여부를 정해준다.

1.3. 데이터셋 스케일 조정

# 스케일 조정
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_valid = scaler.transform(X_valid)
X_test = scaler.transform(X_test)
  • sklearn.preprocessing.StandardScaler(): 표준 정규분포로 스케일링할 준비를 한다.
  • sklearn.prepocessing은 이밖에도 MinMaxScaler, MaxAbsScaler, RobustScaler 등 다양한 스케일링 기법을 제공한다.
  1. MinMaxScaler(): 최소-최대 스케일 변환, 이상치 영향이 크다.
  2. MaxAbsScaler(): 최대 절대 값을 1로 변환(-1.0 ~ 1.0으로 만드나, 전부 양수인 경우, MinMaxscaler와 동일하다), 이상치 영향이 크다.
  3. StandardScaler(): 표준 정규분포 스케일 변환
  4. RobustScaler(): 중앙값과 사분위 범위(Interquartile range)를 사용하며, StandardScaler보다 표준화 후 동일한 값을 더 넓게 분포 시킴

 

  • StandardScaler().fit_transform(): 데이터셋을 표준 정규분포화 한다. 단, fit_transform에 들어가는 Dataset의 평균과 표준편차를 기준으로 저장하게 된다.
  • StandardScaler().transform(): 데이터셋을 표준 정규분포화 한다. 표준 정규분포화하는 평균과 표준편차는 fit 된 Dataset을 따른다.
  • 앞서 학습하였듯, 스케일링 조정의 기준은 훈련 셋(Train set)이다. 그러므로, fit_transform()에는 Train set이 들어가야 하고, transform에는 검증 셋(Validation set)이나 시험 셋(Test set)이 들어가야 한다.

 

 

 

 

2. 모델 만들기

  • Wide & Deep Learning 같은 순차적이지 않은 신경망을 만들기 위해선 keras의 특징인 함수형 API를 이용해서 Layer를 만들어야 한다.
  • 이번 모델은 1개의 Input Layer와 1개의 Output Layer를 갖는 Wide-Deep Learning 모델을 만들어보겠다.
from tensorflow import keras
from tensorflow.keras.layers import (Input, Dense, Concatenate)
# Model 생성하기
input_ = Input(shape=X_train.shape[1:])
hidden1 = Dense(30, activation="relu")(input_)
hidden2 = Dense(30, activation="relu")(hidden1)
concat = Concatenate()([input_, hidden2])
output = Dense(1)(concat)
model = keras.Model(inputs=[input_], outputs=[output])
  • 함수형 API 모델 생성 방법
  1. Input 객체 생성: Input layer는 배치 크기를 포함하지 않는, array의 모양을 정한다. 즉, Data의 크기가 아닌, 한 Row의 Data의 모양만 반영된다. 예를 들어, shape = (32,)가 되면, 32차원의 벡터가 입력된다는 의미다.
    (변수 이름에 _가 들어간 것은 파이썬에서 언더스코어(_)의 기능을 이용한 것으로, 단순하게 input함수와 충돌되지 않도록 이렇게 만든 것이다.)
  2. 은닉층 객체 생성: 기존처럼 Layer를 add로 만들어진 모델에 은닉층을 쌓는 것이 아닌, 함수로 만들어 변수에 담는다. 여기서 케라스에서 층이 연결될 방법만 선언하였고, 그 어떤 데이터 처리도 발생하지 않은 상태이다.
  3. 결합층 객체 생성: Wide-Deep Learning은 선형 모델과 딥러닝 모델을 동시에 사용하는 방법이므로, 입력될 객체인 input_과 hidden2를 연결해준다.
  4. 출력층 객체 생성: 결합층에서 전달된 내용을 출력함
  5. 모델 생성: 입력과 출력이 지정된 Keras 모델 생성
  • tf.keras.layers.Concatenate(): 연결층이 Tensor를 연결하는 방법은 아래와 같다.
>>> x = np.arange(1, 41, 2).reshape(2,2,5)
>>> x
array([[[ 1,  3,  5,  7,  9],
        [11, 13, 15, 17, 19]],

       [[21, 23, 25, 27, 29],
        [31, 33, 35, 37, 39]]])


>>> y = np.arange(2, 21, 2).reshape(2,1,5)
>>> y
array y:
 [[[ 2  4  6  8 10]]

 [[12 14 16 18 20]]]


>>> tf.keras.layers.Concatenate(axis=1)([x, y])
<tf.Tensor: shape=(2, 3, 5), dtype=int32, numpy=
array([[[ 1,  3,  5,  7,  9],
        [11, 13, 15, 17, 19],
        [ 2,  4,  6,  8, 10]],

       [[21, 23, 25, 27, 29],
        [31, 33, 35, 37, 39],
        [12, 14, 16, 18, 20]]])>
   
   
>>> np.concatenate((x, y), axis=1)
array([[[ 1,  3,  5,  7,  9],
        [11, 13, 15, 17, 19],
        [ 2,  4,  6,  8, 10]],

       [[21, 23, 25, 27, 29],
        [31, 33, 35, 37, 39],
        [12, 14, 16, 18, 20]]])
  • tf.keras.layers.Concatenate()는 np.concatenate() 함수와 동일한 역할을 하며, axis를 어떻게 잡느냐에 따라, 출력되는 Tensor의 모양을 다르게 할 수 있다.

 

 

 

 

3. 모델 학습 및 최종 코드 정리

  • 지금까지 작성했던 코드와 이전에 사용했던 괜찮은 기능들을 합쳐 코드를 정리해보자.
# Import Module
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import callbacks
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.layers import (Input, Dense, Concatenate)
# Dataset 생성
Rawdict = fetch_california_housing()
X_train_all, X_test, y_train_all, y_test = train_test_split(Rawdict.data, Rawdict.target, test_size = 0.3)
X_train, X_valid, y_train, y_valid = train_test_split(X_train_all, y_train_all, test_size = 0.2)

# 스케일 조정
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_valid = scaler.transform(X_valid)
X_test = scaler.transform(X_test)
# 모델 생성
input_ = Input(shape=X_train.shape[1:])
hidden1 = Dense(30, activation="relu")(input_)
hidden2 = Dense(30, activation="relu")(hidden1)
concat = Concatenate()([input_, hidden2])
output = Dense(1)(concat)
model = keras.Model(inputs=[input_], outputs=[output])

# 모델 컴파일
model.compile(optimizer=Adam(learning_rate=0.005),
              loss = "msle",
              metrics=["accuracy"])
# 학습
early_stop = keras.callbacks.EarlyStopping(monitor='val_loss', patience=30, restore_best_weights=True)

history = model.fit(X_train, y_train, 
                    epochs=100,
                    batch_size=32,
                    validation_data=(X_valid, y_valid),
                    callbacks=[early_stop])
Epoch 1/100
362/362 [==============================] - 3s 5ms/step - loss: 0.1195 - accuracy: 0.0031 - val_loss: 0.0360 - val_accuracy: 0.0014
Epoch 2/100
362/362 [==============================] - 1s 1ms/step - loss: 0.0340 - accuracy: 0.0031 - val_loss: 0.0342 - val_accuracy: 0.0014
Epoch 3/100
362/362 [==============================] - 1s 1ms/step - loss: 0.0329 - accuracy: 0.0027 - val_loss: 0.0326 - val_accuracy: 0.0014
Epoch 4/100
362/362 [==============================] - 0s 1ms/step - loss: 0.0336 - accuracy: 0.0034 - val_loss: 0.0321 - val_accuracy: 0.0014

...

Epoch 60/100
362/362 [==============================] - 0s 1ms/step - loss: 0.0219 - accuracy: 0.0045 - val_loss: 0.0263 - val_accuracy: 0.0014
Epoch 61/100
362/362 [==============================] - 0s 1ms/step - loss: 0.0221 - accuracy: 0.0033 - val_loss: 0.0285 - val_accuracy: 0.0014
Epoch 62/100
362/362 [==============================] - 0s 1ms/step - loss: 0.0244 - accuracy: 0.0033 - val_loss: 0.0267 - val_accuracy: 0.0010
Epoch 63/100
362/362 [==============================] - 0s 1ms/step - loss: 0.0227 - accuracy: 0.0032 - val_loss: 0.0264 - val_accuracy: 0.0010
>>> model.evaluate(X_test, y_test)
194/194 [==============================] - 0s 750us/step - loss: 0.0242 - accuracy: 0.0034
[0.02424260601401329, 0.0033914728555828333]
def Drawing_Scalars(history_name):
    
    history_DF = pd.DataFrame(history_name.history)
    # 그래프의 크기와 선의 굵기를 설정해주었다.
    history_DF.plot(figsize=(12, 8), linewidth=3)

    # 교차선을 그린다.
    plt.grid(True)

    plt.legend(loc = "upper right", fontsize =15)

    plt.title("Learning Curve", fontsize=30, pad = 30)
    plt.xlabel('Epoch', fontsize = 20, loc = 'center', labelpad = 20)
    plt.ylabel('Variable', fontsize = 20, rotation = 0, loc='center', labelpad = 40)

    # 위 테두리 제거
    ax=plt.gca()
    ax.spines["right"].set_visible(False) # 오른쪽 테두리 제거
    ax.spines["top"].set_visible(False) # 위 테두리 제거
    
    plt.show()
    
    
Drawing_Scalars(history)

  • 위 모델의 학습 결과를 보면, accuracy는 빠르게 0에 가깝게 떨어진 것을 볼 수 있다.
  • model.evaluate()에서 test set을 대상으로 모델을 평가한 결과 Accuracy는 0.0034가 나왔다.
  • 분류 모델을 사용할 때와, 위와 같은 연속형 데이터에 대한 회귀 모델은 Accuracy의 기준이 다르므로, 위 경우엔 Accuracy, loss 모두 0에 가깝게 나오는 것이 좋다.
  • 참고자료: "머신러닝-5.2. 손실함수(3)-평균제곱근오차(RMSE)"
  • loss는 model.evaluate()에서 0.0242로 최종 손실 값이 0에 가깝게는 나왔으나, 기존 모델보다는 크게 나왔다. 그러나, 손실 값은 상대적인 기준이므로, 0에 가까울수록 좋은 것이긴 하나, 단순하게 나쁘게 봐서는 안된다.

 

[참고 자료]

arxiv.org/abs/1606.07792

 

Wide & Deep Learning for Recommender Systems

Generalized linear models with nonlinear feature transformations are widely used for large-scale regression and classification problems with sparse inputs. Memorization of feature interactions through a wide set of cross-product feature transformations are

arxiv.org

ai.googleblog.com/2016/06/wide-deep-learning-better-together-with.html

 

Wide & Deep Learning: Better Together with TensorFlow

Posted by Heng-Tze Cheng, Senior Software Engineer, Google Research The human brain is a sophisticated learning machine, forming rules by me...

ai.googleblog.com

 

 

 

 지금까지 Input layer가 1개인 Wide & Deep Learning model을 만들어보았다. 그러나, Wide & Deep Learning model의 가장 큰 특징은 Input 되는 층이 지금까지처럼 1개의 동일한 Layer가 아니라, 암기(Linear Learning)를 위한 데이터와 일반화(Deep Learning)를 위한 데이터가 Input 될 층을 따로 구성될 수 있다는 것이다.

 다음 포스트에서는 Input layer가 2개인 경우에 대하여 Wide & Deep Learning model을 만들어보면서, Wide & Deep Learning model에 대해 더 알아보도록 하자.

728x90
반응형

+ Recent posts