내가 ResNet18을 구현하려는 motivation:
Text detection을 위해 Mask RCNN에 주입하기 위해 앞단에 512 by 512의 feature map을 출력으로 내보내는 모델이 필요함.
배경지식
ResNet Architecture
Skip connection
(1) Cifar10 data로 데이터 로딩 및 증강하기..
이번 포스팅의 주제가 아니므로 접어두겠음.
##### preprocess
#Import Libraries
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import tensorflow as tf
import keras
from tensorflow.keras import datasets,models,layers
# Adding TF Cifar10 Data ..
from keras.datasets import cifar10
(X_train, Y_train), (X_test, Y_test) = cifar10.load_data()
# Drawing sample .
plt.imshow(X_train[42])
# Normalize the data.
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')
X_train /= 255.0
X_test /= 255.0
from sklearn.model_selection import train_test_split
X_train, X_val, Y_train, Y_val = train_test_split(X_train, Y_train, \
test_size = 0.2,shuffle = True)
from sklearn.preprocessing import OneHotEncoder
encoder = OneHotEncoder()
encoder.fit(Y_train)
Y_train = encoder.transform(Y_train).toarray()
Y_test = encoder.transform(Y_test).toarray()
Y_val = encoder.transform(Y_val).toarray()
##### data augmentation
from keras.preprocessing.image import ImageDataGenerator
aug = ImageDataGenerator(horizontal_flip=True, width_shift_range=0.05,
height_shift_range=0.05)
aug.fit(X_train)
(2) 모델 정의하는 부분
"""
ResNet-18
Reference:
[1] K. He et al. Deep Residual Learning for Image Recognition. CVPR, 2016
[2] K. He, X. Zhang, S. Ren, and J. Sun. Delving deep into rectifiers:
Surpassing human-level performance on imagenet classification. In
ICCV, 2015.
"""
from keras.callbacks import EarlyStopping
from keras.layers import Dense, Conv2D, MaxPool2D, Flatten, GlobalAveragePooling2D, \
BatchNormalization, Layer, Add
from keras.models import Sequential
from keras.models import Model
import tensorflow as tf
class ResnetBlock(Model):
"""
A standard resnet block.
"""
def __init__(self, channels: int, down_sample=False):
"""
channels: same as number of convolution kernels
"""
super().__init__()
self.__channels = channels
self.__down_sample = down_sample
self.__strides = [2, 1] if down_sample else [1, 1]
KERNEL_SIZE = (3, 3)
# use He initialization, instead of Xavier
# (a.k.a 'glorot_uniform' in Keras), as suggested in [2]
INIT_SCHEME = "he_normal"
self.conv_1 = Conv2D(self.__channels, strides=self.__strides[0],
kernel_size=KERNEL_SIZE, padding="same",
kernel_initializer=INIT_SCHEME)
self.bn_1 = BatchNormalization()
self.conv_2 = Conv2D(self.__channels, strides=self.__strides[1],
kernel_size=KERNEL_SIZE, padding="same",
kernel_initializer=INIT_SCHEME)
self.bn_2 = BatchNormalization()
self.merge = Add()
if self.__down_sample:
# perform down sampling using stride of 2, according to [1].
self.res_conv = Conv2D(
self.__channels, strides=2, kernel_size=(1, 1),
kernel_initializer=INIT_SCHEME, padding="same")
self.res_bn = BatchNormalization()
def call(self, inputs):
res = inputs
x = self.conv_1(inputs)
x = self.bn_1(x)
x = tf.nn.relu(x)
x = self.conv_2(x)
x = self.bn_2(x)
if self.__down_sample:
res = self.res_conv(res)
res = self.res_bn(res)
# if not perform down sample, then add a shortcut directly
x = self.merge([x, res])
out = tf.nn.relu(x)
return out
class ResNet18(Model):
def __init__(self, num_classes, **kwargs):
"""
num_classes: number of classes in specific classification task.
"""
super().__init__(**kwargs)
self.conv_1 = Conv2D(64, (7, 7), strides=2,
padding="same", kernel_initializer="he_normal")
self.init_bn = BatchNormalization()
self.pool_2 = MaxPool2D(pool_size=(2, 2), strides=2, padding="same")
self.res_1_1 = ResnetBlock(64)
self.res_1_2 = ResnetBlock(64)
self.res_2_1 = ResnetBlock(128, down_sample=True)
self.res_2_2 = ResnetBlock(128)
self.res_3_1 = ResnetBlock(256, down_sample=True)
self.res_3_2 = ResnetBlock(256)
self.res_4_1 = ResnetBlock(512, down_sample=True)
self.res_4_2 = ResnetBlock(512)
self.avg_pool = GlobalAveragePooling2D()
self.flat = Flatten()
self.fc = Dense(num_classes, activation="softmax")
def call(self, inputs):
out = self.conv_1(inputs)
out = self.init_bn(out)
out = tf.nn.relu(out)
out = self.pool_2(out)
for res_block in [self.res_1_1, self.res_1_2, self.res_2_1, self.res_2_2, self.res_3_1, self.res_3_2, self.res_4_1, self.res_4_2]:
out = res_block(out)
out = self.avg_pool(out)
out = self.flat(out)
out = self.fc(out)
return out
(3) 정의한 모델 호출하기
호출하기 위한 파라미터에 대한 note
(1) ResNet18(10)에서 10으로 주어진 것은 class 내부에 __init__ constructor에 정의된 num_classes임. →
마지막 레이어 self.fc의 노드 개수로 활용됨.
(2) 또한 model.build() 메서드가 가능한 이유는 keras.models.Model을 상속하기 때문임. 입력으로 사용할 이미지의 차원을 input_shape=(None, w, h, c) 파라미터에 적절히 제공해주면 됨.
model = ResNet18(10)
model.build(input_shape = (None,32,32,3))
#use categorical_crossentropy since the label is one-hot encoded
from keras.optimizers import SGD
# opt = SGD(learning_rate=0.1,momentum=0.9,decay = 1e-04) #parameters suggested by He [1]
model.compile(optimizer = "adam",loss='categorical_crossentropy', metrics=["accuracy"])
# model.summary()
모델 훈련, 시각화, 추론은 접어두기에 넣겠음.
(4) 모델 훈련:
from keras.callbacks import EarlyStopping
es = EarlyStopping(patience= 8, restore_best_weights=True, monitor="val_acc")
#I did not use cross validation, so the validate performance is not accurate.
STEPS = len(X_train) / 256
history = model.fit(aug.flow(X_train,Y_train,batch_size = 256),
steps_per_epoch=STEPS, batch_size = 256, epochs=50,
validation_data=(X_train, Y_train),callbacks=[es])
(5) 훈련 과정 시각화:
def plotmodelhistory(history):
fig, axs = plt.subplots(1,2,figsize=(15,5))
# summarize history for accuracy
axs[0].plot(history.history['accuracy'])
axs[0].plot(history.history['val_accuracy'])
axs[0].set_title('Model Accuracy')
axs[0].set_ylabel('Accuracy')
axs[0].set_xlabel('Epoch')
axs[0].legend(['train', 'validate'], loc='upper left')
# summarize history for loss
axs[1].plot(history.history['loss'])
axs[1].plot(history.history['val_loss'])
axs[1].set_title('Model Loss')
axs[1].set_ylabel('Loss')
axs[1].set_xlabel('Epoch')
axs[1].legend(['train', 'validate'], loc='upper left')
plt.show()
# list all data in history
print(history.history.keys())
plotmodelhistory(history)
(6) 모델을 사용하여 추론하기 (예측):
## Evaluation
ModelLoss, ModelAccuracy = model.evaluate(X_train, Y_train)
print('Model Loss is {}'.format(ModelLoss))
print('Model Accuracy is {}'.format(ModelAccuracy))
# sub = pd.read_csv("../input/cifar-10/trainLabels.csv")
# Y_pred = np.argmax(model.predict(X_test),axis = 1)
# #convert to string label
# labels = ['Airplane', 'Automobile', 'Bird', 'Cat', 'Deer', 'Dog', 'Frog', 'Horse', 'Ship', 'Truck']
# str_pred = [labels[i] for i in Y_pred]
# sub["label"] = pd.DataFrame(str_pred)
Reference
[1] Implementing ResNet-18 Using Keras | Kaggleresnet18-tensorflow/resnet_18.py at master · acoadmarmon/resnet18-tensorflow (github.com)
0. PyTorch Quickstart (0) | 2021.06.07 |
---|---|
aiconnnect (0) | 2021.06.07 |
sigmoid function (0) | 2021.06.03 |
ARIMA (0) | 2021.05.31 |
Weight Regularization: L1 Norm, L2 Norm (0) | 2021.05.28 |