ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [scikit-surprise] SVD Model 알아보기 ( Singular Value Decomposition )
    AI-ML 2024. 5. 7. 05:37
    728x90
    반응형

     

    - 목차

     

    키워드.

    • scikit-surprise
    • Matrix Factorization
    • SVD
    • Latent Space

     

    함께 보면 좋은 글.

    https://westlife0615.tistory.com/851

     

    [scikit-surprise] Dataset 이해하기

    - 목차 키워드.scikit-surpriseCollaborative FilteringRecommender Systemmovielens 들어가며.이번 글에서는 "scikit-surprise" 라이브러리가 제공하는 "Trainset" 모듈에 대해서 알아보려고 합니다.surprise 라이브러리는 R

    westlife0615.tistory.com

     

     

    들어가며.

    이번 글에서는 scikit-surprise 의 SVD 모델에 대해서 살펴보려고 합니다.

    SVD 는 Singular Value Decomposition 의 약자이며, 하나의 행렬을 분해할 수 있는 수학적인 기법입니다.

    사실상 추천 시스템에서 사용하는 SVD 는 수학적인 정의에 100% 상응하지는 않습니다.

    그래서 SVD 의 수학적인 정의에 집착하기 보다는 머신러닝에서 사용하는 하나의 Matrix Factorization 기법이라고 생각하시면 좋을 것 같습니다.

     

    Matrix Factorization.

    추천시스템에서 Matrix Factorization 에 대해서 간단히 살펴보겠습니다.

    하나의 거대한 Matrix 가 존재할 때, 이 Matrix 를 2개의 Matrix 로 분해할 수 있습니다.

    보통 Collaborative Filtering 분야에서 User, Item, Rating 의 값들로 표현되는 Matrix 를 사용하는데요.

    아래와 같은 형태의 데이터들이 사용됩니다.

    왼쪽의 사진은 user - item - rating 으로 표현되는 데이터셋이구요.

    오른쪽 사진은 user, item 을 두 축으로 삼는 Pivot Table 입니다.

     

     

    Matrix Factorization 은 user - item 을 두 축으로 삼는 Pivot Table 에서 수행됩니다.

    Matrix Factorization 의 결과는 User-Item Matrix 을 두개의 Matrix 로 분해하는 것이고,

    하나의 User Latent Matrix, Item Latent Matrix 로 분해되게 됩니다.

    이는 수식으로 아래와 같이 표현되게 됩니다.

    $$ R = U \cdot I $$

    $$ 또는 R_{ij} = \sum U_{i} \cdot I^{T}_{j} $$

    (R, U, I 는 각각 Ratings, Users, Items 에 관한 Matrix 입니다.)

     

    Matrix Factorization 의 결과로서 아래와 같은 2개의 Matrix 로 분해가 되구요.

    분해 방법에 대해서는 이어지는 글에서 다루어보도록 하겠습니다.

     

     

    Latent Space.

    Matrix Factorization 에서 중요한 키워드는 "Latent Space" 입니다.

    Latent Space 의 의미가 "잠재적 공간" 이라는 뜻인데요. 이 의미를 이해하는 것이 중요합니다.

    User - Item 으로 구성된 Matrix 는 N x M 행렬이라고 가정하겠습니다.

    그리고 이 N x M 행렬을 N x K 행렬과 K x M 인 행렬로 분해하였다고 가정하겠습니다.

    $ R_{N \times M} = U_{N \times K} \cdot I_{K \times M} $

     

    이때 User Matrix 와 Item Matrix 는 K 라는 Dimension 을 가지게 되는데요.

    K Dimensionality 가 User 와 Item 에 대한 잠재적 공간의 크기 또는 차원을 제공합니다.

    이를 다른 관점에서 표현하면 user1, user2, ..., userN 은 K 개의 특징을 가지게 된 것이고,

    item1, item2, ..., itemM 에 해당하는 상품들 또한 K 개의 특징을 가지게 됩니다.

     

    즉 아래 이미지와 같이 구성된 User - Item Matrix 가 존재할 때,

    K 가 6 개인 User Latent Matrix 와 Item Latent Matrix 를 구할 수 있습니다.  (K 를 6으로 설정할 때의 예시)

     

    이는 다음과 같이 해석할 수 있습니다.

    K 개의 알 수 없는 잠재적인 Dimension 들이 추가되었지만, 이는 마치 아래와 같은 특징처럼 여겨질 수 있다는 점입니다.

    즉, 의도한건 아니지만 어떠한 잠재적인 기능을 수행하는 K 개의 Feature 들로 새로운 Matrix 가 생성되었고

    이를 Matrix Factorization 이라고 부릅니다.

     

     

     

    SVD 는 어떤 방식으로 학습될까 ?

    SVD 알고리즘은 지도학습의 방식으로 학습을 진행합니다.

    SVD 의 입력 데이터는 Ratings Dataset 과 같은 모습이구요.

    < 입력 데이터 예시 >

     

    Output 결과는 아래의 이미지와 같은 Item Latent Matrix, User Latent Matrix 로 분해된 결과입니다.

     

    즉, 요약하자면 Rating Dataset -> SVD -> User & Item Latent Matrix 의 과정을 수행합니다.

    이를 간단하게 파이썬 코드로 구현해보도록 하겠습니다.

    from surprise import Dataset
    import pandas as pd
    import numpy as np
    import statistics
    
    mv_dataset = Dataset.load_builtin("ml-100k")
    raw_dataset = mv_dataset.raw_ratings
    dataset = pd.DataFrame(raw_dataset, columns=["user", "item", "rating", "timestamp"])
    """
      user item  rating  timestamp
    0  196  242     3.0  881250949
    1  186  302     3.0  891717742
    2   22  377     1.0  878887116
    3  244   51     2.0  880606923
    4  166  346     1.0  886397596
    ...
    """
    n_users = len(dataset["user"].unique())
    n_items = len(dataset["item"].unique())
    n_factors = 500
    n_epochs = 100
    learning_rate = 0.001
    
    """
    모든 user 의 수는 943, item 의 수는 1684 
    n_users : 943 n_items : 1682 n_factors : 500
    """
    
    user_latent_matrix = np.random.randn(n_users, n_factors)
    item_latent_matrix = np.random.randn(n_items, n_factors)
    """
    user_latent_matrix.shape : (943, 500)
    item_latent_matrix.shape : (1682, 500)
    """
    
    # Training Dataset 의 user id, item id 는 실제 사용하는 id 값이며,
    # 이들을 0 부터 시작하는 Index 에 매핑시키기 위한 4개의 Dict
    # 예를 들어, user id '101' -> 0, '1001' -> 1 과 같이 Mapping 합니다.
    user_mapper_from_raw_to_index = dict(zip(sorted(dataset["user"].unique()), list(range(n_users))))
    item_mapper_from_raw_to_index = dict(zip(sorted(dataset["item"].unique()), list(range(n_items))))
    user_mapper_from_index_to_raw = dict(zip(list(range(n_users)), sorted(dataset["user"].unique())))
    item_mapper_from_index_to_raw = dict(zip(list(range(n_items)), sorted(dataset["item"].unique())))
    
    for epoch in range(n_epochs):
        errors = []
        for _, row in dataset.iterrows():
            user_raw_id = row["user"]
            item_raw_id = row["item"]
            rating = row["rating"]
            user_index = user_mapper_from_raw_to_index[user_raw_id]
            item_index = item_mapper_from_raw_to_index[item_raw_id]
    
            # User Latent Matrix 에서 특정 user 의 vector 를 조회
            user_latent_vector = user_latent_matrix[user_index, :]
            item_latent_vector = item_latent_matrix[item_index, :]
    
            predicted_rating = np.dot(user_latent_vector, item_latent_vector)
            error = (rating - predicted_rating)
            user_latent_matrix[user_index, :] = user_latent_matrix[user_index, :] + learning_rate * error
            item_latent_matrix[item_index, :] = item_latent_matrix[item_index, :] + learning_rate * error
            errors.append(abs(error))
    
        print(f"eooch : {epoch}, error : {statistics.mean(errors)}")
    eooch : 0, error : 18.47654656785462
    eooch : 1, error : 18.417382199915714
    eooch : 2, error : 18.419961094404563
    eooch : 3, error : 18.424237719888783
    eooch : 4, error : 18.428607881834083
    eooch : 5, error : 18.432241449984453
    eooch : 6, error : 18.435334219476438
    eooch : 7, error : 18.437911053078878
    eooch : 8, error : 18.44022820551849
    eooch : 9, error : 18.44231429394892
    eooch : 10, error : 18.444225599985696
    eooch : 11, error : 18.44595749464332

     

    최대한 주석을 작성하긴 하였는데, 혹시나 이해가 어려우시거나 문제가 있다면 피드백주시면 좋을 것 같습니다.

    SVD 알고리즘의 핵심은 지도학습의 하나로써 User, Item 의 Latent Matrix 를 튜닝시키는 과정입니다.

    아래의 코드예시처럼 User 와 Item 의 Latent Vector 들을 실제 Rating 값에 가깝게 변화시키면서

    온전한 Latent Matrix 들을 완성합니다.

    predicted_rating = np.dot(user_latent_vector, item_latent_vector)
    error = (rating - predicted_rating)

     

    이렇게 Latent Matrix 들이 완성되게 된다면, 사용자가 평가하지 않은 상품 혹은 한번도 본적이 없는 상품에 대해서 얼마의 선호도를 가지는지를 예측할 수 있습니다.

    예를 들어, "k" User"n" Item 에 대해서 평가를 한적이 없습니다.

    하지만 완성된 User Latent Matrix 의 "k" Vector 와 Item Latent Matrix 의 "n" Vector 를 dot Product 한 결과를 "k" User 의 "n" Item 에 대한 평가로써 예측할 수 있습니다.

     

     

    마치며.

    이번 글에서는 SVD 의 컨셉에 대해서 알아보았는데요.

    이어지는 글에서 SVD 의 구체적인 사용법에 대해서 알아보도록 하겠습니다.

     

    https://westlife0615.tistory.com/858

     

    [scikit-surprise] SVD 모듈 사용하기

    - 목차 키워드.scikit-surpriseSVDMatrix Factorization  들어가며.SVD 에 대해서 설명하는 이전 글과 이어지는 내용입니다. https://westlife0615.tistory.com/844 [scikit-surprise] SVD Model 알아보기 ( Singular Value Decomposi

    westlife0615.tistory.com

     

     

     

     

    반응형
Designed by Tistory.