chullhwan-song / Reading-Paper

151 stars 26 forks source link

BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding #202

Open chullhwan-song opened 5 years ago

chullhwan-song commented 5 years ago
chullhwan-song commented 5 years ago

Abstract

Bert에서의 Transformer 적용 - Transformer

Input

Positional Embedding

def positional_encoding(position, d_model): angle_rads = get_angles(np.arange(position)[:, np.newaxis], np.arange(d_model)[np.newaxis, :], d_model)

배열의 짝수 인덱스(2i)에는 사인 함수 적용

sines = tf.math.sin(angle_rads[:, 0::2])

배열의 홀수 인덱스(2i+1)에는 코사인 함수 적용

cosines = tf.math.cos(angle_rads[:, 1::2]) pos_encoding = np.concatenate([sines, cosines], axis=-1) pos_encoding = pos_encoding[np.newaxis, ...]
return tf.cast(pos_encoding, dtype=tf.float32)

이를 실행

pos_encoding = positional_encoding(50, 512) print (pos_encoding.shape) # 크기 출력

plt.pcolormesh(pos_encoding[0], cmap='RdBu') plt.xlabel('Depth') plt.xlim((0, 512)) plt.ylabel('Position') plt.colorbar() plt.show()

   * 소스2를 보면
        * 50 × 512의 크기를 가지는 포지셔널 인코딩 행렬을 시각화
        * 각 단어가 512차원의 임베딩 벡터를 가질 때 사용
![image](https://user-images.githubusercontent.com/40360823/65472343-fd2ffd80-dead-11e9-9a0d-78d3bdfec165.png)
#### BERT 의 INPUT
* bert는 transformer와 달리, Positional Encoding이 아니라 Positional Embedding 용어로 사용한다라고 언급
    * 실제론   Positional Encoding == Positional Embedding 같은 의미인듯.? 
    *  하다보니, 구성에  잘 안맞게 위의 Position Embeddings를 먼저 설명하는 모양새가 됬다.
![image](https://user-images.githubusercontent.com/40360823/65025828-61b10100-d972-11e9-97d4-f9d9b7fa3bc8.png)
    * 위의 그림에서 첫번째 입력값을 보면, 즉, Sentence pair는 합쳐져서 single sequence로 입력. 다시 말해 그 입력값은 여러개의 문장으로 이루어질수 있고 단 하나의 문장일수도 있다. 
       *  다만, 두개의 문장을 구분하기 위해 [SEP] token을 적용. > QA의 경우 [Question, Paragraph]인데 이를 구분할때도 적용한다.
        * 뒤에 나오지만 또한번 문장을 구분하기 위해, Segment embedding를 적용한다.
    * 바로 위의 그림을 보면, bert는 Token Embeddings+Segment Embeddings+Position Embeddings형태로 확장
    * Token Embeddings는 이미 첫번째 이미지로 이미 설명
    * Segment embedding ? 
       * 이 경우  갑자기 두개의 sentence (Pair)를 받는다고 나와있어서..당황스러운데(위에서 설명)..그건 실제 코드에서..확인해야할듯.. 
       * Segment embedding을 사용하여 앞의 문장에는 sentence A embedding, 뒤의 문장에는 sentence B embedding을 더해줍니다.(모두 고정된 값) ? [BERT 논문정리](https://mino-park7.github.io/nlp/2018/12/12/bert-%EB%85%BC%EB%AC%B8%EC%A0%95%EB%A6%AC/?fbclid=IwAR3S-8iLWEVG6FGUVxoYdwQyA-zG0GpOUzVEsFBd0ARFg4eFXqCyGLznu7w) 에서 이런말을 했는데..??
       * 일단, 두개의 문장이 서로 의미적으로 유사한지 판단하기 위해, 
       * 예를들어,  “I like cats”, “I like dogs” 이면, 
![image](https://user-images.githubusercontent.com/40360823/65101135-4b06ba80-da03-11e9-970e-75c20f475273.png)        
            * 그림에서보면, 나머지 두개의 embedding과 shape(or dim)을 맞추기위해,  0와 1로 채우는듯하다. 즉, 768차원을 문장 구분하여 첫번째 문장에 0으로, 두번째 문장에 1로, 세번째 문자에 2으로..채우는듯하다.(더 봐야함.)
                * 참고로 2가 아니라 0과 1로만 할수도 있을수도...? 좀더 이부분확인필요!!
    * Position Embeddings > 위에서 설명

#### Transformers
* Multi Head Attention   
![image](https://user-images.githubusercontent.com/40360823/65212988-50393780-dadf-11e9-8950-0a7b8ed63c5d.png)
    * Scaled Dot Product Attention > Multi Head Attention를 구성하는 core concept
    * 자세히 보면 입력값이 3개: K? Q? V? >짜증나게 논문에서는 설명이 거의 없다.         
       * Q: Query vector,  K:Key vector, V: Value vector > 라고 나와있는데 당췌!!! 와닿지 않는다. 
![image](https://user-images.githubusercontent.com/40360823/65213347-c7230000-dae0-11e9-92e2-cd0d2ff3a104.png)
          * 같은 입력값에 각각 3개의 linear transformation 나온 결과를 K, Q, V로 
       * 그래서,  왼쪽 그림 : Scaled Dot Product Attention 을 다음 수식으로 나타내고, 하나의 Head를 의미.
![image](https://user-images.githubusercontent.com/40360823/65213524-6ea03280-dae1-11e9-958c-d46dd0781cbf.png)
![image](https://user-images.githubusercontent.com/40360823/65232078-85f41580-db0b-11e9-8786-96fd4aaae75c.png)
       * 오른쪽 그림은 이 Scaled Dot Product Attention를 concat한 결과, 즉 **Multi Head Attention**에 대한 수식은
![image](https://user-images.githubusercontent.com/40360823/65213567-92fc0f00-dae1-11e9-907c-1dab028cd0fd.png)
       * 이 그림보면 구조에 대한 이해도가 올라갈듯~
![image](https://user-images.githubusercontent.com/40360823/65223601-7f11d680-dafc-11e9-9c64-1125ad913c8b.png)
    * 다시 > Q, K, V에 대해 알아보자..당췌 몬지 모르겠다. 여기서 부터는  [Attention 개념정리 부제 : 게임의 구조로 해석한 attention](http://www.modulabs.co.kr/?module=file&act=procFileDownload&file_srl=20755&sid=453cc69bc33037b5ff7a4e635cc1655e&module_srl=17958)를 자료를 참조함.
       * 앞에서 보면, Q,K,V > 원래 BERT의 입격값 = Token Embeddings+Segment Embeddings+Position Embeddings = X를 각각 이X 받아 각각 독립된 변수를 가진 linear transform(wx+b)를 실행한 결과가 Q,K,V 라고 이해가 된다.
        * Q, K의 관계가 중요한것같다.
        * Q는 어떤 정해진 frame같고, K는 그 Q안에 들어가야할 적당(어울리는)한 값들을 의미하는것같다.    
           * 야구라고 가정하면, 이미 룰에 의해 정해진 1~9번타자가 존재하는데 바로 이 틀을 Q = Qi =  {Q1,,Q9}
               * 어떠한 기준을 의미하는듯 > 최적의 값은 정해져 있지 않고, 어떤 상대팀을 만나느냐에 따라 변한다.
           * Q는 이틀에 적당한 실 선수들 : Xj = { K1,,, k9}  > 선수들이므로 꼭 9명은 아닐듯..더많을수도
           * 그래서, attention관점에서보면 Ci = Attn(Xj, Qi) > **이는 각 야구에서의 포지션별 적합도 즈그 cosine distance( dot product )에 의해 그 적합도를 판단** > 위의 그림에서, MatMul
        * 앞에서 Q와 K의 관계가 중요하다고 했는데, 이를 다시 말한다면, **Q-K의 alignment**가 매우 중요하다는것을 의미한다.
        * 이제 야구가 아니고,  문장의 입장에서 보면, Q는 품사(주어, 목적어, 동사..등 또한 품사는 문장의 위치와도 매우 관련성이 크다?) 정도 일거고, 들어오는값 >(X가 hidden layer를 통해 변형된 S(X....=====...S)>) K가 어떤 품사 Q 적합한지에 대한 relation이라고 봐야할듯하다.
        * 이 그림을 자세히 보면 이해가 더 쉬울수 있다. 위의 attention 수식과 맞물려 생각하면 좋음.
![image](https://user-images.githubusercontent.com/40360823/65232397-09156b80-db0c-11e9-8974-2a9721626c0e.png)
        * Q와 K는 초기의 attention 연구:[Neural Machine Translation by Jointly Learning to Align and Translate 간단리뷰](https://github.com/chullhwan-song/Reading-Paper/issues/34)와 비교해보면 다음과 같다는 의미.
![image](https://user-images.githubusercontent.com/40360823/65231876-2a298c80-db0b-11e9-80f9-55fb766c5f32.png)
        * 이제 attention 수식은 다음과 같이 생각해볼수 있다.
![image](https://user-images.githubusercontent.com/40360823/65238094-f05e8300-db16-11e9-905e-a8ea5b41359b.png)
            * X가 각 품사에 대한 확률(attention)에 대한 곱으로 정의(확장..) > 위의 그림/수식들을 상기시켜보면 알수 있음. > X가 Q에 alignment될 확률 P를 곱하다는 의미
            * Q-K의 relation인 Z를 discrete한 latent variable로 생각한다면, atttention p(z|x, q)는 CRF(conditional random field)로 볼 수 있다.
* 한번더!!!! : [딥 러닝을 이용한 자연어 처리 입문 14. 트랜스포머(Transformer) 1) 트랜스포머(Transformer)](https://wikidocs.net/31379) 포스트를 보고, 좀더(다른관점?에서) 이해해보자.
   * 입력 - 위의 Positional Embedding > 소스2
   * 자세히 보면 입력값이 3개: K? Q? V? >짜증나게 논문에서는 설명이 거의 없다. (위에서부터 다시..다른설명)
      * attention함수는  주어진 '쿼리(Query)'에 대해서 모든 '키(Key)'와의 유사도를 각각 구하고, 
      * 이를(이 유사도를) 가중치로 하여 Key와 맵핑되어있는 각각의 '값(Value)'에 반영,  
      * 그리고 유사도가 반영된 '값(Value)'을 모두 가중합하여 리턴.
      * 이는 wx+b형태고 생성.
![image](https://user-images.githubusercontent.com/40360823/65473021-0c647a80-deb1-11e9-8f71-8258bde9e973.png)
         * 논문과 같이 dmodel=512이고 num_heads=8라면, 각 벡터에 3개의 서로 다른 가중치 행렬을 곱하고 64의 크기를 가지는 Q, K, V 벡터를 획득 = 64*8=512         
    * Scaled dot-product Attention
       * q*k : 어떤 k와 유사한지 check
       * 여기에 scaling 개념 추가 > 위의 Q, K, V의 차원 n=64의 값에다 root를 쒸어 나누어줌. > 실제적으론 dk이므로 > K의 차원임.              
![image](https://user-images.githubusercontent.com/40360823/65473569-564e6000-deb3-11e9-8fdc-2aeabd89999a.png)
         * 이는 Attention score를 계산하는 과정임
         * "I  am  a student" 에 대한 예
         * Q(query):단어의 I는 K에 대한 연관된 정도(attention weight)를 계산 > I, am, a, student를 각각 계산한다.
         * (query):단어의 I에 대해서만 나오는데, 나머지도 똑같은과정을 걷쳐야한다.
       *  Attention Score를 계산했으니, attention distribution을 계산. > softmax을 이용함 이후 . >이 attention distribution x V 를 계산하면  attention value를 계산
            * 정리 : Attention Score(Q, K) >Attention distribution = softmax(Attention Score(Q, K)) > Attention value =  softmax(Attention Score(Q, K))xV
![image](https://user-images.githubusercontent.com/40360823/65474036-3d46ae80-deb5-11e9-8201-f9bba03a1d10.png)
                * 언급한데로 Q:단어I 외, 다른 단어들도 똑같은 과정을 걷힘.
         * 위의 과정을 따로따로 아닌 행렬단위로 한다면(실제 코딩에서는 당연히 이렇게..)
![image](https://user-images.githubusercontent.com/40360823/65474229-04f3a000-deb6-11e9-9215-ff851c5a69e5.png)
![image](https://user-images.githubusercontent.com/40360823/65474231-07ee9080-deb6-11e9-93a5-532ae43ae4fe.png)
![image](https://user-images.githubusercontent.com/40360823/65474234-0ae98100-deb6-11e9-906e-7a4143196c7b.png)
          * 소스

Q 행렬, K 행렬, V 행렬, 마스크 수행 여부

def scaled_dot_product_attention(query, key, value, mask): matmul_qk = tf.matmul(query, key, transpose_b=True)

Q 행렬과 K 행렬을 곱한다. 즉, 어텐션 스코어 행렬을 얻는다.

dk = tf.cast(tf.shape(key)[-1], tf.float32) logits = matmul_qk / tf.math.sqrt(dk)

스케일링.

if mask is not None: logits += (mask * -1e9)

필요하다면 마스크를 수행한다. 해당 조건문이 어떤 의미인지는 뒤에서 설명하며 현재는 무시.

attention_weights = tf.nn.softmax(logits, axis=-1)

소프트맥스 함수를 사용하여 어텐션 가중치들. 즉, 어텐션 분포를 얻는다.

output = tf.matmul(attention_weights, value)

어텐션 분포 행렬과 V 행렬을 곱하여 최종 결과를 얻는다.

return output, attention_weights

최종 결과와 어텐션 분포 리턴. 어텐션 분포 또한 리턴하는 이유는 아래에서 값을 출력해보며 함수 테스트를 위함.

    * Multi-head Attention > Scaled dot-product Attention를 여러번? 실행
        * dmodel 의 차원을 가진 단어 벡터를 num_heads로 나눈 차원을 가지는 Q, K, V벡터로 바꾸고 어텐션을 수행
            * 논문에서는 512의 차원의 각 단어 벡터를 8로 나누어 64차원의 Q, K, V 벡터로 바꾸어서 어텐션을 수행
            * 그럼 8개의 multi-head(=num_heads)
![image](https://user-images.githubusercontent.com/40360823/65475797-ccef5b80-debb-11e9-8c8c-746c6870e8b5.png)
            * 이 결과(num_heads=8)의 각 결과들을 concat
![image](https://user-images.githubusercontent.com/40360823/65475844-f7411900-debb-11e9-81cb-ed5c872b884a.png)
            * 소스

class MultiHeadAttention(tf.keras.layers.Layer):

def init(self, d_model, num_heads, name="multi_head_attention"): # 정의하기 super(MultiHeadAttention, self).init(name=name) self.num_heads = num_heads # 8 self.d_model = d_model # 512

assert d_model % self.num_heads == 0

self.depth = d_model // self.num_heads

self.query_dense = tf.keras.layers.Dense(units=d_model) #WQ
self.key_dense = tf.keras.layers.Dense(units=d_model) #WK
self.value_dense = tf.keras.layers.Dense(units=d_model) #WV

self.dense = tf.keras.layers.Dense(units=d_model) #WO

def split_heads(self, inputs, batch_size): # 아래의 call 함수에서 헤드를 나누기 위해서 호출 inputs = tf.reshape( inputs, shape=(batch_size, -1, self.num_heads, self.depth)) return tf.transpose(inputs, perm=[0, 2, 1, 3])

def call(self, inputs): query, key, value, mask = inputs['query'], inputs['key'], inputs[ 'value'], inputs['mask'] batch_size = tf.shape(query)[0]

# 1. WQ, WK, WV에 해당하는 밀집층 지나기
query = self.query_dense(query) # (batch_size, seq_len, d_model) 
key = self.key_dense(key) # (batch_size, seq_len, d_model)
value = self.value_dense(value) # (batch_size, seq_len, d_model)

# 2. 헤드 나누기
query = self.split_heads(query, batch_size) # (batch_size, num_heads, seq_len, d_model/num_heads)
key = self.split_heads(key, batch_size) # (batch_size, num_heads, seq_len, d_model/num_heads)
value = self.split_heads(value, batch_size) # (batch_size, num_heads, seq_len, d_model/num_heads)

# 3. 스케일드 닷 프로덕트 어텐션. 앞서 구현한 함수 사용.
scaled_attention = scaled_dot_product_attention(query, key, value, mask)
scaled_attention = tf.transpose(scaled_attention, perm=[0, 2, 1, 3])

# 4. 헤드 연결(concatenate)하기
concat_attention = tf.reshape(scaled_attention,
                              (batch_size, -1, self.d_model))

# 5. WO에 해당하는 밀집층 지나기
outputs = self.dense(concat_attention)

return outputs # 최종 결과 리턴
chullhwan-song commented 4 years ago

궁금중..

chullhwan-song commented 4 years ago

PPT정리