Deep Learning [Day 8]

 Published On

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

Deep Learning for NLP 1 [TFIDF and NLP Objective]

1. TF-IDF

  • 언어적인 특성을 반영하여 단어를 수치화하는 방법은 벡터를 사용하는 것이다.
  • 데이터를 표현할 때 기본적으로 one-hot encoding 방식을 사용한다.
    • 하지만 이 방식은 단어의 의미나 특성을 표현할 수 없으며, 단어의 수가 매우 많아지면 고차원 저밀도 벡터를 구성한다.
  • 벡터의 크기가 작으면서 단어의 의미를 표현해줄 수 있는 방식이 필요하다.
    • 분포가설에 기반하여, 같은 문맥의 단어(비슷한 위치에 나오는 단어)는 비슷한 의미를 가지는 특징을 사용하여 두 가지 표현법이 있다.
      1) 카운트 기반 방법: 특정 문맥 안에서 단어들이 동시에 등장하는 횟수를 직접 센다.
      2) 예측 방법: 신경망 등을 통해 문맥 안의 단어들을 예측한다.
  • TF-IDF(Term Frequency-Inverse Document Frequency)는 단어의 빈도와 역문서 빈도(문서의 빈도에 특정 식을 취함)를 사용하여 DTM 내의 각 단어들마다 중요한 정도를 가중치로 주는 방법이다.
  • DTM(Document-Term Matrix, 문서 단어 행렬)이란, 다수의 문서에서 등장하는 각 단어들의 빈도를 행렬로 표현하는 것이다.
  • tf(d, t): 특정 문장(d)에서 특정 단어(t)가 몇 번 나오는지
  • df(t): 특정 단어(t)가 등장한 문장의 수
  • idf(d, t): df(t)에 반비례하는 수
  • TF-IDF는 모든 문서에서 자주 등장하는 단어는 중요도가 낮다고 판단하며, 특정 문서에서만 자주 등장하는 단어는 중요도가 높다고 판단한다.
  • TF-IDF 값이 낮으면 중요도가 낮은 것이고, 값이 크면 중요도가 큰 것이다. 즉, the나 a와 같은 불용어는 모든 문서에서 자주 나타나기 때문에 TF-IDF의 값이 다른 단어에 비해 낮다.

1.1. 사이킷런을 이용한 TF-IDF

from sklearn.feature_extraction.text import TfidfVectorizer
corpus = [
    'you know I want your love',
    'I like you',
    'what should I do ',    
]
tfidfv = TfidfVectorizer().fit(corpus)
print(tfidfv.transform(corpus).toarray())
print(tfidfv.vocabulary_)

2. 문서 유사도

2.1. 자카드 유사도

  • A와 B 두 개의 집합이 있을 때, 교집합은 두 개의 집합에서 공통으로 가지고 있는 원소들의 집합이다.
  • 합집합에서 교집합의 비율을 구한다면, 두 집합 A와 B의 유사도를 구할 수 있다는 것이 자카드 유사도의 아이디어다.
  • A / B
  • A: 두 집합의 교집합인 공통된 단어의 개수(unique)
  • B: 집합이 가지는 단어의 개수


  • 장점: 벡터를 사용하는 방법을 몰라도 사용 가능하다.
  • 단점: 계란이나 달걀은 같은 단어이지만, 자카드 유사도는 0과 1로만 나타내기 때문에 유사도가 떨어진다.
#%%

doc1 = "apple banana everyone like likey watch card holder"
doc2 = "apple banana coupon passport love you"

tokenized_doc1 = doc1.split()
tokenized_doc2 = doc2.split()

print(tokenized_doc1)
print(tokenized_doc2)

#%%

# 문서1과 문서2의 합집합
union = set(tokenized_doc1).union(set(tokenized_doc2))
print(union)

#%%

# 문서1과 문서2의 교집합
intersection = set(tokenized_doc1).intersection(set(tokenized_doc2))
print(intersection)

#%%

print(len(intersection) / len(union))

2.2. 코사인 유사도

  • 코사인 유사도는 두 개의 벡터값에서 코사인 각도를 구하는 방법이다. 두 벡터의 방향이 동일한 경우 1의 값을 가지며, 90도의 각을 가지면 0, 180도로 반대의 방향을 가지면 -1의 값을 갖게 된다.
  • 즉, -1과 1사이의 값을 가지며 1에 가까울수록 비슷하다.


#%%

import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer

sent = {"휴일인 오늘도 서쪽을 중심으로 폭염이 이어졌는데요, 내일은 반가운 비 소식이 있습니다.", "피해서 휴일에 놀러왔다가 갑작스런 비로 인해 망연자실하고 있습니다."}

tfidf_vectorizer = TfidfVectorizer()
tfidf_matrix = tfidf_vectorizer.fit_transform(sent)

print(tfidf_matrix)
print('First sentence')
print(tfidf_matrix[0])
print('Second sentence')
print(tfidf_matrix[1])

# Cosine similarity
from sklearn.metrics.pairwise import cosine_similarity
print('Cosine similarity: ', cosine_similarity(tfidf_matrix[0], tfidf_matrix[1]))

#%%

sent = {"영희가 눈물을 훔치며, 말을 이어나갔다.", "철수가 닭똥같은 눈물을 흐리며, 말을 멈추었다."}

tfidf_vectorizer = TfidfVectorizer()
tfidf_matrix = tfidf_vectorizer.fit_transform(sent)

print(tfidf_matrix)
print('First sentence')
print(tfidf_matrix[0])
print('Second sentence')
print(tfidf_matrix[1])
print('Cosine similarity: ', cosine_similarity(tfidf_matrix[0], tfidf_matrix[1]))

#%%

sent = {"밥을 먹는다", "밥을 먹다", "밥을 먹었다"}

tfidf_vectorizer = TfidfVectorizer()
tfidf_matrix = tfidf_vectorizer.fit_transform(sent)

print(tfidf_matrix)
print('First sentence')
print(tfidf_matrix[0])
print('Second sentence')
print(tfidf_matrix[1])
print('Third sentence')
print(tfidf_matrix[2])

print('Cosine similarity: ', cosine_similarity(tfidf_matrix[0], tfidf_matrix[1]))
print('Cosine similarity: ', cosine_similarity(tfidf_matrix[0], tfidf_matrix[2]))
print('Cosine similarity: ', cosine_similarity(tfidf_matrix[1], tfidf_matrix[2]))

#%%

from numpy import dot
from numpy.linalg import norm
import numpy as np

def cos_sim(A, B):
  return dot(A, B)/(norm(A)*norm(B))

# 밥을 / 먹는다 / 먹다 / 먹었다
doc1=np.array([1,0,0,0])
doc2=np.array([1,0,1,0])
doc3=np.array([1,0,0,1])

print(cos_sim(doc1, doc2))
print(cos_sim(doc1, doc3))
print(cos_sim(doc2, doc3))

2.3. 유클리디언 유사도

  • 유클리드 거리는 문서의 유사도를 구할 때 자카드 유사도나 코사인 유사도에 비해 유용한 방법은 아니다.
  • 다차원 공간에서 두 점 사이의 거리를 계산하는 유클리드 거리 공식


#%%

import numpy as np
def dist(x, y):
  return np.sqrt(np.sum((x-y)**2))

doc1 = np.array((2, 3, 0, 1))
doc2 = np.array((1, 2, 3, 1))
doc3 = np.array((2, 1, 2, 2))
docQ = np.array((1, 1, 0, 1))

print(dist(doc1, docQ))
print(dist(doc2, docQ))
print(dist(doc3, docQ))

Deep Learning for NLP 2 [Text Understanding]

IMDB 영화 리뷰 데이터 처리

#%%

import os
import re
import pandas as pd
import tensorflow as tf
from tensorflow.keras import utils

# IMDB 데이터 다운로드
data_set = tf.keras.utils.get_file(
    fname="imdb.tar.gz",
    origin="http://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz",
    extract=True
)

def directory_data(directory):
  data = {}
  data["review"] = []
  for file_path in os.listdir(directory):
    with open(os.path.join(directory, file_path), "r", encoding="utf-8") as file:
      data["review"].append(file.read())
  return pd.DataFrame.from_dict(data)

def data(directory):
  pos_df = directory_data(os.path.join(directory, "pos"))
  neg_df = directory_data(os.path.join(directory, "neg"))
  pos_df["sentiment"] = 1
  neg_df["sentiment"] = 0
  return pd.concat([pos_df, neg_df])

train_df = data(os.path.join(os.path.dirname(data_set), "aclImdb", "train"))
test_df = data(os.path.join(os.path.dirname(data_set), "aclImdb", "test"))

#%%

train_df.head()

#%%

reviews = list(train_df["review"])

# 문자열 문장 리스트를 토큰화
tokenized_reviews = [r.split() for r in reviews]
# 토큰화된 리스트에 대한 각 길이를 저장(단어의 길이)
review_by_token = [t for t in tokenized_reviews]
review_len_by_token = [len(t) for t in tokenized_reviews]
# 토큰화된 것을 붙여서 음절의 길이를 저장(문장의 길이 - 공백을 제거해서 그냥 한줄로 표현)
review_by_alphabet = [s.replace(' ', '') for s in reviews]
review_len_by_alphabet = [len(s.replace(' ', '')) for s in reviews]

#%%

print(tokenized_reviews[:10])

#%%

print(review_by_token[:10])

#%%

print(review_len_by_token[:10])

#%%

print(review_by_alphabet[:10])

#%%

print(review_len_by_alphabet[:10])

#%%

import matplotlib.pyplot as plt
# 이미지 사이즈 선언, figsize: [가로, 세로] 형태의 튜플로 입력
plt.figure(figsize=(12, 5))

# 히스토그램 선언
# bins: 히스토그램 값들에 대한 버켓 범위
# range: x축 값의 범위
# alpha: 그래프 색상 투명도
# color: 그래프 색상
# label: 그래프에 대한 라벨
plt.hist(review_len_by_token, bins=50, alpha=0.5, color='r', label='word')
plt.hist(review_len_by_alphabet, bins=50, alpha=0.5, color='b', label='alphabet')

# 그래프 제목, x축 라벨, y축 라벨
plt.title("Review Length Histogram")
plt.xlabel("Review Length")
plt.ylabel("Number of Review")

#%% md

단어는 2,000개밖에 쓰지 않았지만, 문장의 길이는 훨씬 더 많다는 것을 알 수 있다.

#%%

import numpy as np

print("문장 최대길이: ", np.max(review_len_by_token))
print("문장 최소길이: ", np.min(review_len_by_token))
print("문장 평균길이: ", np.mean(review_len_by_token))
print("문장 길이 표준편차: ", np.std(review_len_by_token))
print("문장 중간길이: ", np.median(review_len_by_token))

# 사분위의 대한 경우는 0~100 스케일로 되어있음
print("제 1 사분위 길이: ", np.percentile(review_len_by_token, 25)) # 1/4의 위치(하위 25%)
print("제 3 사분위 길이: ", np.percentile(review_len_by_token, 75)) # 3/4의 위치(상위 25%)

#%% md

### 문장 내 단어 수에 대한 히스토그램

#%%

plt.figure(figsize=(12, 5))
# 박스플롯 생성
# 첫 번재 파라미터: 여러 분포에 대한 데이터 리스트를 입력
# labels: 입력한 데이터에 대한 라벨
# showmeans: 평균값을 마크함
plt.boxplot([review_len_by_token], labels=['token'], showmeans=True)

#%% md

### 문장 내 알파벳 수에 대한 히스토그램

#%%

plt.figure(figsize=(12, 5))
plt.boxplot([review_len_by_alphabet], labels=['alphabet'], showmeans=True)

#%%

from wordcloud import WordCloud, STOPWORDS

my_stopwords = STOPWORDS.add("br")
wordcloud = WordCloud(stopwords=STOPWORDS, background_color='black', width=800, height=600).generate(" ".join(train_df["review"]))

plt.figure(figsize=(15, 10))
plt.imshow(wordcloud)
plt.axis("off")
plt.show()

#%% md

### 긍정/부정의 분포 확인

#%%

import seaborn as sns

sentiment = train_df["sentiment"].value_counts()
fig, axe = plt.subplots(ncols=1)
fig.set_size_inches(6, 3)
sns.countplot(train_df["sentiment"])
plt.show()

Tags: DeepLearning

Comments:

comments powered by Disqus

© 2021 - MH.Ji. All rights reserved
Built using Jekyll