Content-Based-Filtering: 와인 추천 시스템

Cherry·2023년 10월 2일
0

콘텐츠 기반 필터링

콘텐츠 기반 필터링은 사용자가 소비한 아이템에 대해 아이템의 내용(content)이 비슷하거나 특별한 관계가 있는 다른 아이템을 추천하는 방법을 말합니다. 아이템의 내용은 아이템을 표현할 수 있는 데이터를 지칭하는데, 아이템 카테고리, 아이템 이름과 같은 텍스트 데이터, 이미지 데이터가 주로 사용됩니다

⇒ 제가 만드려는 와인추천 시스템은 사용자 데이터가 없기 때문에 콘텐츠 기반 필터링으로 진행됩니다.

content-based filtering 자체적 한계

  • 비슷한 item만 추천해주고 새로운 item을 추천해주지 못합니다.

Wine content-based filtering

  • feature
    • type
    • aroma
    • winery
    • country
    • grape

TF-IDF란?

  • TF(term-frequency)는 특정한 단어가 문서 내에 얼마나 자주 등장하는지를 나타내는 값.
    • 이 값이 높을수록 문서에서 중요하다고 생각할수 있습니다
    • 해당 단어가 해당 문서에 몇번 나오는지를 나타내는 지표
    • 하지만 하나의 문서에서 많이 나오지 않고 다른 문서에서 자주 등장하면 단어의 중요도는 낮아집니다.
  • DF(document-frequency)- 문서 빈도
    • 이 값의 역수를 IDF(inverse document frequency)라고 합니다.
    • 해당 단어가 해당 문서에 몇번 나오는지를 나타내는 지표

⇒ TF-IDF는 TF와 IDF를 곱한 값

→ 점수가 높은 단어일수록 다른 문서에는 많지 않고 해당 문서에서 자주 등장하는 단어를 의미합니다.

CountVectorizer

: 각 문서에서 해당 단어가 나타나는 횟수

  • 모든 문서에서 자주 쓰일 수 밖에 없는 단어들이 중요하다고 인식이 됩니다.

main.py


	# 최신 빈티지로 묶어주기
    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()
  • main.py를 실행하면 db에서 wines data를 불러옵니다.
  • wine을 id로만 unique하게 만들기 위해 id으로 group by를 하며 제일 최신 vintage data를 불러옵니다

Recommendation runner

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")
  • tf-idf으로 나온 cosine simularity와 countvectorizer로 나온 cosine simularity의 평균으로 비슷한 와인을 추출해줍니다.
💡 추천 결과
{
        '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'}
        ]
}

0개의 댓글