junyong1111 / UROP

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

UROP

주제 : 최신머신러닝 시스템을 적용한 추천시스템 연구및 분석

1주차 (주제 선정)
  1. GNN(Graph Neural Network)
  2. 메타러닝(Meta-Learning)
    • 거리 기반
    • 최적화 학습 방식
    • 모델 기반
  3. LSTM
2주차 (추천시스템 분석)

2주차(추천 시스템 연구동향 분석)

--# 메타러닝

메타러닝

정의

  • 떠오르는 학습 방법이며 적은 데이터를 효율적으로 학습(퓨샷 러닝)
  • ‘메타’라는 단어는 한 차원 위의 개념적 용어로 대상의 전반적인 특성을 반영
  • 데이터의 패턴을 정해진 프로세스로 학습하는 것이 아닌 데이터의 특성에 맞춰서 모델 네트워크의 구조를 변화시키면서 학습
  • 배우는 방법을 배우는 것 (Learning to learn)

ex) 하이퍼파라미터 최적화, 자동 신경망 네트워크 설계 등등..

퓨샷러닝(Few Shot Learning)

퓨샷 러닝에서의 데이터셋은 크게 두 가지로 나뉜다.

  1. 서포트 셋(Support Set)
    1. 데이터 셋을 학습에 사용하는 데이터
  2. 쿼리 셋 (Query Set)
    1. 데이터 셋을 테스트에 사용하는 데이터

이러한 퓨샷 러닝 태스크를 “N-way K-shot”이라고 하며 여기서 n은 카테고리의 개수를 뜻하고 k는 카테코리당 이미지의 수를 의미한다. 일반적으로 성능은 N에 반비례하며 k에 비례한다.

—# 예를 들어 개, 고양이, 말 3개의 카테리고가 있고 각각 4장씩 이미지가 존재한다면

→ 3-way 4-shot이다.

특징

일반적으로 딥러닝 모델을 훈련시킬 때 대용량의 데이터가 필요하며 현실적으로 방대한 양의 데이터셋을 구축하기는 쉽지 않다. 퓨샷 러닝은 적은 데이터만 있어도 성능이 우수한 모델링이 가능하다.

접근 방법

메타 러닝 및 퓨샷 러닝의 대표적 접근 방법은 거리 학습 기반과 모델 기반 학습 방식과 최적화 학습 방식이 존재한다.

  • 거리 학습 기반 : 효율적 거리 측정 학습
  • 모델 기반 학습 : 메모리를 이용한 순환 신경망
  • 최적화 학습 : 모델 파라미터 최적화

거리 학습 기반(Metric Based Learning)

메타 러닝의 방법론 중 하나인 거리 학습 기반은 서포트 셋과 쿼리 셋 간의 거리(유사도)를 측정하는 방식으로 대신한다. 대표적인 알고리즘은 샴 네트워크(Siamese Neural Network)가 있다.

모델은 주어진 서포트 데이터를 특징 공간에 나타내서 특징을 뽑아내며 같은 클래스면 거리를 가깝게 다른 클래스면 멀게 하는 방식으로 데이터를 분류한다. 쿼리 데이터를 유클리디안 거리가 가까운 서포트 데이터의 클래스로 예측하는 방식이다.

모델 기반 학습 방식(Model based learning)

모델 기반 학습 방식은 적은 수의 학습 단계로도 모델의 파라미터를 효율적으로 학습할 수 있는 방식이다.

모델에 별도의 메모리를 두어 학습 속도를 조절한다. 대표적인 알고리즘으로는 MANN가 있다.

과거 데이터를 외부 메모리에 저장함으로 효율적으로 문제를 해결하는 방법을 습득하며 새로운 정보를 빠르게 인코딩하고 몇 개의 샘플만 가지고도 새로운 태스크에 적용할 수 있도록 설계되었다.

최적화 학습 방식(Optimizer learning)

Few Shot task를 파라미터 최적화 문제로 생각한다.

일반적으로 딥러닝 모델은 기울기의 역전파를 통해 학습을 진행하지만 기울기 기반 최적화 기법은 큰 스케일의 데이터를 위해 설계가 되었기 때문에 적은 수의 샘플에 대한 최적화 기법을 다룬다. 대표적인 알고리즘으로는 MAML알고리즘이 있다.

0. 추천 시스템 기법 연구동향 분석

정보 통신의 발달로 최근에는 온라인으로 쇼핑하는 사람들이 증가하였다.

하지만 고객의 입장에서는 판매자와 직접적인 소통이 불가능해 다양해지는 아이템들에 대한 사전지식 부족으로 원하는 물건을 고르는데 어려움이 존재한다. 이를 해결하기 위하여 다양한 방법의 추천시스템이 존재한다. 현재는 정보필터링 방법과 연관성 분석 등이 있다.

가장 간단한 추천 방식

IMG_52C0ED242834-1

  1. 베스트셀러기반
    • 특징 : 판매량이 많은 순서대로 상품을 추천
    • 장점 : 개인정보 없이 신속한 추천 가능
    • 단점 : 개인화된 추천이 불가능
  2. 최소질의대상 상품결정
    • 특징 : 직접 설문하여 얻은 정보를 통해 상품을 추천
    • 장점 : 개인화된 추천이 가능
    • 고객의 응답이 불완전할 경우 신뢰성 저하
베스트셀러기반 최소질의대상 상품결정
특징 판매량이 많은 순서대로 추천 직접 설문하여 얻은 정보를 통해 추천
장점 개인정보 없이 신속한 추천 개인화된 추천 가능
단점 개인화된 추천이 불가능 고객의 응답이 불완전할 경우 신뢰성 ↓

정보 필터링

정보 필터링 기법에는 크게 3가지 존재한다.

  1. 콘텐츠 기반
  2. 협력 필터링
  3. 하이브리드
1. 콘텐츠 기반

특징

  • 사용자가 아이템에 대해 평가한 점수 혹은 과거 구매내역을 바탕으로 미리 선정된 기준을 통해 분류된 아이템 카테고리와 유사도를 계산한 후 추천

장점

  • 협력필터링과 다르게 추천 대상과 취향이 비슷한 이웃 사용자를 찾을 필요가 없다.
    • 독립적인 정보만을 필요로 한다.
    • 다른 사용자의 정보가 부족할 경우 사용
  • 새로운 아이템에 대한 평가점수가 존재하지 않더라도 First rater문제가 발생하지 않는다.
    • 새로운 아이템이여도 그 아이템 속성에 맞게 카테고리에 할당되어 유사도로 추천하기때문
❗ first rater : 새로운 아이템에 대해 누군가가 점수를 주기 전까지 핻아 아이템은 추천 리스트에 포함될 수 없는 문제

단점

  • 과거 구매이력과 정보가 부족할 경우 성능을 보장할 수 없다.
    • 구매이력과 프로필 정보가 없다면 해당 방법으로 구현하는것은 불가능
  • 다른 사용자들의 취향이나 선호도를 반영하지 못해 너무 비슷한 상품들만 추천될 수 있음
    • 이러한 문제를 과도한 특수화라고 함
    • 무작위 요소를 추가하는 유전자 알고리즘이나 돌연변이 방식을 사용하면 보다 우수한 성능 보장

접근 방식

(1)아이템 속성 분석

구조적 데이터

  • 아이템의 속성이 명확히 정의 되어 있어 있음
  • 속성의 개수가 비교적 적고, 각 속성에 해당하는 값이 모두 존재해야 함

비구조적 데이터

  • 이미지,소리, 텍스트와 같은 데이터
  • 최근 들어 급증하고 잇고, 속성 정의가 어렵다는 단점이 존재
  • 텍스트로 이루어진 콘텐츠에 대한 연구가 활발히 이루어지고 있음
    • 키워드 분석과 의미 분석 2가지 방식

비구조적 데이터(키워드 분석)

아이템간의 키워드를 비교하여 유사도를 계산하여 사용자가 선호하는 아이템과 키워드 유사도가 높은 아이템을 추천하며 대표적으로는 TF-IDF가 있다.

TF : 아이템 내에서 한 단어가 출현한 빈도를 나타내는 값으로, 아이템에서 많이 출현한 단어일수록 높은 값

IDF : 출현 빈도가 높더라도 불용어에 해당할 확률이 크므로 해당하는 키워드가 전체 아이템에서 얼마나 가치 있는 키워드인지

아이템 내에서 많이 출현하지만 전체 아이템 집합에서는 드물게 출현하는 단어일수록 높은 TF-IDF 값을 가짐

  • 위 과정으로 가중치가 높은 상위 N개의 단어 키워드로 테이블을 생성
  • 아이템간 유사도를 계산하기 위해 코사인 유사도를 사용

문제점

  • 하나의 단어가 다양한 의미를 가지는 경우
    • 실제로는 유사하지않지만 추천 가능성
  • 여러 단어가 하나의 의미를 가지는 경우
    • 내용적으로는 유사하지만 유사도가 낮으므로 추천 누락

위 문제점을 해결하기 위하여 의미론 분석의 중요성이 대두되고 있다.

의미론 분석에서는 품사 별 동의어 관계를 링크로 연결한 대형 네트워크 형태의 워드넷(Wordnet)이 가장 널리 이용되고 있다.

  • 사용자 프로필 정보를 이용하여 추천

    (+) 과거 구매이력 및 다른 사용자 데이터가 없어도 추천 가능

    (+) 평가 데이터가 부족한 경우 널리 쓰임

    (-) 정확한 정보를 입력하는 사용자를 확보하기 어려움

    (-) 사용자 선호 경향 변화에 대한 대처가 힘듬

    (-) 정교한 추천 성능을 보장할 수 없음

  • 과거 구매이력 정보를 분석하여 추천

    (+) 과거 구매이력을 성능향상

    (+) 사용자 선호 경향 변화에 대한 대처 가능

    (-) 과거 데이터가 필요

    (-) 너무 비슷한 아이템만을 추천할 가능성 존재

콘텐츠 기반 접근방식은 사용자가 직접 입력한 정보를 이용하여 추천하는 방식과 사용자가 과거에 구매한 이력 정보를 통해 추천하는 방식이 존재한다. 여기서 아이템을 추천하기 위해서 TF-IDF식을 통해 아이템 내에서 등장하는 키워드에 대한 값을 계산하는데 해당 식은 단지 아이템 내 단어 출현 빈도수를 계산하므 로 여러 단어가 하나의 의미를 가지거나, 하나의 단어가 다양한 의미를 가지는 경우 제대로 된 추천 가능성이 낮다. 이를 해결하기 위하여 의미론적 분석이 필요하며 워드넷이 가장 널리 이용되고 있다.

2. 협력 필터링

“이웃이 다른 이웃을 돕는다면 우리의 커뮤니티는 더 강해집니다.” -제니퍼 팔카

특징

  • 현재까지 가장 우수한 성능을 나타낸다고 알려진 기법으로 특정 아이템에 대해 선호도가 유사한 고객들은 다른 아이템에 대해서도 비슷한 선호도를 보일 것 이라는 기본 가정을 바탕으로 시용자가 아이템에 대해 평가한 정보를 사용해 선호도를 예측한다.

장점

  • 콘테츠 기반의 방식보다 정확도가 우수하다.

단점

  • 유사도를 측정할 만한 충분한 데이터가 존재하기 않다면 예측이 불가능하다 (Cold Start)
  • 누군가가 점수를 주기 전까지는 추천이 이루어 질 수 없다(first rater)
  • 코사인거리나 피어슨 상관게수를 이용할 경우 데이터 희소성 문제가 발생
  • 데이터 희소성을 해결하기 위한 다양한 연구
    • 논문추천 시스템
      • 논문의 키워드를 활용하는 방식을 제안
      • 나이브 베이즈 모델 사용
      • 새로운 고객에 대해서는 추천목록을 제공하는 것이 어려움
    • 협력필터링 기법과 사회연결망 기법의 중심성을 결합
      • 많은 사람들과 비슷한 선호도를 갖는 고객의 취향은 대중적이고 신뢰성이 높다는 가정
      • 고객들 간 유사도를 기반으로 네트워크를 생성
      • 중심성이 높은 사용자들을 새로운 고객의 이웃으로 설정
      • 기존 나이브 베이즈 모델보다 정확성을 향상 시킴
    • 주요 사용자와 일반 사용자로 분류
      • 주요 사용자 군집 생성
      • 일반 사용자 군집 생성
      • 주요 사용자 군집에는 있지만 일반 사용자 군집에는 없는 아이템을 일반 사용자에게 추천
    • 데이터의 차원을 축소하는 방법
      • 중요하지 않은 사용자나 아이템을 제거 하는 특이값 분해
      • 원본 데이터보다 노이즈가 적어 우수한 성능을 나타냄
    • 낮은 순위 데이터에 손실함수 개념 적용

확장성

  • 계산량이 많을 경우 유사도가 큰 순서대로 N개의 사용자 혹은 아이템만을 선정하여 선호도를 예측
  • 일관성이 없는 의견을 가진 사용자는 오히려 방해가 됨
    • 콘텐츠기반 + 협력필터링을 모두 사용하여 가중 평균값을 에측 값으로 활용
    • 두 값의 차이가 큰 사용자의 가중치를 낮게 주어 예측 오차를 줄이는 방법
  • 고의로 긍정적인 평가 혹은 부정적인 평가를 하는 경우
    • 기계학습 알고리즘을 통해 미리 학습한 뒤 공격을 탐지하는 기법

접근 방식

  • 기억 기반 협력필터링

    • 유사도가 높은 사용자가 선택한 아이템을 추천해주는 방식
      • 모델을 구축하지 않고 추천이 요굴될 때마다 휴리스틱 기법을 통해 결과를 도출
    • 같은 항목에 같은 점수를 준 두 고객이 존재할 경우, 이들의 유사도는 높으며 비슷한 취향을 갖고 있다고 볼 수 있다.
    • 사용자 기반 : 사용자가 입력한 선호도 정보를 이용하여 추천대상고객의 구매이력과 가장 비슷한 사용자가 구매한 아이템을 추천하는 방식
    • 아이템 기반 : 추천하고자하는 아이템을 구매한 사람들이 공통적으로 구매한 아이템을 구매한 사람들에게 해당 아이템을 추천해주는 방식

    유사도

    • 피어슨 상관게수
      • 1에 가까울수록 양의 상관관계, -1에 가까울수록 음의 상관관계, 0은 상관관계 없음
    • 코사인 유사도
      • 다양한 고객들이 서로 다른 척도를 사용할 경우 파악하는 것이 어렵다.
      • 이를 해결하기 위해 보완 코사인 유사도를 제안
    • 스피어만 순위 상관계수
      • 사용자 a와 b의 점수를 각각 순위로 변환한 뒤, 차이를 통해 유사도를 측정
      • 점수의 분포가 매우 극단적일 경우 유용하다.
      • 사용자가 여러 아이템에 대하여 같은 평가점수를 준 경우에는 유사도 측정이 어려움

    선호도 예측

    • 가중합
      • 추천 대상 고객과 사용자간의 유사도가 높을수록 큰 가중치를 부여
    • 단순가중평균
      • 아이템 기반 협력필터링에서 사용
      • TF-IDF를 활용
  • 모델 기반 협력필터링

    • 기억 기반 협력필터링을 기본으로 하되, 기계학습 또는 데이터마이닝 기법을 활용하는 것
    • 베이지안, 선형 회귀분석, 마코프 결정 프로세스 등…
    • 나이브 베이즈 모델
      • 문서분류에서 가장 우수한 성능을 보이고 있는 알고리즘으로 베이즈 정리에 이론적 근거를 두고 있음
      • 각 카테고리에 할당 될 확률을 계산하는 방법
      • 평가 정보나 이용정보가 부족한 신규 컨텐츠 추천 문제를 해결
      • 콘텐츠기반 접근방식의 과도한 특성화 문제의 해결 가능성 제시
    • 군집화
      • 대표적으로 K-means, DBSCAN, OPTICS 존재
      • 기존 협력필터링에서 유사도를 측정하는 단계에 앞서, 유사한 그룹을 나누는 과정이 추가
      • 예측 정확도가 높아지는 장점
      • 데이터를 분할함에 따라 데이터 희소성 문제 발생
  • 차원 축소

    • 사용자가 구매한 상품이 너무 적으면 추천이 불가능
    • 사용자가 구매한 상품이 너무 많으면 많은 계산 비용이 발생
    • LSI기법을 적용하여 고차원의 행렬을 저차원의 행렬로 차원을 축소하는 방법 존재
    • 차원수가 너무 작으면 계산 속도는 빠르지만 정확도가 낮음
    • 차원수가 너무 높으면 계산 속도는 느리지만 정확도가 높음
      • 적절한 착원 선택이 중요
      • 1차적으로는 군집화를 진행
      • 엔트로피 가중치와 특이값 분행를 동시에 적용

협력 필터링 접근방식은 이웃한 사용자 또는 아이템의 유사도로 추천하는 기법이다. 협력 필터링 방식은 콘텐츠 기반 방식보다 더 정확하다는 장점이 존재하지만 Cold start, first rater문제가 있다. 이를 해결 하기 위해 다양한 연구가 진행 중에 있다. 협력 필터링 기법은 크게 2가지 방식이 존재한다. 첫 번째로는 기억 기반 협력 필터링이며 해당 방식은 사용자 또는 아이템 기반 방식이 존재한다. 모델 기반 방식은 기억 기반 방식을 기반으로 기계학습과 같은 모델을 사용하는 방법이다. 기억 기반 협업 필터링은 새로운 사용자나 아이템에 대한 추천에 대해 비교적 불안정하며, 사용자나 아이템이 증가할 때 시간과 메모리 문제가 발생할 수 있는 반면, 모델 기반 협업 필터링은 새로운 사용자나 아이템에 대한 추천이 가능하며, 적은 메모리와 높은 정확도를 보장한다. 하지만, 모델링이 어려워 다른 방법보다 많은 전문 지식이 필요

3. 하이브리드

특징

  • 각 방식의 장점을 극대화하면서 단점은 보완하고 다양한 정보를 효과적으로 활용할 수 있다.
  • 모델의 형태에 따라 크게 네 가지로 분류가 된다.
    1. 독릭된 추천 결과를 조합
    2. 콘텐츠 기반 정보를 협업필터링에 적용
    3. LSI, PLSI와 같은 알고리즘을 이용하여 협업필터링의 정보를 콘텐츠기반 접근방식에 융합
    4. 협업필터링과 콘텐츠기반 접근방식을 동시에 고려하는 단일 모델을 구축
      1. MCMC 와 같은 추정모델 또는 베이지안 학습법이 이용

종류

  1. 다른 추천 기준을 지닌 여러개의 알고리즘을 학습한 뒤 가중평균합을 구하는 방법
    • 여러 추천 알고리즘들의 결과를 전반적으로 이용 가능
    • 각 추천 점수를 정규화하여야 하며, 가중치를 잘 정의해야 함
  2. 여러 개의 추천 엔진중 현재 상황에 가장 적절한 추천 엔진 선택
    • 현재의 상황을 인지하기 위한 추가적인 계산이 필요’
  3. 추천 결과를 혼합하여 보여주는 방법
    • 다양성을 높게 보여줄 수 있음
  4. 모든 변수를 하나의 알고리즘의 변수로 병합
  5. 한 알고리즘이 추천한 아이템을 다른 알고리즘의 후보로 이용
  6. 각각의 알고리즘 추천 점수를 바탕으로 메타 알고리즘을 학습하는 앙상블 방법

$MCNee et al.(2006)$

  • 콘텐츠기반과 협업힐터링에 사용할 수 있는 알고리즘을 제시
  • 이웃기반 협업필터링
    • 세렌디피티(의도치 않게, 우연성)가 높은 경향이 있음
  • 나이브 베이지안 판별기
    • 선택한 논문이 많이 참조된 논문을 먼저 추천
  • PLSI
    • 사용자-아이템 행렬을 차원축소 방법 중 하나인 PLSI를 통해 학습
    • 선택한 논문과 분야적으로 매우 유사한 논문들이 우선적으로 추천
  • TF-IDF
    • TF-IDF를 이용하여 콘텐츠 기반 추천
    • 선택한 논문들과 내용이 매우 유사한 논문들이 우선적으로 추천.

$Vozalis and Margaritis(2004)$

  • 인구통게학 정보를 이용하여 유사한 이웃들의 정보만을 이용하는 연쇄방법을 제안

$Chow et al.(2014)$

  • 사용자의 선호정보를 결합하여 개인화 추천 알고리즘을 고안

$Basilico and Hofmann(2004)$

  • 사용자-아이템 행렬과 사용자-아이템의 특성 변수들을 모두 종합하여 유사도 커널을 정의

$Ganu et al.(2009)$

  • 리뷰 데이터로 추출된 토픽과 의미 벡터를 이용
  • 사용자나 아이템의 해당 리뷰 유무에 따라 추천 알고리즘을 선택할 수 있는 방법을 제안하고 성능을 입증

$Mcauley and Leskovec(2013)$

  • 리뷰 데이터로부터 토픽을 추출 시 리뷰 데이터와 점수를 모두 이용하여 토픽을 추출하는 HFT를 제안

$Vozalis and Margaritis(2004)$

  • 아이템들에는 숨겨진 변수가 있다고 가정
  • 숨겨진 변수를 리뷰 데이터로부터 추출
  • 사용자가 선택한 아이템과 유사한 속성을 지닌 다른 아이템을 추천
3주차(GNN기반 추천 시스템 조사)

3주차(GNN기반 추천 시스템 조사)

GNN(Grpah Neural Network)

Graph neural networks in recommender systems: a survey

0. 개요

유튜브, 넷플릭스 ,스포티파이와 같은 플랫폼에서 사용자의 방대한 항목(제품, 영화, 뉴스…)에서 관심 있는 항목을 탐색하기 위해 추천 시스템을 사용한다. 사용자의 과거 상호 작용(클릭, 시청, 읽기, 구매…)을 통해 사용자의 선호도를 정확하게 모델링하는 것이 효과적인 추천 시스템의 핵심이다.

대체로 지난 수십 년 동안 추천 시스템의 주류 모델링 패러다임은 이웃 방법 → 표현 학습 기반으로 발전해왔다.

—# 이웃방법

이웃방법

  • 아이템 기반 이웃방법

    아이템기반 이웃방법은 사용자가 상호작용한 과거 아이템과 유사한 아이템을 사용자에게 직접 추천한다. 초기 아이템 기반 이웃 접근 방식은 단순성, 효율성, 효과성으로 인해 실제 애플리케이션에서 큰 성공을 거두었다.

—# 표현학습

표현 학습

  • 사용자 - 아이템

    사용자와 아이템을 공유 공간에서 연속 벡터로 인코딩하여 직접 비교하는 표현 학습 기반 방식으로 사용자와 아이템 간의 관계를 명시적으로 모델링하는 방법론이다. 이 접근 방식은 비선형적이고 단순하지 않은 사용자-아이템 관계를 효과적으로 포착하고 풍부한 데이터 소스를 쉽게 통합할 수 있다는 점에서 인기를 얻고 있다.

넷플릭스 프라이즈 경진대회에서 행렬 인수분해 모델이 기존의 이웃 방식보다 추천에 더 우수하다는 사실이 입증된 이후 표현 기반 모델에 대한 관심이 급증했고 그 후 인수분해부터 딥러닝 모델에 이르기까지 사용자와 아이템의 표현을 학습하기 위한 다양한 방법이 제안되었다.

오늘날 딥러닝 모델은 비선형적이고 사소하지 않은 사용자-아이템 관계를 효과적으로 포착하고 문맥, 텍스트, 시각 정보 등 풍부한 데이터 소스를 쉽게 통합할 수 있는 능력으로 학술 연구 및 산업 응용 분야에서 추천 시스템을 위한 주요 방법론으로 자리 잡았다.

이러한 딥러닝 알고리즘 중에서도 추천 시스템의 정보를 그래프 관점에서 고려하는 그래프 학습 기반 방식이 있다. 추천 시스템에 있는 대부분의 데이터는 기본적으로 그래프 구조를 가지고 있다.

—# 추천 시스템에서 그래프 구조의 예로는 이분 그래프가 있으며 이 유형의 그래프는 사용자와 아이템의 두 집합으로 구성되며, 사용자와 아이템 간의 상호 작용(클릭, 보기, 읽기, 구매…)를 나타내는 링크가 있다.

—#이분 그래프
  • 그래프의 정점의 집합을 둘로 나눴을 때, 각 집합에 속한 정점끼리는 서로 인접하지 않도록 분할할 수 있는 그래프를 이분 그래프라고 한다.
  • 밑에 그림의 예제에서 사용자를 노란색 정점, 아이템을 파란색 정점이라고 생각하면 된다.
  • 사용자 정점은 서로 연결될 수 없으며, 아이템 정점 또한 마찬가지이다.
스크린샷 2023-03-15 오후 6 48 36

이 논문에서는 추천 시스템에서 데이터는 종종 그래프 구조를 가지며, 사용자 및 아이템 노드는 관찰된 상호 작용을 나타내는 링크로 연결되어 있다고 설명한다. 그래프 학습 알고리즘은 사회적 관계 및 지식 그래프와 같은 외부 정보를 포함하여 이러한 데이터를 모델링할 수 있는 방법을 제공한다. 과거에는 인수분해 기반, 분산 표현 기반, 신경 임베딩 기반 등의 그래프 임베딩 기법이 이러한 목적으로 사용되었지만, 최근에는 그래프 신경망(GNN)이 그래프 구조의 데이터에서 학습하는 데 탁월한 능력을 보여 많은 추천 모델에 사용되고 있다.

기존 추천 시스템은 사용자-아이템간의 상호작용만을 추천 신호로 사용하지만, GNN은 상호작용 그래프의 토폴로지 구조를 사용하여 사용자 및 아이템 표현을 개선할 수 있다. 이는 암시적으로만 했던 기존 방식과달리 GNN은 명시적으로 인코딩 할 수 있기 때문이다.

—# 토폴로지 구조

그래프에서 서로 다른 요소간의 관계를 의미한다. 추천 시스템의 맥락에서 이는 사용자가 아이템과 상호 작용하는 방식 또는 아이템이 서로 연관되는 방식을 나타낼 수 있다. GNN은 이러한 토폴로지 구조를 사용자와 아이템 간의 상호 작용에만 의존하는 기존 방식보다 더 나은 추천을 위한 추가 신호로 사용할 수 있다.

GNN(그래프 신경망)은 추천 시스템을 위한 유용한 도구로, 추천을 개선할 수 있는 아이템간의 복잡한 관계를 탐색할 수 있기 때문이다.

학계 연구에 따르면 GNN은 최근 몇 년간 추천 시스템에서 놀라운 성공을 거둔 기법이다. 많은 연구에 따르면 GNN 기반 접근 방식은 공개적으로 사용 가능한 샘플 데이터세트에서 이전 방법보다 성능이 뛰어나고 새로운 최첨단 결과를 달성하는 것으로 나타났다. 또한 GNN은 세션 기반, POI, 그룹, 멀티미디어, 번들 추천 등 다양한 추천 작업에 적용될 수 있는 다양한 변형이 있다.

GNN은 웹 스케일 추천 시스템과 같은 사용 애플리케이션에 사용되어 고품질 추천 결과를 생성하는데 도움이 되었다. 예를 들어 Pinterest는 수십억 개의 노드와 에지로 구성된 그래프에 PinSage라는 GNN기반 모델을 구현하여 사용자 참여도를 향상시켰다.

—# PinSage

PinSage는 30억 개의 노드와 180억 개의 엣지로 구성된 그래프를 기반으로 핀터레스트(Pinterest)에서 개발 및 배포한 GCN(그래프 컨볼루션 네트워크)알고리즘 모델이다. 랜덤워크 기반 기술을 사용하여 고품질의 추천 결과를 생성하며, 온라인 A/B테스트에서 사용자 참여도를 향상시키는 것으로 나타났다.

이 논문은 추천 시스템 분야에서 이 특정 조사가 기존 조사와 어떻게 다른지 논의하고 있다. 추천 시스템에 대한 다양한 관점에 초점을 맞춘 설문조사는 이미 존재하지만, 현재 GNN이 어떻게 추천시스템에 사용되고 있는지에 대한 종합적인 검토는 거의 없다. 본 조사는 GNN기반 추천 시스템의 발전 상황을 종합적으로 검토하고, 사용 정보 유형과 추천 작업에 따라 기존 작품을 분류하며, 이 분야의 미해결 과제와 향후 방향에 대해 논의하는 것을 목표로 한다.

이 논문은 GNN기술을 사용하는 추천 시스템에 대한 최신 문헌을 검토하는 것을 목표로 한다. 또한 이 논문에서는 GNN기반 추천 모델을 정리하기 위해 새로운 분류 체계를 제안하고 있으며 이 분류 체계는 다음과 같이 다섯 가지 그룹으로 정의할 수 있다.

  1. 사용자-항목 협업 필터링
  2. 순차적 추천
  3. 소셜 추천
  4. 지식 그래프 기반 추천
  5. 기타 작업(ex : POI 및 멀티미디어 추천)
1. 배경 및 분류(추천시스템과 GNN)

1.1 추천 시스템

추천 시스템은 사용자의 과거 행동이나 관심사를 기반으로 사용자에게 아이템을 추천하는 도구이다. 이 시스템은 학습된 사용자 및 아이템 표현과 점수 함수를 사용하여 항목에 대한 사용자의 선호도를 추정하며 결과 선호도 점수는 확률로 표시 될 수 있다.

추천 시스템의 일반적인 작업 중 하나는 사용자-아이템 협업 필터링으로, 시스템이 상호 작용을 기반으로 사용자와 아이템의 표현을 학습하는 것이다. 또 다른 방법은 시간 경과에 따른 상호 작용의 순차적 패턴을 분석하여 사용자 표현을 개선하는 것이다.

추천 시스템 분야는 사용자 익명성과 세션 세분화에 따라 다음과 같이 2가지로 나뉠 수 있다.

  1. 순차적 추천
    • 클릭, 시청, 읽기, 구매 등 아이템에 대한 사용자의 과거 상호 작용을 고려하는 시스템
  2. 세션 기반 추천
    • 순차적 추천에서 한 단계 더 나아가 익명의 사용자도 고려하고 사용자의 행동을 세션으로 세분화하여 이를 통해 개별 사용자의 행동 패턴에 따라 더욱 개인화된 추천을 제공

—# 세션

세션은 일정한 기간 내에 웹사이트에서 발생한 사용자 상호작용의 집합이다. 예를 들어 사용자가 웹사이트에 접속하고, 여러 페이지를 방문하고 장바구니에 상품을 담고 결제하고 나가는 과정을 하나의 세션이라 볼 수 있다.

GNN이 추천에 기여하는 바에 대한 연구 목적상 그 차이는 중요하지 않기 때문에 모든 추천 시스템을 “순차적 추천”으로 지칭하고 있다. 또한 사회적 관계를 사용하여 사용자 표현을 개선하는 “소셜 추천”이라는 또 다른 유형이 있다

—# 소셜 추천

소셜 추천은 사회적 관계를 맺고 있는 사람들이 비슷한 관심사와 선호도를 가지고 있으며, 서로의 선택에 영향을 미칠 수 있다는 것을 의미한다. 연구자들은 사용자의 선호도에 초점을 맞추는 것뿐만 아니라 지식 그래프에서 아이템 간의 속성과 의미 관계를 사용하여 아이템의 표현을 개선함으로써 이러한 추천 접근 방식을 개선하려고 노력한다.

1.2 GNN

최근에는 물리 시스템, 단백질 구조, 지식 그래프 등 그래프로 표현되는 데이터와 관련된 작업에서 뛰어난 성능을 보이는 GNN이라는 알고리즘을 사용하는 새롭고 혁신적인 시스템들이 등장햇다. 이 파트에서는 그래프가 무엇인지 설명하고 이미 존재하는 GNN 기술에 대한 개요를 분석한다.

그래프는 점(노드)과 이를 연결하는 선(엣지)의 집합이다. 노드의 이웃은 노드에 연결된 노드의 집합이다. 그래프는 방향이 있는 방향 그래프거나 방향이 없는 무방향 그래프가 존재하며, 또한 동질(모든 노드와 엣지가 동일)이거나 이질(노드 또는 엣지가 다름)일 수도 있다. 하이퍼그래프는 하나의 엣지가 두 개이상의 노드를 연결할 수 있는 그래프 유형이다.

GNN은 반복적인 프로세스를 사용하여 인접 노드의 특징 정보를 집계하고 이를 현재 중앙 노드 표현과 통합한다. 이는 집계 및 업데이트 작업을 모두 포함하는 여러 레이어를 쌓아 수행된다. 그 결과 제공된 그래프 데이터를 기반으로 보다 정확한 예측이 가능하다.

집계 단계에서는 평균 풀링 또는 어텐션 메커니즘을 사용하여 인접 노드의 특징을 결합한다. 업데이트 단계에서는 GRU라는 연결 또는 합계 연산과 같은 다양한 전략을 사용하여 집계된 피처를 중앙 노드 피처와 통합한다.

추천 시스템에 사용되는 그래프 신경망(GNN)기법은 5가지가 존재한다.

1. GNC(Graph Convolutional Network)
  • GCN은 Graph Convolutional Network의 약자로, 그래프 구조의 데이터에 합성곱 연산을 적용하는 GNN의 한 종류이다. 합성곱 연산은 이미지나 텍스트와 같은 격자 구조의 데이터에 잘 작동하는데, 이를 그래프 구조의 데이터에도 확장할 수 있다.
  • GCN은 각 노드가 자신과 인접한 노드들의 정보를 공유하고 학습하는 방식으로 작동한다. 이렇게 하면 노드 간의 상관관계를 잘 반영할 수 있다.
2. GraphSAGE(Graph SAmple and aggreGatE)
  • GraphSAGE는 각 노드가 자신과 인접한 노드들의 특성을 샘플링하고 집계하는 함수를 학습하는 방식으로 작동
  • GraphSAGE는 각 노드에 대해 고정된 수의 이웃을 선택한 다음 평균, 합계 또는 최대 풀링 기법을 사용하여 해당 이웃의 정보를 집계한다.
  • 이 집계된 정보를 현재 노드의 특징과 연결하고 비선형 활성화 함수와 학습 가능한 변환 행렬을 통과시켜 해당 노드의 업데이트된 특징을 얻는다.
  • GraphSAGE의 장점은 새로운 노드나 그래프에도 임베딩을 생성할 수 있다는 것이다. 기존GNN은 각 노드마다 고유한 임베딩을 학습하므로 새로운 데이터에 적용하기 어렵지만 GraphSAGE인접한 노드들의 특성을 활용하여 임베딩을 생성하므로, 이전에 보지 못한 데이터에도 유연하게 대응할 수 있다.
3. GAT(Graph Attention Network)
  • Graph Attention Network의 약자로, 그래프 구조의 데이터에 어텐션 메커니즘을 적용하는 GNN의 한 종류이다. 어텐션 메커니즘은 입력 데이터의 중요한 부분에 집중하는 방식으로 작동하는데, 이를 그래프 구조의 데이터에도 확장가능하다.
  • GAT은 각 노드가 자신과 인접한 노드들의 정보를 어텐션 가중치로 조합하여 학습하는 방식으로 작동한다. 이렇게 하면 노드 간의 상관관계와 특성을 잘 반영할 수 있습니다. GAT은 인접 행렬을 사용하지 않고 각 이웃 노드마다 다른 어텐션을 부여하여 자신의 임베딩을 업데이트한다.
  • 일반적으로 LeakyReLU함수를 사용한다.
  • GAT을 사용하는 이유는 사용자와 아이템 간의 관계를 그래프로 표현하고, 어떤 아이템에 더 집중할지 결정할 수 있기 때문
4. GGNN(Graph Gated Neural Network)
  • 그래프 구조의 데이터에 GRU를 적용하는 신경망의 한 종류이다.
  • GGNN은 그래프의 노드와 엣지에 있는 특성을 활용하여 노드 임베딩을 생성하고, 이를 바탕으로 다양한 그래프 분석 문제를 해결할 수 있다.
  • GGNN은 message passing이라는 방식으로 학습합니다. message passing이란 각 노드가 자신과 인접한 노드들과 정보를 주고받는 과정으로 GGNN은 message passing을 통해 각 노드의 임베딩을 업데이트하고, GRU를 사용하여 임베딩의 변화를 제어한다.

—# GRU

GRU는 Gated Recurrent Unit의 약자로, 순환 신경망(RNN)의 한 종류이다. GRU는 RNN이 장기 의존성 문제를 해결하기 위해 LSTM을 변형한 모델로, LSTM보다 간단한 구조를 가지고 있고 LSTM과 비슷한 성능을 보여주면서도 매개변수가 적기 때문에 효율적인 학습이 가능하다.

5. HGNN(Hypergraph Neural Network)
  • 하이퍼그래프 구조를 사용하는 신경망의 한 종류
  • 하이퍼그래프란 간단한 그래프와 달리 엣지가 두 개 이상의 노드를 연결할 수 있는 구조로, 고차원이고 복잡한 데이터 상관관계를 표현할 수 있다.
  • HGNN은 다중 모달 데이터나 이질적인 데이터와 같은 복잡한 실제 문제에 적용될 수 있으며 기존의 그래프 신경망보다 더 효과적인 표현 학습을 가능하게 한다.

Untitled

1.3 GNN을 사용하는 이유

최근 연구자들은 추천 시스템에 그래프 신경망을 사용하는 많은 연구를 제안하고 있다. GNN을 사용하는 가장 큰 이유는 GNN 기술은 데이터를 그래프 구조로 표현하고 분석하는 데 유용하기 때문이다. 많은 추천 시스템이 사용자-아이템 간의 상호작용을 위한 이분 그래프나 아이템 주문을 위한 시퀀스 그래프와 같은 그래프 구조를 사용한다. 사회적 관계나 지식 그래프와 같은 다른 데이터 유형도 자연스럽게 그래프 구조를 갖는다.

다양한 유형의 데이터에 대해 효과적인 추천모델을 만드는 일은 쉽지 않다. 그러나 GNN을 사용하면 더 나은 추천 결과를 위한 패턴을 효과적으로 학습할 수 있다. 예를 들어 GNN은 사용자-아이템 표현을 학습하고 비순차적 추천 작업에 대한 사용자 선호도를 예측할 수 있다.

또한 GNN은 소셜 네트워크와 같은 추가 정보를 사용자-아이템 이분법 관계에 통합 그래프로 통합하여 보다 정확한 추천을 제공한다.

GNN은 사용자와 아이템 간의 상호 작요인 협업 신호를 보고 이러한 신호를 사용하여 사용자와 아이템을 더 잘표현한다.

스크린샷 2023-03-15 오후 10 51 58

이 단원에서는 추천 시스템이 그래프 구조를 사용하여 추천을 개선하는 방법에 대해 설명한다. 특히, 아이템과 아이템 간의 상호작용 그래프를 구성하고 랜덤워크 알고리즘을 사용하여 항목의 순위를 매기는 ItemRank의 사용에 대해 중점적으로 설명한다 GNN은 그래프가 아닌 모델보다 추천에 더 효과적이다.

1.4 GNN을 기반으로 한 추천시스템의 종류

GNN은 그래프 신경망을 사용하여 과거 상호 작용에서 사용자 선호도를 모델링하는 추천 시스템의 한 유형이다. 이번 파트에서는 그래프 임베딩 프레임워크에 따라 분류하고 각 유형과 관련된 장점과 한계를 분석한다.

그래프가 구조화되는 방식은 표현되는 정보의 유형에 따라 달라진다. 예를 들어 소셜 네트워크는 동종 그래프로 표현할 수 있고, 사용자-아이템 상호 작용은 이분 그래프 또는 두 개의 동종 그래프(사용자-사용자 및 아이템-아이템)로 표현할 수 있다. 효율적인 그래프 신경망 구조를 설계하는 것은 집계 및 업데이트 작업, 네트워크 깊이와 같이 사용되는 정보의 유형에 따라 달라진다. 소셜 추천은 소셜 네트워크 정보를 사용하고 지식 그래프 기반 추천은 지식 그래프 내 아이템 간의 의미 관계를 활용하는 등 추천 작업은 사용되는 정보의 유형과 밀접하게 연관되어 있다.

2. USER-ITEM 협업 필터링

사용자-아이템 협업 필터링은 사용자가 상호작용하는 아이템을 사용하여 아이템의 표현을 개선하는 것이다. GNN 기법은 정보 확산을 모델링하고 사용자-아이템간의 상호 작용에서 고차 연결성을 포착하는 효율성을 개선하는 데 사용되었다. 다음 이미지는 사용자-아이템간의 상호작용 정보에 GNN을 적용하는 과정이다.

스크린샷 2023-03-15 오후 11 23 17

협업 신호를 캡처하는 데 GNN기법을 충분히 활용하기 위해 고려해야 할 네 가지 주요 이슈가 있다.

1. Graph Construction(그래프 구성)

  • GNN을 사용하여 분석할 데이터를 위한 구조를 만드는 것
  • 원본 그래프에는 사용자와 아이템 노드 및 이들의 상호 작용이 있다.
  • GNN을 적용할 때는 원래의 이분 그래프를 사용할지, 2홀 이웃을 기반으로 동질 그래프를 만들지 결정해야 한다.
  • 시간을 절약하기 위해서는 전체 그래프 대신 대표적인 이웃을 샘플링 해야함

2. Neighbor Aggregation(이웃 집계)

  • 네트워크에서 인접한 노드의 정보를 결합하는 프로세스를 말한다.
  • 이웃 노드의 중요성을 고려할지, 중앙 노드와의 상호 작용을 모델링할지 등 이웃 노드를 어떻게 처리할지 결정하는 작업이 포함되어 있다.

3. Information Update(정보 업데이트)

  • 중앙 노드의 표현을 이웃 노드의 표현과 결합하는 방법

4. Final Node Representation(최종 노드 표현)

  • 사용자가 어떤 아이템을 좋아할지 예측하려면 사용자의 전반적인 선호도를 이해해야 함
  • 사용자와 사용자가 상호 작용한 아이템에 대한 모든 정보를 결합
  • 마지막 레이어의 노드 표현을 사용할지 아니면 모든 레이어의 노드 표현을 결합하여 최종 표현을 만들지 선택

2.1 Graph Construction(그래프 구성)

많은 연구에서 사용자와 아이템의 이분법 그래프에 GNN을 사용했다고 설명한다. 그러나 원본 그래프에 직접 GNN을 사용하는 것은 비효율적일 수 있다. 그 이유는 원본 그래프가 사용자-아이템 표현을 학습하기에 충분히 포괄적이지 않기 때문이다. 효율성은 대규모 그래프에 대한 이웃 정보를 계산할 때 문제가 되며, 이는 높은 계산 비용을 초래한다.

사용자-아이템간의 상호작용 데이터에 대한 GNN의 효과와 효율성을 개선하기 위해 연구자들은 원래의 이분법 그래프 구조를 강화하는 전략을 모색해 왔다. 그래프 신경망을 사용하여 사용자-아이템 협업 필터링을 개선하는 두가지 전략이 있다.

(1) 그래프 구조를 풍부하게 하기 위해 엣지를 추가

(2) 가상 노드를 도입하여 사용자-아이템 상호작용을 보다 명확하게 포착

Multi-GCCF, DGCF, DHCF, HiGNN 등 여러 방법에서 이러한 전략을 채택하여 사용자-아이템 표현을 더 잘 학습하고 계산 비용을 절감했다.

대규모 그래프 기반 추천 작업을 위해 GNN을 효율적이고 확장 가능하게 만들기 위해 샘플링 전략이 제안되었다. PinSage는 ****무작위 도보 기반 샘플링(Random-walk-based sampling) 기법을 사용하여 중앙 노드에 직접 인접하지 않더라도 방문 횟수가 가장 높은 인접 노드를 선택하는 방법이다. Multi-GCCF 및 NIA-GCN과 같은 방법은 무작위 샘플링 전략을 사용하여 고정된 수의 인접 노드를 선택한다. 이 전략은 원본 그래프 정보 유지와 계산 효율성 사이의 균형을 유지한다. 그래프 기반 추천 모델의 품질과 효율성은 이웃을 구성하는 데 사용되는 샘플링 전략에 따라 달라지며, 보다 효율적인 샘플링 전략을 연구하고 개발해야 할 필요성이 있다.

Untitled

2.2 Neighbor Aggregation(이웃 집계)

그래프 이론 분야에서 정보가 네트워크를 통해 확신되는 방식은 인접 노드의 정보가 결합되는 방식에 따라 달라진다. 집계 단계가 이를 결정하며, 평균 풀링은 인접 노드의 정보를 균등하게 결합하는 데 사용되는 일반적인 방법이다. 그러나 이 방법은 더 중요한 그래프에는 적합하지 않을 수 있다. 이 문제를 해결하기 위해 일부 방법에서는 그래프에서의 위치에 따라 노드에 가중치를 할당하는 “차수 정규화” 를 사용한다.

—# 평균 풀링

평균 풀링은 인접 노드의 평균을 취하여 노드에 대한 표현을 형성하는 것이다.

**PinSage(그래프 기반 추천 시스템)**는 랜덤 워크라는 샘플림 전략을 사용하여 이웃의 벡터 표현을 집계한다. 이 방법은 정규화된 방문 횟수를 기반으로 이웃에게 중요성을 할당하지만 연결된 노드 간의 관계는 고려하지 않는다.

상식적으로 사용자의 관심사와 일치하는 아이템이 더 자주 추천되어야 한다. MCCFDisenHAN은 관심도 메커니즘을 사용해 사용자의 관심사를 기반으로 어떤 아이템이 더 중요한지 학습한다. NGCF는 사용자가 관심 있는 항목의 기능이나 사용자가 추천 항목에서 원하는 기능을 강화하기 위해 요소별 상품을 사용한다. NIA-GCN은 그래프 기반 추천 작업에서 이웃 내 관계 정보를 보존하는 문제를 해결하는 방법이다. 사용자-사용자 또는 아이템-아이템 관계와 같은 이웃 간의 상호 작용을 명시적으로 포착하기 위해 모든 두 이웃간의 요소별 곱셈을 사용하는 쌍별 이웃 집계 접근 방식을 사용한다.

2.3 Information Update(정보 업데이트)

노드의 이웃 노드로부터 정보를 수집한 후에는 추가 정보 전파를 위해 노드의 표현을 업데이트하는 것이 중요하다. 노드의 표현을 업데이트하는 기존 방법은 노드의 원래 정보를 유지하는지 아니면 이웃 노드의 집계된 표현을 위해 완전히 폐기하는지에 따라 2가지 범주로 분류할 수 있다.

  1. 사용자-아이템 노드의 원본 정보를 완전히 버리고 이웃 노드의 집계된 표현을 새로운 중앙 노드 표현으로 사용

  2. 노드자체와 그 이웃 메시지를 모두 고려(두가지 표현을 결합)

    두 가지 표현을 결합하는 다양한 방법이 존재한다. 가장 간단한 방법은 두 표현을 더하거나 평균을 내는 것이다. 또 다른 접근 방식은 두 표현을 연결하고 비선형 변환을 적용하여 더 복잡한 특징 상호 작용을 허용하는 것이다. 그러나 일부 연구에 따르면 비선형 변환이 성능을 크게 향상시키지 못하기 때문에 일부 모델은 비선형성을 제거하여 프로세스를 단순화한다.

2.4 Final Node Representation(최종 노드 표현)

GNN에서는 레이어별 프로세스를 통해 각 깊이에 대한 노드 표현을 생성한다. 예측을 하려면 사용자와 아이템의 전체적인 표현이 필요하다. 일반적으로 마지막 레이어의 노드 벡터를 최종 표현으로 하지만 서로 다른 레이어에서 얻은 표현은 연결을 통해 전달되는 서로 다른 메시지를 강조한다. 하위 레이어 표현을 개별 특징을 반영하고 상위 레이어 표현은 이웃 특징을 반영한다. 최근 연구에서는 서로 다른 계층의 메시지를 통합하여 출력으로 표현된 모든 연결으 이점을 활용한다.

2.5 요약

1. Graph Construction(그래프 구성)

그래프 구성이란 사용자와 아이템 간의 관계를 표현하기 위해 그래프 구조를 만드는 과정을 말하며 사용자-아이템 이분 그래프는 이러한 관계를 표현하는 간단한 방법입니다. 그러나 일부 노드의 연결 수가 적은 경우 에지 또는 노드를 추가하면 그래프 구조를 개선할 수 있다. 노드 주변을 샘플링하는 것도 계산 효율성을 위해 수행할 수 있지만 효과와 효율성 간의 절충이 필요로한다. 효과적인 샘플링 전략에 대해서는 더 많은 연구가 필요하다.

2. Neighbor Aggregation(이웃 집계)

이웃 집계는 그래프 또는 네트워크에서 인접한 노드의 정보를 결합하는 프로세스를 말한다. 인접 노드가 서로 다른 경우, 세심한 가중치를 사용하여 정보를 결합하는 것이 동일한 가중치를 사용하거나 차수별로 정규화하는 것보다 낫지만 인접 노드가 비슷한 경우에는 동일한 가중치를 사용하거나 차수별로 정규화하는 것이 더 쉬울 수 있다. 프로세스를 개선하기 위해 이웃 노드가 서로 영향을 미치는 방식이나 중앙 노드가 이웃 노드에 연결되는 방식을 명시적으로 모델링하는 것이 유용할 수 있지만, 더 많은 데이터 세트에서 테스트해야 한다.

3. Information Update(정보 업데이트)

원래 노드를 제거하는 대신 원래 형태와 주변 노드의 정보를 사용하여 노드를 업데이트하는 것이 더 좋다. 최근의 일부 연구에 따르면 일반적인 그래프 신경망 모델의 단순화된 버전이 실제로 원래 모델보다 더 나은 성능을 발휘할 수 있다는 사실이 밝혀졌다.

4. Final Node Representation(최종 노드 표현)

마지막 레이어의 표현만 사용하는 대신 모든 레이어를 함께 사용하는 것이 좋다. 가중 풀링과 연결이라는 두 가지 일반적인 기술이 있다.

스크린샷 2023-03-20 오후 7 09 38
3. 순차적 추천

순차적 추천은 사용자의 이전 행동을 기반으로 다음에 사용자가 관심을 가질 만한 것을 예측하는 방법이다. 여기에는 사용자가 상호 작용한 아이템의 순서에서 패턴을 캡처하고 해당 순서를 기반으로 추천을 생성하는 것이 포함됩니다. 아이템의 시퀀스를 일종의 그래프로 취급할 수 있으며, 이러한 개념으로 인해 사용자의 순차적 행동에서 전환 패턴을 포착하는 데 그래프 신경망(GNN)이 점점 더 많이 사용되고 있다. GNN은 시퀀스를 그래프로 모델링하고 아이템 전환의 패턴을 분석하는 데 도움이 될 수 있다. 순차 추천에서 GNN을 사용할 때 그래프 구성, 정보 전파, 순차 선호도라는 세 개의 주요 이슈를 해결해야 한다. 다음 그림은 순차 추천에서 GNN의 전체 프레임워크이다.

스크린샷 2023-03-20 오후 6 57 46

1. Graph Construction(그래프 구성)

  • 데이터를 시퀀스 그래프로 변환해야 함
  • 각 시퀀스에 대한 하위 그래프를 독립적으로 구성
  • 연속된 두 아이템 사이가 아닌 연속된 여러 아이템 사이에 엣지를 추가
  • 위 2개의 항목 중 더 나은것을 선택해야 함

2. Information Propagation(정보 전파)

  • 전환 패턴을 포착하기 위해 최적의 정보 전파 방법을 결정하는 것이 중요
  • 연결된 아이템에 대해 순차 순서를 구분해야 하는지 여부는 불분명

3. Sequential Preference(순차 추천)

  • 사용자의 가장 최근 활동을 기반으로 사용자의 다음 선호도를 예측
  • 사용자의 순차적 행동에서 전환 패턴을 포착하기 위해 사용자의 시간적 선호도를 아이템 표현을 시퀀스로 통합
  • 연속적인 시간 패턴을 향상시키는 두 가지 방법은 주의 풀링 또는 RNN 구조를 활용

3.1 Graph Construction(그래프 구성)

사용자-아이템 상호작용은 이분 그래프 구조를 가지는 반면 순차적 동작은 시간 순서대로 시퀀스로 표현된다. 순차 추천에 GNN을 사용하기 위해서는 사용자의 순차적 행동을 기반으로 한 순서 그래프를 구성해야 한다.

다음 그림은 순차적 행동으로부터 그래프를 생성하는 다양한 방법을 보여준다. 이 과정은 순차적 추천에 GNN을 적용하는 데 필요하다. 가장 간단한 방법은 각 시퀀스의 각 항목을 노드로 처리하고 연속적으로 클릭한 두 아이템 사이에 엣지를 추가하여 각 시퀀스에 대한 방향 그래프를 구성하는 것이다. 하지만 사용자 시퀀스의 길이가 짧은 경우가 많이 때문에 이 접근 방식은 대부분의 시나리오에서 효과적이지 않을 수 있다.

최근 연구에서는 동일한 사용자 또는 전체 데이터 세트의 추가 행동 시퀀스를 사용하여 원래 시퀀스 그래프 구조를 보강하는 전략을 제안했고 이러한 전략은 단일 시퀀스 내에 더 많은 노드와 연결을 추가하는 전략과 여러 시퀀스를 함께 결합하는 전략으로 나눌 수 있다.

스크린샷 2023-03-20 오후 7 01 59
  1. 사용자 행동 데이터의 추가 시퀀스를 사용하여 아이템과 아이템간의 관계에 대한 이해도를 높임

    • 과거 사용자 시퀀스나 다른 유형의 행동 시퀀스와 같은 다양한 소스에서 가져올 수 있음(HetGNN)

    —# HetGNN : 모든 행동 시퀀스를 분석하고 동일한 시퀀스에 있는 아이템 사이에 행동 유형을 엣지 유형으로 엣지를 생성

  2. 전체 시퀀스의 전반적인 특성을 반영하기 위해 연속된 아이템 사이에 엣지를 추가하거나 가상 ‘스타’ 노드를 도입하는 등 현재 시퀀스의 그래프 구조를 조정

3.2 Information Propagation(정보 전파)

GNN프레임워크는 일반적으로 이전 아이템과 다음 아이템의 정보를 평균화하여 결합하고 GRU 구성 요소를 사용하여 인접 노드와 중앙 노드의 정보를 통합하여 방향 그래프에 정보를 전당하는 데 사용된다. 이를 통해 시퀀스 크래프에서 아이템 간의 전환 패턴을 효율적으로 전파하고 캡쳐할 수 있다. 일부 방법은 어텐션 메거니즘을 사용하여 특정 이웃에 더 많은 중요성을 부여한다.

3.3 Sequential Preference(순차 추천)

GNN은 아이템 간의 장거리 종속성을 포착하는 데 한계가 있다. 따라서 사용자의 시퀀스 선호도를 반영하기 위해 시퀀스의 마지막 아이템의 표현을 제한한다. 따라서 연구자들은 효과적인 시퀀스 표현을 얻기 위해 시퀀스 내 아이템 표현을 통합하는 전략을 제안했다.

시퀀스에서는 아이템마다 중요도나 우선순위가 다르기 때문에 아이템 표현을 효과적으로 통합하기 위해 어텐션 메커니즘이 일반적으로 사용된다.

NISER와 GCE-GNN과 같은 일부 접근 방식은 위치 임베딩을 추가하여 위치를 인식하는 아이템 표현을 얻고, FGNN은 반복적인 사용자 선호도 업데이트를 위해 GRU와 어텐션 메커니즘을 사용한다. 한편, DHCN과 COTREC은 세션 간 그래프를 통합하여 두 단계에서 학습한 순차적 표현을 결합함으로써 시퀀스 그래프를 강화한다.

3.4 요약

1. Graph Construction(그래프 구성)

그래프를 구성하는 방법은 다양하며 어떤 방법이 더 나은지에 대한 합의는 아직 없으며 세션 간 그래프를 통합하면 추천 시스템의 성능을 개선하는 데 도움이 될 수 있다.

  1. 단순히 연속된 항목 사이에 엣지를 추가
  2. 행동 시퀀스의 그래프 구조를 조정

2. Information Propagation(정보 전파)

그래프 네트워크 전체에 정보를 전파하는 방법은 다양하며 방법마다 계산 비용과 성능 향상 측면에서 서로 다른 장단점이 존재하며 이 또한 합의된 최적의 방법은 아직 없다. 일부 더 복잡한 방법은 계산이 증가하는 대신 더 나은 성능을 얻을 수 있다. 최종적으로는 어떤 방법을 사용할지 결정하는 것은 애플리케이션의 특정 요구 사항과 제약 조건에 따라 달라질 수 있다.

3. Sequential Preference(순차 추천)

특정 순서에 따라 항목을 추천할 때는 일반적으로는 어텐션 메커니즘을 사용하여 해당 아이템의 표현을 결합한다. 연구자들은 성능을 더욱 개선하기 위해 위치 임베딤을 추가하여 아이템의 순서를 파악하는 실험을 진행하였고 RNN 구조를 사용하면 모든 순차적 추천 작업에서 성능을 향상시킬 수 있는지는 아직 모른다.

해당 그림은 세 가지 주요 문제를 해결하기 위해 검토된 모델에서 사용된 주요 전략에 대한 개요이다.

스크린샷 2023-03-21 오후 7 20 58
4. 소셜 추천

소셜 추천 시스템은 사용자의 소셜 네트워크에서 얻은 정보를 사용하여 추천을 개선한다. 이는 소셜 네트워크에 연결된 사람들은 비슷한 선호도를 가질 것이라는 생각에 기반한 것이다. ex)같은 전공은 가진 그룹들은 같은 관심사를 가질 확률이 높음

소셜 추천 시스템에서는 사용자 간의 소셜관계를 정규화 도구로 사용하여 사용자 표현을 제한하는 방법도 있고, 이러한 관계를 입력을 통합하여 원래 사용자 임베딩을 개선하는 방법도 있다. 그래프 학습 관점에서 보면, 이전 연구들은 각 사용자의 1차 이웃을 모델링하는 것으로 볼 수 있다. 하지만 실제로 사용자는 친구의 친구에게도 영향을 받을 수 있다. 이전 연구들에서 고차 영향 확산을 무시하면 최적이 아닌 추천 성능으로 이어질 수 있다. GNN은 사용자가 다른 사람들과의 관계에 의해 어떻게 영향을 받는지 시물레이션할 수 있기 때문에 추천 시스템에서 소셜 정보를 모델링하는 데 널리 사용된다. GNN은 재귀적인 소셜 확산 과정을 포착할 수 있어 사용자 행동과 선호도를 정확하게 표현할 수 있다.

GNN을 사용하여 소셜 정보를 모델링할 때는 다음과 같은 두 가지 주요 문제를 해결해야 한다

1. Influence of Friends (친구의 영향력)

  • 모든 친구가 동일한 수준의 영햑력을 가지고 있는가
  • 일부 친구가 다른 친구보다 더 많은 영향력을 가지고 있는가
  • 정확한 추천을 하려면 다양한 수준의 영향력을 가진 친구를 구분하는 것이 중요

2. Preference Integration(선호도 통합)

  • 친구와의 사회적 관계와 아이템과의 상호작용을 통합해야 함

4.1 Influence of Friends (친구의 영향력)

소셜 네트워크에서는 사용자 간의 연결이 얼마나 강한지 불분명한 경우가 많다. 이러한 연결을 사용하여 추천을 할 때는 각 친구가 얼마나 영향력이 있는지 파악해야 추천이 더 정확해질 수 있다. 기존 모델에서는 모든 친구의 영향력이 동일하다고 가정했지만, 사용자는 사회적 유대 관계가 강하거나 선호도가 비슷한 친구의 영향을 받을 가능성이 더 높기 때문에 이는 정확하지 않다. DiffNet은 평균 풀링 연산을 사용하여 이 문제를 해결하려고 시도했지만, 이러한 동등한 영향력 가정은 실제로는 신뢰할 수 없다. 현재는 이를 고려하기 위해 어텐션 메커니즘을 사용하여 서로 다른 이웃의 영향력을 차별화하여 전반적인 성능을 개선하는 것이 일반적인 접근 방식이다. Song 연구원 등은 사용자의 행동을 모델링하기 위해 순환 신경망을 사용하고, 현재 관심사를 기반으로 소셜 연결의 영향력을 파악하기 위해 그래프 어텐션 신경망을 사용하는 DGRec이라는 방법을 제안했다. 어텐션 메커니즘은 추천 시스템에서 친구의 영향력을 결정할 때 평균 풀링 연산에 비해 더 나은 접근 방식이며 친구마다 영향력 수준이 다르다는 가정을 확인하여 서로 다른 친구의 영향력을 구분할 수 있다. 신뢰할 수 없는 소셜 관계를 포함하면 성능에 부정적인 영향을 미칠 수 있으므로 추천에 통합할 소셜 관계를 신중하게 선택하는 것은 중요하다. ESRP는 자동 인코딩 메커니즘을 사용하여 관련 없는 관계를 필터링하고 새로운 이웃을 탐색함으로써 이 문제를 해결한다. DiffNetLG는 암묵적 로컬 영향력을 사용하여 관찰되지 않은 사회적 관계를 예측한다.

4.2 Preference Integration(선호도 통합)

소셜 추천에는 사용자-아이템 상호작용과 소셜 그래프라는 2가지 유형의 관계가 포함된다. 소셜 정보를 사용하여 사용자 선호도를 더 잘 이해하기 위해 이 2가지 네트워크를 결합하는 2가지 전략이 있다.

  1. 밑에 그림 (a)와 같이 각 네트워크에서 별도의 사용자 특징을 학습한 다음 이를 최정 선호도 벡터에 통합하는 방법
  2. 밑에 그림(b)와 같이 사용자-아이템 상호작용과 소셜 네트워크를 단일 네트워크로 결합하고 GNN을 사용하여 네트워크 전체에 정보를 확산시키는 방법
스크린샷 2023-03-21 오후 7 51 40

1번 방식으로 개별적으로 처리할 경우 사용자-아이템 이분 그래프에 대한 고급 방법을 직접 적용할 수 있다는 장점이 있으며, 영향력 프로세스를 시뮬레이션 하는 데 GNN기법이 적합하다는 장점이 있다. 두 관계에서 학습한 사용자 표현의 통합은 두 네크워크 사용자 표현을 합치는 선형성 또는 다층 퍼셉트론을 사용하여 두 표현 간의 기능 상호 작용을 강화하는 비선형성 조합 메커니즘을 사용할 수 있으며 널리 채택되고 있다.

DiffNet은 두 공간의 사용자 표현을 동등하게 취급하고 합 풀링 연산을 통해 결합하는 반면, DANSER은 동일한 가중치 조합을 제공하는 대신 사용자-아이템 쌍을 이루는 특징에 따라 가중치를 동적으로 할당한다.

2번 방식은 두 네크워크를 하나로 통합하고 GNN기법을 적용하여 영향력 프로세스를 시뮬레이션하는 것이다. 1번 방식의 장점은 두 네트워크의 확산 깊이를 차별화할 수 있다는 점이며, 2번째 전략의 장점은 고차적인 사회적 영향력과 관심사 확산을 모두 통합적으로 모델링할 수 있다는 점이다. 선형성 및 비선형성 조합을 포함하여 두 관계에서 학습한 사용자 특징을 통합하는 데 다양한 방법이 사용된다.

DiffNet++는 2단계 관심 네트워크를 통해 그래프에서 사용자 노드를 업데이트하는 모델이다. 그래프에서 서로 다른 두 가지 유형의 이웃, 즉 상호작용한 아이템과 소셜 네트워크의 친구로부터 얻은 정보를 결합한다. 이는 각 유형의 이웃에 대해 개별적으로 GAT 메커니즘을 사용하여 수행된다.

SEFrame은 그래프 네트워크를 사용하여 사회적 관계, 사용자-아이템 상호작용, 아이템 전환에서 얻은 정보를 결합하는 방식이다. 2단계 어텐션 네트워크를 활용하여 정보를 전파한다. 어떤 전략이 가장 효과적인지는 아직 근거가 없기 때문에 불확실하다.

4.3 요약

소셜 추천에는 두 가지 이슈가 존재한다.

  1. Influence of Friends (친구의 영향력)

    모든 친구를 동등하게 대하기보다는 서로 다른 친구의 영향력을 고려하는 것이 좋다. 한 가지 접근 방식은 소셜네트워크의 노이즈를 기반으로 소셜 관계를 자동으로 조정하는 것이다.

  2. Preference Integration(선호도 통합)

    선호도 통합은 추천 시스템에서 사용자 선호도를 형성하기 위해 2가지 정보 소스, 즉 사용자-아이템 상호작용과 소셜 관계를 결합하는 것을 말한다. 이러한 그래프를 결합하는 방법은 두 그래프를 개별적으로 고려할지 하나의 그래프로 통합할지에 따라 달라진다. 별도의 그래프를 사용하는 경우, 사용자 선호도는 이 두 그래프에서 학습된 전체 특징을 통합한 것인 반면 통합 그래프의 경우 일반적으로 채택되는 전략은 집계 스키마이다.

스크린샷 2023-03-21 오후 8 23 31
5. 지식 기반

소셜 네트워크는 사용자 관계에 대한 이해를 높이는 데 사용되며, 지식 그래프는 속성을 통해 아이템 특징을 향상시키는 데 사용된다. 이를 통해 아이템 간의 연관성을 탐색하고 사용자 상호 작용을 기반으로 추천 아이템의 해석 가능성을 개선하는 등의 이점을 얻을 수 있다. 하지만 지식 그래프는 여러 엔티티 유형과 관계로 구성된 복잡한 구조로 인해 사용하기 어려울 수 있다. 일반적으로 사용되는 방법은 지식 그래프 임베딩(KGE)를 사용하여 지식 그래프를 전처리하여 개체와 관계의 임베딩을 학습하지만, 이러한 방법은 전환 제약 조건으로 의미적 연관성을 모델링하는 데 중점을 두기 때문에 링크 예측과 같은 그래프 관련 작업에 더 적합하다. 메타 경로 기반 방법은 예측 모델에 입력되는 메타 경로를 수동으로 정의하며 도메인 지식이 필요하고 복잡한 지식 그래프에 대해 시간이 많이 든다.

지식 그래프 기반 추천의 개념에는 사용자-아이템 상호작용 데이터와 지식 그래프를 모두 사용하여 아이템 간의 관계를 파악함으로써 아이템에 대한 사용자의 선호도를 결정하는 것이 포함된다. 이 접근 방식은 사용자-아이템 상호작용 신호를 지식 그래프와 통합하는 방법과 지식 그래프에서 엔티티 간의 다양한 유형의 관계를 고려한 집계 함수를 설계하는 방법이라는 2가지 주요 과제를 해결 해야한다.

—# 엔티티

지식 기반 추천시스템에서 엔티티란 텍스트에서 의미있는 정보를 추출하기 위해 이름이 있는 사람, 장소, 조직 등과 같은 명사적인 개체를 의미하며 이러한 엔티티들은 자연어 처리 기술을 이용해 추출된다.

1. Graph Construction(그래프 구성)

  • 그래프에 사용자 노드를 포함
  • 사용자 노드를 사용하여 관계의 중요도를 구분

2. Relation-aware aggregation(관계 인식 집계)

  • 지식 그래프에는 엔티티 간에 다양한 유형의 관계가 존재
  • 관계 인식 집계는 이러한 연결된 엔티티의 정보를 효과적으로 결합

5.1 Graph Construction(그래프 구성)

그래프를 만들 때 중요한 고려 사항 중 하나는 사용자 상호 작용의 신호를 그래프에서 사용할 수 있는 지식과 결합하는 방법이다. 이를 그래프 구축에서 협업 신호와 지식 정보를 통합하는 작업이라고 한다.

지식 그래프 기반 추천을 개선하기 위해 사용자 노드를 엔티티 유형으로 통합하고 사용자아와 아이템 간의 관계를 “상호작용”으로 표현하여 사용자-아이템 상호작용을 지식 그래프와 결합하는 것이 한 가지 접근 방식이다. 이는 KGAT, MKGAT, CKAN과 같은 모델에서 수행된다. 연구자들은 지식 그래프에서 사용자-아이템쌍과 사용자가 과거에 상호작용한 아이템 및 관련 의미를 연결하는 하위 그래프를 구축하는 데 주력했다. 이는 지식 그래프의 풍부한 정보를 활용하여 아이템 간의 연관성을 명시적으로 파악하고 아이템에 대한 사용자의 선호도를 추정함으로써 지식 그래프 기반 추천 시스템을 개선하기 위한 것이다.

AKGE는 TransR을 사용하여 엔티티의 임베딩을 사전 학습하고, 연결된 엔티티 간의 쌍별 유클리드 거리를 계산한 후, 경로가 짧을 수록 더 안정적인 연결을 반영한다는 가정하에 대상 사용자와 아이템 노드 간의 거리가 가장 짧은 K개의 경로를 선택하여 지식 그래프에서 하위 그래프를 구성한다.

ATBRG라는 방법은 여러 계층의 엔티티 이웃을 철저히 검색하여 겹치는 엔티티를 사용하여 사용자 행동과 대상 아이템을 연결하는 경로를 복원함으로써 이 문제를 해결한다. 이 접근 방식은 사용자-아이템 쌍과 더 관련성이 높은 하위 그래프를 얻는 데 도움이 된다. ATBRG 방법은 중요한 정보에 집중하기 위해 링크가 하나만 있는 엔티티를 잘라내어 그래프의 크기를 조절하는데 도움이 된다. 이 접근 방식은 효과적이지만 엔티티 임베딩을 사전 학습하거나 경로를 철저하게 검색하고 정리하는 데 많은 시간이 소요될 수 있어서 효율적인 하위 그래프 구성 전략을 찾으려면 추가 조사가 필요하다.

일부 연구자들은 지식 그래프에서 관계에 대한 사용자 선호도를 고려하기 위해 다른 접근 방식을 사용한다. KGCN과 KGNN-LS는 사용자 노드를 사용하여 엔티티 간의 다양한 관계에 가충치를 할당한다.

5.2 Relation-aware aggregation(관계 인식 집계)

그래프 신경망은 연결된 개체와 개체 간의 관계를 모두 고려하여 지식 그래프에서 정보를 더 잘 포착할 수 있다. 추천 시스템에서는 사용자의 역할도 중요하다. 그래프 주의 네트워크(GAT)를 사용하면 연결된 노드를 기반으로 적응형 가중치를 부여할 수 있으며, 대부분의 작업은 점수 함수를 사용하여 연결된 엔티티의 가중 평균을 기반으로 중앙 노드를 업데이트한다. 일부 연구에서는 사용자 노드를 지식 그래프에서 또 다른 유형의 엔티티로 취급하며, 사용자 선호도가 업데이트 과정에서 다른 엔티티에 전달된다. 이러한 작업은 관계에 대한 사용자의 관심사를 명시적으로 모델링하지 않고, 연결된 노드와 관계에 따라 엔티티의 영향력을 차별화하며 KGAT모델은 관계 공간에서 연결된 엔티티 간의 거리를 기반으로 가중치를 할당한다.

5.3 요약

지식 그래프 기반 추천에는 두 가 지이슈가 존재한다. 1. Graph Construction(그래프 구성)

  • 사용자 노드를 엔티티 유형으로 간주
  • 사용자 노드를 관계를 구분하기 위해 암시적으로 사용

2. Relation-aware aggregation(관계 인식 집계)

  • 연결된 엔티티의 정보를 집계하기 위해 GAT의 변형을 사용
  • 사용자 노드가 없는 그래프의 경우 사용자 표현을 사용하여 관계에 가중치 할당
  • 관련성이 높은 엔티티와 관계에 초점을 맞출 수 있다는 장점이 있지만 계산 시간이 더 많이 걸리고 추가 조사가 필요하다.
6. 결론

GNN을 추천시스템에 사용하는 것은 인기를 끌고 있다. 그 이유는 그래프 데이터에 대한 학습에서 GNN이 우수한 결과를 보여줬기 때문이다. 최근의 GNN 기반 추천 시스템 관련 연구들을 정리하기 위한 분류 체계를 제안하고, 각 분류별 대표 모델들이 채택하고 있는 주요 이슈와 전략, 그리고 장점과 한계가 있다. 이 논문에서는 GNN에 대한 전반적인 이해를 제공하는 것을 목표로 한다.

4주차(GNN을 이용하여 추천 시스템 구현 LightGCN)

4주차(GNN을 이용하여 추천 시스템 구현 LightGCN)

GNN(Graph Neural Networks)

1. Graph

그래프는 점(노드)과 이를 연결하는 선(엣지)의 집합이다. 노드의 이웃은 노드에 연결된 노드의 집합이다. 그래프는 방향이 있는 방향 그래프거나 방향이 없는 무방향 그래프가 존재하며, 또한 동질(모든 노드와 엣지가 동일)이거나 이질(노드 또는 엣지가 다름)일 수도 있다. 하이퍼그래프는 하나의 엣지가 두 개이상의 노드를 연결할 수 있는 그래프 유형이다.

1

Untitled 크게

Grpah = G(V,E)

2. GNN

GNN은 Graph Neural Network의 약자로, 그래프 데이터 분석에서 사용하는 딥러닝 모델이다. 예를 들어, 소셜 네트워크는 친구 관계를 그래프로 표현할 수 있다.

GNN은 이러한 그래프 데이터를 입력으로 받아서, 각 노드의 임베딩을 추출하거나, 그래프 전체를 분류하는 등의 작업을 수행한다. GNN은 일반적으로 메시지 전달 방식을 사용하여, 각 노드의 임베딩을 계산합니다. 이때, 각 노드의 이웃 노드와의 관계를 이용하여, 해당 노드의 임베딩을 갱신한다.

GNN은 반복적인 프로세스를 사용하여 인접 노드의 특징 정보를 집계하고 이를 현재 중앙 노드 표현과 통합한다. 이는 집계 및 업데이트 작업을 모두 포함하는 여러 레이어를 쌓아 수행된다. 그 결과 제공된 그래프 데이터를 기반으로 보다 정확한 예측이 가능하다.

집계 단계에서는 평균 풀링 또는 어텐션 메커니즘을 사용하여 인접 노드의 특징을 결합한다. 업데이트 단계에서는 GRU라는 연결 또는 합계 연산과 같은 다양한 전략을 사용하여 집계된 피처를 중앙 노드 특징과 통합한다.

그래프에서 중앙노드란, 그래프 구조에서 중심에 위치한 노드를 의미한다. 예를 들어, 소셜 네트워크에서 친구 관계를 그래프로 표현했을 때, 친구들 사이에서 가장 많은 연결을 가지고 있는 노드를 중앙노드라고 부를 수 있다.

  • GNN Task
    • Node : 특정 노드가 어떤 레이블에 속하는지
    • Edge : 연결되지 않은 두 정점이 연결가능성이 있는지
    • Graph : 그래프 자체가 어떤 레이블에 속하는지

  • GNN의 구성 요소

GNN에서 그래프를 구성하기 위해서는 3가지가 필요하다.

  1. Vertex(node)
  2. Edge
  3. Feature
2

—# Vertext와 Edge와의 관계를 adjacency matrix(인접행렬)로 나타냄

—# Feature는 Feature metrix(특징 행렬)로 나타냄

Graph represctation learning이 먼저 시작하고 딥러닝을 접목시킨 것이 GNN이다.

추천 시스템에 사용되는 그래프 신경망(GNN)기법은 5가지가 존재한다.

  • 1. GCN(Graph Convolutional Network)

  • 2. GraphSAGE(Graph SAmple and aggreGatE)

  • 3. GAT(Graph Attention Network)

  • 4. GGNN(Graph Gated Neural Network)

  • 5. HGNN(Hypergraph Neural Network)

3. 노드 임베딩

GNN은 여러 노드들의 고차원 정보를 저차원 공간으로 임베딩하여 그 특성을 파악한다.

그래프 데이터 분석에서 노드 임베딩은 각 노드를 벡터 형태로 변환하는 것을 의미한다. 이를 통해 노드 간의 유사도를 계산하거나, 머신러닝 모델의 입력으로 활용할 수 있다.

예를 들어, 소셜 네트워크 그래프에서 각 노드는 사용자를 나타내고, 각 노드의 임베딩은 해당 사용자의 특성을 나타내는 벡터로 변환다. 이렇게 변환된 벡터를 이용해 사용자 간의 유사도를 계산하거나, 머신러닝 모델의 입력으로 활용할 수 있다. 또한, 임베딩은 다른 그래프 데이터 분석 알고리즘과 결합하여 그래프의 정보를 보다 효과적으로 활용할 수 있도록 한다.

3-1. 임베딩 종류

  1. 그래프 스펙트럴 방법: 그래프 스펙트럴 방법은 노드 임베딩을 특잇값 분해(SVD)나 라플라스 행렬(Laplacian matrix) 등의 그래프 스펙트럴 분석을 통해 추출 이 방법은 그래프 전체의 정보를 이용해 노드 임베딩을 학습하며, 그래프의 대칭성과 성질을 이용해 임베딩을 생성한다. 대표적으로 DeepWalk, node2vec, LINE, SDNE 등이 있다.

  2. 그래프 신경망 방법: 그래프 신경망 방법은 메시지 패싱(Message Passing)을 이용해 노드의 임베딩을 학습한다. 메시지 패싱은 이웃 노드들과의 정보교환을 통해 노드의 임베딩을 계산한다. 이 방법은 노드간의 구조적인 관계와 특성을 잘 반영할 수 있으며, 그래프의 구조와 노드의 특성을 모두 고려할 수 있다. 대표적으로 GCN, GAT, GraphSAGE, Graph Convolutional Matrix Completion (GCMC) 등이 있습니다.

  • Neighborhood-based Methods

    노드의 임베딩을 학습하기 위해 노드의 이웃 노드들의 특징을 이용하는 방법. 이웃 노드들의 특징을 평균 내거나, 가중 평균을 취하는 등의 방법으로 노드의 임베딩을 계산한다. 예를 들어, GCN(Graph Convolutional Network)은 이웃 노드들의 특징과 가중치를 합해서 노드의 임베딩을 계산한다.

  • Random Walk-based Methods

    노드의 임베딩을 학습하기 위해 무작위로 그래프를 탐색하는 방법입니다. 무작위로 그래프를 탐색하면서 노드의 이웃 노드들과의 유사도를 계산하고, 이를 이용해 노드의 임베딩을 학습합니다. 예를 들어, DeepWalk는 무작위로 그래프를 탐색하면서 각 노드와 인접한 노드들의 시퀀스를 생성하고, 이를 이용해 Word2Vec과 유사한 방법으로 노드의 임베딩을 학습합니다.

  • Spectral-based Methods

    노드의 임베딩을 학습하기 위해 그래프의 라플라시안 행렬을 이용하는 방법입니다. 그래프의 라플라시안 행렬을 이용하면 그래프의 고유벡터를 계산할 수 있으며, 이를 이용해 노드의 임베딩을 계산합니다. 예를 들어, GraphSAGE는 그래프의 라플라시안 행렬을 이용해 노드의 임베딩을 계산합니다.

  • Attention-based Methods

    노드의 임베딩을 학습하기 위해 노드 간의 상호작용을 모델링하는 방법입니다. 이 방법은 노드 간의 유사도를 계산하고, 이를 이용해 노드의 임베딩을 계산합니다. 예를 들어, GAT(Graph Attention Network)은 이웃 노드들과의 상호작용을 계산하는데, 이를 위해 어텐션 메커니즘을 사용합니다

메시지 기반 노드 임베딩

3
  • A 노드에 특징을 업데이트 하기 위해서 자신과 연결된 이웃 노드들의 특징을 집계하여 자신의 특징을 업데이트 하는 방식으로 1번 연결된 이웃에 대한 특징만을 집계하는것으로 인공지능 One-layer와 유사하다.
4
  • 위와 같은 방식에서 A와 이웃된 노드의 또 다른 이웃들의 정보를 기준으로 레이어를 증가시킬 수 있다.

https://github.com/pyg-team/pytorch_geometric

데이터 셋

  1. Cora: 컴퓨터 과학 논문 데이터셋으로, 논문을 노드로, 각 논문의 키워드를 feature로 갖고 있다.
  2. Citeseer: Cora와 비슷한 데이터셋으로, 컴퓨터 과학 분야의 논문을 노드로, 논문의 키워드를 feature로 갖고 있다.
  3. Pubmed: 의학 분야의 논문 데이터셋으로, 논문을 노드로, 논문의 키워드를 feature로 갖고 있다.
  4. Reddit: 소셜 미디어 사이트인 Reddit의 서브레딧 데이터셋으로, 각 포스트를 노드로, 사용자가 작성한 텍스트를 feature로 갖고 있다.
  5. PPI (Protein-Protein Interaction): 단백질 상호작용 데이터셋으로, 각 단백질을 노드로, 단백질의 특징을 feature로 갖고 있다.

PyTorch Geometric에서는 TUDataset, Planetoid, Coauthor 등 다양한 데이터셋을 제공하고 있고, DGL에서도 Reddit, Amazon 등 대표적인 GNN 데이터셋을 제공한다.

라이브러리

Python에서 GNN 모델 설계를 지원하는 다양한 라이브러리들이 존재한다.

  1. PyTorch Geometric: PyTorch Geometric는 PyTorch 기반의 GNN 라이브러리로 대규모 그래프에서 GNN을 학습시키기 위한 다양한 유틸리티 함수와 함께 다양한 GNN 모델을 구현할 수 있다.
  2. Deep Graph Library (DGL): DGL은 MXNet, PyTorch 및 TensorFlow에서 GNN 모델을 구현하고 학습시키기 위한 라이브러리. 여러 가지 그래프 레이아웃과 함께 다양한 GNN 모델과 레이어를 구현할 수 있다.
  3. Spektral: Spektral은 Keras 기반의 GNN 라이브러리로 다양한 GNN 모델을 쉽게 구현하고 학습시킬 수 있다.
  4. NetworkX: NetworkX는 Python에서 네트워크 분석을 위한 라이브러리입니다. NetworkX를 사용하여 GNN 모델을 구현하고 학습시킬 수 있다.
GCN(Graph Convolution Network)
1 2 3 4

H → 특징 행렬의 Coulmn을 통과

5 6
  • A = 인접 행렬
  • H = 특징 행렬
  • W = 가중치 행렬

HW (특징 행렬 X 가중치 행렬)

  • 현재 예제에서는 가중치 행렬의 필터 갯수를 6개로 했지만 보통 16인듯
7

AHW (인접행렬 X 특징 행렬 X 가중치 행렬)

8

READOUT - Permutation Invariance

  • 1번 그래프와 2번 그래프는 같은 그래프이지만 특징 행렬의 표현이 달라짐
  • 그래프는 순서의 상관없이 같은 값이 나와야 함
9

마지막 H(특징 행렬)에 MLP를 적용해서 벡터로 만들어서 활성화 함수를 통과

Overall

X → H(특징 행렬)

A → A(인접 행렬)

10 ### **Cora Dataset을 이용하여 GCN 논문 실습(Ensigner)**

https://www.youtube.com/watch?v=JfBMCFVEuoM

1. Graph Convolution Networks(GCN)

  • SEMI-SUPERVISED CLASSIFICATION WITH RAPH CONVOLUTIONAL NETWORKS

    1609.02907.pdf

ICLR 2017년에 나온 GCN에 대해 자세하게 다룬 첫 논문

2. Cora dataset

  • Nodes = publications(책, 또는 논문)
    • 2708개의 publications(책, 논문)들이 있으며 7개의 라벨링이 되어 있음
  • Edges = Citations(인용)
    • 각 논문당 인용에 대한 5429의 링크들이 존재
  • Node Features = Word vectors
    • 하나의 노드에는 1433사이즈의 유니크한 words가 있다.

구성

Cora dataset은 2개의 파일로 구성되어 있다.

  1. Cora.content
11
  • Paper id : 논문의 고유 ID(논문의 이름이 아님)
  • Word attributes : (특징 벡터) 각 논문의 특징을 나타내는 키워드 값을 벡터화
  • Class label : 각 논문이 어떤 주제인지 알 수 있음
  1. Cora.cites

    12
  • ID of cited paper : 인용된 논문

  • ID of citing paper : 인용하는 논문

    —# 오른쪽 논문이 왼쪽 논문을 인용 1033 논문이 35논문을 인용

3. 코드 실습

데이터 불러오기

  • 인접 행렬
    • sparse 행렬(서로서로 연결된 비율이 매우 낮음)
    • 약 13,264개가 Not nonzero, 행렬의 사이즈는 2,709 X 2,709이다.
    • 약 0.18퍼센트만이 데이터에 값이 있음
  • 특징 벡터
    • 각 노드에 해당하는 워드가 임베딩 되어 벡터 형태로 저장
  • 레이블
    • 7개의 레이블을 클래스 번호로 레이블링(카테고리화)
  • 학습 데이터
    • 0 ~ 139(140)개 데이터를 사용
  • 검증 데이터
    • 200 ~ 499(300)개 데이터를 사용
  • 테스트 데이터
    • 500 ~ 1400(1000)개를 사용

통상적으로 학습 : 7 검증 : 1 테스트 : 2 이지만 각자에 맞게 설정

Model(Pytorch)

  1. Model 정의
  • GraphConvolution
from torch import nn
from torch.nn.parameter import Parameter
import torch
import torch.nn.functional as F
import math

class GraphConvolution(nn.Module):
    def __init__(self, feature_numbers, out_numbers, bias=False) -> None:
        super().__init__()
        self.bias = bias
        self.w = Parameter(torch.FloatTensor(feature_numbers, out_numbers))
        if bias:
            self.b = Parameter(torch.FloatTensor(out_numbers))
        self.reset_parameters()
        
    def reset_parameters(self):
        stdv = 1. / math.sqrt(self.w.size(1))
        self.w.data.uniform_(-stdv, stdv)
        if self.bias is not False:
            self.b.data.uniform_(-stdv, stdv)
            
    def forward(self, x, adj):
		#-- input data 
		#-- X = 특징 행렬
		#-- adj = 인접 행렬(sparse matrix)
        support = torch.mm(x, self.w)
        out = torch.spmm(adj, support)
		#-- spmm(sparse mulple : sparse행렬 연산시 적은 메모리 사용 효율적으로 처리)
        if self.bias:
            out = out + self.b

        return out
  • NodeClassificationGCNN
class NodeClassificationGCNN(nn.Module):

    def __init__(self, feature_num, node_representation_dim, nclass, droupout=0.2, bias=False) -> None:
        super().__init__()
#-- 특징 행렬의 Column, Demention(CNN에서의 채널 수), output_dim
        self.gconv1 = GraphConvolution(feature_num, node_representation_dim, bias)
#-- 이전 레이어에서 나온 output, 최종 output class(7)
        self.gconv2 = GraphConvolution(node_representation_dim, nclass, bias)
        self.dropout = droupout

    def forward(self, x, adj):
        x = F.relu(self.gconv1(x, adj))
        x = F.dropout(x, self.dropout, self.training)
        x = F.relu(self.gconv2(x, adj))
        return F.log_softmax(x, dim=1)
#-- log를 이용하여 -무한대 값을 출력 가능
  1. Model 실행
from GCNN import NodeClassificationGCNN

#-- 특징벡터의 column개수, 256(CNN에서의 필터의 개수), 클래수 개수)
model = NodeClassificationGCNN(features.shape[1], 256, np.max(labels.detach().numpy())+1)
  • 학습
import torch.optim as optim
import torch.nn.functional as F

epochs=100
optimizer = optim.Adam(model.parameters(),lr=0.01)
train_losses=[]
val_losses=[]
train_accuracy=[]
val_accuracy=[]
for epoch in range(epochs):
    model.train()
    train_labels=labels[idx_train]
    val_labels=labels[idx_val]
    
    
    optimizer.zero_grad()
    output = model(features, adj)
    train_loss=F.nll_loss(output[idx_train],train_labels)
    train_losses.append(train_loss)
    t_a=accuracy(output[idx_train],train_labels)
    train_accuracy.append(t_a)
    print(f"Training epoch {epoch} ; accuracy: {accuracy(output[idx_train],train_labels)}; loss: {train_loss.item()}")
    train_loss.backward()
    optimizer.step()
    
    model.eval()
    output = model(features, adj)
    val_loss=F.nll_loss(output[idx_val],val_labels)
    val_losses.append(val_loss)
    v_a=accuracy(output[idx_val],val_labels)
    val_accuracy.append(v_a)
    print(f"Validation epoch {epoch} ; accuracy: {accuracy(output[idx_val],val_labels)}; loss: {val_loss.item()}")
LightGCN

LightGCNCollaborative Filtering (CF)에서의 **User-Item Interaction Matrix(**사용자-아이템 상호작용 매트릭스)를 활용한 추천 시스템에서 사용되는 모델 중 하나이다.

LightGCN은 그래프 신경망 모델 중 가장 간단하면서도 성능이 우수한 모델 중 하나이다. 모델 구조는 사용자와 아이템 간의 상호작용을 표현하는 유저-아이템 행렬을 이용하여 그래프를 만든다. 이때 그래프의 각 노드는 유저와 아이템을 나타내며, 각 엣지는 유저와 아이템 간의 상호작용을 나타냅니다.

LightGCN은 다른 CF(협업 필터링) 모델과 달리, User와 Item간의 interaction(상호작용) 매트릭스를 이용하여 User와 Item을 Embedding시키는 작업만을 수행한다. 즉, User-Item 간의 side-information이나 Auxiliary feature를 사용하지 않는다.

LightGCN에서 사용되는 GCN은 message passing과정에서 User와 Item 간의 interaction이 Embedding 공간에서 얼마나 가까운지를 계산하는데에만 사용된다 이를 위해 User와 Item 간의 interaction matrix를 정규화하고, GCN의 weight를 1로 고정하는 등의 가벼운 방식을 적용하여 모델의 학습 속도를 높인다.

LightGCN의 장점

  • 사용할 Auxiliary feature가 없어 매우 가볍고 단순하며 빠른 속도를 보여줌.
  • SOTA(CF모델)보다 높은 성능을 보여줌

LightGCN의 단점

  • cold-start problem을 해결하지 못함
  • 과적합될 가능성 존재
  • 데이터가 매우 sparse한 경우 학습 성능이 떨어짐

코드 구현

  • LightGCN 논문 요약

    넷플릭스, 트위터, 스포티파이 같은 디지털 서비스 뒤에는 사용자의 관심사를 예측하고 구매, 시청, 읽기에 영향을 주는 추천 시스템이 있다. 이 글에서는 PyTorch와 PyG로 구현된 그래프 기반 협업 필터링 추천 시스템 모델인 LightGCN에 대해 살펴본다.

    LightGCN with PyTorch Geometric

목표 : 사용자가 아직 평점을 매기지 않은 영화를 추천

  • 노드 : 사용자, 영화
  • 엣지 : 사용자와 영화 간의 상호작용(평점

데이터 전처리

  • 사용자가 좋아할 만한 영화만 추천하고 싶기 때문에, 0.5 ~ 5점 척도였던 평점(엣지)를 4점 이상으로만 포함하는 방식으로 데이터를 전처리

훈련 목표(Task)

  • 두 노드 사이에 유효한 엣지가 존재하는지 여부를 예측하는 엣지 예측 작업
  • 이를 위해 그래프 구조에서 사용자와 영화 노드 간의 구분이 없는 동질 그래프로 설정
  • 그래프가 사용자와 영화 사이에만 에지가 존재하는 이분형 그래프
  • 사용자와 영화를 동일한 유형의 노드로 취급할 수 있음.
  • 동종 그래프 표현의 인접 행렬은 희소 행렬
    • 희소 행렬 : 인접 행렬의 요소 대부분이 0
5주차 GNN(NGCF)

Collaborative Filtering(협업 필터링)

Collaborative Filtering(협업 필터링)
Collaborative Filtering

Collaborative Filtering(협업 필터링)

다른 사용자의 구매 이력, 평가 등을 바탕으로 다른 사용자와의 유사도를 측정하여 사용자가 좋아할만한 상품을 추천하는 기술이다.

ex) A,B 두 명의 사용자가 있다고 가정

  • A : 햄버거를 좋아함
  • B : A와 B의 음식 취향이 비슷함
  • B 에게 A가 좋아하는 햄버거를 추천

Collaborative Filtering 종류

  • Neighborhood Method
    • 유저와 아이템의 데이터를 연관하여 추천하기 때문에 쉽다.
    • 복잡한 계산 과정이 필요하여 확장이 힘들다.
  • Latent Factor Model
    • 기본적으로 사용
    • Matrix Factorization
      • Netflix Prize 논문에서 사용

Matrix Factorization

유저와 아이템간의 상호작용에는 유저의 행동, 아이템 평점에 영향을 주는 **잠재된 요인(Latent factor)**가 있고, 그 잠재된 요인을 고려하여 유저에게 적합한 아이템을 추천하는 방법

큰 행렬을 작은 두 개의 행렬로 분해하는 기법으로 데이터의 잠재적인 구조를 추출할 수 있다. 예를 들어, 사용자-아이템 행렬을 분해하면 사용자와 아이템 간의 특성을 추출할 수 있다. 이렇게 추출된 특성을 사용하여 사용자와 아이템을 새로운 공간에서 표현하면, 이를 활용하여 사용자에게 아직 구매하지 않은 상품을 추천할 수 있다.

$유저-아이템 행렬 = 유저잠재요인 * 아이템잠재요인$

1 2

하지만 Matrix Factorization의 경우 내적을 사용하기 때문에 복잡한 유저와 아이템의 관계를 1차원 레이어에 담아내기 어렵다는 문제점이 발생

Neural Collaborative Filtering

Neural Collaborative Filtering

Deep learning 모델을 이용하여 추천 시스템을 구현하는 방법이다.

NCF 모델은 사용자와 아이템 간의 상호작용 데이터를 입력으로 받아, 사용자와 아이템 간의 상호작용 예측을 위해 neural network를 사용한다. 일반적으로 NCF 모델은 사용자와 아이템을 표현하는 임베딩(Embedding) 층과, 이를 조합하여 예측하는 fully connected 레이어들로 이루어져 있다.

NCF 모델은 deep learning을 사용하기 때문에, 기존의 MF 방법에 비해 더 복잡한 패턴을 추출할 수 있다. 또한, 큰 규모의 데이터셋에 대한 적응력도 높습니다.

하지만, NCF 모델의 경우 학습 시간이 오래 걸리며,, MF 방법보다 파라미터 수가 더 많기 때문에, 적은 데이터셋에서는 과적합 문제가 발생할 가능성이 높습니다.

3

  • 사용자 서브넷
  • 아이템 서브넷

각 서브넷은 사용자 및 아이템에 대한 정보를 입력받아 잠재요인(Latent factor)을 추출하는 역할은 하며, 두 개의 서브넷은 MLP로 구성되어 있다. MLP는 잠재요인을 특징으로 표현하는데 사용되며 이 특징은 각 사용자와 아이템 간의 상호작용을 예측하기 위해 내적으로 결합된다.

정리하자면 NCF는 사용자와 아이템의 특징 벡터를 입력으로 받고, 이 를 MLP에서 잠재요인으로 변환하고, 잠재요인 벡터를 내적하여 사용자와 아이템 간의 상호작용을 예측하는 것이다.

Generalized MF(GMF)

GMF는 Matrix Factorization (MF) 모델의 변형으로 MF 모델과는 달리 사용자와 아이템의 벡터를 요소별 곱셈으로 구한다.

내적은 요소별 곱셈으로 표현될 수 있어 GMF는 이러한 특성을 사용하여 벡터 내적 대신 요소별 곱셈을 사용하여 적은 수의 파라미터로도 MF보다 더 높은 성능을 발휘할 수 있다.

4

Neural Graph Collaborative Filtering

NGCF(Neural Graph Collaborative Filtering)는 그래프 신경망(Graph Neural Network)을 기반으로한 협업 필터링 알고리즘으로 그래프 신경망은 노드 간의 상호작용 관계를 모델링할 수 있는 강력한 모델링 도구로, 이를 활용하여 사용자와 아이템 간의 상호작용 정보를 모델링하여 추천을 한다. 잠재 요인 간의 상호작용을 고려하지 않고, 단순히 내적을 사용하여 예측을 수행하는 기존 모델들과 달리 잠재 요인 간의 상호작용을 고려하여 더 나은 성능을 보인다.

1

High-order Connectivity for u1

경로 길이가 1보다 큰 노드에서 u1에 도달하는 경로를 의미하며 이를 통해 3가지를 알 수 있음.

  1. u1과 u2가 i2와 상호작용함으로 두 유저간의 행동 유사성이 존재
  2. u1과 유사한 u2가 i4를 소비함 → u1이 i4를 소비할 가능성이 존재
  3. u1은 i5보다 i4에 더 큰 관심이 있을 가능성이 존재
    1. u1은 u2와 i2와 상호작용함으로 연결되어 있으며 u2은 i4와 i5를 소비
    2. u1과 u3은 i3에 상호작용함으로 연결되어 있으며 u3은 i4를 소비

NGCF 특징

  1. 그래프 신경망을 활용하여 사용자와 아이템 간의 상호작용 정보를 모델링
  2. 메모리 기반 협업 필터링 알고리즘보다 훨씬 빠르고 효율적으로 추천
  3. 주변 이웃의 상호작용 정보를 잘 고려하여 정확한 추천
  4. 다양한 협업 필터링 알고리즘과 비교하여 높은 성능

NGCF 구조

2

  1. Embedding Layer
  • 유저와 아이템에 대해 임베딩 하는 레이어
  • end - to end 방식으로 학습
  1. Embedding Propagation Layers
  • 논문에서 제안하는 중요한 레이어이며 기존 논문들과 차별되는 부분
  • 유저와 아이템의 표현을 관련 짓기 위해 주변 유저와 아이템의 1차 연결 정보를 명시적으로 활용
  • 그래프 구조를 따라 협업 시그널을 캡쳐
  • 유저 및 아이템 임베딩을 개선하기 위해 GNN 메시지 전달 아키텍쳐 구축

Embedding Propagation Layers

연결된 유저-아이템 간의 임베딩 전파를 수행하며, Message Construction, Message Aggregation 두 가지 주요작업을 진행

  1. 유저가 아이템을 소비
  2. 소비된 아이템 정보가 유저에게 전달
  3. 특정 유저가 아이템을 소비한 정보는 아이템 feature로 사용 가능
  4. 위 feature를 토대로 아이템또는 유저의 유사성을 측정

Message Construction

  • 아이템 정보가 유저에게 전달되는 메시지를 구성
  • 각각의 초기 Embedding 벡터의 값은 일반적으로 무작위로 초기화

ex) 사용자 A와 아이템 B가 연결된 그래프가 있다고 가정

  1. 먼저, 사용자 A와 아이템 B는 각각의 Embedding 벡터를 가지고 있다. 이 Embedding 벡터는 사용자 A와 아이템 B의 속성 정보를 압축하여 나타낸 벡터
  2. 사용자 A는 연결된 아이템 B와의 관계 정보를 담은 Embedding 벡터를 생성하고, 이Embedding 벡터는 아이템 B의 Embedding 벡터를 사용하여 생성
  3. 아이템 B는 연결된 사용자 A와의 관계 정보를 담은 Embedding 벡터를 생성. 이 Embedding 벡터는 사용자 A의 Embedding 벡터를 사용하여 생성
  4. 마지막으로, 생성된 두 개의 Embedding 벡터를 결합하여 사용자 A와 아이템 B 사이의 메시지를 생성

이러한 방식으로 각각의 노드들이 서로 메시지를 생성하고 전파하면서 그래프 전체의 정보를 반영하여 모델을 학습

Message Aggregation

  • 유저의 표현 또는 아이템의 표현들을 집계
  • 생성된 메시지를 모아서 새로운 임베딩을 생성
  • 메시지를 모으는 과정에서 이웃 노드 간의 정보를 공유
  • 합치거나 평균을 내는 방식

ex) 노드 u1과 이웃 노드 u2, u3, u4사이의 메시지를 모으는 과정에서 u2, u3,u4간에도 정보가 공유된다.

3

이 과정에서 그래프의 연결 정도를 나타내는 라플라스 매트릭스로 Propagation Rule를 구현하면 모든 유저와 아이템에 대한 표현을 효율적으로 동시에 업데이트 가능

4

5

3. Prediction Layer

  • 서로 다른 에이어에서 나온 표현은 각기 다른 메시지를 강조
  • 유저 선호도 반영에 서로 다른 기여를 하게 됨
  • 따라서 이 정보들을 모두 합쳐서 유저를 위한 최종 임베딩 구성
  • 일반적으로 활성화 함수를 사용 안함
  • 내적을 사용
    • 사용자의 벡터와 아이템의 벡터가 비슷할수록(상호작용이 많을수록) 예측 점수가 높아짐
    • 각각의 아이템에 대한 예측점수

4**. Out Layer**

  • 내적을 사용
    • Prediction Layer에서 나온 예측 점수로 사용자와 아이템간의 상호작용 점수를 계산
    • 아이템 중에서 가장 선호할 만한 아이템을 최종적으로 추천
  • 유저는 선호도가 가장 높은 아이템을 추천 받음

공개된 데이터를 가지고 제안한 알고리즘을 조합해

6주차 (Meta-Learning의 특성과 원리 이해)

논문 자료(Deep Meta-learning in Recommendation Systems : A Servey)를 참고하여 정리하였습니다.

심층 신경망 기반 추천 시스템은 최근 몇 년 동안 성공을 거두었지만, 여전히 충분한 데이터 부족과 계산 비효율성 등의 문제에 직면해 있다. 메타러닝은 알고리즘의 학습 효율성과 일반화 능력을 향상시키는 새로운 접근 방식으로, 추천 시스템의 데이터 희소성 문제를 해결할 수 있는 가능성을 보여주었다. 사용자 및 아이템 Cold start 와 같이 데이터가 제한된 시나리오에서 딥 메타러닝을 사용하여 성능을 개선하는 많은 연구가 발표되었다. 이 논문에서는 딥 메타러닝 기반 추천 방법을 추천 시나리오, 메타러닝 기법, 메타지식 표현으로 분류하여 개요를 제공한다. 기존 방법들이 메타러닝을 활용해 추천 모델의 일반화 능력을 어떻게 향상시켰는지 살펴보고, 한계점에 대해서 알아본다.

0.메타러닝

정의

  • 떠오르는 학습 방법이며 적은 데이터를 효율적으로 학습(퓨샷 러닝)
  • ‘메타’라는 단어는 한 차원 위의 개념적 용어로 대상의 전반적인 특성을 반영
  • 데이터의 패턴을 정해진 프로세스로 학습하는 것이 아닌 데이터의 특성에 맞춰서 모델 네트워크의 구조를 변화시키면서 학습
  • 배우는 방법을 배우는 것 (Learning to learn)

ex) 하이퍼파라미터 최적화, 자동 신경망 네트워크 설계 등등..

메타러닝은 데이터와 연산이 부족한 상황에서 모델의 일반화 능력을 향상시키는 데 중점을 둔 머신 러닝의 한 유형이다. 여러 작업에 대한 이전 학습 과정에서 사전 지식을 확보하여 새로운 작업에 대한 빠른 학습을 돕고 보이지 않는 작업에 대해 우수한 일반화 성능을 발휘할 수 있다. 메타러닝은 고정된 학습 알고리즘을 대체할 수 있는 더 나은 학습 전략을 학습하는 데 초점을 맞추기 때문에 딥러닝 모델의 표현 학습 능력을 향상시키는 것과는 차이점이 있다. 보이지 않는 작업에 빠르게 적응할 수 있는 잠재력이 커서 이미지 인식, 자연어 처리, 강화 학습 등 다양한 연구 영역에 적용되고 있다.

뿐만 아니라 제한된 인스터스와 비효율적인 연산으로 어려움을 겪고 있는 추천 시스템 등 다양한 연구 영역에 적용되고 있다. 고급 메타러닝 기법은 우선 기존 딥 추천 모델을 학습할 때 데이터 부족을 해소 하기 위해 적용된다. 예를 들면 최적화 기반 메타 학습 프레임워크인 MAML은 콜트 스타트 추천 시나리오에서 큰 효과를 보여준다.

이 논문에서는 이 분야의 연구를 정리하기 위해 세 가지 관점으로 분류법을 제시하고 메타러닝 기술을 활용하여 다양한 형태의 메타 지식을 추출하는 다양한 방법을 논의

1번 섹션에서는 메타러닝 기법의 기본 사항과 메타러닝 기법이 적용되는 일반적인 추천 시나리오를 다룬다. 2번 섹션은 세 가지 관점의 분류법을 제시하고, 섹션 3에서는 메타러닝 추천 작업을 구성하는 다양한 방법을 요약하며, 섹션 4에서는 메타러닝 기법을 적용한 기존 방법의 방법론적 세부 사항을 자세히 설명한다.

1. 메타러닝 기법의 기본 사항

이 섹션에서는 딥 메타러닝 기반 추천 방법을 논의하는데 필요한 기초적인 이해를 제공하며 메타러닝의 개념을 소개하고 기존 머신 러닝과 대조한다. 이후 메트릭 기반, 모델 기반, 최적화 기반 기법 등 메타러닝 기법의 세 가지 주요 흐름과 몇 가지 대표작을 간락하게 소개한다. 마지막으로는 메타러닝 기법이 연구되고 적용된 대표적인 추천 시나리오도 소개한다.

1.1 Meta-Learning

메타 학습은 기본 학습 모델의 일반화 능력을 향상시켜 새로운 작업을 더 효율적으로 학습할 수 있도록 하는 프로세스이다. 이는 여러 작업에 대한 사전 지식, 즉 메타 지식을 학습함으로 이루어진다. 서로 다른 작업의 학습 과정은 메타 학습 방법으로 관찰된 훈련 인스턴스로 취급되며, 메타 지식의 형태를 추출하고 정의하여 새로운 작업을 보다 효과적으로 학습할 수 있도록 한다.

훈련 단계에서 훈련 데이터는 작업 배포에서 샘플링되며 메타 훈련 데이터세트로 표신된다. 각 작업은 지원 집합으로 표시되는 자체 훈련 인스턴스와 쿼리 집합으로 표시되는 평가 인스턴스로 구성된다. 작업에 대한 매핑 함수의 작업별 파라미터는 경험적 손실 함수를 최소화하여 얻고 학습된 매핑 함수는 작업의 동일한 분포에서 샘플링된 평가 인스턴스 세트에 대해 평가된다.

메타학습의 목표는 모든 작업에 대한 작업별 학습이 더 나은 성과를 낼 수 있도록 안내할 수 있는 최적의 파라메터를 학습하는 것이다. 메타 학습 방법의 학습 목표는 각 직업의 해당 쿼리 세트에 대해 더 나은 성능을 관찰하는 것이며, 메타 지식은 여러 작업에 걸쳐 학습되어 작업 간 특성을 파악하고 작업 차이에 대한 뛰어난 일반화 능력을 갖는다.

메타러닝 패러다임은 크게 3가지 점에서 기존 머신 러닝과 다르다.

  1. 기존 머신 러닝은 특정 작업의 성능을 개선하는 데 중점을 두는 반면, 메타 러닝은 새로운 작업을 학습하는 방법을 배우는 데 중점.
  2. 메타 학습에서는 여러 개의 작은 작업을 사용하여 더 큰 작업에 적용할 수 있는 지식을 추출.
  3. 메타 학습 과정에는 두 가지 수준의 학습이 포함되는데, 내부 수준에서는 작업 별 정보를 학습하고 여러 인스턴스에 걸쳐 학습하고 일반화 가능한 지식을 추출하는 외부 수준용 학습 인스턴스를 생성.

1.2 Meta-Learning 기술의 주요 기법

퓨샷러닝(Few Shot Learning)

퓨샷 러닝에서의 데이터셋은 크게 두 가지로 나뉜다.

  1. 서포트 셋(Support Set)
    1. 데이터 셋을 학습에 사용하는 데이터
  2. 쿼리 셋 (Query Set)
    1. 데이터 셋을 테스트에 사용하는 데이터

이러한 퓨샷 러닝 태스크를 “N-way K-shot”이라고 하며 여기서 n은 카테고리의 개수를 뜻하고 k는 카테코리당 이미지의 수를 의미한다. 일반적으로 성능은 N에 반비례하며 k에 비례한다.

—# 예를 들어 개, 고양이, 말 3개의 카테리고가 있고 각각 4장씩 이미지가 존재한다면

→ 3-way 4-shot이다.

메타 학습 기법에는 대표적으로 세 가지가 존재하며 각 기법에 대한 장단점을 비교한다.

  • 거리 학습 기반 : 효율적 거리 측정 학습
  • 모델 기반 학습 : 메모리를 이용한 순환 신경망
  • 최적화 학습 : 모델 파라미터 최적화

거리 학습 기반(Metric Based Learning)

메타 러닝의 방법론 중 하나인 거리 학습 기반은 서포트 셋과 쿼리 셋 간의 거리(유사도)를 측정하는 방식으로 대신한다. 대표적인 알고리즘은 샴 네트워크(Siamese Neural Network)가 있다.

모델은 주어진 서포트 데이터를 특징 공간에 나타내서 특징을 뽑아내며 같은 클래스면 거리를 가깝게 다른 클래스면 멀게 하는 방식으로 데이터를 분류한다. 쿼리 데이터를 유클리디안 거리가 가까운 서포트 데이터의 클래스로 예측하는 방식이다.

계산 부담이 적지만 작업 분배가 복잡할 경우 성능이 불안정

모델 기반 학습 방식(Model based learning)

모델 기반 학습 방식은 적은 수의 학습 단계로도 모델의 파라미터를 효율적으로 학습할 수 있는 방식이다.

모델에 별도의 메모리를 두어 학습 속도를 조절한다. 대표적인 알고리즘으로는 MANN가 있다.

과거 데이터를 외부 메모리에 저장함으로 효율적으로 문제를 해결하는 방법을 습득하며 새로운 정보를 빠르게 인코딩하고 몇 개의 샘플만 가지고도 새로운 태스크에 적용할 수 있도록 설계되었다.

최적화 단계가 간단하고 적용 범위가 넓지만, 분산되지 않은 작업에 대해서는 성능이 떨어짐

최적화 학습 방식(Optimizer learning)

Few Shot task를 파라미터 최적화 문제로 생각한다.

일반적으로 딥러닝 모델은 기울기의 역전파를 통해 학습을 진행하지만 기울기 기반 최적화 기법은 큰 스케일의 데이터를 위해 설계가 되었기 때문에 적은 수의 샘플에 대한 최적화 기법을 다룬다. 대표적인 알고리즘으로는 MAML알고리즘이 있다.

기본 모델 구조에 구애받지 않으며 복잡한 작업 분포에서 더 나은 일반화 능력을 보이지만 계산량이 많다는 단점.

1.3 추천시스템 시나리오

추천 시스템과 관련하여 메타러닝이 연구된 다양한 시나리오가 있다. 이런 시나리오에는 콜드 스타트 추천, 클릭률 예측, 온라인 추천, 관심 지점 추천, 순차적 추천이 포함된다. 이외에도 메타 학습이 연구된 다른 추천 시나리오도 있다.

Cold Start

콜드 스타트 추천은 새로운 사용자나 아이템이 등장할 때 흔히 발생하는 사용자-아이템 상호 작용 데이터가 제한적인 경우 추천 시스템에서 어려움을 겪게 된다. 기존의 협업 필터링 방식이나 딥러닝 방식은 풍부한 학습 데이터가 필요하기 때문에 이러한 상황에서는 성능이 좋지 않을 수 있다. 콘텐츠 기반 방식은 다양한 콘텐츠 정보를 사용하여 추가적인 의미 정보를 통해 사용자와 항목의 표현을 향상시키므로 상호작용 데이터에 대한 요구가 약하된다. 콜드 스타트 추천은 소수 학습에서 응용할 수 있으며, 데이터 부족을 완화하기 위해 메타 학습 기법을 활용한다.

클릭률 예측

클릭률(CTR) 예측은 게시된 광고의 가치를 결정하는 데 도움이 되므로 온라인 광고에서 중요하다. CTR예측 모델은 광고/사용자 ID 및 기타 기능에 대한 잠재벡터를 학습하는 임베딩 레이어와 정교한 모델과 기능 상호 작용을 모델링하는 예측 레이어로 구성된다. 그러나 이러한 모델은 클릭 기록이 제한적인 신규 광고에서는 콜드 스타트 문제로 인해 성능이 저하된다. 콜드 스타트 광고에 대한 임베딩 학습을 강화하기 위해 메타 학습 방법이 연구되고 있다.

온라인 추천

온라인 추천은 대규모 추천 시스템에서 과거의 기록에만 의존하지 않고 실시간 사용자 상호작용 데이터를 기반으로 추천 모델을 업데이트하는 데 사용되는 기술이다. 이 접근 방식을 사용하면 동적인 사용자 선호도와 추세를 파악할 수 있다. 하지만 효율적인 계산 방법이 필요하다. 온라인 추천 시나리오에서 실시간 데이터로부터 트렌드를 빠르게 포착하기 위해 메타러닝이 도입된다.

관심 지점 추천

관심 지점 추천은 사용자의 체크인 기록을 기반으로 개인화된 위치 추천을 제공하는 추천 시스템의 한 유형이다. 이러한 유형의 추천은 과거 체크인 데이터에서 공간적-시간적 종속성을 발견하는 데 크게 의존한다. 하지만 이 시나리오에서는 사용자가 소수의 POI만 방문하는 경향이 있기 때문에 데이터 희소성이 문제가 된다. 이를 해결하기 위해 메타러닝 기반 POI 추천 방법이 연구되고 있다.

—# POI는 관심 있는 사항을 나타내는 지도의 특정 지점

순차 추천

순차 추천은 사용자의 상호 작용 순서를 사용하여 진화하는 선호도를 파악하는 추천 시스템의 한 유형이다. 이 방법은 사용자 관심사 진화의 순차적 패턴을 발견하는 데 중점을 두며, 마르코프 체인, 순환 신경망, 자기 관심 기반 네트워크와 같은 방법을 사용하여 좋은 결과를 얻었다. 하지만 순차 추천의 성능은 시퀀스의 충분한 항목에 의존하며, 수가 적을 경우 성능이 크게 저하될 수 있다. 딥 메타러닝 기반 추천 시스템의 분류 체계는 아래 그림과 같이 추천 시나리오, 메타러닝 기술, 메타 지식 표현이라는 세 가지 독립적은 축을 기반으로 구축된다.

1
2. 분류

이 섹션에서는 딥 메타러닝 기법을 사용하는 추천 시스템을 위한 분류 체계를 작성하며 분류 체계를 기반으로 기존 방법의 주요 특징을 요약한다. 추천 시스템의 분류 체계는 추천 시나리오, 메타러닝 기술, 메타지식표현의 세 가지 축으로 정의된다. 다른 분류법도 존재하지만 추천 시스템에 초점을 맞추지는 않았다.

  1. 추천 시나리오(어디서)

    추천 시나리오에는 콜드 스타트 추천, 클릭률 예측, 온라인 추천, 관심 지점 추천, 순차적 추천이 포함된다.

  2. 메타 학습 기법(어떻게)

    메타 학습 기법이란 추천 모델이 새로운 업무에 일반화할 수 있는 능력을 향상시키기 위해 메타 학습을 적용하는 방식이다. 메타 학습 기법에는 거리(메트릭)기반, 모델 기반, 최적화 기반 세 가지가 존재한다.

  3. 메타 지식 표현(무엇을)

    메타 지식 표현 축은 추천 모델의 빠른 학습을 개선하는 데 사용할 수 있는 지식의 형태를 나타낸다. 메타 지식의 일반적인 유형에는 파라미터 초기화, 파라미터 변조, 하이퍼파라미터, 샘플 가중치, 임베딩 공간, 메타 모델 등이 있다. 메타 학습 기술마다 메타 지식 표현에 뚜렷한 특징이 있다. 예를 들어, 파라미터 초기화는 일반적으로 최적화 기반 메타 학습을 통해 이루어지며, 파라미터 변조는 모델 기반 메타 학습에 속할 가능성이 더 높다. 여러 유형의 메타 지식 표현이 하이브리드 방식으로 동시에 학습되는 상황도 존재한다.

3. 추천을 위한 메타 학습 작업

이 섹션에서는 추천 시스템의 맥락에서 메타 학습 작업을 구성하는 방법에 대해 설명한다. 메타 학습 작업을 구성하는 과정은 일반적인 딥러닝 작업과는 다르며, 이 섹션에서는 문헌에서 사용되는 다양한 메타 학습 작업 구성 방법을 소개한다.

메타 학습에는 각각 훈련 인스턴스 세트(support set) 와 평가 인스턴스 세트(query set)로 분리된 메타 학습 및 메타 테스트 작업을 구성하는 것이 포함된다. 목표는 suppot set에서 빠르게 학습하고 query set에서 보이지 않는 인스턴스에 대해 더 나은 성능을 발휘하는 것이다. 단일 작업 수준에서는 학습 목표가 일반적인 딥러닝과 유사하지만 메타 러닝에서는 데이터 불충분성이 강조된다. 더 높은 수준에서는 보이지 않는 작업의 평가 인스턴스에서 더 나은 성능을 발휘하여 여러 새로운 작업에 대한 메타러닝 방법의 일반화 능력과 빠른 학습 능력을 평가하는 것이 목표이다.

추천 시나리오에 메타러닝을 적용할 때는 작업을 구성하는 방식이 중요하며 추천 시나리오의 특정 요구 사항에 맞게 조정해야 한다. 예를 들어, 이미지 인식과 같은 소수의 샷으로 분류하는 작업에서 메타 학습 작업을 구성하는 일반적인 방법은 클래스 풀에서 N개의 클래스를 무작위로 샘플링한 다음 각 클래스에 속하는 K개의 인스턴스를 샘플링 하는 것이다. 자연어 처리 및 추천 시스템을 비롯한 다양한 분야의 메타 학습에서 작업을 구성하기 위한 다양한 설정들이 존재한다. 추천 시스템에서 작업은 주로 콜드 스타트 문제를 해결하기 위해 사용자와 항목을 기반으로 구성된다. 사용자별 태스크는 동일한 사용자에게 속한 인스턴스를 포함하며, 항목별 태스크는 동일한 항목을 기반으로 인스턴스를 그룹화한다. 목표는 support 집합에 대해 모델을 학습시키고 query 집합에 대해 평가하는 것이다.

메타 학습 기반 추천 방법은 작업의 상호작용 속성에 따라 다음과 같이 4가지 범주로 나눌 수 있다.

  1. 사용자별
  2. 항목별
  3. 시간별
  4. 시퀀스별

사용자별

사용자별 작업은 support 및 query 집합을 포함한 작업의 모든 인스턴스가 동일한 사용자에게 속하는 추천 시스템에서 작업을 구성하는 방식이다. 이 접근 방식은 사용자의 제한된 상호 작용으로부터 선호도를 빠르게 학습하는 것을 목표로 하며, 이는 사용자 콜드 스타트 문제를 해결하기 위한 중요한 작업이다. 목표는 support 세트에 대한 모델을 훈련하고 동일한 사용자의 query세트에 있는 상호 작용에 대해 평가하는 것이다. 메타 학습 방법은 충분한 수의 사용자별 작업에서 사용자 선호도 학습에 대한 메타 지식을 추출하여 새로운 사용자의 보이지 않는 작업에 직면했을 때 선호도 학습을 용이하게 한다.

예를 들어, u1이라는 특정 사용자가 있고 이 사용자의 항목과의 상호작용이 지원 집합 S1(일부 항목 평점 포함)과 쿼리 집합 Q1(다른 항목 평점 포함)의 두 집합으로 나뉘는 경우, S1에 대한 모델을 훈련하여 u1에 대한 Q1 예측을 잘 수행하도록 하려고 한다. 이처럼 다양한 사용자와 관련된 여러 유사한 작업에 메타 학습 기법을 사용함으로써 개별 사용자나 항목에 대해 개별적으로 사용할 수 있는 데이터가 많지 않은 경우에도 효율적인 모델을 학습하는 능력을 향상시킬 수 있을 것으로 기대한다.

항목별

항목별 작업에서는 동일한 항목과 관련된 상호작용이 함께 그룹화된다. support set과 query set은 여러 사용자와 동일한 항목간의모든 상호작용을 포함한다. 목표는 support set을 관찰한 후 query set에서 평가 인스턴스의 등급 또는 상호 작용 확률을 예측하는 것이다. 메타 학습을 사용하면 콜드 스타트 항목에 대한 전반적인 선호도를 빠르게 파악하여 정확한 예측과 추천을 할 수 있다.

시간별

추천 시스템의 시간별 작업에는 시간 슬롯에 따라 상호 작용 데이터를 여러 작업으로 분할하는 작업이 포함됩니다. 이러한 작업은 두 개의 연속된 시간 슬롯의 데이터를 사용하여 순차적으로 구성되며, 지원 세트는 현재 시간 슬롯의 데이터로 구성되고 쿼리 세트는 다음 시간 슬롯의 데이터로 구성됩니다. 이러한 작업은 온라인 환경에서 모델을 효율적으로 업데이트하는 것을 목표로 하며, 메타 학습을 사용하여 순차적인 시간별 작업에서 메타 지식을 추출하여 모델 업데이트를 용이하게 할 수 있습니다. 시간별 작업의 상호 작용은 더 이상 사용자나 항목별로 구분되지 않습니다.

시퀀스별

시퀀스별 작업은 서로 다른 사용자 또는 세션의 상호 작용 시퀀스를 별도의 작업으로 취급하는 추천 시스템의 작업 구성 유형입니다. 예를 들어 사용자의 상호작용 시퀀스는 타임스탬프로 정렬된 상호작용 목록으로 표시됩니다. 상호작용 시퀀스는 지원 세트와 쿼리 세트로 나뉘며, 전자의 상호작용은 학습 데이터로 사용되고 후자의 상호작용은 평가에 사용됩니다. 시퀀스별 작업은 익명 세션을 포함할 수 있고 상호작용 시퀀스의 후속 작업을 인스턴스로 사용할 수 있다는 점에서 사용자별 작업과 다릅니다.

7~8주차 (Meta-Learning의 특성과 원리 이해 ProtoCF)

7~8주차 (Meta-Learning의 특성과 원리 이해 ProtoCF)

논문 자료(Deep Meta-learning in Recommendation Systems : A Servey)

심층 신경망 기반 추천 시스템은 최근 몇 년 동안 성공을 거두었지만, 여전히 충분한 데이터 부족과 계산 비효율성 등의 문제에 직면해 있다. 메타러닝은 알고리즘의 학습 효율성과 일반화 능력을 향상시키는 새로운 접근 방식으로, 추천 시스템의 데이터 희소성 문제를 해결할 수 있는 가능성을 보여주었다. 사용자 및 아이템 Cold start 와 같이 데이터가 제한된 시나리오에서 딥 메타러닝을 사용하여 성능을 개선하는 많은 연구가 발표되었다. 이 논문에서는 딥 메타러닝 기반 추천 방법을 추천 시나리오, 메타러닝 기법, 메타지식 표현으로 분류하여 개요를 제공한다. 기존 방법들이 메타러닝을 활용해 추천 모델의 일반화 능력을 어떻게 향상시켰는지 살펴보고, 한계점에 대해서 알아본다.

0. 메타러닝

정의

  • 떠오르는 학습 방법이며 적은 데이터를 효율적으로 학습(퓨샷 러닝)
  • ‘메타’라는 단어는 한 차원 위의 개념적 용어로 대상의 전반적인 특성을 반영
  • 데이터의 패턴을 정해진 프로세스로 학습하는 것이 아닌 데이터의 특성에 맞춰서 모델 네트워크의 구조를 변화시키면서 학습
  • 배우는 방법을 배우는 것 (Learning to learn)

ex) 하이퍼파라미터 최적화, 자동 신경망 네트워크 설계 등등..

메타러닝은 데이터와 연산이 부족한 상황에서 모델의 일반화 능력을 향상시키는 데 중점을 둔 머신 러닝의 한 유형이다. 여러 작업에 대한 이전 학습 과정에서 사전 지식을 확보하여 새로운 작업에 대한 빠른 학습을 돕고 보이지 않는 작업에 대해 우수한 일반화 성능을 발휘할 수 있다. 메타러닝은 고정된 학습 알고리즘을 대체할 수 있는 더 나은 학습 전략을 학습하는 데 초점을 맞추기 때문에 딥러닝 모델의 표현 학습 능력을 향상시키는 것과는 차이점이 있다. 보이지 않는 작업에 빠르게 적응할 수 있는 잠재력이 커서 이미지 인식, 자연어 처리, 강화 학습 등 다양한 연구 영역에 적용되고 있다.

뿐만 아니라 제한된 인스터스와 비효율적인 연산으로 어려움을 겪고 있는 추천 시스템 등 다양한 연구 영역에 적용되고 있다. 고급 메타러닝 기법은 우선 기존 딥 추천 모델을 학습할 때 데이터 부족을 해소 하기 위해 적용된다. 예를 들면 최적화 기반 메타 학습 프레임워크인 MAML은 콜트 스타트 추천 시나리오에서 큰 효과를 보여준다.

이 논문에서는 이 분야의 연구를 정리하기 위해 세 가지 관점으로 분류법을 제시하고 메타러닝 기술을 활용하여 다양한 형태의 메타 지식을 추출하는 다양한 방법을 논의

1번 섹션에서는 메타러닝 기법의 기본 사항과 메타러닝 기법이 적용되는 일반적인 추천 시나리오를 다룬다. 2번 섹션은 세 가지 관점의 분류법을 제시하고, 섹션 3에서는 메타러닝 추천 작업을 구성하는 다양한 방법을 요약하며, 섹션 4에서는 메타러닝 기법을 적용한 기존 방법의 방법론적 세부 사항을 자세히 설명한다.

1. 메타러닝 기법의 기본 사항

이 섹션에서는 딥 메타러닝 기반 추천 방법을 논의하는데 필요한 기초적인 이해를 제공하며 메타러닝의 개념을 소개하고 기존 머신 러닝과 대조한다. 이후 메트릭 기반, 모델 기반, 최적화 기반 기법 등 메타러닝 기법의 세 가지 주요 흐름과 몇 가지 대표작을 간락하게 소개한다. 마지막으로는 메타러닝 기법이 연구되고 적용된 대표적인 추천 시나리오도 소개한다.

1.1 Meta-Learning

메타 학습은 기본 학습 모델의 일반화 능력을 향상시켜 새로운 작업을 더 효율적으로 학습할 수 있도록 하는 프로세스이다. 이는 여러 작업에 대한 사전 지식, 즉 메타 지식을 학습함으로 이루어진다. 서로 다른 작업의 학습 과정은 메타 학습 방법으로 관찰된 훈련 인스턴스로 취급되며, 메타 지식의 형태를 추출하고 정의하여 새로운 작업을 보다 효과적으로 학습할 수 있도록 한다.

훈련 단계에서 훈련 데이터는 작업 배포에서 샘플링되며 메타 훈련 데이터세트로 표신된다. 각 작업은 지원 집합으로 표시되는 자체 훈련 인스턴스와 쿼리 집합으로 표시되는 평가 인스턴스로 구성된다. 작업에 대한 매핑 함수의 작업별 파라미터는 경험적 손실 함수를 최소화하여 얻고 학습된 매핑 함수는 작업의 동일한 분포에서 샘플링된 평가 인스턴스 세트에 대해 평가된다.

메타학습의 목표는 모든 작업에 대한 작업별 학습이 더 나은 성과를 낼 수 있도록 안내할 수 있는 최적의 파라메터를 학습하는 것이다. 메타 학습 방법의 학습 목표는 각 직업의 해당 쿼리 세트에 대해 더 나은 성능을 관찰하는 것이며, 메타 지식은 여러 작업에 걸쳐 학습되어 작업 간 특성을 파악하고 작업 차이에 대한 뛰어난 일반화 능력을 갖는다.

메타러닝 패러다임은 크게 3가지 점에서 기존 머신 러닝과 다르다.

  1. 기존 머신 러닝은 특정 작업의 성능을 개선하는 데 중점을 두는 반면, 메타 러닝은 새로운 작업을 학습하는 방법을 배우는 데 중점.
  2. 메타 학습에서는 여러 개의 작은 작업을 사용하여 더 큰 작업에 적용할 수 있는 지식을 추출.
  3. 메타 학습 과정에는 두 가지 수준의 학습이 포함되는데, 내부 수준에서는 작업 별 정보를 학습하고 여러 인스턴스에 걸쳐 학습하고 일반화 가능한 지식을 추출하는 외부 수준용 학습 인스턴스를 생성.

1.2 Meta-Learning 기술의 주요 기법

퓨샷러닝(Few Shot Learning)

퓨샷 러닝에서의 데이터셋은 크게 두 가지로 나뉜다.

  1. 서포트 셋(Support Set)
    1. 데이터 셋을 학습에 사용하는 데이터
  2. 쿼리 셋 (Query Set)
    1. 데이터 셋을 테스트에 사용하는 데이터

이러한 퓨샷 러닝 태스크를 “N-way K-shot”이라고 하며 여기서 n은 카테고리의 개수를 뜻하고 k는 카테코리당 이미지의 수를 의미한다. 일반적으로 성능은 N에 반비례하며 k에 비례한다.

—# 예를 들어 개, 고양이, 말 3개의 카테리고가 있고 각각 4장씩 이미지가 존재한다면

→ 3-way 4-shot이다.

메타 학습 기법에는 대표적으로 세 가지가 존재하며 각 기법에 대한 장단점을 비교한다.

  • 거리 학습 기반 : 효율적 거리 측정 학습
  • 모델 기반 학습 : 메모리를 이용한 순환 신경망
  • 최적화 학습 : 모델 파라미터 최적화

거리 학습 기반(Metric Based Learning)

메타 러닝의 방법론 중 하나인 거리 학습 기반은 서포트 셋과 쿼리 셋 간의 거리(유사도)를 측정하는 방식으로 대신한다. 대표적인 알고리즘은 샴 네트워크(Siamese Neural Network)가 있다.

모델은 주어진 서포트 데이터를 특징 공간에 나타내서 특징을 뽑아내며 같은 클래스면 거리를 가깝게 다른 클래스면 멀게 하는 방식으로 데이터를 분류한다. 쿼리 데이터를 유클리디안 거리가 가까운 서포트 데이터의 클래스로 예측하는 방식이다.

계산 부담이 적지만 작업 분배가 복잡할 경우 성능이 불안정

모델 기반 학습 방식(Model based learning)

모델 기반 학습 방식은 적은 수의 학습 단계로도 모델의 파라미터를 효율적으로 학습할 수 있는 방식이다.

모델에 별도의 메모리를 두어 학습 속도를 조절한다. 대표적인 알고리즘으로는 MANN가 있다.

과거 데이터를 외부 메모리에 저장함으로 효율적으로 문제를 해결하는 방법을 습득하며 새로운 정보를 빠르게 인코딩하고 몇 개의 샘플만 가지고도 새로운 태스크에 적용할 수 있도록 설계되었다.

최적화 단계가 간단하고 적용 범위가 넓지만, 분산되지 않은 작업에 대해서는 성능이 떨어짐

최적화 학습 방식(Optimizer learning)

Few Shot task를 파라미터 최적화 문제로 생각한다.

일반적으로 딥러닝 모델은 기울기의 역전파를 통해 학습을 진행하지만 기울기 기반 최적화 기법은 큰 스케일의 데이터를 위해 설계가 되었기 때문에 적은 수의 샘플에 대한 최적화 기법을 다룬다. 대표적인 알고리즘으로는 MAML알고리즘이 있다.

기본 모델 구조에 구애받지 않으며 복잡한 작업 분포에서 더 나은 일반화 능력을 보이지만 계산량이 많다는 단점.

1.3 추천시스템 시나리오

추천 시스템과 관련하여 메타러닝이 연구된 다양한 시나리오가 있다. 이런 시나리오에는 콜드 스타트 추천, 클릭률 예측, 온라인 추천, 관심 지점 추천, 순차적 추천이 포함된다. 이외에도 메타 학습이 연구된 다른 추천 시나리오도 있다.

Cold Start

콜드 스타트 추천은 새로운 사용자나 아이템이 등장할 때 흔히 발생하는 사용자-아이템 상호 작용 데이터가 제한적인 경우 추천 시스템에서 어려움을 겪게 된다. 기존의 협업 필터링 방식이나 딥러닝 방식은 풍부한 학습 데이터가 필요하기 때문에 이러한 상황에서는 성능이 좋지 않을 수 있다. 콘텐츠 기반 방식은 다양한 콘텐츠 정보를 사용하여 추가적인 의미 정보를 통해 사용자와 항목의 표현을 향상시키므로 상호작용 데이터에 대한 요구가 약하된다. 콜드 스타트 추천은 소수 학습에서 응용할 수 있으며, 데이터 부족을 완화하기 위해 메타 학습 기법을 활용한다.

클릭률 예측

클릭률(CTR) 예측은 게시된 광고의 가치를 결정하는 데 도움이 되므로 온라인 광고에서 중요하다. CTR예측 모델은 광고/사용자 ID 및 기타 기능에 대한 잠재벡터를 학습하는 임베딩 레이어와 정교한 모델과 기능 상호 작용을 모델링하는 예측 레이어로 구성된다. 그러나 이러한 모델은 클릭 기록이 제한적인 신규 광고에서는 콜드 스타트 문제로 인해 성능이 저하된다. 콜드 스타트 광고에 대한 임베딩 학습을 강화하기 위해 메타 학습 방법이 연구되고 있다.

온라인 추천

온라인 추천은 대규모 추천 시스템에서 과거의 기록에만 의존하지 않고 실시간 사용자 상호작용 데이터를 기반으로 추천 모델을 업데이트하는 데 사용되는 기술이다. 이 접근 방식을 사용하면 동적인 사용자 선호도와 추세를 파악할 수 있다. 하지만 효율적인 계산 방법이 필요하다. 온라인 추천 시나리오에서 실시간 데이터로부터 트렌드를 빠르게 포착하기 위해 메타러닝이 도입된다.

관심 지점 추천

관심 지점 추천은 사용자의 체크인 기록을 기반으로 개인화된 위치 추천을 제공하는 추천 시스템의 한 유형이다. 이러한 유형의 추천은 과거 체크인 데이터에서 공간적-시간적 종속성을 발견하는 데 크게 의존한다. 하지만 이 시나리오에서는 사용자가 소수의 POI만 방문하는 경향이 있기 때문에 데이터 희소성이 문제가 된다. 이를 해결하기 위해 메타러닝 기반 POI 추천 방법이 연구되고 있다.

—# POI는 관심 있는 사항을 나타내는 지도의 특정 지점

순차 추천

순차 추천은 사용자의 상호 작용 순서를 사용하여 진화하는 선호도를 파악하는 추천 시스템의 한 유형이다. 이 방법은 사용자 관심사 진화의 순차적 패턴을 발견하는 데 중점을 두며, 마르코프 체인, 순환 신경망, 자기 관심 기반 네트워크와 같은 방법을 사용하여 좋은 결과를 얻었다. 하지만 순차 추천의 성능은 시퀀스의 충분한 항목에 의존하며, 수가 적을 경우 성능이 크게 저하될 수 있다. 딥 메타러닝 기반 추천 시스템의 분류 체계는 아래 그림과 같이 추천 시나리오, 메타러닝 기술, 메타 지식 표현이라는 세 가지 독립적은 축을 기반으로 구축된다.

1
2. 분류

이 섹션에서는 딥 메타러닝 기법을 사용하는 추천 시스템을 위한 분류 체계를 작성하며 분류 체계를 기반으로 기존 방법의 주요 특징을 요약한다. 추천 시스템의 분류 체계는 추천 시나리오, 메타러닝 기술, 메타지식표현의 세 가지 축으로 정의된다. 다른 분류법도 존재하지만 추천 시스템에 초점을 맞추지는 않았다.

  1. 추천 시나리오(어디서)

    추천 시나리오에는 콜드 스타트 추천, 클릭률 예측, 온라인 추천, 관심 지점 추천, 순차적 추천이 포함된다.

  2. 메타 학습 기법(어떻게)

    메타 학습 기법이란 추천 모델이 새로운 업무에 일반화할 수 있는 능력을 향상시키기 위해 메타 학습을 적용하는 방식이다. 메타 학습 기법에는 거리(메트릭)기반, 모델 기반, 최적화 기반 세 가지가 존재한다.

  3. 메타 지식 표현(무엇을)

    메타 지식 표현 축은 추천 모델의 빠른 학습을 개선하는 데 사용할 수 있는 지식의 형태를 나타낸다. 메타 지식의 일반적인 유형에는 파라미터 초기화, 파라미터 변조, 하이퍼파라미터, 샘플 가중치, 임베딩 공간, 메타 모델 등이 있다. 메타 학습 기술마다 메타 지식 표현에 뚜렷한 특징이 있다. 예를 들어, 파라미터 초기화는 일반적으로 최적화 기반 메타 학습을 통해 이루어지며, 파라미터 변조는 모델 기반 메타 학습에 속할 가능성이 더 높다. 여러 유형의 메타 지식 표현이 하이브리드 방식으로 동시에 학습되는 상황도 존재한다.

3. 추천을 위한 메타 학습 작업

이 섹션에서는 추천 시스템의 맥락에서 메타 학습 작업을 구성하는 방법에 대해 설명한다. 메타 학습 작업을 구성하는 과정은 일반적인 딥러닝 작업과는 다르며, 이 섹션에서는 문헌에서 사용되는 다양한 메타 학습 작업 구성 방법을 소개한다.

메타 학습에는 각각 훈련 인스턴스 세트(support set) 와 평가 인스턴스 세트(query set)로 분리된 메타 학습 및 메타 테스트 작업을 구성하는 것이 포함된다. 목표는 suppot set에서 빠르게 학습하고 query set에서 보이지 않는 인스턴스에 대해 더 나은 성능을 발휘하는 것이다. 단일 작업 수준에서는 학습 목표가 일반적인 딥러닝과 유사하지만 메타 러닝에서는 데이터 불충분성이 강조된다. 더 높은 수준에서는 보이지 않는 작업의 평가 인스턴스에서 더 나은 성능을 발휘하여 여러 새로운 작업에 대한 메타러닝 방법의 일반화 능력과 빠른 학습 능력을 평가하는 것이 목표이다.

추천 시나리오에 메타러닝을 적용할 때는 작업을 구성하는 방식이 중요하며 추천 시나리오의 특정 요구 사항에 맞게 조정해야 한다. 예를 들어, 이미지 인식과 같은 소수의 샷으로 분류하는 작업에서 메타 학습 작업을 구성하는 일반적인 방법은 클래스 풀에서 N개의 클래스를 무작위로 샘플링한 다음 각 클래스에 속하는 K개의 인스턴스를 샘플링 하는 것이다. 자연어 처리 및 추천 시스템을 비롯한 다양한 분야의 메타 학습에서 작업을 구성하기 위한 다양한 설정들이 존재한다. 추천 시스템에서 작업은 주로 콜드 스타트 문제를 해결하기 위해 사용자와 항목을 기반으로 구성된다. 사용자별 태스크는 동일한 사용자에게 속한 인스턴스를 포함하며, 항목별 태스크는 동일한 항목을 기반으로 인스턴스를 그룹화한다. 목표는 support 집합에 대해 모델을 학습시키고 query 집합에 대해 평가하는 것이다.

메타 학습 기반 추천 방법은 작업의 상호작용 속성에 따라 다음과 같이 4가지 범주로 나눌 수 있다.

  1. 사용자별
  2. 항목별
  3. 시간별
  4. 시퀀스별

사용자별

사용자별 작업은 support 및 query 집합을 포함한 작업의 모든 인스턴스가 동일한 사용자에게 속하는 추천 시스템에서 작업을 구성하는 방식이다. 이 접근 방식은 사용자의 제한된 상호 작용으로부터 선호도를 빠르게 학습하는 것을 목표로 하며, 이는 사용자 콜드 스타트 문제를 해결하기 위한 중요한 작업이다. 목표는 support 세트에 대한 모델을 훈련하고 동일한 사용자의 query세트에 있는 상호 작용에 대해 평가하는 것이다. 메타 학습 방법은 충분한 수의 사용자별 작업에서 사용자 선호도 학습에 대한 메타 지식을 추출하여 새로운 사용자의 보이지 않는 작업에 직면했을 때 선호도 학습을 용이하게 한다.

예를 들어, u1이라는 특정 사용자가 있고 이 사용자의 항목과의 상호작용이 지원 집합 S1(일부 항목 평점 포함)과 쿼리 집합 Q1(다른 항목 평점 포함)의 두 집합으로 나뉘는 경우, S1에 대한 모델을 훈련하여 u1에 대한 Q1 예측을 잘 수행하도록 하려고 한다. 이처럼 다양한 사용자와 관련된 여러 유사한 작업에 메타 학습 기법을 사용함으로써 개별 사용자나 항목에 대해 개별적으로 사용할 수 있는 데이터가 많지 않은 경우에도 효율적인 모델을 학습하는 능력을 향상시킬 수 있을 것으로 기대한다.

항목별

항목별 작업에서는 동일한 항목과 관련된 상호작용이 함께 그룹화된다. support set과 query set은 여러 사용자와 동일한 항목간의모든 상호작용을 포함한다. 목표는 support set을 관찰한 후 query set에서 평가 인스턴스의 등급 또는 상호 작용 확률을 예측하는 것이다. 메타 학습을 사용하면 콜드 스타트 항목에 대한 전반적인 선호도를 빠르게 파악하여 정확한 예측과 추천을 할 수 있다.

시간별

추천 시스템의 시간별 작업에는 시간 슬롯에 따라 상호 작용 데이터를 여러 작업으로 분할하는 작업이 포함됩니다. 이러한 작업은 두 개의 연속된 시간 슬롯의 데이터를 사용하여 순차적으로 구성되며, 지원 세트는 현재 시간 슬롯의 데이터로 구성되고 쿼리 세트는 다음 시간 슬롯의 데이터로 구성됩니다. 이러한 작업은 온라인 환경에서 모델을 효율적으로 업데이트하는 것을 목표로 하며, 메타 학습을 사용하여 순차적인 시간별 작업에서 메타 지식을 추출하여 모델 업데이트를 용이하게 할 수 있습니다. 시간별 작업의 상호 작용은 더 이상 사용자나 항목별로 구분되지 않습니다.

시퀀스별

시퀀스별 작업은 서로 다른 사용자 또는 세션의 상호 작용 시퀀스를 별도의 작업으로 취급하는 추천 시스템의 작업 구성 유형입니다. 예를 들어 사용자의 상호작용 시퀀스는 타임스탬프로 정렬된 상호작용 목록으로 표시됩니다. 상호작용 시퀀스는 지원 세트와 쿼리 세트로 나뉘며, 전자의 상호작용은 학습 데이터로 사용되고 후자의 상호작용은 평가에 사용됩니다. 시퀀스별 작업은 익명 세션을 포함할 수 있고 상호작용 시퀀스의 후속 작업을 인스턴스로 사용할 수 있다는 점에서 사용자별 작업과 다릅니다.

4. 추천시스템을 위한 메타러닝 방법

메타 학습은 다양한 시나리오에서 학습하고 그 지식을 새로운 상황에 적용하여 추천 시스템을 개선하는 데 도움이 되는 방법이다. 이 섹션에서는 사용자의 상호 작용이 거의 없는 콜드 스타트 추천이나 상호 작용이 거의 없는 항목과 같은 다양한 추천 시나리오에서 이 접근 방식을 어떻게 사용할 수 있는지 설명한다. 또한 이러한 다양한 시나리오에서 성능을 향상시키기 위해 메타러닝을 사용하는 관련 작업과 그 방법에 대한 개요를 제공

4**.1 Cold Start 추천시스템에서의 메타러닝**

추천 시스템에서 사용자나 항목에 대한 상호 작용이나 데이터가 충분하지 않은 경우를 '콜드 스타트'라고 하며, 이 경우 추천의 정확도가 떨어질 수 있습니다. 메타 학습 방법은 소량의 데이터로 학습하는 소수 학습 기법을 사용하여 이러한 유형의 시나리오를 개선하는 데 도움이 될 수 있다. 이는 추천 시스템에서 메타 학습을 가장 일반적으로 적용하는 방법이다.

메타 학습은 더 나은 매개변수 초기화와 적응형 하이퍼파라미터를 학습하기 위해 로컬과 전역의 두 가지 수준에서 시스템을 최적화하는 것입니다. 이 최적화의 목표는 새롭거나 알려지지 않은 데이터 포인트에 대한 권장 사항을 개선하여 사용자와 항목 모두에 대한 콜드 스타트 문제를 해결하는 것입니다. 모델 기반 매개변수 변조 및 메트릭 기반 임베딩 공간 학습과 같은 다양한 방법이 사용됩니다.

1

그림 4는 최적화 기반 파라미터 초기화 및 적응형 하이퍼파라미터를 위한 프레임워크를 보여줍니다. 이 프레임워크에는 로컬 적응과 글로벌 최적화의 두 가지 수준의 최적화가 포함된다. 메타 학습기는 이 접근 방식을 사용하여 여러 작업에 걸쳐 업데이트된다. 파라미터 초기화와 적응형 하이퍼파라미터는 모두 다양한 메타 학습기 설계를 기반으로 학습할 수 있다.

표 5는 콜드 스타트 추천에 사용되는 최적화 기반 메타러닝 방법을 세 가지 관점에서 요약한 것입니다. 기존 방법은 메타 지식 표현의 형태에 따라 파라미터 초기화적응형 하이퍼파라미터의 두 가지 범주로 나뉩니다. 두 가지 유형에 대한 일반적인 프레임워크는 그림 4에 제시되어 있으며, 각 유형에 대한 구체적인 방법은 별도로 설명합니다.

2

Optimization-based Parameter Initialization

최적화 기반 파라미터 초기화는 추천 모델의 초기 파라미터를 설정하는 방식이다. 메타 지식을 시작점으로 사용하고, 이후 2단계 최적화를 통해 업데이트하여 정확도를 높인다. 콜드 스타트 추천을 개선하기 위해 MAML프레임 워크를 사용하는 MeLU라는 방법이 있다. MeLU는 신경망 기반 추천 모델의 전역 매개변수 초기화를 사전 지식으로 학습하여 개인화된 사용자 선호도 추정을 보다 정확하게 하는 것을 목표로 한다. 이 접근 방식에는 각 사용자에 대한 매개변수를 로컬로 업데이트하고 학습된 초기화를 기반으로 작업별 모델을 구성하는 것이 포함된다.

#— Optimization-based Parameter Initialization

아래 그림과 같이 서로 다른 Task(L1, L2, L3)를 가진 동일한 구조의 model은 각각의 task만 잘 맞추면 될 뿐 다른 Task는 고려하지 않지만 최적화 기반 파라미터 초기화는 이 Model의 서로 다른 Task의 공통적인 최적화된 파라미터를 찾는 것이다. 일반적으로 회귀 문제에서는 class는 의미가 없으므로 몇 개의 학습데이터만을 의미한 K-shot만 의미를 가진다.

1
#— MeLU(Meta-Learned User preference estimator)

MeLU의 작동 원리는 다음과 같다. 먼저, MeLU는 기존 추천 시스템(예: Netflix)에서 처음으로 사용자에게 제공하는 항목을 증거 후보라고 부르고.그런 다음 사용자가 선택한 항목을 기반으로 추천이 이루어진다. MeLU는 메타러닝을 사용하여 몇 가지 예제만으로 새로운 작업을 빠르게 적용할 수 있다. 따라서 MeLU는 몇 가지 항목만 소비한 새로운 사용자의 선호도를 추정할 수 있다. 또한 MeLU는 사용자 지정 선호도 추정을 위한 구별 항목을 결정하는 증거 후보 선택 전략을 제공한다.

#— MAML(Model-Agnostic Meta-Learning)

MAML은 주어진 작업에서 최적의 모델 파라미터를 찾는 대신, 다양한 작업에서 빠르게 적응하고 일반화할 수 있는 모델 파라미터를 찾는 것을 목표로 합니다. 이를 위해 MAML은 작업 집합에서 샘플 작업을 선택하고, 이를 사용하여 모델 파라미터를 업데이트합니다. 이 과정을 여러 번 반복하면서, 적응성을 갖는 모델 파라미터를 학습합니다.

#— MPML(Meta Probabilistic Machine Learning)

여러 초기화 매개변수 세트를 사용하고 콜드 스타트 작업에 대한 지원 세트에 대한 로컬 업데이트 후 성능에 따라 매개변수를 할당하는 방식입니다. 최적화 기반 메타러닝 기법을 통해 단순한 FCN 기반 협업 필터링 모델을 넘어 다양한 형태의 추천 모델에 대한 초기화 학습에 사용

매개변수를 초기화하는 3가지 방법

MeLU는 모든 작업 간에 전역 초기화를 공유하는 반면, MAMO와 PAML은 각각 사용자 프로필과 선호도를 고려하여 작업별 초기화를 맞춤화한다.

1

MAMO

MAMO는 메모리 네트워크를 사용하여 작업별 빠른 가중치 메모리를 저장한다. MAML 프레임워크에서 학습한 전역 초기화를 기본 모델에 할당하기 전에, 주어진 사용자의 프로필 표현으로 바른 가중치 메모리 MW를 쿼리하여 개인화된 bias를 생성한다. 메모리 업데이트 비율이 최적화에 사용됨에 따라 트레이닝 작업과 하이퍼파라미터를 통해 2개의 메모리 행렬이 업데이트 되며 MAMO는 사용자 프로필에 따라 작업별 초기화를 맞춤화 한다.

PAML

PAML은 비슷한 선호도를 가진 사용자들이 사전 지식을 공유하는 방법으로 사용자별 선호도에 따라 모델의 전역 초기화를 조정한다. 이는 외부 메타 모델을 사용하여 각 사용자의 선호도에 맞게 초기화를 조정하는 방식으로 이루어진다.

선호도별 게이트는 공유되는 사전 지식의 양을 제어하도록 설게되어 있다. 사용자의 표현에는 상호 작용, 소셜 네트워크의 명시적 친구, 의미 관계의 암시적 친구가 포함된다. 사용자 관계는 소셜 네트워크와 HIN을 모두 통하여 포괄적으로 모델링된다. 선호도별 초기화를 수행한 후 최적화 기반 메타 학습을 활용하여 기본 추천 모델과 앞서 설명한 선호도 모델링 모듈과 함께 FCN 기반 평점 예측 모듈을 포함하는 메타 모델의 파라미터를 최적화한다. PAML은 MAMO처럼 사용자 프로필 정보에만 집중하는 것이 아닌 여러 유형의 사용자 관계를 기반으로 다양한 작업을 구분한다.

Optimization-based Adaptive Hyperparameters

메타 학습을 사용하여 항목이나 제품 추천과 같이 처음부터 시작해야 하는 다양한 유형의 작업에 대해 하이퍼파라미터를 조정하고 최적화하는 방법을 학습.

이러한 방법 중 하나인 MetaCS는 다른 접근 방식과 유사하게 이중 수준 최적화를 사용한다. 각 방법의 세부 사항은 설계된 메타 모델의 각 주요 역할과 함께 아래의 표에 요약되어 있다.

2

로컬 적응 중 중에 추천 모델의 학습 속도를 조정하는 방법으로는 다음과 같다. 제공된 방정식은 수렴을 개선하기 위해 고정 및 가변 요인에 따라 학습률이 업데이트 된다. 이 접근 방식에는 더 나은 성능을 위한 사전 지식을 제공하기 위해 모델 매개변수와 학습률과 같은 하이퍼파라미터를 모두 최적화하는 메타러닝이 포함된다.

사용 가능한 데이터가 많지 않은 상황에서 추천 모델을 개선하는 방법

메타 학습을 사용하면 모델이 최적의 매개 변수를 스스로 학습할 수 있을 뿐만 아니라 학습률과 같은 중요한 정보도 학습하여 더 나은 추천을 할 수 있다. 하지만 이 방법은 전체적으로(모든 작업에 걸쳐) 업데이트 비율을 최적화하지만, 각 작업의 로컬 적응 중에는(특정 권장 사항에 대해 작업할 때) 변경되지 않는다는 점에 유의해야 한다.

  • MetaCF

MetaCF는 협업 필터링을 기본 모델로 사용하는 추천 모델이다. 이 모델은 MAML 프레임워크를 활용하여 FISM의 항목 임베딩 및 NGCF의 임베딩 변환 매개변수와 같은 학습 가능한 매개변수에 대한 메타학습 초기화를 수행한다. 또한 MetaCF는 MetaCS와 유사한 업데이트 전략을 사용하여 적절한 학습 속도를 자동으로 학습한다.

—# FISM

팩토리얼 아이템 유사도 모델의 약자로 추천 시스템에서 희소 데이터와 콜드 스타트 시나리오를 처리하는 데 효과적

Metric-based Embedding Space Learning(거리 기반 임베딩 공간 학습)

메트릭 기반 메타 학습을 사용하여 새 아이템의 임베딩과 사용자 간의 유사성을 측정하기 위한 공유 공간을 학습한다. 이는 사용 가능한 상호 작용 기록이 거의 없거나 전혀 없는 경우 콜드 스타트 추천에 도움이 된다.

‘메트릭 기반 메타 학습’은 아직 시스템과 많은 상호작용을 하지 않은 사용자에게 항목을 추천하는데 도움이 된다. 메트릭 기반 방법을 사용하여 서로 다른 아이템이 얼마나 유사한지 측정하기 위한 공유 메트릭 공간을 생성하여 특정 아이템에 대한 데이터가 많지 않은 경우에도 관련 항목을 추천할 수 있는 Sankar등의 연구를 참조하고 있다.

3

ProtoCF

ProtoCF는 추천 시스템에서 새로운 아이템과 사용자 간의 유사성을 측정하는 데 사용되는 방법이다. 프로토타입 네트워크에서 영감을 얻은 접근 방식을 사용하여 사용자와의 제한된 상호 작용을 기반으로 덜 인기 있는 아이템의 프로토타입을 생성하는 방법을 학습한다. 이러한 프로토타입은 공유 메트릭공간에서 새로운 Coldstart 아이템과 기본 사용자 임베딩의 유사성을 비교하는 데 사용된다. 간단하게 말해, ProtoCF는 알려진 정보가 거의 없는 상태에서 예시(프로토타입)을 만들고 이를 기존 사용자 선호도와 비교함으로써 아직 관련 데이터가 많지 않은 경우에도 유사한 제품이나 서비스를 추천하는데 도움을 준다.

ProtoCF
1

ProtoCF는 추천 시스템에서 새로운 아이템과 사용자 간의 유사성을 측정하는 데 사용되는 방법이다. 프로토타입 네트워크에서 영감을 얻은 접근 방식을 사용하여 사용자와의 제한된 상호 작용을 기반으로 덜 인기 있는 아이템의 프로토타입을 생성하는 방법을 학습한다. 이러한 프로토타입은 공유 메트릭공간에서 새로운 Coldstart 아이템과 기본 사용자 임베딩의 유사성을 비교하는 데 사용된다. 간단하게 말해, ProtoCF는 알려진 정보가 거의 없는 상태에서 예시(프로토타입)을 만들고 이를 기존 사용자 선호도와 비교함으로써 아직 관련 데이터가 많지 않은 경우에도 유사한 제품이나 서비스를 추천하는데 도움을 준다.

ProtoCF 예시

가정 : 책을 판매하는 새로운 온라인 스토어를 위한 추천 시스템 구축

인기 도서와 그 등급에 대한 데이터는 어느 정도 존재하지만, 새로 출시되거나 덜 인기 있는 도서에 대한 정보는 많지 않다. (Cold start) 이 때 ProtoCF는 덜 인기 있는 타이틀의 프로타입(예시)를 생성하여 이 시나리오에 도움을 줄 수 있다. 지금까지 단 3명의 사용자만 평가한 새 책이 있다고 가정해보면 ProtoCF는 이러한 제한된 상호 작용을 사용하여(3명의 평가) 이 책의 프로토타입을 만들고 기존 사용자 선호도와 비교하여 해당 책과 유사한 주제에 관심이 있을 수 있는 다른 사용자에게 추천할 수 있다. (데이터가 아예 없는 경우에는 정확한 추천을 제공하지 못할 가능성이 높다.)

추천 시스템의 콜드 스타트 문제를 해결하는 데 도움이 되는 사전 훈련이라는 방법이 있다. 이 방법은 새로운 사용자와 아이템에 대한 임베딩을 학습하기 위한 GNN을 훈련하기 위해 몇 번의 샷 재구성 작업을 사용한다. 목표는 단순히 사용자와 아이템 간의 유사성을 측정하는 것뿐만 아니라, 새로운 엔티티의 재구성된 임베딩을 풍부한 상호작용을 통해 학습한 기준 데이터 임베딩과 비교하여 효과적인 임베딩 공간을 학습하는 것이다.

—# 사전훈련

콜드 스타트 문제를 해결하기 위해 사용되는 방법이다. 사전 학습은 소량의 데이터로 GNN을 훈련하기 위해 소수의 샷 재구성 작업을 사용하여 몇 가지 예제와의 상호 작용을 기반으로 새로운 엔티티에 대한 임베딩을 학습할 수 있도록 한다. 간단히 말해서 이는 “재구성 작업”이라는 프로세스를 사용하여 GNN을 훈련하는 것이더. 이러한 작업은 충분한 상호작용이 있는 사용자/아이템을 선택하고 각 대상 사용자/아이템에 대해 몇 개의 이웃을 샘플링한 다음 콜드 스타트 상황을 시뮬레이션한다.

예시

아직 노래를 많이 들어본 적이 없는 신규 사용자(콜드 스타트 사용자)가 있다고 가정

콜드 스타트 대상 사용자를 선정하고, 그 주변 사용자(비슷한 음악 청취 이력)몇 명을 샘플링한다. 그 다음, 이 샘플을 입력 데이어로 사용하여 타켓 콜드 스타트 사용자가 선호하는 장르 또는 아티스트에 대한 각 임베딩을 재구성한다. 이렇게 재구성된 임베딩을 다양한 노래를 많이 들은 적 있는 사용자의 더 풍분한 상호작용(상호작용이 더 많은 데이터)을 통해 학습한 임베딩과 비교한다.

Few - Shot Item recommendation

1

K- shot 아이템 추천

  • 각각의 아이템에 대한 K개의 상호작용이 주어지면 few-shot 개인화된 추천 생성

    (이미지 task에서 5개의 사진이 주어진 것처럼 5개의 상호작용이 주어졌다고 가정)

    추천 시스템에서는 way는 없다.

K - shot 아이템 추천을 위한 메타러닝 학습

ProtoType(예시) 생성

  • 각각의 유저에게 가장 가까운(유사한)아이템 프로토타입(예시)를 기준으로 아이템을 추천한다

첫 번째 단계는 학습 데이터를 이용하여 추론 모형을 학습하는 것이다. 이 때, 모형의 구조와 모수(parameter)를 결정하게 된다. 이 과정에서는 주어진 학습 데이터를 이용하여 모형이 가장 적합한 구조와 모수를 찾아내는 최적화 문제를 푸는 것이다다.

두 번째 단계는 학습된 추론 모형을 이용하여 새로운 데이터에 대한 예측을 수행하는 것이다. 이 때, 모형이 학습한 지식을 이용하여 새로운 입력 데이터에 대한 출력을 예측하게 된다. 이 과정에서는 일반적으로 모형의 성능을 평가하기 위해, 일부 데이터를 테스트 데이터로 따로 떼어내어 이를 이용하여 모형의 예측 성능을 평가하게 된다.

episodic few-shot item

2

episodic Few-shot Learning은 일반적으로 episodic memory라는 개념을 사용한다. 이는 일종의 메모리 버퍼로서, 일련의 서포터 데이터 샘플로 이루어진 작은 데이터 집합을 저장한다. 새로운 쿼리 데이터가 제공될 때마다, 모델은 episodic memory에서 일부 샘플을 추출하여 학습에 사용한다. 이를 통해 적은 수의 샘플로도 높은 일반화 능력을 달성할 수 있다.

  1. 유저와 아이템 각각의 벡터를 생성한다.. ProtoCF는 유저와 아이템 각각의 벡터를 생성하여 학습한다. 벡터의 차원은 사용자가 정의하며, 일반적으로는 사용자와 아이템의 특징을 잘 나타내는 크기로 설정된다.
  2. 프로토타입 생성 및 학습 ProtoCF는 유저와 아이템 벡터 각각에 대해 프로토타입(prototype)을 생성한다다. 이때 프로토타입은 해당 벡터와 가장 가까운 다른 벡터들의 평균이다. 예를 들어, 유저 A와 가장 가까운 10명의 유저들의 벡터를 평균하여 유저 A의 프로토타입을 만들 수 있다. 이렇게 생성된 프로토타입은 해당 유저 혹은 아이템 벡터의 대표값이 된다.
  3. 예측 수행 ProtoCF는 아직 평점이 매겨지지 않은 아이템에 대해 예측을 수행한다. 이때, 해당 아이템과 가장 가까운 프로토타입을 가진 유저들의 벡터를 사용하여, 해당 아이템의 평점을 예측한다.
9주차 (Meta-Learning-Metric-ProtoCF)

9주차 (Meta-Learning-Metric-ProtoCF)

논문 자료(ProtoCF :Prototypical Collaborative Filtering for Few-shot Recommendation)

ProtoCF

0. 초록

Prototypical Collaborative Filtering for Few-shot Recommendation

현재 추천 시스템은 인기 있는 항목을 추천하는 데는 효과적이지만, 상호 작용이 적은 덜 인기 있는 '롱테일' 항목에는 어려움을 겪는다. 저자는 실험을 통해 기존의 신경 추천 시스템이 관련성이 높은 롱테일 항목의 순위를 정확하게 매길 수 없음을 보여준다.

저자들은 현재 추천 시스템에서 롱테일 문제를 해결하기 위한 새로운 접근 방식을 제안하며 “Few-Shot learning” 이라는 기술을 사용하여 강력한 프로토타입 표현을 생성함으로써 이러한 유형의 항목에 대한 추천 방법을 학습하는 ProtoCF라는 프레임워크를 만든다.

ProtoCFEpisodic Few-shot leaning을 사용하여 롱테일 내 아이템 순위를 모방하는 다양한 작업으로부터 학습한다. 또한 아키텍처에 관계없이 다른 추천 시스템에서 지식을 추출하고 전송하기 위해 Knowledge distillation에 기반한 또 다른 기술을 제안한다.

Episodic few-shot learning

Episodic few-shot learning은 머신러닝에서 소량의 데이터로 학습하는 데 사용되는 기법이다. ProtoCF는 꼬리 부분의 아이템 순위를 모방하는 다양한 작업을 학습하여 상호작용이 거의 없는 롱테일 아이템을 추천하는 방법을 학습한다는 의미이다. 좀 더 자세하게 설명하자면, 큰 작업을 더 작은 ‘에피소드’ 또는 하위 작업으로 세분화 한 다음 이러한 에피소드를 학습하는 방법이다. 각 에피소드는 학습 중인 각 클래스 또는 카테고리에 대해 적은 수의 예제 또는 인스턴스(샷)로 구성된다. 이 작업을 여러 에피소드에 걸쳐 반복적으로 수행함으로써 모델은 매우 적은 데이터로도 일반화하고 정확한 예측을 하는 방법을 학습할 수 있다. 정리하자면, Episodic few-shot learning을 통해 ProtoCF는 학습 과정에서 제한된 양의 관련 정보만 사용하여 상호 작용이 드문 롱테일 아이템 추천을 효과적으로 처리할 수 있다.

예시

다양한 종류의 꽃을 인식하도록 모델을 훈련하고 싶다고 가정

수천 개의 이미지가 포함된 대규모 데이터 세트가 있지만 각 꽃 유형에는 몇 개의 예시(몇 장의 사진)만 있다. 에피소드식 소수 샷 학습을 사용하려면 작업을 더 작은 에피소드로 나누어야 한다.

  1. 각 에피소드마다 다섯 가지 유형의 꽃을 무작위로 선택
  2. 각 유형에 대해 다섯 개의 이미지를 선택
  3. 총 5개 클래스의 5개 이미지 총 25개의 이미지로 에피소드가 구성
  4. 모델이 모든 클래스의 예시를 충분히 볼 때까지 다양한 조합으로 이 과정을 여러 번 반복
  5. 모델은 이전에 본 적이 없는 새로운 사례에 대한 데이터가 거의 주어지지 않더라도 일반화하고 정확하게 예측하는 방법을 학습

특정 예제를 암기하는 것이 아닌 여러 작은 작업에 패턴을 학습하여 일반화 가능

Knowledge distillation

지식 증류는 크고 복잡한 모델(교사 모델)에서 더 작고 빠른 모델(학생 모델)로 지식을 이전하기 위해 딥 러닝에서 사용되는 기술이다. 일반적으로 딥러닝 모델에는 많은 매개변수가 있으며 상당한 컴퓨팅 리소스가 필요하다. 이는 작업에서 우수한 성능으로 이어질 수 있지만, 실제 애플리케이션에서 모델을 사용하기 어렵거나 비실용적으로 만들 수도 있다. 지식 증류는 교사 모델에서 학습한 지식을 학생 모델로 전송하여 이 문제를 해결한다. 여기에는 두 모델을 특정 작업에 대해 학습시키되, 특정 입력에 대해 두 모델의 출력 간의 유사성을 장려하는 추가 손실 함수를 사용하는 것이 포함된다. 두 네트워크가 훈련 중에 특정 입력에 대해 유사한 출력을 생성한다면 새로운 데이터가 주어졌을 때에도 유사한 출력을 생성할 수 있어야 한다는 것이다. 지식 증류를 적용하면 일반적으로 더 작은 학생 네트워크가 더 큰 학생 네트워크와 비슷하거나 더 나은 성능을 내면서도 크기와 복잡성이 감소하여 추론 시간이 더 효율적이다. 전반적으로 지식 증류는 정확도를 크게 떨어뜨리지 않으면서도 큰 신경망에 비해 계산 비용이 적게 드는 작지만 정확한 신경망을 훈련할 수 있게 해준다.

예시

이미지를 1000개의 서로 다른 카테고리로 정확하게 분류할 수 있는 크고 복잡한 신경망이 있다고 가정

이 모델은 너무 크고 느려서 모바일 디바이스나 기타 저전력 플랫폼에서는 사용할 수 없다. 모델을 더 효율적으로 만들려면 지식 증류를 사용하여 지식을 더 작은 학생 네트워크로 옮길 수 있다. 두 네트워크를 동일한 작업(예: 이미지 분류)으로 훈련시키되, 특정 입력에 대한 출력 간의 유사성을 장려하는 추가 손실 함수를 추가한다. 학습 중에 교사 모델은 각 입력 이미지에 대한 일반 출력 확률에 더해 소프트 타깃을 생성합니다. 이러한 소프트 타깃은 기존의 분류 작업에서처럼 단일한 인코딩 레이블이 아니라 가능한 모든 클래스에 대한 확률 분포입니다. 그런 다음 학생 네트워크는 예측된 확률이 실제 확률과 얼마나 가까운지 측정하는 교차 엔트로피 손실 함수를 사용하여 학습하는 동안 이러한 소프트 타겟과 실제 레이블을 통해 학습합니다. 이렇게 함으로써 크기와 복잡성이 줄어들어 계산 비용이 적게 들면서 교사 모델의 행동을 모방하려고 시도합니다. 이 접근 방식을 사용하여 학습된 결과 학생 모델은 원래 교사 모드보다 훨씬 작지만 중요한 기능을 학습했기 때문에 새로운 데이터가 제공될 때 비슷하거나 더 나은 성능을 발휘한다.

논문 저자들은 ProtoCF 방식이 전반적으로 추천 정확도 측면에서 기존 방법보다 뛰어나며 (Recall@50 5% 향상), 특히 상호작용이 거의 없는 롱테일 아이템의 경우(Recall@50 60~80% 향상) 더욱 효과적이라는 것을 보여준다.

1. 서론

NCF(Neural Collaborative Filtering)는 발전을 가져왔지만 인기 있는 항목에 편항되어 있고 상호 작용이 드문 롱테일 항목에는 어려움을 겪고 있다. 이러한 상호작용이 없는 롱테일 문제를 메타 학습을 통해 해결하는 새로운 접근 방법인 ProtoCF를 제안한다. 저자들은 실험을 통해 ProtoCF가 전체 추천에서 최신 접근 방법보다 우수한 성능을 보이는 동시에 상호작용이 20개 미만인 꼬리 항목에 대해서도 상당한 정확도를 얻을 수 있음을 보여준다.

1

대부분의 추천시틈은 인기 있는 아이템에 초점을 맞추고 덜 인기 있는 아이템은 무시하는 방식이다. 이는 개인화를 제한하고 덜 인기 있는 품목을 판매하는 비즈니스가 고객을 유치하기 어렵게 만들기 때문에 문제가 될 수 있다. 다양성을 높이고 잘 알려지지 않은 카테고리에서 더 많은 수익을 창출하기 위해 ‘롱테일’ 아이템에 더 많은 주의를 기울이는 시스템을 만들것을 제안한다.

다양성과 수익윽 높이기 위해 롱테일 아이템을 추천하는 것은 중요하다. 추천 시스템의 영향력이 커지면서 추천 시스템에서 추천하지 않는 롱테일 아이템에 대한 차별에 대한 윤리적 우려가 제기되고 있다.

Empirical evidence of long-tail challenges(롱테일 과제에 대한 경험적 증거)

  • 롱테일 아이템의 순위를 정학하게 매기는 데 어려움이 있다. 인기도에 따라 아이템 인벤토리를 두 세트(인기, 비인기)로 나누고 각 세트 내에서 개인화된 순위를 평가한 결과 순위 간에 상당한 성능 차이가 있음을 발견했다. 이는 현재의 추천 시스템이 롱테일 아이템에 대한 충분한 표현이 부족하다는 것을 말해준다.
2

모델이 항목의 순위를 얼마나 잘 매기는 지 결과 롱테일 아이템에 대한 순위 정확도가 낮음

롱테일 아이템을 추천하는 문제를 해결하기 위한 이전 시도정규화분포적 선행사라는 2가지 전략에 중점을 두었다. 그러나 이러한 방법은 정적 가설과 과적한 가능성으로 인해 롱테일 아이템이 포함한 대규모 아이템들(인벤토리)에는 효과적이지 않을 수 있다.

  1. 정규화
    • 항목 간 연관성 또는 동시 발생을 사용
  2. 분포적 선행
    • 잠재 공간에서 아이템 선호도에 대한 가정을 포함
    • 특정 유형의 아이템은 그 특성이나 특징에 따라 사용자가 선호하는 경향이 있다고 가정
      • 액션영화와 상호작용을 했다면 사용자가 액션영화를 선호하는 경향이 있다고 가정

롱테일 아이템을 개선하기 위한 방법이 있다. 한 가지 접근 방식은 아이템 속성이나 지식 그래프와 같은 외부 정보를 사용하는 것이지만, 이는 한계가 있으며, 다양한 시나리오에 적용되지 않을 수 있다. 대신, 저자는 외부 정보에 의존하지 않고 딥 러닝 신경 모델 기반 추천자를 사용하여 롱테일 항목에 대한 개인화된 추천을 할 것을 제안한다.(ProtoCF)

신경망 기반 추천 시스템을 사용하여 인기 있는 항목에 대한 지식을 추출하고 이 지식을 이전하여 덜 인기 있는 항목에 대해 학습한다. 모델 학습중에 덜 인기 있는 아이템과의 상호 작용을 시물레이션하기 위해 episodic few-shot learning이라는 방법을 사용한다. 인기 있는 아이템의 상호작용을 하위 샘플링함으로써 덜 인기 있는 아이템과의 상호작용의 희소성을 극복할 수 있다.

—# 하위 샘플링(Sub-sampling)

데이터가 풍부한 헤드(인기 있는)아이템에서 상호작용을 하위 샘플링 한다는 것은 인기 있는 아이템에서 일부 상호작용만 무작위로 선택하여 보관한다는 것을 의미한다.

ProtoCF는 기본 추천자를 사전 학습시켜 아이템과 아이템 간의 관계를 학습한 다음, few-shot 러닝을 사용하여 사용자와 아이템에 대한 공유 메트릭 공간을 생성하며 각각에 대한 프로토타입을 생성하는 방법을 학습한다.

❗ 정리 : 인기도가 낮고 상호 작용이 낮은 아이템(롱테일)은 추천하는 데 다르는 어려움이 존재한다. 현재의 신경망 모델은 우수한 성능을 보이지만 롱테일 아이템에 대해서는 정확도가 낮다. 이 문제를 해결하기 위해 정규화, 분포적 선행 방법등이 있지만 한계점이 있다. 논문 저자들은 메타러닝을 이용한 ProtoCF 방법을 추천하며 이 방법은 최신 기술보다 롱테일 문제에 있어서 높은 정확도를 보여준다.
2. 관련 작업

해당 섹터에서는 롱테일 아이템을 추천은 데이터의 희소성으로 인한 어려움에 대해 간략하게 설명하며 정규화 및 데이터 증강과 같은 다양한 기법을 통해 데이터 희소성 문제를 해결하는 데 중점을 둔다.

Neural Collaborative Filtering

NCF는 사용자 아이템 상호작용의 과거 데이터를 사용하여 잠재 공간에서 사용자-아이템 표현을 학습한다. NCF는 비선형 표현, 신경 상호작용 모델링, 그래프 기반 표현 학습등이 있다. 그러나 이러한 개선에도 불구하고 롱테일 아이템에 대한 과제는 여전히 남아 있다.

Sparsity-aware Recommendation

상호 작용 데이터가 희서하면 롱테일 아이템은 정확하게 추천되지 않는 ‘상호 작용 희소성’ 문제가 발생한다. 이 문제를 해결하기 위한 한 가지 접근 방식은 유사성에 따라 아이템을 함께 그룹화하는 클러스터링이다. 하지만 클러스터링은 도움이 되지 않는 결과를 초래하고 개인화를 저하시킬 수 있다.

희소성 문제를 해결하기 위한 다양한 기법

  • 정규화 기법

    CF모델을 사용하여 아이템 동시 발생 행렬을 인수 분해하거나 가우시안 사전을 정규화 기법으로 사용하는 기법

  • 적대적 정규화 또는 등급 생성

    적대적 정규화 또는 등급 생성과 같은 기법을 통한 “데이터 증강”

하지만 이러한 기법에는 제한적인 가정이 있거나 계산 비용이 많이 들어 대규모 인벤토리(아이템)에는 적합하지 않을 수 있다.

Few-Shot Learning

소수 샷 학습은 제한된 수의 학습 예제로 새로운 작업을 빠르게 학습할 수 있는 모델을 설계하는 것으로 메타 학습은 여러 분야에서 성공을 거두었다

메타 학습을 사용하여 상호작용이 없는 신규 사용자를 위한 추천을 개선하는 방법과 모델 매개변수를 조정하고 사용자/아이템 기본 설정에 맞게 조정하는 다양한 방법이 있다. 하지만 이러한 방법은 사용자와 아이템간의 상호작용이 제한되어 있을 때 추천을 만드는 시나리오에는 적용되지 않는다.

메트릭 학습 접근법

거리 기반 학습은 모델이 별도의 조정 없이 새로운 작업에 일반화하는데 사용할 수 있는 메트릭 공간을 학습할 수 있도록 하는 방법이다.

ProtoCF는 롱테일 아이템을 맞춤 추천하기 위해 제한된 훈련 예제로 새로운 작업을 빠르게 학습할 수 있는 기능인 단발성 학습을 사용한다. 해당 방법은 신경 기반 추천자와 소수 샷 추천자가 포함된 2단계 프로세스를 제안하여 롱테일 아이템의 정보 표현을 학습한다.

기존의 소수 샷 학습 방법은 제한된 클래스에 초점을 맞췄지만, 이 새로운 접근 방식은 추천 애플리케이션의 방대한 아이템 인벤토리를 위해 설계되었다. 이는 롱테일 아이템 추천을 위한 거리 기반 소수 샷 학습에 대한 첫 번째 연구이다.

❗ 정리: 기존의 접근 방식은 한계점이 명학하여 메타러닝 방식이 제안되었다. 하지만 메타러닝도 기존의 콜드 스타투 추천 방식은 대부분 부수적인 정보가 있는 제로 샷 시나리오를 다루었지만 롱테일 상호작용 시나리오에서는 적용하기 어려웠고 이를 해결하고자 ProtoCF 학습이 나왔다 해당 방법은 별도의 적응 없이 새로운 작업에 일반화 할수 있는 메트릭 공간을 학습하는 것으로 롱테일 아이템 추천을 위한 메트릭 기반 few-shot 러닝에 최초의 연구이다.
3. 문제 정의

해당 섹터에서는 롱테일 아이템으로 알려진 특정 아이템에 대한 상호 작용이 부족한 추천 시스템의 문제를 설명한다.

목표는 사용자에게 롱테일 아이템을 추천할 수 있는 개인화된 추천 시스템을 만드는 것이다. 이 접근 방식에는 사용자-아이템 표현을 학습하기 위해 신경 기반 추천자를 학습 시킨 다음, 기본 추천자로부터 지식을 추출하고 전달하여 소수 추천 항목을 학습하는 few-shot 추천자가 포함된다. 또한 기존의 신경 추천은 롱테일 아이템에 대해 좋은 성과를 내기 어려우며, 제안된 접근 방식은 전체 아이템 세트에서 합리적인 성능을 유지하면서 아이템에 대한 성능을 개선하는 것을 목표로 한다.

4. ProtoCF 프레임워크

이 섹션에서는 신경 기반 추천자(RB)를 훈련시켜 헤드 아이템에 대한 사용자 표현을 학습하고 아이템 간의 관계를 추론하는 few-shot 추천을 위한 ProtoCF를 사용한다. 그런 다음 RB에서 지식을 추출하고 전달하여 few-shot 아이템을 추천하는 few-shot 추천자(RF)를 제시한다.

4-1 Neural Base Recommender(신경 기반 추천자 - RB)

롱테일 아이템 추천을 개선하기 위해 고안된 ProtoCF는 2단계 프로세스를 사용한다.

먼저 ‘base recommender’가 인기 있는 항목에 대한 사용자 선호도를 학습한 다음 ‘few-shot recommender’가 이 지식을 사용하여 덜 인기 있는 항목에 대한 추천을 한다. base recommender는 신경망을 사용하여 사용자와 아이템의 표현을 학습하고, few-shot recommnder는 이 정보를 사용하여 개인화된 추천을 생성한다.

  • 4-1-1 사용자-아이템 표현 학습

사용자와 아이템의 표현을 학습하기 위해 CF에는 행렬 인수분해, VAE, GNN등 다양한 전략들이 있다. 이 섹션에서는 특히 사용자와 아이템의 임베딩을 각각 학습하는 선호도 인코더 FU와 FI에 중점을 둔다. 선호도 인코더는 사용자와 아이템 모두에 대한 임베딩을 학습하는 신경망 모델이다. 이러한 임베딩은 사용자와 아이템의 관련성을 나타내는 점수를 계산하는 데 사용된다.

정리

일반적인 방법 중 하나는 행렬 인수분해로, 사용자-아이템 상호 작용의 큰 행렬을 장르나 가격대 같은 잠재 요인(또는 특징)을 나타내는 작은 행렬로 분해하는 것입니다. 또 다른 방법으로 언급된 자동 인코더는 신경망을 사용하여 입력 데이터를 저차원 임베딩으로 압축하는 동시에 각 항목이나 사용자의 선호도에 대한 중요한 정보를 보존합니다. 이 논문에서는 대신 '선호도 인코더'인 FU와 FI를 사용할 것을 제안합니다. 이 또한 사용자(hu)와 항목(hi) 모두에 대한 임베딩을 학습하는 신경망 모델입니다. 이러한 모델의 목표는 여전히 동일합니다. 다른 유사한 아이템/사용자와의 과거 상호작용을 기반으로 특정 사용자가 해당 아이템을 선호할 가능성을 나타내는 점수를 계산하는 것입니다.

  • 4-1-2 훈련 목표

사용자와 아이템의 상호 작용을 기반으로 관련성 순위를 학습하는 RB라는 신경 기반 추천자의 아키텍처는 다음과 같다. Fint함수는 사용자-아이템 관련성을 측정하며 일반적으로 내부 제품을 사용하여 모델링된다. RB는 코사인 유사성을 사용하여 학습되며 모델 추론 중에 헤드 및 테일 아이템의 통합 추천을 용이하게 한다.

4-2 Few-Shot Item Recommender

이 섹션에서는 롱테일 아이템 추천을 다음과 같이 공식화 한다.

각 꼬리 아이템마다 최대 k명의 상호작용한 사용자로 구성된다 (일반적으로 K ≈ 5 ~ 20)

4-2-1 Few-shot task 공식화

ProtoCF는 헤드 아이템과 테일 아이템을 모두 포함한 전체 아이템 세트에서 작동하는 메타 학습 작업 모음을 통해 에피소드 학습에 기반을 두고 있다. 메타트레닝 중에 few-shot 아이템의 상호 작용 분포를 시물레이션하고 메타트레이닝과 추론 간의 일관성을 보장하여 롱테이 아이템간의 분포 불일치를 해소한다.

에피소드 학습은 각 에피소드가 소수의 예제로 구성된 에피소드 모음에서 학습하는 머신러닝의 한 유형이다.

에피소드식 few-shot 학습을 사용하여 꼬리 부분의 항목 순위를 모방하도록 설계된 다양한 메타트레닝 작업 모음에서 메타 지식을 추출하는 것을 포함한다. 또한 지식 증류에 기반한 새로운 아키첵처를 제안하여 신경 기반 추천자로부터 지식을 추출, 연관, 전송한다

이 프로세스는 사용자와의 몇 가지 상호작용을 기반으로 아이템을 추천하도록 추천 시스템을 훈련하는 것이다. 시스템은 각각 아이템의 하위 집합과 해당 항목과 상호 작용한 사용자 집합으로 구성된 작업에 학습된다. 이 시스템은 support set에 있는 일부 사용자의 상호 작용을 기반으로 아이템을 추천하는 방법을 학습하고, 하위 집합에 있는 항목과의 상호 작용을 기반으로 사용자 쿼리 집합에 아이템을 추천하는 능력을 평가 받는다.

4-2-2 Initial Item Prototype

사용자 및 아이템의 공유 메트릭 공간을 학습하여 support set(사용자와의 상호 작용)을 기반으로 각 항목에 대한 표현을 만드는 프로세스이다. 메트릭 공간은 프로토타입 벡터를 중심으로 각 아이템의 상호 작용한 사용자를 클러스트링한다. 사용자 선호도 인코더는 각 사용자를 잠재 임베딩 공간에 매핑하도록 정의된다. RB로부터 지식을 전송하기 위해 few-shot 인코더는 사전 학습된 인코더에서 초기화된 매개변수를 갖지만 학습 가능한 매개변수로 매개변수화된다. 각 아이템에 대한 프로토타입 벡터는 임베드된 support set의 평균 벡터로 계산된다.

4-2-3 헤드-테일 메타 지식 이전

신경망이 획득한 항목간 관계 지식을 활용한다.

신경 기반 추천자로부터 지식을 추출,연관 전송하는 지식 증류 기반 기법을 사용한다. RB는 항목과 항목 간의 관계 지식을 획득하고, 이를 귀납적 편향으로 사용하여 아이템 프로토타입 파이를 향상시킨다. 하지만 ProtoCF로 직접 지식을 전송하는 것은 계산적으로 어렵다.

이 문제를 극복하기 위해 저자들은 RB가 발견한 아이템-아이템 근접성 지식을 간결하게 표현하고, 이 지식을 전송하여 아이템 프로토타입을 개선한다. RB의 잠재 공간에서 아이템과 아이템 간의 근접성을 모델링하는 기준 벡터로 M개의 그룹 임베딩 ZM세트를 학습한다. 그룹 임베딩 ZM은 RB에 의해 식별된 겹치는 아이템 클러스터의 판별 중심이다.

임베딩과 어텐션 메커니즘을 사용하여 서로 다른 중심을 연관시키고 희박한 지원 집합을 가진 소수 샷 항목에 지식을 전달합니다. 이 방법은 또한 지식 증류 전략을 사용하여 항목과 항목의 관계를 포착하는 컴팩트한 그룹 임베딩을 학습합니다. 이 전략은 학생 모델이 교사 모델의 예측을 모방하도록 장려하고 교사 모델의 항목 근접도 분포를 모방하도록 학습시킵니다. 두 분포는 과제 수준 유사성 간의 교차 엔트로피를 최소화하여 정렬되며, 교사 모델에서 그룹 임베딩으로 지식을 전달하고 나머지 프레임워크와 함께 학습됩니다.

4-2-4 Item Prototype Fusion via Neural Gating

프로토타입을 사용하여 관련 아이템에서 전달된 지식을 캡처하고 게이팅 매커니즘을 사용하여 이러한 프로토타입에서 두드러진 특징 차원을 선택한다. 신경 게이팅 레이어는 가장 중요한 특징을 선택하도록 조정하고 최종 게이팅된 아이템 프로토타입을 생성한다.

4-2-5 Few-shot Rocommender 학습

해당 학습은 프로토타입과 아이템 간의 유사성을 측정하여 수행된다. 이 방법은 추천과 실측 데이터 상호 작용간의 nagative log-likelighood를 최소화한다. 전체 손실은 few-shot 추천 손실과 지식 증류 손실로 구성된다.

4-2-6 추론

RB(기본 추천자)와 희귀 아이템 데한 RF(few-shot 추천자)라는 두 가지 우형을 사용한다. 이 시스템은 사용자와 아이템 프로토타입 간의 유사성을 계산하여 각 사용자에 대한 추천을 생성한다. 이 시스템은 보간 방식을 사용하여 두 추천자의 추천을 결합하여 효과의 균형을 맞춘다.

5. 결론

ProtoCF는 소수학습을 사용하여 롱테일 아이템에 대한 임베딩을 학습한다. 또한 ProtoCF는 임의의 기본 추천자로부터 지식을 전송하여 상호작용이 거의 없는 항목에 대한 프로토타입을 구축한다. 실험 결과, 전체적으로 5%, 훈련 상호작용이 20개 미만인 롱테일 아이템의 경유 60~80퍼까지 최신 기술을 능가하는 것으로 나타났다.

1
10주차 (NGCF 직접 구현)

10주차 (NGCF 직접 구현)

CF + GNN 모델 조사

그래프 구성의 차이

일반적인 GNN모델들은 단순하게 사용자-아이템 이분 그래프에 직접 GNN을 적용하면 다음과 같은 문제

  1. 학습하기에 표현이 충분하지 않음
  2. 대규모 그래프의 경유 높은 계산 비용 발 생

아래 모델들은 개선

GCCF(Graph Convolutional Collaborative Filtering)

기존의 사용자와 아이템 간의 상호작용 행렬을 분해하여 임베딩 하는 방식의 MF 모델을 개선하기 위하여 나온 모델로 그래프 구조를 고려하여 사용자와 아이템 간의 상호작용을 모델링한다. GCCF에서는 그래프를 구성할 때, 사용자와 아이템을 노드로 표현하고, 이들 간의 상호작용을 엣지로 표현합니다. 이 그래프를 바탕으로 GCN을 적용하여 사용자와 아이템의 임베딩을 학습하고, 이를 기반으로 새로운 사용자와 아이템에 대한 예측을 수행한다.

사용자 아이템 상호작용
노드 노드 엣지

GCCF는 여러 개의 레이어로 이루어진 GCN을 사용하여 그래프 구조를 반영한.다ㄷ이 때, GCN의 각 레이어에서는 아래 레이어에서 정보를 전파하여 현재 레이어의 임베딩을 업데이트한다. 이를 통해 모델은 상호작용이 일어난 사용자와 아이템의 정보뿐만 아니라, 이들 간의 관계 정보도 함께 고려하여 임베딩을 학습한다.

Multi-GCCF

복수의 도메인에서의 추천을 위한 모델

이전에는 도메인 간의 차이 때문에 각 도메인마다 개별적인 모델을 만들어야 했으나, Multi-GCCF는 여러 도메인 간의 유사성을 고려하여 한번에 학습이 가능하다.

Multi-GCCF 모델은 GCCF 모델에서 발전된 모델로, 다수의 도메인에서의 유저-아이템 평가행렬을 이용하여 학습한다. 이때 모든 도메인이 같은 잠재공간(latent space)에서의 임베딩을 공유하게 된다.

  • 각 모델에서 학습한 잠재공간을 가중치를 부여하여 더하고, 최종적으로 평균을 내어 공유하는 방법

Multi-GCCF는 아래와 같은 특징을 가집니다.

  1. 다수의 도메인에서의 평가행렬을 고려하여 하나의 모델로 학습할 수 있다.
  2. 공통 잠재공간에서의 임베딩을 공유하게 되어, 서로 다른 도메인에서의 유저-아이템 간의 관계를 고려하여 추천을 수행할 수 있다.
  3. 다양한 종류의 데이터(텍스트, 이미지 등)를 적절하게 통합하여 추천에 반영할 수 있다.

하지만 Multi-GCCF 모델의 학습이 상대적으로 더 어려우며, 도메인의 수가 많을수록 성능 저하가 발생할 가능성이 있다 따라서 모델 구성 및 하이퍼파라미터 설정에 대한 연구가 필요하다.

DGCF(Deep Graph Collaborative Filtering)

사용자와 아이템을 독립적으로 임베딩한 후 이를 조합하여 예측하는 방식과 달리, 사용자와 아이템이 모두 그래프 상의 노드로 표현되며 이를 바탕으로 예측을 수행한다.

DGCF 모델은 사용자-아이템 그래프와 아이템-아이템 그래프를 모두 활용한다. 사용자-아이템 그래프는 사용자가 아이템을 구매한 기록을 가지고 노드를 형성하며, 아이템-아이템 그래프는 같은 사용자가 여러 아이템을 구매한 경우 해당 아이템들 간의 연결을 형성하여 그래프를 생성합니다.

DGCF 모델은 두 개의 임베딩 행렬과 연결 예측 함수로 이루어져 있다. 첫 번째 임베딩 행렬은 사용자를, 두 번째 임베딩 행렬은 아이템을 나타냅니다. 임베딩 행렬은 초기에 무작위로 초기화되며, 학습 과정에서 각 노드의 임베딩 벡터가 업데이트된다. 연결 예측 함수는 두 개의 임베딩 벡터를 입력으로 받아 두 벡터의 내적을 통해 예측 점수를 계산한다.

DGCF 모델에서는 GNN을 사용하여 사용자-아이템 그래프와 아이템-아이템 그래프를 학습한다. GNN은 각 노드의 임베딩 벡터를 입력으로 받아 해당 노드와 이웃한 노드들의 정보를 고려한 새로운 임베딩 벡터를 출력한다. 이 때, DGCF 모델에서는 사용자와 아이템에 대해 서로 다른 GNN을 사용한다.

❗ Multi-GCCF 와 DGCF 는 원본 그래프에 2홉 이웃 간의 에지를 추가하여 사용자-사용자 및 아이템-아이템 그래프를 얻는다.
11주차 (커스텀 데이터를 이용하여 NGCF와 LightCGN 학습 및 비교)

11주차 (커스텀 데이터를 이용하여 NGCF와 LightCGN 학습 및 비교)

NGCF Light GCN
장점 - 사용자-아이템 상호작용을 고려하여 협업 필터링 성능 향상 - 경량화된 모델 구조로 효율적인 학습과 추론 가능
- 잠재 요인 모델과 그래프 신경망을 융합하여 성능 향상 - 그래프 구조를 활용하여 사용자-아이템 관계 모델링
- 깊은 상호작용을 고려하여 예측 성능 향상 - 계산 및 메모리 효율성이 높은 대규모 데이터셋 지원
- 스케일링에 강건한 모델 - 설계 및 구현이 간단하여 사용자 적용 용이
단점 - 계산 복잡성이 높아 학습에 시간이 오래 걸릴 수 있음 - 상호작용을 잘 모델링하지 못할 수 있음
- 메모리 사용량이 크며 메모리 효율성에 제약이 있을 수 있음 - 추가 정보 고려가 제한적
- 모델 설계와 튜닝이 복잡하여 구현과 관리가 어려울 수 있음 - 복잡한 상호작용 모델링에 제한적
NGCF Light GCN
모델 구조 심층 신경망 구조 경량화된 그래프 합성곱 네트워크
그래프 활용 사용자-아이템 상호작용 데이터와 그래프 정보 활용 그래프 구조만 활용
추가 정보 활용 그래프 정보와 함께 사용자-아이템 상호작용 모델링 추가 정보 없이 그래프만 활용
계산 및 메모리 효율성 계산과 메모리 사용량이 큼 계산 및 메모리 효율성이 높음
모델 설계와 구현 설계와 튜닝이 복잡함 설계와 구현이 간단함
대규모 데이터셋 학습 스케일링에 비교적 강건함 대규모 데이터셋에서 효율적인 학습이 가능함
  • NGCF의 추가 정보
  1. 사용자 프로필 정보: 사용자의 나이, 성별, 관심사 등과 같은 프로필 정보를 활용할 수 있다. 이러한 정보는 사용자의 취향을 파악하고 유사한 사용자를 찾는 데 도움을 줄 수 있다.
  2. 아이템 속성 정보: 아이템의 카테고리, 장르, 키워드 등과 같은 속성 정보를 활용할 수 있다. 이러한 정보는 아이템 간의 유사성을 계산하고 사용자의 관심사와 일치하는 아이템을 추천하는 데 활용될 수 있다.
  3. 시간 정보: 사용자-아이템 상호작용이 발생한 시간 정보를 활용할 수 있다. 예를 들어, 최근 상호작용은 더 중요하게 고려할 수 있다.

STEP 1 : 데이터세트 준비(커스텀, 예제)

추천시스텀에 사용하는 다양한 데이터셋 정보 https://think-tech.tistory.com/6

커스텀 데이터(책 평가 데이터)
  • 커스텀 데이터

Book-Crossing 데이터셋은 책정보(제목, 작가 연도 등), 평점, 유저 정보(유저 id, age)로 구성된 책 추천 데이터세트로 kaggle에서 다운로드 가능하다.

Book-Crossing Dataset

### 책 평가 데이터 정보
- Book : 책번호(International Standard Book Number), 
					제목, 작가, 출판년도, 출판사로 구성된 271379개의 데이터

- Users :User ID, 나이로 총 2개로 구성된 278859개의 데이터

- Ratings : User ID, 책번호, 평가 점수 3개로 구성된 1149780개의 데이터
  • 커스텀 데이터 - 전처리
  1. NGCF와 LightGCN에는 평점 데이터만 필요함으로 Ragins.csv만 가져와서 처리한다.

  2. 평가 점수가 0점인 데이터는 제거하며 이로인해 인덱스가 꼬이므로 인덱스를 다시 리셋해준다.

  3. Ragins에 있는 유저와 아이템에 대한 인덱스를 처음부터 재정렬해준다.

  4. NGCF와 Light GCN에서 요구하는 데이터로 변경하기 위해서 user, item, label의 형태로 변경

    1

    전처리가 끝난 Ragins 데이터

ratings = pd.read_csv("/content/Ratings.csv", sep=';',encoding="latin-1")
ratings = ratings[ratings['Rating'] != 0] # rating이 0인 것은 제거
ratings.reset_index(drop=True, inplace=True) #-- 결측으로 인한 인덱스 리셋

ratings['User-ID'] = pd.factorize(ratings['User-ID'])[0]
ratings['ISBN'] = pd.factorize(ratings['ISBN'])[0]

#-- 데이터가 너무 커서 10000개의 데이터로 자르고 테스트 진행
data = ratings
# data = ratings.truncate(after=433670/2)
data.set_axis(labels=['user','item','label'],axis=1,inplace=True)
#-- 데이터 레이블은 user, item label으로 변경해줘야 random_split 가능하다.
data.head()
  • 커스텀 데이터 - 학습 데이터 스플릿
  1. 학습데이터, 평가데이터, 테스트 데이터를 8:1:1 비율로 random_split
  2. NGCF, LightGCN을 사용하기 위해서는 라이브러리에서 요구하는 DatasetPure 자료구조로 변경

—# DatasetPure는 딕셔너리 자료구조의 형태이며 다음과 같은 Key, Value값이 매핑되어 있다.

DatasetPure.n_users ⇒ n개의 유저 데이터

DatasetPure.n_items ⇒ n개의 아이템 데이터

train_data, eval_data, test_data = random_split(data, multi_ratios=[0.8, 0.1, 0.1])
print("train_data is : ", train_data.shape)
print("eval_data  is : ", eval_data.shape)
print("test_data  is : ", test_data.shape)
"""
train_data is :  (346936, 3)
eval_data  is :  (25774, 3)
test_data  is :  (25672, 3)
"""
train_data, data_info = DatasetPure.build_trainset(train_data)
eval_data = DatasetPure.build_evalset(eval_data)
test_data = DatasetPure.build_testset(test_data)
print(data_info)  #n_users: 68291, n_items: 158953, data density: 0.0032 %
예제 데이터(MovieLens)
  • 예제 데이터

MovieLens 데이터셋은 영화(ID, 제목, 연도, 장르 등), 평점, 유저 정보(유저 id, 성별, age)로 구성된 영화 데이터셋으로 다양한 모델에서 사용하는 대표적인 데이터셋이다. 다양한 크기의 데이터가 있지만 예제에서는 100000개의 데이터셋을 사용

MovieLens

###  영화 평가 데이터 정보

- 유저 : UserID, Gender, Age, Occupation, Zip-code

- 아이템(영화) : MovieID, Title, (Year), Genres(19가지)

- 평점 : UserID, MovieID, Rating(1~5), TimeStam
  • 예제 데이터 - 전처리
  1. NGCF와 LightGCN에는 평점 데이터만 필요함으로 Raging 데이터만 가져와서 처리한다.
  2. NGCF와 Light GCN에서 요구하는 데이터로 변경하기 위해서 user, item, labe, time의 형태로 변경
#-- movies data
movie_data = pd.read_csv("/content/mydrive/MyDrive/UROP/DataSet/sample_movielens_rating.dat", 
										sep="::",
	                  names=["user", "item", "label", "time"])
  • 예제 데이터 - 학습 데이터 스플릿
  1. 학습데이터, 평가데이터, 테스트 데이터를 8:1:1 비율로 random_split
  2. NGCF, LightGCN을 사용하기 위해서는 라이브러리에서 요구하는 DatasetPure 자료구조로 변경

—# DatasetPure는 딕셔너리 자료구조의 형태이며 다음과 같은 Key, Value값이 매핑되어 있다.

DatasetPure.n_users ⇒ n개의 유저 데이터

DatasetPure.n_items ⇒ n개의 아이템 데이터

# split whole data into three folds for training, evaluating and testing
movie_train_data, movie_eval_data, movie_test_data = random_split(movie_data, multi_ratios=[0.8, 0.1, 0.1])

movie_train_data, movie_data_info = DatasetPure.build_trainset(movie_train_data)
movie_eval_data = DatasetPure.build_evalset(movie_eval_data)
movie_test_data = DatasetPure.build_testset(movie_test_data)
print(movie_data_info)  # n_users: 5894, n_items: 3253, data sparsity: 0.4172 %
커스텀 데이터 예제 데이터
유저 데이터 68291(명) 5894(명)
아이템 데이터 158953(개) 3253(개)
평가한 데이터 비율 0.0032(%) 0.4172(%)

STEP 2 : 모델 정의(NGCF, LightGCN)

커스텀 데이터(NGCF, LightGCN)

NGCF

라이브러리에서 설명하는 NGCF의 인자들의 기본값은 다음과 같다.

class libreco.algorithms.NGCF(
			task, data_info, loss_type='cross_entropy', 
			embed_size=16, n_epochs=20, lr=0.001, lr_decay=False, epsilon=1e-08, 
			amsgrad=False, reg=None, batch_size=256, num_neg=1, node_dropout=0.0, 
			message_dropout=0.0, hidden_units=(64, 64, 64), margin=1.0, 
			sampler='random', seed=42, device='cuda', lower_upper_bound=None
)
  • task : 해당 알고리즘이 수행할 Task에 대한 인자이며 NGCF는 오직 *ranking만 가능하다.*
  • data_info : 데이터 전처리에서 만들어준 data정보이며 라이브러리에서 원하는 자료구조이다.
  • loss_type : {'cross_entropy', 'focal', 'bpr', 'max_margin'}, 가 있으며 기본적으로는 'cross_entropy'가 설정되어 있으며 focal은 직접 만들 손실함수이며 정확도가 높다고 함
  • embed_size : 임베딩 사이즈에 대한 인자이며 기본값 16이며 32으로 변경하여 사용
  • n_epochs : 트레이닝 epochs이며 50으로 변경하여 사용
  • lr : 학습률
  • lr_decay : 학습률 감쇠를 사용할 지 여부이며 기본값 False로 설정
  • epsilon : 아담 옵티마이저의 수치 안정성을 향상시키기 위한 값으로 기본 값 1e-08 설정
  • amsgrad : Adam(Adaptive Moment Estimation)의 개선 버전 사용 여부 기본 값 False 설정
  • reg : Regularization이며 None으로 설정
  • batch_size : 학습 배치 사이즈이며 기본값 256으로 하면 너무 25 에폭 학습 3시간 이상 소요 되므로 LightCGN과 마찬가지로 2048로 변경하여 설정
  • num_neg : 각각의 Positive smaple에 대한 Negative smaple의 개수이며 기본값 1로 설정
  • node_dropout : node의 드롭아웃 설정이며 기본값 0.0을 적용하여 드롭아웃 적용 안함
  • message_dropout : message의 드롭아웃 설정이며 마찬가지로 적용 안함
  • hidden_units : 임베딩 전파에서 사용하는 히든 레이어의 개수이며 반드시 튜플 형태로 전달
  • margin : 최대 마진손실에 사용되는 마진값으로 기본 값 1설정
  • sampler : random', 'unconsumed', 'popular가 있으며 기본값인 random 샘플 적용
    • random : 무작위 샘플링
    • unconsumed : 대상 사용자가 소비하지 않은 아이템을 샘플링
    • popular : 인기있는 아이템을 negative하게 샘플링
  • lower_upper_bound : 하한 및 상한 평가점수를 제한할 수 있음, 기본값인 제한없음 설정
ngcf = NGCF(
    task="ranking", 
    #-- task = NGCF 모델은 오직 ranking task만 가능하다.
    data_info=data_info,
    #-- data_info = 위에서 얻은 데이터의 정보
    loss_type="focal",
    #-- loss_type = {'cross_entropy', 'focal', 'bpr', 'max_margin'}, default: 'cross_entropy'}
    embed_size=32,
    #-- 임베딩의 벡터 사이즈
    n_epochs=50,
    lr=1e-3,
    batch_size=2048,
    epsilon=1e-08,
    num_neg=1,
    hidden_units=(64, 64, 64),
    margin=1.0, 
    sampler='random',
    #-- sampler ({'random', 'unconsumed', 'popular'}, default: 'random')
    seed=42,
    device="cuda",
)

LightGCN

라이브러리에서 설명하는 LightGCN의 인자들의 기본값은 다음과 같다.

class libreco.algorithms.LightGCN(
			task, data_info, loss_type='cross_entropy', 
			embed_size=16, n_epochs=20, lr=0.001, lr_decay=False, epsilon=1e-08, 
			amsgrad=False, reg=None, batch_size=256, num_neg=1, dropout_rate=0.0, 
			n_layers = 3, margin=1.0, 
			sampler='random', seed=42, device='cuda', lower_upper_bound=None
)
  • task : 해당 알고리즘이 수행할 Task에 대한 인자이며 ranking으로 설정
  • data_info : 데이터 전처리에서 만들어준 data정보이며 라이브러리에서 원하는 자료구조이다.
  • loss_type : {'cross_entropy', 'focal', 'bpr', 'max_margin'}, 가 있으며 기본적으로는 'bpr'가 설정되어 있으며 기본값 설정
  • embed_size : 임베딩 사이즈에 대한 인자이며 기본값 16 설정
  • n_epochs : 트레이닝 epochs이며 50으로 변경하여 사용
  • lr : 학습률
  • lr_decay : 학습률 감쇠를 사용할 지 여부이며 기본값 False로 설정
  • epsilon : 아담 옵티마이저의 수치 안정성을 향상시키기 위한 값으로 기본 값 1e-08 설정
  • amsgrad : Adam(Adaptive Moment Estimation)의 개선 버전 사용 여부 기본 값 False 설정
  • reg : Regularization이며 None으로 설정
  • batch_size : 학습 배치 사이즈이며 기본값 256으로 하면 너무 25 에폭 학습 3시간 이상 소요 되므로 LightCGN과 마찬가지로 2048로 변경하여 설정
  • num_neg : 각각의 Positive smaple에 대한 Negative smaple의 개수이며 기본값 1로 설정
  • dropout_rate : 노드가 드롭아웃 될 확률이며 기본값 0으로 설정
  • n_layers : GCN 레이어의 개수이며 기본값 3으로 설정
  • margin : 최대 마진손실에 사용되는 마진값으로 기본 값 1설정
  • sampler : random', 'unconsumed', 'popular가 있으며 기본값인 random 샘플 적용
    • random : 무작위 샘플링
    • unconsumed : 대상 사용자가 소비하지 않은 아이템을 샘플링
    • popular : 인기있는 아이템을 negative하게 샘플링
  • lower_upper_bound : 하한 및 상한 평가점수를 제한할 수 있음, 기본값인 제한없음 설정
Books_lightgcn = LightGCN(
    task="ranking",
    data_info=data_info,
    loss_type="bpr",
    embed_size=16,
    n_epochs=50,
    lr=1e-3,
    batch_size=2048,
    num_neg=1,
    device="cuda",
)
예제 데이터(NGCF, LightGCN)

NGCF

라이브러리에서 설명하는 NGCF의 인자들의 기본값은 다음과 같다.

class libreco.algorithms.NGCF(
			task, data_info, loss_type='cross_entropy', 
			embed_size=16, n_epochs=20, lr=0.001, lr_decay=False, epsilon=1e-08, 
			amsgrad=False, reg=None, batch_size=256, num_neg=1, node_dropout=0.0, 
			message_dropout=0.0, hidden_units=(64, 64, 64), margin=1.0, 
			sampler='random', seed=42, device='cuda', lower_upper_bound=None
)
  • task : 해당 알고리즘이 수행할 Task에 대한 인자이며 NGCF는 오직 *ranking만 가능하다.*
  • data_info : 데이터 전처리에서 만들어준 data정보이며 라이브러리에서 원하는 자료구조이다.
  • loss_type : {'cross_entropy', 'focal', 'bpr', 'max_margin'}, 가 있으며 기본적으로는 'cross_entropy'가 설정되어 있으며 focal은 직접 만들 손실함수이며 정확도가 높다고 함
  • embed_size : 임베딩 사이즈에 대한 인자이며 기본값 16이며 32으로 변경
  • n_epochs : 트레이닝 epochs이며 50으로 변경하여 사용
  • lr : 학습률
  • lr_decay : 학습률 감쇠를 사용할 지 여부이며 기본값 False로 설정
  • epsilon : 아담 옵티마이저의 수치 안정성을 향상시키기 위한 값으로 기본 값 1e-08 설정
  • amsgrad : Adam(Adaptive Moment Estimation)의 개선 버전 사용 여부 기본 값 False 설정
  • reg : Regularization이며 None으로 설정
  • batch_size : 학습 배치 사이즈이며 기본값 256으로 하면 너무 25 에폭 학습 3시간 이상 소요 되므로 LightCGN과 마찬가지로 2048로 변경하여 설정
  • num_neg : 각각의 Positive smaple에 대한 Negative smaple의 개수이며 기본값 1로 설정
  • node_dropout : node의 드롭아웃 설정이며 기본값 0.0을 적용하여 드롭아웃 적용 안함
  • message_dropout : message의 드롭아웃 설정이며 마찬가지로 적용 안함
  • hidden_units : 임베딩 전파에서 사용하는 히든 레이어의 개수이며 반드시 튜플 형태로 전달
  • margin : 최대 마진손실에 사용되는 마진값으로 기본 값 1설정
  • sampler : random', 'unconsumed', 'popular가 있으며 기본값인 random 샘플 적용
    • random : 무작위 샘플링
    • unconsumed : 대상 사용자가 소비하지 않은 아이템을 샘플링
    • popular : 인기있는 아이템을 negative하게 샘플링
  • lower_upper_bound : 하한 및 상한 평가점수를 제한할 수 있음, 기본값인 제한없음 설정
movie_ngcf = NGCF(
    task="ranking", 
    #-- task = NGCF 모델은 오직 ranking task만 가능하다.
    data_info= movie_data_info,
    #-- data_info = 위에서 얻은 데이터의 정보
    loss_type="focal",
    #-- loss_type = {'cross_entropy', 'focal', 'bpr', 'max_margin'}, default: 'cross_entropy'}
    embed_size=32,
    #-- 임베딩의 벡터 사이즈
    n_epochs=50,
    lr=1e-3,
    batch_size=2048,
    epsilon=1e-08,
    num_neg=1,
    hidden_units=(64, 64, 64),
    margin=1.0, 
    sampler='random',
    #-- sampler ({'random', 'unconsumed', 'popular'}, default: 'random')
    seed=42,
    device="cuda",
)

LightGCN

라이브러리에서 설명하는 LightGCN의 인자들의 기본값은 다음과 같다.

class libreco.algorithms.LightGCN(
			task, data_info, loss_type='cross_entropy', 
			embed_size=16, n_epochs=20, lr=0.001, lr_decay=False, epsilon=1e-08, 
			amsgrad=False, reg=None, batch_size=256, num_neg=1, dropout_rate=0.0, 
			n_layers = 3, margin=1.0, 
			sampler='random', seed=42, device='cuda', lower_upper_bound=None
)
  • task : 해당 알고리즘이 수행할 Task에 대한 인자이며 ranking으로 설정
  • data_info : 데이터 전처리에서 만들어준 data정보이며 라이브러리에서 원하는 자료구조이다.
  • loss_type : {'cross_entropy', 'focal', 'bpr', 'max_margin'}, 가 있으며 기본적으로는 'bpr'가 설정되어 있으며 기본값 설정
  • embed_size : 임베딩 사이즈에 대한 인자이며 기본값 16 설정
  • n_epochs : 트레이닝 epochs이며 50으로 변경하여 사용
  • lr : 학습률
  • lr_decay : 학습률 감쇠를 사용할 지 여부이며 기본값 False로 설정
  • epsilon : 아담 옵티마이저의 수치 안정성을 향상시키기 위한 값으로 기본 값 1e-08 설정
  • amsgrad : Adam(Adaptive Moment Estimation)의 개선 버전 사용 여부 기본 값 False 설정
  • reg : Regularization이며 None으로 설정
  • batch_size : 학습 배치 사이즈이며 기본값 256으로 하면 너무 25 에폭 학습 3시간 이상 소요 되므로 LightCGN과 마찬가지로 2048로 변경하여 설정
  • num_neg : 각각의 Positive smaple에 대한 Negative smaple의 개수이며 기본값 1로 설정
  • dropout_rate : 노드가 드롭아웃 될 확률이며 기본값 0으로 설정
  • n_layers : GCN 레이어의 개수이며 기본값 3으로 설정
  • margin : 최대 마진손실에 사용되는 마진값으로 기본 값 1설정
  • sampler : random', 'unconsumed', 'popular가 있으며 기본값인 random 샘플 적용
    • random : 무작위 샘플링
    • unconsumed : 대상 사용자가 소비하지 않은 아이템을 샘플링
    • popular : 인기있는 아이템을 negative하게 샘플링
  • lower_upper_bound : 하한 및 상한 평가점수를 제한할 수 있음, 기본값인 제한없음 설정
Movies_lightgcn = LightGCN(
    task="ranking",
    data_info= movie_data_info,
    loss_type="bpr",
    embed_size=16,
    n_epochs=50,
    lr=1e-3,
    batch_size=2048,
    num_neg=1,
    device="cuda",
)

STEP 3 : 모델 학습(NGCF, LightGCN)

커스텀 데이터(NGCF, LightGCN)

NGCF

라이브러리에서 설명하는 학습 함수의 인자들의 기본값은 다음과 같다.

fit(
		train_data, neg_sampling, 
		verbose=1, shuffle=True, eval_data=None, 
		metrics=None, k=10, eval_batch_size=8192, 
		eval_user_num=None, num_workers=0
)
  • train_data : 학습 데이터
  • neg_sampling : 학습을 위한 negative 샘플링 여부
  • verbose : 1보다 큰 값을 넣으면 학습 도중 학습에 대한 자세한 여부가 나옴
  • shuffle : 학습 데이터 셔플
  • eval_data : 평가를 위한 eval data사용 여부이며 평가 데이터를 넣어줘야 함
  • metrics : 평가 지표 리스트
  • K : 상위 몇 개의 데이터를 추천할지
  • eval_batch_size : 평가 데이터 배치 사이즈
  • eval_user_num : 평가할 사용자 수, 양수로 설정하면 평가 데이터에서 무작위로 사용자를 샘플링
  • num_workers : 학습 데이터 로딩에 사용할 서브 프로세스 수입니다. 0은 멀티프로세싱보다 느린 메인 프로세스에서 데이터를 로드한다는 의미이며 기본 값 0 설정
ngcf.fit(
    train_data,
    neg_sampling=True,
    verbose=1,
    #-- verbose가 1보다 크면 자세한 내용이 학습 중 프린트
    eval_data=eval_data,
    metrics=["loss", "roc_auc", "precision", "recall", "ndcg"],
)

LightGCN

라이브러리에서 설명하는 LightGCN의 인자들의 기본값은 다음과 같다.

fit(
		train_data, neg_sampling, verbose=1, shuffle=True, 
		eval_data=None, metrics=None, k=10, eval_batch_size=8192, 
		eval_user_num=None, num_workers=0)
  • train_data : 학습 데이터
  • neg_sampling : 학습을 위한 negative 샘플링 여부
  • verbose : 1보다 큰 값을 넣으면 학습 도중 학습에 대한 자세한 여부가 나옴
  • shuffle : 학습 데이터 셔플
  • eval_data : 평가를 위한 eval data사용 여부이며 평가 데이터를 넣어줘야 함
  • metrics : 평가 지표 리스트
  • K : 상위 몇 개의 데이터를 추천할지
  • eval_batch_size : 평가 데이터 배치 사이즈
  • eval_user_num : 평가할 사용자 수, 양수로 설정하면 평가 데이터에서 무작위로 사용자를 샘플링
  • num_workers : 학습 데이터 로딩에 사용할 서브 프로세스 수입니다. 0은 멀티프로세싱보다 느린 메인 프로세스에서 데이터를 로드한다는 의미이며 기본 값 0 설정
Books_lightgcn.fit(
    train_data,
    neg_sampling=True,
    verbose=2,
    eval_data=eval_data,
    metrics=["loss", "roc_auc", "precision", "recall", "ndcg"],
)
예제 데이터(NGCF, LightGCN)

NGCF

라이브러리에서 설명하는 학습 함수의 인자들의 기본값은 다음과 같다.

fit(
		train_data, neg_sampling, 
		verbose=1, shuffle=True, eval_data=None, 
		metrics=None, k=10, eval_batch_size=8192, 
		eval_user_num=None, num_workers=0
)
  • train_data : 학습 데이터
  • neg_sampling : 학습을 위한 negative 샘플링 여부
  • verbose : 1보다 큰 값을 넣으면 학습 도중 학습에 대한 자세한 여부가 나옴
  • shuffle : 학습 데이터 셔플
  • eval_data : 평가를 위한 eval data사용 여부이며 평가 데이터를 넣어줘야 함
  • metrics : 평가 지표 리스트
  • K : 상위 몇 개의 데이터를 추천할지
  • eval_batch_size : 평가 데이터 배치 사이즈
  • eval_user_num : 평가할 사용자 수, 양수로 설정하면 평가 데이터에서 무작위로 사용자를 샘플링
  • num_workers : 학습 데이터 로딩에 사용할 서브 프로세스 수입니다. 0은 멀티프로세싱보다 느린 메인 프로세스에서 데이터를 로드한다는 의미이며 기본 값 0 설정
movie_ngcf.fit(
    movie_train_data,
    neg_sampling=True,
    verbose=1,
    #-- verbose가 1보다 크면 자세한 내용이 학습 중 프린트
    eval_data=movie_eval_data,
    metrics=["loss", "roc_auc", "precision", "recall", "ndcg"],
)

LightGCN

라이브러리에서 설명하는 LightGCN의 인자들의 기본값은 다음과 같다.

fit(
		train_data, neg_sampling, verbose=1, shuffle=True, 
		eval_data=None, metrics=None, k=10, eval_batch_size=8192, 
		eval_user_num=None, num_workers=0)
  • train_data : 학습 데이터
  • neg_sampling : 학습을 위한 negative 샘플링 여부
  • verbose : 1보다 큰 값을 넣으면 학습 도중 학습에 대한 자세한 여부가 나옴
  • shuffle : 학습 데이터 셔플
  • eval_data : 평가를 위한 eval data사용 여부이며 평가 데이터를 넣어줘야 함
  • metrics : 평가 지표 리스트
  • K : 상위 몇 개의 데이터를 추천할지
  • eval_batch_size : 평가 데이터 배치 사이즈
  • eval_user_num : 평가할 사용자 수, 양수로 설정하면 평가 데이터에서 무작위로 사용자를 샘플링
  • num_workers : 학습 데이터 로딩에 사용할 서브 프로세스 수입니다. 0은 멀티프로세싱보다 느린 메인 프로세스에서 데이터를 로드한다는 의미이며 기본 값 0 설정
Movies_lightgcn.fit(
    movie_train_data,
    neg_sampling=True,
    verbose=2,
    eval_data=movie_eval_data,
    metrics=["loss", "roc_auc", "precision", "recall", "ndcg"],
)

STEP 4 : 모델 평가(NGCF, LightGCN) ⇒ 해야함

커스텀 데이터(NGCF, LightGCN)

NGCF

라이브러리에서 설명하는 evaluate 함수의 인자들의 기본값은 다음과 같다.

evaluate(
    model=ngcf,
    data=test_data,
    neg_sampling=True,
    metrics=["loss", "roc_auc", "precision", "recall", "ndcg"],
)
  • metrics

    • loss: 모델이 예측한 값과 실제 값의 차이를 측정하는 지표
    • ROC-AUC: 이진 분류 문제에서 많이 사용되는 평가지표
    • Precision: 모델이 추천한 상위 n개의 아이템 중 실제로 선호하는 아이템의 비율을 나타내는 지표
    • Recall: 실제 선호하는 아이템 중 모델이 추천한 상위 n개의 아이템의 비율을 나타내는 지표
    • NDCG: 추천 시스템에서 상위 n개의 추천 아이템이 실제 선호하는 아이템에 얼마나 가까운지 측정
    - loss': 0.5198264936902364,
    - roc_auc': 0.8554520877989423,
    - precision': 0.003323974277692238,
    - recall': 0.015959115378586058,
    - ndcg': 0.015552468873683481}

2211 User 에게 Item 5를 추천 예측과 2211 User에게 상위 7개 아이템을 추천

user_id = 2211
item_id = 5
# predict preference of user 2211 to item 5
print(ngcf.predict(user=user_id, item=item_id)[0]*100)
# recommend 7 items for user 2211
arr = ngcf.recommend_user(user=user_id, n_rec=7).get(user_id)
42.98396408557892
array([   40,  3105, 70325,  5346,   644, 37161, 13188])

#-- 임베딩 레이어를 32로 증가 + 크로스 엔트로피 사용
85.98052859306335
array([  33, 1567,  644, 2542, 2551, 1782, 2540]
""'
40    Miss Zukas and the Raven's Dance
Name: Title, dtype: object
3105    Transvergence
Name: Title, dtype: object
70325    Sophocles Papas: The Guitar, His Life
Name: Title, dtype: object
5346    A New Song (Mitford Years (Paperback))
Name: Title, dtype: object
644    Out of the Deep I Cry : A Clare Fergusson/Russ...
Name: Title, dtype: object
37161    Tears of the Night Sky (Dragonlance Chaos Wars...
Name: Title, dtype: object
13188    The Ark
Name: Title, dtype: object
"""

LightGCN

라이브러리에서 설명하는 LightGCN evaluate의 인자들의 기본값은 다음과 같다.

evaluate(
    model=Books_lightgcn,
    data=test_data,
    neg_sampling=True,
    metrics=["loss", "roc_auc", "precision", "recall", "ndcg"],
)
  • loss: 모델이 예측한 값과 실제 값의 차이를 측정하는 지표
  • ROC-AUC: 이진 분류 문제에서 많이 사용되는 평가지표
  • Precision: 모델이 추천한 상위 n개의 아이템 중 실제로 선호하는 아이템의 비율을 나타내는 지표
  • Recall: 실제 선호하는 아이템 중 모델이 추천한 상위 n개의 아이템의 비율을 나타내는 지표
  • NDCG: 추천 시스템에서 상위 n개의 추천 아이템이 실제 선호하는 아이템에 얼마나 가까운지 측정
- 'loss': 0.7724095080219578,
- 'roc_auc': 0.8023822472142028,
- 'precision': 0.003142831265283942,
- 'recall': 0.015095409642973187,
- 'ndcg': 0.01568260847208047}
User 2211에게 아이템 5 추천한 결과
- 96.57081961631775
- {2211: array([ 819,  206, 1567, 1200,  629,  643, 3120])}
예제 데이터(NGCF, LightGCN)

NGCF

라이브러리에서 설명하는 evaluate 함수의 인자들의 기본값은 다음과 같다.

evaluate(
    model=movie_ngcf,
    data=movie_test_data,
    neg_sampling=True,
    metrics=["loss", "roc_auc", "precision", "recall", "ndcg"],
)
  • metrics

    • loss: 모델이 예측한 값과 실제 값의 차이를 측정하는 지표
    • ROC-AUC: 이진 분류 문제에서 많이 사용되는 평가지표
    • Precision: 모델이 추천한 상위 n개의 아이템 중 실제로 선호하는 아이템의 비율을 나타내는 지표
    • Recall: 실제 선호하는 아이템 중 모델이 추천한 상위 n개의 아이템의 비율을 나타내는 지표
    • NDCG: 추천 시스템에서 상위 n개의 추천 아이템이 실제 선호하는 아이템에 얼마나 가까운지 측정
    - 'loss': 0.5240759073181087,
    - 'roc_auc': 0.8248748577474521,
    -'precision': 0.007502708559046588,
    - 'recall': 0.03739828759936866,
    - 'ndcg': 0.03341032193692315}

2211 User 에게 Item 5를 추천 예측과 2211 User에게 상위 7개 아이템을 추천

user_id = 2211
item_id = 5
# predict preference of user 2211 to item 110
print(movie_ngcf.predict(user=user_id, item=item_id)[0]*100)
# recommend 7 items for user 2211
arr = movie_ngcf.recommend_user(user=user_id, n_rec=7).get(user_id)
arr
38.170212507247925
array([1234,  913,  969,  919, 1270, 2396, 1230])

LightGCN

라이브러리에서 설명하는 LightGCN evaluate의 인자들의 기본값은 다음과 같다.

evaluate(
    model=Movies_lightgcn,
    data=movie_test_data,
    neg_sampling=True,
    metrics=["loss", "roc_auc", "precision", "recall", "ndcg"],
)
  • loss: 모델이 예측한 값과 실제 값의 차이를 측정하는 지표
  • ROC-AUC: 이진 분류 문제에서 많이 사용되는 평가지표
  • Precision: 모델이 추천한 상위 n개의 아이템 중 실제로 선호하는 아이템의 비율을 나타내는 지표
  • Recall: 실제 선호하는 아이템 중 모델이 추천한 상위 n개의 아이템의 비율을 나타내는 지표
  • NDCG: 추천 시스템에서 상위 n개의 추천 아이템이 실제 선호하는 아이템에 얼마나 가까운지 측정
- eval log_loss: 0.7936
- eval roc_auc: 0.7750
- eval precision@10: 0.0079
- eval recall@10: 0.0379
- eval ndcg@10: 0.0386

User 2211에게 아이템 5 추천한 결과
- 84.41452383995056
- {2211: array([2858,  260, 2028, 1210, 1196, 1270, 2997])}

2211 User 에게 Item 5를 추천 예측과 2211 User에게 상위 7개 아이템을 추천

커스텀 데이터 2211 ⇒ Item 5 User 2211
NGCF 42.9%
array([ 40, 3105, 70325, 5346, 644, 37161, 13188])
LightGCN 96% array([ 819, 206, 1567, 1200, 629, 643, 3120])}
예제 데이터 2211 ⇒ Item 5 User 2211
NGCF 31.5%
array([ 913, 1252, 912, 1148, 1073, 919, 923])
LightGCN 84.4% array([2858, 260, 2028, 1210, 1196, 1270, 2997])}
12주차 (NGCF metrics 분석, Recommender FrameWork)

12주차 (NGCF metrics 분석, Recommender FrameWork)

라이브러리에서 사용하는 NGCF Metrics 분석

  1. Pointwise: Pointwise 방법론은 각 사용자-아이템 쌍에 대해 개별적으로 예측 값을 생성하고 평가하는 방법입니다. 이 방법론에서는 각 쌍의 예측 값과 실제 평가 값 사이의 차이를 최소화하거나 평가 지표를 최적화하는 것이 목표입니다. 대표적인 예로는 평점 예측 문제가 있습니다. 사용자-아이템 쌍에 대해 평점을 예측하고, 실제 평점과의 차이를 최소화하여 모델을 학습합니다.
  2. Listwise: Listwise 방법론은 추천 목록 전체에 대해 평가하고 최적화하는 방법입니다. 이 방법론에서는 추천 목록의 순서를 고려하여 평가 지표를 최적화하거나 추천 목록 자체를 최적화하는 것이 목표입니다. Listwise 방법론은 사용자의 선호도 순서를 모델에 반영하여 개별적인 아이템 예측보다 전체 목록의 품질을 개선하는 데 초점을 둡니다. 예를 들어, 검색 결과의 순위매기기나 추천 시스템에서의 Top-N 추천 문제에 사용될 수 있습니다.

Pointwise와 Listwise 방법론은 모델의 학습과 평가 방식에서 차이가 있습니다. Pointwise는 개별 예측 값에 대해 최적화를 수행하고, Listwise는 전체 추천 목록에 대한 최적화를 수행합니다. 선택은 문제의 성격과 데이터의 특성에 따라 달라질 수 있습니다

eval_result = evaluate(
    model=movie_ngcf,
    data=movie_test_data,
    neg_sampling=True,
    metrics=["loss", "precision", "recall"],
    k = 10
)
POINTWISE_METRICS = {
    "loss",
    "log_loss",
    "balanced_accuracy",
    "roc_auc",
    "pr_auc",
    "roc_gauc",
}
LISTWISE_METRICS = {"precision", "recall", "map", "ndcg", "coverage"}
RANKING_METRICS = POINTWISE_METRICS | LISTWISE_METRICS
  • precision_at_k 함수

    def precision_at_k(y_true, y_reco, k):
        common_items = set(y_reco).intersection(y_true)
        #-- 추천된 아이템 중 실제로 사용자가 선호하는(구매하거나 클릭한) 아이템의 갯수 / k
        return len(common_items) / k

    TF/(TF + FT) → 전체 아이템 중 실제 아이템 비율을 다시 K로 나눔

#-- 실제값들과 예측값, 샘플링된 유저들, k를 인자로 listwise_scores 계산

  • listwise_scores(fn, y_trues, y_recos, users, k) 함수

    scores = list()
    for u in users:
    	y_true = y_true_lists[u]
    	y_reco = y_reco_lists[u]
    	scores.append(fn(y_true, y_reco, k))
    return np.mean(scores)

    precision_at_k를 통해 얻은 값들을 scores 리스트에 모두 저장 후 평균값 계산

LightGCN 프레임워크 만들기

13주차 (LightGCN 분석, 순차적 추천 LSTM)

13주차 (LightGCN 분석, 순차적 추천 LSTM)

Light GCN 코드 실행

pip install LibRecommender
#-- https://github.com/massquantity/LibRecommender#references 라이브러리

scatter와 sparse 라이브러리를 다운로드 하기 위해서는 컴퓨터의 cuda 버전을 확인하고 그 버전에 맞는 라이브러리를 다운로드해야 합니다.

print(torch.__version__
print(torch.version.cuda)
#-- 위 명령어로 컴퓨터의 Cuda 버전을 확인 colab은 cu118을 사용하고 있으므로 cu118.html사용
2.0.1+cu118
11.8

버전 확인 후 라이브러리 다운로드

pip install torch-geometric
pip install -q git+https://github.com/snap-stanford/deepsnap.git
"""
DeepSNAP는 그래프 신경망(Graph Neural Networks)을 구축하고 훈련하기 위한 파이썬 패키지
DeepSNAP은 PyTorch 기반의 그래프 신경망 라이브러리 PyG(PyTorch Geometric)에서 영감을 받아 개발되었다. DeepSNAP은 PyG와 유사한 인터페이스를 제공하여 그래프 데이터를 로드하고 전처리할 수 있다.
"""
pip install -U -q PyDrive
pip install torch_scatter -f https://data.pyg.org/whl/torch-2.0.0+cu118.html
pip install torch-sparse -f https://data.pyg.org/whl/torch-2.0.0+cu118.html
  1. 필요 라이브러리 import(여기서부터 파이썬 코드)
import numpy as np
import pandas as pd
from libreco.data import random_split, DatasetPure
from libreco.algorithms import LightGCN  # pure data, algorithm NGCF
from libreco.evaluation import evaluate
#-- Libreco 라이브러리 LightGCN을 위한 import

import random
from tqdm import tqdm
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from torch import nn, optim, Tensor
from torch_sparse import SparseTensor, matmul
from torch_geometric.utils import structured_negative_sampling
from torch_geometric.data import download_url, extract_zip
from torch_geometric.nn.conv.gcn_conv import gcn_norm
from torch_geometric.nn.conv import MessagePassing
from torch_geometric.typing import Adj
#-- import required modules Blog LightGCN을 위한 import 
  1. 데이터 준비
  • 데이터를 다운받고 압축해제 후 ratings.csv와 movies.csv의 path를 저장
# download the dataset
url = 'https://files.grouplens.org/datasets/movielens/ml-latest-small.zip'
extract_zip(download_url(url, '.'), '.')

rating_path = '/content/ml-latest-small/ratings.csv'
item_path = "/content/ml-latest-small/movies.csv"
라이브러리 설치를 제외한 통코드이며 데이터를 다운로드 후 패스 지정까지 했다고 가정
#-- 데이터 전처리

def libco_LightGCN_data(path, data_frame, ratio = [0.8, 0.1, 0.1], excluded_user=None):
  #-- data
  data = pd.read_csv(path)
  data.rename(columns = {data_frame[0] : "user", data_frame[1]:"item", data_frame[2]:"label",},
              inplace = True)
  
  # print(data.head(10))
  #-- 테스트를 위한 특정 유저의 가장 앞에 있는 데이터 n개 삭제
  if excluded_user is not None:
    user_data = data[data["user"] == excluded_user]
    user_data_5stars = user_data[user_data["label"] == 5.0]  # Filter the data with a rating of 5.0
    if not user_data_5stars.empty:
      excluded_number = 15
      user_data_5stars = user_data_5stars.head(excluded_number)
      data = data.drop(user_data_5stars.index)

  # print(data.head(10))

  # split whole data into three folds for training, evaluating and testing
  train_data, eval_data, test_data = random_split(data, multi_ratios= ratio)

  train_data, data_info = DatasetPure.build_trainset(train_data)
  eval_data = DatasetPure.build_evalset(eval_data)
  test_data = DatasetPure.build_testset(test_data)
  tet_data = [train_data, eval_data, test_data]
  print(data_info)  # n_users: 5894, n_items: 3253, data sparsity: 0.4172 %  
  return tet_data, data_info
  #-- Libco -- 

def blog_LightGCN_data(path, data_frame, rating_threshold =4, excluded_user=None):

  #-- 유저, 아이템 매핑
  rating_data = pd.read_csv(path[0], index_col= data_frame[0])
  item_data = pd.read_csv(path[1], index_col= data_frame[1])
  data = pd.read_csv(path[0])

  print("1번째 데이터 삭제하기 전")
  print(rating_data.head(10))

  #-- 테스트를 위한 특정 유저의 가장 앞에 있는 데이터 삭제

  if excluded_user is not None:
    user_data = data[data[data_frame[0]] == excluded_user]
    user_data_5stars = user_data[user_data[data_frame[2]] == 5.0]  # Filter the data with a rating of 5.0r_user_data[r_user_data[data_frame[2]] == 5.0]  # Filter the data with a rating of 5.0

    r_user_data =  rating_data[rating_data.index == excluded_user].reset_index()

    if not user_data_5stars.empty:
      excluded_number = 15
      user_data_5stars = user_data_5stars.head(excluded_number)     
      r_user_data_5stars = r_user_data[r_user_data[data_frame[2]] == 5.0]
      data = data.drop(user_data_5stars.index)
      
      row_index = []
      for i in range(0,excluded_number):
        row_index.append(r_user_data_5stars.iloc[i].name)

      rating_data = rating_data.reset_index().drop(row_index[0])
      rating_data.set_index(data_frame[0], inplace=True)
      for i in range(1, len(row_index)) :
        rating_data = rating_data.reset_index().drop(row_index[i]-i)
      # rating_data = rating_data.drop(rating_data.iloc.nam)
        rating_data.set_index(data_frame[0], inplace=True)
  
  print("\n\n\n1번째 데이터 삭제하기 후")
  print(rating_data.head(10))

  # # rating_data의 컬럼 확인
  # print(rating_data.head(5))

  user_mapping = {index: i for i, index in enumerate(rating_data.index.unique())}
  item_mapping = {index: i for i, index in enumerate(item_data.index.unique())}

  #-- 매핑 데이터로 edge연결
  edge_index = None
  src = [user_mapping[index] for index in data[data_frame[0]]]
  dst = [item_mapping[index] for index in data[data_frame[1]]]
  edge_attr = torch.from_numpy(data[data_frame[2]].values).view(-1, 1).to(torch.long) >= rating_threshold
  
  edge_index = [[], []]
  for i in range(edge_attr.shape[0]):
    if edge_attr[i]:
        edge_index[0].append(src[i])
        edge_index[1].append(dst[i])
  edge_index = torch.tensor(edge_index)

  #-- 데이터 분할 8:1:1
  num_users, num_movies = len(user_mapping), len(item_mapping)
  num_interactions = edge_index.shape[1]
  all_indices = [i for i in range(num_interactions)]

  train_indices, test_indices = train_test_split(
      all_indices, test_size=0.2, random_state=1)
  val_indices, test_indices = train_test_split(
      test_indices, test_size=0.5, random_state=1)

  train_edge_index = edge_index[:, train_indices]
  val_edge_index = edge_index[:, val_indices]
  test_edge_index = edge_index[:, test_indices]

  print("Train Edge Data : ", train_edge_index.shape)
  print("Val Edge Data :", val_edge_index.shape)
  print("Test Edge Data : ", test_edge_index.shape)    

  #-- 연결된 엣지를 희소행렬로 변환
  # convert edge indices into Sparse Tensors: https://pytorch-geometric.readthedocs.io/en/latest/notes/sparse_tensor.html
  from torch_geometric.utils import train_test_split_edges

  train_sparse_edge_index = SparseTensor(row=train_edge_index[0], col=train_edge_index[1], sparse_sizes=(
      num_users + num_movies, num_users + num_movies))
  val_sparse_edge_index = SparseTensor(row=val_edge_index[0], col=val_edge_index[1], sparse_sizes=(
      num_users + num_movies, num_users + num_movies))
  test_sparse_edge_index = SparseTensor(row=test_edge_index[0], col=test_edge_index[1], sparse_sizes=(
      num_users + num_movies, num_users + num_movies))

  tet_edge_data = [edge_index, train_edge_index,val_edge_index, test_edge_index]  
  tet_sparse_data = [train_sparse_edge_index, val_sparse_edge_index, test_sparse_edge_index]
  return tet_edge_data, tet_sparse_data, user_mapping, item_mapping
#-- Blog --

#-- 모델 정의

def libco_LightGCN(data_info, embed_size =32, n_epochs = 20):
  lightgcn = LightGCN(
      task="ranking",
      data_info= data_info,
      loss_type="bpr",
      embed_size= embed_size,
      n_epochs=n_epochs,
      lr=1e-3,
      batch_size=2048,
      num_neg=1,
      device="cuda",
  )
  return lightgcn
#-- Libco --#

class Blog_LightGCN(MessagePassing):
      def __init__(self, num_users, num_items, embedding_dim=64, K=3, add_self_loops=False):
          super().__init__()
          self.num_users, self.num_items = num_users, num_items
          self.embedding_dim, self.K = embedding_dim, K
          self.add_self_loops = add_self_loops
          self.users_emb = nn.Embedding(
              num_embeddings=self.num_users, embedding_dim=self.embedding_dim) # e_u^0
          self.items_emb = nn.Embedding(
              num_embeddings=self.num_items, embedding_dim=self.embedding_dim) # e_i^0
          nn.init.normal_(self.users_emb.weight, std=0.1)
          nn.init.normal_(self.items_emb.weight, std=0.1)
      def forward(self, edge_index: SparseTensor):
          # compute \tilde{A}: symmetrically normalized adjacency matrix
          edge_index_norm = gcn_norm(
              edge_index, add_self_loops=self.add_self_loops)
          emb_0 = torch.cat([self.users_emb.weight, self.items_emb.weight]) # E^0
          embs = [emb_0]
          emb_k = emb_0

          # multi-scale diffusion
          for i in range(self.K):
              emb_k = self.propagate(edge_index_norm, x=emb_k)
              embs.append(emb_k)
          embs = torch.stack(embs, dim=1)
          emb_final = torch.mean(embs, dim=1) # E^K
          users_emb_final, items_emb_final = torch.split(
              emb_final, [self.num_users, self.num_items]) # splits into e_u^K and e_i^K
          # returns e_u^K, e_u^0, e_i^K, e_i^0
          return users_emb_final, self.users_emb.weight, items_emb_final, self.items_emb.weight

      def message(self, x_j: Tensor) -> Tensor:
          return x_j

      def message_and_aggregate(self, adj_t: SparseTensor, x: Tensor) -> Tensor:
          # computes \tilde{A} @ x
          return matmul(adj_t, x)

#-- 손실함수 bpr
def bpr_loss(users_emb_final, users_emb_0, pos_items_emb_final, pos_items_emb_0, neg_items_emb_final, neg_items_emb_0, lambda_val):
    reg_loss = lambda_val * (users_emb_0.norm(2).pow(2) +
                             pos_items_emb_0.norm(2).pow(2) +
                             neg_items_emb_0.norm(2).pow(2)) # L2 loss

    pos_scores = torch.mul(users_emb_final, pos_items_emb_final)
    pos_scores = torch.sum(pos_scores, dim=-1) # predicted scores of positive samples
    neg_scores = torch.mul(users_emb_final, neg_items_emb_final)
    neg_scores = torch.sum(neg_scores, dim=-1) # predicted scores of negative samples

    loss = -torch.mean(torch.nn.functional.softplus(pos_scores - neg_scores)) + reg_loss
    return loss

def blog_LightGCN(num_users, num_movies, embed_size):
  # defines LightGCN model
  model = Blog_LightGCN(num_users, num_movies, embedding_dim = embed_size)
  return model

#-- Blog --#

#-- 모델 학습
def libco_fit(model, train_data, eval_data):
  model.fit(
        train_data,
        neg_sampling=True,
        verbose=1,
        eval_data=eval_data,
        metrics=["loss", "roc_auc", "precision", "recall", "ndcg"],
    )
#-- Libco--#

# function which random samples a mini-batch of positive and negative samples
def sample_mini_batch(batch_size, edge_index):
    edges = structured_negative_sampling(edge_index)
    edges = torch.stack(edges, dim=0)
    indices = random.choices(
        [i for i in range(edges[0].shape[0])], k=batch_size)
    batch = edges[:, indices]
    user_indices, pos_item_indices, neg_item_indices = batch[0], batch[1], batch[2]
    return user_indices, pos_item_indices, neg_item_indices

# helper function to get N_u
def get_user_positive_items(edge_index):
    user_pos_items = {}
    for i in range(edge_index.shape[1]):
        user = edge_index[0][i].item()
        item = edge_index[1][i].item()
        if user not in user_pos_items:
            user_pos_items[user] = []
        user_pos_items[user].append(item)
    return user_pos_items

# computes recall@K and precision@K
def RecallPrecision_ATk(groundTruth, r, k):
    num_correct_pred = torch.sum(r, dim=-1)  # number of correctly predicted items per user
    # number of items liked by each user in the test set
    user_num_liked = torch.Tensor([len(groundTruth[i])
                                  for i in range(len(groundTruth))])
    recall = torch.mean(num_correct_pred / user_num_liked)
    precision = torch.mean(num_correct_pred) / k
    return recall.item(), precision.item()

# computes NDCG@K
def NDCGatK_r(groundTruth, r, k):
    assert len(r) == len(groundTruth)

    test_matrix = torch.zeros((len(r), k))

    for i, items in enumerate(groundTruth):
        length = min(len(items), k)
        test_matrix[i, :length] = 1
    max_r = test_matrix
    idcg = torch.sum(max_r * 1. / torch.log2(torch.arange(2, k + 2)), axis=1)
    dcg = r * (1. / torch.log2(torch.arange(2, k + 2)))
    dcg = torch.sum(dcg, axis=1)
    idcg[idcg == 0.] = 1.
    ndcg = dcg / idcg
    ndcg[torch.isnan(ndcg)] = 0.
    return torch.mean(ndcg).item()

# wrapper function to get evaluation metrics
def get_metrics(model, edge_index, exclude_edge_indices, k):
    user_embedding = model.users_emb.weight
    item_embedding = model.items_emb.weight

    # get ratings between every user and item - shape is num users x num movies
    rating = torch.matmul(user_embedding, item_embedding.T)

    for exclude_edge_index in exclude_edge_indices:
        # gets all the positive items for each user from the edge index
        user_pos_items = get_user_positive_items(exclude_edge_index)
        # get coordinates of all edges to exclude
        exclude_users = []
        exclude_items = []
        for user, items in user_pos_items.items():
            exclude_users.extend([user] * len(items))
            exclude_items.extend(items)

        # set ratings of excluded edges to large negative value
        rating[exclude_users, exclude_items] = -(1 << 10)

    # get the top k recommended items for each user
    _, top_K_items = torch.topk(rating, k=k)

    # get all unique users in evaluated split
    users = edge_index[0].unique()

    test_user_pos_items = get_user_positive_items(edge_index)

    # convert test user pos items dictionary into a list
    test_user_pos_items_list = [
        test_user_pos_items[user.item()] for user in users]

    # determine the correctness of topk predictions
    r = []
    for user in users:
        ground_truth_items = test_user_pos_items[user.item()]
        label = list(map(lambda x: x in ground_truth_items, top_K_items[user]))
        r.append(label)
    r = torch.Tensor(np.array(r).astype('float'))

    recall, precision = RecallPrecision_ATk(test_user_pos_items_list, r, k)
    ndcg = NDCGatK_r(test_user_pos_items_list, r, k)

    return recall, precision, ndcg
# wrapper function to evaluate model
def evaluation(model, edge_index, sparse_edge_index, exclude_edge_indices, k, lambda_val):
    # get embeddings
    users_emb_final, users_emb_0, items_emb_final, items_emb_0 = model.forward(
        sparse_edge_index)
    edges = structured_negative_sampling(
        edge_index, contains_neg_self_loops=False)
    user_indices, pos_item_indices, neg_item_indices = edges[0], edges[1], edges[2]
    users_emb_final, users_emb_0 = users_emb_final[user_indices], users_emb_0[user_indices]
    pos_items_emb_final, pos_items_emb_0 = items_emb_final[
        pos_item_indices], items_emb_0[pos_item_indices]
    neg_items_emb_final, neg_items_emb_0 = items_emb_final[
        neg_item_indices], items_emb_0[neg_item_indices]

    loss = bpr_loss(users_emb_final, users_emb_0, pos_items_emb_final, pos_items_emb_0,
                    neg_items_emb_final, neg_items_emb_0, lambda_val).item()

    recall, precision, ndcg = get_metrics(
        model, edge_index, exclude_edge_indices, k)

    return loss, recall, precision, ndcg
def blog_fit(model, blog_tet_edge_data, blog_tet_sparse_data, epochs = 50):
  # define contants
  ITERATIONS = epochs
  BATCH_SIZE = 2048
  LR = 1e-3
  ITERS_PER_EVAL = 300
  ITERS_PER_LR_DECAY = 300
  K = 10
  LAMBDA = 1e-6

  model = model.to(device)
  model.train()

  optimizer = optim.Adam(model.parameters(), lr=LR)
  scheduler = optim.lr_scheduler.ExponentialLR(optimizer, gamma=0.95)

  edge_index = blog_tet_edge_data[0].to(device)
  train_edge_index = blog_tet_edge_data[1].to(device)
  train_sparse_edge_index = blog_tet_sparse_data[0].to(device)

  val_edge_index = blog_tet_edge_data[2].to(device)
  val_sparse_edge_index = blog_tet_sparse_data[1].to(device)

  # training loop
  train_losses = []
  val_losses = []

  for iter in range(ITERATIONS):
      # forward propagation
      users_emb_final, users_emb_0, items_emb_final, items_emb_0 = model.forward(
          train_sparse_edge_index)

      # mini batching
      user_indices, pos_item_indices, neg_item_indices = sample_mini_batch(
          BATCH_SIZE, train_edge_index)
      user_indices, pos_item_indices, neg_item_indices = user_indices.to(
          device), pos_item_indices.to(device), neg_item_indices.to(device)
      users_emb_final, users_emb_0 = users_emb_final[user_indices], users_emb_0[user_indices]
      pos_items_emb_final, pos_items_emb_0 = items_emb_final[
          pos_item_indices], items_emb_0[pos_item_indices]
      neg_items_emb_final, neg_items_emb_0 = items_emb_final[
          neg_item_indices], items_emb_0[neg_item_indices]

      # loss computation
      train_loss = bpr_loss(users_emb_final, users_emb_0, pos_items_emb_final,
                            pos_items_emb_0, neg_items_emb_final, neg_items_emb_0, LAMBDA)

      optimizer.zero_grad()
      train_loss.backward()
      optimizer.step()

      if iter % ITERS_PER_EVAL == 0:
          model.eval()
          val_loss, recall, precision, ndcg = evaluation(
              model, val_edge_index, val_sparse_edge_index, [train_edge_index], K, LAMBDA)
          print(f"[Iteration {iter}/{ITERATIONS}] train_loss: {round(train_loss.item(), 5)}, val_loss: {round(val_loss, 5)}, val_recall@{K}: {round(recall, 5)}, val_precision@{K}: {round(precision, 5)}, val_ndcg@{K}: {round(ndcg, 5)}")
          train_losses.append(train_loss.item())
          val_losses.append(val_loss)
          model.train()

      if iter % ITERS_PER_LR_DECAY == 0 and iter != 0:
          scheduler.step()
  iters = [iter * ITERS_PER_EVAL for iter in range(len(train_losses))]

#-- 모델 평가

def libco_eval(model, test_data):
  result = evaluate(
      model=model,
      data=test_data,
      neg_sampling=True,
      metrics=["loss", "roc_auc", "precision", "recall", "ndcg"],
  )
  print(result)

#-- Libco --

def blog_eval(model, blog_tet_edge_data, blog_tet_sparse_data , K = 10):
  # evaluate on test set
  LAMBDA = 1e-6
  model.eval()
  test_edge_index = blog_tet_edge_data[3].to(device)
  test_sparse_edge_index = blog_tet_sparse_data[2].to(device)

  test_loss, test_recall, test_precision, test_ndcg = evaluation(
              model, test_edge_index, test_sparse_edge_index, [blog_tet_edge_data[1], blog_tet_edge_data[2]], K, LAMBDA)

  print(f"[test_loss: {round(test_loss, 5)}, test_recall@{K}: {round(test_recall, 5)}, test_precision@{K}: {round(test_precision, 5)}, test_ndcg@{K}: {round(test_ndcg, 5)}")
#-- Blog --

#-- 모델 예측

def lilco_predict(model, user_id, path, K = 10):
  df = pd.read_csv(path[1])
  rec_arr = model.recommend_user(user=user_id, n_rec=K).get(user_id)
  
  titles = []
  genres = []
  for i in rec_arr:
    title = df[df['movieId'] == i]['title']
    genre = df[df['movieId'] == i]['genres']
    titles.append(title)
    genres.append(genre)
    # print(f"title: {title}, genres: {genre} ")

  return titles, genres
#-- Libco  --#

def blog_predict(model, user_id ,TopK, path, user_mapping, item_mapping, edge_index):

  df = pd.read_csv(path[1])
  movieid_title = pd.Series(df.title.values,index=df.movieId).to_dict()
  movieid_genres = pd.Series(df.genres.values,index=df.movieId).to_dict()

  user_pos_items = get_user_positive_items(edge_index)
  user = user_mapping[user_id]
  e_u = model.users_emb.weight[user]
  scores = model.items_emb.weight @ e_u

  values, indices = torch.topk(scores, k=len(user_pos_items[user]) + TopK)

  movies = [index.cpu().item() for index in indices if index in user_pos_items[user]][:TopK]
  movie_ids = [list(item_mapping.keys())[list(item_mapping.values()).index(movie)] for movie in movies]
  titles = [movieid_title[id] for id in movie_ids]
  genres = [movieid_genres[id] for id in movie_ids]
 
  # print(f"Here are some movies that user {user_id} rated highly")
  # for i in range(TopK):
  #     print(f"title: {titles[i]}, genres: {genres[i]} ")

  movies = [index.cpu().item() for index in indices if index not in user_pos_items[user]][:TopK]
  movie_ids = [list(item_mapping.keys())[list(item_mapping.values()).index(movie)] for movie in movies]
  titles = [movieid_title[id] for id in movie_ids]
  genres = [movieid_genres[id] for id in movie_ids]

  # print(f"Here are some suggested movies for user {user_id}")
  return titles, genres

#-- 최종 함수

def libco(data_frame, path, embed_size, n_epochs, excluded_user):
  lib_tet_data, lib_data_info = libco_LightGCN_data(path[0], data_frame, excluded_user = excluded_user_id)
  lib_model = libco_LightGCN(lib_data_info, embed_size =embed_size, n_epochs = int(n_epochs/3)) 
  libco_fit(lib_model, lib_tet_data[0], lib_tet_data[1])
  libco_eval(lib_model, lib_tet_data[2])
  titles, genres = lilco_predict(lib_model,user_id, path, K = K)
  return titles,genres
  
def blog(data_frame, path, embed_size, n_epochs, excluded_user):
  blog_tet_edge_data, blog_tet_sparse_data, user_mapping, item_mapping = blog_LightGCN_data(path, data_frame, 4, excluded_user_id)
  blog_model = blog_LightGCN(len(user_mapping), len(item_mapping), embed_size= 32)
  blog_fit(blog_model, blog_tet_edge_data, blog_tet_sparse_data, epochs =n_epochs)
  blog_eval(blog_model, blog_tet_edge_data, blog_tet_sparse_data, 10)
  titles, genres = blog_predict(blog_model, user_id , K, path, user_mapping, item_mapping, blog_tet_edge_data[0])
  return titles,genres

#-- 원하는 파라메터 수정
data_frame = ["userId", "movieId", "rating"]
path = [rating_path, item_path]
embed_size = 16
n_epochs = 20000
user_id = 1
excluded_user = 1
K = 15

#-- 데이터 전처리, 모델정의, 모델학습, 모델평가, 모델 예측 진행
lib_titles, lib_genres = libco(data_frame, path,embed_size, n_epochs, excluded_user)
blog_titles, blog_genres = blog(data_frame, path, embed_size, n_epochs, excluded_user)

#-- 각각의 모델의 추천 데이터 확인
for i in range(K):
      print(f"title: {lib_titles[i].values}, genres: {lib_genres[i].values} ")
print("========================================================")
for i in range(K):
      print(f"title: {blog_titles[i]}, genres: {blog_genres[i]} ")

#-- 삭제한 데이터 확인
data = pd.read_csv(path[0])
user_data = data[data[data_frame[0]] == excluded_user]
user_data_5stars = user_data[user_data[data_frame[2]] == 5.0]  # Filter the data with a rating of 5.0r_user_data[r_user_data[data_frame[2]] == 5.0]  # Filter the data with a rating of 5.0
# print(user_data_5stars.head(10))
data = user_data_5stars.head(15)
moviedIds = data['movieId'].values

df = pd.read_csv(path[1])

titles = []
genres = []
for i in moviedIds:
    title = df[df['movieId'] == i]['title']
    genre = df[df['movieId'] == i]['genres']
    titles.append(title)
    genres.append(genre)
for i in range(len(moviedIds)):
  print(f"title: {titles[i].values}, genres: {genres[i].values} ")
❗ **만약 코드를 다시 돌릴 시 항상 아래의 데이터 전처리 함수 코드를 실행 필수!**

각각의 함수들 설명

  • libco_LightGCN_data : 라이브러리 GCN을 위한 데이터를 전처리하는 함수

  • blog_LightGCN_data : 블로그에서 구현한 GCN을 위한 데이터를 전처리하는 함수

    **excluded_number = 15는 유저가 높은 점수를 준 아이템을 삭제하는 개수입니다.**
    
❗ **만약 코드를 다시 돌릴 시 항상 아래의 해당 코드를 다시 돌려줘야 함**
def libco_LightGCN_data(path, data_frame, ratio = [0.8, 0.1, 0.1], excluded_user=None):
  #-- data
  data = pd.read_csv(path)
  data.rename(columns = {data_frame[0] : "user", data_frame[1]:"item", data_frame[2]:"label",},
              inplace = True)
  
  # print(data.head(10))
  #-- 테스트를 위한 특정 유저의 가장 앞에 있는 데이터 n개 삭제
  if excluded_user is not None:
    user_data = data[data["user"] == excluded_user]
    user_data_5stars = user_data[user_data["label"] == 5.0]  # Filter the data with a rating of 5.0
    if not user_data_5stars.empty:
      excluded_number = 15
      user_data_5stars = user_data_5stars.head(excluded_number)
      data = data.drop(user_data_5stars.index)

  # print(data.head(10))

  # split whole data into three folds for training, evaluating and testing
  train_data, eval_data, test_data = random_split(data, multi_ratios= ratio)

  train_data, data_info = DatasetPure.build_trainset(train_data)
  eval_data = DatasetPure.build_evalset(eval_data)
  test_data = DatasetPure.build_testset(test_data)
  tet_data = [train_data, eval_data, test_data]
  print(data_info)  # n_users: 5894, n_items: 3253, data sparsity: 0.4172 %  
  return tet_data, data_info
  #-- Libco -- 

def blog_LightGCN_data(path, data_frame, rating_threshold =4, excluded_user=None):

  #-- 유저, 아이템 매핑
  rating_data = pd.read_csv(path[0], index_col= data_frame[0])
  item_data = pd.read_csv(path[1], index_col= data_frame[1])
  data = pd.read_csv(path[0])

  print("1번째 데이터 삭제하기 전")
  print(rating_data.head(10))

  #-- 테스트를 위한 특정 유저의 가장 앞에 있는 데이터 삭제

  if excluded_user is not None:
    user_data = data[data[data_frame[0]] == excluded_user]
    user_data_5stars = user_data[user_data[data_frame[2]] == 5.0]  # Filter the data with a rating of 5.0r_user_data[r_user_data[data_frame[2]] == 5.0]  # Filter the data with a rating of 5.0

    r_user_data =  rating_data[rating_data.index == excluded_user].reset_index()

    if not user_data_5stars.empty:
      excluded_number = 15
      user_data_5stars = user_data_5stars.head(excluded_number)     
      r_user_data_5stars = r_user_data[r_user_data[data_frame[2]] == 5.0]
      data = data.drop(user_data_5stars.index)
      
      row_index = []
      for i in range(0,excluded_number):
        row_index.append(r_user_data_5stars.iloc[i].name)

      rating_data = rating_data.reset_index().drop(row_index[0])
      rating_data.set_index(data_frame[0], inplace=True)
      for i in range(1, len(row_index)) :
        rating_data = rating_data.reset_index().drop(row_index[i]-i)
      # rating_data = rating_data.drop(rating_data.iloc.nam)
        rating_data.set_index(data_frame[0], inplace=True)
  
  print("\n\n\n1번째 데이터 삭제하기 후")
  print(rating_data.head(10))

  # # rating_data의 컬럼 확인
  # print(rating_data.head(5))

  user_mapping = {index: i for i, index in enumerate(rating_data.index.unique())}
  item_mapping = {index: i for i, index in enumerate(item_data.index.unique())}

  #-- 매핑 데이터로 edge연결
  edge_index = None
  src = [user_mapping[index] for index in data[data_frame[0]]]
  dst = [item_mapping[index] for index in data[data_frame[1]]]
  edge_attr = torch.from_numpy(data[data_frame[2]].values).view(-1, 1).to(torch.long) >= rating_threshold
  
  edge_index = [[], []]
  for i in range(edge_attr.shape[0]):
    if edge_attr[i]:
        edge_index[0].append(src[i])
        edge_index[1].append(dst[i])
  edge_index = torch.tensor(edge_index)

  #-- 데이터 분할 8:1:1
  num_users, num_movies = len(user_mapping), len(item_mapping)
  num_interactions = edge_index.shape[1]
  all_indices = [i for i in range(num_interactions)]

  train_indices, test_indices = train_test_split(
      all_indices, test_size=0.2, random_state=1)
  val_indices, test_indices = train_test_split(
      test_indices, test_size=0.5, random_state=1)

  train_edge_index = edge_index[:, train_indices]
  val_edge_index = edge_index[:, val_indices]
  test_edge_index = edge_index[:, test_indices]

  print("Train Edge Data : ", train_edge_index.shape)
  print("Val Edge Data :", val_edge_index.shape)
  print("Test Edge Data : ", test_edge_index.shape)    

  #-- 연결된 엣지를 희소행렬로 변환
  # convert edge indices into Sparse Tensors: https://pytorch-geometric.readthedocs.io/en/latest/notes/sparse_tensor.html
  from torch_geometric.utils import train_test_split_edges

  train_sparse_edge_index = SparseTensor(row=train_edge_index[0], col=train_edge_index[1], sparse_sizes=(
      num_users + num_movies, num_users + num_movies))
  val_sparse_edge_index = SparseTensor(row=val_edge_index[0], col=val_edge_index[1], sparse_sizes=(
      num_users + num_movies, num_users + num_movies))
  test_sparse_edge_index = SparseTensor(row=test_edge_index[0], col=test_edge_index[1], sparse_sizes=(
      num_users + num_movies, num_users + num_movies))

  tet_edge_data = [edge_index, train_edge_index,val_edge_index, test_edge_index]  
  tet_sparse_data = [train_sparse_edge_index, val_sparse_edge_index, test_sparse_edge_index]
  return tet_edge_data, tet_sparse_data, user_mapping, item_mapping
#-- Blog --
  1. 모델정의

2개 모델 모두 기본값으로 진행하며 embed_size와 epochs만 인자로 넘겨줍니다.

  • libco_LightGCN : 라이브러리 모델 정의
  • Blog_LightGCN : 블로그 모델 정의
def libco_LightGCN(data_info, embed_size =32, n_epochs = 20):
  lightgcn = LightGCN(
      task="ranking",
      data_info= data_info,
      loss_type="bpr",
      embed_size= embed_size,
      n_epochs=n_epochs,
      lr=1e-3,
      batch_size=2048,
      num_neg=1,
      device="cuda",
  )
  return lightgcn
#-- Libco --#

class Blog_LightGCN(MessagePassing):
      def __init__(self, num_users, num_items, embedding_dim=64, K=3, add_self_loops=False):
          super().__init__()
          self.num_users, self.num_items = num_users, num_items
          self.embedding_dim, self.K = embedding_dim, K
          self.add_self_loops = add_self_loops
          self.users_emb = nn.Embedding(
              num_embeddings=self.num_users, embedding_dim=self.embedding_dim) # e_u^0
          self.items_emb = nn.Embedding(
              num_embeddings=self.num_items, embedding_dim=self.embedding_dim) # e_i^0
          nn.init.normal_(self.users_emb.weight, std=0.1)
          nn.init.normal_(self.items_emb.weight, std=0.1)
      def forward(self, edge_index: SparseTensor):
          # compute \tilde{A}: symmetrically normalized adjacency matrix
          edge_index_norm = gcn_norm(
              edge_index, add_self_loops=self.add_self_loops)
          emb_0 = torch.cat([self.users_emb.weight, self.items_emb.weight]) # E^0
          embs = [emb_0]
          emb_k = emb_0

          # multi-scale diffusion
          for i in range(self.K):
              emb_k = self.propagate(edge_index_norm, x=emb_k)
              embs.append(emb_k)
          embs = torch.stack(embs, dim=1)
          emb_final = torch.mean(embs, dim=1) # E^K
          users_emb_final, items_emb_final = torch.split(
              emb_final, [self.num_users, self.num_items]) # splits into e_u^K and e_i^K
          # returns e_u^K, e_u^0, e_i^K, e_i^0
          return users_emb_final, self.users_emb.weight, items_emb_final, self.items_emb.weight

      def message(self, x_j: Tensor) -> Tensor:
          return x_j

      def message_and_aggregate(self, adj_t: SparseTensor, x: Tensor) -> Tensor:
          # computes \tilde{A} @ x
          return matmul(adj_t, x)

#-- 손실함수 bpr
def bpr_loss(users_emb_final, users_emb_0, pos_items_emb_final, pos_items_emb_0, neg_items_emb_final, neg_items_emb_0, lambda_val):
    reg_loss = lambda_val * (users_emb_0.norm(2).pow(2) +
                             pos_items_emb_0.norm(2).pow(2) +
                             neg_items_emb_0.norm(2).pow(2)) # L2 loss

    pos_scores = torch.mul(users_emb_final, pos_items_emb_final)
    pos_scores = torch.sum(pos_scores, dim=-1) # predicted scores of positive samples
    neg_scores = torch.mul(users_emb_final, neg_items_emb_final)
    neg_scores = torch.sum(neg_scores, dim=-1) # predicted scores of negative samples

    loss = -torch.mean(torch.nn.functional.softplus(pos_scores - neg_scores)) + reg_loss
    return loss

def blog_LightGCN(num_users, num_movies, embed_size):
  # defines LightGCN model
  model = Blog_LightGCN(num_users, num_movies, embedding_dim = embed_size)
  return model

#-- Blog --#
  1. 모델학습
  • libco_fit : 라이브러리 모델 학습
  • blog_fit : 위 함수를 제외하고는 모두 블로그 모델을 위한 함수들입니다.
def libco_fit(model, train_data, eval_data):
  model.fit(
        train_data,
        neg_sampling=True,
        verbose=1,
        eval_data=eval_data,
        metrics=["loss", "roc_auc", "precision", "recall", "ndcg"],
    )
#-- Libco--#

# function which random samples a mini-batch of positive and negative samples
def sample_mini_batch(batch_size, edge_index):
    edges = structured_negative_sampling(edge_index)
    edges = torch.stack(edges, dim=0)
    indices = random.choices(
        [i for i in range(edges[0].shape[0])], k=batch_size)
    batch = edges[:, indices]
    user_indices, pos_item_indices, neg_item_indices = batch[0], batch[1], batch[2]
    return user_indices, pos_item_indices, neg_item_indices

# helper function to get N_u
def get_user_positive_items(edge_index):
    user_pos_items = {}
    for i in range(edge_index.shape[1]):
        user = edge_index[0][i].item()
        item = edge_index[1][i].item()
        if user not in user_pos_items:
            user_pos_items[user] = []
        user_pos_items[user].append(item)
    return user_pos_items

# computes recall@K and precision@K
def RecallPrecision_ATk(groundTruth, r, k):
    num_correct_pred = torch.sum(r, dim=-1)  # number of correctly predicted items per user
    # number of items liked by each user in the test set
    user_num_liked = torch.Tensor([len(groundTruth[i])
                                  for i in range(len(groundTruth))])
    recall = torch.mean(num_correct_pred / user_num_liked)
    precision = torch.mean(num_correct_pred) / k
    return recall.item(), precision.item()

# computes NDCG@K
def NDCGatK_r(groundTruth, r, k):
    assert len(r) == len(groundTruth)

    test_matrix = torch.zeros((len(r), k))

    for i, items in enumerate(groundTruth):
        length = min(len(items), k)
        test_matrix[i, :length] = 1
    max_r = test_matrix
    idcg = torch.sum(max_r * 1. / torch.log2(torch.arange(2, k + 2)), axis=1)
    dcg = r * (1. / torch.log2(torch.arange(2, k + 2)))
    dcg = torch.sum(dcg, axis=1)
    idcg[idcg == 0.] = 1.
    ndcg = dcg / idcg
    ndcg[torch.isnan(ndcg)] = 0.
    return torch.mean(ndcg).item()

# wrapper function to get evaluation metrics
def get_metrics(model, edge_index, exclude_edge_indices, k):
    user_embedding = model.users_emb.weight
    item_embedding = model.items_emb.weight

    # get ratings between every user and item - shape is num users x num movies
    rating = torch.matmul(user_embedding, item_embedding.T)

    for exclude_edge_index in exclude_edge_indices:
        # gets all the positive items for each user from the edge index
        user_pos_items = get_user_positive_items(exclude_edge_index)
        # get coordinates of all edges to exclude
        exclude_users = []
        exclude_items = []
        for user, items in user_pos_items.items():
            exclude_users.extend([user] * len(items))
            exclude_items.extend(items)

        # set ratings of excluded edges to large negative value
        rating[exclude_users, exclude_items] = -(1 << 10)

    # get the top k recommended items for each user
    _, top_K_items = torch.topk(rating, k=k)

    # get all unique users in evaluated split
    users = edge_index[0].unique()

    test_user_pos_items = get_user_positive_items(edge_index)

    # convert test user pos items dictionary into a list
    test_user_pos_items_list = [
        test_user_pos_items[user.item()] for user in users]

    # determine the correctness of topk predictions
    r = []
    for user in users:
        ground_truth_items = test_user_pos_items[user.item()]
        label = list(map(lambda x: x in ground_truth_items, top_K_items[user]))
        r.append(label)
    r = torch.Tensor(np.array(r).astype('float'))

    recall, precision = RecallPrecision_ATk(test_user_pos_items_list, r, k)
    ndcg = NDCGatK_r(test_user_pos_items_list, r, k)

    return recall, precision, ndcg
# wrapper function to evaluate model
def evaluation(model, edge_index, sparse_edge_index, exclude_edge_indices, k, lambda_val):
    # get embeddings
    users_emb_final, users_emb_0, items_emb_final, items_emb_0 = model.forward(
        sparse_edge_index)
    edges = structured_negative_sampling(
        edge_index, contains_neg_self_loops=False)
    user_indices, pos_item_indices, neg_item_indices = edges[0], edges[1], edges[2]
    users_emb_final, users_emb_0 = users_emb_final[user_indices], users_emb_0[user_indices]
    pos_items_emb_final, pos_items_emb_0 = items_emb_final[
        pos_item_indices], items_emb_0[pos_item_indices]
    neg_items_emb_final, neg_items_emb_0 = items_emb_final[
        neg_item_indices], items_emb_0[neg_item_indices]

    loss = bpr_loss(users_emb_final, users_emb_0, pos_items_emb_final, pos_items_emb_0,
                    neg_items_emb_final, neg_items_emb_0, lambda_val).item()

    recall, precision, ndcg = get_metrics(
        model, edge_index, exclude_edge_indices, k)

    return loss, recall, precision, ndcg
def blog_fit(model, blog_tet_edge_data, blog_tet_sparse_data, epochs = 50):
  # define contants
  ITERATIONS = epochs
  BATCH_SIZE = 2048
  LR = 1e-3
  ITERS_PER_EVAL = 300
  ITERS_PER_LR_DECAY = 300
  K = 10
  LAMBDA = 1e-6

  model = model.to(device)
  model.train()

  optimizer = optim.Adam(model.parameters(), lr=LR)
  scheduler = optim.lr_scheduler.ExponentialLR(optimizer, gamma=0.95)

  edge_index = blog_tet_edge_data[0].to(device)
  train_edge_index = blog_tet_edge_data[1].to(device)
  train_sparse_edge_index = blog_tet_sparse_data[0].to(device)

  val_edge_index = blog_tet_edge_data[2].to(device)
  val_sparse_edge_index = blog_tet_sparse_data[1].to(device)

  # training loop
  train_losses = []
  val_losses = []

  for iter in range(ITERATIONS):
      # forward propagation
      users_emb_final, users_emb_0, items_emb_final, items_emb_0 = model.forward(
          train_sparse_edge_index)

      # mini batching
      user_indices, pos_item_indices, neg_item_indices = sample_mini_batch(
          BATCH_SIZE, train_edge_index)
      user_indices, pos_item_indices, neg_item_indices = user_indices.to(
          device), pos_item_indices.to(device), neg_item_indices.to(device)
      users_emb_final, users_emb_0 = users_emb_final[user_indices], users_emb_0[user_indices]
      pos_items_emb_final, pos_items_emb_0 = items_emb_final[
          pos_item_indices], items_emb_0[pos_item_indices]
      neg_items_emb_final, neg_items_emb_0 = items_emb_final[
          neg_item_indices], items_emb_0[neg_item_indices]

      # loss computation
      train_loss = bpr_loss(users_emb_final, users_emb_0, pos_items_emb_final,
                            pos_items_emb_0, neg_items_emb_final, neg_items_emb_0, LAMBDA)

      optimizer.zero_grad()
      train_loss.backward()
      optimizer.step()

      if iter % ITERS_PER_EVAL == 0:
          model.eval()
          val_loss, recall, precision, ndcg = evaluation(
              model, val_edge_index, val_sparse_edge_index, [train_edge_index], K, LAMBDA)
          print(f"[Iteration {iter}/{ITERATIONS}] train_loss: {round(train_loss.item(), 5)}, val_loss: {round(val_loss, 5)}, val_recall@{K}: {round(recall, 5)}, val_precision@{K}: {round(precision, 5)}, val_ndcg@{K}: {round(ndcg, 5)}")
          train_losses.append(train_loss.item())
          val_losses.append(val_loss)
          model.train()

      if iter % ITERS_PER_LR_DECAY == 0 and iter != 0:
          scheduler.step()
  iters = [iter * ITERS_PER_EVAL for iter in range(len(train_losses))]
  plt.plot(iters, train_losses, label='train')
  plt.plot(iters, val_losses, label='validation')
  plt.xlabel('iteration')
  plt.ylabel('loss')
  plt.title('training and validation loss curves')
  plt.legend()
  plt.show()
  1. 모델 평가

모델 각각에 대해서 평가

def libco_eval(model, test_data):
  result = evaluate(
      model=model,
      data=test_data,
      neg_sampling=True,
      metrics=["loss", "roc_auc", "precision", "recall", "ndcg"],
  )
  print(result)

#-- Libco --

def blog_eval(model, blog_tet_edge_data, blog_tet_sparse_data , K = 10):
  # evaluate on test set
  LAMBDA = 1e-6
  model.eval()
  test_edge_index = blog_tet_edge_data[3].to(device)
  test_sparse_edge_index = blog_tet_sparse_data[2].to(device)

  test_loss, test_recall, test_precision, test_ndcg = evaluation(
              model, test_edge_index, test_sparse_edge_index, [blog_tet_edge_data[1], blog_tet_edge_data[2]], K, LAMBDA)

  print(f"[test_loss: {round(test_loss, 5)}, test_recall@{K}: {round(test_recall, 5)}, test_precision@{K}: {round(test_precision, 5)}, test_ndcg@{K}: {round(test_ndcg, 5)}")
  1. 예측
  • lilco_predict
  • blog_predict

2개의 해당 user에게 K개의 영화 제목과 장르를 추천하여 return 합니다.

def lilco_predict(model, user_id, path, K = 10):
  df = pd.read_csv(path[1])
  rec_arr = model.recommend_user(user=user_id, n_rec=K).get(user_id)
  
  titles = []
  genres = []
  for i in rec_arr:
    title = df[df['movieId'] == i]['title']
    genre = df[df['movieId'] == i]['genres']
    titles.append(title)
    genres.append(genre)
    # print(f"title: {title}, genres: {genre} ")

  return titles, genres
#-- Libco  --#

def blog_predict(model, user_id ,TopK, path, user_mapping, item_mapping, edge_index):

  df = pd.read_csv(path[1])
  movieid_title = pd.Series(df.title.values,index=df.movieId).to_dict()
  movieid_genres = pd.Series(df.genres.values,index=df.movieId).to_dict()

  user_pos_items = get_user_positive_items(edge_index)
  user = user_mapping[user_id]
  e_u = model.users_emb.weight[user]
  scores = model.items_emb.weight @ e_u

  values, indices = torch.topk(scores, k=len(user_pos_items[user]) + TopK)

  movies = [index.cpu().item() for index in indices if index in user_pos_items[user]][:TopK]
  movie_ids = [list(item_mapping.keys())[list(item_mapping.values()).index(movie)] for movie in movies]
  titles = [movieid_title[id] for id in movie_ids]
  genres = [movieid_genres[id] for id in movie_ids]
 
  # print(f"Here are some movies that user {user_id} rated highly")
  # for i in range(TopK):
  #     print(f"title: {titles[i]}, genres: {genres[i]} ")

  movies = [index.cpu().item() for index in indices if index not in user_pos_items[user]][:TopK]
  movie_ids = [list(item_mapping.keys())[list(item_mapping.values()).index(movie)] for movie in movies]
  titles = [movieid_title[id] for id in movie_ids]
  genres = [movieid_genres[id] for id in movie_ids]

  # print(f"Here are some suggested movies for user {user_id}")
  return titles, genres

위 함수들을 모두 하나의 함수로 만들고 예측된 K개의 영화정보들을 반환해준다.

def libco(data_frame, path, embed_size, n_epochs, excluded_user):
  lib_tet_data, lib_data_info = libco_LightGCN_data(path[0], data_frame, excluded_user = excluded_user_id)
  lib_model = libco_LightGCN(lib_data_info, embed_size =embed_size, n_epochs = int(n_epochs/3)) 
  libco_fit(lib_model, lib_tet_data[0], lib_tet_data[1])
  libco_eval(lib_model, lib_tet_data[2])
  titles, genres = lilco_predict(lib_model,user_id, path, K = K)
  return titles,genres
  
def blog(data_frame, path, embed_size, n_epochs, excluded_user):
  blog_tet_edge_data, blog_tet_sparse_data, user_mapping, item_mapping = blog_LightGCN_data(path, data_frame, 4, excluded_user_id)
  blog_model = blog_LightGCN(len(user_mapping), len(item_mapping), embed_size= 32)
  blog_fit(blog_model, blog_tet_edge_data, blog_tet_sparse_data, epochs =n_epochs)
  blog_eval(blog_model, blog_tet_edge_data, blog_tet_sparse_data, 10)
  titles, genres = blog_predict(blog_model, user_id , K, path, user_mapping, item_mapping, blog_tet_edge_data[0])
  return titles,genres

아래 코드에서 수정 가능한 변수

  • embed_size
  • n_epochs
  • user_id : 추천해줄 유저
  • excluded_user : 추천 유저와 같은 값으로 설정
  • K : K개를 자유롭게 설정
data_frame = ["userId", "movieId", "rating"]
path = [rating_path, item_path]
embed_size = 16
n_epochs = 20000
user_id = 1
excluded_user = 1
K = 15

lib_titles, lib_genres = libco(data_frame, path,embed_size, n_epochs, excluded_user)
blog_titles, blog_genres = blog(data_frame, path, embed_size, n_epochs, excluded_user)

라이브러리와 블로그 모델의 예측값들 출력

for i in range(K):
      print(f"title: {lib_titles[i].values}, genres: {lib_genres[i].values} ")
print("========================================================")
for i in range(K):
      print(f"title: {blog_titles[i]}, genres: {blog_genres[i]} ")

내가 삭제한 데이터 확인

data = pd.read_csv(path[0])
user_data = data[data[data_frame[0]] == excluded_user]
user_data_5stars = user_data[user_data[data_frame[2]] == 5.0]  # Filter the data with a rating of 5.0r_user_data[r_user_data[data_frame[2]] == 5.0]  # Filter the data with a rating of 5.0
# print(user_data_5stars.head(10))
data = user_data_5stars.head(15)
moviedIds = data['movieId'].values

df = pd.read_csv(path[1])

titles = []
genres = []
for i in moviedIds:
    title = df[df['movieId'] == i]['title']
    genre = df[df['movieId'] == i]['genres']
    titles.append(title)
    genres.append(genre)
for i in range(len(moviedIds)):
  print(f"title: {titles[i].values}, genres: {genres[i].values} ")

LightGCN

해당 모델이 실제로 평가를 제대로 하는지 확인하기 위해서 user가 가장 높게 평가한 데이터 1개를 제외하고 학습 후 해당 아이템을 추천해주는지 확인

  1. 데이터 프레임에서 해당 유저가 5점을 평가한 데이터 중 15개를 제거
#-- 테스트를 위한 특정 유저의 가장 앞에 있는 데이터 n개 삭제
  if excluded_user is not None:
    user_data = data[data["user"] == excluded_user]
    user_data_5stars = user_data[user_data["label"] == 5.0]  # Filter the data with a rating of 5.0
    if not user_data_5stars.empty:
      excluded_number = 15
      user_data_5stars = user_data_5stars.head(excluded_number)
      data = data.drop(user_data_5stars.index)

if excluded_user is not None:
    user_data = data[data[data_frame[0]] == excluded_user]
    user_data_5stars = user_data[user_data[data_frame[2]] == 5.0]  # Filter the data with a rating of 5.0r_user_data[r_user_data[data_frame[2]] == 5.0]  # Filter the data with a rating of 5.0

    r_user_data =  rating_data[rating_data.index == excluded_user].reset_index()

    if not user_data_5stars.empty:
      excluded_number = 3
      user_data_5stars = user_data_5stars.head(excluded_number)     
      r_user_data_5stars = r_user_data[r_user_data[data_frame[2]] == 5.0]
      data = data.drop(user_data_5stars.index)
      row_index = []
      for i in range(0,excluded_number):
        row_index.append(r_user_data_5stars.iloc[i].name)

      rating_data = rating_data.reset_index().drop(row_index[0])
      rating_data.set_index(data_frame[0], inplace=True)
      for i in range(1, len(row_index)) :
        rating_data = rating_data.reset_index().drop(row_index[i]-i)
      # rating_data = rating_data.drop(rating_data.iloc.nam)
        rating_data.set_index(data_frame[0], inplace=True)

위 과정을 통해 excluded_user가 가장 높게 평가한 데이터 중 가장 앞에 있는 데이터를 삭제한다.

  • user1이 5점을 평가한 데이터중 가장 위에 있는 15개 데이터를 삭제 후 진행

    userId movieId rating timestamp
    1 1 4.0 964982703
    1 3 4.0 964981247
    1 6 4.0 964982224
    1 47 5.0 964983815
    1 50 5.0 964982931
    1 70 3.0 964982400
    1 101 5.0 964980868
    1 110 4.0 964982176
    1 151 5.0 964984041
    1 157 5.0 964984100
  • 데이터 삭제 이후

    userId movieId rating timestamp
    1 1 4.0 964982703
    1 3 4.0 964981247
    1 6 4.0 964982224
    1 70 3.0 964982400
    1 110 4.0 964982176
    1 163 5.0 964983650
    1 216 5.0 964981208
    1 223 3.0 964980985
  1. 이후 이전 코드와 마찬가지로 학습 진행

    • user_id : 테스트를 진행할 유저
    • k = 15개의 상위 목록 중 확인
    data_frame = ["userId", "movieId", "rating"]
    path = [rating_path, item_path]
    embed_size = 32
    n_epochs = 10000
    user_id = 1
    excluded_user = 1
    K = 15
    
    lib_titles, lib_genres = libco(data_frame, path,embed_size, n_epochs, excluded_user)
    blog_titles, blog_genres = blog(data_frame, path, embed_size, n_epochs, excluded_user)
  2. 결과 확인

    title: ['Star Trek: The Motion Picture (1979)'], genres: ['Adventure|Sci-Fi'] 
    title: ['Jaws (1975)'], genres: ['Action|Horror'] 
    title: ['Thing, The (1982)'], genres: ['Action|Horror|Sci-Fi|Thriller'] 
    title: ['Mystery Science Theater 3000: The Movie (1996)'], genres: ['Comedy|Sci-Fi'] 
    title: ['Mars Attacks! (1996)'], genres: ['Action|Comedy|Sci-Fi'] 
    title: ['Army of Darkness (1993)'], genres: ['Action|Adventure|Comedy|Fantasy|Horror'] 
    title: ['Babe: Pig in the City (1998)'], genres: ['Adventure|Children|Drama'] 
    title: ['Arachnophobia (1990)'], genres: ['Comedy|Horror'] 
    title: ['Cool Hand Luke (1967)'], genres: ['Drama'] 
    title: ['Nutty Professor, The (1996)'], genres: ['Comedy|Fantasy|Romance|Sci-Fi'] 
    title: ['Star Trek VI: The Undiscovered Country (1991)'], genres: ['Action|Mystery|Sci-Fi'] 
    title: ["One Flew Over the Cuckoo's Nest (1975)"], genres: ['Drama'] 
    title: ['Godfather, The (1972)'], genres: ['Crime|Drama'] 
    title: ['Little Shop of Horrors (1986)'], genres: ['Comedy|Horror|Musical'] 
    title: ['Aliens (1986)'], genres: ['Action|Adventure|Horror|Sci-Fi'] 
    ========================================================
    title: Shawshank Redemption, The (1994), genres: Crime|Drama 
    title: Pulp Fiction (1994), genres: Comedy|Crime|Drama|Thriller 
    **title: Star Wars: Episode IV - A New Hope (1977), genres: Action|Adventure|Sci-Fi** 
    **title: Usual Suspects, The (1995), genres: Crime|Mystery|Thriller** 
    title: Godfather, The (1972), genres: Crime|Drama 
    title: Terminator 2: Judgment Day (1991), genres: Action|Sci-Fi 
    title: Lord of the Rings: The Return of the King, The (2003), genres: Action|Adventure|Drama|Fantasy 
    title: Lord of the Rings: The Fellowship of the Ring, The (2001), genres: Adventure|Fantasy 
    title: Lord of the Rings: The Two Towers, The (2002), genres: Adventure|Fantasy 
    **title: Seven (a.k.a. Se7en) (1995), genres: Mystery|Thriller** 
    title: Apollo 13 (1995), genres: Adventure|Drama|IMAX 
    title: Sixth Sense, The (1999), genres: Drama|Horror|Mystery 
    title: Memento (2000), genres: Mystery|Thriller 
    title: One Flew Over the Cuckoo's Nest (1975), genres: Drama 
    title: Aladdin (1992), genres: Adventure|Animation|Children|Comedy|Musical
    **title: ['Seven (a.k.a. Se7en) (1995)'], genres: ['Mystery|Thriller']  ->
    title: ['Usual Suspects, The (1995)'], genres: ['Crime|Mystery|Thriller']**  ->
    title: ['Bottle Rocket (1996)'], genres: ['Adventure|Comedy|Crime|Romance'] 
    title: ['Rob Roy (1995)'], genres: ['Action|Drama|Romance|War'] 
    title: ['Canadian Bacon (1995)'], genres: ['Comedy|War'] 
    title: ['Desperado (1995)'], genres: ['Action|Romance|Western'] 
    title: ['Billy Madison (1995)'], genres: ['Comedy'] 
    title: ['Dumb & Dumber (Dumb and Dumber) (1994)'], genres: ['Adventure|Comedy'] 
    **title: ['Star Wars: Episode IV - A New Hope (1977)'], genres: ['Action|Adventure|Sci-Fi'] ->**
    title: ['Tommy Boy (1995)'], genres: ['Comedy'] 
    title: ['Jungle Book, The (1994)'], genres: ['Adventure|Children|Romance'] 
    title: ['Fugitive, The (1993)'], genres: ['Thriller'] 
    title: ["Schindler's List (1993)"], genres: ['Drama|War'] 
    title: ['Tombstone (1993)'], genres: ['Action|Drama|Western'] 
    title: ['Pinocchio (1940)'], genres: ['Animation|Children|Fantasy|Musical']

    블로그LightGCN같은 경우 유저가 평가한 5점 짜리 영화를 3개정도 추천해줬지만 라이브러리는 1개도 추천하지 못했다..

    코드 수정 방법

    • 추가적인 아이디어 고민
    • 유사도 계산 방법 고민
    • 16일 8시 줌 미팅
    • 결과보고서 제출 준비
      • UROP : Light GCN이 결론

순차적 추천

Session-based recommendation RNN 논문

1. INTRODUCTION

세셔 긴반 추천은 과거 클릭을 고려하지 않는 단순한 방법을 사용한다. 추천 시스템은 사용자의 선호도에 따라 사용자에게 추천하는 데 사용된다. 순차적 데이터를 처리하는데 있어서 큰 성공을 이룬 RNN을 적용하여 처음 클릭한 항목을 초기 입력으로 간주하고 랭킹 손실 함수를 사용하여 모델을 학습시키는 방식으로 RNN을 활용할 수 있다.

2. Related Work

2.1 Session-Based Recommendation: 기존의 방법들에서는 사용자의 마지막 클릭 아이템을 기준으로 유사한 아이템을 찾는데, 이로 인해 과거의 클릭 정보가 무시되는 문제가 있다. 이를 해결하기 위해 2가지 방법이 있다.

  • MDP (Markov Decision Process): MDP 방법은 아이템 간의 전이 확률을 기반으로 다음 추천을 계산하는 간단한 방법이다. 하지만 아이템 수가 많아질수록, 즉 유저가 선택할 수 있는 폭이 넓어질수록 MDP 기반의 접근 방법만으로는 상태 공간을 관리하기 어려워진다.
예시

우리는 주말에 놀러갈 수 있는 여러 가지 활동을 고려하고 있습니다. 우리의 상태(State)는 현재 날씨에 따라 "맑음", "흐림", "비"로 정의됩니다. 우리는 두 가지 행동(Action)을 선택할 수 있습니다: "공원에서 산책하기" 또는 "영화관에서 영화 보기". 각 상태와 행동에는 보상(Reward)이 있습니다. 마지막으로, 날씨는 상태 전이 확률(Transition Probability)에 의해 변경됩니다.

  1. 상태(State):
    • 맑음 (Sunny)
    • 흐림 (Cloudy)
    • 비 (Rainy)
  2. 행동(Action):
    • 공원에서 산책하기 (Go for a walk in the park)
    • 영화관에서 영화 보기 (Go to the movie theater)
  3. 보상(Reward):
    • 공원에서 산책하기:
      • 맑음: +10
      • 흐림: +5
      • 비: -10
    • 영화관에서 영화 보기:
      • 맑음: +5
      • 흐림: +10
      • 비: +15
  • GFF (General Factorization Framework): GFF는 아이템에 대해 두 종류의 잠재 벡터를 사용합니다. 하나는 아이템 자체를 나타내는 벡터이고, 다른 하나는 session으로서의 아이템을 나타내는 벡터입니다. 이렇게 하면 어떤 세션은 session으로서의 아이템들의 평균으로 표현될 수 있지만 GFF는 session 간의 순서를 고려하지 않습니다.
예시

우리는 음악 스트리밍 서비스를 운영하고 있습니다. 사용자는 세션 동안 여러 개의 곡을 재생합니다. 세션은 사용자의 한 번의 방문을 나타내며, 세션 동안 재생된 곡들은 아이템으로 간주됩니다. 우리의 목표는 현재 세션에서 다음에 재생할 아이템을 추천하는 것입니다.

  1. 잠재 벡터 생성:
    • 아이템 잠재 벡터(Item Embedding): 각 아이템은 고유한 잠재 벡터를 가지며, 이 벡터는 아이템 자체의 특성을 반영합니다.
    • 세션 잠재 벡터(Session Embedding): 세션은 세션 동안 재생된 아이템의 평균 잠재 벡터로 표현됩니다. 이 벡터는 세션의 특성을 반영하며, 세션 간의 순서는 고려하지 않습니다.
  2. 추천 계산:
    • 현재 세션에 대해 세션 잠재 벡터를 계산합니다.
    • 세션 잠재 벡터와 아이템 잠재 벡터 사이의 유사도를 계산하여 다음에 재생할 아이템을 추천합니다.
    • 예를 들어, 세션 잠재 벡터와 각 아이템 잠재 벡터 간의 코사인 유사도를 계산할 수 있습니다.

2.2 Deep Learning in Recommendation: Restricted Boltzmann Machines (RBM)은 Collaborative Filtering 모델에서 사용자와 아이템 간의 상호작용을 기반으로 우수한 성능을 보다. 최근에는 Deep Model들이 구조화되지 않은 컨텐츠에서 특징을 추출하기 위해 사용되고 전통적인 CF 방법과 함께 사용되어 왔다.

Session-Based Recommendation에서 MDP와 GFF 방법이 과거 클릭 정보를 고려하기 위한 방법이다.

Deep Learning은 RBM과 함께 사용되며 구조화되지 않은 컨텐츠에서 특징을 추출하기 위해 사용된다.

3. Recommendation with RNNs

  1. 문제 정의:
    • 세션 기반 추천은 사용자의 과거 상호작용 세션 데이터를 기반으로 다음에 사용자가 상호작용할 아이템을 예측하는 문제이다.
    • 각 세션은 사용자가 연속적으로 상호작용한 아이템의 시퀀스로 표현된다.
  2. 임베딩:
    • 세션의 아이템 시퀀스와 각 아이템을 임베딩하여 순환 신경망에 입력으로 사용한다.
    • 임베딩은 아이템과 세션 간의 관련성을 반영하는 벡터 표현이다.
  3. 순환 신경망(RNN):
    • RNN은 시퀀스 데이터를 처리하는데 효과적인 신경망 구조이다.
    • 세션의 아이템 시퀀스를 입력으로 받아 순차적으로 처리하면서 은닉 상태(hidden state)를 업데이트한다.
    • RNN은 사용자의 상호작용 패턴을 학습하여 다음 아이템을 예측하는데 활용된다.
  4. 예측:
    • 순환 신경망을 통해 학습된 모델을 사용하여 다음에 상호작용할 아이템을 예측한다.
    • 예측은 소프트맥스 함수를 통해 다음 아이템에 대한 확률 분포를 계산하는 과정이다.
    • 가장 확률이 높은 아이템을 추천으로 제시한다.
예시

우리는 온라인 음악 스트리밍 서비스를 운영하고 있습니다. 각 사용자는 세션 동안 여러 곡을 연속적으로 듣습니다. 우리의 목표는 각 사용자의 세션에서 다음에 들을 곡을 추천하는 것입니다.

  1. 데이터 전처리:
    • 사용자의 상호작용 데이터는 세션으로 구성되어 있습니다. 각 세션은 사용자가 연속적으로 재생한 곡들의 시퀀스로 표현됩니다.
    • 예를 들어, 사용자 A의 세션은 [곡1, 곡2, 곡3]과 같이 구성될 수 있습니다.
    • 이러한 세션 데이터를 모델의 입력으로 사용하기 위해 각 곡을 임베딩하여 벡터 형태로 변환합니다.
  2. 모델 구성:
    • RNN을 사용하여 세션의 아이템 시퀀스를 처리하는 모델을 구성합니다.
    • 임베딩된 곡 벡터를 순차적으로 입력으로 주어 RNN을 통해 시퀀스를 처리합니다.
    • RNN은 각 시간 단계에서 이전 은닉 상태와 현재 입력을 기반으로 새로운 은닉 상태를 계산합니다.
  3. 학습:
    • 모델은 학습 데이터를 사용하여 파라미터를 조정합니다.
    • 각 세션의 입력 시퀀스와 실제로 다음에 재생한 곡을 비교하여 모델의 예측 오차를 최소화하도록 학습합니다.
    • 손실 함수를 사용하여 예측 오차를 측정하고, 역전파 알고리즘을 통해 모델의 파라미터를 업데이트합니다.
  4. 추천:
    • 학습된 모델을 사용하여 다음에 재생할 곡을 추천합니다.
    • 사용자의 현재 세션을 입력으로 주어 모델이 다음 곡에 대한 확률 분포를 계산합니다.
    • 가장 확률이 높은 곡을 추천으로 제시합니다.

예를 들어, 사용자 A의 현재 세션은 [곡1, 곡2]입니다. 모델은 입력으로 받은 세션을 기반으로 다음에 재생할 곡인 곡3을 추천할 수 있습니다. 이 추천은 사용자의 과거 상호작용 패턴을 학습하여 개인화된 추천을 제공하는 것입니다.

이와 같이 RNN을 사용한 세션 기반 추천은 사용자의 과거 상호작용을 기반으로 다음에 상호작용할 아이템을 예측하여 추천하는 방법입니다.

About

License:MIT License


Languages

Language:Jupyter Notebook 100.0%