뮤트 개발일지

AIFFEL 아이펠 37일차 본문

AIFFEL

AIFFEL 아이펠 37일차

박뮤트 2022. 2. 21. 11:09

사이킷런을 활용한 추천 시스템 입문

추천 시스템

1. 범주형 데이터를 다룬다: 액션, 로맨스, 스릴러 등이 영화 item 데이터와 a, b, c 같은 user 데이터를 취급한다. 

2. (숫자 벡터로 변환한 뒤) 유사도를 계산한다: 범주형 데이터들을 좌표에 나타내는데, 좌표에 나타내기 위해서는 숫자로 이뤄진 벡터(numerical vector)로 변환해야 한다. 그리고 그 거리를 계산하여 유사도를 계산한다.

 

코사인 유사도

두 벡터 간의 코사인 값을 이용해 두 벡터의 유사도를 계산한다. 코사인 유사도는 두 벡터의 방향이 이루는 각에 코사인을 취해 구하는데, 두 벡터의 방향이 동일한 경우는 1, 90도의 각을 이루면 0, 반대방향이면 -1을 가진다. 따라서 코사인 유사도는 -1 ~ 1의 값이고, 1에 가까울 수록 유사도가 높다고 할 수 있다.

사이킷런을 활용하면 쉽게 코사인 유사도를 구할 수 있다.

from sklearn.metrics.pairwise import cosine_similarity

t1 = np.array([[1, 1, 1]])
t2 = np.array([[2, 0, 1]])
cosine_similarity(t1,t2)

>>> array([[0.77459667]])

추천 시스템의 종류

1. 콘텐츠 기반 필터링

2. 협업 필터링

 - 사용자 기반

 - 아이템 기반

 - 잠재요인 협업 필터링 -> 행렬 인수분해

3. Deep Learning 적용 or Hybrid 방식

(오늘 노드에서는 1, 2번만 살펴볼 것)

콘텐츠 기반 필터링(Content Based Filtering)

콘텐츠의 내용만을 비교해서 추천하는 방식이다. 장르, 배우, 감독 등 정보는 영화의 특성Feature이 되고, 이 특성이 콘텐츠가 비슷하다고 말할 수 있는 요인이 된다.

 

실습)

# 모듈 import
import pandas as pd
import numpy as np
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.metrics.pairwise import cosine_similarity

# 데이터 로드
import os
csv_path = os.getenv('HOME')+'/aiffel/movie_recommendation/movie_dataset.csv'
df = pd.read_csv(csv_path)

# 특성 선택
df.columns
Index(['index', 'budget', 'genres', 'homepage', 'id', 'keywords',
       'original_language', 'original_title', 'overview', 'popularity',
       'production_companies', 'production_countries', 'release_date',
       'revenue', 'runtime', 'spoken_languages', 'status', 'tagline', 'title',
       'vote_average', 'vote_count', 'cast', 'crew', 'director'],
      dtype='object')

위의 많은 특성 중 아래 특성만 골라서 사용할 것

features = ['keywords','cast','genres','director']
def combine_features(row):
    return row['keywords']+" "+row['cast']+" "+row['genres']+" "+row['director']

combine_features(df[:5])
0    culture clash future space war space colony so...
1    ocean drug abuse exotic island east india trad...
2    spy based on novel secret agent sequel mi6 Dan...
3    dc comics crime fighter terrorist secret ident...
4    based on novel mars medallion space travel pri...
dtype: object
for feature in features:
    df[feature] = df[feature].fillna('')

df["combined_features"] = df.apply(combine_features,axis=1)
df["combined_features"]
0       culture clash future space war space colony so...
1       ocean drug abuse exotic island east india trad...
2       spy based on novel secret agent sequel mi6 Dan...
3       dc comics crime fighter terrorist secret ident...
4       based on novel mars medallion space travel pri...
                              ...                        
4798    united states\u2013mexico barrier legs arms pa...
4799     Edward Burns Kerry Bish\u00e9 Marsha Dietlein...
4800    date love at first sight narration investigati...
4801     Daniel Henney Eliza Coupe Bill Paxton Alan Ru...
4802    obsession camcorder crush dream girl Drew Barr...
Name: combined_features, Length: 4803, dtype: object

벡터화하고, 유사도 구하기. 사이킷런의 Countervectorizer()을 사용하면 편리하다.

cv = CountVectorizer()
count_matrix = cv.fit_transform(df["combined_features"])
print(type(count_matrix))
print(count_matrix.shape)
print(count_matrix)
<class 'scipy.sparse.csr.csr_matrix'>
(4803, 14845)
  (0, 3115)	1
  (0, 2616)	1
  (0, 4886)	1
  (0, 12386)	2
  (0, 14235)	1
  (0, 2755)	1
  (0, 12299)	1
  (0, 11517)	1
  (0, 14561)	1
  (0, 14820)	1
  (0, 11490)	1
  (0, 12134)	1
  (0, 14291)	1
  (0, 12567)	1
  (0, 7496)	1
  (0, 8831)	1
  (0, 11217)	1
  (0, 86)	1
  (0, 144)	1
  (0, 4435)	1
  (0, 11745)	1
  (0, 4566)	1
  (0, 6542)	1
  (0, 2061)	1
  (1, 86)	1
  :	:
  (4801, 10069)	1
  (4801, 5844)	1
  (4801, 252)	1
  (4801, 4098)	1
  (4801, 14796)	1
  (4801, 11361)	1
  (4801, 2978)	1
  (4801, 12036)	1
  (4801, 6138)	1
  (4802, 9659)	1
  (4802, 3812)	1
  (4802, 1788)	2
  (4802, 4210)	1
  (4802, 5181)	1
  (4802, 2912)	1
  (4802, 3821)	1
  (4802, 1069)	1
  (4802, 11185)	1
  (4802, 3681)	1
  (4802, 5399)	1
  (4802, 3894)	1
  (4802, 2056)	1
  (4802, 3093)	1
  (4802, 4502)	1
  (4802, 5900)	2
cosine_sim = cosine_similarity(count_matrix)
print(cosine_sim)
print(cosine_sim.shape)
[[1.         0.10540926 0.12038585 ... 0.         0.         0.        ]
 [0.10540926 1.         0.0761387  ... 0.03651484 0.         0.        ]
 [0.12038585 0.0761387  1.         ... 0.         0.11145564 0.        ]
 ...
 [0.         0.03651484 0.         ... 1.         0.         0.04264014]
 [0.         0.         0.11145564 ... 0.         1.         0.        ]
 [0.         0.         0.         ... 0.04264014 0.         1.        ]]
(4803, 4803)

유사한 영화 3편 추천하기

def get_title_from_index(index):
    return df[df.index == index]["title"].values[0]
def get_index_from_title(title):
    return df[df.title == title]["index"].values[0]

movie_user_likes = "Avatar"
movie_index = get_index_from_title(movie_user_likes)
similar_movies = list(enumerate(cosine_sim[movie_index]))

sorted_similar_movies = sorted(similar_movies,key=lambda x:x[1],reverse=True)[1:]

i=0
print(movie_user_likes+"와 비슷한 영화 3편은 "+"\n")
for item in sorted_similar_movies:
    print(get_title_from_index(item[0]))
    i=i+1
    if i==3:
        break
Avatar와 비슷한 영화 3편은 

Guardians of the Galaxy
Aliens
Star Wars: Clone Wars: Volume 1

 

협업 필터링

과거의 사용자 행동 양식 데이터를 기반으로 추천하는 방식이다.

예시 데이터

user_id: 사용자들의 정보

item_id: 영화 정보

rating: 사용자가 영화를 보고 남긴 평점

timestamp: 평점을 매긴 시각

이 데이터를 사용자와 아이템 간 interaction matrix로 변환한다.

- 사용자 기반

동일한 제품에 대해 평점을 매긴 데이터를 분석하여 추천하는 방식

"당신과 비슷한 고객들이 다음 상품을 구매했습니다."

=> user4에 user2가 긍정적이라고 평점을 준 item3을 추천하는 것!

 

- 아이템 기반

아이템 간의 유사도를 측정하여 해당 아이템을 추천하는 방식. 일반적으로 사용자 기반보다 아이템 기반 방식이 정확도가 더 높다.

"이 상품을 선택한 다른 고객들은 다음 상품을 구매했습니다."

 

행렬 인수분해

잠재요인 협업 필터링은 평점행렬을 행렬 인수분해를 통해 잠재요인을 분석한다. 행렬 인수분해에는 SVD(Singular Vector Decomposition), ALS(Alternating Least Squares), NMF(Non-Negative Factorization)가 있다.

 

SVD 특잇값 분해

M * N 형태의 행렬 A를 UΣV^T의 형태로 분해하는 것

읽어보기...

https://angeloyeo.github.io/2019/08/01/SVD.html

 

특이값 분해(SVD) - 공돌이의 수학정리노트

 

angeloyeo.github.io

 

https://darkpgmr.tistory.com/106

 

[선형대수학 #4] 특이값 분해(Singular Value Decomposition, SVD)의 활용

활용도 측면에서 선형대수학의 꽃이라 할 수 있는 특이값 분해(Singular Value Decomposition, SVD)에 대한 내용입니다. 보통은 복소수 공간을 포함하여 정의하는 것이 일반적이지만 이 글에서는 실수(real

darkpgmr.tistory.com

Truncated SVD

잘린 SVD, 다른 말로 LSA(Latent Semantic Analysis)잠재 의미 분석이라고 한다. 추천 시스템에서 행렬 인수분해는 SVD 중에서도 Truncated SVD를 사용한다. Truncated SVD를 이용해 분해한 뒤 복원하면 SVD처럼 완벽히 같은 행렬이 나오지 않는데, 그 이유는 Truncated SVD는 차원을 축소한 다음 행렬을 분해하기 때문이다. 사이킷런에서는 Truncated SVD 기능을 함수로 제공한다.

 

행렬 인수분해와 잠재요인 협업 필터링

R: 사용자와 아이템 사이의 행렬

P: 사용자와 잠재요인 사이의 행렬

Q: 아이템과 잠재요인 사이의 행렬 -> 전치 행렬로 나타냄

 

사용자가 평점을 매기는 요인을 잠재요인으로 취급한 뒤, SVD 기법을 이용해 분해하고 다시 합치는 방법으로 영화에 평점을 매긴 이유를 벡터화하여 이를 기반으로 추천한다.

'AIFFEL' 카테고리의 다른 글

AIFFEL 아이펠 39일차  (0) 2022.02.23
AIFFEl 아이펠 38일차  (0) 2022.02.22
AIFFEL 아이펠 36일차  (0) 2022.02.18
AIFFEL 아이펠 35일차  (0) 2022.02.18
AIFFEL 아이펠 34일차  (0) 2022.02.17