본문 바로가기
  • 데이터에 가치를 더하다, 서영석입니다.
공부하는 습관을 들이자/Deep Learning (NLP,LLM)

딥러닝을 이용한 자연어처리 입문 #12

by 꿀먹은데이터 2024. 1. 3.

딥러닝을 이용한 자연어처리 입문 #12 10) 엘모 

10) 엘모 (Embeddings from Language Model, ELMo)

  • 언어 모델로 하는 임베딩
  • 특징 : 사전 훈련된 언어 모델 사용

1. ELMo(Embeddings from Language Model)

ex) Word2Vec 이나 GloVe 등의 임베딩 방법론으로 bank 라는 단어를 [0.2 0.8 -1.2]라는 임베딩 벡터로 임베딩 하였다고 가정

  • 이 단어는 bank account & river bank 에서의 bank는 전혀 다른 의미임에도 불구하고 모두 같은 벡터 사용

⇒ 같은 표기의 단어라도 문맥에 따라 다른 워드 임베딩 → 자연어 처리의 성능 상승

⇒ 워드 임베딩 시 문맥을 고려해서 임베딩 하겠다는 아이디어 : 문맥을 반영한 워드 임베딩

2. biLM (Bidirectional Language Model)의 사전 훈련

  • 단어를 예측하는 작업인 언어 모델링 상기

  • RNN 언어 모델은 문장으로부터 단어 단위로 입력받음
  • RNN 내부의 은닉상태 ht는 시점이 지날수록 업데이트

⇒ RNN의 ht의 값이 문장의 문맥 정보를 점차적으로 반영한다고 함

  • ELMo : 순방향 RNN 뿐만 아니라, 반대방향으로 문장을 스캔하는 역방향 RNN 또한 활용

biLM (Bidirectional Language Model)

  • 다층 구조를 전제 = 은닉층이 최소 2개 이상

  • 주의 !
  • 양방향 RNN ≠ ELMo의 biLM
    • 양방향 RNN : 순방향 RNN의 은닉 상태와 역방향의 RNN의 은닉 상태를 연결하여 다음층의 입력으로 사용
    • biLM : 순방향 언어모델과 역방향 언어모델이라는 두 개의 언어 모델을 별개로 보고 학습

3. biLM의 활용

  • biLM이 언어 모델링을 통해 학습된 후 ELMo가 사전 훈련된 biLM을 통해 입력 문장으로부터 단어를 임베딩하기 위한 과정

  • play라는 단어를 임베딩 하기 위해서는 각 층의 결과값을 재료로 사용
  • 순방향 언어 모델과 역방향 언어 모델의 각 층의 출력값을 연결하고 추가 작업 진행

⇒ 완성된 벡터 = ELMo 표현

 

ELMo를 입력으로 사용하고 수행하고 싶은 텍스트 분류, 질의응답 시스템 등의 자연어 처리 작업

ex) 텍스트 분류 작업 - ELMo 표현을 어떻게 텍스트 분류 작업에 사용?

  • ELMo 표현을 기존의 임베딩 벡터와 함께 사용
    1. 텍스트 분류 작업을 GloVe와 같은 기존 방법론 사용한 임베딩 벡터 준비했다고 가정
    • GloVe를 이용한 임베딩 벡터만 텍스트 분류 작업에 사용하는 것이 아니라 준비된 ELMo 표현을 GloVe 임베딩 벡터와 연결해서 입력으로 사용
    1. biLM의 가중치는 고정, s1, s2, s3, r은 훈련 과정에서 학습

11) 임베딩 벡터의 시각화 (Embedding Visualization)

  • 학습한 임베딩 벡터들 시각화
  • 파일 다운 → 업로드

12) 문서 벡터를 이용한 추천 시스템 (Recommendation System using Document Embedding)

  • 문서들을 고정된 길이의 벡터로 변환한다면 벡터 간 비교로 문서들을 서로 비교 가능
  • 문서 벡터로 변환하는 방법
    • 단어 벡터를 얻은 뒤 문서에 존재하는 단어 벡터들의 평균을 문서 벡터로 간주
  • 문서 내 각 단어들을 Word2Vec을 통해 벡터로 변환
  • 이들의 평균으로 문서 벡터를 얻어 선호하는 도서와 유사한 도서 찾아주는 시스템

1. 데이터 로드

  • 데이터 : 책의 이미지와 책의 줄거리를 크롤링한 데이터
import urllib.request
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import requests
import re
from PIL import Image
from io import BytesIO
from nltk.tokenize import RegexpTokenizer
import nltk
from gensim.models import Word2Vec
from gensim.models import KeyedVectors
from nltk.corpus import stopwords
from sklearn.metrics.pairwise import cosine_similarity
df = pd.read_csv("data.csv") # 데이터를 데이터프레임으로 로드
print('전체 문서의 수 :',len(df))

>>> 전체 문서의 수 : 2382
# 상위 5개의 행만 출력
df[:5]
  • 줄거리에 해당하는 열 : Desc
  • 해당 열에 있는 데이터에 대해서 Word2Vec학습하기 위해 전처리 진행
def _removeNonAscii(s):
    return "".join(i for i in s if  ord(i)<128)

def make_lower_case(text):
    return text.lower()

def remove_stop_words(text):
    text = text.split()
    stops = set(stopwords.words("english"))
    text = [w for w in text if not w in stops]
    text = " ".join(text)
    return text

def remove_html(text):
    html_pattern = re.compile('<.*?>')
    return html_pattern.sub(r'', text)

def remove_punctuation(text):
    tokenizer = RegexpTokenizer(r'[a-zA-Z]+')
    text = tokenizer.tokenize(text)
    text = " ".join(text)
    return text

df['cleaned'] = df['Desc'].apply(_removeNonAscii)
df['cleaned'] = df.cleaned.apply(make_lower_case)
df['cleaned'] = df.cleaned.apply(remove_stop_words)
df['cleaned'] = df.cleaned.apply(remove_punctuation)
df['cleaned'] = df.cleaned.apply(remove_html)
  • 해당 열에 대해 전처리 수행 후 cleaned 라는 열에 저장
# 상위 5개의 행만 출력
df['cleaned'][:5]

>>>
0    know power shifting west east north south pres...
1    following success accidental billionaires mone...
2    tap power social software networks build busin...
3    william j bernstein american financial theoris...
4    amazing book joined steve jobs many akio morit...
Name: cleaned, dtype: object
  • 전처리 과정에서 빈 값이 생긴 행 → NaN으로 변환 후 해당 행 제거
df['cleaned'].replace('', np.nan, inplace=True)
df = df[df['cleaned'].notna()]
print('전체 문서의 수 :',len(df))

>>> 전체 문서의 수 : 2381 # NaN 제거하여 전체 문서의 수 -1개
# 토큰화 수행하여 corpus 라는 리스트에 저장
corpus = [] # 해당 리스트를 통해 Word2Vec 훈련
for words in df['cleaned']:
    corpus.append(words.split())

2. 사전 훈련된 워드 임베딩 사용하기

urllib.request.urlretrieve("<https://s3.amazonaws.com/dl4j-distribution/GoogleNews-vectors-negative300.bin.gz>", \\
                           filename="GoogleNews-vectors-negative300.bin.gz")

word2vec_model = Word2Vec(size = 300, window=5, min_count = 2, workers = -1)
word2vec_model.build_vocab(corpus)
word2vec_model.intersect_word2vec_format('GoogleNews-vectors-negative300.bin.gz', lockf=1.0, binary=True)
word2vec_model.train(corpus, total_examples = word2vec_model.corpus_count, epochs = 15)
  • 데이터가 충분하지 않은 상황에서 사전 훈련된 워드 임베딩을 단어 벡터의 초기값으로 사용하면 성능 향상 가능

3. 단어 벡터의 평균 구하기

  • 각 문서에 존재하는 단어들의 벡터값의 평균 → 해당 문서의 벡터값 연산
def get_document_vectors(document_list):
    document_embedding_list = []

    for line in document_list:
        doc2vec = None
        count = 0
        for word in line.split():
            if word in word2vec_model.wv.vocab:
                count += 1
                if doc2vec is None:
                    doc2vec = word2vec_model[word]
                else:
                    doc2vec = doc2vec + word2vec_model[word]

        if doc2vec is not None:
            doc2vec = doc2vec / count
            document_embedding_list.append(doc2vec)

    return document_embedding_list
document_embedding_list = get_document_vectors(df['cleaned'])
print('문서 벡터의 수 :',len(document_embedding_list))

>>> 문서 벡터의 수 : 2381

4. 추천 시스템 구현하기

  • 각 문서 벡터 간의 코사인 유사도