레이블이 attention인 게시물을 표시합니다. 모든 게시물 표시
레이블이 attention인 게시물을 표시합니다. 모든 게시물 표시

2020년 11월 8일 일요일

가장 짧게 transformer 설명

어떤 시계열 데이터가 있는데, 시계열 길이는 32이고 속성 수는 16개라 가정하자.  

그러면 데이터 shape은 original=(bs, slen=32, nfeat=16)이다. bs는 batch_size이다. 여기서, 첫번째 데이터인 (32,16) 행렬만 추출하여 tansformer연산을 해보자.


1. (32,16)을 속성 수가 16이 아닌 8로 만드는 linear층을 적용한다. 물론 8은 바꿀수 있다. 

그러면 데이터는 (32,8)이된다. 그리고 속성은 더이상 origianl이 아닌 latent텐서가 된다. 


2. transformer의 self attention사용할 예정이므로 1에 설명된 방법으로 original로부터 (32,8)인 query, key, value 텐서 3개를 만든다. 


3. 먼저 query와 key에 대해 attention(가중치) 연산을 수행한다 (self attention연산 이해는 이미지를 활용한 아래 사례를 참조한다). 

query*key'=(32,8)x(8,32)을 하면 (32,32)크기가 나온다. 이것은 시간 t=i에서 8개 속성과 t=j에서 8개 속성을 유사도 비교 연산한 것이다. 

즉, 서로 다른 시간에서 신호 속성들의 유사도를 따지는 것이다. 현 신호는 이전 어떤 시간대에서 관련성이 있는지를 평가한다. 


따라서 (32,32)행렬 첫 행은 t=1(8속성)과 t=1,2,3,...,32까지를 비교한 값들이고, 이 값을 정규화(softmax)하여 저장한 것이다. 

이 값을 value의 첫번째 속성인 (32,1)에다 곱해준다: a1*v1+a2*v2+a3*v3+...+a32*v32


또, (32,32)행렬의 두번째 행은 t=2(8속성)와 t=1,2,3,...,32까지 속성을 비교한 것이다. 이 값도 마찬가지로 

value의 두번째 속성인(32,2)에 있는 32개 벡터와 차례로 곱해준다: a1*v1+a2*v2+...+a32*v32



4. 이런 식으로 연산을 하면 결과 행렬식 (32,8)이 생기는데, 이것이 한개의 self-attention head이다. 


5. attention head는 보통 여러개가 되고, 이를 함께 연결하여(concat) 큰 행렬 하나로 묶어 내고, 이를 linear층을 통과시켜 original data와 크기가 같은 (32,16) 행렬을 만든다. 


6. original 입력(32,16)은 attention head 출력(32,16)과 더해주는데 이는 original data의 크기값(scale)이 Attention Head 출력에 잘 반영되도록 하기 위함이다.



(사례)



그림은 Input sequence에 대한 Transformer 연산을 좀 더 쉽게 이해하기 위해 dog and dog 이미지를 이용하여 self attention 방법을 보여준다. 이미지를 4개의 patch로 분할하여 patch가 나타난 순서대로 일렬로 세워서 벡터로 만들고 이를 곱해준다. 

곱한 결과인 softmax($QK^T$) 행렬을 보면, 


- 자기 자신 patch끼리의 상관성 높음 (대각방향)    

- Noisy한 배경 끼리의 상관성은 낮음 (비대각 방향)

- Patch 2와 Patch3은 둘 다 dog이므로 상관성 높음 ($a_3^2$와 $a_2^3$ 높음)

- $V$와 곱하면 Output 2, 3 만 높음 (attention 됨). dog가 있는 patch가 attention 되었음. 

- 입력에서 반복 출현하는 유의미(salient) 객체는 상관성 높음 (자기 상관성이던 다른 dog와 상관성이던 배경보다 높아짐)

 

Output행렬의 첫 행을 보면 t=1~4의 4개의 데이터 $V$에 attention(가중치) 값 $a_j^i$값이 곱해진 형태이다. 이것은 sequence 데이터(t=1~4)에 가중치 weight($a_j^i$)가 곱해진 것이며, fully connected net이나 convolution연산과 모양이 동일하다.  

Transformer는 학습되고 나면 고정된 가중치를 가지는 FCN이나 CNN구조와 달리, 위 그림에서 나타나는 가중치 $a_j^i$처럼 Input의 함수이다. 달라지는 Input에 따라 고정된 값이 아니라 바뀌는 값이다. 따라서 FCN이나 CNN에 비해 inductive bias가 휠씬 적게 된다 [1]. 



Reference

1. PR-297 Youtube

2. ViT pytorch 코드 리뷰 (github 원본)

2018년 5월 8일 화요일

Attention

$\underline{Dense\ attention}$

입력 특징(속성)의 어떤 하나에 attention

inputs = Input(shape=(32,)) #32개 중 중요한 것 찾음.

# Dense출력수는 inputs수와 같고 softmax로 확률값 만듬
attention_probs = Dense(32, activation='softmax')(inputs) #attention vector

#dense출력을 inputs에 곱해 어떤 값에 attention할지 $\mathit{dense가\ 학습}$
attention_mul = merge( [inputs, attention_probs], output_shape=32, mode='mul')

# attention된 vector를 분류기에 넣음
attention_mul = Dense(64)(attention_mul)
output = Dense(1, activation='sigmoid')(attention_mul)

model = Model(input=[inputs], output=output)



$\underline{시계열\ attention}$

Timestep 어떤 순간에 attention하게 한다.

# 20 Timestep 중 중요한 step
# inputs.shape = (batch_size, TIME_STEPS, input_dim) #(None,20,32)

# Timestep <-> 특징축 교환
a = Permute((2, 1))(inputs) #(None,32,20)

#Timestep축 값 20에 대한 정규화
a = Dense(TIME_STEPS, activation='softmax')(a) #(None,32,20):

# 다시 되돌림
a_probs = Permute((2, 1))(a) #(None,20,32)

#두 값 곱함:(None,20,32)
#Timestep 어디에 attention할지 $\mathit{dense가\ 학습}$
output_attention_mul = merge([inputs, a_probs], mode='mul')



$\underline{영어-일어\ 번역}$

$\mathit{먼저\ encoder-decoder\ 구조\ 만듬}$

# (None,20) -> (None,20,64): 20자 영단어를 64차원 임베딩
encoder = Embedding(54, 64, input_length=20, mask_zero=True)(encoder_input)

# 매 Timestep마다 출력(64개)
encoder = LSTM(64, return_sequences=True, unroll=True)(encoder)

#맨 마지막 timestep 값(None, 64)
encoder_last = encoder[:,-1,:]

# (None,20) -> (None,20,64) #20자 일단어를 64차원 임베딩
decoder = Embedding(89, 64, input_length=20, mask_zero=True)(decoder_input)

# encoder 출력 (None,64) 2개를 초기 state로 입력
decoder = LSTM(64, return_sequences=True, unroll=True)(decoder, initial_state=[encoder_last, encoder_last])



$\mathit{attention\ 기능\ 부가}$

###################################################
# encoder, decoder 출력은 같은 뜻이니 서로 유사해야 한다고 하면
# 영어 추출한 64개 속성과 일어추출 64 속성은 비슷해야 한다.
# -> 둘 다 2번축끼리 곱함: (None,20,64)x(None,20,64)->(None,20,20)
#
# 아래 코드 보면 decoder가 앞, encoder가 뒤 이므로 출력 Tensor는 (None,20,20)이고 
# 1번축 20개는 decoder timestep, 2번축 20개는 encoder timestep이면서 pdf로 정규화.
# 1번축 $i$번째 step입력에 대응되는 encoder 20개 입력간 유사성을 pdf로 표현한다.
attention = dot([decoder, encoder], axes=[2, 2])

# (None,20,20) 속성 2번축 20개 값 정규화
attention = Activation('softmax', name='attention')(attention)

# 이는 encoder 출력 중 어디에 집중해 주어야 하는지를 모델링
# attention=(None,20,20) 2번축 x encoder=(None,20,64) 1번축
#    -> (None, 20,64)
# encoder timestep에 대한 prob.
context = dot([attention, encoder], axes=[2,1])
#
#
# attention된 encoder features + decoder features 결합
# encoder 집중화 출력(context정보)과 decoder출력을 묶음
# (None,20,64)+(None,20,64) -> (None,20,128)
decoder_combined_context = concatenate([context, decoder])


# Has another weight + tanh layer as described in equation (5) of the paper
# 128 -> 64 -> 89: 3층 분류기
output = TimeDistributed(Dense(64, activation="tanh"))(decoder_combined_context)
output = TimeDistributed(Dense(89, activation="softmax"))(output)

# 모델
model = Model(inputs=[encoder_input, decoder_input], outputs=[output])
model.compile(optimizer='adam', loss='binary_crossentropy')


$\cdot$앞에서는 input를 dense에 넣어 input 어디에 attention할지를 dense가 학습
$\cdot$여기서는 dense 대신 decoder 사용. 즉, decoder출력 만드는 RNN 후반부가 attention 학습

$\cdot$영어->일어 번역으로 encoder에 영어입력, decoder에 일어 입력이다.
$\cdot$일어입력 20 timestep 어떤 순간 특징 64개는 영어 20 timestep 각 순간 특징(64개)와 유사성 체크.
(ex.) 일어 5 step째 입력과 영어 0~19 step별 유사성: decoder X encoder이므로 (None,5,:)에 저장.
       일어 7 step째 입력과 영어 0~19 step별 유사성: (None,7,:)에 저장.