728x90
반응형

 이전 포스트까지 Wide & Deep Learning 모델을 사용해서 다중 입력 모델을 만들어보았다. 이번엔 이전 모델에 추가로 출력층을 추가하여 출력층을 2개로 만들어 보도록 하자.

 

 

다중 출력 모델

  • 일반적으로 다중 출력 모델을 사용하는 이유는 다음과 같다.

  1. 프로세스가 서로 독립적인 경우:
    동일한 데이터 셋에서 두 개 이상의 출력층을 생성하지만, 출력된 결과는 전혀 다른 작업을 위해 실행되는 경우로, 예를 들어 인터넷 사용 Log 데이터를 기반으로, 대상의 관심사 파악(분류 모델)과 인터넷 사용률 예측(회귀 모델)은 별개의 목적을 위해 진행된다.
  2. 프로세스가 한 목적을 위해 상호 보완적으로 이루어지는 경우:
    하나의 목적을 이루기 위해 두 개 이상의 출력 값이 필요하여, 동일한 데이터 셋에서 두 개 이상의 출력층을 뽑아내는 경우로, 예를 들어 영상 속 물건을 인식하는 모델을 만든다면, 영상 속 물건의 위치(회귀 모델)와 물건의 종류(분류 모델)를 동시에 파악해야 한다.
  3. 규제 도구로써의 다중 출력 모델:
    하위 네트워크가 나머지 네트워크에 의존하지 않고, 그 자체로 유용한 성능을 내는지 확인을 하기 위한 규제 기법으로 사용

 이번 포스트에서는 Wide & Deep Learning model에 다중 출력 모델을 규제 도구로써 사용해보도록 하자.

 

 

 

 

1. 규제 도구로써의 다중 출력 모델

  • 보조 출력층(Auxiliary Output)을 통해, 하위 네트워크가 나머지 네트워크에 의존하지 않고 그 자체로 유용한 것을 학습하는지 확인한다. 위 모델대로라면, Deep model에서의 출력된 값이 Wide model과의 연결로 인해, 원하는 결과를 뽑아내는 것인지, Deep model으로 인해 그러한 결과가 나왔는지 확인할 수 있다.
  • 이를 통해, 과대 적합을 감소시키고, 모델의 일반화 성능이 높이도록 학습에 제약을 가할 수 있다.

 

 

 

 

2. 모델 생성 이전까지의 코드 가져오기

  • 이전 포스트에서 만들었던, 모델 이전까지의 코드를 모두 가지고 온다.
  • 입력층이 2개일 때는 입력 데이터를 목적에 맞게 나눴어야 했지만, 이번엔 출력층이 다른 데이터를 출력하는 것이 아니라, 보조 출력층(Auxiliary Output)과 주 출력층(Main Output)이 동일한 정답에 대해 어느 정도의 손실 값과 정확도를 내보내는지 알고 싶은 것이므로, Label dataset을 나누지 않아도 된다.
#################################### 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)
########################################################################################





#################################### Import Dataset ####################################
# 캘리포니아 데이터 가져오기
Rawdict = fetch_california_housing()
CaliFornia_DF = pd.DataFrame(Rawdict.data, columns=Rawdict.feature_names)
########################################################################################




#################################### Data Handling #####################################
# 데이터를 쪼개기 좋게 변수의 순서를 바꾸자
CaliFornia_DF = CaliFornia_DF[["HouseAge", "Population", "Latitude", "Longitude", "MedInc",
                               "AveRooms", "AveBedrms", "AveOccup"]]

# train, validation, test set으로 쪼갠다.
X_train_all, X_test, y_train_all, y_test = train_test_split(CaliFornia_DF.values, 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 layer의 수만큼 쪼갠다.
X_train_A, X_train_B = X_train[:, :1], X_train[:,1:]
X_valid_A, X_valid_B = X_valid[:, :1], X_valid[:,1:]
X_test_A, X_test_B = X_test[:, :1], X_test[:,1:]
########################################################################################

 

 

 

 

3. 모델 생성

######################################## Model ########################################
input_A = Input(shape=[1], name = "deep_input")
input_B = Input(shape=[7], name = "wide_input")
hidden1 = Dense(30, activation="relu", name = "hidden1")(input_A)
hidden2 = Dense(30, activation="relu", name = "hidden2")(hidden1)
concat = concatenate([input_B, hidden2], name = "concat")
output = Dense(1, name="main_output")(concat)

# 보조 출력층 생성
aux_output = Dense(1, name="aux_output")(hidden2)
model = keras.Model(inputs=[input_A, input_B], outputs=[output, aux_output])
########################################################################################
>>> model.summary()
Model: "model"
__________________________________________________________________________________________________
Layer (type)                    Output Shape         Param #     Connected to                     
==================================================================================================
deep_input (InputLayer)         [(None, 1)]          0                                            
__________________________________________________________________________________________________
hidden1 (Dense)                 (None, 30)           60          deep_input[0][0]                 
__________________________________________________________________________________________________
wide_input (InputLayer)         [(None, 7)]          0                                            
__________________________________________________________________________________________________
hidden2 (Dense)                 (None, 30)           930         hidden1[0][0]                    
__________________________________________________________________________________________________
concat (Concatenate)            (None, 37)           0           wide_input[0][0]                 
                                                                 hidden2[0][0]                    
__________________________________________________________________________________________________
main_output (Dense)             (None, 1)            38          concat[0][0]                     
__________________________________________________________________________________________________
aux_output (Dense)              (None, 1)            31          hidden2[0][0]                    
==================================================================================================
Total params: 1,059
Trainable params: 1,059
Non-trainable params: 0
__________________________________________________________________________________________________
  • 앞서 학습했던 방법처럼 보조 출력층 생성 역시 Keras API 함수로 구현하는 것은 꽤 단순하다.
  • 보조 출력층(aux_output)과 데이터를 받는 층(hidden2)을 연결시키고, model에서 outputs을 2개 다 잡아주면 된다.

 

 

 

 

4. 모델 컴파일

  • 다중 입력 모델과 달리 다중 출력 모델에서는 모델 컴파일 방법이 바뀌게 된다.
  • 이는, 컴파일에서 출력층에서 모델을 평가하게 되는 손실 함수를 결정하기 때문이고, 이 손실 함수는 출력층마다 다르게 설정해야 하기 때문이다.
# 모델 컴파일
model.compile(optimizer=Adam(learning_rate=0.005),
              loss = ["msle", "msle"],
              metrics=["accuracy"],
              loss_weights=[0.9, 0.1])
  • 최적화나 모델 평가 지표는 출력층의 수와 상관없기 때문에 바뀌지 않는다.
  • 손실 함수는 출력층이 2개가 되었으므로, 2개를 잡아줘야 한다(만약, 손실 함수를 하나만 잡아준다면, 모든 출력의 손실 함수가 같다고 가정한다 - 위 경우에는 출력층이 회귀모형이므로, msle로 같은 손실 함수를 사용하므로, msle 하나만 사용해도 된다).
  • loss_weights: 출력 층별 손실 값의 가중치를 정해준다. 케라스는 기본적으로 출력된 손실 값들을 모두 더해 최종 손실을 구하며, 이를 기반으로 학습을 한다. 여기서 사용된 보조 출력은 규제로 사용되었기 때문에 주 출력이 더 중요하다. 그러므로, 주 손실 값에 더 많은 가중치를 부여하였다.

 

 

 

 

5. 모델 학습 및 평가

  • 모델 학습과 평가에서의 차이는 Label 역시 각 출력층에 맞게 2개가 들어가야 한다는 것이다.
  • 해당 다중 출력 모델은 규제 목적으로 보조 출력층을 추가한 것이므로, 동일한 데이터를 label로 사용해도 된다.
early_stop = keras.callbacks.EarlyStopping(monitor='val_loss', patience=30, restore_best_weights=True)

# 학습
history = model.fit([X_train_A, X_train_B], [y_train, y_train],
                    epochs=300,
                    batch_size = 32,
                    validation_data=([X_valid_A, X_valid_B], [y_valid, y_valid]),
                    callbacks=[early_stop])
Epoch 1/300
362/362 [==============================] - 3s 6ms/step - loss: 0.2162 - main_output_loss: 0.1988 - aux_output_loss: 0.3726 - main_output_accuracy: 0.0027 - aux_output_accuracy: 0.0023 - val_loss: 0.0697 - val_main_output_loss: 0.0633 - val_aux_output_loss: 0.1276 - val_main_output_accuracy: 0.0048 - val_aux_output_accuracy: 0.0048
Epoch 2/300
362/362 [==============================] - 1s 2ms/step - loss: 0.0650 - main_output_loss: 0.0584 - aux_output_loss: 0.1243 - main_output_accuracy: 0.0026 - aux_output_accuracy: 0.0026 - val_loss: 0.0619 - val_main_output_loss: 0.0544 - val_aux_output_loss: 0.1295 - val_main_output_accuracy: 0.0048 - val_aux_output_accuracy: 0.0048
Epoch 3/300
362/362 [==============================] - 1s 2ms/step - loss: 0.0584 - main_output_loss: 0.0509 - aux_output_loss: 0.1260 - main_output_accuracy: 0.0027 - aux_output_accuracy: 0.0027 - val_loss: 0.0598 - val_main_output_loss: 0.0522 - val_aux_output_loss: 0.1279 - val_main_output_accuracy: 0.0048 - val_aux_output_accuracy: 0.0048
Epoch 4/300
362/362 [==============================] - 1s 2ms/step - loss: 0.0555 - main_output_loss: 0.0480 - aux_output_loss: 0.1229 - main_output_accuracy: 0.0018 - aux_output_accuracy: 0.0018 - val_loss: 0.0591 - val_main_output_loss: 0.0514 - val_aux_output_loss: 0.1281 - val_main_output_accuracy: 0.0045 - val_aux_output_accuracy: 0.0048

...

Epoch 54/300
362/362 [==============================] - 0s 1ms/step - loss: 0.0522 - main_output_loss: 0.0444 - aux_output_loss: 0.1227 - main_output_accuracy: 0.0024 - aux_output_accuracy: 0.0024 - val_loss: 0.0556 - val_main_output_loss: 0.0476 - val_aux_output_loss: 0.1276 - val_main_output_accuracy: 0.0045 - val_aux_output_accuracy: 0.0048
Epoch 55/300
362/362 [==============================] - 1s 1ms/step - loss: 0.0524 - main_output_loss: 0.0446 - aux_output_loss: 0.1225 - main_output_accuracy: 0.0026 - aux_output_accuracy: 0.0026 - val_loss: 0.0540 - val_main_output_loss: 0.0460 - val_aux_output_loss: 0.1263 - val_main_output_accuracy: 0.0045 - val_aux_output_accuracy: 0.0048
Epoch 56/300
362/362 [==============================] - 1s 2ms/step - loss: 0.0525 - main_output_loss: 0.0448 - aux_output_loss: 0.1215 - main_output_accuracy: 0.0024 - aux_output_accuracy: 0.0024 - val_loss: 0.0539 - val_main_output_loss: 0.0458 - val_aux_output_loss: 0.1265 - val_main_output_accuracy: 0.0048 - val_aux_output_accuracy: 0.0048
Epoch 57/300
362/362 [==============================] - 0s 1ms/step - loss: 0.0535 - main_output_loss: 0.0457 - aux_output_loss: 0.1234 - main_output_accuracy: 0.0033 - aux_output_accuracy: 0.0033 - val_loss: 0.0543 - val_main_output_loss: 0.0463 - val_aux_output_loss: 0.1264 - val_main_output_accuracy: 0.0045 - val_aux_output_accuracy: 0.0048
>>> model.evaluate([X_test_A, X_test_B], [y_test, y_test])

194/194 [==============================] - 0s 1ms/step - loss: 0.0533 - main_output_loss: 0.0454 - aux_output_loss: 0.1241 - main_output_accuracy: 0.0029 - aux_output_accuracy: 0.0032
[0.05328081175684929,
 0.04541592299938202,
 0.12406466901302338,
 0.0029069767333567142,
 0.0032299740705639124]
  • 이전과 달리 값이 굉장히 많이 나오기 때문에 이를 파악하기 어려울 수 있는데, 그 내용은 생각보다 상당히 단순하다.
  • 손실 값은 loss: 0.0533, main_output_loss: 0.0454, aux_output_loss: 0.1241이 나왔다.
  • 여기서 loss만 신경 쓰면 된다. 위에서 우리는 main_output_loss와 aus_output_loss의 가중치를 0.9, 0.1로 부여하였는데, loss는 각 손실 값에 해당하는 가중치를 곱하여 합한 값이기 때문이다.

$$ 0.0533 = 0.9*0.0454 + 0.1*0.1241 $$

  • Deep model의 손실 값과 Wide & Deep Learning model의 손실 값을 동시에 반영하여, 총 손실 값을 계산하였으므로, Deep model이 Wide model과의 결합 없이도 우수한 성능을 보이는 것을 알 수 있다.
  • 주 출력층의 Accuracy는 0.0029, 보조 출력층의 Accuracy도 0.0032로 Deep model, Wide & Deep Learning model 모두 Accuracy가 괜찮게 나왔다. 이로 인해 Deep model 자체만으로도 우수한 성능을 보이는 것을 알 수 있다.

5.1. 손실 값과 정확도를 시각화해보자.

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)

  • 굉장히 많은 지표들이 추가되었지만, Early Stopping의 기준으로 사용한 val_loss를 본다면, patience가 30이었으므로, epochs 27에서 모델이 수렴하였다는 것을 알 수 있다.

 

[참고 자료]

 

 

 이번 포스트에서는 다중 출력 모델을 Wide & Deep Learning model에서의 규제 기법으로 사용해보았다. Deep model의 결괏값에 대한 평가가 모델 전체 평가에 반영되었으므로, Deep Model의 일반화가 잘 이루어진 모델이 만들어졌다고 할 수 있다. 

 지금까지 Wide & Deep Learning 모델을 기반으로 다중 입력, 다중 출력 모델을 만드는 방법과 이를 통해 Wide & Deep Learning Model을 더 잘 사용할 수 있도록 해보았다.

728x90
반응형
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
반응형
728x90
반응형

 꽤 많은 블로그나 책에서 머신러닝을 다룰 때, 회귀모형, 로지스틱 회귀모형을 꼭 다루고 넘어가는데, 이번 포스트에서는 우리가 통계학에서 흔히 다루는 회귀모형과 딥러닝이 대체 어떤 관계길래 다들 회귀모형부터 다루는지에 대해 알아보고자 한다.

 사실 우리는 이미 이전 포스트에서 회귀모형, 로지스틱 회귀모형을 만들어보았으며, 만약, 통계에 조금 익숙한 사람이라면, 분석된 결과나 그 과정을 보면서, 이게 회귀분석 아닌가? 하는 생각이 들었을지도 모른다(물론, Odd Ratio, $R^2$과 같은 익숙한 지표들이 그대로 등장하진 않았지만, 그것과 유사한 역할을 하는 지표를 이미 봤을 것이다).

 

 

회귀모형(Regression Model)

 통계에 익숙한 사람이라면, 통계의 꽃인 회귀 모형에 대해 이미 잘 알고 있을 것이다. 우리가 기존에 학습했던, "Tensorflow-1.4. 기초(5)-하이퍼 파라미터 튜닝", "Tensorflow-2.4. 타이타닉 생종자 분류 모델(3)-하이퍼 파라미터 튜닝"에서 만들었던 모델이 바로 회귀모형이다.

 

 

 

 

1. 통계학과 딥러닝의 회귀모형

 회귀모형에 대해 잘 모를 수 있으므로, 통계학에서의 회귀모형에 대해 아주 단순하게 설명해보도록 하겠다.

  • 회귀모형은 데이터 간에 어떠한 경향성이 있다는 생각에서 시작된다.
  • 회귀모형은 독립변수와 종속변수가 서로 인과 관계가 있다고 할 때, 독립변수가 변하면 종속변수가 변하게 된다.

회귀식: $ y = b_1x_1 + b_2x_2 + b_3x_3 + c $

  • 회귀모형은 데이터에 가장 적합한 계수($b_1, b_2, b_3, c$)를 구하는 것이 목적이다.
  • 회귀모형은 그 적합한 계수를 찾는 방법으로 평균 제곱 오차(MSE)를 사용한다.
    (실제값과 예측값의 편차 제곱의 평균, 참고: "딥러닝-5.1. 손실함수(2)-평균제곱오차(MSE)")

 회귀식은 퍼셉트론 공식과 아주 똑 닮았다("머신러닝-2.1. 퍼셉트론(2)-논리회로"). 그리고 딥러닝을 통해 우리는 각각의 파라미터(가중치와 편향)를 찾아낼 수 있다.

 독립변수(Dataset이자 상수)의 변화에 따른 종속변수(Label, 상수)의 변화를 가장 잘 설명할 수 있는 계수(weight과 bias)를 찾아내는 것은 회귀분석이며, 이 점이 다층 퍼셉트론을 이용하여, 데이터 자체를 가장 잘 설명할 수 있는 파라미터를 찾아내는 딥러닝(Deep Learning)과 같다고 할 수 있다.

1.1. 이 밖의 회귀모형의 특징

  • 회귀모형은 기본적으로 연속형 데이터를 기반으로 한다.
  • 독립변수, 종속변수가 모두 연속형 데이터여야 한다.
  • 연속형 데이터가 아닌 독립변수 존재 시, 이를 가변수(Dummy variable)로 만든다.

 

 

 

 

2. 데이터셋

 이번 학습에서는 R을 사용해본 사람이라면 아주 친숙한 데이터 중 하나인 자동차 연비 데이터(MPG)를 이용해서 회귀모형을 만들어보도록 하겠다.

# Import Module
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
from sklearn.model_selection import train_test_split

import pandas as pd
import numpy as np
>>> dataset_path = keras.utils.get_file("auto-mpg.data", "http://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data")
Downloading data from http://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data
32768/30286 [================================] - 0s 4us/step
  • tf.keras.utils.get_file()을 사용하면, 각종 데이터셋을 쉽게 가져올 수 있다.
  • 데이터셋을 가지고 오는 곳은 UCI 머신러닝 저장소로 인터넷이 안 되는 환경이라면 데이터를 다운로드할 수 없으니, 조심하자.
  • sklearn처럼 데이터셋을 Dictionary에 깔끔하게 저장하여, 데이터를 다운로드하면, 모든 데이터에 대한 정보가 있는 상태가 아니므로, 데이터에 대해 파악하기 위해선 UCI 머신러닝 저장소에서 데이터에 대해 검색하여, 데이터 정보를 찾아봐야 한다.
  • 해당 포스팅에서는 "Tensorflow 공식 홈페이지의 자동차 연비 예측하기: 회귀"를 참고하여 데이터셋을 준비하였다.
column_names = ['MPG','Cylinders','Displacement','Horsepower','Weight',
                'Acceleration', 'Model Year', 'Origin']
Rawdata = pd.read_csv(dataset_path, names=column_names, na_values = "?",
                      comment='\t', sep=" ", skipinitialspace=True)
Rawdata

 

 

 

 

3. 데이터 전처리

3.1. 결측 값 처리

>>> Rawdata.isna().sum()
MPG             0
Cylinders       0
Displacement    0
Horsepower      6
Weight          0
Acceleration    0
Model Year      0
Origin          0
dtype: int64

>>> print("전체 데이터에서 결측값 행의 비율:", Rawdata.isna().sum(1).sum()/len(Rawdata))
전체 데이터에서 결측값 행의 비율: 0.01507537688442211
  • 위 결과를 보니, Horsepower에서 결측 값이 6개 발생한 것을 알 수 있다.
  • 전체 데이터에서 결측 값 행의 비율을 보니 0.015로 매우 미미한 양이므로, 제거하도록 하자.
Rawdata.dropna(inplace = True)

 

3.2. 범주형 데이터의 원-핫 벡터화

  • 칼럼 Origin은 숫자로 표시되어 있지만, 실제론 문자인 범주형 데이터이므로, 원-핫 벡터화 해주자.
    (이는 회귀 모델에서의 가변수 처리와 매우 유사한 부분이라고 할 수 있다.)
# One-Hot Vector 만들기
def make_One_Hot(data_DF, column):
    
    target_column = data_DF.pop(column)

    for i in sorted(target_column.unique()):

        new_column = column + "_" + str(i)
        data_DF[new_column] = (target_column == i) * 1.0
  • 이전 참고 포스트에서 만들었던, 원-핫 벡터 코드보다 판다스의 성격을 잘 활용하여 만든 코드다
  • DataFrame.pop(column): DataFrame에서 선택된 column을 말 그대로 뽑아낸다. 그로 인해 기존 DataFrame에서 해당 column은 사라지게 된다.
  • (target_column == i) * 1.0: Boolearn의 성질을 사용한 것으로 target_column의 원소를 갖는 위치는 True로, 원소가 없는 곳은 False가 된다. 이에 1.0을 곱하여 int로 바꿔줬다. Python은 동적 언어이므로, 1.0을 곱해주어도 int형으로 변하게 된다.

 

3.3. 데이터셋 분리

  • train set과 test set은 7:3으로 분리하도록 하겠다.
  • validation set은 이 단계에서 뽑지 않고, 학습 과정(fit)에서 뽑도록 하겠다.
  • MPG 변수는 연비를 의미하며 종속변수에 해당하므로, 이를 Label로 사용하겠다.
# Dataset 쪼개기
label = Rawdata.pop("MPG").to_numpy()
dataset = Rawdata.values
X_train, X_test, y_train, y_test = train_test_split(dataset, label, test_size = 0.3)

 

3.4. 특성 스케일 조정

  • 해당 데이터 셋의 각 칼럼의 데이터는 다른 변수에서 나온 것이므로, 각각 변수별 기준으로 정규화해주어야 한다.
  • 표준 정규 분포를 사용하여 특성 스케일 조정을 하도록 하겠다.
  • 원-핫 벡터가 사용된 7:9까지의 열을 제외한 나머지 열에 대해 각각 특성 스케일 조정을 하겠다.
# 특성 스케일 조절
mean_point = X_train[:, :6].mean(axis=0)
std_point = X_train[:, :6].std(axis=0)

X_train[:, :6] = ((X_train[:, :6] - mean_point)/std_point)
X_test[:, :6] = ((X_test[:, :6] - mean_point)/std_point)
  • Numpy의 특징 중 하나인 Broadcasting 덕에 쉽게 정규화할 수 있다.

 

 

 

 

4. 모델 학습 및 평가

  • 모델은 은닉층이 1개 있는 단층 퍼셉트론(Sigle-later Perceptron)으로 만들어보겠다.
    (은닉층을 2개 이상 넣어 다층 퍼셉트론으로 만들어도 상관없다)
  • model은 Sequential 함수 안에 layer를 넣어주는 방법으로 만들어보겠다.
    (추천하지는 않는 방법이지만 이런 방법도 있다는 것을 보여주고자 사용해보았다)
  • 출력층의 활성화 함수는 선형(Linear)으로 설정하였다.
    (activation의 Default는 linear이므로, 따로 설정하지 않아도 된다)
  • 조기 종료(Early stop)를 콜백 함수로 주도록 하겠다.
# model 생성
model = keras.Sequential([
    keras.layers.Dense(60, activation = "relu"),
    keras.layers.Dense(1, activation = "linear")
])

# model compile 설정
opt = keras.optimizers.Adam(learning_rate=0.005)
model.compile(optimizer=opt, loss="mse", metrics = ["accuracy"])

# model 학습
early_stop = keras.callbacks.EarlyStopping(monitor='val_loss', patience=10, restore_best_weights=True)
model.fit(X_train, y_train, epochs=300, validation_split=0.2, callbacks=[early_stop])
Epoch 1/300
7/7 [==============================] - 2s 248ms/step - loss: 572.1581 - accuracy: 0.0000e+00 - val_loss: 497.8600 - val_accuracy: 0.0000e+00
Epoch 2/300
7/7 [==============================] - 0s 19ms/step - loss: 516.3237 - accuracy: 0.0000e+00 - val_loss: 443.4315 - val_accuracy: 0.0000e+00
Epoch 3/300
7/7 [==============================] - 0s 18ms/step - loss: 454.6065 - accuracy: 0.0000e+00 - val_loss: 385.6940 - val_accuracy: 0.0000e+00
Epoch 4/300
7/7 [==============================] - 0s 19ms/step - loss: 390.3198 - accuracy: 0.0000e+00 - val_loss: 321.7114 - val_accuracy: 0.0000e+00

...

Epoch 79/300
7/7 [==============================] - 0s 8ms/step - loss: 5.6932 - accuracy: 0.0000e+00 - val_loss: 7.2688 - val_accuracy: 0.0000e+00
Epoch 80/300
7/7 [==============================] - 0s 8ms/step - loss: 5.9354 - accuracy: 0.0000e+00 - val_loss: 7.3990 - val_accuracy: 0.0000e+00
Epoch 81/300
7/7 [==============================] - 0s 8ms/step - loss: 5.5025 - accuracy: 0.0000e+00 - val_loss: 7.3694 - val_accuracy: 0.0000e+00
Epoch 82/300
7/7 [==============================] - 0s 8ms/step - loss: 5.8313 - accuracy: 0.0000e+00 - val_loss: 7.2677 - val_accuracy: 0.0000e+00
<tensorflow.python.keras.callbacks.History at 0x1e00b62b1f0>
# model 평가
>>> model.evaluate(X_test, y_test)
4/4 [==============================] - 0s 2ms/step - loss: 10.2492 - accuracy: 0.0000e+00
[10.24919319152832, 0.0]

 

 

 

 

5. 정리

  • 출력층에 활성화 함수로 기본값인 선형 함수(Linear)가 사용되었다.
  • 활성화 함수로 선형 함수가 사용되는 경우, 단순하게 이전 층에서 출력된 값에 대하여 선형 함수의 계수 값만큼만 곱하므로, 은닉층에서의 값을 보존한 상태로 출력하게 된다.
  • 참고: "딥러닝-3.0. 활성화함수(1)-계단함수와 선형함수"
  • 손실 함수로 평균 제곱 오차(MSE)를 사용하였다.
  • 참고: "딥러닝-5.1. 손실함수(2)-평균제곱오차(MSE)"
  • 회귀모형은 주어진 데이터가 어떠한 경향성을 갖고 있다는 가정하에 변수들 사이의 경향성을 가장 잘 설명할 수 있는 계수들을 찾아내는 것이다.
  • 우리는 딥러닝 모델을 사용해서 Feature로 독립변수들을 넣었고, Label에 종속변수를 넣었다.
  • 우리는 딥러닝 모델을 이용해 Feature가 Label에 대한 어떠한 경향성, 즉 패턴을 갖고 있다고 보고, 그 패턴을 나타내는 파라미터(weight, bias)를 찾아냈다.
  • 회귀분석은 일반적으로 최소제곱법(OLS)을 통해 최적의 가중치를 찾아내지만, 우리는 최적의 가중치를 경사 하강법과 역전파를 통한 학습으로 찾아내었다.
  • 출력층의 활성화 함수를 Sigmoid 함수로 사용하여 이진 분류 하면, 로지스틱 회귀모형(Logistic Regression model)이 된다.

 

 

 

 지금까지 딥러닝(Deep Learning)을 사용하여, 회귀모델을 만들어보았다. 앞서 "딥러닝을 통해 이런 것도 되는구나!"하고 넘어갔던 부분들이 사실은 회귀모델이고, 회귀모델의 아이디어와 딥러닝의 아이디어의 유사한 부분에서 꽤 재미를 느꼈을 것이다.

 다음 포스트에서는 Keras의 장점인 함수형 API를 이용해 모델을 만들어, 얕은 신경망(Wide model)과 깊은 신경망(Deep model)을 합친 Wide & Deep Learning에 대해 학습해보도록 하겠다.

728x90
반응형
728x90
반응형

 이전 포스트에서는 TensorBoard의 Log Data가 쌓일 경로 설정과 TensorBoard 실행하기에 대해 알아보았다. 이번 포스트에서는 TensorBoard를 실제로 사용하는 방법에 대해 알아보도록 하겠다.

 

 

1. SCALARS

  • Tensorboard를 실행하면, 처음 뜨는 화면으로, 이전 포스트에서 생성하였던 Scalar 값들을 이용해서 손실 값과 정확도를 이용해 그래프를 그린 것이다.
  • 해당 그래프를 통해, 훈련 셋(Train set)과 검증 셋(Validation set)의 손실 값(Loss)과 정확도(Accuracy)의 추이를 볼 수 있다.
  • 이전에 학습과정을 확인했던 포스팅에서는 Matplotlib.pyplot을 사용해서, 그래프를 그렸다.

  • 이전 포스팅을 그대로 따라왔다면, 다음과 같은 화면이 뜰 것이다.

  • 그래프 아래에 빨간 부분 클릭 시, 그래프가 커지게 된다.
  • 위 그래프는 우리가 이전에 만들었던 matplotlib.pyplot으로 뽑았던 결과보다 자세한 결과를 보여준다. train_set과 validation_set이 다른 경향을 보여주는 부분을 크게 확대해서 보여주었다.
  • 그래프를 보면 연한 파란색과 빨간색 그래프가 희미하게 있는 것을 볼 수 있는데, 이 희미한 그래프는 실제 값이고, 이를 부드럽게 만든 것이 우리가 보고 있는 진한 파란색, 빨간색 그래프다. 이 부드러운 정도는 좌측의 "Smoothing" 바를 움직여서 조절할 수 있다.
  • 위 그래프에서 epoch_accuracy는 epoch별 accuracy의 변화를 보여준다. epoch_loss는 epoch별 loss의 변화를 보여준다.
  • 위 그래프를 보면, train set에 대한 학습 과정을 보여주는 파란색은 epoch가 지날수록 train set에 최적화되어 가므로, 지속적으로 증가하거나 감소하는 것을 볼 수 있으나, valiation set인 빨간색은 accuracy에서는 증가하다가 감소하는 부분이 생기고, loss에서는 accuracy보다 더 큰 폭으로 출렁이다가, epoch 11을 최솟값으로 지속적으로 증가하는 것을 볼 수 있다.
  • 해당 모델은 조기종료를 사용하였으며, 조기종료에서의 patience 파라미터를 10으로 주어, 최솟값이 등장한 이후 10만큼 추가 epoch를 실시해, 그 안에 보다 작은 최솟값이 등장하지 않는다면, 학습을 멈추게 하였다.

 

 

 

 

2. GPAPHS

  • 좌측 상단의 SCALARS 옆에 있는 GRAPHS를 클릭하면, 다음과 같은 화면이 뜬다.

  • 이는 노드와 엣지 등을 이용하여, 그래프로 학습이 일어난 과정을 시각화한 것이다.
  • 위 내용을 보면, 무언가 처음 보는 것이 엄청 많기 때문에 이해하기가 쉽지 않다.
  • 우리가 모델을 만들 때, 사용했었던 Sequential 쪽으로 마우스를 드래그하고, 휠을 스크롤하여, 큰 화면으로 만들어보면, sequential 우측 상단에 + 마크가 있는 것을 확인할 수 있다. 이를 더블 클릭해보자.

  • 이를 보면 우리가 모델을 만들 때 사용했던, Flatten, Hidden1, Hidden2, Hidden3, Output layer가 등장하는 것을 알 수 있다.
  • Hidden1, Hidden2, Hidden3는 모델을 생성할 때, name 파라미터를 이용해서 부여한 이름이다. 
  • 모서리가 둥근 직사각형을 클릭하면 그 안에 요약되어 있는 내용들을 볼 수 있다.
  • 이를 이용해서 모델의 각 부분에서 어떻게 학습이 이루어졌는지 확인할 수 있다.

 

 

 

 

3. TIME SERIES

  • TIME SERIES에서는 실시간으로 그래프의 변화를 볼 수 있다.

  • 먼저, Setting을 손봐서, 그래프 자동 Update 시간을 바꾸자.

  • Reload Period는 자동 Update 시간으로 TensorBoard로 볼 데이터의 크기 학습 시간 등을 고려하여 설정해야 한다. 최소 시간은 15초이므로, 15로 설정하도록 하겠다.
  • 모델은 조기 종료의 patience를 50으로 잡아, 학습이 오랫동안 이뤄지도록 하겠다.
  • 해당 학습 코드는 아래와 같다.
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras.layers import (Dense, BatchNormalization, Dropout, Flatten)
from tensorflow.keras.datasets.mnist import load_data

import numpy as np
import os
import datetime




# Dataset 준비
(train_images, train_labels), (test_images, test_labels)= load_data()

# 무작위로 샘플 추출
np.random.seed(1234)
index_list = np.arange(0, len(train_labels))
valid_index = np.random.choice(index_list, size = 5000, replace = False)

# 검증셋 추출
valid_images = train_images[valid_index]
valid_labels = train_labels[valid_index]

# 학습셋에서 검증셋 제외
train_index = set(index_list) - set(valid_index)
train_images = train_images[list(train_index)]
train_labels = train_labels[list(train_index)]

# min-max scaling
min_key = np.min(train_images)
max_key = np.max(train_images)

train_images = (train_images - min_key)/(max_key - min_key)
valid_images = (valid_images - min_key)/(max_key - min_key)
test_images = (test_images - min_key)/(max_key - min_key)



################################ Model ################################
model = keras.models.Sequential()
model.add(keras.layers.Flatten(input_shape=[28, 28], name="Flatten"))
model.add(Dense(300, activation="relu", name="Hidden1"))
model.add(Dense(200, activation="relu", name="Hidden2"))
model.add(Dense(100, activation="relu", name="Hidden3"))
model.add(Dense(10, activation="softmax", name="Output"))
#######################################################################



opt = keras.optimizers.Adam(learning_rate=0.005)
model.compile(optimizer = opt,
              loss = "sparse_categorical_crossentropy",
              metrics = ["accuracy"])
              
              
              

dir_name = "Learning_log"

def make_Tensorboard_dir(dir_name):
    root_logdir = os.path.join(os.curdir, dir_name)
    sub_dir_name = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
    return os.path.join(root_logdir, sub_dir_name)

# 콜백함수 - 텐서보드
TB_log_dir = make_Tensorboard_dir(dir_name)
TensorB = keras.callbacks.TensorBoard(log_dir = TB_log_dir)

# 콜백함수 - 조기 종료
early_stop = keras.callbacks.EarlyStopping(monitor="val_loss", min_delta=0, patience=50, restore_best_weights=True)


# 모델 학습
history = model.fit(train_images, train_labels,
                    epochs=100,
                    batch_size=32,
                    validation_data=(valid_images, valid_labels),
                    callbacks=[early_stop, TensorB])
  • 이를 실행하고 TensorBoard를 보도록 하자.

728x90
반응형

'Machine Learning > TensorBoard' 카테고리의 다른 글

Tensorboard - 텐서보드 시작하기  (0) 2021.02.18
728x90
반응형

지난 포스트에서 기계학습(Machine Learning)에 대해 간략하게 알아보았다.
이번 포스트에선 기계학습을 수월하게 할 수 있게 해주는 텐서플로우(Tensorflow)에 대해 알아보도록 하자.

 

 

텐서플로우(Tensorflow)란?

  • 텐서플로우는 구글에서 2015년에 공개한 기계학습 라이브러리로, 일반인들도 기계학습을 사용할 수 있을정도로 난이도가 낮고, 아주 강력한 성능을 가지고 있다.
  • 단, 텐서플로우는 딥러닝 알고리즘인 인공신경망이 한 번 학습되기 시작하면 신경망의 구조가 고정되어버리기 때문에 특정 프로젝트에 최적화하여 사용하는데 한계가 있다.
  • 최적화까지 감안하여 기계학습을 하기 위해선, 신경망의 구조까지 스스로 학습하며 변하는 페이스북 인공지능팀에서 개발한 파이토치(PyTorch)를 사용해야한다(텐서플로우와 파이토치의 성능차이는 상당히 큰 편이다).
    • 텐서플로우는 참고 자료가 더 많고, 사용자가 파이토치보다 텐서플로우가 많은 편이므로, 텐서플로우를 먼저 학습하고, 그 후에 파이토치를 학습할 예정이다.
  • GPU, CPU 2가지 버전이 존재하며, GPU는 tensorflow-gpu라는 패키지를 따로 다운로드 받아야한다.
    • GPU는 NVIDIA의 CUDA를 사용하므로, NVIDIA 그래픽 카드가 필수이다.
  • 본 포스트에서는 텐서플로우 Version 2.0.0으로 학습할 예정이다.
  • 텐서플로우2는 Keras를 기반으로 작동한다.

 

※ CPU와 GPU를 왜 구분해서 사용하는 것일까???

  • CPU와 GPU의 차이를 간단하게 설명하자면 다음과 같다.
  • CPU는 아~~~주 머리가 좋은 친구로, 전교 10등 안에 들어가는 수학 천재인 친구다. 수능에서 4점짜리 문제들도 쉽게 풀정도로 어려운 수학 문제도 큰 힘을 들이지 않고 풀 수 있는 친구지만, 당연히 숫자가 적고, 한 번에 할 수 있는 일이 많지가 않다.
  • GPU는 기본적인 수학 능력을 갖춘 평범한 친구들이다. 수능에서 2점짜리 문제 같이 간단한 수학 문제라면 손 쉽게 풀 수 있지만, 어려운 수학 문제를 푸는데는 한 세월이 걸린다. 숫자가 1,000명 정도로 엄청나게 많다.
  • 자, 이를 더 간추려 말해보면 CPU는 머리가 좋지만 숫자가 적고, GPU는 머리는 평범하지만 숫자가 많은 친구들이다.
  • Python이나 R은 기본적으로 CPU 연산을 하며, CPU 연산을 하다보니 아무리 어려운 문제라 할지라도 크게 힘들이지 않고 풀 수 있지만, 머신러닝에서는 이야기가 달라진다. 딥러닝에서 사용되는 가장 대표적인 알고리즘인 인공신경망은 기본적으로 행렬 연산을 통해 계산되는데, 이러한 행렬 연산은 더하기, 빼기, 곱하기, 나누기와 같은 기본적인 사칙연산을 수십 만번 실시한다고 생각하면 된다.
  • 자, 사칙연산 수십 만번을 머리가 엄청 좋은 한 명에게 시키는 것과 평범하지만 사칙연산은 충분히 해내는 천 명에게 시키는 것, 이 둘 중 무엇이 더 효율적일까?? 당연히 사칙연산을 충분히 해낼 수 있는 천 명에게 문제를 주는 것이 훨씬 빠르지 않겠는가.
  • 이처럼 기계학습의 특징은 어려운 연산도 물론 있지만, 이 어려운 연산은 CPU에게 맡기면 되고, 쉬운 연산은 GPU에게 맡기는 방식을 사용한다. 즉, 컴퓨터의 자원을 이분화시켜 사용하는 것이 기계학습의 특징이다.

 

 

 

텐서플로우(cpu) 설치 방법

  • 텐서플로우만 설치하는 것은 난이도가 상당히 낮은 편이다.
  • 텐서플로우는 버전 2.0.0을 설치할 것이다.
    1. PyCharm의 UI를 이용해서 설치하기
      File > Settings > +버튼 >  tensorflow 검색 및 선택 > Specify version 체크 > 드롭박스에서 버전 2.0.0으로 선택 > Install Package 클릭
    2. 터미널에서 설치하기(아나콘다 가상환경을 만든 경우 *추천)
      cmd 실행 > conda env list(가상환경 목록 확인) > conda activate 가상환경이름 > pip install tensorflow==2.0.0

 

 

 

텐서플로우-gpu 설치 방법

  • tensorflow-gpu 설치를 위해서는 몇 가지 사전 작업이 필요하다.
    1. NVIDIA 그래픽 카드
    2. CUDA Toolkit 10.0 Version 설치
    3. cuDNN 설치
    4. PATH 설정
  • tensorflow-gpu는 gpu를 이용해서 연산하므로, 컴퓨터 환경에 gpu 셋팅을 해줘야한다.
  • NVIDIA 그래픽 카드가 설치되어있다는 전재하에 설치 해보도록 하겠다.
  • CUDA Toolkit의 버전은 11까지 나왔지만, 아직 호환성에 문제가 있는지 제대로 설치가 되지 않는다. 또한, 우리는 텐서플로우 버전 2.0으로 작업할 예정이므로, 이에 맞는 CUDA Toolkit Version 10.0을 사용해보도록 하자.
  • 설치 환경은 Windows이다.

 

 

1. CUDA Toolkit을 설치해보자.

CUDA Toolkit 10.0 버전 싸이트로 들어가자

 

CUDA Toolkit 10.0 Archive

Select Target Platform Click on the green buttons that describe your target platform. Only supported platforms will be shown. Operating System Architecture Distribution Version Installer Type Do you want to cross-compile? Yes No Select Host Platform Click

developer.nvidia.com

 

내 환경에 맞는 버전으로 다운로드 받자

  • Windows 환경에서 사용할 예정이다.
  • Windows는 10을 사용하고 있다.
  • 오프라인 환경에서도 사용할 수 있도록 Local로 다운로드 받겠다.
    (인터넷이 되는 환경이라면, 시간이 오래걸리므로 exe(network)로 다운로드 받도록 하자.)

 

이제 설치를 해보자.

  • CUDA  설치는 크게 어렵지 않다.
  • 따로 경로나 설정에서 손을 볼 것은 없으므로, 그냥 쭉 진행하면 된다.

 

 

 

2. cuDNN을 설치해보자.

 

 

NVIDIA cuDNN

NVIDIA cuDNN The NVIDIA CUDA Deep Neural Network library (cuDNN) is a GPU-accelerated library of primitives for deep neural networks. cuDNN provides highly tuned implementations for standard routines such as forward and backward convolution, pooling, norma

developer.nvidia.com

  1. Download cuDNN을 클릭한다.
  2. cuDNN은 로그인을 해야만 설치 가능하므로, 아이디가 없다면, 아이디를 만들고 로그인하도록 하자.

 

 

CUDA와 내 환경에 맞는 버전으로 설치하자.

  • CUDA v10.0으로 설치하였으므로, cuDNN은 v7.6.5.로 다운로드 받겠다.
  • Windows 10 환경으로 설치하겠다.

 

 

  • cuDNN을 CUDA가 설치된 위치에 덮어씌우도록 하자.
    • 만약  CUDA를 설치할 때, 따로 경로를 손대지 않았다면 아래 경로에 덮어 씌우면 된다.
    • C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.0
    • 위 경로에 cuDNN 압축파일을 해제하여 나온 파일들을 덮어씌우자.

 

 

 

3. PATH를 설정해주자.

  • cmd에 들어가자.
  • 아래와 같게 타이핑 해주자
    • SET PATH=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.0\bin;%PATH%
    • SET PATH=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.0\extras\CUPTI\libx64;%PATH%
    • SET PATH=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.0\include;%PATH%
    • SET PATH=C:\tools\cuda\bin;%PATH%

 

 

 

위까지 작업을 마치면, tensorflow-gpu를 설치할 환경이 완료된 것이다. 만약 위 셋팅을 마쳤음에도 tensorflow-gpu가 설치되지 않는다면, Visual Studio를 설치해주거나 업데이트 해주도록 하자.

자 지금까지 텐서플로우를 사용할 수 있는 환경 셋팅을 모두 완료해보았다. 텐서플로우 설치는 pip install을 이용해서 설치하면 되며, 이에 대한 내용은 python 카테고리에서 패키지 설치법을 확인해보도록 하자.

다음 포스트에서는 텐서플로우를 쓰는법에 대해 차근차근 알아가보자.

728x90
반응형
728x90
반응형

기계학습(Machine Learning)이란?

사진 출처: 워너 브라더스 코리아

호아킨 피닉스 주연의 조커(Joker, 2019)란 영화를 본 적이 있는가?

이 영화에서 흥미로운 부분은 영화 밖 관객과 영화 안 관객이 조커에게 갖는 인식이 다르다는 것이다.

 훗날 조커가 될 아서 플렉은 정신 질환과 장애, 불안정한 일자리, 건강이 좋지 않은 홀어머니를 모시고 사는 편치 않은 삶을 살고 있지만, 다른 이들을 행복하게 만드는 사람이 되기 위해 노력하는 삶을 지내고 있다.

 지하철에서 만난 어린아이를 웃기기 위해 얼굴로 장난을 치거나, 웃긴 개그들을 정리해놓은 개그 노트를 만들고, 개그맨이라는 꿈을 향해 어떻게든 노력하며, 망상 속이긴 했지만 행복한 삶을 꿈꾸는 그런 사람이다.

 하지만, 사회는 아서 플렉에게 그리 호의적이지 않았고, 광대 분장을 하고 입간판을 들고 광고를 하는 그는 강도질을 당하거나, 건장한 백인 남성들에게 둘러싸여 위협을 받거나, 그의 잘못이 없음에도 불구하고 일방적으로 그를 해고해버리는 등, 아서 플렉을 둘러싼 사회는 그를 하루하루 꿈으로부터 밀어내고 있다.

 영화가 절정에 도달아 아서 플렉이 결국 조커로 각성하여, 추가 살인을 저지르고 폭동의 심볼이 되었을 때, 관객들은 사회의 부조리에 희생당하고 결국 사회에 폭발적인 분노를 표출한 조커에게 감정 이입하여 조커로부터 카타르시스를 느끼게 되며, 영화 안의 관객들은 조커라는 괴물에게 공포를 느끼게 된다.

 

 

 

 자, 이번엔 빅데이터 분석가의 관점에서 위 내용을 바라보도록 하자, 만약 살인범인 아서 플렉에 대해 영화 내부의 인물이, 그가 어째서 살인범이 되게 되었는지에 대해 분석해본다면 어떤 결과가 나올까?

 영화 안에 있는 인물들이 아서 플렉에 대해 얻을 수 있는 정보는 다음과 같다. "흡연자, 편모가정, 빈곤층, 정신 장애 보유, 신체 기형 보유, 일용직 노동자" 이러한 특징을 가진 사람들과 그렇지 않은 사람들을 비교해보니, "이런 특징을 가진 사람들이 그렇지 않은 사람보다 범죄를 저지를 확률이 높다! 그러니 이런 특성을 가지고 있는 사람들을 감시해야 한다!"라는 결론을 내릴 수 있다.

 영화 밖 관객인 당신은 위 결론에 대해 동의할 수 있는가? "아서 플렉을 괴물로 만든 것은, 그에게 친절하게 굴지 않은, 그를 둘러싼 사회지 않느냐!!"라는 생각을 하며, 반발을 할 수도 있을 것이다.

 우리가 접하는 대부분의 데이터는 어떠한 사건의 본질, 그 현상 자체에 대한 것이 아닌, 연구자의 의도, 관점, 생각, 가설이라는 주관이 섞여있는 상태에서 생성이 된다. 그러다 보니, 우리는 실제 현상으로부터 상당히 거리가 떨어져 있을지도 모르는 데이터를, 실체라고 오판할 수 있으며, 본질에 다가가고자 하는 시도인 분석이, 도리어 본질로부터 멀어지는 행동이 될 수도 있다.

 

 

 

어떻게 하면 위 문제를 해결할 수 있을 것인가?

 사람의 인식은, 어떠한 대상에 대하여, 자신의 지식, 경험 등을 기반으로, 대상을 분류하는 과정을 통해 이루어지며, 우리는 인식 능력이 좋은 사람을 "통찰력 있는 사람", "시야가 넓은 사람"이라고 종종 이야기한다.

 하지만, 사람이 가질 수 있는 지식, 경험은 매우 한정적이고, 그 양 역시 많지 않으며, 그 대상에 대해 받아들이는 정보 역시 온전하다고 할 수 없다. 장님에게 코끼리를 설명하라고 하면, 다리를 만진 장님은 코끼리에 대해서 "코끼리는 기둥이다!"라고 할 것이고, 코끼리의 꼬리를 만진 장님은 "코끼리는 밧줄이다!"라고 할 것이며, 코끼리의 코를 만진 장님은 "코끼리는 두꺼운 뱀이다!"라고 할 것인데, 눈이 보이는 우리 역시도 한눈에 보이지 않는 어떠한 대상에 대해, 심지어 눈에 보인다고 할지라도, 우리가 정의 내리고자 하는 어떠한 현상의 경계, 본질을 인식할 수 없다.

 예를 들어, 당신이 청년 실업의 본질을 알고 싶다면, 과연 그 청년 실업이라는 현상의 경계는 어디서부터 어디까지이겠는가? 그리고 당신이 비교적 정확한 경계를 찾아낸다고 할지라도, 그 경계가 불변하겠는가?

 

 

 

 자, 사람의 인식이 가진 한계가 위와 같다고 하면, 기계 즉, 컴퓨터에게 실제 현상에 관련된 것인지 아닌지는 모르겠지만 발생한 모든 데이터를 주고 그 데이터들을 분류해보라고 하면 어떨까?

 사람 한 명이 처리할 수 있는 데이터의 한계가 있고, 여럿이서 데이터를 공동으로 처리하는 경우, 의사소통의 문제 등으로 인해, 현상을 이해하는데 문제가 생길 수 있다면, 어마어마하게 좋은 컴퓨터에게 어마어마한 양의 데이터(빅 데이터)를 주고, 그 어마어마한 양의 데이터를 어떤 규칙에 따라 학습시키고, 데이터들을 분류해 나가다 보면, 조커와 같은 살인자 집단이 갖는 공통된 패턴을 찾아낼 수 있고, 보다 본질에 대한 명확한 이해를 할 수 있지 않을까?

 위 내용들을 단순화시켜 말하자면, 무엇인지 모르는 본질이 숨어있을 엄청나게 거대한 빅 데이터를, 컴퓨터에게 주고, 빅 데이터를 학습시켜서 컴퓨터가 그 안 속에 숨어있는 어떠한 패턴을 찾아내게 하는 것. 이 일련의 활동이 기계 학습(Machine Learning)이며, 우리는 데이터 자체에서 패턴을 찾아낼 수 있다.

 우리는 이 기계 학습을 통해서, 우리가 지금까지 알지 못했던 빅 데이터 속에 숨겨져 있는 어떠한 패턴, 이론, 변수 등을 찾아내어, 지금까지의 이론을 기반으로 하여 시작하는 연구에서, 실제 데이터를 통해서 이론을 찾아내는 연구로 현상에 대해 접근하는 방법을 바꿔서 다가갈 수 있다.

 이 것이 우리가 기계학습을 공부해야 하는 이유이며, 기계학습으로 해낼 수 있는 가능성이 바로 이것이라 말할 수 있다.

 

 

 

 자, 지금까지 기계 학습(Machine learning)이 무엇인지에 대해 이야기해보았다. 위키피디아나 책에서 나온 정의는 잘 와 닿지가 않아, 기계학습에 대한 필자의 생각을 정리해본 내용이다. 보다 자세한 내용은 위키피디아나 책을 찾아보길 바라며, 이제 천천히 기계학습에 대해 본격적으로 접근해보도록 하자.

 본 블로그에서 기계학습 부분은 파이썬을 이용해서 실습할 예정이므로, 다음 포스트에서는 파이썬 기계학습의 대명사인 텐서플로우(Tensorflow) 설치 방법에 대해 학습해보도록 하겠다.

728x90
반응형

+ Recent posts