본문 바로가기
자연어처리(NLP) & CHAT GPT/NLP

[NLP] 언어 모델링 - 실습

by 11car28z 2023. 7. 31.

간단한 유사도 측정

import numpy as np
from numpy import dot
from numpy.linalg import norm #벡터 크기 구하기
#영화 3가지
#각 열은 영화 대사에 등장한 단어들(사랑, 돈, 운명, 증오)
d1 = np.array([1,1,1,0])
d2 = np.array([0,1,1,1])
d3 = np.array([0,5,5,5])

문서1과 2사이의 유클리디안 거리 구하기
d1d2 사이거리 = 루트 2
d2d3 사이거리 = 루트 48

유클리디안 거리 공식으로는 d2문서가 d1과 가까움.

#d1, d2코사인 유사도
dot(d1, d2) / (norm(d1)*norm(d2))
#d2, d3코사인 유사도
dot(d3, d2) / (norm(d3)*norm(d2))

자카드 유사도

자카드 유사도(d1, d2) = (d1 교집합 d2) / (d1 합집합 d2)

0~1 값
1:분모 = 분자 | 두 문서에 완벽하게 동일한 단어들이 있다.
0:동일한 단어가 없다.

EX)
d1 = '대한민국 하늘 육지'
d2 = '바다 들판 우주'
자카드 유사도 = 0

d1 = '바다 하늘 육지'
d2 = '바다 들판 우주'
자카드 유사도 = 1/5

 


단어가방(Bag Of Words): corpus에 등장한 단어들의 빈도수 조사

ex) array([[1, 2, 2, 1, 1, 1]])

from sklearn.feature_extraction.text import CountVectorizer
#CountVectorizer: 단어의 빈도수로 벡터 생성
corpus=['나는 지금 자연어 처리를 공부해요. 나는 자연어 좋아요.']
vector=CountVectorizer()
vector.fit_transform(corpus)
#fit: 각각 유니크한 단어별로 인덱스 부여
#transform: corpus에 저장된 문장을 fit결과에 맞춰 변환(빈도수)해라

ex)
corpus=['나는 지금 자연어 처리를 공부해요. 나는 자연어 좋아요.']
fit 각 단어마다 인덱스 (0~5)
transform [2 1 2 1 1 1]

sparse matrix(희소행렬: 행렬 요소값이 대부분 0인 행렬 => 공간 낭비, 연산량이 많음)
[
  [2 1 2 1 1 1]
]

vector.fit_transform(corpus).toarray()
vector.vocabulary_ #인덱스 번호 출력

 

영화 추천 시스템 만들기

영화의 줄거리를 기반으로 추천(tfidf -> cosine sim[코사인유사도] -> 추천)

 

영화 데이터로 유사도 측정해보기

import pandas as pd
from sklearn.metrics.pairwise import cosine_similarity

1.데이터 불러오기

from google.colab import drive
drive.mount('/content/drive')
df = pd.read_csv("/content/drive/MyDrive/NLP/movies_metadata.csv")

2.상위 5000개의 영화데이터만 추출하여 변수에 저장

df = df.head(10000)
df

3.description열 추출

결측값 처리방법: groupby, fillna, replace, interpolate

df.info()
df.isnull().sum(axis=0) #열단위로 결측값 정보
#Nan을 빈 문자열로 대체하기
df['overview'] = df['overview'].fillna("")
df['overview'].isnull().sum() #결측값 없음

4.TFIDF 행렬 구성 (오천*단어개수)

tfidf = TfidfVectorizer(stop_words = 'english')

# stop_words = 'english' 에 해당하는 단어를 제거하고 작동
tfidf_m = tfidf.fit_transform(df['overview'])
tfidf_m.shape #크기(10000, 32350) 단어 32350개
tfidf_m

5.코사인 유사도

cos_sim = cosine_similarity(tfidf_m, tfidf_m)
cos_sim.shape
cos_sim[0] #토이스토리와 다른 영화 사이의 코사인 유사도

6.자기 자신 제외하고 그중에서 상위 10개 찾기

df['title'] # title 행인덱스, 테이터 출력
df.index# 행의 인덱스 번호가 나온다.
data = dict(zip(df['title'],df.index)) # key-value 쌍
i = data['Toy Story']
print(i)
data
def recommend(t, cos_sim=cos_sim): #t:영화 제목 전달
  idx = data[t] #해당 영화의 인덱스 값

  #내림 차순 정렬후 상위 10뽑기, 제일 높은 값은 자기 자신이니 0번 제거. 1번부터 10까지
  sim_scores = list(enumerate(cos_sim[idx]))

  ss = sorted(sim_scores, key = lambda x:x[1],reverse = True)#인덱스번호, 코사인유사도

  ss = ss[1:11]

  res = [i[0] for i in ss]#상위 10개 인덱스 번호

  return df['title'].iloc[res]#상위 10개 인덱스와 영화 제목
recommend('Toy Story')

단어가방(Bag Of Words)

corpus에 등장한 단어들의 빈도수 조사

ex) array([[1, 2, 2, 1, 1, 1]])

from sklearn.feature_extraction.text import CountVectorizer
#CountVectorizer: 단어의 빈도수로 벡터 생성
corpus=['나는 지금 자연어 처리를 공부해요. 나는 자연어 좋아요.']
vector=CountVectorizer()
vector.fit_transform(corpus)
#fit: 각각 유니크한 단어별로 인덱스 부여
#transform: corpus에 저장된 문장을 fit결과에 맞춰 변환(빈도수)해라

ex)
corpus=['나는 지금 자연어 처리를 공부해요. 나는 자연어 좋아요.']
fit 각 단어마다 인덱스 (0~5)
transform [2 1 2 1 1 1]

sparse matrix(희소행렬: 행렬 요소값이 대부분 0인 행렬 => 공간 낭비, 연산량이 많음)
[
  [2 1 2 1 1 1]
]

 

vector.fit_transform(corpus).toarray()
vector.vocabulary_ #인덱스 번호 출력

TFIDF(TF * IDF)

참고 문서 링크: https://ko.wikipedia.org/wiki/Tf-idf

 

tf-idf - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. TF-IDF(Term Frequency - Inverse Document Frequency)는 정보 검색과 텍스트 마이닝에서 이용하는 가중치로, 여러 문서로 이루어진 문서군이 있을 때 어떤 단어가 특정 문서

ko.wikipedia.org

 

1. DTM 만들기

from sklearn.feature_extraction.text import CountVectorizer,TfidfVectorizer
#CountVectorizer: DTM을 만든다.
#TfidfVectorizer :
corpus=[#문서 3개가 있다고 가정
    'I love you.',
    'I can do it',
    'I want your love'
]
#행 문서 | 열 단어 종류 개수
v = CountVectorizer()
v.fit_transform(corpus)
#유니크한 단어를 뽑고 빈도수 조사
#fit정보를 바탕으로 수치로 변환
v.fit_transform(corpus).toarray()
#3행 7열: 문서를 구성하는 단어들을 수치로 표시
#출력값은 DTM이다. CountVectorizer으로 DTM을 만든다.
v.vocabulary_
#인덱스번호와 함께 딕셔너리 형태로 출력
#내부적으로 I의 단어길이가 2보다 작아서 없어짐.​

2.TFIDF객체 생성하기

tfv = TfidfVectorizer()
tfv.fit_transform(corpus).toarray()
# tfidf 형태로 출력

# tfidf값이 크다 tf , idf 모두 크다 -> 단어의 빈도수가 큼
#idf가 크려면 분모의 df(t)가 커질수록 =  idf가 작아짐 =   단어가 많은 문서에서 등장, 단어가 흔하다.는 의미
#특정 단어가 많이 등장하면서, 흔치 않은 단어여야한다.

#ex) "love" 단어는 여러문서에 걸쳐서 나온다. can은 흔하지 않은 단어다.
#한 문서에서 'love'와 'can'이 등장하는 빈도는 같지만 'love'는 다른 문서에도 흔함. 'can'은 이 문장에서만 볼 수 있어 흔치 않다.
#'can'이 더 중요한 요소다.

# 각각의 문서에서 중요한 단어가 무엇인지 알 수 있음.

NLP 인코딩 과정 정리

1.문서구성 단어 추출

2.DTM 구성: 문서단위로 단어의 빈도수 행렬

3.유클리디안 거리가 아닌 코사인 유사도를 구함
-> 단순히 두벡터의 거리만 아니라 두벡터간 각도를 이용해 유사도를 나타냄

4.DTM을 TFIDF로 변환해서 사용

5,TFIDF로 코사인유사도을 이용해서 유사한 문서를 검색
(1)해당 단어를 매우 중요한 단어로 사용한다는 것을 구분할 수 있음
(2)빈도수가 많지만 다른 문서에서도 등장하면 중요하지 않은 단어
(3)다른 문서와 비교해서 중요도를 알 수 있어 tfidf를 사용한다.

ex) 'The' 단어는 빈도수 높음, 단어를 포함하는 문서 많음, tfidf값 작음.  -> 중요한 단어 아님

 

참고)
TF-IDF 값은 한 문서에서 단어가 등장하는 빈도가 높을수록 커지고(term frequency),
반대로 코퍼스에서 해당 단어를 포함하는 문서가 많을수록 반비례해서 작아진다(inverse document frequency).

특정 문서 내에서 단어 빈도가 높을 수록, 그리고 전체 문서들 중 그 단어를 포함한 문서가 적을 수록 TF-IDF값이 높아진다.
따라서 이 값을 이용하면 모든 문서에 흔하게 나타나는 단어를 걸러내는 효과를 얻을 수 있다.
IDF의 로그 함수 안의 값은 항상 1 이상이므로, IDF값과 TF-IDF값은 항상 0 이상이 된다.
특정 단어를 포함하는 문서들이 많을 수록 로그 함수 안의 값이 1에 가까워지게 되고,
이 경우 IDF값과 TF-IDF값은 0에 가까워지게 된다.