2018. 8. 9. 17:49ㆍkaggle
영화 추천 시스템 2편
link_small = pd.read_csv('input/links_small.csv')
link_small = link_small[link_small['tmdbId'].notnull()]['tmdbId'].astype('int') md = md.drop([19730, 29503, 35587]) md['id'] = md['id'].astype('int') smd = md[md['id'].isin(link_small)]
2. 영화의 설명을 벡터화 시키기
smd['tagline'] = smd['tagline'].fillna('')
smd['description'] = smd['overview'] + smd['tagline']
smd['description'].fillna('')
np.nan
tf = TfidfVectorizer(analyzer='word', ngram_range=(1, 2), min_df=0, stop_words='english')
tfidf_matrix = tf.fit_transform(smd['description'])
'word'를 기준으로 vector화 시킨다.
min_df, max_df: 문서에서 토큰이 나타난 횟수를 기준으로 단어장을 구성할 수도 있다. 토큰의 빈도가 max_df로 지정한 값을 초과 하거나 min_df로 지정한 값보다 작은 경우에 무시한다.
stop_words='english' 문서에 단어장을 생성할 때 무시할 수 있는 단어를 말한다.
ngram_range(1, 2) 단어장 생성에 사용할 토큰의 크기를 결정한다.
analyzer='word' 문자열 또는 함수
이런다음 TfidfVectorizer 인코딩에 대해 알아보자!
단어를 갯수 그대로 카운트하지 않고 모든 문서에 공통적으로 들어있는 단어의 경우
문서 구별 능력이 떨어진다고 보아 가중치를 축소하는 방법이다.
tf-idf(d,t)=tf(d,t)⋅idf(t)
tf: 특정한 단어의 빈도수
idf: 특정한 단어가 들어 있는 문서의 수에 반비례하는 수
3. cosineSimilarty를 사용하여 두영화 사이에서 유사성을 계산하기
cosine Similarity를 사용해서 두 영화 사이의 유사성을 계산합니다.
cosine(x, y) = (x.y⊺) / (|| x ||. || y ||)
TF-IDF Vectorizer를 사용했기 때문 Dot Product를 계산하면 Cosine Similarity Score가 바로 제공됩니다.
cosine_sim = linear_kernel(tfidf_matrix, tfidf_matrix)
cosine_sim[0]
array([1. , 0.00680476, 0. , ..., 0. , 0.00344913,
0. ])
데이터 정제 중
smd = smd.reset_index() titles = smd['title']
indces = pd.Series(smd.index, index=titles)
smd에 인덱스를 포함하고 타이틀을 만든다. pd.Series를 통해서 타이틀을 인덱스로 하고 indces를 만듭니다. 선형 커널로 만든 consine_sim의 값을
def getrecommandations(title):
index = indces[title]
sim_scores = list(enumerate(cosine_sim[index]))
sim_scores = sorted(sim_scores, key=lambda x:x[1], reverse=True)
sim_scores = sim_scores[1:31]
movie_indices = [i[0] for i in sim_scores]
return titles.iloc[movie_indices]
list(enumerate(cosine_sim[0]))
[(0, 1.0000000000000018), (1, 0.0068047556717484225), (2, 0.0), (3, 0.0), (4, 0.0), (5, 0.0), (6, 0.0), (7, 0.0), (8, 0.0), (9, 0.0), (10, 0.0), (11, 0.0), (12, 0.0), (13, 0.0), (14, 0.0), (15, 0.0), (16, 0.006787806467693803)] 인덱스 값과 입려된 인덱스와 다른 인덱스 영화들이 얼마나 비슷한지 점수가 나온다.
이런 점수를 기준으로 정렬 후 30개의 후보를 빱아서 타이틀을 출력시켜준다.
getrecommandations('The Dark Knight')
4. 감독, 배우등을 고려해서 추천해주기
1. credit과 keywords파일 읽어오기
credits = pd.read_csv('movieRecommaned/input/credits.csv')
keywords = pd.read_csv('movieRecommaned/input/keywords.csv')
1편에 등록된 파일 들 중 하나이다.
keywords['id'] = keywords['id'].astype(int)
credits['id'] = credits['id'].astype(int)
md['id'] = md['id'].astype(int)
int로 형태 변환을 시켜준다.
md = md.merge(credits, on='id')
md = md.merge(keywords, on='id')
id 값을 기준으로 크레딧과 키워드를 md에 합쳐준다.
smd = md[md['id'].isin(link_small)]
동일한 id가 link_small에 존재하는 데이터만 smd에 넣어준다.
2. 데이터 정재하기
2-1. 배우와 감독 데이터 정제하기
smd['cast'] = smd['cast'].apply(literal_eval) #cast데이터를 ''문자열안에 있는 document를
smd['crew'] = smd['crew'].apply(literal_eval) #진짜 document로 만든다.
smd['keywords'] = smd['keywords'].apply(literal_eval)
smd['cast_size'] = smd['cast'].apply(lambda x :len(x)) #배우 수를 적는다.
smd['crew_size'] = smd['crew'].apply(lambda x: len(x)) #스태프 수를 적는다.
스태프들 중에서 감독만 가져온다. 왜냐면 감독도 영화 선택에 영향을 끼치니깐
def get_director(x):
for i in x:
if i['job'] == 'Director':
return i['name']
return np.nan
smd에 감독 데이터를 집어넣는다.
smd['director'] = smd['crew'].apply(get_director)
배우들 이름을 넣는다.
smd['cast'] = smd['cast'].apply(lambda x : [i['name'] for i in x] if isinstance(x, list) else [])
가장 영향력있는 배우 세명을 넣는다.
smd['cast'] = smd['cast'].apply(lambda x: x[:3] if len(x) >3 else x)
키워드도 넣어준다.
smd['keywords'] = smd['keywords'].apply(lambda x: [i['name'] for i in x] if isinstance(x, list) else [])
일단 우리가 원하는 것은 장르, 감독, 주인공, 및 키워드로 이루어진 모든 영화에 대한 메타 데이터 덤프를 만드는 것 입니다.
그런 다음 CountVectorizer를 사용하여 다른 영화들과 얼마나 비슷한지 비교하고 점수를 기준으로 정렬합니다.
smd['cast'] = smd['cast'].apply(lambda x : [str.lower(i.replace(" ", "")) for i in x])
smd['director'] = smd['director'].astype(str).apply(lambda x: str.lower(x.replace(" ", "")))
smd['director'] = smd['director'].apply(lambda x: [x, x, x])
배우와 감독 데이터들을 소문자화 시켜주고 띄어쓰기를 없애줍니다. 그리고 감독은 강조시키기 위해 세번 반복해서 적어줬습니다.
s = smd.apply(lambda x: pd.Series(x['keywords']), axis=1).stack().reset_index(level=1, drop=True)
s.name = 'keywords'
keywords라는 인덱스를 기준으로 각 영화마다의 장르를 동일한 인덱스로 표현할 수 있다.
s = s.value_counts()
independent film 610
woman director 550
murder 399
duringcreditsstinger 327
based on novel 318
Name: keywords, dtype: int64
s = s[s>1]
하나밖에 없는 키워드는 영향력이 적으므로 없애준다.
stemmer = SnowballStemmer('english')
stemmer.stem('dogs')
dogs, dogsss 같은 원형에서 변형된 단어를 원형으로 바꾸어준다. 위와 같은 경우에는 dog가 된다.
def filter_keywords(x):
words = []
for i in x:
if i in s:
words.append(i)
return words
키워드를 골라내서 인덱스에 담는다. 그후 리턴해준다.
위에서 구했던 s는 우리가 직접적으로 사용할 수 있는 데이터인지 아닌지 구분해주기 위한 것 입니다.
smd['keywords'] = smd['keywords'].apply(filter_keywords)
0 [jealousi, toy, boy, friendship, friend, rival...
1 [boardgam, disappear, basedonchildren'sbook, n...
2 [fish, bestfriend, duringcreditssting]
3 [basedonnovel, interracialrelationship, single...
4 [babi, midlifecrisi, confid, age, daughter, mo...
단어들을 원형으로 만들어주고 띄어쓰기를 없애줍니다.
smd['keywords'] = smd['keywords'].apply(lambda x :[stemmer.stem(i) for i in x])
smd['keywords'] = smd['keywords'].apply(lambda x: [str.lower(i.replace(" ", "")) for i in x])
전체적인 데이터들을 합쳐서 soup로 만들어줍니다. 각 soup 사이 데이터마다 띄어쓰기를 추가합니다.
smd['soup'] = smd['keywords']+smd['cast']+smd['director']+smd['genres']
smd['soup'] = smd['soup'].apply(lambda x: ' '.join(x))
단어별로 벡터화 시켜주기
count = CountVectorizer(analyzer='word', ngram_range=(1, 2), min_df=0, stop_words='english')
count_matrix = count.fit_transform(smd['soup'])
CountVectorizer를 이용해서 벡터화 시켜줍니다. 각각의 옵션이 뭘의미하는지는 위의 옵션 설명이 있습니다.
CounterVectorizer는 문서 집합에서 단어 토큰을 생성하고 각 단어의 수를 세어 BOW 인코딩한 벡터를 만든다.
진행하는 순서는 아래와 같습니다.
- 문서를 토큰 리스트로 변환한다.
- 각 문서에서 토큰의 출현 빈도를 센다.
- 각 문서를 BOW 인코딩 벡터로 변환한다.
cosine_sim = cosine_similarity(count_matrix, count_matrix)
4. 영화 추천 받기
smd = smd.reset_index()
titles = smd['title']
indces = pd.Series(smd.index, index=smd['title'])
인덱스를 영화이름으로 만들어줘서 위에서 만들었던 영화 타이틀을 입력하면 영화 리스트를 반환해주는 함수를 이용합니다.
getrecommandations('The Dark Knight')
8031 The Dark Knight Rises
6218 Batman Begins
6623 The Prestige
2085 Following
7648 Inception
4145 Insomnia
3381 Memento
8613 Interstellar
7659 Batman: Under the Red Hood
1134 Batman Returns
8927 Kidnapping Mr. Heineken
5943 Thursday
1260 Batman & Robin
9024 Batman v Superman: Dawn of Justice
4021 The Long Good Friday
.....
이번에는 무조건 배트맨 보다는 크리스토퍼 놀란이 연출한 영화들이 몇몇이 상위권임을 알 수 있습니다.
추천함수
def getrecommandations(title):
index = indces[title]
sim_scores = list(enumerate(cosine_sim[index]))
sim_scores = sorted(sim_scores, key=lambda x:x[1], reverse=True)
sim_scores = sim_scores[1:31]
movie_indices = [i[0] for i in sim_scores] return titles.iloc[movie_indices]
'kaggle' 카테고리의 다른 글
[kaggle] 영화 추천 시스템 (3) | 2018.08.08 |
---|