사용자 클러스터링과 하이브리드 콘텐츠 추천 시스템 구현기

wonjun_choi·2025년 7월 2일

졸업작품

목록 보기
6/6
post-thumbnail

1. 프로젝트 개요

이 프로젝트에서는 사용자 프로파일 기반 클러스터링콘텐츠 추천 시스템을 구현했다. 목표는 비슷한 관심사를 가진 사용자들을 그룹화해 각 그룹의 성향에 맞는 콘텐츠를 추천함으로써 개인화 품질을 높이는 것이었다.사용자 채팅 기능과 북마크 저장기능에 연동하여 사용자 프로파일 모델을 업데이트하는 로직과 벡터 데이터베이스 기반 유사도 검색 인프라는 만들어둔 상태였다. 사용자별 관심사 임베딩 벡터(1536차원)가 저장돼 있었고 코사인 유사도를 계산하는 서비스가 존재했기에 이를 최대한 활용했다. 이러한 토대를 바탕으로 추가 모듈을 개발해 K-평균 클러스터링 기반 유저 세분화협업 + 콘텐츠 기반 하이브리드 추천 알고리즘을 완성했다.

시스템은 크게 두 부분으로 구성됐다.
1. 유저 클러스터링 서비스 – 유사한 프로파일을 가진 사용자들을 군집화하고 분석했다.
2. 콘텐츠 추천 엔진 – 해당 클러스터 정보를 활용해 개인화된 콘텐츠를 추천했다.

클러스터링 시스템
├── 데이터 수집 레이어
│   ├── 사용자 프로파일 벡터 추출
│   ├── 행동 데이터 수집
│   └── 메타데이터 정규화
├── 클러스터링 엔진
│   ├── K-means 클러스터링
│   ├── 계층적 클러스터링
│   └── 클러스터 최적화
├── 클러스터 분석 엔진
│   ├── 클러스터 특성 추출
│   ├── 키워드 분석
│   └── 카테고리 분포 분석
└── 결과 저장 및 관리
    ├── 클러스터 메타데이터 저장
    ├── 사용자-클러스터 매핑
    └── 성능 지표 추적

위와 같은 구조로 시스템을 설계함으로써 사용자 관심사에 기반한 지능형 추천 기능을 단계적으로 구축할 수 있었다. 다음 섹션부터 각 구성 요소의 구현 방법과 사례를 자세히 살펴봤다.

2. 유저 클러스터링 방식

사용한 데이터 설명

클러스터링에는 사용자 프로파일 벡터 데이터를 활용했다. 각 사용자는 자신만의 관심사 프로파일 벡터를 가지고 있었는데, 이는 사용자가 북마크한 콘텐츠들의 키워드와 카테고리를 종합해 고차원 임베딩으로 표현한 것이었다. 벡터 차원은 1536으로, OpenAI 임베딩 모델 등을 통해 생성됐다. 예를 들어 사용자의 북마크 이력에 ‘머신러닝’ 관련 글이 많으면 해당 방향의 벡터 값이 높아졌다. 이러한 벡터는 pgvector와 같은 벡터 검색 DB에 저장돼 빠른 유사도 연산에 사용됐고, 클러스터링의 입력 데이터로 활용됐다.

추가로 각 프로파일에는 대표 키워드 리스트카테고리 분포 정보도 존재했는데, 이는 클러스터 결과를 해석하거나 이름을 붙이는 데 활용됐다. 클러스터링에는 기본적으로 임베딩 벡터만 사용하되, 사후 분석을 위해 해당 사용자 군집의 주요 키워드와 카테고리 비중을 계산했다.

전처리 과정

본격적인 군집화 전에 데이터 전처리를 수행했다. 우선, 사용자 프로파일 벡터들을 균일한 스케일로 다루기 위해 정규화(normalization)를 적용했다. 구체적으로 각 벡터를 단위벡터 형태로 스케일링해 거리 계산 시 편차를 줄였다. 또한 클러스터링 결과 품질을 높이기 위해 변별력이 낮은 특징을 줄이는 작업도 진행했다. 예를 들어 모든 사용자가 공통으로 갖는 관심 카테고리보다는 개별 그룹을 잘 구분짓는 키워드에 가중치를 뒀다.

다만 적절한 클러스터 수 범위를 결정하지 못해, 임의로 10개를 사용하였다.
엘보우 메서드가 클러스터 수 선정에 자주 사용된다는 사실을 확인하여, 추후 사용자 데이터가 쌓이거나
테스트할 기회가 있다면 이 방법을 적용해볼 예정이다.

클러스터링 알고리즘 및 이유

클러스터링 알고리즘으로 K‑평균(K‑means)을 채택했다. K‑평균은 구현이 단순하고 대용량 데이터에서 계산 효율성이 높으며, 결과로 도출되는 클러스터 중심점(centroid)을 해석하기 쉽다는 장점을 가졌다. 특히 임베딩 벡터 공간에서 유클리드 거리를 기반으로 빠르게 수렴했고, 실시간 업데이트에도 대응 가능했다.

또한 계층적 클러스터링을 보조적으로 활용했다. 1차 K‑평균으로 거대 그룹을 형성한 뒤, 특정 클러스터 내에서 세분화가 필요한 경우 계층적 군집화를 적용해 하위 그룹을 찾았다. 이를 통해 너무 큰 클러스터를 의미 있는 하위 세그먼트로 나눠, 보다 정교한 사용자 세분화를 달성했다. 다만 계층적 방법은 계산 비용이 높아 모든 데이터에 적용하지 않고, K‑평균 결과에 따라 선택적으로 사용하는 전략을 취했다.

더불어 증분(온라인) 클러스터링 기법을 도입해 새로운 사용자가 유입될 때마다 전체 재학습 없이도 실시간으로 가까운 클러스터에 편입할 수 있도록 했다. 일정 수 이상 신규 사용자가 누적되면 배치 작업 시 전체 재학습을 트리거하는 방식으로, 데이터 드리프트에 유연하게 대응하도록 설계했다.

클러스터링 작업은 백그라운드 배치 작업으로 수행되도록 구현했다. 예를 들어, 매일 새벽이나 주간 단위로 클러스터링을 재실행해 최신 데이터를 반영했다. 스케줄러와 Celery 워커를 이용해 클러스터링을 자동화했고, 클러스터 품질 모니터링 지표(실루엣 스코어 등)가 일정 기준 미만이면 자동으로 재클러스터링을 트리거하도록 했다. 이러한 설계로 시스템은 데이터 드리프트나 신규 사용자 유입에 대응하며 군집 결과를 유지 관리했다.

결과 해석 및 시각화 사례

클러스터링을 완료한 뒤 각 클러스터의 특징을 해석하고 의미 있는 이름을 부여했다. 클러스터 중심 벡터를 구성하는 상위 키워드와 주된 카테고리를 조합하고, 해당 그룹 사용자들의 행동 패턴(활동량, 신기술 선호도 등)을 고려해 이름을 생성했다. 아래는 클러스터 자동 명명 함수의 간략한 예시다.

# 클러스터 이름 생성 로직
def generate_cluster_name(cluster_data):
    # 1. 상위 키워드 기반 명명
    top_keywords = extract_top_keywords(cluster_data, top_k=3)

    # 2. 카테고리 기반 명명
    dominant_category = get_dominant_category(cluster_data)

    # 3. 행동 패턴 기반 명명
    behavior_pattern = analyze_behavior_pattern(cluster_data)

    # 4. 종합적 이름 생성
    return f"{dominant_category}_{top_keywords[0]}_{behavior_pattern}"

해당 로직을 통해 클러스터 주요 성격을 한눈에 알 수 있는 레이블을 만들었다. 예를 들어, 주요 키워드가 ‘AI’와 ‘Machine Learning’이고 해당 그룹 사용자들이 새로운 기술 트렌드에 민감한 행동 패턴을 보이면 “AI_Machine_Learning_Early_Adopters”라는 이름을 얻고자 하였다. 또 다른 클러스터는 “Frontend_React_Weekend_Developers”(프론트엔드 React 기술을 주로 주말에 학습하는 사용자 그룹)로 명명됐고, “Data_Science_Python_Researchers” 같은 이름의 클러스터도 도출되게 하였다. 이러한 이름은 군집 성향을 잘 나타내 줘, 이후 추천 시스템이 왜 해당 콘텐츠를 추천하는지 설명할 때 활용하려한다. 예를 들어 “당신은 AI_Machine_Learning_Early_Adopters 그룹에 속해 있어 최신 머신러닝 글을 추천했다.”와 같은 식으로 투명성을 제공하려한다.

참고 : 아직 차원 축소(예: t‑SNE, UMAP)나 실루엣 분포 그래프 등 시각적 검증 작업을 수행하지 않았다. 추후 적절한 k 산정이 완료되면 시각화를 포함한 품질 검증을 계획하고 있다.

3. 콘텐츠 추천 방식

추천 기준 및 알고리즘 설명

콘텐츠 추천 엔진은 하이브리드 추천 알고리즘을 구현해 개인화된 결과를 생성했다. 크게 세 가지 축을 고려했다.

  1. 협업 필터링 – 같은 클러스터 사용자가 좋아한 콘텐츠를 추천했다. 즉, 나와 유사한 관심사를 지닌 그룹의 인기 북마크기사를 우선 후보로 삼았다.
    - wikipedia - Collaborative filtering

    - medium - Collaborative Filtering in Recommender System: An Overview
  2. 콘텐츠 기반 필터링 – 사용자의 프로파일 벡터와 유사한 콘텐츠를 추천했다. 벡터 공간에서 사용자의 관심사와 가장 가까운 콘텐츠를 찾았다. 이를 위해 pgvector를 활용한 벡터 유사도 검색과 OpenAI 임베딩 모델을 사용한 의미적 유사도 계산을 적용했다.
  3. 인기 트렌드 반영 – 전체 사용자들 사이에서 인기 있는 최신 콘텐츠를 일부 추천 풀에 포함했다. 이는 새로운 사용자나 관심사 범위가 좁은 경우를 대비해 콜드스타트 문제를 완화하고 전체 트렌드도 놓치지 않도록 하기 위함이었다.

엔진은 세 요소에서 얻은 추천 후보를 가중 합산해 최종 점수를 매겼다.

최종점수 = (클러스터점수 × 0.4) + (콘텐츠점수 × 0.5) + (인기도점수 × 0.1) + 신선도보정 + 다양성보정

가중치 0.4, 0.5, 0.1은 실험적으로 정한 값으로, 콘텐츠 기반 필터링에 가장 높은 비중을 두되 클러스터 기반 협업 효과도 충분히 반영하도록 했다. 또한 신선도 보정다양성 보정 항목을 추가했다. 신선도 보정은 최신 콘텐츠일수록 점수에 **가산점(+30%)**을 주어 사용자가 최근 정보를 접할 수 있도록 했고, 다양성 보정은 추천 목록이 한쪽 주제에 치우치지 않도록 비슷한 콘텐츠가 연속 등장하면 점수를 조정해 주제 다양성을 확보했다.

추천 엔진의 동작 과정은 다음과 같았다.

  1. 프로파일 분석: 사용자의 프로파일 벡터를 불러오고 해당 사용자가 속한 클러스터 정보를 확인했다. 과거 피드백(클릭, 저장 여부 등)이 있으면 개인 선호도를 반영할 가중치를 계산했다.

  2. 추천 후보 수집

    • 클러스터 기반 후보: 사용자 클러스터 내 인기 콘텐츠를 조회했다.
    • 콘텐츠 기반 후보: 벡터 유사도 검색을 통해 사용자 프로파일 벡터와 가장 유사한 콘텐츠를 N개 추출했다.
    • 인기 기반 후보: 플랫폼 전체에서 인기 있는 최신 콘텐츠 중 사용자가 아직 보지 않은 것을 일부 가져왔다.
  3. 점수 계산 및 정렬: 수집된 후보 각각에 최종점수 공식을 적용해 점수를 산출한 뒤 내림차순 정렬했다.

  4. 후보 필터링: 이미 본 적 있거나 북마크한 콘텐츠를 제외하고, 점수가 낮은 항목이나 부적절한 내용을 걸러냈다.

  5. 추천 결과 구성: 최종 후보를 추천 결과로 선정했고, 각 아이템에 추천 사유를 생성해 붙였다.

  6. 추천 제공: API로 추천 목록을 제공했다. 예: GET /api/v1/profiles/{user_id}/recommendations

  7. 피드백 수집 및 학습: 사용자의 행동을 로그로 남기고, 특정 액션별 가중치를 달리 부여해 프로파일을 미세하게 조정했다.

  8. 성능 모니터링: CTR, 저장률, 다양성, 신선도 등을 대시보드로 추적했고 A/B 테스트로 알고리즘 파라미터를 최적화했다.

요약하면, 콘텐츠 추천 알고리즘은 협업콘텐츠 유사도의 장점을 결합하고 실시간 피드백 루프를 통해 시간이 지날수록 똑똑해지는 구조로 설계됐다.

추천 결과 예시

머신러닝에 관심이 많은 사용자 A에게는 다음과 같은 콘텐츠가 추천됐다.

  • 클러스터 기반 추천: “Top 10 최신 머신러닝 논문 목록”
  • 콘텐츠 기반 추천: “GPT-4를 활용한 ML 모델 개발 튜토리얼”
  • 인기 트렌드 추천: “AI 스타트업 동향 2025”

각 추천에는 “비슷한 사용자가 많이 본 콘텐츠”, “관심 분야와 85% 유사”, “최근 화제의 글” 등의 라벨이 함께 제공됐다.

프론트엔드 개발자 B에게는 “React 최신 훅 베스트 프랙티스”, “2025년 주목할 웹 개발 트렌드” 등이 추천됐다. 이렇게 직접적인 관심사 콘텐츠연관된 새로운 콘텐츠를 조합해 사용자에게 친숙하면서도 지루하지 않은 리스트를 제공했다.

4. 구현 시 어려웠던 점 및 해결 과정

도전 과제 1: 최적의 클러스터 수 선택

문제: 클러스터 수를 어떻게 정할지 어려웠다.
TODO: 엘보우 메서드와 시각화를 통해 적정 k값을 찾으려한다.

도전 과제 2: 실시간 개인화 및 데이터 동기화

문제: 사용자 행동이 반영되는 시점을 어떻게 앞당길지 고민됐다.
해결: 프로파일 업데이트 리스너로 행동 발생 시 벡터를 즉시 재계산하고 증분 클러스터링을 도입했다. 검색어 임베딩은 비동기로 처리하거나 유사 키워드 사전을 캐싱해 지연을 최소화했다.

5. 마무리 및 느낀 점

임베딩 기반 사용자 프로파일에 클러스터링을 더해 “누가 비슷한가”를 파악하고, 그 정보를 협업 필터링에 얹어 “무엇을 보여줄까”를 결정했다. 아직 클러스터 최적 k, 시각적 품질 검증, 추천 파라미터 탐색 등 남은 과제가 많다. 그러나 증분 클러스터링을 통한 신속한 신규 사용자 반영과 하이브리드 추천 구조로 실서비스에 적용 가능함을 확인했다. 차후 A/B 테스트와 온라인 학습을 통해 개인화 품질을 지속적으로 향상시킬 계획이다.

0개의 댓글