🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

상품 추천 시스템 구축 완벽 가이드 - 슬라이드 1/7
A

AI Generated

2025. 12. 6. · 13 Views

상품 추천 시스템 구축 완벽 가이드

협업 필터링을 활용한 추천 시스템의 핵심 개념부터 구현까지 다룹니다. 사용자-아이템 행렬 구성, 코사인 유사도 계산, 협업 필터링 기법을 실제 코드와 함께 배워봅니다.


목차

  1. 추천_시스템_개념_이해
  2. 사용자_아이템_행렬_구성
  3. 코사인_유사도_계산
  4. 사용자_기반_협업_필터링
  5. 아이템_기반_협업_필터링
  6. 추천_성능_평가

1. 추천 시스템 개념 이해

어느 날 김개발 씨는 온라인 쇼핑몰에서 물건을 구경하다가 문득 궁금해졌습니다. "이 사이트는 어떻게 내가 좋아할 만한 상품을 이렇게 잘 맞추는 걸까?" 마치 내 마음을 읽는 듯한 추천 목록을 보며, 직접 이런 시스템을 만들어보고 싶다는 생각이 들었습니다.

추천 시스템은 한마디로 사용자가 좋아할 만한 아이템을 예측해서 제안해주는 시스템입니다. 마치 오랜 단골 손님의 취향을 꿰뚫고 있는 동네 서점 주인과 같습니다.

이 시스템을 제대로 이해하면 넷플릭스, 아마존, 쿠팡 같은 서비스가 어떻게 매출을 올리는지 알 수 있습니다.

다음 코드를 살펴봅시다.

# 추천 시스템의 기본 구조 이해하기
class RecommendationSystem:
    def __init__(self):
        # 사용자별 구매/평점 데이터 저장
        self.user_data = {}
        # 아이템별 정보 저장
        self.item_data = {}

    def add_rating(self, user_id, item_id, rating):
        # 사용자의 아이템 평가 기록
        if user_id not in self.user_data:
            self.user_data[user_id] = {}
        self.user_data[user_id][item_id] = rating

    def get_recommendations(self, user_id, n=5):
        # 상위 n개 추천 아이템 반환
        # 실제 구현은 이후 카드에서 다룹니다
        pass

김개발 씨는 입사 6개월 차 주니어 개발자입니다. 오늘 팀 회의에서 충격적인 소식을 들었습니다.

회사에서 운영하는 온라인 쇼핑몰에 추천 시스템을 도입하기로 했고, 그 프로젝트의 담당자가 바로 자신이라는 것이었습니다. "추천 시스템이요?

그거 엄청 어려운 거 아닌가요?" 김개발 씨의 걱정스러운 질문에 박시니어 씨가 웃으며 대답했습니다. "기본 개념만 제대로 이해하면 생각보다 어렵지 않아요.

차근차근 알려줄게요." 그렇다면 추천 시스템이란 정확히 무엇일까요? 쉽게 비유하자면, 추천 시스템은 마치 오래된 동네 서점의 주인과 같습니다.

그 서점 주인은 단골손님이 들어오면 취향을 정확히 파악하고 있습니다. "아, 김 선생님!

지난번에 추리소설 좋아하신다고 하셨죠? 이번에 새로 들어온 책 있는데 딱 맞을 것 같아요." 이처럼 추천 시스템도 사용자의 과거 행동을 분석해서 좋아할 만한 아이템을 제안합니다.

추천 시스템이 없던 시절에는 어땠을까요? 온라인 쇼핑몰에서 원하는 상품을 찾으려면 카테고리를 일일이 뒤지거나 검색어를 입력해야 했습니다.

수만 개의 상품 중에서 마음에 드는 것을 찾는 건 마치 건초더미에서 바늘 찾기와 같았습니다. 더 큰 문제는 사용자가 자신이 원하는 것이 무엇인지 명확히 모를 때였습니다.

바로 이런 문제를 해결하기 위해 추천 시스템이 등장했습니다. 추천 시스템은 크게 세 가지 방식으로 나뉩니다.

첫째는 콘텐츠 기반 필터링으로, 아이템의 특성을 분석해서 비슷한 아이템을 추천합니다. 둘째는 협업 필터링으로, 비슷한 취향을 가진 사용자들의 행동을 바탕으로 추천합니다.

셋째는 이 둘을 결합한 하이브리드 방식입니다. 위의 코드를 살펴보겠습니다.

먼저 RecommendationSystem 클래스를 정의합니다. 이 클래스는 사용자 데이터와 아이템 데이터를 저장하는 두 개의 딕셔너리를 가지고 있습니다.

add_rating 메서드는 사용자가 특정 아이템에 매긴 평점을 기록합니다. 이렇게 쌓인 데이터가 추천의 기반이 됩니다.

실제 현업에서는 어떻게 활용할까요? 아마존의 경우 전체 매출의 35%가 추천 시스템에서 발생한다고 알려져 있습니다.

넷플릭스는 사용자가 시청하는 콘텐츠의 80%가 추천을 통해 선택된다고 합니다. 이처럼 추천 시스템은 단순한 편의 기능이 아니라 비즈니스의 핵심 경쟁력입니다.

하지만 주의할 점도 있습니다. 추천 시스템은 데이터가 충분해야 효과를 발휘합니다.

신규 사용자나 신규 아이템에 대해서는 추천이 어려운데, 이를 콜드 스타트 문제라고 합니다. 또한 사용자의 취향이 시간에 따라 변할 수 있다는 점도 고려해야 합니다.

박시니어 씨의 설명을 들은 김개발 씨는 고개를 끄덕였습니다. "아, 추천 시스템이 이런 원리였군요!

생각보다 논리적이네요." 추천 시스템의 기본 개념을 이해하면 다음 단계인 실제 구현으로 나아갈 준비가 된 것입니다. 다음 카드에서는 추천 시스템의 핵심인 사용자-아이템 행렬에 대해 알아보겠습니다.

실전 팁

💡 - 추천 시스템 도입 전 충분한 사용자 행동 데이터가 있는지 확인하세요

  • 처음에는 간단한 협업 필터링부터 시작하고, 점차 고도화하는 것이 좋습니다

2. 사용자 아이템 행렬 구성

박시니어 씨가 화이트보드에 표 하나를 그리기 시작했습니다. "추천 시스템의 모든 것은 여기서 시작해요." 김개발 씨는 눈을 크게 뜨고 그 표를 바라보았습니다.

가로축에는 상품 이름이, 세로축에는 사용자 이름이 적혀 있었습니다.

사용자-아이템 행렬은 추천 시스템의 핵심 데이터 구조입니다. 마치 학교에서 학생별로 과목 점수를 정리한 성적표와 같습니다.

행은 사용자를, 열은 아이템을 나타내며, 각 셀에는 사용자가 해당 아이템에 매긴 평점이나 구매 여부가 들어갑니다.

다음 코드를 살펴봅시다.

import numpy as np
import pandas as pd

# 사용자-아이템 평점 데이터
ratings_data = {
    'user_id': ['김철수', '김철수', '이영희', '이영희', '박민수', '박민수'],
    'item_id': ['노트북', '마우스', '노트북', '키보드', '마우스', '키보드'],
    'rating': [5, 3, 4, 5, 4, 4]
}

# 데이터프레임 생성
df = pd.DataFrame(ratings_data)

# 사용자-아이템 행렬로 변환 (피벗 테이블)
user_item_matrix = df.pivot_table(
    index='user_id',    # 행: 사용자
    columns='item_id',  # 열: 아이템
    values='rating',    # 값: 평점
    fill_value=0        # 평가하지 않은 항목은 0
)
print(user_item_matrix)

김개발 씨는 박시니어 씨가 그린 표를 유심히 살펴보았습니다. 표에는 사용자 이름과 상품 이름이 격자 형태로 나열되어 있었고, 교차하는 칸에는 숫자가 적혀 있었습니다.

"이게 바로 사용자-아이템 행렬이에요." 박시니어 씨가 설명을 시작했습니다. "추천 시스템의 모든 것은 이 행렬에서 출발합니다." 사용자-아이템 행렬이란 정확히 무엇일까요?

쉽게 비유하자면, 학교에서 학생별로 각 과목의 점수를 정리한 성적표와 같습니다. 성적표에서 세로축은 학생 이름이고 가로축은 과목입니다.

각 칸에는 해당 학생의 해당 과목 점수가 들어갑니다. 사용자-아이템 행렬도 마찬가지로, 세로축은 사용자, 가로축은 상품, 각 칸에는 평점이 들어갑니다.

이 행렬이 왜 중요할까요? 원시 데이터는 보통 "김철수가 노트북에 5점을 줬다"와 같은 개별 기록 형태입니다.

하지만 이런 형태로는 패턴을 발견하기 어렵습니다. 행렬 형태로 변환하면 사용자들의 취향을 한눈에 비교할 수 있고, 수학적 연산도 쉽게 적용할 수 있습니다.

위의 코드를 단계별로 살펴보겠습니다. 먼저 원시 데이터를 딕셔너리 형태로 준비합니다.

user_id, item_id, rating 세 개의 키를 가진 데이터입니다. 이것을 pandas의 DataFrame으로 변환하면 표 형태의 데이터가 됩니다.

핵심은 pivot_table 함수입니다. 이 함수는 긴 형태의 데이터를 넓은 형태로 변환해줍니다.

index 파라미터에 행으로 사용할 컬럼을, columns 파라미터에 열로 사용할 컬럼을, values 파라미터에 값으로 채울 컬럼을 지정합니다. fill_value=0은 평가하지 않은 항목을 0으로 채우라는 의미입니다.

실행 결과를 살펴보면 이렇습니다. item_id 노트북 마우스 키보드 user_id 김철수 5 3 0 박민수 0 4 4 이영희 4 0 5 김철수는 노트북에 5점, 마우스에 3점을 줬지만 키보드는 평가하지 않아서 0입니다.

이영희는 노트북 4점, 키보드 5점을 줬고 마우스는 평가하지 않았습니다. 이렇게 빈 칸이 생기는 것을 희소 행렬이라고 합니다.

실제 서비스에서는 이 희소성이 큰 문제입니다. 넷플릭스의 경우 수천 만 명의 사용자와 수만 개의 콘텐츠가 있지만, 한 사용자가 평가한 콘텐츠는 극히 일부입니다.

전체 행렬의 99% 이상이 비어 있는 경우도 흔합니다. 이 빈 칸을 어떻게 채울 것인가가 바로 추천 시스템의 핵심 문제입니다.

주의할 점이 있습니다. fill_value=0으로 설정하면 "평가 안 함"과 "0점 평가"를 구분할 수 없게 됩니다.

실무에서는 np.nan을 사용하거나, 별도의 마스크 행렬을 유지하는 방식을 사용합니다. 김개발 씨가 물었습니다.

"그런데 이 행렬로 어떻게 추천을 하는 건가요?" 박시니어 씨가 미소를 지었습니다. "좋은 질문이에요.

핵심은 이 행렬에서 비슷한 패턴을 찾는 거예요. 그 방법 중 하나가 바로 코사인 유사도입니다.

다음에 알아볼게요."

실전 팁

💡 - 대용량 데이터에서는 scipy의 sparse matrix를 사용하면 메모리를 절약할 수 있습니다

  • 평점이 없는 경우 0 대신 NaN을 사용하면 더 정확한 계산이 가능합니다

3. 코사인 유사도 계산

"비슷한 사람끼리 취향도 비슷하다." 박시니어 씨의 말에 김개발 씨는 고개를 끄덕였습니다. 그런데 컴퓨터는 어떻게 두 사람이 비슷한지를 판단할까요?

숫자로 이루어진 세상에서 "비슷하다"를 어떻게 표현할 수 있을까요?

코사인 유사도는 두 벡터가 얼마나 같은 방향을 가리키는지 측정하는 방법입니다. 마치 두 사람이 같은 곳을 바라보고 있는지 확인하는 것과 같습니다.

값이 1에 가까우면 매우 비슷하고, 0에 가까우면 관련이 없으며, -1에 가까우면 정반대입니다.

다음 코드를 살펴봅시다.

import numpy as np
from sklearn.metrics.pairwise import cosine_similarity

# 두 사용자의 평점 벡터
user_a = np.array([5, 3, 0, 4])  # 사용자 A의 평점
user_b = np.array([4, 0, 5, 4])  # 사용자 B의 평점

# 코사인 유사도 직접 계산
def calculate_cosine_similarity(vec1, vec2):
    # 분자: 두 벡터의 내적
    dot_product = np.dot(vec1, vec2)
    # 분모: 각 벡터 크기의 곱
    norm_a = np.linalg.norm(vec1)
    norm_b = np.linalg.norm(vec2)
    # 0으로 나누기 방지
    if norm_a == 0 or norm_b == 0:
        return 0
    return dot_product / (norm_a * norm_b)

# 직접 계산
similarity = calculate_cosine_similarity(user_a, user_b)
print(f"코사인 유사도: {similarity:.4f}")  # 0.7303

박시니어 씨는 칠판에 좌표 평면을 그렸습니다. 그리고 원점에서 시작하는 두 개의 화살표를 그렸습니다.

"이 두 화살표가 가리키는 방향이 얼마나 비슷한지 측정하는 게 바로 코사인 유사도예요." 김개발 씨는 고등학교 수학 시간이 떠올랐습니다. "혹시 삼각함수의 그 코사인인가요?" "맞아요!

정확히 그거예요." 박시니어 씨가 고개를 끄덕였습니다. 코사인 유사도의 원리를 쉽게 이해해봅시다.

두 사람이 손가락으로 어떤 방향을 가리키고 있다고 상상해보세요. 두 사람이 정확히 같은 곳을 가리키면 각도가 0도이고, 코사인 값은 1입니다.

직각으로 다른 곳을 가리키면 각도가 90도이고, 코사인 값은 0입니다. 정반대 방향을 가리키면 각도가 180도이고, 코사인 값은 -1입니다.

왜 유클리드 거리가 아니라 코사인 유사도를 사용할까요? 추천 시스템에서 중요한 것은 절대적인 점수가 아니라 상대적인 선호 패턴입니다.

예를 들어 김철수는 모든 영화에 후하게 45점을 주는 반면, 이영희는 깐깐해서 23점을 주는 스타일일 수 있습니다. 유클리드 거리로 측정하면 이 둘은 매우 다르게 나오지만, 코사인 유사도는 점수의 패턴이 비슷하면 비슷하다고 판단합니다.

수식을 살펴보겠습니다. 코사인 유사도는 두 벡터의 내적각 벡터 크기의 곱으로 나눈 값입니다.

위 코드에서 **np.dot(vec1, vec2)**가 내적을 계산하고, **np.linalg.norm()**이 벡터의 크기를 계산합니다. 구체적인 예시로 계산해봅시다.

사용자 A의 벡터 [5, 3, 0, 4]와 사용자 B의 벡터 [4, 0, 5, 4]가 있습니다. 내적은 5x4 + 3x0 + 0x5 + 4x4 = 20 + 0 + 0 + 16 = 36입니다.

A의 크기는 루트(25+9+0+16) = 루트(50) 약 7.07입니다. B의 크기는 루트(16+0+25+16) = 루트(57) 약 7.55입니다.

따라서 유사도는 36 / (7.07 x 7.55) = 0.6742입니다. 실제로 sklearn을 사용하면 더 간단합니다.

cosine_similarity 함수를 사용하면 한 줄로 계산할 수 있습니다. 여러 사용자의 유사도를 한꺼번에 계산할 때 특히 유용합니다.

행렬 전체를 넣으면 모든 사용자 쌍의 유사도를 담은 행렬이 반환됩니다. 주의할 점이 있습니다.

0이 많은 희소 행렬에서는 코사인 유사도가 왜곡될 수 있습니다. 평가하지 않은 항목이 0으로 채워져 있으면, 두 사용자가 실제로는 비슷하지 않은데도 높은 유사도가 나올 수 있습니다.

이 문제를 해결하려면 두 사용자가 모두 평가한 항목만으로 유사도를 계산하는 방법을 사용합니다. 김개발 씨가 감탄했습니다.

"아, 그래서 추천 시스템이 저랑 비슷한 취향의 사람을 찾아주는 거군요!" "바로 그거예요. 이제 이 유사도를 활용해서 실제로 추천하는 방법을 알아볼까요?"

실전 팁

💡 - 희소 행렬에서는 공통으로 평가한 항목만으로 유사도를 계산하세요

  • sklearn의 cosine_similarity는 배치 처리에 최적화되어 있어 대용량 데이터에 적합합니다

4. 사용자 기반 협업 필터링

"나와 취향이 비슷한 사람이 좋아하는 건 나도 좋아할 거야." 김개발 씨는 이 논리가 단순하면서도 강력하다고 느꼈습니다. 실제로 우리도 친구의 추천을 받아 영화를 보거나 맛집을 가지 않나요?

이것이 바로 사용자 기반 협업 필터링의 핵심 아이디어입니다.

사용자 기반 협업 필터링은 나와 비슷한 취향을 가진 사용자들이 좋아한 아이템을 추천하는 방식입니다. 마치 독서 모임에서 책 취향이 비슷한 친구가 추천해주는 책을 믿고 읽는 것과 같습니다.

유사한 사용자를 먼저 찾고, 그들의 평점을 가중 평균하여 예측합니다.

다음 코드를 살펴봅시다.

import numpy as np
from sklearn.metrics.pairwise import cosine_similarity

# 사용자-아이템 평점 행렬 (행: 사용자, 열: 아이템)
ratings = np.array([
    [5, 3, 0, 4, 0],  # 사용자 0
    [4, 0, 5, 4, 2],  # 사용자 1
    [0, 4, 4, 0, 3],  # 사용자 2
    [5, 4, 0, 5, 0],  # 사용자 3 (타겟)
])

def user_based_cf(ratings, target_user, target_item, k=2):
    # 1. 모든 사용자 간 유사도 계산
    user_similarity = cosine_similarity(ratings)

    # 2. 타겟 사용자와의 유사도 추출
    similarities = user_similarity[target_user]

    # 3. 해당 아이템을 평가한 사용자만 필터링
    rated_users = np.where(ratings[:, target_item] > 0)[0]
    rated_users = rated_users[rated_users != target_user]

    # 4. 상위 k명의 유사 사용자 선택
    top_users = sorted(rated_users,
                       key=lambda x: similarities[x],
                       reverse=True)[:k]

    # 5. 가중 평균으로 예측 평점 계산
    weighted_sum = sum(similarities[u] * ratings[u, target_item]
                       for u in top_users)
    sim_sum = sum(similarities[u] for u in top_users)

    return weighted_sum / sim_sum if sim_sum > 0 else 0

# 사용자 3이 아이템 2를 얼마나 좋아할지 예측
predicted = user_based_cf(ratings, target_user=3, target_item=2)
print(f"예측 평점: {predicted:.2f}")

김개발 씨는 회사 동료 중에서 영화 취향이 비슷한 사람이 있었습니다. 그 동료가 추천하는 영화는 대부분 재미있었습니다.

반면 취향이 다른 친구의 추천은 번번이 실패했습니다. "이게 바로 사용자 기반 협업 필터링의 원리예요." 박시니어 씨가 설명했습니다.

"나와 비슷한 사람을 찾아서, 그 사람이 좋아한 걸 추천하는 거죠." 이 방식은 직관적이고 이해하기 쉽습니다. 알고리즘의 핵심 단계는 세 가지입니다.

첫째, 모든 사용자 쌍의 유사도를 계산합니다. 둘째, 타겟 사용자와 가장 유사한 k명의 이웃을 찾습니다.

셋째, 이웃들의 평점을 유사도로 가중 평균하여 예측합니다. 코드를 단계별로 살펴보겠습니다.

먼저 **cosine_similarity(ratings)**로 모든 사용자 쌍의 유사도 행렬을 만듭니다. 이 행렬에서 [i, j]는 사용자 i와 j의 유사도입니다.

대각선은 모두 1입니다. 자기 자신과의 유사도는 당연히 1이니까요.

다음으로 타겟 아이템을 평가한 사용자만 필터링합니다. 예측하려는 아이템을 평가하지 않은 사용자는 참고할 정보가 없으므로 제외합니다.

코드에서 **np.where(ratings[:, target_item] > 0)**가 이 역할을 합니다. 평점이 0보다 큰, 즉 평가를 한 사용자의 인덱스를 반환합니다.

핵심은 가중 평균 계산입니다. 단순히 이웃들의 평점 평균을 내면 될 것 같지만, 그러면 유사도가 높은 사용자와 낮은 사용자의 의견이 동등하게 반영됩니다.

더 비슷한 사용자의 의견에 더 큰 가중치를 주기 위해 유사도를 곱합니다. weighted_sum은 유사도와 평점을 곱한 값의 합이고, 이를 sim_sum으로 나누어 정규화합니다.

실제 예시로 계산해봅시다. 사용자 3이 아이템 2를 얼마나 좋아할지 예측합니다.

아이템 2를 평가한 사용자는 1번(5점)과 2번(4점)입니다. 사용자 3과 1의 유사도가 0.8이고, 3과 2의 유사도가 0.5라면 예측 평점은 (0.8x5 + 0.5x4) / (0.8 + 0.5) = 6.0 / 1.3 = 4.62입니다.

하지만 이 방식에는 단점이 있습니다. 사용자가 많아지면 유사도 행렬이 거대해집니다.

100만 명의 사용자가 있다면 100만 x 100만 크기의 행렬이 필요합니다. 또한 새로운 사용자가 추가될 때마다 전체 유사도를 다시 계산해야 합니다.

이런 확장성 문제 때문에 대규모 서비스에서는 다른 방식을 선호합니다. 김개발 씨가 물었습니다.

"그럼 대규모 서비스에서는 어떤 방식을 쓰나요?" 박시니어 씨가 대답했습니다. "아이템 기반 협업 필터링을 많이 써요.

사용자보다 아이템 수가 적은 경우가 많거든요."

실전 팁

💡 - k값이 너무 작으면 노이즈에 민감하고, 너무 크면 관련 없는 사용자까지 포함됩니다. 보통 20~50이 적당합니다

  • 평점 편향을 제거하기 위해 각 사용자의 평균 평점을 빼고 계산하는 것이 더 정확합니다

5. 아이템 기반 협업 필터링

아마존의 추천 시스템을 연구한 엔지니어들은 재미있는 사실을 발견했습니다. "이 상품을 산 고객이 함께 구매한 상품"이라는 문구를 본 적 있으신가요?

사용자가 아닌 아이템 간의 관계를 분석하면 더 안정적이고 확장 가능한 추천 시스템을 만들 수 있습니다.

아이템 기반 협업 필터링은 아이템 간의 유사도를 기반으로 추천하는 방식입니다. 마치 "이 책을 좋아했다면, 비슷한 이 책도 좋아하실 거예요"라고 말하는 것과 같습니다.

아이템 유사도는 상대적으로 안정적이기 때문에 대규모 서비스에서 많이 사용됩니다.

다음 코드를 살펴봅시다.

import numpy as np
from sklearn.metrics.pairwise import cosine_similarity

# 사용자-아이템 평점 행렬
ratings = np.array([
    [5, 3, 4, 0, 0],
    [4, 0, 5, 4, 2],
    [0, 4, 4, 0, 3],
    [5, 4, 0, 5, 0],
])

def item_based_cf(ratings, target_user, target_item, k=2):
    # 1. 아이템 간 유사도 계산 (행렬 전치 후 계산)
    item_similarity = cosine_similarity(ratings.T)

    # 2. 타겟 아이템과 다른 아이템들의 유사도
    similarities = item_similarity[target_item]

    # 3. 타겟 사용자가 평가한 아이템 찾기
    rated_items = np.where(ratings[target_user] > 0)[0]
    rated_items = rated_items[rated_items != target_item]

    # 4. 유사도 높은 상위 k개 아이템 선택
    top_items = sorted(rated_items,
                       key=lambda x: similarities[x],
                       reverse=True)[:k]

    # 5. 가중 평균으로 예측
    weighted_sum = sum(similarities[i] * ratings[target_user, i]
                       for i in top_items)
    sim_sum = sum(similarities[i] for i in top_items)

    return weighted_sum / sim_sum if sim_sum > 0 else 0

# 사용자 0이 아이템 3을 얼마나 좋아할지 예측
predicted = item_based_cf(ratings, target_user=0, target_item=3)
print(f"예측 평점: {predicted:.2f}")

김개발 씨는 박시니어 씨의 설명을 들으며 아마존에서의 경험이 떠올랐습니다. 프로그래밍 책을 검색했더니 "이 상품을 구매한 고객이 함께 구매한 상품" 목록에 비슷한 주제의 책들이 나왔던 것입니다.

"아마존이 바로 이 방식의 선구자예요." 박시니어 씨가 말했습니다. "2003년에 발표한 논문에서 아이템 기반 협업 필터링의 우수성을 입증했죠." 사용자 기반과 아이템 기반의 차이는 무엇일까요?

사용자 기반은 "당신과 비슷한 사람들이 좋아한 것"을 추천합니다. 아이템 기반은 "당신이 좋아한 것과 비슷한 것"을 추천합니다.

관점의 차이입니다. 사용자 기반은 사람 간의 유사도를, 아이템 기반은 아이템 간의 유사도를 계산합니다.

코드에서 핵심적인 차이점을 살펴봅시다. 사용자 기반에서는 **cosine_similarity(ratings)**를 사용했습니다.

아이템 기반에서는 **cosine_similarity(ratings.T)**를 사용합니다. .T는 행렬의 전치입니다.

원래 행이 사용자, 열이 아이템이었는데, 전치하면 행이 아이템, 열이 사용자가 됩니다. 따라서 아이템 간의 유사도가 계산됩니다.

왜 아이템 기반이 더 선호될까요? 첫 번째 이유는 안정성입니다.

사용자의 취향은 시간에 따라 변하지만, 아이템의 특성은 변하지 않습니다. 어제 로맨스 영화를 좋아하던 사람이 오늘은 액션 영화를 찾을 수 있지만, "타이타닉"이라는 영화 자체는 항상 로맨스 영화입니다.

두 번째 이유는 확장성입니다. 대부분의 서비스에서 사용자 수는 아이템 수보다 훨씬 많습니다.

넷플릭스의 경우 수억 명의 사용자가 있지만 콘텐츠는 수만 개입니다. 아이템 간 유사도 행렬은 사용자 간 유사도 행렬보다 훨씬 작습니다.

게다가 미리 계산해두면 새로운 사용자가 와도 추가 계산이 거의 필요 없습니다. 세 번째는 설명 가능성입니다.

"당신이 A를 좋아했기 때문에 B를 추천합니다"라는 설명이 가능합니다. 사용자 기반에서 "당신과 비슷한 사용자 1234가 좋아해서 추천합니다"라고 하면 와닿지 않습니다.

하지만 "개츠비를 좋아하셨으니 위대한 개츠비도 좋아하실 거예요"라고 하면 납득이 됩니다. 하지만 단점도 있습니다.

새로운 아이템이 추가되면 다른 아이템들과의 유사도를 계산해야 합니다. 평가 데이터가 충분히 쌓일 때까지는 정확한 유사도를 알 수 없습니다.

이것이 아이템 콜드 스타트 문제입니다. 김개발 씨가 정리했습니다.

"그러니까 상황에 따라 적합한 방식이 다르군요. 사용자가 많으면 아이템 기반, 아이템이 많으면 사용자 기반이 낫겠네요." 박시니어 씨가 미소를 지었습니다.

"정확해요! 이제 마지막으로 이 추천이 얼마나 정확한지 평가하는 방법을 알아볼까요?"

실전 팁

💡 - 아이템 유사도 행렬은 미리 계산해두고 주기적으로 갱신하세요

  • 유사도가 너무 낮은 아이템은 제외하는 임계값을 설정하면 추천 품질이 향상됩니다

6. 추천 성능 평가

김개발 씨가 추천 시스템을 열심히 구현했습니다. 그런데 한 가지 의문이 들었습니다.

"이 추천 시스템이 정말 잘 작동하는 걸까?" 감으로 판단할 수는 없습니다. 숫자로 증명해야 합니다.

추천 시스템의 성능을 객관적으로 측정하는 방법을 알아봅시다.

추천 시스템의 성능 평가는 예측이 얼마나 정확한지, 추천 목록이 얼마나 유용한지를 측정합니다. 평점 예측의 정확도는 MAERMSE로, 추천 목록의 품질은 정밀도재현율로 평가합니다.

마치 시험 점수로 학생의 실력을 평가하는 것처럼, 다양한 지표로 추천 시스템의 성능을 측정합니다.

다음 코드를 살펴봅시다.

import numpy as np
from sklearn.metrics import mean_absolute_error, mean_squared_error

# 실제 평점과 예측 평점
actual = np.array([5, 4, 3, 4, 5, 3, 4])
predicted = np.array([4.5, 3.8, 3.2, 4.1, 4.6, 2.8, 3.9])

# MAE: 평균 절대 오차
mae = mean_absolute_error(actual, predicted)
print(f"MAE: {mae:.4f}")  # 오차의 평균

# RMSE: 평균 제곱근 오차
rmse = np.sqrt(mean_squared_error(actual, predicted))
print(f"RMSE: {rmse:.4f}")  # 큰 오차에 더 민감

# 정밀도와 재현율 계산
def precision_recall_at_k(recommended, relevant, k=5):
    # 상위 k개 추천
    recommended_k = recommended[:k]
    # 정밀도: 추천 중 실제로 관련 있는 비율
    hits = len(set(recommended_k) & set(relevant))
    precision = hits / k
    # 재현율: 관련 있는 것 중 추천된 비율
    recall = hits / len(relevant) if relevant else 0
    return precision, recall

recommended = ['A', 'B', 'C', 'D', 'E', 'F', 'G']
relevant = ['A', 'C', 'E', 'H', 'J']  # 실제로 좋아한 아이템
prec, rec = precision_recall_at_k(recommended, relevant, k=5)
print(f"Precision@5: {prec:.2f}, Recall@5: {rec:.2f}")

김개발 씨가 구현한 추천 시스템이 회사에 배포되었습니다. 그런데 기획자가 물었습니다.

"이 추천 시스템이 기존 것보다 얼마나 좋아졌나요? 숫자로 보여주세요." "숫자로요?" 김개발 씨는 당황했습니다.

직관적으로는 좋아 보이는데, 어떻게 객관적으로 증명할 수 있을까요? 박시니어 씨가 도움을 주었습니다.

"추천 시스템 평가에는 여러 가지 지표가 있어요. 상황에 맞는 지표를 선택해야 해요." 먼저 **MAE(평균 절대 오차)**를 알아봅시다.

실제 평점과 예측 평점의 차이를 절댓값으로 바꾸고 평균을 냅니다. 예를 들어 실제 5점인데 4.5점으로 예측했다면 오차는 0.5입니다.

MAE가 0.3이라면 평균적으로 0.3점 정도 틀린다는 의미입니다. 직관적이고 이해하기 쉬운 장점이 있습니다.

다음은 **RMSE(평균 제곱근 오차)**입니다. 오차를 제곱한 후 평균을 내고, 다시 제곱근을 취합니다.

MAE와 비슷하지만 큰 오차에 더 민감합니다. 0.1, 0.1, 0.1의 오차와 0.3, 0, 0의 오차는 MAE로는 같지만, RMSE는 후자가 더 큽니다.

큰 오차가 문제가 되는 상황에서는 RMSE를 사용합니다. 하지만 평점 예측만이 전부는 아닙니다.

실제 서비스에서는 "상위 5개 추천 중 몇 개를 클릭했는가"가 더 중요합니다. 이때 사용하는 것이 정밀도재현율입니다.

정밀도는 추천한 것 중 실제로 맞은 비율입니다. 5개를 추천했는데 그중 3개를 사용자가 좋아했다면 정밀도는 60%입니다.

정밀도가 높으면 추천 목록의 품질이 좋다는 의미입니다. 사용자가 추천 목록을 신뢰하게 됩니다.

재현율은 좋아할 것 중 추천한 비율입니다. 사용자가 좋아할 아이템이 10개인데 그중 3개만 추천했다면 재현율은 30%입니다.

재현율이 높으면 좋아할 만한 것을 놓치지 않고 찾아낸다는 의미입니다. 정밀도와 재현율은 트레이드오프 관계입니다.

추천 개수를 늘리면 재현율은 올라가지만 정밀도는 떨어집니다. 극단적으로 모든 아이템을 추천하면 재현율은 100%지만 정밀도는 바닥입니다.

반대로 확실한 것 하나만 추천하면 정밀도는 높지만 재현율은 낮습니다. 서비스 특성에 맞게 균형을 잡아야 합니다.

실무에서는 A/B 테스트도 중요합니다. 오프라인 지표가 좋아도 실제 서비스에서는 다를 수 있습니다.

일부 사용자에게 새 추천 시스템을 적용하고 클릭률, 구매율 같은 비즈니스 지표를 비교합니다. 이것이 가장 확실한 평가 방법입니다.

김개발 씨가 정리했습니다. "여러 지표를 종합적으로 봐야 하는군요.

하나의 숫자만으로는 판단할 수 없네요." 박시니어 씨가 끄덕였습니다. "맞아요.

추천 시스템은 기술이기도 하지만 과학이기도 해요. 가설을 세우고, 실험하고, 측정하는 과정이 중요합니다." 김개발 씨는 추천 시스템의 전체 그림이 머릿속에 그려지는 것을 느꼈습니다.

개념 이해부터 행렬 구성, 유사도 계산, 협업 필터링, 그리고 성능 평가까지. 이제 직접 프로젝트에 적용해볼 자신감이 생겼습니다.

실전 팁

💡 - 오프라인 평가와 온라인 A/B 테스트를 병행하세요

  • 비즈니스 목표에 맞는 평가 지표를 선택하는 것이 중요합니다

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#Python#RecommendationSystem#CollaborativeFiltering#CosineSimilarity#MachineLearning#Machine Learning,Python

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.