콘텐츠 기반 필터링은 사용자가 소비한 아이템에 대해 아이템의 내용(content)이 비슷하거나 특별한 관계가 있는 다른 아이템을 추천하는 방법을 말합니다. 아이템의 내용은 아이템을 표현할 수 있는 데이터를 지칭하는데, 아이템 카테고리, 아이템 이름과 같은 텍스트 데이터, 이미지 데이터가 주로 사용됩니다
⇒ 제가 만드려는 와인추천 시스템은 사용자 데이터가 없기 때문에 콘텐츠 기반 필터링으로 진행됩니다.
content-based filtering 자체적 한계
⇒ TF-IDF는 TF와 IDF를 곱한 값
→ 점수가 높은 단어일수록 다른 문서에는 많지 않고 해당 문서에서 자주 등장하는 단어를 의미합니다.
: 각 문서에서 해당 단어가 나타나는 횟수
# 최신 빈티지로 묶어주기
wines['vintage'] = wines['vintage'].astype(str)
wines = wines.groupby('id').apply(lambda x: x[x['id'] == x['id'].max()]).reset_index(drop=True)
# 추천 와인 찾기
data = RecommendationRunner(wines=wines).run()
class RecommendationRunner(BaseRunner):
def __init__(self, wines=None):
if wines is not None:
self.data, self.csm_tf, self.csm_count = WineRecommendationModel(wines=wines).run()
self.csm = (self.csm_tf + self.csm_count) / 2
def recommendation(self, idx):
score_series = list(enumerate(self.csm[idx]))
score_series = sorted(score_series, key=lambda x: x[1], reverse=True)
score_series = score_series[1:11] # not recommending wine itself, starting from 1
wine_indices = [i[0] for i in score_series]
return self.data.loc[wine_indices, ["id"]].to_dict("records")
def run(self):
self.data["recommended_ids"] = self.data.index.to_series().apply(self.recommendation)
return self.data[['id', 'recommended_ids']].to_dict("records")
{
'id': 'usa-sterling-vineyards-sauvignon-blanc',
'recommended_ids': [
{'id': 'usa-chris-hamilton-cellars-sauvignon-blanc'},
{'id': 'usa-high-valley-sauvignon-blanc'},
{'id': 'usa-stag-s-leap-wine-cellars-sauvignon-blanc'},
{'id': 'usa-brandlin-sauvignon-blanc'},
{'id': 'usa-jb-neufeld-sauvignon-blanc'},
{'id': 'usa-silverado-vineyards-land-lane-ranch-sauvignon-blanc'},
{'id': 'usa-ojai-mcginley-vineyard-sauvignon-blanc'},
{'id': 'usa-vina-robles-estate-sauvignon-blanc'},
{'id': 'usa-groth-sauvignon-blanc'},
{'id': 'usa-adamvs-sauvignon-blanc'}
]
}