Open chullhwan-song opened 5 years ago
이는 RNN계열을 사용하지 않아서 발생한 문제라 볼수있다.
def position_encoding(sequence_length, dim_model):
# First part of the PE function: sin and cos argument
position_enc = np.array([[pos / (10000 ** (2 * i / dim_model)) for i in range(dim_model)] for pos in range(sequence_length)])
# Second part, apply the cosine to even columns and sin to odds.
position_enc[:, 0::2] = np.sin(position_enc[:, 0::2]) # dim 2i
position_enc[:, 1::2] = np.cos(position_enc[:, 1::2]) # dim 2i+1
output = tf.convert_to_tensor(position_enc, dtype=tf.float32)
return output
def get_angles(pos, i, d_model):
angle_rates = 1 / np.power(10000, (2 * (i//2)) / np.float32(d_model))
return pos * angle_rates
def positional_encoding(position, d_model): angle_rads = get_angles(np.arange(position)[:, np.newaxis], np.arange(d_model)[np.newaxis, :], d_model)
sines = tf.math.sin(angle_rads[:, 0::2])
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)
* 소스
def scaled_dot_product_attention(query, key, value, mask): matmul_qk = tf.matmul(query, key, transpose_b=True)
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)
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 # 최종 결과 리턴