CapsNet (Dynamic Routing between Capsules) 논문 정리

j___의 블로그·2022년 7월 4일
0

  • CNN 아키텍쳐의 한계를 지적하며 등장한 Neural Network 1세대 연구자인 Hinton 교수의 논문임.

기존 CNN의 한계

위의 이미지는 기존 CNN입니다. 여기서 Maxpooling은 이미지의 여러 공간을 줄여나가면서 Convolution filter가 한번에 볼 수 있는 사이즈를 키웁니다.

위처럼 FPS에서 보는 시야각이 넓어진다는 좋은 비유는 참 좋은 것 같습니다. 즉 Maxpooling과 같은 풀링은 필터가 더 넓은 범위를 볼 수 있게 합니다. 그래서 CNN의 초반의 레이어들은 좁은 범위의 세부적인 정보들을 처리하지만, 후반레이어에 갈 수록 넓은 범위를 처리할 수 있게 됩니다.

위의 이미지가 그 예시입니다. 처음 레이어는 선들, 색들과 같이 작은 범위의 정보들을 처리하지만, 후반부에 갈수록 더 넓은 글로벌한 정보들을 처리할 수 있게 됩니다.

Equivariance vs. Invariance

보통의 CNN에서 Maxpooling은 Invariance를 유지하기 적합하기 때문에, 사용된다고 알려져왔습니다. 스케일이 다르건, 방향이 뒤집히건 인풋을 넣었을 때, 동일한 결과를 뽑아낸다는 것이지요. 하지만 힌튼교수는 CNN을 사용하면서 Invariance가 아닌 Equivariance를 유지해야 한다고 이야기 합니다.

CNN은 각 피처를 줄여나가기 때문에, 통상적으로 스케일이 다른 값을 넣어도 동일한 이미지로 유지하도록 만듭니다. 그러나, 스케일링이 달라지면, 피처맵도 스케일링에 따라서 달라져야 한다고 생각합니다.

인간의 계층적 인식 방법

인간의 시야의 경우, 단층적으로 이것을 인식하는 것이 아니라, 다층적으로 인식합니다. 하나의 사진을 보고도, 해당 사진에 다양한 특징들을 고려해서 이미지를 인식하는 반면, CNN의 Maxpooling의 경우, 이미지의 뷰를 키워가면서 데이터를 보는 것에 불과하기 때문입니다. 그래서 저자들은 Capsule을 통해 이미지 내에 다양한 요소를 캡슐로 고려하고, 고려된 캡슐에서 이미지를 인식시키고자 합니다.

논문 구현

첫번째 레이어

Conv1 has 256, 9 × 9 convolution kernels with a stride of 1 and ReLU activation

conv1 = layers.Conv2D(
  filters=256, 
  kernel_size=9, 
  strides=1, 
  padding='valid', 
  activation='relu', 
  name='conv1'
)(x)
  • This layer converts pixel intensities to the activities of local feature detectors that are then used as inputs to the primary capsules

두번째 레이어

그림처럼 20 x 256짜리 컨볼루션 아웃풋을 통해 Primary Caps를 생성한다.

def squash(vectors, axis=-1):
    """
    The non-linear activation used in Capsule. It drives the length of a large vector to near 1 and small vector to 0
    :param vectors: some vectors to be squashed, N-dim tensor
    :param axis: the axis to squash
    :return: a Tensor with same shape as input vectors
    """
    s_squared_norm = K.sum(K.square(vectors), axis, keepdims=True)
    scale = s_squared_norm / (1 + s_squared_norm) / K.sqrt(s_squared_norm + K.epsilon())
    return scale * vectors
    
def PrimaryCap(inputs, dim_capsule, n_channels, kernel_size, strides, padding):
    """
    Apply Conv2D `n_channels` times and concatenate all capsules
    :param inputs: 4D tensor, shape=[None, width, height, channels]
    :param dim_capsule: the dim of the output vector of capsule
    :param n_channels: the number of types of capsules
    :return: output tensor, shape=[None, num_capsule, dim_capsule]
    """
    output = layers.Conv2D(filters=dim_capsule*n_channels, kernel_size=kernel_size, strides=strides, padding=padding,
                           name='primarycap_conv2d')(inputs)
    outputs = layers.Reshape(target_shape=[-1, dim_capsule], name='primarycap_reshape')(output)
    return layers.Lambda(squash, name='primarycap_squash')(outputs)

PrimaryCaps를 생성하는 코드입니다. PrimaryCaps는 그림처럼 6x8짜리 레이어가 32개 존재합니다.
여기서는 reshape을 통해 (6,6,256) 사이즈 피처 맵을 (6632, 8, 1) 로 변환합니다.

세번째 레이어


이후 캡슐 라우팅은 위의 알고리즘을 통해 진행됩니다.

def call(self, inputs, training=None):
        inputs_expand = K.expand_dims(inputs, 1)

        inputs_tiled = K.tile(inputs_expand, [1, self.num_capsule, 1, 1])
        inputs_hat = K.map_fn(lambda x: K.batch_dot(x, self.W, [2, 3]), elems=inputs_tiled)

        # Begin: Routing algorithm ---------------------------------------------------------------------#
        b = tf.zeros(shape=[K.shape(inputs_hat)[0], self.num_capsule, self.input_num_capsule])

        assert self.routings > 0, 'The routings should be > 0.'
        for i in range(self.routings):
            c = tf.nn.softmax(b, dim=1)
            outputs = squash(K.batch_dot(c, inputs_hat, [2, 2]))  # [None, 10, 16]

            if i < self.routings - 1:
                b += K.batch_dot(outputs, inputs_hat, [2, 3])
        # End: Routing algorithm -----------------------------------------------------------------------#

        return outputs

이를 구현한 방법은 다음과 같습니다.


Reference

  1. https://blog.naver.com/PostView.naver?blogId=qbxlvnf11&logNo=222105994766
  2. https://jayhey.github.io/deep%20learning/2017/11/28/CapsNet_1/
  3. https://jayhey.github.io/deep%20learning/2017/11/28/CapsNet_2/
  4. https://github.com/XifengGuo/CapsNet-Keras
profile
💧 Constant dropping wears away a stone. 🪨

0개의 댓글