Dynamic Routings Between Capsules(CapsNet) - 2
저번 포스트에서는 CNN의 단점, 그중에서도 max pooling의 단점에 대해 자세히 알아보았습니다. 이러한 단점을 극복하기 위해 캡스넷(CapsNet)이 나왔으며 다이나믹 루팅(Dynamic routing)이라는 풀링의 단점을 보완한 방법이 있다고 설명드렸습니다. 이번 포스트에서는 캡스넷(CapsNet)의 네트워크 구조와 다이나믹 루팅(Dynamic routing)의 작동 방식에 대해 자세히 알아보도록 하겠습니다.
CapsNet architecture
캡스넷 네트워크 구조는 위 그림과 같습니다. 숫자로 정리해보면 아래 과정을 거치게 됩니다.
- Input : 28 x 28 x 1 (우리가 잘 알고 있는 MNIST 데이터)
- Conv1 kernel : 9 x 9 x 256, stride 1 + ReLU
- Output : 20 x 20 x 256
- Primary Caps : 9 x 9 x (32 x 8), stride 2 + ReLU(?)
- Digit Caps : dynamic routing
처음 입력으로 들어오는 28 x 28 x 1의 MNIST digit이 들어옵니다. 그 다음까지는 우리가 알고있는 기존 CNN 구조와 똑같습니다. 9 x 9짜리 256개의 필터를 가진 커널을 거쳐 20 x 20짜리 256갤의 피쳐 맵을 만듭니다. 그 이후에 PrimaryCaps를 형성하는 과정에 들어가게 되는데, 이 부분이 굉장히 헷갈립니다. 이름은 PrimaryCaps지만 우리가 원래 알고있던 콘볼루션 필터를 거치는 작업을 거칩니다. 20 x 20짜리 256개의 피쳐맵에 9 x 9짜리 32 x 8개의 피쳐맵을 만드는 콘볼루션 필터를 거치면 PrimaryCaps가 나오게 됩니다.
그렇게 나온 벡터를 위 그림처럼 reshape을 하게 됩니다. 즉, 캡슐 하나 당 8개의 property를 가질 수 있게(property의 설명이 기억이 안나신다면 이전 포스트를 참고하시면 됩니다) 콘볼루션 필터를 거친 6 x 6 x (32 x 8) 피쳐맵을 (6 x 6 x 32) x 8로 reshape을 시켜주게 됩니다. 이렇게 되면 총 6 x 6 x 32 = 1152개의 각각 8개의 property를 가지는 첫 번째 캡슐들이 생성됩니다. 이해가 잘 안되시는 분들을 위해 텐서플로우로 캡스넷을 구현 한 깃허브 페이지 중에서 가장 별이 많은 코드를 가져왔습니다.
# version 1
'''
Args:
input : [batch_size, 9, 9, 256]
num_outputs: the number of capsule in this layer. 논문에선 32
vec_len: integer, the length of the output vector of a capsule. 논문에선 8
kernel size : 9 x 9
stride : 2
'''
capsules = []
for i in range(vec_len):
# each capsule i: [batch_size, 6, 6, 32]
with tf.variable_scope('ConvUnit_' + str(i)):
caps_i = tf.contrib.layers.conv2d(input, num_outputs,
kernel_size, stride=[1,2,2,1],
padding="VALID", activation_fn=tf.nn.relu)
caps_i = tf.reshape(caps_i, shape=(cfg.batch_size, -1, 1, 1))
capsules.append(caps_i)
assert capsules[0].get_shape() == [cfg.batch_size, 1152, 1, 1]
capsules = tf.concat(capsules, axis=2)
위 코드는 코드 작성자가 제일 처음 만들었던 코드입니다. 사실 처음 논문 그림을 봤다면 저렇게 for문을 걸어서 여러번 콘볼루션 필터를 돌리는게 맞습니다. 하지만 잘 생각해보면 굳이 그렇게 할 필요 없이 한 번에 (32 x 8)개의 피쳐 맵을 생성하고 reshape을 하는게 계산 복잡도가 더 낮습니다. 논문을 다 읽고 그런 생각이 들었었는데 오랜만에 다시 저 깃허브 페이지에 들어갔더니 코드가 아래처럼 좀 더 효율적인 계산복잡도를 가지게 수정되었습니다.
# version 2
capsules = tf.contrib.layers.conv2d(input, num_outputs * vec_len,
kernel_size, stride, padding="VALID",
activation_fn=tf.nn.relu)
출처 : https://github.com/naturomics/CapsNet-Tensorflow/blob/master/capsLayer.py
논문 그림 중 PrimaryCaps라고 써진 직육면체(?) 하나를 가져온다 생각하면 위 그림의 왼쪽 그림처럼 생각하시면 편합니다. 저 초록색 벡터가 하나의 캡슐이고 총 8개의 property를 가지게 됩니다. 즉, (36 x 32 = 1152)개의 캡슐이 한 캡슐 당 8개의 element(property)를 가지게 됩니다. 그리고 이 캡슐들이 다이나믹 루팅(dynamic routing) 과정을 통해 상위 레벨의 캡슐과 이어집니다.
최종적으로는 총 10개의 캡슐이 output으로 나옵니다. MNIST데이터셋의 클래스가 0부터 9까지로 10개이기 때문이겠죠? 각 캡슐은 16개의 원소로 이루어진 벡터이며 원소는 전부 entity(여기서는 digit)의 property를 나타냅니다. 이 property들이 과연 무엇을 뜻하는지는 실험 결과 설명 부분에서 말씀드리겠습니다.
Dynamic routing
이제 이전 포스트 그리고 이 포스트에서 계속 이야기해온 다이나믹 루팅(dynamic routing)에 관하여 설명드리도록 하겠습니다.
논문에서 다이나믹 루팅 알고리즘을 설명한 그림입니다. 이 논문에서는 총 2개의 캡슐 레이어가 있기 때문에 위 설명 과정에서 PrimaryCaps가 layer l이 되고 DigitCaps가 layer l+1이 된다고 생각하시면 될 것 같습니다. 그리고 PrimaryCaps는 총 1152개가 있으며 DigitCaps는 10개입니다. 즉, i가 1부터 1152까지, j가 1부터 10까지 있습니다.
다이나믹 루팅은 몇 번의 이터레이션을 거칠지 하이퍼 파라미터를 지정해줄 수 있습니다. 먼저 PrimaryCaps와 DigitCaps를 이어주는 값 ${ b }_{ ij }$을 0으로 설정을 합니다.
그리고 이 ${b}_{ij}$를 softmax에 통과시켜 coupling coefficient를 만듭니다.
\[C_{ ij }=softmax({ b }_{ ij })=\frac { { b }_{ ij } }{ \sum _{ k }^{ }{ { b }_{ ik } } }\]위 식이 coupling coefficient ${C}_{ij}$를 만드는 식입니다. 이렇게 소프트맥스를 거쳐주면 모든 coupling coefficient의 합이 1이 되게 만들 수 있으며 이 값은 캡슐 i와 캡슐 j의 연결 강도가 됩니다. 그 이후에는 PrimaryCaps의 property를 8개에서 16개로 바꿔주는 작업을 거칩니다.
\[{\hat { u }_{ j|i } } ={ W }_{ ij }{ u }_{ i }\] \[{ s }_{ j }=\sum _{ i }^{ }{ { c }_{ ij }{ \hat { u } }_{ j|i } }\]- L2 norm : probability of entity exists
- Elements : properties of entity
이렇게 DigitCaps까지 완료가 되었다면, 마지막으로 ${b}_{ij}$를 업데이트 하는 과정을 거치게 됩니다.
\[{ a }_{ ij }={ v }_{ j }\cdot { \hat { u } }_{ j|i },\quad { b }_{ ij }\leftarrow { b }_{ ij }+{ \hat { u } }_{ j|i }\cdot { v }_{ j }\]이번 포스트에서는 캡스넷의 네트워크 구조와 다이나믹 루팅에 대하여 자세히 알아보았습니다. 다음 포스트에서는 캡스넷에서 사용되는 margin loss와 digit reconstruction을 통한 정규화 그리고 실험 결과들에 대해 살펴보겠습니다.
Reference
- Sabour, Sara, Nicholas Frosst, and Geoffrey E. Hinton. “Dynamic routing between capsules.” Advances in Neural Information Processing Systems. 2017.