Computer Vision/Deep learning

23. 케라스를 사용해 ResNet-34 CNN 구현

jwjwvison 2021. 6. 13. 13:54

 먼저 ResidualUnit 층을 만든다.

class ResidualUnit(keras.layers.Layer):    #잔차 유닛
  def __init__ (self,filters,strides=1,activation='relu',**kwargs):
    super(). __init__(**kwargs)
    self.activation=keras.activations.get(activation)
    self.main_layers=[
                      keras.layers.Conv2D(filters,3,strides=strides,
                                          padding='same',use_bias=False),
                      keras.layers.BatchNormalization(),
                      self.activation,
                      keras.layers.Conv2D(filters,3,strides=1,
                                          padding='same',use_bias=False),
                      keras.layers.BatchNormalization(),
                      self.activation,
                      keras.layers.Conv2D(filters,3,strides=1,
                                          padding='same',use_bias=False),
                      keras.layers.BatchNormalization()
    ]
    self.skip_layers=[]
    if strides>1:                #strides가 2이면 잔차유닛의 출력에 바로 더해줄수 없기 때문에 다음 과정을 거친다
      self.skip_layers=[
                        keras.layers.Conv2D(filters,1,strides=strides,
                              padding='same',use_bias=False),
                        keras.layers.BatchNormalization()]

  def call(self,inputs):
    z=inputs
    for layer in self.main_layers:
      z=layer(z)
    skip_z=inputs
    for layer in self.skip_layers:
      skip_z=layer(skip_z)
    return self.activation(z+skip_z)

 

 이 코드는 다음 그림을 구현한 것이다.

 

 이 네트워크는 연속되어 길게 연결된 층이기 때문에 Sequential 클래스를 사용해 ResNet-34 모델을 만들 수 있다. (ResidualUnit 클래스를 준비해놓았으니 잔차 유닛을 하나의 층처럼 취급할 수 있다).

model= keras.models.Sequential()
model.add(keras.layers.Conv2D(64,7,strides=2,input_shape=[224,224,3],padding='same',use_bias=False))
model.add(keras.layers.BatchNormalization())
model.add(keras.layers.Activation('relu'))
model.add(keras.layers.MaxPool2D(pool_size=3,strides=2,padding='same'))
prev_filters=64
for filters in [64] * 3 + [128] * 4 + [256] * 6 + [512] * 3:
  strides=1 if filters==prev_filters else 2
  model.add(ResidualUnit(filters,strides=strides))
  prev_filters=filters

model.add(keras.layers.GlobalAvgPool2D())
model.add(keras.layers.Flatten())
model.add(keras.layers.Dense(10,activation='softmax'))

 이 코드에서 조금 복잡한 부분은 모델에 ResidualUnit 층을 더하는 반복 루프이다. 처음 3개 RU는 64개의 필터를 가지고 그다음 4개 RU는 128개의 필터를 가지는 식이다. 필터 개수가 이전 RU와 동일할 경우는 스트라이드를 1로 설정한다. 아니면 스트라이드를 2로 설정한다. 그다음 ResidualUnit을 더하고 마지막에 prev_filters를 업데이트 한다.

 

 일반적으로 GoogLeNet이나 ResNet 같은 표준 모델을 직접 구현할 필요가 없다. keras.application 패키지에 준비되어 있는 사전훈련된 모델을 코드 한 줄로 불러올 수 있다. 예를 들어 다음 코드로 이미지넷 데이터셋에서 사전훈련된 ResNet-50 모델을 로드할 수 있다.

model=keras.applications.resnet50.ResNet50(weights='imagenet')

 

 이 모델을 사용하려면 이미지가 적절한 크기인지 확인해야 한다. ResNet-50 모델은 224x224 픽셀 크기의 이미지를 기대한다(모델이 다르면 299x299와 같이 기대하는 크기가 다를 수 있다). 텐서플로의 tf.image.resize() 함수로 앞서 적제한 이미지의 크기를 바꿔보자.

images_resized=tf.image.resize(images,[224,224])

 

 사전훈련된 모델은 이미지가 적절한 방식으로 전처리되었다고 가정한다. 경우에 따라 0에서 1사이 또는 -1에서 1 사이의 입력을 기대한다. 이를 위해 모델마다 이미지를 전처리해주는 preprocess_input() 함수를 제공한다. 이 함수는 픽셀값이 0에서 255 사이라고 가정한다. 따라서 (앞에서 0에서 1사이로 바꾸었기 때문에) images_resized에 255를 곱해야 한다.

inputs=keras.applications.resnet50.preprocess_input(images_resized * 255)

 

 이제 사전훈련된 모델을 사용해 예측을 수행할 수 있다.

y_proba=model.predict(inputs)

 출력 Y_proba는 행이 하나의 이미지이고 열이 하나의 클래스(여기에서는 1000개의 클래스가 있다)인 행렬이다.

 

 최상위 K개의 예측을 클래스 이름과 예측 클래스의 추정 확률을 출력하려면 decode_predictions() 함수를 사용한다. 각 이미지에 대해 최상위 K개의 예측을 담은 리스트를 반환한다. 각 예측을 클래스 아아디, 이름, 확륭을 포함한 튜플이다.

top_k=keras.applications.resnet50.decode_predictions(y_proba,top=3)
for image_index in range(len(images)):
  print('이미지 #{}'.format(image_index))
  for class_id,name,y_proba in top_k[image_index]:
    print(' {} - {:12s} {:.2f}%'.format(class_id,name,y_proba * 100))
  print()

 

'Computer Vision > Deep learning' 카테고리의 다른 글

25. 분류와 위치 추정  (0) 2021.06.15
24. 사전훈련된 모델을 사용한 전이 학습  (0) 2021.06.13
22. ResNet, Xception  (0) 2021.06.12
21. LeNet-5, AlexNet, GoogleNet  (0) 2021.06.12
20. CNN 구조  (0) 2021.06.12