반응형

towardsdatascience.com의 [기사] 내용임


Complete Guide to Advanced CNNs in Tensorflow 2


    복잡한 구조로 더 깊은 모델을 생성하고 모델을 더 향상시키는 다른 레이어에 대한 학습

    글쓴이가 처음 만든 ConvNet 모델은 개와 고양이 모델이었고 이 모델은 배치 정규화와 max-pooling 레이어의 혼합과 오버피팅에 대응하기 위하 dropout도 있는 Conv2d 레이어를 갖는 순차적(Sequential) 모델의 기본적인 구조였다. 이 모든 것에 Dense 레이어로 이어지는 flatten 레이어가 뒤따른다. 이 레이어들은 'add'를 사용하여 모델에 추가했다. 이 모델은 충분히 잘 동작했고 충분히 좋은 정확도를 보였다. 그래서 글쓴이는 유명한 MNIST 데이터셋에 동일한 구조를 사용하여 테스트했다. 해야 할 것은 dense layer의 손실을 binary cross-entropy에서 categorical cross-entropy로 바꾸고 CNN을 마스터했다고 느꼈다. 하지만, 그렇지 않았다. Conv2d 레이어의 다른 타입으로 sparsely connected architecture를 만들고 오버피팅ㅇ과 더 많은 것에 대한 다른 기술을 적용하는 것이 더 많이 있다.

sparse - from wiki
sparse network는 해당 네터워크안에서 가능한 최대 연결의 수도바 더 적은 연결을 갖는다.(반대는 dense or complete network). Sparse network는 social/computer 네트워크처럼 실제 네트워크의 연구로 먼저 시뮬레이션 된 상대적으로 새로운 분야이다.

Tables of Contents

  1. Advanced Conv2D layers - 발전된 Conv2D 레이어
    • Depthwise Separable Convolution - 깊이방향 분할가능한 합성곱
    • Dilated Convolution - 확장된 합성곱
  2. Counter Overfitting - 오버피팅 대응
    • Spatial Dropout - 공간적 드롭아웃
    • Gaussian Dropout - 가우시안 드롭아웃
    • Activity Regularization - 액티비티 규칙화
  3. Complex Architectures - 복잡한 구조
    • Sparsely Connected Architectures - 희소연결 구조
    • Skip Connection
  4. Conclusion


Advanced Conv2D layers

Depthwise Separable Convolutions

    더 효과적이고 더 적은 메모리를 사용하며 더 적게 연산한다. 그리고 몇몇 경우에서는 더 나은 결과를 보이기까지 한다. 이런 특성때문에 모델이 CPU와 RAM이 제한되는 Edge/IoT 디바이스에 배포되어져야 할 때 일반적으로 사용된다. 이것은 일반적인 합성곱 레이어 프로세스를 두개의 프로세스로 나눈다. 즉, depthwise 합성곱과 pointwise 합성곱이다. Depthwise Convolution에서 커널(kernel)은 한번에 하나의 채널에 대해 반복한다. Pointwise convolution은 채널의 수를 늘리기 위해 1X1 커널을 사용한다. 이 방법으로 필요한 곱샘의 총 수가 줄어들고 그것은 네트워크를 더 빠르게 만든다. 이 방법에 대한 자세한 내용은 다음 [기사]를 참고하면 된다.



[이미지 소스]

tensorflow.keras.layers.SeparableConv2D(32, (3, 3), padding="same")

Depthwise convolution, Depthwise separable convolution, Pointwise convolution : [참고]

Dilated Convolution

    확장된 합성곱(Dilated Convolution)은 일반적인 convolution 레이어와 depthwise separable convolution레이어에서 구현될 수 있다. 이는 갭(gap)을 갖는 일반적인 합성곱 연산이다. 더 큰 수용장(receptive field), 효과적인 계산과 더 적은 메모리 소모를 제공하는 것과 함께 해상도와 데이터의 순서 또한 보존한다. 따라서 일반적으로 모델의 성능을 개선한다.



[이미지 소스 - [링크]]

시스템적 확장은 해상도 또는 대상범위의 손실 없이 수용장의 기하급수적인 확장을 지원한다.

(a) F1은 1-확장된 합성곱에 의해 F0으로부터 생성되었다. F1내 각 요소는 $3 \times 3$의 수용장을 갖는다.

(b) F2은 2-확장된 합성곱에 의해 F1으로부터 생성되었다. F2내 각 요소는 $7 \times 7$의 수용장을 갖는다.

(c) F3은 4-확장된 합성곱에 의해 F2으로부터 생성되었다. F3내 각 요소는 $15 \times 15$의 수용장을 갖는다.

각 레이어와 연관된 파라미터의 수는 동일하다. 수용장은 기하급수적으로 증가하지만 파라미터의 수는 선형적으로 증가한다.


#In Normal Conv2D
tf.keras.layers.Conv2D(32, (3, 3), padding='same', dilation_rate=(2, 2))

#In Seperable Conv2D
tf.keras.layers.SeparableConv2D(no_of_filters, (3, 3), padding='same', dilation_rate=(2, 2))


Counter Overfitting

Spatial Dropout

    일반적인 드롭아웃(dropout)에서, 몇몇 임의로 선택된 뉴런은 훈련동안 무시된다. 디들 뉴런은 순방향에서는 기여하지 않고 가중치도 역방향 동안 갱신되지 않는다. 이는 네트워크가 다수의 독립된 내부적인 표현(representation)을 학습하도록 하고 훈련데이터에서 오퍼피팅의 가능성을 줄인다. Spatial dropout에서는 뉴런을 빼는(drop) 대신, 전체 특성맵을 뺀다. [Spatial 문서를 위한 케라스 문서]에서는 아래와 같이 기술하고 있다.

만약 특성맵에서 인접한 픽셀들이 강하게 연관되어 있다면(일반적으로 초기 합성곱 레이어에서의 경우) 규칙적인 드롭아웃은 활성화(activation)을 정규화 하지 않고 반대로 효과적인 학습률이 감소될 것이다. 이런 경우, Spatial dropout은 특성맵간 독립을 촉진하는데 도움이 되고 대신 사용되어야 한다.

tf.keras.layers.SpatialDropout2D(0.5)

Gaussian Dropout

    가우시안 드롭아웃은 드롭아웃과 가우시안 노이즈의 결합이다. 이는 일부 뉴럴을 빼는 것과 함께 1-중심 가우시안 노이즈 또한 적용한다는 것을 의미한다. 일반적인 드롭아웃처럼, 비율(rate)인자를 갖는다. [문서]에서는 아래와 같이 기술하고 있다.

Float, drop 가능성(드올바웃과 같이). 곱하는 노이즈(multiplicative noise)는 표준편차 sqrt $(rate \div (1 - rate))$을 갖을 것이다.

    좀 더 자세하 내용은 [링크]을 참조하자.

tf.keras.layers.GaussianDropout(0.5)

Activity Regularization

    네트워크를 더 일반화하기 위해 정규화는 네트워크를 약간 변화시킨다. 이는 뉴럴 네트워크가 희소 특성(sparse feature) 또는 모델이 보지못한 데이터에서 더 잘 수행할 수 있도록 만드는 원시 관측의 내부 표현(internal representations of raw observations)을 학습하는 것을 부추긴다. 여기에는 세가지 형태의 정규화 기술이 지원된다.

  • l1 : Acitvity가 절대값의 합으로 계산된다.
  • l2 : Activity가 제곱된 값의 합으로 계산된다.
  • l1_l2 : Activity가 절대값의 합과 제곱된 값의 합으로 계산된다.



L1과 L2 정규화식. $\lambda$는 정규화 파라미터임.

    가중치 지표 값(the value of weight metrics) 'regularization term'의 추가로 감소하고 이는 오버피팅을 감소시키는 더 간단한 모델로 이어진다. 더 자세한 정보는 [링크]를 참고하자.


#L1 regularization
tf.keras.layers.ActivityRegularization(l1=0.001)

#L2 regularizaton
tf.keras.layers.ActivityRegularization(l2=0.001)

#L1_L2 regularization
tf.keras.layers.ActivityRegularization(l1=0.001, l2=0.001)


Complex Architectures

Sparsely Connected Architectures

    Densely connected와 Sparsely connected의 두가지 형태의 구조(architecture)가 있다.


    이는 [Inception]네트워크로 대중화되었다. 이들 구조에 필요한 것은 무엇일까? 단지 레이어를 추가하여 더 깊은 네트워크가 깊어질수록 결과가 좋지 않을까? 전혀 그렇지 않다. 더큰 모델은 모델이 특시 더 작은 데이터셋에서 오버피팅 되는 경향이 있고 또한 훈련을 위해 필요한 컴퓨팅 파워의 총량이 증가한다. 때때로 훈련 오류마저 더 나빠진다. Sparsely connected 구조는 모델의 깊이(depth)와 너비(width)를 증가시키지만 필요한 컴퓨팅 파워를 초과하지 않는다. 또한 합성곱을 위한 다른 크기의 커널을 사용할 수 있다. 이는 테스트 객체가 다른 사이즈일때 모델에 도움이 된다.

    일반적으로, densely connected 구조를 만들기 위해서는
tf.keras.layers.add
를 사용한다. 또한 모델을 시각화하기 위해 tf.keras.utils.plot_model사용할 수 있다.


from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, add, Input
from tensorflow.keras.utils import plot_model

model = Sequential()
model.add(Conv2D(64, (3, 3), activation = 'relu', padding='same', input_shape=(128, 128, 3)))
model.add(Conv2D(64, (3, 3), activation = 'relu', padding='same'))
model.add(Conv2D(128, (3, 3), activation = 'relu', padding='same'))
plot_model(model)


    Sparsely connected 구조를 만들기 위해 단지 몇몇 레이어를 다수의 레이어에 할당하고 이것들을 합치기 위해(join) tf.keras.layers.concatenate 를 사용한다.


from tensorflow.keras.models import Model
from tensorflow.keras.layers import Conv2D, Input, concatenate
from tensorflow.keras.utils import plot_model

input_flow = Input(shape=(128, 128, 3))
x = Conv2D(64, (3, 3), activation='relu', padding='same', input_shape=(128, 128, 3))(input_flow)
one = Conv2D(64, (3, 3), activation='relu', padding='same')(x)
two = Conv2D(64, (5, 5), activation='relu', padding='same')(x)
three = Conv2D(64, (7, 7), activation='relu', padding='same')(x)
x = concatenate([one, two, three])
x = Conv2D(128, (3, 3), activation = 'relu', padding='same')(x)
model = Model(inputs=input_flow, outputs=x)
plot_model(model)



Skip Connections

    이것은 [ResNet50] 네트워크로 대중화되었다. Skip connection을 묶는 주요 개념은 [Deep Residual Learning for Image Recognition] 논문에서처럼 Microsoft Research에 의해 아래와 같이 주어진 문제를 해결하기 위한 것이었다.

더 깊은 네트워크가 모이는 것을 시작할 수 있을 때, 악화되는 문제가 나타났다: 네트워크 깊이가 증가하는 것으로 정화도는 포화되고(놀랍지 않을 수 있다.) 빠르게 악화된다. 예상과 다르게, 그같은 악화(degradation)은 오버피팅에 의한 것이 아니며, 적절한 deep 모델에 더 많은 레이어를 추가하는 것은 높은 훈련 에러로 연결된다...


[이미지 소스]

    이것의 이점은 더 쉽게 가중치를 위한 좋은 값을 추정할 수 있고 모델이 더 일반화되는 것이다.

    만약 sparsely connected 구조를 어떻게 만드는지 이해했다면, 이것이 어떻게 동작하는지 쉽게 추측할 수 있지만, 이번 경우에는 합치기 위해(join) tf.keras.layers.Add를 사용한다.


from tensorflow.keras.models import Model
from tensorflow.keras.layers import Conv2D, Input, Add
from tensorflow.keras.utils import plot_model

input_flow = Input(shape=(128, 128, 3))
x = Conv2D(64, (3, 3), activation='relu', padding='same', input_shape=(128, 128, 3))(input_flow)
one = Conv2D(64, (3, 3), activation='relu', padding='same')(x)
two = Conv2D(64, (5, 5), activation='relu', padding='same')(one)
three = Conv2D(64, (7, 7), activation='relu', padding='same')(two)
x = Add()([x, three])
x = Conv2D(128, (3, 3), activation = 'relu', padding='same')(x)
model = Model(inputs=input_flow, outputs=x)
plot_model(model)




Conclusion

    이들 기술을 사용하는 것은 모델을 개선하겠지만, 항상 그렇지는 않다. 이는 일반적인 Conv2D 레이어를 가진 단순한 densely connected 구조보다 잘못 동작할수도 있다. 따라서 어떤 전락이 최고의 결과인지 시도해봐야 한다.

검토 목적으로, 위의 개념을 사용한 모델을 아래와 같이 만들었다. 아래 모델은 단지 검토 목적이기 때문에 이 모델이 더 나은 결과를 보여준다는 보장은 없다.


from tensorflow.keras.models import Model
from tensorflow.keras.layers import SeparableConv2D
from tensorflow.keras.layers import MaxPooling2D
from tensorflow.keras.layers import BatchNormalization
from tensorflow.keras.layers import SpatialDropout2D, GaussianDropout, Dropout, ActivityRegularization
from tensorflow.keras.layers import Flatten, Dense
from tensorflow.keras.layers import Input
from tensorflow.keras.layers import Add, concatenate
from tensorflow.keras.utils import plot_model

input_shape = (128, 128, 3) # provide input shape
classes = 10 # replace classes with the number of classes
input_flow = Input(shape=input_shape) 
x = SeparableConv2D(32, (3, 3), padding='same', activation='relu')(input_flow)
x = SeparableConv2D(32, (3, 3), padding='same', activation='relu')(x)
x = BatchNormalization()(x)
x = MaxPooling2D((2, 2), padding='same')(x)
x = SeparableConv2D(64, (3, 3), padding='same', activation='relu')(x)
one = SeparableConv2D(64, (3, 3), padding='same', activation='relu', dilation_rate=(2, 2))(x)
one = SpatialDropout2D(0.25)(one)
one = SeparableConv2D(64, (3, 3), padding='same', activation='relu', dilation_rate=(2, 2))(one)
two = SeparableConv2D(64, (5, 5), padding='same', activation='relu', dilation_rate=(2, 2))(x)
two = SpatialDropout2D(0.25)(two)
two = SeparableConv2D(64, (5, 5), padding='same', activation='relu', dilation_rate=(2, 2))(two)
three = SeparableConv2D(64, (7, 7), padding='same', activation='relu', dilation_rate=(2, 2))(x)
three = SpatialDropout2D(0.25)(three)
three = SeparableConv2D(64, (7, 7), padding='same', activation='relu', dilation_rate=(2, 2))(three)
x = concatenate([one, two, three])
x = ActivityRegularization(l1=0.001)(x)
x = SeparableConv2D(128, (3, 3), padding='same', activation='relu')(x)
x = BatchNormalization()(x)
x = MaxPooling2D((2, 2), padding='same')(x)
one = SeparableConv2D(128, (3, 3), padding='same', activation='relu', dilation_rate=(2, 2))(x)
one = GaussianDropout(0.25)(one)
one = SeparableConv2D(128, (3, 3), padding='same', activation='relu', dilation_rate=(2, 2))(one)
two = SeparableConv2D(128, (5, 5), padding='same', activation='relu', dilation_rate=(2, 2))(x)
two = GaussianDropout(0.25)(two)
two = SeparableConv2D(128, (5, 5), padding='same', activation='relu', dilation_rate=(2, 2))(two)
three = SeparableConv2D(128, (7, 7), padding='same', activation='relu', dilation_rate=(2, 2))(x)
three = GaussianDropout(0.25)(three)
three = SeparableConv2D(128, (7, 7), padding='same', activation='relu', dilation_rate=(2, 2))(three)
concat = concatenate([one, two, three], axis=3)
concat = ActivityRegularization(l2=0.001)(concat)
concat = SeparableConv2D(128, (3, 3), padding='same', activation='relu')(concat)
x = Add()([x, concat])
x = Flatten()(x)
x = Dense(128, activation='relu')(x)
x = Dense(classes, activation='softmax')(x) 
model = Model(inputs=input_flow, outputs=x)
plot_model(model, show_shapes=True)
반응형

+ Recent posts