cs [논문 리뷰] Siamese Neural Networks for One-shot Image Recognition
본문 바로가기
  • 매일 한걸음씩
  • 매일 한걸음씩
개발/CV(Computer Vision)

[논문 리뷰] Siamese Neural Networks for One-shot Image Recognition

by 시몬쯔 2021. 5. 27.
728x90
반응형

회사에서 모델을 만들기 위해 고민하다가 클래스당 이미지 수가 적어 어떻게 분류할 지 검색해보다가 가장 클래식(?)한 방법인 샴 네트워크를 사용해보는 게 어떨까 싶어 논문을 이 기회에 한 번 리뷰해보고자 한다.

Abstract

보통의 이미지 분류 문제에서는 뉴럴 네트워크를 이용해서 이미지로부터 feature를 뽑아내어 예측하는 방식인데, 이는 계산량이 클뿐만 아니라 데이터가 부족할 때 해결하기가 어렵다. 이러한 경우에 사용되는 것이 "One-shot learning"인데, 이 분야는 새로운 클래스의 예시가 하나 주어졌을 때 예측하는 것이 가능하다.

이 논문에서는 샴 뉴럴 네트워크를 학습하는 방법에 대해 다루는데, 말그대로 쌍둥이처럼 닮은 네트워크 구조를 가진다. 인풋간의 유사도를 rank하는 방법으로 기존 클래스의 새로운 데이터뿐만 아니라 새로운 클래스의 데이터로부터 특징을 잘 뽑아낼 수 있다. Convolution을 사용하며 one-shot 분류 문제에 대해 SOTA수준의 성능을 보여준다...!



먼저, 어떻게 이 아이디어가 나오게 됐는지 알아보자.

인간은 새로운 패턴을 인식하는데에 어려움을 크게 느끼지 않는다. 특히, 자극이 주어졌을 때 인간은 빠르게 컨셉을 이해하고 이러한 컨셉을 기반으로 미래에 인식할 때 차이를 인식할 수 있다.

머신러닝은 계속해서 다양한 응용분야에서 SOTA의 성능을 보여주고 있는데, 이러한 알고리즘들은 보통 supervised 방식이므로 unlabeled data가 주어졌을 때는 종종 잘 작동하지 않는다.

이에 이 논문에서는 supervised data가 적게 주어졌을 때, 재학습하지 않고 이전에 보지 않은 익숙하지 않은 클래스를 일반화하는 것에 초점을 맞춘다.

특히 흥미로운 task중 하나는 오직 하나의 예시만 본 상황에서 분류를 하는 것이다. 이를 One-shot Learning이라 부른다. 💢 이는 Zero-shot learning과는 별개의 task인데, zero-shot learning은 어떠한 예시도 보지 않은 상황에서 예측을 하는 task이기 때문이다.


Approach


일반적으로, 우리는 supervised metric-based approach로 image representation을 학습한다.
(ex. CNN과 같은 네트워크에서 말단 벡터를 image feature로 쓰는 경우)
그리고나서, 재학습없이 이 representation을 one-shot learning에 사용한다.

이 논문의 실험에서는, 글자 인식에 초점을 맞춰서 실험을 했는데, 다른 모달리티에서도 사용이 가능하다고 한다.
이 글자 도메인에서는 큰 Siamese convolutional neural networks를 사용했는데, 이는 세 가지 관점에서 유용하다.

🌟🌟

a) 유용한 이미지 특유의 특징들을 학습할 수 있다. 이를 통해 unknown class 분포에 대해서도 예측이 가능하다.

b) 일반적인 최적화 방법론을 사용하여 쉽게 학습이 가능하다. (pairs 기반)

c) domain specific 지식에 의존하지 않는다.



One-shot 이미지 분류 모델을 개발하기 위해서는, 첫번째로 이미지 쌍의 클래스 identity를 구분할 수 있는 neural network을 학습해야 한다. (일반적인 이미지 인식의 verification task)

저자들은 이 verification에 잘 작동하는 네트워크가 one-shot 분류를 잘 일반화할 것이라는 가설을 세웠는데, verification 모델은 input 쌍이 같은 class인지 다른 class인지를 뜻하는 확률에 따라 학습을 한다.

👉🏻 그러므로 이 모델은 새로운 이미지를 평가하는 데에 사용될 수 있는데, 새로운 클래스당 정확히 하나의 쌍에 대해서다. Verification 네트워크에서 가장 높은 점수의 쌍은 one-shot task에 가장 높은 확률값을 부여받게 된다.
이 Verification model에 의해 학습된 features가 충분하다면 (같은 쌍이라고 하거나, 다른 쌍이라고 하기에) 이 features는 다른 알파벳에 대해서도 충분히 구분이 가능해진다.


Deep Siamese Networks for Image Verification


Siamese nets 구조는 처음 1990년도 초기에 소개되었는데, 이는 signature verification을 image matching problem으로 풀기 위함이였다. Siamese neural network은 쌍둥이 네트워크 구조로 되어있는데 이는 두 개의 서로다른 input을 받아들이고 상단에 energy function에 의해 합쳐진다. 이 energy function은 highest level feature representation사이의 몇개의 metric을 계산한다.

쌍둥이 네트워크간의 파라미터들은 묶여진 형태이다. 이 묶여진 형태를 통해 두 개의 비슷한 이미지들은 feature space에서 매우 다른 위치로 매핑되지 않게 된다. (묶이지 않고 별개로 학습된다면 서로 다른 위치로 매핑될 수 있기 때문)

또한 네트워크가 대칭적이기 때문에 두 개의 다른 이미지들을 넣을 때 top conjoining layer는 같은 metric을 계산하여 마치 같은 두 개의 이미지를 넣은 것처럼 보이지만 사실은 반대의 쌍둥이를 넣은 것이다.

처음 Siamese network이 소개된 논문에서는, 저자들이 contrastive energy function을 사용했는데 이는 like pairs(positive pairs)에 대해서 energy를 줄이고 unlike pairs(negative pairs)에 대해서는 energy를 늘리기 위한 dual term을 가지고 있다.

그러나, 이번 논문에서는 twin feature vectors $h_1$, $h_2$간에 Weighted $L_1$ distance를 sigmoid function과 함께 사용했다. ( 👉🏻 sigmoid를 사용했으므로 자연스럽게 cross-entropy objective가 loss function으로 선택된다.)

L1 distance
말단에 sigmoid를 사용
loss로 cross-entropy를 사용

또한, original 논문에서는 similarity metric를 직접적으로 학습한데에 비해, 이 논문에서는 방금 언급한 cross entropy를 사용했다.

1. Model


<code> : https://github.com/tensorfreitas/Siamese-Networks-for-One-Shot-Learning

 convolutional_net = Sequential() convolutional_net.add(Conv2D(filters=64, kernel_size=(10, 10), activation='relu', input_shape=self.input_shape, kernel_regularizer=l2( l2_regularization_penalization['Conv1']), name='Conv1')) convolutional_net.add(MaxPool2D()) convolutional_net.add(Conv2D(filters=128, kernel_size=(7, 7), activation='relu', kernel_regularizer=l2( l2_regularization_penalization['Conv2']), name='Conv2')) convolutional_net.add(MaxPool2D()) convolutional_net.add(Conv2D(filters=128, kernel_size=(4, 4), activation='relu', kernel_regularizer=l2( l2_regularization_penalization['Conv3']), name='Conv3')) convolutional_net.add(MaxPool2D()) convolutional_net.add(Conv2D(filters=256, kernel_size=(4, 4), activation='relu', kernel_regularizer=l2( l2_regularization_penalization['Conv4']), name='Conv4')) convolutional_net.add(Flatten()) convolutional_net.add( Dense(units=4096, activation='sigmoid', kernel_regularizer=l2( l2_regularization_penalization['Dense1']), name='Dense1')) # Now the pairs of images input_image_1 = Input(self.input_shape) input_image_2 = Input(self.input_shape) encoded_image_1 = convolutional_net(input_image_1) encoded_image_2 = convolutional_net(input_image_2) # L1 distance layer between the two encoded outputs # One could use Subtract from Keras, but we want the absolute value l1_distance_layer = Lambda( lambda tensors: K.abs(tensors[0] - tensors[1])) l1_distance = l1_distance_layer([encoded_image_1, encoded_image_2]) # Same class or not prediction prediction = Dense(units=1, activation='sigmoid')(l1_distance) self.model = Model( inputs=[input_image_1, input_image_2], outputs=prediction) # Define the optimizer and compile the model optimizer = Modified_SGD( lr=self.learning_rate, lr_multipliers=learning_rate_multipliers, momentum=0.5) self.model.compile(loss='binary_crossentropy', metrics=['binary_accuracy'], optimizer=optimizer) 


모델은 convolutional layers들의 연속으로 이루어져 있는데 각 layer마다 filter의 사이즈가 다르다.
filter의 수는 16의 배수로 특정되어 있는데, 성능을 최적화하기 위함이다. (위의 코드에서는 64, 128, 128, 256으로 사용)

또한, output feature maps에 ReLU activation function을 적용하고 선택적으로 max pooling을 적용한다.

따라서, $k$번째 filter map은 다음과 같은 형태를 가진다.


마지막 convolutional layer의 유닛들은 하나의 벡터로 flatten된다. 이 layer 뒤에는 fully-connected layer와 각 siamese twin간의 distance metric을 계산하기 위한 layer가 있다. (위의 코드로 더 이해가 쉬울 듯)

더 정확하게는, 예측 벡터가 다음과 같이 주어진다.(weighted L1 distance이므로)

$$
p = \sigma\left(\sum_{j} \alpha_{j}\left|\mathbf{h}_{1, L-1}^{(j)}-\mathbf{h}_{2, L-1}^{(j)}\right|\right)
$$

2. Learning

<Loss function>

M : minibatch size
$ \(\mathbf{y}\left(x_{1}^{(i)}, x_{2}^{(i)}\right)\) $ : M 길이의 벡터로 minibatch에 대한 라벨을 가지고 있다.
($x_1$, $x_2$가 같은 클래스면 1, 아니면 0)

최종 loss function은 다음과 같다.(binary cross-entropy)

<Optimization>

Standard backpropagation을 사용하는데 언급한대로 weight를 묶은 형태이기 때문에 twin network간에 gradient가 더해진다.
epoch $T$에서의 update rule은 다음과 같다.


<Weight initialization>

모든 Network convolutional layer weights는 평균 0, 표준편차 $10^{-2}$의 정규분포로 초기화한다.
또한, Biases는 평균 0.5, 표준편차 $10^{-2}$의 정규분포로 초기화한다.

Fully-connected layer의 biases는 같은 방식으로 초기화하지만, weight는 표준편차를 크게하여 $2*10^{-1}$에서 초기화한다.

<Learning schedule>

Learning rate는 epoch마다 1퍼센트씩 감소한다. 즉 이전의 learning rate의 0.99배가 된다.(local minima를 피하기 위해)

또한 각 레이어마다 momentum을 수정하며 linearly하게 증가한다.

+ Validation error가 20epochs동안 감소하지 않으면 학습을 멈춘다.

<Hyperparameter optimization>

이 논문에서는 Whetlab의 베타버전을 사용했는데, Whetlab은 베이지안 optimization 프레임워크로, 이를 통해 하이퍼파라미터 선택을 할 수 있다.

Learning schedule과 regularization 하이퍼파라미터들에 대해서는, layerwise learning rate $\eta_{j} \in\left[10^{-4}, 10^{-1}\right]$, layerwise momentum $\mu_{j} \in[0,1]$, 또 layerwise $L_2$ regularization penalty $\lambda_{j} \in[0,0.1]$로 설정했다. 네트워크 하이퍼파라미터에 대해서는 Convolutional filter 사이즈를 3X3부터 20X20으로 저장하며 각 layer에서 필터의 수는 16부터 256까지로 정했다. 마지막으로 Fully-connected layer는 128부터 4096 units으로 정했다.

<Affine distortions>

추가적으로, 저자들은 학습 set을 small affine distortions으로 증강시켰는데 아래와 같다.

각 이미지 쌍 $x_1, x_2$에 대해서, affine transformations의 쌍($T_1, T_2$)을 만들어내어 다음과 같은 새로운 이미지가 나오게 된다.

이 Transformation들은 multidimensional uniform distribution에 의해 stochastic하게 결정된다.


Experiments

1. Omniglot Dataset

초반에 언급한대로 논문에서는 아래와 같은 글자들을 모아놓은 Omniglot dataset를 이용하여 실험을 하였다.

각 알파벳의 글자 수는 15개부터 40개까지로 다양하다.

2. Verification

저자들은 3개의 서로다른 사이즈의 데이터셋(30,000, 90,000, 150,000)을 사용하여 Verification network를 학습시켰는데, 랜덤하게 same, different pair들을 샘플링함으로써 사용했다.

성능을 모니터링하기 위해 저자들은 두 가지 방법을 사용했는데,

1. 10가지 알파벳과 4가지 추가적인 drawers로부터 10,000개의 example 쌍들로 validation set을 만들었다.
(나머지 10개의 알파벳과 4개의 drawers는 테스트를 위해 남겨놓음)

2. 같은 알파벳과 drawers를 사용하여 320개의 one-shot 인식 trials의 집합을 validation set으로 만들었다. 이를 통해 언제 학습을 멈출지를 알 수 있게 된다.

아래 테이블에서 정확성을 확인할 수 있다.

3. One-shot Learning

한번 샴네트워크를 최적화시키고 나면, 이제 discriminative potential을 사용할 수 있게 된다.

Test image $x$를 받았을 때 이를 C개의 카테고리 중 하나로 분류하고 싶다고 해보자.
또 다른 이미지들 $\left\{\mathbf{x}_{c}\right\}_{c=1}^{C}$ 를 받았다고 해보자. (C개의 카테고리의 각 예시들이 된다.)
이제 이 분류를 위해, $x$, $x_c$를 사용하여 네트워크에 쿼리를 날릴 수 있다.

다음의 식을 이용하여 가장 확률이 높은 클래스를 테스트 이미지 $x$의 클래스로 예측할 수 있게 된다.

다른 모델들과 비교한 정확성은 다음과 같다.

4. MNIST One-shot Trial

저자들은 또한 MNIST dataset에 대해서도 정확성을 측정하였는데 결과는 다음과 같다.
엄청난 정확성에서의 개선을 볼 수 있다.


Conclusion

이 논문에서는 one shot classification의 한 방법론에 대해 제안하였다.
다른 베이스라인 모델들보다 성능이 나은 것 또한 확인하였다.



728x90
반응형

댓글