ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Keras] Embedding Layer에 word2vec 주입하기
    💫 Computer Science/Python & AI Framework 2020. 2. 21. 17:08

    ● Embedding Methods

    NLP task를 수행하기 전, 단어를 벡터로 만드는 임베딩 작업을 케라스를 이용해서 하는 방법은 크게 두 가지가 있습니다.

     

     

     

    • 케라스의 내장 함수인 Embedding()을 사용하기
    • Pre-trained word embedding 가져와서 Embedding Layer에 주입하기

     

     

     

     

     

     

    1. Keras Embedding Layer

    • 예제

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    from tensorflow.keras.preprocessing.text import Tokenizer
    from tensorflow.keras.preprocessing.sequence import pad_sequences
    import numpy as np
     
    sentences = ['nice great best amazing''stop lies''pitiful nerd''excellent work''supreme quality''bad''highly respectable']
    y_train = [1001101]
     
    = Tokenizer()
    t.fit_on_texts(sentences)
    vocab_size = len(t.word_index) + 1
    print(vocab_size) # 16
     
    X_encoded = t.texts_to_sequences(sentences)
    print(X_encoded)
    # [[1, 2, 3, 4], [5, 6], [7, 8], [9, 10], [11, 12], [13], [14, 15]]
     
    max_len=max(len(l) for l in X_encoded)
    print(max_len) # 4
     
    X_train=pad_sequences(X_encoded, maxlen=max_len, padding='post')
    y_train=np.array(y_train)
    print(X_train)
    # [[ 1  2  3  4]
    #  [ 5  6  0  0]
    #  [ 7  8  0  0]
    #  [ 9 10  0  0]
    #  [11 12  0  0]
    #  [13  0  0  0]
    #  [14 15  0  0]]
     
    from tensorflow.keras.models import Sequential
    from tensorflow.keras.layers import Dense, Embedding, Flatten
     
    model = Sequential()
    model.add(Embedding(vocab_size, 4, input_length=max_len)) # 모든 임베딩 벡터는 4차원.
    model.add(Flatten()) # Dense의 입력으로 넣기위함.
    model.add(Dense(1, activation='sigmoid'))
     
    model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['acc'])
    model.fit(X_train, y_train, epochs=100, verbose=2)
    cs

     

     

    model.add(Embedding(vocab_size, 4, input_length=max_len)) 

     

    이렇게 Embedding()을 사용하기 위해서는 입력 될 각 단어들은 모두 정수 인덱싱이 되어 있어야 합니다.

     

     

    그 과정은,

     

    어떤 단어가 주어지고 -> 단어에 고유한 정수 값을 부여하고 -> Embedding()에 주입하고 -> 임베딩 층은 밀집벡터를 출력

     

     

    예를 들어

    ['nice great best amazing'] 라는 문장이 주어졌을 때 이 문장은 [1, 2, 3, 4]와 같이 정수 값이 부여됩니다. 이를 임베딩층에 주입하는 것인데, 문장의 토큰의 갯수가 모두 다르므로 이 토큰의 갯수를 맞추기 위해 패딩작업을 해야 합니다.

    ['stop lies']라는 문장의 정수 값이 [5, 6]이고 문장의 최대길이가 4 일때 4로 패딩을 해주면 [0, 0, 5, 6]으로 바꾸어 문장의 길이(토큰의 갯수)를 똑같이 맞춰준뒤 임베딩 층에 주입하는 것 입니다.

     

    처음에 임베딩 층(Embedding())을 접했을 때, 이 층에 넣기만 하면 벡터로 바뀌는 것이 신기했는데 그 원리를 살펴보면,

    임베딩 층은 입력 정수에 대해 밀집 벡터(dense vector)로 맵핑하고 이 밀집 벡터는 인공 신경망의 학습 과정에서 가중치가 학습되는 것과 같은 방식으로 훈련됩니다. 훈련 과정에서 단어는 모델이 풀고자하는 작업에 맞는 값으로 업데이트 됩니다.

     

    정수를 밀집 벡터 또는 임베딩 벡터로 맵핑한다는 것은 어떤 의미일까요? 특정 단어와 맵핑되는 정수를 인덱스로 가지는 테이블로부터 임베딩 벡터 값을 가져오는 룩업 테이블이라고 볼 수 있습니다. 그리고 이 테이블은 단어 집합의 크기만큼의 행을 가지므로 모든 단어는 고유한 임베딩 벡터를 가집니다.

     

     

     

    Embedding()의 Lookup Table 예시

     

     

     

     

     

     

     

    이 임베딩 층의 원리가 룩업테이블을 참조하는 형식이기 때문에, 임베딩 층의 입력을 원-핫 벡터로 넣지 않아도 되는 것입니다. 

     

     

     

    • 케라스 Embedding()을 사용하는 방법 정리

     

    1. 문장 토큰화
    = Tokenizer()
    t.fit_on_texts(sentences)
    vocab_size = len(t.word_index) + 1

     

     

    2. 토큰화 된 단어를 정수로
    X_encoded = t.texts_to_sequences(sentences)

     

     

    3. 문장 길이를 모두 동일하게 패딩
    X_train=pad_sequences(X_encoded, maxlen=max_len, padding='post')
    y_train=np.array(y_train)

     

     

    4. 임베딩 층에 주입
    model = Sequential()
    model.add(Embedding(vocab_size, 4, input_length=max_len))

     

     

     

    • Embedding()의 파라미터

     

    - vocab_size : 전체 단어 집합의 크기입
    - output_dim : 워드 임베딩 후의 임베딩 벡터의 차원
    - input_length : 입력 시퀀스의 길이

     

     

    Embedding()은 (number of samples, input_length)인 2D 정수 텐서를 입력받습니다. 이 때 각 sample은 정수 인코딩이 된 결과로, 정수의 시퀀스입니다. Embedding()은 워드 임베딩 작업을 수행하고 (number of samples, input_length, embedding word dimentionality)인 3D 실수 텐서를 리턴합니다

     

     

     

     

     

     

    2. Pre-trained word embedding

    이는 위키디피아, 네이버 지식인 등의 많은 양의 코퍼스로 Word2vec, FastText, GloVe 등의 임베딩 패키지를 이용하여 미리 훈련된 언어모델(임베딩 벡터)를 가지고와서 사용하는 방법입니다.

     

     

    • 예제

    우선, 위 예제에서 X_train데이터의 패딩작업까지는 동일하나 그 이후 사전 훈련된 모델의 '단어':'벡터' 쌍을 얻고 훈련시킬 데이터에 매핑해야 하는 과정이 필요합니다.

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    from tensorflow.keras.preprocessing.text import Tokenizer
    from tensorflow.keras.preprocessing.sequence import pad_sequences
    import numpy as np
     
    sentences = ['nice great best amazing''stop lies''pitiful nerd''excellent work''supreme quality''bad''highly respectable']
    y_train = [1001101]
     
    = Tokenizer()
    t.fit_on_texts(sentences)
    vocab_size = len(t.word_index) + 1
    print(vocab_size) # 16
     
    X_encoded = t.texts_to_sequences(sentences)
    print(X_encoded)
    # [[1, 2, 3, 4], [5, 6], [7, 8], [9, 10], [11, 12], [13], [14, 15]]
     
    max_len=max(len(l) for l in X_encoded)
    print(max_len) # 4
     
    X_train=pad_sequences(X_encoded, maxlen=max_len, padding='post')
    y_train=np.array(y_train)
    print(X_train)
    # [[ 1  2  3  4]
    #  [ 5  6  0  0]
    #  [ 7  8  0  0]
    #  [ 9 10  0  0]
    #  [11 12  0  0]
    #  [13  0  0  0]
    #  [14 15  0  0]]
     
    cs

     

    사용할 사전 훈련 모델은 구글에서 제공하는 Word2Vec으로 생성한 모델입니다.

    다운로드

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    import numpy as np
    import gensim
     
    # 구글의 사전 훈련된 Word2vec 모델을 로드합니다.
    word2vec_model = gensim.models.KeyedVectors.load_word2vec_format('GoogleNews-vectors-negative300.bin.gz', binary=True)
     
    print(word2vec_model.vectors.shape) # (3000000, 300)
     
    embedding_matrix = np.zeros((vocab_size, 300))
    # 단어 집합 크기의 행과 300개의 열을 가지는 행렬 생성. 값은 전부 0으로 채워진다.
    print(np.shape(embedding_matrix)) # (16, 300)
     
    def get_vector(word):
        if word in word2vec_model:
            return word2vec_model[word]
        else:
            return None
     
    for word, i in t.word_index.items(): # 훈련 데이터의 단어 집합에서 단어와 정수 인덱스를 1개씩 꺼내온다.
        temp = get_vector(word) # 단어(key) 해당되는 임베딩 벡터의 300개의 값(value)를 임시 변수에 저장
        if temp is not None: # 만약 None이 아니라면 임베딩 벡터의 값을 리턴받은 것이므로
            embedding_matrix[i] = temp # 해당 단어 위치의 행에 벡터의 값을 저장한다.
     
     
    from tensorflow.keras.models import Sequential
    from tensorflow.keras.layers import Dense, Embedding, Flatten
     
    model = Sequential()
    = Embedding(vocab_size, 300, weights=[embedding_matrix], input_length=max_len, trainable=False)
    model.add(e)
    model.add(Flatten())
    model.add(Dense(1, activation='sigmoid'))
    model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['acc'])
    model.fit(X_train, y_train, epochs=100, verbose=2)
    cs

     

     

    word2vec_model = gensim.models.KeyedVectors.load_word2vec_format('GoogleNews-vectors-negative300.bin.gz', binary=True)

     

     

    embedding_matrix = np.zeros((vocab_size, 300))

    def get_vector(word):

        if word in word2vec_model:

            return word2vec_model[word]

        else:

            return None

     

    for word, i in t.word_index.items(): 

        temp = get_vector(word) 

        if temp is not None:

            embedding_matrix[i] = temp

     

     

    사전훈련된 모델은 총 3000000개 단어가 300차원을 가진 벡터로 매핑되어 있습니다.

    이 모델의 '단어' -> '벡터 값'을 훈련 시킬 데이터의 단어와 매핑하여 벡터 값을 embedding_matrix에 집어넣습니다.

     

     

    사전훈련된 모델에서 'nice'라는 단어의 벡터값 확인

     

    print(word2vec_model['nice'])
    [ 0.15820312 0.10595703 -0.18945312 0.38671875 0.08349609 -0.26757812 0.08349609 0.11328125 -0.10400391 0.17871094 -0.12353516 -0.22265625 ... 중략 ... -0.16894531 -0.08642578 -0.08544922 0.18945312 -0.14648438 0.13476562 -0.04077148 0.03271484 0.08935547 -0.26757812 0.00836182 -0.21386719]

     

     

     

    훈련시킬 데이터 안의 'nice'라는 단어의 인덱스 확인

     

    print('단어 nice의 정수 인덱스 :', t.word_index['nice'])
    단어 nice의 정수 인덱스 : 1

     

     

     

    'nice'의 정수 인덱스가 1 이므로 embeddding_matrix[1]에는 사전훈련된 모델에서 'nice'의 벡터값이 들어가 있어야 합니다.

     

    print(embedding_matrix[1])
    [ 0.15820312 0.10595703 -0.18945312 0.38671875 0.08349609 -0.26757812 0.08349609 0.11328125 -0.10400391 0.17871094 -0.12353516 -0.22265625 ... 중략 ... -0.16894531 -0.08642578 -0.08544922 0.18945312 -0.14648438 0.13476562 -0.04077148 0.03271484 0.08935547 -0.26757812 0.00836182 -0.21386719]

     

     

    이렇게 되면 예제의 훈련 데이터들의 각 단어들은

     

    ([('nice', 1), ('great', 2), ('best', 3), ('amazing', 4), ('stop', 5), ('lies', 6), ('pitiful', 7), ('nerd', 8), ('excellent', 9), ('work', 10), ('supreme', 11), ('quality', 12), ('bad', 13), ('highly', 14), ('respectable', 15)])

    사전훈련된 모델안의 벡터 값을 가지게 됩니다.

     

     

     

    이제 이를 임베딩 층에 주입합니다.

    model = Sequential()
    = Embedding(vocab_size, 300, weights=[embedding_matrix], input_length=max_len, trainable=False)
    model.add(e)

     

    GloVe로 사전 훈련된 벡터를 Embedding()에 주입하는 방법도 위와 동일합니다.

     

     

    훈련 데이터가 적을 때 Embedding()으로 해당 문제에 특화된 embedding vector를 만들어내는 것은 쉽지 않기 때문에, 훈련 데이터가 적다면 Pre-trained 모델을 임베딩 층에 주입하여 사용하는 방법이 더 좋을 수도 있습니다. 

     

     

    댓글

Designed by Tistory.