-
Notifications
You must be signed in to change notification settings - Fork 0
[T1219_최현진]SATRN 실험
chjin0725 edited this page Dec 29, 2021
·
27 revisions
- 위의 예시에서 처럼 어떤 수식이 적힌 이미지를 input으로 받으면 이 수식에 대한 latex문법 표현식을 출력해주는 모델을 만드는 프로젝트이다.
- 위의 예시에서의 수식이미지는 컴퓨터로 인쇄된 수식이미지 이지만 실제로는 손으로 쓴 수식이미지도 사용되었다.
- 이 프로젝트에서 사용한 데이터에서는 이미 text localization이 수행된 데이터들이다. 즉, 이 프로젝트에서는 text recognition만 한다.
- input 이미지는 CNN을 거쳐서 feature map이 된 후 Transformer 구조를 가진 네트워크를 통과하여 최종 output이 된다.
- Transformer구조를 가진 부분은 기존 Transformer와 구조가 유사하지만 input으로 자연어가 아닌 feature map을 받으므로 이 부분을 고려하여 기존 Transformer와는 조금씩 다른 부분들을 가지고 있다.
- SATRN 논문리뷰 : https://github.com/bcaitech1/p4-fr-9-googoo/discussions/4
대회서버가 불안정하여 리더보드에 제출이 안되는 이유로 모든 실험은 리더보드 점수가 아닌 validation data에 대한 점수를 기반으로 진행 하였다. 다만 다른 분들의 말에 따르면 validation 점수와 리더보드 점수가 거의 똑같다고 한다.
- 주어진 baseline을 그대로 사용하여 30epoch단위로 끊어서 실행하였다. 아래는 validation data에 대한 sentence accuracy이다.
- 30epoch 이후 부터 학습을 이어서 진행해 보았다. 아래는 이에 대한 sentnece accuracy이다.
- overfit이 계속 일어나서 중간에 중단하였다. 30epoch정도가 적절해 보인다.
- 다만 아래 그림(첫 30epoch동안의 learning rate)에서처럼 총 epoch수를 기준으로 learning rate가 cyclic하게 조절되는 스케쥴러를 사용하였기 때문에 처음부터 총 epoch수를 30이 아니라 50이나 다른 수로 잡는다면 결과가 달라질 수 있다. 하지만 30epoch씩 두번 하는건 과적합을 일으킨다. 두번째 30에폭에서는 learning rate가 다시 증가하게 되고 learning rate가 너무 크면 score가 튈 수 있기 때문이다.
- 나중에 더 해보니 처음부터 50에폭으로 잡으면 accuracy가 더 올라간다. 60에폭으로 하면 더더 올라간다.
- epoch을 크게할 수록 learning rate가 더 천천히 증가하다가 천천히 감소하기 때문에 더 안정적으로 학습이 가능한 것으로 보인다.
- 베이스라인으로 받은 STARN 코드가 잘못되어 있는 것 같다. Transformer구조의 decoder부분의 구현이 잘못되어 있다.
- 관련 issue 링크: https://github.com/bcaitech1/p4-fr-9-googoo/issues/5
- 코드 구현 관련된 부분을 수정후 batch size = 34, epoch = 50으로 하여 학습.
- 50 epoch에서 validation sentence accuracy = 0.6973. batch size = 34, epoch = 50일 때의 수정 전(0.6887)과 비교하여 약 1%정도 올랐다.
- 코드에서 loss함수에 ignore_index를 추가하였다.
- 기존 베이스라인 코드에서는 CrossEntoropyLoss에 패딩토큰에 대한 ignore_index를 추가하지 않는 형태였는데 이 부분이 이해가 안되서 왜 이렇게 하는지 질문을 해보니 그냥 오타였다.
- ignore_index를 추가하여 패딩토큰에 대한 loss는 무시하면 EOS토큰까지의 토큰들을 잘 예측하는 것에 더 집중할 수 있기때문에 성능이 더 오를것이라 생각하였다.
- 다른 팀원분께서 실험을 해주셨는데 아무것도 안한 기본 베이스라인 코드에서 ignore index만 추가해주니 리더보드 에서의 sentence accuracy가 0.6825에서 0.7037로 올랐다.
- 베이스라인 코드에는 encoder의 feedforward network로 fully connected가 사용되었다(아래 그래프의 SATRN_v4).
- 원래 SATRN논문에서 사용한 방식대로 depth-wise separable convolution으로 교체하여 학습해보았다(아래 그래프의 SATRN_v5).
- 처음에는 점수가 더 잘나오다가 끝에 가서는 기존 모델(v4)보다 점수가 더 낮게 되었다(v4의 accuracy : 0.6973, v5의 accuracy:0.689).
- 위의 그래프에서 처럼 train data에 대한 sentence accuracy는 v5가 v4보다 항상 높았다.
- 과적합이라 판단하고 drop out rate를 기존의 0.1에서 0.3으로 높여서 다시 실험해보았다.
- 위의 그래프에서 처럼 drop out을 0.3으로 준 모델(v6)의 validation sentence accuracy가 v5(drop out 0.1)보다 안좋게 나왔다.
- drop out을 너무 과하게 준 것으로 보이므로 0.2로 줄여서 다시 실험해 보았다.
- 위의 그래프에서 처럼 drop out을 0.2로 준 모델(v7)이 v5보다 0.003정도 더 높게 나왔다. 그러나 여전히 v4보다는 작다.
- drop out을 좀 더 조절해주면 좀 더 accuracy를 높일 수도 있을 것 같았지만 시간이 너무 오래 걸려서 하지 않았다. 한다고 하더라도 v4보다 크게 높은 점수를 얻을 수 있을 것 같진 않았다.
- 이에 대해 가설을 하나 세웠는데 현재 가지고 있는 train 데이터로는 validation 데이터에게 까지 적용할 수 있는 global한 feature를 더 이상 학습할 수 없는게 아닐까 하는 것이다. 즉, 학습할 수 있는 global feature는 전부 학습해서 더 이상 남아있지 않아서 더 좋은 모델을 써도 점수가 안오르거나 심지어 오버피팅으로 점수가 낮아지는게 아닐까? 마치 2차 다항함수로부터 뽑힌 데이터에 대해 fitting을 할 때 2차 다항함수 모델을 쓰나 10차 다항함수 모델을 쓰나 fitting결과는 비슷한 것과 비슷한 맥락이다(regularization이 잘 되어 있다는 가정하에).
- 그래서 depth-wise separable convolution을 적용해도 학습할 수 있는 global feature가 더 이상 없으니 depth-wise separable convolution을 적용하나 안하나 결과가 비슷한 것 같다. 다만 train data에만 적용되는 local feature는 train loss가 0이 될 때까지 계속 학습할 수 있으므로 depth-wise separable convolution을 적용했을 때의 train sentence accuracy가 더 높은 것이다.
- 이를 확인해보려면 데이터가 더 있어야 할 것 같다. fully connected로는 global feature를 더 학습하는 것에 한계가 있지만 depth-wise separable convolution으로는 fully connected보다 더 많은 global feature를 학습할 수 있을만큼 데이터가 많다면 확인해 볼 수 있을 것이다.
- 마침 다른 팀원분께서 128x128로 resize해서 사용하던 이미지 데이터를 64 x 256으로 resize 해보는 실험을 해보신 결과 validation data에 대한 점수를 크게 올려 주셨다.(validation sentence accuracy가 0.7401까지 올라갔다) validation data에 대한 점수가 올랐다는 것은 이렇게 resize 된 데이터에는 학습할 수 있는 global feature가 기존보다 더 많다는 것이므로 데이터를 늘리지 않고도 resize만 해서 위의 가설을 실험해 볼 수 있을 것 같다.
- 64 x 256으로 resize하고 depth-wise separable convolution적용하여 실험 해보니 validation sentence accuracy가 기존의 0.7404에서 0.7527까지 올라갔다
- CBAM 논문에서는 attention을 계산 할 때 spatial attention만 한게 아니라 아래 그림처럼 channel attention도 하였다.
- CBAM에서는 channel attention과 spatial attention을 번갈아 가면서 적용하는 방식으로 score를 올렸다고 한다.
- SATRN도 image를 input으로 받고 attention을 사용하는 모델이므로 CBAM의 channel attention을 SATRN 모델에도 적용하면 성능 개선에 도움이 될 수 있을것 같다.
- attention을 계산하는 방식은 꽤 다양한데 CBAM 에서는 Transformer에서와는 다른 방식의 attenton을 사용하고 있다.
- CBAM에서의 attention 계산방식이 파라미터 수가 더 적다. 현재 사용중인 모델을 기준으로 Transformer의 self attention으로 channel attention을 적용할 경우 필요한 파라미터수는 한번 channel attention을 할 때마다 대략 36만개(query, key, value에 대한 linear layer가 따로 존재하고 output linear layer도 존재)인 반면 CBAM에서의 방식을 쓰면 한번 channel attention을 할 때마다 20000개 정도의 파라미터가 필요하다(CBAM에서는 하나의 channle attn 모듈이 가지는 파라미터는 shared mlp밖에 없기 때문. mlp의 크기도 transformer의 self attention에서 linear layer의 크기보다 더 작다.). 다만 CBAM에서는 self attention을 하는 방식은 아니기 때문에 성능의 향상은 덜 할것이라 생각된다.
- 파라미터 수가 훨씬 작은 CBAM의 channel attention을 적용해 봐야 겠다.
- encoder layer에서 self attention에 들어가기 바로 전에 channel attention을 적용하도록 구현하였다(하나의 encoder layer 안에서 channel attn -> self attn -> feedforward 순으로 적용).
- 아래 그림에서 처럼 구현 하였다. 그림에서 w는 feature map의 값이고 p는 positional encoding 값이다.
- 시간을 아끼기 위해 10에폭으로 실험을 하였다. 실험 결과 점수가 하락하였다(SATRN이 기존 모델, SATRN_channel_attn이 channel attention을 적용한 모델).
- 아무래도 positional encoding때문인 것 같다. CBAM에서의 channel attention은 feature map에 max,avg pooling을 적용하는데 SATRN에서는 feature map에 2D positional encoding을 더하기 때문에 여기서 pooling을 하면 더해진 positional encoding값이 노이즈처럼 작용할 수 있을 것이다.
- feature map에 positional encoding을 더한 상태로 channel attention을 하면 position정보까지 고려해서 attention weight를 구할 수 있는게 아닐까?
- 자세히 생각해보면 그렇지 않은 것 같다.
- 위의 그림은 feature map에 positional encoding을 더하는 것을 나타내는 그림이다. shape이 (C,H,W)인 feature map에 같은 크기의 positional encoding을 더하고 있다.
- positional_encoding[:,0,0]은 h=0, w=0인 위치의 position을 나타내는 벡터이다(그림의 파란색으로 빗금친 부분). 이런식으로 하나의 position을 하나의 vector로 나타낸다.
- 여기에 channel attention을 적용할 경우 그림에서 feature map과 positional encoding을 더한 결과인 result의 각 채널에 대해서 pooling을 하게 된다. 즉, 그림에서 result에서 빨간색으로 빗금친 부분에 global pooling을 적용하여 하나의 값으로 만드는 식으로 총 C개의 값을 만들고 fully connected를 통과시켜 총 C개의 attention weight를 만들게 된다.
- 이때 pooling을 하는 경우를 잘 생각해 봐야 한다. 예를 들어 그림의 빨간색 빗금친 부분에서 pooling을 한다면 이렇게 하는게 position정보를 고려해서 pooling을 하는 것이라 할 수 있을까?
- 빨간색 빗금친 부분에는 각 position에 대한 positional encoding vector가 더해진 것이 아니다. 각 position에 대한 positional encoding vector의 첫번째 값들만 더해진 것이다.
- 즉, 빨간색 빗금친 부분에 대해서 pooling을 한 결과는 각 position에 대한 정보를 고려해서 pooling을 한 것이 아니라 각 position에 대한 positional encoding vector의 첫번째 값만 고려해서 pooling을 한 것이다.
- 첫번째 값만으로는 그 값이 어떤 position을 나타내는지를 알기는 힘들 것이다. 즉 더해진 첫번째 값들은 어떤 정보를 나타낸다고 보기 힘들다.
- 그래서 노이즈라고 생각하였다.
- 만에 하나 더해진 첫번째 값들이 position정보를 가지고 있다 해도 이상태에서 pooling을 하는 것 또한 맞지 않을 수 있다.
- 예를 들어 max pooling을 한다면 max pooling은 주어진 kernel size내의 feature 값들 중 가장 큰값을 대표로 쓰는 방식인데 feature에 position정보가 더해진 상태에서 max pooling을 할 때 feature자체의 값은 더 작은데 position값이 더 커서 제일 큰 값이 될 수도 있기 때문에 문제가 될 수 있다. 이 경우 또한 position정보가 노이즈 처럼 작용하고 있는 것이다.
- 다만 positional encoding값 자체는 -1~1사이의 작은 값이므로 크게 영향이 없을 수도 있다.
- channel attention을 적용하기 위해서는 positional encoding을 따로 적용할 수 있어야 한다.
- 즉 positional encoding을 더하기 전의 feature map에 channel attention을 적용하고 그 뒤에 positional encoding을 적용해야 한다.
- 그냥 생각해보면 positional encoding을 더하기 전에 channel attn을 하고 그 뒤에 positional encoding을 더하면 될 것 같지만(아래 그림에서 처럼), 이는 encoder layer가 1개 일때만 가능할 것 같다. encoder layer가 2개 이상이라면 이러한 방식을 적용해도 첫번째 layer의 output에는 positional encoding이 더해진 상태이기 때문에 두번째 layer에서의 channel attention은 positional encoding이 더해진 상태에서 적용되게 된다. 사실 이렇게 해도 될지 안될지는 좀 애매하기 때문에 실험을 해봐야 할 것 같다(결국 시간이 없어서 못했음).
- 관련된 논문으로 positional encoding을 따로 적용할 수 있는 방식을 소개하는 논문을 찾을 수 있었다.(https://arxiv.org/abs/2006.15595)
- 논문 자체의 초점은 positional encoding을 따로 적용하는 것 보다는 현재의 positional encoding 적용방식 자체의 문제점을 지적하고 이를 해결하는 방안을 소개하는 논문이지만 해결 방식에서 positional encoding을 따로 적용해주는 방식을 쓰고 있다(자세한 설명은 여기 참고 바람).
- 해당 논문에서 제시한 방법을 사용하면 positional encoding에 대한 query,key의 내적을 따로 미리 계산해 두고 positional encoding이 더해지지 않은 feature map을 channel attention으로 먼저 통과시켜 주게 된다. 그 다음 self attention 단계에서 feature map만을 이용하여 query, key ,value를 만들고(이전 방식에서는 feature map에 positional encoding을 더한 것으로 query, key, value를 만들었다) query,key의 내적를 계산한다. 그 후 여기에 미리 계산해놓은 positional encoding에 대한 query,key의 내적을 더해주게 된다. 아래 그림에서 왼쪽이 기존 방식이고 오른쪽이 논문에서 제시한 방식을 적용한 것이다.
- 이런식으로 channel attention을 적용하는 단계에서 positional encoding의 영향을 받지 않게 할 수 있다.
-
positional encoding을 따로 적용한 상태에서 channel attention을 적용함으로써 accuracy를 올릴 수 있었다(SATRN: 기존모델, SATRN_channel_attn_PE: channel attention을 적용하고 positional encoding을 따로 적용한 모델).
-
근데 점수가 올라간 게 channel attn을 적용한 것때문이 아니라 positional encoding을 따로 적용해서 올라간게 아닐까 하는 생각이 들어서 따로 더 실험을 해보았다.
-
기존 모델에 channel attention은 적용하지 않은 상태로 positional encoding을 따로 적용한 것도 실험해 보았다(SATRN: 기존모델, SATRN_PE: positional encoding을 따로 적용한 모델).
- positional encoding을 따로 적용해도 점수가 오른다. 다만 여기에 channel attention을 적용해 주면 더 올릴 수 있다.
- 학습이 완료된 모델로 validation dataset에 대해서 inference를 한 후 잘 맞추는 데이터와 못 맞추는 데이터를 나눠서 살펴보았다.
- 대체적으로 잘 맞추는 데이터는 sequence 길이가 짧고 잘 못 맞추는 것은 sequence 길이가 긴 편이다. 못맞춘 데이터들 중에 sequence가 짧은 데이터의 경우 같은 토큰을 연속으로 예측해서 아쉽게 틀리는 경우와 대소문자를 잘못 구분하거나 비슷한 다른문자로 인식하는 경우(근데 직접 눈으로 봐도 헷갈리만 하다)도 많았다.
- 아래 그림은 맞춘 데이터(correct)와 틀린 데이터(error)의 길이 분포이다. x축은 sequence length이고 y축은 데이터 갯수이다.
- 긴 sequence일수록 모든 time step에서 정확한 토큰을 예측하여 정확히 문장전체를 예측하기가 어려워 진다(문장 전체를 정확히 예측할 확률인 joint probability자체가 낮아질 수 밖에 없음).
- 일단 beam search를 이용하면 greedy보다는 확률이 높은 sequence를 찾을 수 있기 때문에 긴 문장을 예측하는데 도움이 될 것 같다. 또한 같은 토큰을 연속해서 예측한 결과 뿐만 아니라 다른 예측결과도 찾게 되므로 같은 토큰을 연속으로 예측하는 경우도 줄여줄 수 있을 것 같다.
- 시간이 부족하여 구현하지 못하였다.
- positional encoding에도 dropout을 적용하는게 맞는걸까?
- positional encoding vector가 나타내는 것은 position정보 뿐이다.
- dropout을 적용하면 제거되지 않은 값들은 (1 / (1-dropout rate))가 곱해져서 rescale된다.
- 특정 위치의 positional encoding vector에 대해서 매번 다른 값이 제거되고 rescale될 경우 같은 위치를 매번 다른 벡터로 나타내게 된다.
- 예를 들어서 첫번째 position을 나타내는 벡터가 [1, 1, 1, 1, 1]이고 dropout을 0.1확률로 적용한다면 어쩔때는 [0, 1.111, 1.111, 1.111, 1.111]이 첫번째 포지션을 나타내게 되고 어쩔때는 [1.111, 1.111, 1.111, 0, 1.111]이 첫번째 포지션을 나타내게 될 것이다.
- 이건 잘못된게 아닐까 싶다.
- 실험해보고 싶은데 시간이 없다.
- [8월 17일 업데이트] 조교님께 질문해보니 positional encoding에 적용되는 dropout은 column단위가 아니라 row단위로 적용된다고 한다. 즉, 각 positional encoding벡터에 대해서 벡터의 원소를 랜덤하게 dropout시키는게 아니라 positional encoding 벡터 자체를 랜덤하게 drop out 시킨다고 한다(부캠이 끝난 뒤에도 질문을 계속 받아주신 조교님께 정말 감사드린다).
- 직접 transformer 코드를 찍어보면서 확인하셨다고 한다. 나도 직접 찍어보고 확인해봐야 겠다.
- teacher forcing ratio를 조금씩 낮추는 방식을 실험해보고 싶었는데 못했다
- 1등팀은 teacher forcing ratio를 cos형태로 낮추는 방식을 사용하였는데 점수가 크게 올랐다고 한다. 일반화성능을 높이는데 좋은 기술인 것 같다.
- 90도, 180도, 270도 돌아간 데이터들에 대해서 주어진 이미지가 몇도 돌아간 이미지인지 분류를 해주는 모델을 학습해보지 못한게 아쉽다. 이러한 간단한 분류기로 90도, 180도, 270도 돌아간 이미지같은 아웃라이어를 커버해줄 수 있었을 것 같다.