반응형

어텐션 메커니즘 (Attention Mechanism)

  • seq2seq 모델의 문제점 : 전체 문장에 대해서 context를 단 한 번에 넘겨줌
  • 매 순간마다 데이터를 다 넣어주기 위해 사용

 

Seq2seq model

  • 전체 문장에 대한 정보를 한꺼번에 벡터 하나로 만들어서 넘겨주니까 token에서의 정보는 하나도 안 남아있고, 전체 문장에 대한 Context만 넘어감

 

Attention은 Seq2seq model을 보완하고자 만든 것

 

 

Attention

  • 쿼리에 대해서 모든 키와의 유사도를 각각 구하고, 유사도를 키와 맵핑되어 있는 각각의 value에 반영
  • Query : t 시점의 디코더 셀에서의 은닉 상태
  • Keys : 모든 시점의 인코더 셀의 은닉 상태
  • Values : 모든 시점의 인코더 셀의 은닉 상태

 

→ Attention Value는 Query랑 Key가 들어가서 Value가 곱해져서 다 합쳐진 거임 (먼 소리야ㅎㅎ,,)

 

 

Dot-product Attention

  • 매 순간마다 앞에 있던 것을 다 같이 보겠다는 것
  • 기본적으로 곱하기임

 

→ softmax Layer에서 더 연관이 있는 단어에 weight를 더 많이 줘서 weight들을 전부 더해서 다음에 나올 단어를 예측한다.

 

그 외의 Attention 종류들

 

 

 

Attention 실습

  • 전에 만들었던 glove_lstm_classification.ipynb 에 attention을 추가해서 만듬
    • 다른 거는 똑같고, model 쪽에 attention 추가
from collections import Counter
import urllib.request
import pandas as pd
import numpy as np
import tensorflow as tf

import nltk
from nltk.tokenize import sent_tokenize
from nltk import WordPunctTokenizer

nltk.download('punkt')
from google.colab import drive
drive.mount('/content/drive')
# spam classification data loading
urllib.request.urlretrieve("https://raw.githubusercontent.com/mohitgupta-omg/Kaggle-SMS-Spam-Collection-Dataset-/master/spam.csv", filename="spam.csv")
data = pd.read_csv('spam.csv', encoding='latin-1')
data[:5]
texts = list(data['v2'])
labels = list(data['v1'])

print(texts[:5])
print(labels[:5])

print(Counter(labels))
# glove vector model initialize
glove = {}
with open('/content/drive/My Drive/Colab Notebooks/data/news_sample/glove.6B.50d.txt', 'r', encoding='utf-8') as fr:
    for line in fr.readlines():
        temp = line.strip().split()
        word = temp[0]
        vector = temp[1:]

        glove[word] = list(map(float, vector))
def tokenize(document):
  words = []
  sentences = sent_tokenize(document) # sentence tokenizing

  for sentence in sentences:
    words.extend(WordPunctTokenizer().tokenize(sentence)) # word tokenizing

  return [word.lower() for word in words] # case normalization

def get_vector(sentence):
  tokens = tokenize(sentence)
  vector = [glove[token] if token in glove.keys() else [0]*50 for token in tokens]

  while len(vector) < 256:
    vector.append([0] * 50)
  
  return vector[:256]
x = [get_vector(text) for text in texts]
x_train, x_test = np.array(x[:5000]), np.array(x[5000:])
y = [0 if label == 'spam' else 1 for label in labels]
y_train, y_test = np.array(y[:5000]), np.array(y[5000:])

 

반응형
  • 모델 생성
    • tf.keras.layers.LSTM(128) → 자동으로 LSTM이 다대일로 해서 제일 마지막 결과만 여기 layer에서 출력해준다.
    • (tf.keras.layers.LSTM(128, return_sequences=True))
      → 모든 칸에 대해서 반환
    • Query밖에 없고 Key와 Value가 필요한 경우
      → 세 개를 똑같은걸 넣어줄 때 salf-attention이라고 함
    • tf.keras.layers.Attention()([lstm_layer, lstm_layer, lstm_layer])
      → 원래는 Query, Key, Value 순서대로 넣어야 되는데 지금 Query 하나밖에 없기 때문에 똑같은 거를 넣어줌,, 이거를 salf-attention이라고 함
input_layer = tf.keras.layers.Input(shape=(x_train.shape[1], x_train.shape[2]))
lstm_layer = tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(128, return_sequences=True))(input_layer)
attention = tf.keras.layers.Attention()([lstm_layer, lstm_layer, lstm_layer])
flatten = tf.keras.layers.Flatten()(attention)
dense1_layer = tf.keras.layers.Dense(64, activation = 'relu')(flatten)
dense2_layer = tf.keras.layers.Dense(2, activation = 'softmax')(dense1_layer)

model = tf.keras.Model(inputs=input_layer, outputs=dense2_layer)
model.summary()

 

model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.fit(x_train, y_train, epochs=2)

 

model.evaluate(x_test, y_test, verbose=2)

결과

 

glove 벡터를 가지고 넣어줬는데 모델 자체에 embedding Layer를 추가할 수 있음! 그럼 vocabulary 아이디만 넣어주고 모델을 활용할 수 있음

 

 

Embedding + lstmm + attention 분류 실습 - embedding_layer_lstm_attention_classification.ipynb

from collections import Counter
import urllib.request
import pandas as pd
import numpy as np
import tensorflow as tf

import nltk
from nltk.tokenize import sent_tokenize
from nltk import WordPunctTokenizer

nltk.download('punkt')
# spam classification data loading
urllib.request.urlretrieve("https://raw.githubusercontent.com/mohitgupta-omg/Kaggle-SMS-Spam-Collection-Dataset-/master/spam.csv", filename="spam.csv")
data = pd.read_csv('spam.csv', encoding='latin-1')
data[:5]
texts = list(data['v2'])
labels = list(data['v1'])

print(texts[:5])
print(labels[:5])

print(Counter(labels))

 

  • vocab을 만드는 코드 생성
  • vector는 vocab이라는 것을 받아서 vocab에 있으면 vocab에 token ID 만 줄 거임, 없을 때는 vocab에 unk을 넣어서 줄거임
  • vector가 256보다 작을 때는 <pad>라고 줄 거임
def tokenize(document):
  words = []
  sentences = sent_tokenize(document) # sentence tokenizing

  for sentence in sentences:
    words.extend(WordPunctTokenizer().tokenize(sentence)) # word tokenizing

  return [word.lower() for word in words] # case normalization

def make_vocab(documents):
  word2index = {'<unk>':0, '<pad>':1}
  for document in documents:
    tokens = tokenize(document)
    for token in tokens:
      if token not in word2index.keys():
        word2index[token] = len(word2index)

  return word2index

def get_vector(sentence, vocab):
  tokens = tokenize(sentence)
  vector = [vocab[token] if token in vocab.keys() else vocab['<unk>'] for token in tokens]

  while len(vector) < 256:
    vector.append(vocab['<pad>'])
  
  return vector[:256]

 

  • vocab을 어떻게 만들었는지 확인, tokenize에 있는 결과 확인, vector 결과 확인
vocab = make_vocab(texts)
print(vocab)
print(texts[0])
print(tokenize(texts[0]))
print(get_vector(texts[0], vocab))

→ get_vector(texts[0], vocab) : 19까지는 ID가 있고 나머지 뒤에 1들은 pad가 채워짐 (256보다 짧기 때문에)

 

  • x_train = 5000 * 256
x = [get_vector(text, vocab) for text in texts]
x_train, x_test = np.array(x[:5000]), np.array(x[5000:])
y = [0 if label == 'spam' else 1 for label in labels]
y_train, y_test = np.array(y[:5000]), np.array(y[5000:])
print(x_train.shape)

결과

 

 

  • 256개의 token에 대해서 embedding Layer가 붙어야 함
    • tf.keras.layers.Embedding(len(vocab), 100) → 100 : 사이즈 → 임베딩 테이블이 word2vec랑 똑같이 생김 → 각 vocab에 대해서 100차원짜리 임베딩 테이블 생성
    • tf.keras.layers.Dropout(0.5)(embedding_table(input_layer)) → 임베딩 테이블에 input이 들어가서 dropout
    • 이렇게 만들면 glove 벡터를 따로 불러서 넣어줄 필요 없이 자체적으로 word2vec 모델을 안에 내장해버림,, → word2vec 모델도 실시간으로 같이 한꺼번에 학습됨! 학습 속도는 느려질 수 있음
input_layer = tf.keras.layers.Input(shape=(x_train.shape[1]))
embedding_table = tf.keras.layers.Embedding(len(vocab), 100)
embedded_layer = tf.keras.layers.Dropout(0.5)(embedding_table(input_layer))
lstm_layer = tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(128, return_sequences=True))(embedded_layer)
attention = tf.keras.layers.Attention()([lstm_layer, lstm_layer, lstm_layer])
flatten = tf.keras.layers.Flatten()(attention)
dense1_layer = tf.keras.layers.Dense(64, activation = 'relu')(flatten)
dense2_layer = tf.keras.layers.Dense(2, activation = 'softmax')(dense1_layer)

model = tf.keras.Model(inputs=input_layer, outputs=dense2_layer)
model.summary()

 

model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.fit(x_train, y_train, epochs=2)

model.evaluate(x_test, y_test, verbose=2)

 

반응형

+ Recent posts