///
Search
Duplicate
🎯

2강 - 컨텐츠 기반 모델 (유사도 함수, TF-IDF)

실제 산업에서 많이 사용하는, 컨텐츠 기반 추천시스템

1) 컨텐츠 기반 추천시스템이란? (feat. 정의, Represented Items)

컨텐츠 기반 추천시스템은 사용자가 이전에 구매한 상품 중에서 좋아하는 상품들이나 유사한 상품들을 추천하는 방법이다. 유사한 상품들을 찾는 방법에는 Items를 벡터 형태로 표현한 뒤, 유사한 벡터를 찾는 방식이 있다. 이때 Represented Items란 Items를 벡터 형태로 표현한 것으로, 도메인에 따라 각기 다른 방법이 적용된다.
Items는 크게 Text와 Image, 두 가지 형태를 띄고 있다. 먼저 Text는 흔히 알려져 있는 Bert, Word2Vec, TF-IDF, Countervectorizer 등의 자연어처리 모델을 통해서 벡터로 바꿀 수 있으며 Image는 CNN, 레즈넷, VGG 등을 통해 이미지를 표로 만들어 벡터 형태로 바꿀 수 있다. 이러한 과정을 통해 자신과 유사한 순서가 정해지면, 이를 통해 유사한 벡터를 추출한 뒤 이것을 추천하게 되는데 이것이 바로 Represented Items이다.

2) 유사도 함수, 유사도를 구하는 방법

유클리디안 유사도
문서 간의 유사도를 계산하는 첫 번째 방식으로, 유클리디안 유사도는 유클리디안 거리에 역수를 취한 값이다. 이때, 분모가 0이 되는 것을 막기 위해서 아주 작은 수인 ‘1e-5’를 더해준다. 즉, 유클리디안 유사도는 [ 1 / (유클리디안 거리 + 1e - 5) ] 이다.
pq=(pq)(pq)=√∥p2+q22pq∥p-q∥=√(p-q)⋅(p-q)=√∥p∥^2+∥q∥^2-2p⋅q
계산하기가 쉽고, 벡터 컬럼 하나하나가 크기에 민감하거나 중요할 때 유클리디안 유사도를 사용하면 효과를 볼 수 있다는 장점이 있다. 하지만 p와 q의 분포 및 스케일이 다르거나 범위가 다른 경우에 유클리디안 유사도를 사용한다면 상관성을 놓치는 문제가 발생할 수 있다.
코사인 유사도
문서 간의 유사도를 계산하는 두 번째 방식으로, A와 B의 크기 및 내적값을 통해 구할 수 있다. 즉, 두 문서가 얼마나 비슷한 방향을 가지고 있는지를 방향에 대한 값인 θ를 중심으로 얼마만큼의 cos (θ)를 가지는지를 찾으면 그것이 바로 유사도이다. 유사도의 크기는 -1에서 1까지로 나타낼 수 있으며 cos (θ)가 동일한 방향이라면 1, 90° 방향이라면 0, 정반대의 방향이라면 -1을 가진다.
코사인 유사도는 주로 벡터의 크기가 중요하지 않은 경우에 거리를 측정하기 위한 메트릭으로 사용된다. 즉, 빈도수 자체가 아니라 문서 내의 비율을 확인하기 위해 사용하는 것이다. 하지만 코사인 유사도의 가장 큰 문제점은 벡터의 크기가 중요한 경우에 잘 작동하지 않는다는 것이다.
여기서 잠깐! 유클리디안 유사도와 코사인 유사도를 간단하게 비교해보자.
유클리디안 유사도는 앞에서 말한 것처럼 유클리디안 거리를 통해서 가장 가까운 벡터를 찾는 방식이다. 이와 달리 코사인 유사도는 cos (θ)의 방향, 즉 벡터 간의 방향을 찾기 때문에 만약 구하고자 하는 A와 B가 수직선에 가깝게 위치해 있다면 유클리디안 유사도와 달리 유사도가 굉장히 달라진다는 것을 알 수 있다.
피어슨 유사도
문서 간의 유사도를 계산하는 세 번째 방식으로, 문서에 등장하는 단어의 빈도수와 등장하는 횟수를 통해 구할 수 있다.
마땅한게 없어서 띤크님 블로그에서 식 퍼왔습니다..^^ 데이터 분석 알고리즘 - 유사도 분석 : 네이버 블로그 (naver.com)
자카드 유사도
문서 간의 유사도를 계산하는 네 번째 방식으로, 문서에 등장하는 단어들이 문서 간에서 얼마만큼 겹치는 지에 대해서 계산하여 구할 수 있다. 공집합 나누기 합집합의 형태로, 유사도는 0에서 1 사이의 값을 갖는다.
유클리디안, 코사인, 피어슨, 자카드 유사도 외에 Divergence, Dice, Sorensen 유사도 등도 현업에서 자주 쓰이는 유사도이다. 추천시스템, 고객 집단이나 도메인 등에 따라서 유사도를 다르게 구하며 선택하는 기준 역시 상황에 따라 상이하다. 정확도나 평가지표가 좋은 순으로 선택할 수도 있고, 콜드스타트나 구매율과 같이 필요한 상황에 따라 가중치를 적용하기도 한다.

3) TF-IDF의 정의와, 각각의 의미

TF-IDF는 단어 빈도 TF, 역문서 빈도 DF, 단어의 가중치 IDF 세 가지 지표를 통해서 문서 내 단어의 가중치를 계산하는 방법이다. 즉, 이는 다른 문서에서는 거의 등장하지 않지만 특정 문서 내에서만 자주 등장하는 단어가 있다면 그 단어를 찾아 이를 통해 문서의 유사도를 계산하는 방법이다. TF-IDF는 주로 문서의 핵심어 추출, 문서 간의 유사도 계산, 검색 결과 중요도 측정 작업 등에서 활용된다.
먼저 TF(d, t)는 특정 문서 d에서 특정 단어 t가 등장하는 횟수를 모두 기록한 것이고, DF(t)는 특정 단어 t가 등장한 문서가 몇 개인지 전체 문서 중에서 특정 단어가 등장한 문서의 개수를 나타낸다. 마지막으로 IDF(d, t)는 DF(t)에 반비례하는 수로 너무 많이 등장하는 단어는 오히려 덜 중요한 단어이기 때문에 이처럼 너무 많이 등장하는 단어들에는 log를 취한 뒤 DF(t) 값을 분모로 내리는 등 패널티를 줘서 중요한 단어를 적절한 방식을 통해 잡아내고자 한다. IDF(d, t)의 값은 다음과 같이 구할 수 있으며, TF-IDF(d, t)는 다음과 같다.
idf(d,t)=log[n/1+df(t)]idf(d, t) = log[ n / 1 + df(t)]
TF(d,t)IDF(d,t)=TFIDF(d,t)TF(d,t)*IDF(d,t)=TF-IDF(d,t)
코사인 유사도를 통해 문서 내 단어의 TF-IDF 값을 계산하여 문서 간의 유사도를 계산하는 알고리즘 역시 존재하는데, 직관적인 해석이 가능하고 앞에서 말한 것처럼 특정 문서 내에서만 자주 등장하는 단어에 가중치를 두어 계산하기 때문에 쓰는 것이 매우 편리하다. 다만 메모리를 효과적으로 다루기 어려워 메모리 문제가 크게 발생한다는 단점이 있어 문서의 크기나 개수가 조금만 커지더라도 활용하는 데 어려움이 있다.

TF-IDF 코드

docs = [ '먹고 싶은 사과', # 문서0 '먹고 싶은 바나나', # 문서1 '길고 노란 바나나 바나나', # 문서2 '저는 과일이 좋아요' # 문서3 ]
Python
복사
# Counter Vectorizer 객체 생성(TF까지의 과정만 담긴) from sklearn.feature_extraction.text import CountVectorizer vect = CountVectorizer()
Python
복사
# 문장을 Counter Vectorizer 형태로 변형 countvect = vect.fit_transform(docs) countvect # 4x9 : 4개의 문서에 9개의 단어
Python
복사
# toarray()를 통해서 문장이 Vector 형태의 값을 얻을 수 있음 # 하지만, 각 인덱스와 컬럼이 무엇을 의미하는지에 대해서는 알 수가 없음 # sparse matrix -> numpy countvect.toarray()
Python
복사
# 딕셔너리 형태로 출력 가능 vect.vocabulary_
Python
복사
# sorted 함수를 통해 단어 정렬 sorted(vect.vocabulary_)
Python
복사
# Dataframe 형태로 변환 import pandas as pd countvect_df = pd.DataFrame(countvect.toarray(), columns = sorted(vect.vocabulary_)) countvect_df.index = ['문서1', '문서2', '문서3', '문서4'] countvect_df
Python
복사
# 위의 Data Frame 형태의 유사도를 계산 from sklearn.metrics.pairwise import cosine_similarity cosine_similarity(countvect_df, countvect_df)
Python
복사
# 의미 없는 단어를 제거하기 위해 TFfidVectorizer 사용 from sklearn.feature_extraction.text import TfidfVectorizer vect = TfidfVectorizer() tfvect = vect.fit(docs)
Python
복사
tfidv_df = pd.DataFrame(tfvect.transform(docs).toarray(), columns = sorted(vect.vocabulary_)) tfidv_df.index = ['문서1', '문서2', '문서3', '문서4'] tfidv_df
Python
복사
from sklearn.metrics.pairwise import cosine_similarity cosine_similarity(tfidv_df, tfidv_df)
Python
복사
from sklearn.feature_extraction.text import TfidfVectorizer vect = TfidfVectorizer(max_features=4) tfvect = vect.fit(docs)
Python
복사
tfidv_df = pd.DataFrame(tfvect.transform(docs).toarray(), columns = sorted(vect.vocabulary_)) tfidv_df.index = ['문서1', '문서2', '문서3', '문서4'] tfidv_df
Python
복사