주제
1. 추천 시스템
2. 컨텐츠 기반 필터링
3. 협업 필터링
4. 휴대폰 추천 시스템 실습
1. 추천 시스템
- 사용자가 관심을 가질 만한 정보(상품, 서비스 등)를 필터링해서 제공하는 기법
- 사용자의 선호도 및 과거 행동을 기반으로 함
- 이를 통해 사용자 만족감을 높이고, 서비스 사용 시간 및 매출 증대의 효과를 기대할 수 있음
- 추천 시스템의 특징 :
- 정보 과부하 문제 해결
- 수많은 옵션 중 가장 관련도 높은 항목을 선택
- 맞춤형 경험
- 사용자 기호 반영
- 사용자의 충성도와 만족도를 증가
- 비즈니스 가치
- 구매 유도 및 사용자 참여도 증가
- 데이터 확보
- 정보 과부하 문제 해결
- 추천 시스템의 기본 원리 :
- 콘텐츠 기반 필터링(Content-based Filtering)
- 협업 필터링(Collaborative Filtering)
- 하이브리드 추천 시스템
- 활용 사례 :
- 유튜브 영상 추천
- 맞춤형 건강 검진 항목 추천
- 음식 배달 서비스 추천
2. 컨텐츠 기반 필터링
- 내가 좋았던 것을 기반으로 추천
- 사용자가 관심을 보였던 아이템의 특성을 분석하여 이와 유사한 특성을 갖고 있는 다른 아이템을 추천
1. 아이템 프로파일을 구성
- 프로파일 : 아이템을 설명할 특성의 모음집
- 특성은 서로 다른 이종 데잉터의 집합도 가능함
- 예. 유튜브 영상이라면 영상의 주제, 조회수, 좋아요, 인기 프레임 등
2. 각 특성 당 정보 추출
- 특성은 사람이 지정하기 때문에 추천 과정에서 필요한 계산이 불가능
- 프로파일을 구성하는 특성을 숫자의 형태로 변경
- 각 특성 데이터에 적합한 embedding을 진행
- 텍스트라면 NLP, 이미지라면 Image 처리, 숫자라면 범주형 혹은 그대로 사용 등
- 이 과정은 모든 아이템에 동일하게 적용되어야 함
3. 아이템들의 프로파일을 생성
- 사용자가 사용한 아이템의 프로파일과 사용하지 않은 아이템의 프로파일을 생성하는 과정
- 동일한 임베딩 과정을 거쳐야 함
4. 유사도 계산 및 추천
- 사용자 프로파일과 아이템 프로파일 간의 유사도(임베딩 값의 유사도)를 계산
- 코사인 유사도, 유클리드 거리, 피어슨 상관계수 등을 사용
- 만약 사용자가 갖고 있는 아이템이 다수라면 단순 평균, 사용자가 남긴 다른 메타 정보로 가중합을 구하여
- 사용자의 최종 임베딩 값 도출
- 유사도를 기반으로 상위 N개를 추천
- 아직 경험하지 않은 아이템 중 유사도가 높은 아이템을 추천
3. 협업 필터링
- 사용자들의 사이의 상호작용 데이터 혹은 선호도 패턴 데이터를 기반으로 추천을 수행
- 사용자 기반(User-based) :
- 사용자들의 아이템 선호 데이터를 활용
- 비슷한 선호도 또는 행동 패턴을 보이는 사용자의 선호를 추천
- 아이템 기반(Item-based) :
- 사용자들이 아이템을 평가한 데이터를 활용
- 특정 사용자가 사용한 아이템의 평가와 비슷한 아이템을 추천
3.1 사용자 기반 협업 필터링
1. 상호 작용 데이터 준비
- 사용자로부터 얻은 데이터 활용
- 기존 데이터가 없다면 사용할 수 없음 : 콜드 스타트(Cold start)
2. 사용자(행) - 아이템(열) 상호 작용 테이블 생성
- 상호 작용이 없는 경우 누락값으로 표시
3. 사용자 간 유사도 추출
- 유사도란 사용자들이 아이템에 대해 얼마나 비슷한 반응을 보였는지를 의미
- 코사인 유사도, 피어슨 상관계수 등이 활용
4. 유사도 기반 아이템 추천
- 유사도를 기반으로 "이웃"을 선정
- 이웃이 높게 평가한 아이템을 추천
예시)
- 영화A ~ 영화E, 사용자 a ~ f에 대한 사용자 협업 필터링 예시
- 영화를 보고 난 후의 평점(1~5) 데이터를 활용
- a가 본 영화가 아닌 것 중 이웃이 높게 평가한 영화 : 영화E

3.2 아이템 기반 협업 필터링
- 아이템 간의 유사성을 분석해 추천을 수행하는 방법
1. 상호 작용 데이터 준비
2. 아이템 간 유사도 계산
- 수집된 상호 작용 데이터를 기반으로 아이템 유사도를 계산
- 코사인 유사도, 피어슨 상관계수 등
3. 추천 점수 계산
- 아이템 유사도와 사용자 상호 작용 데이터를 조합해 추천 점수 계산
- 사용자가 평가한 아이템과 유사한 아이템들의 대해 가중 평균을 냄
- (사용자의 평가(가중치) * 아이템 유사도)를 모두 더한 값을 사용
4. 최종 아이템 추천
- 추천 점수를 기반으로 추천 진행
동일한 예시)
- 사용자 a가 아직 시청하지 않은 영화 : D, E
- D의 추천 점수 : 4x0.0507 + 4x0.0514 + 4x0.4105 = 0.0504
- E의 추천 점수 : 동일한 방법으로 = 6.1684
- 결론 : 사용자 a에게 영화 E를 추천! (영화E를 더 높게 평가할 확률이 높다)

4. 휴대폰 추천 시스템 실습
- 사용할 데이터 : 케글의 2022년 미국에서 가장 인기있었던 휴대폰 데이터(링크)
- 휴대폰 데이터, 사용자 평가 데이터를 이용하여 추천 시스템 구현이 목표
4.1 컨텐츠 기반 필터링 적용
- 13개의 세부 정부 중 분석에 사용할 특성 선택
- 브랜드, 모델, OS, 메모리, 램, 가격으로 프로파일 구성
- 임베딩 :
- 브랜드, 모델, OS : 텍스트 → TF-IDF 점수 적용
- 메모리, 램, 가격 : 숫자 → MinMax Scaling 적용
# 프로파일 구성을 위한 특성 선택
# 선택한 특성: 'brand', 'model', 'operating system', 'internal memory', 'RAM', 'price'
# 'brand', 'model', 'operating system'을 기반으로 문자열 생성 : TF-IDF에 입력으로 들어갈 예정
data_df['feat_text'] = data_df[['brand', 'model', 'operating system']].astype(str).agg(' '.join, axis=1)
# 'internal memory', 'RAM', 'price'은 숫자형 특성 : MinMax Scailing에 활용 예정
data_df['feat_num_IM'] = data_df['internal memory']
data_df['feat_num_RAM'] = data_df['RAM']
data_df['feat_num_PRC'] = data_df['price']
# Embedding으로 특성 표현
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.preprocessing import MinMaxScaler
# TF-IDF Vectorizer를 사용하여 특성 문자열을 벡터로 변환
tfidf_vectorizer = TfidfVectorizer()
tfidf_matrix = tfidf_vectorizer.fit_transform(data_df['feat_text'])
# MinMaxScaler를 활용해 수치형 데이터를 정규화
scaler = MinMaxScaler()
scaled_features = scaler.fit_transform(data_df[['feat_num_IM', 'feat_num_RAM', 'feat_num_PRC']])
- tifidf_matrix를 dense 형태로 변환 후 stacking하여 TF-IDF 벡터와 스케일링된 수치형 결합
# tfidf_matrix를 dense 형태로 변환하고, 처음 5개 행을 확인
tfidf_matrix_dense = tfidf_matrix.toarray()
# TF-IDF 벡터와 스케일링된 수치형 특성을 결합
import numpy as np
combined_features = np.hstack((tfidf_matrix.toarray(), scaled_features))
유사도 계산 및 추천
- 코사인 유사도를 사용하여 항목 간 유사도 계산
- 두 핸드폰 쌍의 유사도를 미리 구하여 Matrix 형태로 출력
- 정사각 행렬(num_cellphone x num_cellphone)
# 유사도 계산
from sklearn.metrics.pairwise import cosine_similarity
# 코사인 유사도를 사용하여 항목 간 유사도 계산
cosine_sim = cosine_similarity(combined_features, combined_features)

- 추천 함수 구현
- 비교 대상이 되는 핸드폰을 선택
- 유사도 행렬에서 다른 핸드폰과의 유사도를 가져옴
- 유사도를 기반으로 상위 N개의 핸드폰을 선택함
- 선택된 핸드폰의 정보를 출력
# 추천 생성 예시
# 특정 휴대폰과 비슷한 휴대폰을 번환하기 위한 함수 구현
def get_content_based_recommendations(cellphone_index, cosine_sim=cosine_sim, data_df=data_df):
sim_scores = list(enumerate(cosine_sim[cellphone_index]))
sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)
sim_scores = sim_scores[1:6] # 가장 유사한 상위 5개 항목 선택, 0: 자기 자신
cellphone_indices = [i[0] for i in sim_scores]
return data_df.iloc[cellphone_indices][['brand', 'model', 'operating system', 'internal memory', 'RAM', 'price']]
- 존재하지 않는 아이디가 있기 때문에 랜덤하게 N번째 휴대폰에 대한 추천 생성 예시 실행
# 원하는 N 번째의 휴대폰에 대한 추천 생성 예시 실행
from IPython.display import display
N = 4
print('선택한 휴대폰 : ')
display(data_df.iloc[N:N+1][['brand', 'model', 'operating system', 'internal memory', 'RAM', 'price']])
example_recommendations = get_content_based_recommendations(N)
print('\n추천된 비슷한 휴대폰 : ')
display(example_recommendations)

4.2 사용자 기반 협업 필터링
- 사용자 - 핸드폰 상호 작용 데이터 준비
- 사용자 평가 데이터를 이용하여 전체 33개의 핸드폰 중 10개를 제시(없는 부분은 0으로 채워넣기)
- 행에는 사용자 정보, 열에는 핸드폰 정보가 있는 matrix 생성
- 같은 사용자 ID를 기준으로 rating column의 값을 집계
# 사용자 기반 협업 필터링을 위한 사용자-항목 평가 행렬 생성
# 평가 점수가 없는 항목은 0으로 채워 넣기!
user_item_matrix = user_item_df.pivot_table(index='user_id', columns='cellphone_id', values='rating').fillna(0)
user_item_matrix

사용자 간 유사도 추출
- 마찬가지로 코사인 유사도 계산
- 유사도 행렬을 데이터 프레임으로 변환
- 사용자 기반으로 추천을 진행하기 위해서는 "이웃"을 생성해야하기 때문!
# 코사인 유사도 계산
user_similarity = cosine_similarity(user_item_matrix)
print('user_similarity matrix 크기 : ', user_similarity.shape)
# 유사도 행렬을 DataFrame으로 변환
user_similarity = pd.DataFrame(user_similarity, index=user_item_matrix.index, columns=user_item_matrix.index)
display(user_similarity)

유사도 기반 아이템 추천
- 현재 사용자와 다른 사용자 간의 유사도를 이용하여 현재 사용자가 사용해보지 않은 핸드폰 중 유사한 사용자들이 높게 평가한 항목을 추천하는 함수 구현
def recommend_for_user(user_id, user_similarity, user_item_matrix, top_n=5):
# 현재 사용자와 다른 사용자 간의 유사도 가져오기
user_similarities = user_similarity.loc[user_id]
# 가장 유사한 사용자들 찾기
similar_users = user_similarities.sort_values(ascending=False)[1:].index
# 유사한 사용자들이 높게 평가한 항목 추천
recommended_items = pd.Series(dtype='float64')
for similar_user in similar_users:
# 유사한 사용자의 평가 가져오기
user_ratings = user_item_matrix.loc[similar_user]
# 현재 사용자가 평가하지 않은 항목만 선택
user_ratings = user_ratings[user_ratings.index.isin(user_item_matrix.loc[user_id][user_item_matrix.loc[user_id] == 0].index)]
# 추천 목록에 추가
recommended_items = recommended_items.add(user_ratings, fill_value=0)
# 가중치가 높은 순으로 항목 정렬 및 상위 N개 추천
recommended_items = recommended_items.sort_values(ascending=False)[:top_n]
return recommended_items.index.tolist()
- 추천 확인해보기
# 베이스 사용자가 평가한 핸드폰과 해당 점수를 보여주는 코드
base_user_ratings = user_item_df[user_item_df['user_id'] == base_user_idx]
# 베이스 사용자가 평가한 핸드폰의 상세 정보를 가져오기. 원본 핸드폰 데이터와 병합
base_user_rated_phones = pd.merge(base_user_ratings, data_df, left_on='cellphone_id', right_on='cellphone_id', how='left')
# 베이스 사용자가 평가한 핸드폰 및 점수 출력
print(f"베이스 사용자({base_user_idx})가 평가한 핸드폰과 점수:")
display(base_user_rated_phones[['brand', 'model', 'operating system', 'internal memory', 'RAM', 'price', 'rating']])
# 추천 결과로 나온 핸드폰의 스팩 보여주기
# 추천된 핸드폰 ID 리스트를 기반으로 원본 핸드폰 데이터에서 해당 핸드폰의 상세 정보를 가져옴
recommended_phones_info = data_df[data_df['cellphone_id'].isin(recommendations_for_user)]
# 등수 정보를 포함
rankings = [rank + 1 for rank, _ in enumerate(recommendations_for_user)]
# 추천된 핸드폰 ID와 등수를 DataFrame으로 변환
recommended_phones_with_rank = pd.DataFrame({
'cellphone_id': recommendations_for_user,
'rank': rankings
})
# 추천된 핸드폰의 상세 정보와 등수 정보를 병합
recommended_phones_info = pd.merge(recommended_phones_info, recommended_phones_with_rank, on='cellphone_id')
# 추천된 핸드폰의 상세 정보 출력
print("\n추천된 핸드폰의 상세 정보:")
display(recommended_phones_info[['cellphone_id', 'brand', 'model', 'operating system', 'internal memory', 'RAM', 'price', 'rank']])

4.3 아이템 기반 협업 필터링
- 과정은 거의 동일
- 이번에는 아이템 간 유사도를 계산한 행렬을 준비
# 아이템-사용자 평가 행렬 생성
item_user_matrix = user_item_matrix.T
# 코사인 유사도 계산
item_similarity = cosine_similarity(item_user_matrix)
# 유사도 행렬을 DataFrame으로 변환
item_similarity_df = pd.DataFrame(item_similarity, index=item_user_matrix.index, columns=item_user_matrix.index)
- 추천 함수 구현
# 사용자가 평가하지 않은 항목에 대해 추천 점수를 계산하는 함수
def calculate_recommendation_scores(user_id, item_similarity_df, user_item_matrix, topn=5):
# 사용자가 평가한 항목 확인
user_ratings = user_item_matrix.loc[user_id]
rated_items = user_ratings[user_ratings > 0].index
# 사용자가 평가하지 않은 항목 확인
unrated_items = user_ratings[user_ratings == 0].index
# 추천 점수를 저장할 빈 Series 생성
recommendation_scores = pd.Series(index=unrated_items, dtype=np.float64)
# 사용자가 평가하지 않은 각 항목에 대해 추천 점수 계산
for item in unrated_items:
# 현재 항목과 사용자가 평가한 항목들 간의 유사도
item_similarities = item_similarity_df.loc[item, rated_items]
# 유사도와 사용자의 평가 점수의 가중합 계산
weighted_scores = item_similarities * user_ratings[rated_items]
# 가중합의 총합을 추천 점수로 사용
recommendation_scores[item] = weighted_scores.sum() / item_similarities.sum()
# NaN 값을 제거하고 점수가 높은 순으로 정렬
recommendation_scores = recommendation_scores.dropna().sort_values(ascending=False)
return recommendation_scores[:topn]

느낀점
추천 시스템을 만드는 것에 관심이 있었는데 오늘 처음으로 어떻게 만들어지는지 배우고, 실습도 해볼 수 있어서 너무 좋았다. 추천 시스템 프로젝트를 많이 봤었는데 나도 기회가 된다면 꼭 해보고 싶다는 생각이 들었다.
'Data Science > TIL (Today I Learned)' 카테고리의 다른 글
| 프로그래머스 데이터분석 데브코스 1기 - 67일차 (1) | 2024.02.27 |
|---|---|
| 프로그래머스 데이터분석 데브코스 1기 - 66일차 (0) | 2024.02.26 |
| 프로그래머스 데이터분석 데브코스 1기 - 64일차 (0) | 2024.02.22 |
| 프로그래머스 데이터분석 데브코스 1기 - 63일차 (0) | 2024.02.21 |
| 프로그래머스 데이터분석 데브코스 1기 - 62일차 (0) | 2024.02.20 |