Deep learning/이론(hands on machine learning)

5. 케라스로 다층 퍼셉트론 구현(2) - 시퀀셜 API 회귀, 함수형 API

jwjwvison 2021. 6. 2. 21:58

<시퀀셜 API를 사용하여 회귀용 다층 퍼셉트론 만들기>

 캘리포니아 주택 가격 데이터셋으로 바꾸어 회귀 신경망으로 이를 해결해보자.

from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

housing=fetch_california_housing()
X_train_full,X_test,y_train_full,y_test=train_test_split(housing.data,housing.target)
X_train,X_valid,y_train,y_valid=train_test_split(X_train_full,y_train_full)

scaler=StandardScaler()
X_train=scaler.fit_transform(X_train)
X_valid=scaler.transform(X_valid)
X_test=scaler.transform(X_test)

 분류에서 했던 것과 매우 비슷하지만 주된 차이점은 출력층이 활성화 함수가 없는 하나의 뉴런을 가진다는 것과 손실 함수로 평균 제곱 오차를 사용한다는 것이다. 이 데이터셋에는 잡음이 많기 때문에 과대적합을 막는 용도로 뉴런 수가 적은 은닉층 하나만 사용한다.

import tensorflow as tf
from tensorflow import keras

model=keras.models.Sequential([
                               keras.layers.Dense(30,activation='relu',input_shape=X_train.shape[1:]),
                               keras.layers.Dense(1)
])
model.compile(loss='mean_squared_error',optimizer='sgd')
history=model.fit(X_train,y_train,epochs=20,
                  validation_data=(X_valid,y_valid))
mes_test=model.evaluate(X_test,y_test)
X_new=X_test[:3]
y_pred=model.predict(X_new)

 

 Sequential 모델이 매우 널리 사용되지만 입력과 출력이 여러 개거나 더 복잡한 네트워크 토폴로지를 갖는 신경망을 만들어야 할 때가 있다. 이를 위해 케라스는 함수형(functional) API를 제공한다.

 

< 함수형 API를 사용해 복잡한 모델 만들기>

 순차적이지 않은 신경망의 한 예는 와이드 & 딥 (wide & deep)신경망 이다. 이 구조를 이용하면 신경망이 복잡한 패턴과 간단한 규칙을 모두 학습할 수 있다.

 

 이런 신경망을 만들어 캘리포니아 주택 문제를 해결해보자.

input_=keras.layers.Input(shape=X_train.shape[1:])
hidden1=keras.layers.Dense(30,activation='relu')(input_)
hidden2=keras.layers.Dense(30,activation='relu')(hidden1)
concat=keras.layers.Concatenate()([input_,hidden2])
output=keras.layers.Dense(1)(concat)
model=keras.Model(inputs=[input_],outputs=[output])

 케라스 모델을 만들고 나면 나머지는 이전과 동일하다. 모델을 컴파일한 다음 훈련, 평가, 예측을 수행할 수 있다.

keras.utils.plot_model(model, show_shapes=True)

 

 만약 일부 특성은 짧은 경로로 전달하고 다른 특성들은(중복될 수 있다) 깊은 경로로 전달하고 싶다면 여러 입력을 사용하면 된다. 예를 들어 5개 특성을 짧은 경로로 보내고 6개 특성은 깊은 경로로 보낸다고 가정해보자.

input_A=keras.layers.Input(shape=[5],name='wide_input')
input_B=keras.layers.Input(shape=[6],name='deep_input')
hidden1=keras.layers.Dense(30,activation='relu')(input_B)
hidden2=keras.layers.Dense(30,activation='relu')(hidden1)
concat=keras.layers.concatenate([input_A,hidden2])
output=keras.layers.Dense(1,name='output')(concat)
model=keras.Model(inputs=[input_A,input_B],outputs=[output])

 이렇게 모델이 복잡해지면 적어도 가장 중요한 층에는 이름을 붙이는 것이 좋다. fit() 메서드를 호출할 때 하나의 입력 행력을 전달하는 것이 아니라 입력마다 하나씩 행렬의 튜플(X_train_A,X_train_B)를 전달해야 한다. X_valid에도 동일하게 적용된다. evaluate()나 predict()를 호출할 때 X_test와 X_new에도 동일하다.

 

model.compile(loss='mse',optimizer=keras.optimizers.SGD(lr=1e-3))

X_train_A,X_train_B=X_train[:,:5],X_train[:,2:]
X_valid_A,X_valid_B=X_valid[:,:5],X_valid[:,2:]
X_test_A,X_test_B=X_test[:,:5],X_test[:,2:]
X_new_A,X_new_B=X_test_A[:3],X_test_B[:3]

history=model.fit((X_train_A,X_train_B),y_train,epochs=20,
                  validation_data=((X_valid_A,X_valid_B),y_valid))
mse_test=model.evaluate((X_test_A,X_test_B),y_test)
y_pred=model.predict((X_new_A,X_new_B))

 

 여러 개의 출력이 필요한 경우는 많다.

 

 보조 출력을 추가하는 것은 적절한 층에 연결하고 모델의 출력 리스트에 추가하면 된다. 예를 들면 다음 코드는 그림 10-16의 네트워크를 만든다.

 각 출력은 자신만의 손실 함수가 필요하다. 하나의 손실을 전달하면 케라스는 모든 출력의 손실 함수가 동일하다고 가정한다. 기본적으로 케라스는 나열된 손실을 모두 더하여 최종 손실을 구해 훈련에 사용한다. 보조 출력보다 주 출력에 더 관심이 많다면(보조 출력은 규제로만 사용되므로), 주 출력의 손실에 더 많은 가중치를 부여해야 한다. 다행히 모델을 컴파일할 때 손실 가중치를 지정할 수 있다.

model1.compile(loss=['mse','mse'],loss_weights=[0.9,0.1],optimizer='sgd')

 

 이제 모델을 훈련할 때 각 출력에 대한 레이블을 제공해야 한다. 여기에서는 주 출력과 보조 출력이 같은 것을 예측해야 하므로 동일한 레이블을 사용한다. 따라서 y_train 대신에 (y_train,y_train)을 전달한다(y_valid와  y_test 도 동일하다).

history=model1.fit(
    [X_train_A,X_train_B],[y_train,y_train],epochs=20,
    validation_data=([X_valid_A,X_valid_B],[y_valid,y_valid])
)

 

 모델을 평가하면 케라스는 개별 손실과 함께 총 손실을 반환한다.

total_loss,main_loss,aux_loss=model.evaluate(
    [X_test_A,X_test_B],[y_test,y_test]
)

 

 비슷하게 predict() 메서드는 각 출력에 대한 예측을 반환한다.

y_pred_main,y_pred_aux=model.predict([X_new_A,X_new_B])