🤔 Transformer의 특성

📌 Long-Term Dependency (장기의존성)
- Convolution의 경우, 이웃 pixel과의 정보를 교환한다.
- 주로 3×3, 5×5의 작은 kernel을 사용하기에 영상 내 멀리 떨어진 물체간의 상호작용을 하려면 "Deep layer"가 필요하다.
예를 들어, 3×3kernel은 10만큼 떨어진 pixel의 경우, 4개의 층을 거쳐야 비로소 상호작용이 일어날 수 있다.

- Recurrent의 경우, 시간축을 따라 상호작용이 일어난다.
Convolution과 Recurrent는 모두 깊은 신경망으로 상호작용이 일어난다.
하지만 층이 깊어지면서 여러 요소간에 발생한 정보가 혼합되어 중요한 정보가 흐릿해진다.

- Transformer는 start-end까지 "Self-Attention"을 이용해 explicit하게 "Long-term dependency"를 처리한다.
즉, CNN과 RNN은 어느 순간의 이웃 요소만 교류하는 "Locality"를 벗어나지 못한다.
하지만 Transformer는 "Global"정보를 명시적으로 표현, 처리함과 동시에 "Locality Extraction"에도 능숙하다.
[Cordonnier2020; https://arxiv.org/abs/1911.03584]
ViT의 경우, "Self-Attention"을 통해 patch를 주목하게 하여 해결한다.
위의 예시를 보면, 16개의 head를 갖는 6개의 encoder block에 대해
"가운데의 pixel을 query로 사용해 각 head가 어디가 주목하는지" 표시한 그림이다.
- 왼쪽의 얕은 층에서는 query근처에 주목해 "Local 정보"를 추출
- 오른쪽(깊은 층)으로 갈수록 먼 곳까지 주목하는 현상을 확인할 수 있다.
 특히나 Layer 6에서는 head가 주목하는 곳을 달리해 image전체를 살피는 좋은현상이 나타난다.




📌 Scalability (뛰어난 확장성)
- model 및 dataset의 크기면에서 확장성(scalability)가 뛰어나다.
[Kaplan2020; https://arxiv.org/abs/2001.08361]의 연구는 큰 모델을 사용하는 것이 유리하다는 근거를 제시한다.
즉, model크기가 작으면 dataset을 확장하거나 학습시간을 늘려도 성능향상에 한계가 존재한다.

- 다만, NLP의 이런 분석이 CV에 그대로 적용되지는 않는다. 컴퓨터비전은 자율지도학습보다는 "지도학습"에 의존하는 경향이 크다. 따라서 사람이 labeling한 dataset에 영향을 더 많이 받는다.

- ViT의 경우, 모델크기를 다음과 같은 방식으로 키울 수 있다.
encoder block↑ , patch_size ↓ , head , patch_encoding_dimension인 d_model을 늘리는 등의 방식으로 키울 수 있다.



📌 Explainability (뛰어난 설명가능성)
- 사람의 의사결정에 대한 이유를 설명해야 할 때, 이를 설명하는 능력이 뛰어나다.
transformer는 영상을 구성하는 pixel간의 "Self-Attention"을 명시적으로 표현하기에 설명가능한 능력을 구현하기 매우 유리하다. 예를 들어, Object Detection에 적용가능한 Transformer인 DETR의 경우, 박스를 지정할 때 Object Boundary에 "Attention"을 집중하는 것을 알 수 있다.

- 또한 CUB-200과 같은 미세분류 dataset에 대해 미세분류를 수행하는 TransFG라는 transformer model은 전체적인 모양이 비슷한 미세분류 dataset에 대해 "특정한 곳에 집중해 구별"하는 경우에 매우 잘 수행함을 알 수 있다.



📌 Multi-Modal (뛰어난 확장성)
- 모든 기계학습모델은 귀납편향(inductive bias)이 존재한다.
예를들어, CNN의 경우 이웃한 pixel과 비슷한 특성을 갖는다는 "locality"와 물체가 이동하면 이동한 위치에 동일한 특징이 추출된다는 "translation-equivalent" 등이 대표적이다. 
CNN의 경우 이런 귀납편향이 강한 편으로 이를 만족하지 못하는 자연어와 같은 다른 modality-data에 적용은 부자연스러운 결과가 나온다. (물론, RNN도 시간축과 관련된 강한 귀납편향을 갖는다.)
cf) 귀납편향: 기계학습모델이 학습단계에 보지않았던 새로운 sample로 옳게 추론하기 위해 사용하는 가정

- 하지만 transformer의 경우, 귀납편향이 약한 편으로 가장 중요하게 활용되는 "Self-Attention"은 image, text를 포함해 대부분의 signal에서 나타나서 여러 input형태 (multi-modal data)에 적용할 여지가 존재한다.
예를 들어 자연어->그림의 DALL-E 등등 vision과 NLP를 동시에 처리하는 여러 응용에서 backbone으로 활용할 수 있다.




📌 Foundation Model (토대 모델)
- 귀납편향이 강해 응용이 한정적인 CNN, RNN과 달리 귀납편향이 약한 transformer는 multi-modal dataset에 두루 적용가능하며 확장성과 성능이 뛰어나다는 점으로 토대 모델이 될 수 있었다.
최근에는 Florence라는 computer vision분야의 토대모델이 발표되었다.

 

 

 

[ViT : Vision Transformer

🧐  Computer Vision with Transformer
오랫동안 Convolution Neural Network는 Computer Vision에서 가장 성공한 model로 군림했다.
2017년, transformer의 공개 이후 이 둘을 접목하려는 시도가 이루어졌었다.
- 초기에는 CNN을 backbone으로 두고 transformer로 추출한 self-attention 정보를 보조로 사용하는 방식을 택하였다. ex) Attention augmented convolutional network [Bello2019; https://arxiv.org/abs/1904.09925]
구조는 다음과 같다.  기존 CNN: feature map을 생성, 다음 층으로 전달하는데 이때, self-attention으로 증강시킨 신경망에서는 feature map과 transformer의 MHA로 만든 feature map을 결합해 다음층으로 전달한다.

이후로도 여러 시도가 있었는데, Transformer를 backbone으로 하거나 Convolution을 아예 없애고 Attention만으로 구성하는 등 여러 시도가 있었는데, 그 시도의 대표적인 예시들이 아래 예시들이다.
∙ ViT [Dosovitskiy2021; https://arxiv.org/abs/2010.11929]
∙ DETR [Carion2020; https://arxiv.org/abs/2005.12872]
∙ SETR [Zheng2021; https://arxiv.org/abs/2012.15840]
∙ MOTR, TransTrack, TrackFormer [Zeng2021; https://arxiv.org/abs/2105.03247 , Sun2021; https://arxiv.org/abs/2012.15460 , Meinhardt2021; https://arxiv.org/abs/2101.02702]

최근에는 classification, detection, segmentation, tracking문제를 "하나의 모델"로 모두 해결하는 방식으로 진화했는데  Swin Transformer가 대표적이다. [Liu2021a; https://arxiv.org/abs/2103.14030]

cf. 최신연구결과를 정리한 survey paper
- [Han2022; https://arxiv.org/abs/2012.12556  ,  Khan2022; https://arxiv.org/abs/2101.01169]

앞서 언급한 것들처럼 computer vision에 적용된 transformer model을 통칭해 Vision Transformer라 부른다.


🧐  ViT :
Transformer for classification 
ViT
는 [Vaswani2017; https://arxiv.org/abs/1706.03762]에서 소개된 transformer구조를 최대한 그대로 따라 실험한다.
다만, 기존 transformer는 언어 번역 등의 목적을 위해 개발되었기에 문장은 단어가 순서대로 배열된 1차원 구조의 data이다.
따라서 ViT는 image의 pixel이 2차원 구조로 배열된 data이기에 transformer에 조금의 변형을 가미한다.

Classification Task에서는 image를 입력받아 class의 probability vector를 출력하는데, 이는 즉 [Vaswani]의 Transformer구조에서 Encoder부분만 있으면 된다.
- Encoder는 Decoder로 tensor를 전달하는 대신, 추출한 feature map을 class probability vector로 변환, output layer를 통해 출력하면 되기 때문이다.
- 따라서 전체적인 ViT의 구조는 아래와 같다.
위의 사진에서 알 수 있듯, encoder에 input tensor를 입력시의 과정은 다음과 같다.

<Encoder에 image를 입력하는 과정>
① image를 p×p개의 patch로 나눈다. 

p²개의 patch를 일렬로 배치, 이어붙인다. 
ex) h×w×3라면 ①과 ②를 통해 p²×(h/p * w/p * 3)으로 변환된다.

③ pixel을 일렬로 이어 붙인 후 embedding진행 
ex) 3hw / p²차원의 벡터로 만든다.
또한 이 벡터를 embedding층으로 d_model차원으로 변환한다.
이 벡터(d_model)를 행에 쌓으면 p²×d_model크기의 행렬이 된다.


④ 위치 인코딩(Position Encoding)을 더해, Encoder로 입력한다.

ex) X = P+S라는 식을 적용할 수 있는데, 여기서 S는 p²×d_model의 행렬이고 P는 위치 인코딩 행렬이다.

지금까지 설명한 vision transformer는 class probability vector만 출력하면 되기에 가장 단순한 편에 속하며, DETR의 경우, 출력이 가변개수인 box이기에 Encoder와 Decoder를 모두 사용해야 한다.
또한 작은 dataset에 대해서는 transformer가 열등한 경향이 있기에 주의해서 사용해야한다.

 

 

 

 

😶 실습 _ by tensorflow

import os
import cv2
import math
import wandb
import shutil
import random
import numpy as np
import pandas as pd
import tensorflow as tf
import matplotlib.pyplot as plt

from tensorflow import keras
from tensorflow.keras.preprocessing.image import load_img
from tensorflow.keras.preprocessing.image import ImageDataGenerator

from tensorflow.keras.models import *
from tensorflow.keras.layers import *
from tensorflow.keras.losses import *
from tensorflow.keras.metrics import *
from tensorflow.keras.optimizers import *
from tensorflow.keras.activations import *

from tensorflow.keras.regularizers import *

from tensorflow.keras.callbacks import *
from tensorflow.keras.preprocessing import *
(x_train,y_train),(x_test,y_test)=keras.datasets.cifar10.load_data()

n_class=10                  			# 부류 수
img_siz=(32,32,3)
img_expanded_siz=(72,72,3)

patch_siz=6                 			# 패치 크기
p2=(img_expanded_siz[0]//patch_siz)**2 # 패치 개수
d_model=64                  			# 임베딩 벡터 차원
h=8                         			# 헤드 개수
N=6                         			# 인코더 블록의 개수
class Patches(Layer):
    def __init__(self, patch_size):
        super(Patches, self).__init__()
        self.p_siz = patch_size

    def call(self, img):
        batch_size = tf.shape(img)[0]
        patches = tf.image.extract_patches(images=img, sizes=[1,self.p_siz,self.p_siz,1], strides=[1,self.p_siz,self.p_siz,1], rates=[1,1,1,1], padding="VALID")
        patch_dims = patches.shape[-1]
        patches = tf.reshape(patches,[batch_size, -1, patch_dims])
        return patches

class PatchEncoder(Layer):
    def __init__(self, p2, d_model): # p2: 패치 개수(p^2)
        super(PatchEncoder, self).__init__()
        self.p2 = p2
        self.projection = Dense(units=d_model)
        self.position_embedding = Embedding(input_dim=p2, output_dim=d_model)

    def call(self,patch):
        positions = tf.range(start=0, limit=self.p2, delta=1)
        encoded = self.projection(patch) + self.position_embedding(positions)
        return encoded
def ViT_classifier():
    input = Input(shape=(img_siz))
    norm = Normalization()(input)
    exp = Resizing(img_expanded_siz[0],img_expanded_siz[1])(norm)
    
    x = RandomFlip('horizontal')(exp)
    x = RandomRotation(factor=0.02)(x)
    x = RandomZoom(height_factor=0.2, width_factor=0.2)(x)
    
    patches = Patches(patch_siz)(x)		# 패치 생성
    x = PatchEncoder(p2,d_model)(patches)	# 패치 인코딩

    for _ in range(N):			# 다중 인코더 블록
        x1 = LayerNormalization(epsilon=1e-6)(x)		# 층 정규화
        x2 = MultiHeadAttention(num_heads=h,key_dim=d_model//h,dropout=0.1)(x1,x1)			# MHA
        x3 = Add()([x2,x])		# 지름길 연결
        x4 = LayerNormalization(epsilon=1e-6)(x3)	# 층 정규화
        x5 = Dense(d_model*2,activation='relu')(x4)
        x6 = Dropout(0.1)(x5)
        x7 = Dense(d_model,activation='relu')(x6)   
        x8 = Dropout(0.1)(x7)        
        x = Add()([x8,x3])		# 지름길 연결
    
        x = LayerNormalization(epsilon=1e-6)(x)
        x = Flatten()(x)
        x = Dropout(0.5)(x)   
        x = Dense(2048,activation=tf.nn.gelu)(x)    
        x = Dropout(0.5)(x)
        x = Dense(1024,activation=tf.nn.gelu)(x)    
        x = Dropout(0.5)(x)    
        output = Dense(n_class,activation='softmax')(x)
        
        model = Model(inputs=input,outputs=output)
        return model 

    
model=ViT_classifier()
model.layers[1].adapt(x_train)
model.summary()


Model: "model_1"
__________________________________________________________________________________________________
 Layer (type)                   Output Shape         Param #     Connected to                     
==================================================================================================
 input_2 (InputLayer)           [(None, 32, 32, 3)]  0           []                               
                                                                                                  
 normalization_1 (Normalization  (None, 32, 32, 3)   7           ['input_2[0][0]']                
 )                                                                                                
                                                                                                  
 resizing_1 (Resizing)          (None, 72, 72, 3)    0           ['normalization_1[0][0]']        
                                                                                                  
 random_flip_1 (RandomFlip)     (None, 72, 72, 3)    0           ['resizing_1[0][0]']             
                                                                                                  
 random_rotation_1 (RandomRotat  (None, 72, 72, 3)   0           ['random_flip_1[0][0]']          
 ion)                                                                                             
                                                                                                  
 random_zoom_1 (RandomZoom)     (None, 72, 72, 3)    0           ['random_rotation_1[0][0]']      
                                                                                                  
 patches_1 (Patches)            (None, None, 108)    0           ['random_zoom_1[0][0]']          
                                                                                                  
 patch_encoder_1 (PatchEncoder)  (None, 144, 64)     16192       ['patches_1[0][0]']              
                                                                                                  
 layer_normalization_3 (LayerNo  (None, 144, 64)     128         ['patch_encoder_1[0][0]']        
 rmalization)                                                                                     
                                                                                                  
 multi_head_attention_1 (MultiH  (None, 144, 64)     16640       ['layer_normalization_3[0][0]',  
 eadAttention)                                                    'layer_normalization_3[0][0]']  
                                                                                                  
 add_2 (Add)                    (None, 144, 64)      0           ['multi_head_attention_1[0][0]', 
                                                                  'patch_encoder_1[0][0]']        
                                                                                                  
 layer_normalization_4 (LayerNo  (None, 144, 64)     128         ['add_2[0][0]']                  
 rmalization)                                                                                     
                                                                                                  
 dense_7 (Dense)                (None, 144, 128)     8320        ['layer_normalization_4[0][0]']  
                                                                                                  
 dropout_5 (Dropout)            (None, 144, 128)     0           ['dense_7[0][0]']                
                                                                                                  
 dense_8 (Dense)                (None, 144, 64)      8256        ['dropout_5[0][0]']              
                                                                                                  
 dropout_6 (Dropout)            (None, 144, 64)      0           ['dense_8[0][0]']                
                                                                                                  
 add_3 (Add)                    (None, 144, 64)      0           ['dropout_6[0][0]',              
                                                                  'add_2[0][0]']                  
                                                                                                  
 layer_normalization_5 (LayerNo  (None, 144, 64)     128         ['add_3[0][0]']                  
 rmalization)                                                                                     
                                                                                                  
 flatten_1 (Flatten)            (None, 9216)         0           ['layer_normalization_5[0][0]']  
                                                                                                  
 dropout_7 (Dropout)            (None, 9216)         0           ['flatten_1[0][0]']              
                                                                                                  
 dense_9 (Dense)                (None, 2048)         18876416    ['dropout_7[0][0]']              
                                                                                                  
 dropout_8 (Dropout)            (None, 2048)         0           ['dense_9[0][0]']                
                                                                                                  
 dense_10 (Dense)               (None, 1024)         2098176     ['dropout_8[0][0]']              
                                                                                                  
 dropout_9 (Dropout)            (None, 1024)         0           ['dense_10[0][0]']               
                                                                                                  
 dense_11 (Dense)               (None, 10)           10250       ['dropout_9[0][0]']              
                                                                                                  
==================================================================================================
Total params: 21,034,641
Trainable params: 21,034,634
Non-trainable params: 7
__________________________________________________________________________________________________
model.compile(optimizer=Adam(),loss='sparse_categorical_crossentropy',metrics=['accuracy'])

hist=model.fit(x_train,y_train,batch_size=128,epochs=100,validation_data=(x_test,y_test),verbose=1)

Epoch 93/100
391/391 [==============================] - 711s 2s/step - loss: 0.2404 - accuracy: 0.9194 - val_loss: 0.7578 - val_accuracy: 0.7829
Epoch 94/100
391/391 [==============================] - 710s 2s/step - loss: 0.2437 - accuracy: 0.9194 - val_loss: 0.7725 - val_accuracy: 0.7860
Epoch 95/100
391/391 [==============================] - 709s 2s/step - loss: 0.2327 - accuracy: 0.9227 - val_loss: 0.7490 - val_accuracy: 0.7898
Epoch 96/100
391/391 [==============================] - 711s 2s/step - loss: 0.2296 - accuracy: 0.9238 - val_loss: 0.7564 - val_accuracy: 0.7744
Epoch 97/100
391/391 [==============================] - 712s 2s/step - loss: 0.2274 - accuracy: 0.9245 - val_loss: 0.8361 - val_accuracy: 0.7784
Epoch 98/100
391/391 [==============================] - 709s 2s/step - loss: 0.2267 - accuracy: 0.9238 - val_loss: 0.7484 - val_accuracy: 0.7818
Epoch 99/100
391/391 [==============================] - 711s 2s/step - loss: 0.2237 - accuracy: 0.9265 - val_loss: 0.7732 - val_accuracy: 0.7809
result = model.evaluate(x_test, y_test)
print('정확률 =', result[1] * 100)


epoch = np.arange(1,101)
train_accuracy, val_accuracy = hist.history['accuracy'], hist.history['val_accuracy']
train_loss, val_loss = hist.history['loss'], hist.history['val_loss']


plt.subplot(121)
plt.plot(epoch, train_accuracy, 'b', marker='.', label='train_accuracy')
plt.plot(epoch, val_accuracy, 'r--', marker='.', label='valid_accuracy')
plt.title('Accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.grid()
plt.legend()


plt.subplot(122)
plt.plot(epoch, train_loss, 'b', marker='.', label='train_accuracy')
plt.plot(epoch, val_loss, 'r--', marker='.', label='valid_accuracy')
plt.title('Loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.grid()
plt.legend()

plt.show()




313/313 [==============================] - 2s 8ms/step - loss: 0.7732 - accuracy: 0.7809
정확률 : 78.09000015258789

 

 

 

+ Recent posts