반응형

AN IMAGE IS WORTH 16X16 WORDS: TRANSFORMERS FOR IMAGE RECOGNITION AT SCALE

** 아래의 내용과 이미지는 위의 논문을 기반으로 작성하였습니다.

** 수정할 부분이나 의견이 있다면 댓글로 달아주세요~

 

ABSTRACT

Transfomer architecture는 NLP task에서 공공연히 standard한 모델이 되어왔지만 CV(Computer Vision) task에서는 여전히 한계가 있다.

우리는 CNN을 사용할 필요없이 image를 sequcese of patches로 직접 적용하는 transformer 모델 자체가 classification task에서 좋은 성능을 보인다는 것을 보여준다.

대량의 data를 pre-trained하고 multiple mid-sized or small images를 banchmarks (ImageNet, CIFAR-100, VTAB, etc.)로 transferred할 때 ViT는 적은 computational resource를 사용하여 훈련하여도 SOTA와 비교해도 좋은 결과를 얻을 수 있다.

 

 

1 INTORDUCTION

Transformer(Self-attention-based architectures)가 large dataset으로 훈련된 pre-trained모델로 NLP task에서 많이 채택되고 있다.

CV에서는 CNN을 사용하는 경우가 여전히 우세하다. attention aptterns을 사용할 때 여러 test가 있었지만 아직 충분히 scaled dffectively 모델이 없다.

우리는 image를 잘라서 patches(treated the same way as tokens (words))로 만들고 sequence를 linear ebedding로 만들어 transformer에 넣었다. ViT는 충분한 scale로 pre-trained하고 fewer datapoints로 task를 trasnferred할때 상당히 좋은 결과를 달성할 수 있다.

 

 

2 RELATED WORK

Naive application of self-attention to images would require that each pixel attends to every other pixel. With quadratic cost in the number of pixels, this does not scale to realistic input sizes. Thus, to apply Transformers in the context of image processing, several approximations have been tried in the past.

Many of these specialized attention architectures demonstrate promising results on computer vision tasks, but require complex engineering to be implemented efficiently on hardware accelerators.

Most related to ours is the model of Cordonnier et al. (2020), which extracts patches of size 2 × 2 from the input image and applies full self-attention on top. This model is very similar to ViT, but our work goes further to demonstrate that large scale pre-training makes vanilla transformers competitive with (or even better than) state-of-the-art CNNs. Moreover, Cordonnier et al. (2020) use a small patch size of 2 × 2 pixels, which makes the model applicable only to small-resolution images, while we handle medium-resolution images as well.

We focus on these two latter(ImageNet-21k and JFT-300M) datasets as well, but train Transformers instead of ResNet-based models used in prior works.

 

 

3 METHOD

In model design we follow the original Transformer (Vaswani et al., 2017) as closely as possible. An advantage of this intentionally simple setup is that scalable NLP Transformer architectures – and their efficient implementations – can be used almost out of the box.

 

3.1 VISION TRANSFORMER (ViT)

ViT Architecture

standard Transformer는 input으로 1D sequnce of token embeddings을 가지기 때문에 2D images를 다루기 위해 patches(이미지를 자른 것)를 flatten하고 trainable linear projection을 사용하여 D 차원에 mapping한다.

potision embeddings(1D)는 위치 정보를 유지하기 위해 patch embeddings에 합산한다. sequence of embedding vectors는 encoder의 입력으로 사용된다.

  • linear projection (Eq. 1).the patch embedding projection E (Eq. 1) is applied to patches extracted from a CNN feature map.
    • $z_0 = [x_{class}; x^1_pE;x^2_pE; ...;x^N_pE] + E_{pos}, E \in \mathbb{R}^{(P^2C) \times D}, E_{pos} \in \mathbb{R}^{(N+1)\times D}$

 

BERT의 [class] token과 비슷하게, (Transformer encoder $(z^0_L)$ 의 output 상태가 image representation y 역할을 하는) sequnce of embedded patches 앞에 learnable embedding을 추가한다.

  • (Eq.4)
    • $y = LN(z^0_L)$ , Layernorm (LN)

 

Transformer Encoder는 Multihead self-attention(MSA) 과 MLP block (Eq. 2, 3)의 layers로 구성된다. Layernorm (LN)은 모든 block 이전에 적용되고 residual connection (+ 기호 사용)은 모든 block 이후에 적용된다.

  • (Eq.2)Multihead self-attention(MSA) is an extension of SA in which we run k self-attention operations, called “heads”, in parallel, and project their concatenated outputs. (in Appendix A)
    • $z'l = MSA(LN(z{l-1})) + z_{l-1}$ $l = 1 ... L$
  • (Eq.3)MLP에는 2개의 GELU layers 사용

 

Inductive bias.

CNN에서는 locality, 2D neighborhood structure, and translation equivariance가 전체 모델을 통해 각 layer로 다시 들어간다.

ViT에서는 MLP layers만 local and translationally equivariant이고, self-attention layers는 global이다.

 

Hybrid Architecture.

hybrid model에서는 patch embedding projection E(Eq. 1)에 CNN feature map으로 부터 추출된 patches가 적용된다. 특별한 경우로, patches는 spatial size 1x1 가질수 있다. input sequence는 단순히 spatial dimensions of the feature map을 flattening하고 Transformer dimesion을 projecting하여 얻는다. ( Abstract 에서는 CNN 필요없다고 했지만...)

 

3.2 FINE-TUNING AND HIGHER RESOLUTION

일반적으로 대규모 데이터셋에 대해 ViT를 pre-train하고 (smaller) downstream task로 fine-tune한다. 이를 위해 pre-trained prediction head를 제거하고 zero-initialize된 DxK(K: the number of downstream classes) feedforward layer를 연결한다. 종종 higer resolution으로 fine-tune하는 것이 도움이 된다.

hider resolution을 사용할 때는 patch size를 같게 유지하기 때문에 effective sequence length가 커진다. ViT는 임의의 sequence length를 처리할 수 있지만 pre-trained position embeddings가 더 이상 의미가 없을 수 있다. 따라서 원본에서 location에 따라 pre-trained position embeddings의 2D interpolation을 수행한다. (수동으로 bias 넣음)

 

 

4 EXPERIMENTS

ViT Table1
ViT Figure3, 4

ViT-L/16 means the “Large” variant with 16×16 input patch size.

Note that the Transformer’s sequence length is inversely proportional to the square of the patch size, thus models with smaller patch size are computationally more expensive.

 

ViT attention

Globally, we find that the model attends to image regions that are semantically relevant for classification (Figure 6).

 

 

5 CONCLUSION

image recognition을 Transformers에 직접 적용해보았다. 기존 computer vision에서 self-attention을 사용하는 방법과 달리, 이 논문에서는 image를 a sequence of patches로서 NLP에서 사용되는 standard Transformer encoder로 처리한다.

  • Challenges
    1. 다른 CV task(detection and segmentation)에 ViT 적용하기
    2. self-supervised pre-training 방법 탐색하기

 

나의 결론

  • 최근 동향을 보면, Transformer는 CNN, LSTM 과 같은 general deep learning model이 되었다. 오히려 NLP, CV 등 task에 국한되지 않고 대체적으로 (SOTA에 준하는 혹은 뛰어넘는) 좋은 성능을 보이고 있다. (nlp와 달리 image는 local&global(position) feature(information)가 중요함)
  • 이 논문에서는 image input을 transformer model에 어떻게 넣어야 효과적일까? 에 대한 방법을 소개한다.
    • 방법요약: patch로 이미지를 잘라서(or CNN으로 feature map 생성해서-hybrid architecture) 1dim으로 flatten 시키고 (Transformer가 1d를 input으로 받음) 각 patch별로 position embedding을 붙여서 transformer에 넣는다. (idea는 단순함)
    • 다만, 데이터셋이 적으면 train 하기 어려움 → (google에서 ViT를 large dataset으로 pre-train함) fine-tuning(transferred learning)해서 사용하면 좋은 성능 기대할 수 있음

 

 

반응형
반응형

source: tensorflow.org

오늘은 딥러닝 모델을 생성할 때 많이 사용하는 Python 라이브러리 중 Tensorflow의 기본 동작 원리와 코드, Tensorflow 2.0에서는 어떻게 변화하였는지에 대해 살펴보도록 하겠습니다!

 

텐서플로 1 코드를 텐서플로 2로 바꿔보자

여전히 텐서플로 1.X 버전의 코드를 수정하지 않고 텐서플로 2.0에서 실행할 수 있습니다(contrib 모듈은 제외):

import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()

→ contrib 모듈의 경우 각 함수에 맞게 대체 가능한 모듈 찾아서 변경하기 (이 부분이 조금 귀찮음)

 

텐서플로 1 vs 2 동작원리 살펴보기

1. 텐서플로 1.x 코드

import tensorflow as tf  # v1

# 1. tf.placeholder를 사용하여 그래프에 입력할 값의 형태를 미리 지정한다.
in_a = tf.placeholder(dtype=tf.float32, shape=(2))
in_b = tf.placeholder(dtype=tf.float32, shape=(2))

# 2. 그래프 생성
def forward(x):
    # tf.get_variable을 사용하여 변수를 생성하고 tf.variable scope로 그 범위를 지정한다.
    with tf.variable_scope("matmul", reuse=tf.AUTO_REUSE):
        W = tf.get_variable("W", initializer=tf.ones(shape=(2,2)),
                            regularizer=tf.contrib.layers.l2_regularizer(0.04))
        b = tf.get_variable("b", initializer=tf.zeros(shape=(2)))
        return W * x + b

out_a = forward(in_a)
out_b = forward(in_b)

reg_loss = tf.losses.get_regularization_loss(scope="matmul")

# 3. tf.Session.run을 이용하여 그래프를 실행한다.
# The value returned by run() has the same shape as the fetches argument.
# feed_dict을 이용하여 placeholder의 실제 입력값을 넣는다.
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    outs = sess.run([out_a, out_b, reg_loss],  # fetches
                    feed_dict={in_a: [1, 0], in_b: [0, 1]})

 

 

2. 텐서플로 2.x 코드로 변경

import tensorflow as tf  # v2

# tf.variable_scope 사용하지 않음
W = tf.Variable(tf.ones(shape=(2,2)), name="W")
b = tf.Variable(tf.zeros(shape=(2)), name="b")

@tf.function
def forward(x):	
    return W * x + b

# tf.placeholder & tf.Session.run를 사용하지 않음
# Session.run 대신 실제 입력값의 단순 함수 호출로 변경(즉시 실행가능)
out_a = forward([1,0])
out_b = forward([0,1])

regularizer = tf.keras.regularizers.l2(0.04)
reg_loss = regularizer(W)

: TensorFlow 2 패키지에는 pip 버전 >19.0이 필요합니다. (https://www.tensorflow.org/install)

 

결론

source: https://www.tensorflow.org/guide/migrate#결론

전체적인 과정은 다음과 같습니다:

  1. 업그레이드 스크립트를 실행하세요.
  2. contrib 모듈을 삭제하세요.
  3. 모델을 객체 지향 스타일(케라스)로 바꾸세요.
  4. 가능한 **[tf.keras](<https://www.tensorflow.org/api_docs/python/tf/keras>)**나 **[tf.estimator](<https://www.tensorflow.org/api_docs/python/tf/estimator>)**의 훈련과 평가 루프를 사용하세요.
  5. 그렇지 않으면 사용자 정의 루프를 사용하세요. 세션과 컬렉션은 사용하지 말아야 합니다.

텐서플로 2.0 스타일로 코드를 바꾸려면 약간의 작업이 필요하지만 다음과 같은 장점을 얻을 수 있습니다:

  • 코드 라인이 줄어 듭니다.
  • 명료하고 단순해집니다.
  • 디버깅이 쉬워집니다.

 


텐서플로 2 | Basic Model 만들어보자

→ tensorflow2라고 쓰고 tf.keras 라고 읽는다.

참고: https://www.tensorflow.org/guide/keras

  • 아래의 tensorflow2 모델은 이미지 분류를 예시로 작성하였습니다.

 

1. dataset 생성하기

  • 여러 방법이 있지만 2가지만 소개합니다.
import tensorflow as tf

# Case 1
## ex, 
## x_train = [train_data_dir/file1.jpg, train_data_dir/file2.jpg, train_data_dir/file3.jpg, ...]
## y_train = [class1, class1, class2, ...]

train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))

# 필요에 따라 map_func 사용할 수 있음 (ex, 이미지 read, resize, ...)
train_dataset = train_dataset.map(lambda item1, item2: tf.numpy_function(
                                      map_func, [item1, item2], [tf.float32, tf.float32]))

# Shuffle and batch
train_dataset = train_dataset.shuffle(BUFFER_SIZE, seed=2021).batch(BATCH_SIZE)
train_dataset = train_dataset.prefetch(BUFFER_SIZE//2)
# Case 2
## ex, train_data_dir/class1/file1.jpg, train_data_dir/class1/file2.jpg, train_data_dir/class2/file3.jpg, ...
train_dataset = tf.keras.preprocessing.image_dataset_from_directory(train_data_dir,
                                                                    seed=2021,
                                                                    image_size=(img_height, img_width),
                                                                    batch_size=batch_size)

# class name 찾을 수 있음
class_names = train_dataset.class_names

train_dataset = train_dataset.cache().prefetch(BUFFER_SIZE//2)

 

2. model 생성하기

2.1 함수형

import tensorflow as tf
class_num = 5

def create_image_classification_model(input_shape):
    # 아래의 구조의 경우, tf.keras.Model 대신 tf.keras.models.Sequential 사용 가능
    # 다만, 다중 입력 및 출력, residual connection, attention 등 Model function이 활용성이 더 높음
    model_input = tf.keras.Input(shape=input_shape)

    embedding = tf.keras.layers.Conv2D(2, 3, padding='same', activation='relu')(model_input)

    embedding = tf.keras.layers.GlobalAveragePooling2D()(embedding)

    embedding = tf.keras.layers.Dense(128, activation='relu')(embedding)
    embedding = tf.keras.layers.Dropout(0.2)(embedding)
    
    embedding = tf.keras.layers.Dense(class_num, activation='softmax', name='output')(embedding)
    
    return tf.keras.Model(model_input, embedding, name='image_classification_model')

input_shape = (256, 256, 3)
classification_model = create_image_classification_model(input_shape)

## sample test
temp_input = tf.random.uniform(input_shape, dtype=tf.float32, minval=0, maxval=256)
output = classification_model.predict(tf.expand_dims(temp_input, 0))
output.shape  # (1, 5)

# model architecture(shape) & params 확인
classification_model.summary()

## (구조가 더 복잡한 경우,) model architecture(shape) ploting하여 확인 가능
# tf.keras.utils.plot_model(classification_model, "image_classification_model.png", show_shapes=True)

 

2.2 클래스 상속

import tensorflow as tf
class_num = 5

class ClassificationModel(tf.keras.Model):
    def __init__(self, class_num, dim=128, rate=0.1):
        super(ClassificationModel, self).__init__()
        self.conv2d = tf.keras.layers.Conv2D(2, 3, padding='same', activation='relu')
        
        self.dense1 = tf.keras.layers.Dense(dim, activation='relu')
        self.dense2 = tf.keras.layers.Dense(class_num, activation='softmax', name='output')
        
        self.gapooling = tf.keras.layers.GlobalAveragePooling2D()
        self.dropout = tf.keras.layers.Dropout(rate)
        

    def call(self, inputs):

        embedding = self.conv2d(inputs)

        embedding = self.gapooling(embedding)

        embedding = self.dense1(embedding)
        embedding = self.dropout(embedding)

        embedding = self.dense2(embedding)

        return embedding


input_shape = (256, 256, 3)
classification_model = ClassificationModel(class_num=class_num, rate=0.2)

## sample test
temp_input = tf.random.uniform(input_shape, dtype=tf.float32, minval=0, maxval=256)
output = classification_model(tf.expand_dims(temp_input, 0))

output.shape # TensorShape([1, 5])

# model architecture & params 확인
classification_model.build((None, 256, 256, 3))
classification_model.summary()

 

3. model 훈련하기

classification_model.compile(optimizer='adam',
                             loss=tf.keras.losses.CategoricalCrossentropy(from_logits=False),
                             metrics=['categorical_accuracy'])

history = classification_model.fit(train_dataset,
                                   epochs=EPOCHS,
                                   validation_data=val_dataset,
                                   callbacks=[modelcheck],)  # callbacks: modelcheckpoint, etc

 

  • 시각화하기
    import matplotlib.pyplot as plt
    
    acc = history.history['accuracy']
    val_acc = history.history['val_accuracy']
    
    loss=history.history['loss']
    val_loss=history.history['val_loss']
    
    epochs_range = range(EPOCHS)
    
    plt.figure(figsize=(8, 8))
    plt.subplot(1, 2, 1)
    plt.plot(epochs_range, acc, label='Training Accuracy')
    plt.plot(epochs_range, val_acc, label='Validation Accuracy')
    plt.legend(loc='lower right')
    plt.title('Training and Validation Accuracy')
    
    plt.subplot(1, 2, 2)
    plt.plot(epochs_range, loss, label='Training Loss')
    plt.plot(epochs_range, val_loss, label='Validation Loss')
    plt.legend(loc='upper right')
    plt.title('Training and Validation Loss')
    plt.show()​
    source: https://www.tensorflow.org/tutorials/images/classification

 

4. model save & load, predict 하기

model_save_path = 'image_classification_model'

# model architecture & weight 저장
classification_model.save(model_save_path)

# model architecture & weight 불러오기
new_model = tf.keras.models.load_model(model_save_path)
# new_model.trainable = False  # transfer learning시 pre-trained model을 freezing할 때 사용, layer별로 설정 가능

# 불러온 모델 predict
y_pred = new_model.predict(test_dataset,
                           steps=STEP_SIZE_TEST
                           )

 

 

반응형

+ Recent posts