간단한 DNN예제를 통해 텐서플로우 기본 흐름을 확인해본다.
1. 데이터 로딩
from tensorflow import keras
# keras에서 제공하는 데이터 중 보스턴 집값에 대한 데이터 로딩하기
(train_x, train_y),(test_x,test_y) = keras.datasets.boston_housing.load_data()
2. 전처리
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
x_train = scaler.fit_transform(train_x)
x_test = scaler.transform(test_x)
3. dataset 만들어주기
import tensorflow as tf
# train 데이터를 dataset 형식으로 만들고, train 데이터 개수만큼 섞기, 배치 개수 설정, 배치로 나눠지지 않은 나머지 데이터 처리 방식 설정 등을 해준다.
train_dataset = tf.data.Dataset.from_tensor_slices((x_train, train_y)).shuffle(x_train.shape[0]).batch(100, drop_remainder=True)
test_dataset = tf.data.Dataset.from_tensor_slices((x_test, test_y)).batch(N_BATCHS)
4. 모델 구현
# bottom layer ( 뒤에 오는 layer을 top layers 라고 함)
model = keras.Sequential() # 순서대로 레이어를 쌓는 모델 생성
# input layer
# model.add(layers.InputLayer((13,))) # x_train.shape이 (404, 13) 이므로 input은 다음과 같다.
# 즉, input : (feature수, )
# hidden layer
model.add(layers.Dense(units=64, activation="relu", input_shape=(13,)))# 다음과 같은 코드로 input layer를 생략할 수도 있다.
# model.add(layers.ReLU()) 위에 relu 처리를 다음과 같이 할 수도 있음
model.add(layers.Dense(units=32, activation="relu"))
model.add(layers.Dense(units=16, activation="relu"))
# 관례적으로 Dense layer는 unit 수로를 줄여나간다!!!!!!!!!!!!!!!!!!!!!!!!!
# output layer
model.add(layers.Dense(units=1)) # unit은 예측하려는 값의 개수로 지정
# activation 함수로는 회귀에서는 보통 다음 함수를 사용한다. None, sigmoid, tanh
model.summary() # 모델 구조 확인하기
keras.utils.plot_model(model, show_shapes=True) # 모델 구조
5. 컴파일하기
# optimizer 설정, 손실함수 설정, 평가지표 설정을 해준다.
model.compile(optimizer=keras.optimizers.Adam(learning_rate=0.001), loss="categorical_crossentropy", metrics=["accuracy"])
6. 학습하기
hist = model.fit(train_dataset, # 위에서 batch size는 따로 지정해 줘서 생략가능
epochs=100,
validation_data=test_dataset # dataset을 사용할때는 validation_split말고 validation_data 사용한다.
)
8. 확인하기
plt.subplot(1,2,1)
plt.plot(hist.epoch, hist.history["loss"], label="Train loss")
plt.plot(hist.epoch, hist.history["val_loss"], label="Validation loss")
plt.legend()
plt.subplot(1,2,2)
plt.plot(hist.epoch, hist.history["accuracy"], label="Train Accuracy")
plt.plot(hist.epoch, hist.history["val_accuracy"], label="Validation Accuracy")
plt.legend()
plt.tight_layout()
plt.show()
9. 평가하기
loss, acc = model.evaluate(test_dataset)
print(loss, acc)
Epoch(애폭) : 전체 데이터셋을 몇바퀴 돌릴지에 대한 값.
Step(스탭) : 가중치를 업데이트하는 횟수. 즉, 애폭을 배치로 나눈 값과 같다.
Batch(배치) : 전체 데이터셋을 쪼개는 단위.
image의 경우 0부터 255의 수로 이루어져 있다. 그러므로 MinMaxScaler처럼 해당 수들을 0~1 사이로 정규화시켜주기 위해 255로 나눠준다. 하지만 이때 타입이 uint8인 상태로 나누기를 실행할 시 정수로 나오게 된다. 그러므로 255로 나누기 전에 미리 float32타입으로 소수가 나올 수 있도록 처리해준다.
예제 )
X_train = train_image.astype("float32")/255.
X_test = test_image.astype("float32")/255.
예제 )
y_train = keras.utils.to_categorical(train_label, num_classes=10)
y_test = keras.utils.to_categorical(test_label, num_classes=10)
위 코드는 label값인 y_train과 y_test가 범위형일 경우의 전처리 방식이다. 해당 코드를 통해 one-hot encoding
이 이루어져서 y_train과 y_test의 shape이 다음과 같은 방식으로 바뀐다.
(60000,)
, (10000,))
===> ((60000, 10)
, (10000, 10))
60000개의 train데이터와 10000개의 test데이터가 존재하고, y(label)에 대한 범위가 10가지 있다는 사실을 확인할 수 있다.
모델 구현 방식으로 3가지 정도 존재한다.
# 모델 구현
def create_bigger_model():
model = keras.Sequential()
model.add(layers.Flatten(input_shape=(28,28)))
model.add(layers.Dense(256, activation="relu"))
model.add(layers.Dense(256, activation="relu"))
model.add(layers.Dense(128, activation="relu"))
model.add(layers.Dense(128, activation="relu"))
model.add(layers.Dense(10, activation="softmax", name="output_layer"))
model.compile(optimizer=optimizers.Adam(LEARNING_RATE), loss="categorical_crossentropy", metrics=["accuracy"])
return model
# 모델 생성
model = create_bigger_model()
layer나 unit이 많다면 더 복잡한 모델이 되어 과대적합(overfitting)이 발생할 수 있다. 반대로 layer나 unit이 적다면 단순한 모델이 되어 과속적합(underfitting)이 발생할 수 있다.
Epoch은 전체데이터를 얼마나 많이 돌리면서 학습시킬지 정하는 변수다. 그러므로 epoch가 너무 크면 과대적합(overfitting)이 발생할 수 있다. 반대로 epoch가 너무 작으면 과소적합(underfitting)이 발생할 수 있다.
Dropout 이란 layer에서 모든 unit(노드)이 학습되지 않고, 일부는 학습되지 않도록 하는 방법을 의미한다. Dropout은 overfitting의 원인인 co-adaptation 현상
을 감소/방지하는 효과가 있다.
※ co-adaptation 현상
이란? : 학습을 진행할 때 올바르게 구해진 가중치와 잘못 구해진 가중치가 있을 때, 전체가 업데이트되면서 오히려 올바르게 구해진 가중치까지 함께 업데이트되어 버리는 현상을 의미한다.
예제 )
def create_dropout_model(dropout_rate=0.5):
model = keras.Sequential()
model.add(layers.Flatten(input_shape=(28,28)))
model.add(layers.Dropout(dropout_rate)
model.add(layers.Dense(256, activation="relu"))
model.add(layers.Dropout(dropout_rate)
model.add(layers.Dense(256, activation="relu"))
model.add(layers.Dropout(dropout_rate)
model.add(layers.Dense(128, activation="relu"))
model.add(layers.Dropout(dropout_rate)
model.add(layers.Dense(128, activation="relu"))
model.add(layers.Dropout(dropout_rate)
model.add(layers.Dense(10, activation="softmax", name="output_layer"))
model.compile(optimizer=optimizers.Adam(LEARNING_RATE), loss="categorical_crossentropy", metrics=["accuracy"])
return model
dropout layer는 위 코드와 같이 dropout을 적용해줄 layer마다 앞에 추가로 넣어주는 형태로 사용한다. 또한 얼마나 dropout할지에 대한 dropout rate
값을 지정해준다.
!주의!
: dropout layer는 학습과정에서만 동작해야한다. tensorflow의 keras에서는 알아서 dropout layer가 학습과정에서만 동작하도록 설정되어 있지만, pytorch 같은 다른 라이브러리를 사용할 때는 해당 설정을 따로 해줘야 할 수도 있다.
A데이터셋과 B데이터셋이 같은 종류의 데이터셋인지 아닌지를 구분하기 위해서는 두 데이터셋의 분포를 비교한다. 분포가 비슷하면 같은 데이터셋일 확률이 높은 것이다. 이런 원리로 각 layer들의 입력분포를 비슷하게 만들어 계속해서 같은 종류의 데이터셋(정규 분포)을 입력으로 넣는 느낌을 내는 것이다. 이를 통해 성능을 높일 수 있다.
예제 )
def create_batchnormalizaion_model(dropout_rate=0.5):
model = keras.Sequential()
model.add(layers.Flatten(input_shape=(28,28)))
# 다음과 같은 형식으로 중간에 배치 정규화를 시켜준다.
model.add(layers.Dropout(dropout_rate)) # 1번
model.add(layers.Dense(256)) # 2번
model.add(layers.BatchNormalization()) # 3번 (배치 정규화)
model.add(layers.ReLU()) # 4번
model.add(layers.Dropout(dropout_rate))
model.add(layers.Dense(256))
model.add(layers.BatchNormalization())
model.add(layers.ReLU())
model.add(layers.Dropout(dropout_rate))
model.add(layers.Dense(128))
model.add(layers.BatchNormalization())
model.add(layers.ReLU())
model.add(layers.Dropout(dropout_rate))
model.add(layers.Dense(128))
model.add(layers.BatchNormalization())
model.add(layers.ReLU())
model.add(layers.Dropout(dropout_rate))
model.add(layers.Dense(10))
model.add(layers.BatchNormalization())
model.add(layers.Softmax())
model.compile(optimizer=optimizers.Adam(LEARNING_RATE), loss="categorical_crossentropy", metrics=["accuracy"])
return model
예제 )
# learning_scheduler 생성
lr_scheduler = optimizers.schedules.ExponentialDecay(
initial_learning_rate=0.001, # 첫 learning rate 설정
decay_steps=len(train_dataset), # 몇 스탭마다 learning rate 수정할 지. 현재코드는 1 epoch 당 업데이트를 하기 위해 train_dataset의 전체 스탭수를 업데이트 단위로 설정한 것이다.(1epoch=전체데이터셋수)
decay_rate=0.8, # learning rate 변화율
staircase=True # learing rate를 계단식(디지털식)으로 변화시킬건지, 이어지는식(아날로그식)으로 변화시킬건지 설정
)
# 생성한 learning_scheduler로 compile시키기
model.compile(optimizer=optimizers.Adam(learning_rate=lr_scheduler), loss="categorical_crossentropy", metrics=["accuracy"])
# callback 객체 생성
lr_cb = keras.callbacks.ReduceLROnPlateau(
monitro="val_loss", # 모니터링할 평가지표
factor=0.5, # learning rate 조정비율(디폴트:0.1)로 현재학습률*조정비율로 변경해준다
patience=3, # 에폭구간을 설정한 값으로 해당 epoch 동안 성능개선이 안되면 학습률을 변경한다
verbose=2, # 학습률이 변경될 때마다 로그를 남긴다.(0:로그안남김, 1:간단한로그, 2:조금더상세한로그)
)
# 학습시킬 때 미리 만들어 놓은 callback호출
hist = model.fit(train_dataset, epochs=N_EPOCHS, validation_data=test_dataset,
callbacks=[lr_cb])