본문 바로가기
Study/Machine learning,NLP

NLP(3)-문서 군집화,연관분석,추천시스템

by 왕방개 2024. 3. 14.

1.문서 군집화

1)개요

=>비슷한 구성의 텍스트 문서를 군집화하는 것

=>텍스트 문서들을 읽어서 피처화 한 후 군집 알고리즘을 적용

=>영화의 줄거리가 있는 경우 비슷한 장르의 영화를 군집화 하는 것이 가능

우리나라 VOD 서비스는 대부분 장르를 업로드하는 곳에서 선택합니다

동일한 VOD인데 장르가 다르게 설정되기도 합니다.

 

2)문서 군집화 수행

=>데이터:Opinosis 데이터셋

 

여러 개의 텍스트를 읽어야 할 때는 되도록이면 하나의 디렉토리에 모아놓으면 편리합니다.

 

glob 모듈을 이용해서 경로를 지정하면 경로 안에 있는 모든 파일명을 찾아올 수 있습니다.

확장자 지정도 가능하기 때문에 되도록이면 확장자도 맞추는 것이 좋습니다.

확장자는 or 가 가능합니다.

 

로그파일은 하나로 구성하는 경우가 별로 없기 떄문에 로그를 저장할 디렉토리를 생성하고 그 안에 로그파일들을 모아서 저장하면 데이터 분석에서 편리합니다.

클라우드를 이용하는 경우에도 되도록 로그 파일은 별도의 버킷이나 디렉토리에 모으는 것이 중요합니다

지금은 마이크로 서비스 형태로 구현하기 떄문에 로그를 로컬에 넣어두면 서로 다른 디렉토리에 로그가 쌓이게 됩니다.

 

마이크로 서비스?

R와 CUD를 나눠서 로그를 저장합니다. 아무래도 읽기를 더 많이 하기 때문에 한번에 정리하기 편하기 떄문입니다. 요새는 퍼블릭 클라우드에 따로 모아놓거나 AWS 을 이용합니다.  로그는 날짜 형태로 저장을 많이 함. 서비스를 나누게 되면 R과 CUD 하는 사람들끼리 서비스를 보통 나눠서 작업을 하게 되는데 glob 을 사용하면 따로 분리할 이유가 없음. 로그는 각각의 로컬이 아닌 하나의 공통된 버킷에다가 저장해야함. 클라우드에 있는 데이터를 바로 가져올 수 있는 능력이 필요. 클라우드를 하라는게 아닌 데이터가 어떤 환경에 있을지 모르니까 클라우드에 있는 데이터랑 데이터베이스안에 있는 데이터만큼은 원할때 뽑아낼 수 있어야함

 

=>읽을 파일 경로 만들기

#읽어야 하는 파일의 목록 만들기

import glob, os
path = "C:/Users/User/Desktop/데이터/OpinosisDataset1.0/OpinosisDataset1.0/topics"
# path 에 있는 확장자가 data인 모든 파일의 경로를 저장
all_files = glob.glob(os.path.join(path,"*.data"))
print(all_files)

 

#데이터를 읽어서 저장

#파일의 이름을 저장할 list
filename_list =[]

#텍스트를 저장할 list
opinion_text = []

#모든 파일의 내용을 읽어서 저장

for file_ in all_files:
    #latin 1은 서유럽 인코딩 - 한글이 인됨
    df = pd.read_table(file_,index_col=None, header=0,encoding='latin1')
    # 파일이름만 저장
    filename_ = file_.split("\\")[-1] #디렉토리 기호로 구분해서 마지막 부분을 저장
    filename_ = filename_.split(".")[0]#확장자 제거
    filename_list.append(filename_)

    #내용 저장
    opinion_text.append(df.to_string())
document_df = pd.DataFrame({'filename': filename_list, 'opinion_text':opinion_text})
document_df.head()

 

=>텍스트를 벡터화

#영문 품사 태깅

from nltk.stem import WordNetLemmatizer
import nltk
import string
nltk.download('wordnet')

import nltk
nltk.download('omw-1.4')
  
#구두점 관련된 딕셔너리 생성
#구두점을 None으로치환하기 위한 딕셔너리
#앞의 숫자는 아스키 코드(ASCII Code)값
remove_punct_dict = dict((ord(punct),None) for punct in string.punctuation)
#print(remove_punct_dict)

lemmar = WordNetLemmatizer()

#품사 태깅 해주는 함수
def LemTokens(tokens):
    return [lemmar.lemmatize(token) for token in tokens]

#품사 태깅과 구두점을 제거하는 함수
def LemNormalize(text):
    return LemTokens(nltk.word_tokenize(text.lower().translate(remove_punct_dict)))

from sklearn.feature_extraction.text import TfidfVectorizer

#문장에서 min_df 가 0.05 미만이거나 max_df 0.85이상이면 의미없다 판단하고 삭제
tfidf_vect = TfidfVectorizer(tokenizer = LemNormalize, stop_words = 'english',
                            ngram_range=(1,2),min_df = 0.05,max_df = 0.85)

feature_vect = tfidf_vect.fit_transform(document_df['opinion_text'])

 

=>KMeans 로 군집

from sklearn.cluster import KMeans

km_cluster = KMeans(n_clusters=5,max_iter=10000,random_state=42)
km_cluster.fit(feature_vect)

cluster_label = km_cluster.labels_
cluster_centers = km_cluster.cluster_centers_

print(cluster_label)
print(cluster_centers)

document_df ['cluster_label'] = cluster_label

document_df.head()

 

3)코사인 유사도

=>문장을 벡터로 만들어서 거리를 측정

=>벡터의 크기보다는 벡터의 방향성이 얼마나 유사한지에 기반

두 벡터 사이의 사잇각을 구해서 얼마나 유사한지를 수치로 적용

=>유사한 벡터들은 방향이 같고, 관련성이 없는 벡터들은 방향이 일치하지도 않고, 반대 방향이 아닌 경우

 

4)코사인 유사도 API

=sklearn. feature_extraction.text 패키지를 이용해서 전처리를 하고 sklearn.metrics.pairwise 패키지의 cosin_similarity 함수를 이용해서 측정

#코사인 유사도를 측정하는 함수 - API 로 제공됨

def cos_similiarity(v1,v2):
    dot_product = np.dot(v1,v2)
    L2_norm = (np.sqrt(sum(np.square(v1))*np.sqrt(sum(np.square(v2)))))
    similarity = dot_product / L2_norm

    return similarity
# 샘플 데이터 생성

doc_list = ['I love you','I like you','I love movie']

#문장을 수치 데이터로 변환 - 피처 벡터화
#영문은 바로 가능하고 한글은 형태소 분석을 수행한 후 작업

from sklearn.feature_extraction.text import TfidfVectorizer
tfidf_vect = TfidfVectorizer()
feature_vect = tfidf_vect.fit_transform(doc_list)
print(feature_vect)

 

=>밀집 행렬로 변환 - 희소 행렬은 거리 계산이 안됨

feature_vect_dense = feature_vect.todense()
print(feature_vect_dense)

I 는 불용어로 없어짐

 

=>유사도 출력 = 1이 가장 가까운 값, 0 이 가장 먼 값

vect1 = np.array(feature_vect_dense[0]).reshape(-1,)
vect2 = np.array(feature_vect_dense[1]).reshape(-1,)
vect3 = np.array(feature_vect_dense[2]).reshape(-1,)


print(cos_similiarity(vect1,vect2))
print(cos_similiarity(vect1,vect3))
print(cos_similiarity(vect2,vect3))

거리는 크게 의미가 없는게, 만약 단어가 추가되도 거리 자체에 대한 영향은 크게 없음

 

5)문서 군집이 이 원리를 이용해서 유사도 값이 큰 것끼리 묶는 것입니다

 

#거리를 측정할 벡터 2개 대입
#API 에서는 희소 행렬을 대입하면 밀집 행렬로 변환해서 유사도 측정
from sklearn.metrics.pairwise import cosine_similarity
cos_similiarity_value = cosine_similarity(feature_vect[0],feature_vect)
print(cos_similiarity_value)

 

6)한글 유사도 분석

contents=['안녕하세요 반갑습니다.','나는 고기를 좋아합니다.',
         '우리 과일 먹으러 가자','나는 공원에서 산책하는 것을 좋아해요.',
         '안녕하세요 반갑습니다.']

#한글 형태소 분석
from konlpy.tag import Twitter

twitter = Twitter()
contents_tokens = [twitter.morphs(row) for row in contents]
print(contents_tokens)
# 형태소 분석을 수행한 후 문장으로 변환

contents_for_vectorize =[]

for content in contents_tokens:
    sentence=''
    for word in content:
        sentence = sentence + ' ' + word
    contents_for_vectorize.append(sentence)

print(contents_for_vectorize)

feature_vect = tfidf_vect.fit_transform(contents_for_vectorize)
print(tfidf_vect.get_feature_names_out())
print(feature_vect.toarray().transpose())
new_post = ["우리 과일 먹자"]

new_post_tokens = [twitter.morphs(row) for row in new_post]
new_post_for_vectorize = []

for content in new_post_tokens:
    sentence=''
    for word in content:
        sentence = sentence + ' ' + word
    new_post_for_vectorize.append(sentence)

new_post_vec = tfidf_vect.transform(new_post_for_vectorize)
print(new_post_vec)

 

2.Word2Vec

1)개요

=>단어나 문장을 가지고 다음 단어를 예측하는 것

 

2)Continuous Bag of Words(CBOW)

=>여러 개의 단어를 가지고 다음 단어를 예측하는 것

예시)

'안녕하세요 반갑습니다. 나는 오늘 신사역에 갈 예정입니다.'

위의 문장을 학습 한 후 ( 나는 오늘 신사역에) -> 갈 을 예측

 

3)Skip-Gram

=> 특정 단어를 가지고 다음 단어나 문장을 예측하는 것

=>window size 가 있어서 현재 위치에서 몇 개를 예측할 것인지를 설정

 

예시)

window size 는 1 이라고 하면

나는 -> 오늘

나는 -> 반갑습니다

 

window size 가 2

나는 -> 오늘 

나는 -> 반갑습니다

나는 ->신사역에

나는 ->안녕하세요

 

4)API

=>gensim 이라는 패키지에 Word2Vec 이라는 클래스 구현

=>similarity 라는 함수를 이용해서 2개의 단어의 유사도를 측정해주고 most_similar 라는 함수를 이용해서 가장 유사한 단어를 리턴해주고 positive 인수와 negative 인수를 이용해서 단어간 관계도 찾을 수 있습니다

 

5)네이버 지식인 검색 결과를 이용한 가장 유사한 단어 찾기

=>url : https://search.naver.com/search.naver?where=kin&sm_tab_jum&ie=utf8&query=검색어&kin_start=인덱스

=>텍스트 크롤링

#크롤링

from bs4 import BeautifulSoup #HTML 파싱
import urllib #검색어 인코딩
import requests # html 가져오기
import time # 슬립을 사용하기 위해서
#크롤링 한 결과를 저장할 변수
present_candi_text =[]
url = "https://search.naver.com/search.naver?where=kin&sm_tab_jum&ie=utf8&query="+urllib.parse.quote('화이트데이')+"&kin_start="

for n in range(1,1000,10):
    #html 문자열 가져오기
    response = requests.get(url + str(n))

    #파싱
    soup = BeautifulSoup(response.text,'html.parser')
    tmp = soup.select(' div.question_area > div > a')
    for line in tmp:
        present_candi_text.append(line.getText())
    time.sleep(0.5)

print(present_candi_text)

 

=>한글 형태소 분석

from konlpy.tag import Twitter
twitter = Twitter()

present_text =''

for each_line in present_candi_text:
    present_text = present_text + each_line +'\n'

tokens_ko = twitter.morphs(present_text)
print(tokens_ko)
#가장 많이 등장한 단어

import nltk

ko = nltk.Text(tokens_ko,name='화이트데이')
print(ko.vocab().most_common(50))

이렇게 나왔는데 필요없는 단어들이 너무 많음. 이를 불용어 처리해서 제거해야함

#불필요한 단어 제거
stop_words = ['.',',','\n','?','...','!','가','를','이','입니다','한','고','으로','하고','??','(']

token_ko = [each_word for each_word in tokens_ko
           if each_word not in stop_words]

ko = nltk.Text(tokens_ko,name='화이트데이')
print(ko.vocab().most_common(50))

 

=>가장 많이 나오는 단어 시각화

import matplotlib.pyplot as plt

plt.figure(figsize = (20,10))
ko.plot(50)
plt.show()

=>워드 클라우드

#워드 클라우드

import pytagcloud

data = ko.vocab().most_common(101)

taglist = pytagcloud.make_tags(data,maxsize=200)
for i in taglist:
    if i['tag'] == '화이트데이':
        taglist.remove(i)

pytagcloud.create_tag_image(taglist,'wordcloud.png',size=(900,600),fontname="Korean",rectangular=False)
import matplotlib.image

img = matplotlib.image.imread('wordcloud.png')
plt.imshow(img)

 

=>word2vec을 사용하기 위한 패키지 :gensim

pip insatll gensim

 

=>조사를 없애려고함

from gensim.models import word2vec

twitter =Twitter()
results = []
lines = present_candi_text

for line in lines:
    malist = twitter.pos(line,norm=True,stem=True)
    r = []
    #조사,어미, 구두점을 제거하고 r에 추가
    for word in malist:
        if not word[1] in ['Josa','Eomi','Punctuation']:
            r.append(word[0])

    #앞뒤에있는 좌우 공백 제거
    r1 = (" ".join(r)).strip()
    results.append(r1)

 

data_file ='whitedate.data'

with open(data_file,'w',encoding='utf-8') as fp:
    fp.write('\n'.join(results))
    data = word2vec.LineSentence(data_file)

#sg가 0이면 CBOW 1이면 Skip-Gram
model = word2vec.Word2Vec(data,vector_size = 200, window =10,
                         min_count =2, sg=1)

#연관된 단어 확인 -기본적으로 10개
model.wv.most_similar(positive='화이트데이')

Word2Vec 는 내 문장과 앞 뒤에 있는 단어를 찾아줌. window 를 10으로 설정했기 떄문에 앞뒤 10개 즉 20개를 찾아서 검색합니다.

 

3.연관 규칙 분석

1)개요

=>원본 데이터에서 대상들의 연관된 규칙을 찾는 무방향성 데이터 마이닝 기법

=>하나의 거래나 사건에 포함된 항목간의 관련성을 파악해서 둘 이상의 항목별로 구성된 연관성 규칙을 찾는 탐색적인 방법

=>트랜잭션을 대상으로 트랜잭션 내의 연관성을 분석해서 상품 거래의 규칙이나 패턴을 찾아서 상품 간의 연관성을 도출해내는 분석 방법

 

=>장점

- 조건 반응 표현식의 결과를 이해하기 쉽다.

-편리한 분석 데이터 형태

 

=>단점

-계산 과정에서 연산을 많이 수행

 

=>순차분석

-연관 규칙 분석은 무방향성이라서 A를 산 사람이 B를 살 확률을 확인하는 것이고 순차 분석은 A를 사고 B를 살 확률

-순차 분석은 순서대로

 

2)과정

=>거래 내역 데이터를 가지고 트랜잭셩 객체 생성

=>품목과 트랜잭셩 ID를 관찰

=>지지도,신뢴도 향상도를 이용한 연관 규칙 발견

=>시각화

=>업무 적용

 

3)평가 지표

=>Support(지지도)

- 전체 거래 건수 중에서 X 와 Y 모두 포함된 건수의 비

- X와 Y 모두 포함한 거래 건수 /전체 거래 건수

-전체 품목에서 관련 품목의 거래 확률

-지지도가 낮다는 것은 해당 규칙이 자주 발생하지 않는다는 의미

 

=>Confidence(신뢰도)

- X를 구매한 거래 중에서 Y를 포함하는 거래의 건수의 비

  X와 Y 모두 포함한 거래 건수 / X를 포함하는 거래 건수

 

=>Lift(향상도)

-신뢰도를 지지도로 나눈 값

-두 항목의 독립성 여부를 판단하는 수치

- 1이면 서로 독립이고, 1보다 작으면 음의 상관관계이고 1보다 크면 양의 상관관계를 의미하는 것으로 값이 클루솕 연관성이 높은 것

 

4)API 

=>apyori 패키지의 apriri모듈이 제공

하이퍼 파라미터는 min_support, min_confidence, min_lift

=일반 텍스트 데이터에 적용이 가능

import re

def text_cleaning(text):
    hangul = re.compile('[^ㄱ-ㅣ 가-힣]')
    result = hangul.sub('',text)
    return result

df ['ko_text'] = df['tweet_text'].apply(lambda x: text_cleaning(x))
df.head()

#한국어 불용어 사전 만들기 - https://www.ranks.nl/stopwords/korean

with open("./korean_stopwords.txt",encoding='utf8') as f:
    stopwords = f.readlines()

stopwords = [x.strip() for x in stopwords]
print(stopwords)

 

=>불용어랑 1글자는 제거하는 함수 생성

#문자열을 대입받아서 명사만 추출하고 1글자는 제거하고 불용어도 제거해서 리턴하는 함수
from konlpy.tag import Okt

def get_nouns(x):
    nouns_tagger =Okt()
    nouns = nouns_tagger.nouns(x)
    
    #한글자 제거
    nouns = [noun for noun in nouns if len(noun)>1]

    #불용어 제거
    nouns = [ noun for noun in nouns if noun not in stopwords]
    return nouns

df['nouns'] = df['ko_text'].apply(lambda x: get_nouns(x))
print(df.head())

 연관 규칙 분석을 하고자 할 때는 데이터가 리스트 형태로 만들어지면 됩니다.

상품 거래 연관 규칙 분석을 하고자 할 떄 구매한 상품목록을 list 로 만들면 됩니다.

예시)

['사과','배','한라봉']

=>연관 규칙 분석

#연관 규칙 분석
from apyori import apriori

transactions = [['사과','배','맥주'],['사과','한라봉'],
               ['과자','콜라']]

results = list(apriori(transactions))
for result in results:
    print(result)

#읽어온 데이터에 연관 규칙 분석 적용
transactions = df['nouns'].tolist()

#비어있는 list 제거
transactions = [transaction for transaction in transactions if transaction]
print(transactions)

 

results = list(apriori(transactions,max_length=2, min_support=0.1 , min_confidence=0.2,min_lift=5))
for result in results:
    print(result)

 

#연관 규칙 분석한 결과를 DataFrame으로 변환

columns = ['source','target','support']
network_df = pd.DataFrame(columns = columns)


for result in results:
    items = [ x for x in result.items]
    row = [items[0],items[1],result.support]
    series = pd.Series(row,index = network_df.columns)
    network_df.loc[len(network_df)] = series

print(result)

 

#데이터를 저누 추출
tweet_corpus = "".join(df["ko_text"].tolist())
print(tweet_corpus)

#한글 형태소 분석을 수행한 후 등장 횟수 구하기

from konlpy.tag import Okt
from collections import Counter

nouns_tagger = Okt()
nouns = nouns_tagger.nouns(tweet_corpus)
count = Counter(nouns)

#1글자 제거
remove_char_counter = Counter({x:count[x] for x in count if len(x)>1})
print(remove_char_counter)

 

=> networkx 패키지

- 그래프를 다루기 위한 패키지

- 그래프를 만드는 Graph(방향이 없음), DiGraph(방향성 그래프) 클래스를 제공

- node와 edge 로 구성

- node 를 추가하면 동그라미가 추가되고, edge을 추가하면 선이 추가됨

import networkx as nx

#이미지 크기 설정
plt.figure(figsize = (25,25))

#그래프 인스턴스 생성
G = nx.Graph()

#노드 추가
for index, row in node_df.iterrows():
    G.add_node(row['node'],nodesize=row['nodesize'])

#간선 추가
for index,row in network_df.iterrows():
    G.add_weighted_edges_from([(row['source'],row['target'],row['support'])])
    

#디자인을 설정
pos = nx.spring_layout(G,k=0.6,iterations=50)
sizes = [G.nodes[node]['nodesize'] * 25 for node in G]
nx.draw(G,pos=pos,node_size =sizes)

#레이블 출력 - 한글의 경우는 한글 폰트를 선택
nx.draw_networkx_labels(G,pos =pos,font_family='Malgun Gothic',font_size =25)

ax = plt.gca()
plt.show()

 

4.추천 시스템

1)추천 시스템 알고리즘

=>콘텐츠 기반 필터링

=>협업 필터링

-최근접 이웃 협업 필터링

-잠재 요인 협업 필터링

 

=>초창기에는 콘텐츠 기반 필터링이나 최근접 이웃 필터링을 주로 이용했는데 넷플릭스 추천 시스템이 행렬 분해 기법을 이용한 잠재 요인 협업 필터링을 사용하면서 최근에는 잠재요인 협업 필터링을 주로 이용하고 두가지 알고리즘을 혼합해서 사용

 

2)콘텐츠 기반 필터링

=>사용자가 특정한 아이템을 선호하는 경우 그 아이템과 비슷한 콘텐츠를 가진 다른 아이템을 추천하는 방ㅇ식

어떤 영화를 보고 추천하거나 높은 평점을 제시하는 경우, 그 영화의 장르나 배우, 감독등을 파악해서 유사한 다른 영화를 추천

 

 

3) 협업 필터링

=> 사용자가 아이템에 매긴 평점 정보나 상품 구매 이력 같은 사용자 행동 양식만을 기반으로 추천

=>사용자-아이템 평점 매트릭스와 같은 축적된 사용자 행동 데이터를 기반으로 사용자가 아직 평가하지 않은 아이템을 예측 평가하는 것

 

 

               item1 item2 item3 item4

user1        3                   3

user2        4         2                 3

user3                   1        2       2

 

이 경우 user 1 에게 item 4를 추천합니다. user 1이 산 물건을 user 2와 user3 이 구매를 했는데 , item 2보다 item 4에 더 높은 평점을 매겼으므로 item 4를 추천

 

=>최근접 이웃 협업 필터링

-사용자 기반 : 나와 유사한 고객의 다음 상품도 구매하도록 하는 것

 

 

-아이템 기반:  이 상품을 선택한 다른 고객들은 다음 상품도 구매

 

대체적으로 아이템 기반을 더 많이 사용하는데 사용자 기반은 점수를 다 매겨놔야만 가능하기 때문입니다. 

 

=>잠재요인 협업 필터링

-사용자 - 아이템 평점 매트릭스 속에 숨어있는 잠재 요인을 추출해서 추천 예측을 할 수 있게 하는 기법

- 최근접 이웃 협업 필터링을 실무에서 사용하기 어려운 이유는 사용자와 유사한 아이템을 구매한 사용자를 찾는 것은 쉽지 않은 일

쇼핑몰이나 넷플릭스 OTT 같은 기업의 데이터는 굉장히 많아서 내가 산 상품을 전부 산 또는 내가 본 영화를 전부 본 유저를 찾기는 쉽지 않습니다

-희소 행렬을 작은 단위의 밀집 행렬로 분해를 한후 다시 결합을 하면 희소 행렬에서 0이었 부분을 채우게 됩니다

행렬 분해를 하는 방법은 여러가지 방법이 존재합니다. (SVD 등등)

 

4)콘텐츠 기반 필터링

=>데이터: TMBD 5000 이라는 영화 관련 데이터

=>https://www.kaggle.com/datasets/tmdb/tmdb-movie-metadata

movies data에는 genre와 keywords column이 존재

 

genres 와 keywords 는 하나의 문자열이 아니고 list 입니다
csv 파일을 만드려고 list 을 문자열로 만들기 위해서 ""로 감싸는 형태로 만듬
파이썬이나 자바스크립트는 json 포맷의 문자열로 데이터를 표현합니다. 파이썬이나 자바스크리트에서는 문자열로 만들어진 데이터를 원래의 자료형으로 되돌리는 API 가 제공됩니다.

python에서는 ast 모듈의 literal_eval 이라는 함수를 이용하면 됩니다

 

예시)

"[apple,pear,watermelon]" ->literal_eval(데이터) -> [apple,pear,watermelon] 

 

#원하는 컬럼만 추출

movies_df = movies[["id","title","genres","vote_average","vote_count",
                   "popularity","keywords","overview"]]

print(movies_df.head)
print(movies_df[['genres','keywords']][:1])

이런식으로 존재해서 이를 끄집어내야함

 

from ast import literal_eval

#keywords 와 genres 데이터를 list 로 변환

movies_df ['genres'] = movies_df['genres'].apply(literal_eval)
movies_df ['keywords'] = movies_df['keywords'].apply(literal_eval)

#dict에서 name 의 값만 모아서 list 생성
movies_df ['genres']= movies_df['genres'].apply(lambda x: [y['name'] for y in x])
movies_df ['keywords']= movies_df['keywords'].apply(lambda x: [y['name'] for y in x])

print(movies_df[['genres','keywords']][:1])

 

=>장르 유사도 측정

-문자열을 feature 로 만드는 방법은 CountVectorizer(등장횟수), TfidfVectorizer(등장횟수에 따른 가중치를 부여하는 방식)

-장르를 feature 벡터화

from sklearn.feature_extraction.text import CountVectorizer
#콤마 제거
movies_df['genres_literal'] = movies_df['genres'].apply(lambda x :(' ').join(x))
#print( movies_df['genres_literal'][:1])

#피처벡터화 수행

count_vect = CountVectorizer(min_df = 0.0 , ngram_range=(1,2))
genre_mat = count_vect.fit_transform(movies_df['genres_literal'])
print(genre_mat.shape)
#장르의 코사인 유사도 측정

from sklearn.metrics.pairwise import cosine_similarity

genre_sim = cosine_similarity(genre_mat,genre_mat)
print(genre_sim[:1])

genre_sim_sorted_ind = genre_sim.argsort()[:,::-1]
print(genre_sim_sorted_ind[:1]) #영화 인덱스 조회

=>영화 정보를 출력하는 함수

#영화정보 데이터프레임과 영화정보 인덱스 와 영화 제목 과 추천할 영화 개수를 입력하면
#영화 제목과 평점을 리턴해주는 함수

def find_sim_movies(df,sorted_idx, title_name, top_n=10):
    #title_name 에 해당하는 영화의 인덱스를 찾아오기
    title_movie = df[df['title']==title_name]
    title_index = title_movie.index
    #이영화에 해당하는 데이터와 코사인 유사도가 가장 높은 top_n 개의 인덱스 추출
    #자기자신을 제외하고자 하면 :(top_n)을 1:(top_n+1)로 수정
    title_index = title_movie.index.values
    similar_indexes = sorted_idx[title_index,:(top_n)]
    
    #1차원배열로 변횐
    similar_indexes = similar_indexes.reshape(-1)


    return df.iloc[similar_indexes]

 

=>출력

similar_movies = find_sim_movies(movies_df, genre_sim_sorted_ind,
                               'The Godfather',10)

print(similar_movies)

similar_movies = find_sim_movies(movies_df, genre_sim_sorted_ind,
                               'Avatar',10)

print(similar_movies[['title','vote_average','vote_count']])

vote_average 나 vote_count에 기준을 좀 줘야 더 좋아보임

similar_movies = find_sim_movies(movies_df, genre_sim_sorted_ind,
                               'Avatar',10)

print(similar_movies[['title','vote_average','vote_count']]
     .sort_values('vote_average',ascending=False))

 

=>가중 평점 

-평점을 많이 매기지 않는 데이터가 평점이 높게 나오거나 낮게 나오는  경향이 있을 수 있습니다.

-데이터가 적으면 극단치의 영향을 받기 때문입니다.

-데이터 개수에 따른 보정을 해줄 필요가 있습니다.

- IMDB 에서 권장한거는 (v/(v+m))* R + (m/(v+m))*C 

v:개별 영화에 부여한 평점

m: 평점을 부여하기 위한 최소 투표 횟수

R: 개별 영화에 평균 평점

C: 전체 영화에 대한 평균 평점

#가중평점 부여

C = movies_df ['vote_average'].mean()
m = movies_df ['vote_count'].quantile(0.6)
print("C:",C)
print("m:",m)

def weighted_vote_average(record):
    v = record['vote_count']
    R = record['vote_average']

    return((v/(v+m))* R + (m/(v+m))*C )
    
movies_df['weighted_vote'] = movies_df.apply(weighted_vote_average,axis=1)
print(movies_df[['title','vote_average','vote_count','weighted_vote']][:10])
print(movies_df[['title','vote_average','vote_count','weighted_vote']].sort_values('weighted_vote',ascending=False)[:10])

 

항상 가중치를 부여하는 부분에 고민을 해봐야합니다. 또 다른 가중치를 주기도 하는데, 컨텐츠가 만들어진 날짜에 가중치를 부여합니다. 최근에 끝난 것을 가중치를 부여하는것에 대해 생각해볼 수 있습니다. 

 

'Study > Machine learning,NLP' 카테고리의 다른 글

NLP(2) - 실습  (0) 2024.03.14
NLP(1)- 자연어 처리  (0) 2024.03.13
머신러닝(10)-군집  (0) 2024.03.11
머신 러닝(9)-차원 축소  (0) 2024.03.07
머신러닝(8)-실습(2)  (0) 2024.03.07