AIVLE TIL ('23.04.04) 4차 미니프로젝트 2일차

cjkangme·2023년 4월 4일
0

에이블스쿨

목록 보기
47/81
post-thumbnail

참고 자료
구글 머신러닝 텍스트 가이드
딥 러닝을 이용한 자연어처리 입문

무엇을 했나?

이론 정리

카운트 기반의 단어 표현

딥러닝을 이용한 자연어 처리 입문 - 카운트 기반의 단어 표현

  • 모델링을 위한 데이터 전처리의 목적은 바로 단어의 수치화이다. 그 중 카운트 기반의 단어 표현은 단어의 출현 빈도에 집중한 표현이다.

Bag of words

  • you know I want your love. because I love you.

  • 다음의 문장을 아래와 같이 나타낼 수 있다.

단어 벡터 : [2, 1, 2, 1, 2, 1, 1]
단어 사전 : {you : 0, know : 1 , I : 2, want : 3, love : 4, because : 5, your : 6}
  • Bag of words는 문장 또는 문서의 단어를 빈도수를 이용한 벡터를 나타내고, 각 벡터가 어떤 단어를 가리키는지 인덱스를 통해 파악한다.
  • 위 예시에서 0번째 인덱스에 있는 2you임을 나타낸다.

  • bigram을 기준으로 단어의 출현 빈도를 나타낸 그래프이다. 코로나 19에 대한 출현빈도가 매우 높은 것을 알 수 있다.
  • 만약 문서의 주제를 찾아야 하는 문제라면 '코로나 19'가 들어간 문서가 코로나 19를 주제로 삼고 있다는 것을 알 수 있다.
  • 이렇게 카운트 기반의 단어 표현은 단어의 중요도를 나타낼 수 있다.

TF-IDF

  • 이번엔 unigram을 기준으로 단어의 출현 빈도를 나타낸 그래프이다.
  • ~으로, ~에서, ~다로, ~라고와 같은 조사의 출현 빈도가 매우 높은 것을 알 수 있다.
  • 이들을 빈도수가 높지만, 사실 문서의 주제를 파악하는데 있어 하나도 중요하지 않은 요소이다.
  • 그러나 Bag of words에서는 이를 고려하지 못한다.
  • TF-IDF는 이를 고려하여 중요하지 않은 단어에 적은 가중치를 부여하는 방법이다.
  • 다음의 세가지 문장을 보자
정부가 코로나19 위기 단계 하향 여부를 5월 초 결정하기로 했습니다.
각종 지원을 정상화하면서도 고위험군은 보호하기 위해 마련했다고 중대본은 설명했습니다.
"손 씻기, 환기 및 소독, 기침 예절 등 개인 및 공동체가 지켜야 할 기본 수칙을 일상에서 실천해달라"고 당부했습니다.
  • '했습니다'를 하나의 토큰으로 볼 때 '했습니다'는 모든 문장에 등장한다.
  • 반면 '코로나19', '고위험군', '소독'과 같은 토큰은 각 문장에만 등장하는 단어이다.
  • 대략적으로 설명하면 TF-IDF는 모든 문서에 등장하는 단어에 낮은 가중치를 주고, 다른 문서에 잘 등장하지 않는 단어에 높은 가중치를 주어 조금 더 정확한 중요도를 파악할 수 있다.
  • 구글 가이드에 의하면 Bag of Words보다 TF-IDF가 0.25 ~ 15% 정도 더 높은 정확도를 보인다고 한다. 하지만 경우에 따라 처리에 2배 더 오래 걸릴 수 있으니 적절한 선택이 필요하다.

시퀀스 기반의 단어 표현

  • 카운팅 기반의 단어 표현에는 명확한 한계가 있는데, 바로 단어의 순서를 전혀 고려하지 못한다는 것이다.
  • you know I want your love. because I love you
  • know you your you want I love because love I
  • 카운팅 기반의 단어라면 두 문장을 구분하지 못할 것이다.
  • 시퀀스 기반의 단어 표현은 문서에 있는 각 단어에 1대1 맵핑되는 정수를 부여하고, 그 정수로 단어를 나타내는 것이다.
단어 시퀀스 : [0 1 2 3 6 4 5 2 4 0]
단어 사전 : {you : 0, know : 1 , I : 2, want : 3, love : 4, because : 5, your : 6}
  • 1:1로 맵핑된 단어 사전만 알고 있으면 시퀀스를 단어로, 단어를 시퀀스로 자유롭게 변환할 수 있다.

임베딩 벡터

  • 문제는 시퀀스 기반이든 카운팅 기반이든 단어를 그대로 벡터로 표현하는 한 발생하는 문제가 있다.
문서 1
안녕?

문서 2
제가 LA에 있을때는 말이죠... (중략) ... 태균이가 어 선배님 정말 오랜만입니다. 잘지냈습니까? 이러는거에요 ... (후략)
  • 머신러닝 또는 딥러닝 학습을 위해서는 Input 데이터가 모두 똑같은 shape을 가져야하는데, 그러기 위해서는 문서 1에 의미없는 값(pad)를 채워 길게 늘이거나, 문서 2를 잘라내어야 한다.
  • 그러나 문서 2를 크게 잘라내면 정보가 많이 손실되기 때문에 정확한 예측이 어렵다.
  • 반대로 문서 1에 pad를 너무 많이 채우면 모델이 모든 문장을 전부 pad로 학습해도 적은 loss를 얻게 되어 학습이 잘 이루어지지 않게된다.
  • 이를 보완해주는 것이 바로 임베딩 벡터이다.
  • 임베딩 벡터는 모든 단어를 n개의 feature 갖는 벡터로 간주하는데, 단어의 집합인 문장 및 문서를 이들 벡터의 연산으로 나타낸다.
  • 그리고 연산 결과에 따른 loss를 학습하여 각 단어가 정답에 가까운 벡터값을 갖게 한다.
안녕 = [1 1]
LA = [0 0]
오랜만입니다 = [0.5 0.5]
잘 지냈습니까 = [-0.5 -0.5]
  • 만약 이 상태에서 오랜만입니다 + 잘지냈습니까 = [0 0]으로 나타낼 수 있는데 이는 LA와 전혀 상관이 없다.
  • 그런데 학습을 통해 임베딩 벡터가 업데이트 되면
안녕 = [0.8 0.8]
LA = [-2 -2]
오랜만입니다 = [0.6 0.6]
잘 지냈습니까 = [0.1 0.1]
  • 오랜만입니다 + 잘지냈습니까 = [0.7 0.7]안녕과 유사한 값을 갖게 된다.

  • 예시에서는 단 2개의 벡터로 이를 나타냈지만, 실제 모델에서는 관습적으로 128 또는 256차원의 벡터를 사용한다.

  • 이러한 임베딩 기법 덕분에 Input 데이터의 크기를 줄이면서도 정보의 손실 및 왜곡 없이 단어를 모델에 넣을 수 있게 된다.

실습

불용어 처리

# 정규식 라이브러리 불러오기
import re

# 숫자, 알파벳, 한글이 아닌 모든 문자를 띄어쓰기로 변환
def stopwords(text):
    return re.sub('[^0-9a-zA-Zㄱ-ㅣ가-힣()\[\]]', ' ' ,text)

x_train = x_train.apply(stopwords)
x_test = x_test.apply(stopwords)
  • 단어 토큰화에 앞서 분석의 의미가 없을거라 생각하는 특수문자를 모두 제거하였다.
  • 다만 괄호 및 대괄호는 남겨두었는데, 데이터에는 코드도 있기 때문에 코드에서 많이 사용하는 기호를 남겨둔다면 모델이 코드인지 아닌지 분류하는데 도움이 될까 싶어서이다.

TF-IDF (n-gram 카운팅 기반)

# Countvectorizer 및 Kkma 토크나이저 불러오기
from sklearn.feature_extraction.text import TfidfVectorizer
from konlpy.tag import Kkma

# vectorizer 선언 (unigram)
tokenizer = Kkma()
vectorizer = TfidfVectorizer(tokenizer=tokenizer.morphs, ngram_range=(1, 1))
# 벡터화
x_train_uni = vectorizer.fit_transform(x_train)
x_test_uni = vectorizer.transform(x_test)

# vectorizer 선언 (bigram)
vectorizer = CountVectorizer(tokenizer=tokenizer.morphs, ngram_range=(2, 2))
# 벡터화
x_train_bi = vectorizer.fit_transform(x_train)
x_test_bi = vectorizer.transform(x_test)
  • kkma 형태소 분석기를 통해 토큰화한 문장을 TfidfVectorizer에 넣어 TF-IDF 행렬을 구하였다.

  • TF-IDF가 중요하지 않은 단어를 잘 구분해 줄 것이라 믿고 별도의 불용어 처리는 하지 않았다.

  • 단 bigram을 기준으로 쪼갰을 때 약 7만개에 달하는 어마어마한 단어 종류가 나와 데이터를 줄이기 위해 약 20000개 정도로 한도를 설정해야 하여 다시 구해야 할 것 같다.

시퀀스 기반 단어 표현

# 필요 라이브러리 불러오기
import tensorflow as tf
from tensorflow import keras

from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences

# 꼬꼬마 형태소 분석기를 이용해 토큰화합니다.
tokenizer = Kkma()

x_train_token = [tokenizer.morphs(sent) for sent in x_train]
x_test_token = [tokenizer.morphs(sent) for sent in x_test]

# 문자열 상태인 토큰을 시퀀스로 바꾸어 줍니다.
seq_train = Tokenizer(filters='', lower=True)
seq_test = Tokenizer(filters='', lower=True)

seq_train.fit_on_texts(x_train)
seq_test.fit_on_texts(x_test)

x_train_seq = seq_train.texts_to_sequences(x_train)
x_test_seq = seq_test.texts_to_sequences(x_test)
  • 단어를 시퀀스로 바꾸는 과정이다.
  • 이제 Input 모양을 맞추기 위해서 문서에 pad를 채우거나 잘라내는 과정을 거쳐야 한다.

  • 토큰의 길이 분포를 확인해봤는데 최대 843부터 2까지 매우 넓은 분포를 보였다.
  • 대부분의 값이 0 ~ 100 구간에 존재하므로 pad의 양을 줄이는 것이 전체 모델 성능에 더 좋을 것이라 생각하여 시퀀스 길이가 128이 되도록 하였다.
# 128 정도로 설정해보자.
x_train_pad = pad_sequences(x_train_seq, maxlen=128, padding='pre', truncating='post')
x_test_pad = pad_sequences(x_test_seq, maxlen=128, padding='post', truncating='post')

Word2Vec

  • 임베딩 매트릭스를 구하는 과정인데, 이는 교안에 제공된 코드를 그대로 사용했기 때문에 올릴 수 는 없다.
  • 사실 모델에서 자체적으로 임베딩 레이어를 제공하지만, 가지고 있는 코퍼스 데이터를 바탕으로 임베딩 레이어를 만들면 OOV(단어 사전에 없는 모르는 단어) 문제가 발생할 가능성이 줄어들기 때문에 별도로 만들어 주는 것도 좋은 접근이다.

0개의 댓글