ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [scikit-surprise] Dataset 이해하기
    AI-ML 2024. 5. 14. 07:23
    728x90
    반응형

     

    - 목차

     

    키워드.

    • scikit-surprise
    • Collaborative Filtering
    • Recommender System
    • movielens

     

    들어가며.

    이번 글에서는 "scikit-surprise" 라이브러리가 제공하는 "Trainset" 모듈에 대해서 알아보려고 합니다.

    surprise 라이브러리는 Recommender System 의 구축을 도와주는 라이브러리이구요.

    구체적으로 "Collaborative Filtering" 의 기능을 손쉽게 구현하기 위한 모듈들을 내장합니다.

    특히 Matrix Factorization 기반의 상품 추천 기능을 효과적으로 구현할 수 있는데,

    이 과정에서 사용되는 Trainset 모듈의 특징과 사용법에 대해서 알아보도록 하겠습니다.

     

    참고로 surprise 라이브러리의 Github 주소를 아래와 같습니다.

    https://github.com/NicolasHug/Surprise

     

    GitHub - NicolasHug/Surprise: A Python scikit for building and analyzing recommender systems

    A Python scikit for building and analyzing recommender systems - NicolasHug/Surprise

    github.com

     

    Collorative Filtering.

    Collaborative Filtering 의 상세한 내용은 아래 링크의 페이지를 참고해주세요.

     

    https://westlife0615.tistory.com/297

     

    Collaborative Filtering 이해하기

    - 목차 키워드.Collabortive FilteringImplicit vs ExplicitData Imputation 들어가며."Collaborative Filtering (협업 필터링)" 은 흔히 "Content-based Filtering" 과 흔히 비교됩니다. 두가지 기법 모두 특정 사용자가 선호할

    westlife0615.tistory.com

     

    먼저 "Collaborative Filtering (협업필터링)" 에 대해서 간단히 살펴보겠습니다.

    협업 필터링은 user 와 item 간의 상호작용 데이터를 활용합니다.

    보통 아래와 같은 데이터셋이 흔히 사용됩니다.

    사용자 A 가 상품 B 에 몇점의 점수를 주었는지에 대한 Rating Dataset 이 활용되구요.

     

    이렇게 쌓인 Rating Dataset 을 통해서 user 와 item 간의 관계를 분석합니다.

    Rating Dataset 은 아래와 같은 Matrix 로 표현이 가능한데요.

    movieId 에 해당하는 각각의 Vector 들은 유사도를 비교하여 유사 상품들은 알아낼 수 있습니다.

    이를 다른 관점에서 표현하자면,

    Item A 와 Item B 를 선호하거나 비선호하는 User 들의 패턴이 유사하다면, Item A 와 B 는 유사한 상품으로 취급할 수 있다는 의미입니다.

     

     

     

    이러한 방식으로 Collaborative Filtering 이 동작합니다.

     

     

    scikit-surprise 사용하기.

    먼저 아래의 명령어를 통해서 scikit-surprise 라이브러리를 설치할 수 있습니다.

    pip install scikit-surprise

     

    Load Movielens Dataset.

    숫자 인식 데이터를 제공하는 MNIST 처럼 추천 시스템 모델링에선 Movielens 데이터셋이 활용됩니다.

    그리고 scikit-surprise 의 Dataset 모듈은 Movielens 데이터셋을 로드하는 내장함수를 가집니다.

    활용 예시는 아래와 같습니다.

    from surprise import Dataset
    data = Dataset.load_builtin("ml-100k")

     

    load_builtin 함수를 통해서 Movielens 의 데이터셋을 조회할 수 있구요.

    load_builtin 함수는 Dataset 객체를 반환하게 됩니다.

     

    raw_ratings.

    Dataset 객체는 내부적으로 raw_ratings 변수를 가집니다.

    raw_ratings 는 List 타입의 변수로써 Ratings Dataset 을 그 값으로 취합니다.

    raw_ratings 는 길이가 4인 Tuple 을 가지는 List 로 구현되어 있구요.

    Tuple 의 각 값들은 user, item, rating, timestamp 을 값을 가집니다.

     

    raw_ratings 변수를 DataFrame 으로 변환하면 아래와 같습니다.

    import pandas as pd
    pd.DataFrame(Dataset.load_builtin("ml-100k").raw_ratings, 
    	columns=['user', 'item', 'rating', 'timestamp'])

     

    load_from_df.

    load_from_df 함수는 Pandas DataFrame 으로부터 Dataset 을 생성합니다.

    load_from_df 함수는 입력값으로 사용된 DataFrame 을 파싱하여 Dataset 객체를 생성합니다.

    파싱하는 칼럼의 순서는 user, item, rating 와 같습니다.

    또한 Reader 라는 추가적인 인자를 필요로 하는데요.

    Reader 는 rating 의 범위를 제한합니다.

    저의 경우에는 0 ~ 5 점의 범위를 가지므로 Reader(rating_scale=[0, 5]) 를 입력하였습니다.

    from surprise import Dataset, Reader
    import pandas as pd
    
    ratings = [
        ['user1', 'item1', 1],
        ['user1', 'item2', 2],
        ['user1', 'item3', 3],
        ['user1', 'item4', 4],
        ['user2', 'item1', 5],
        ['user2', 'item2', 1],
        ['user2', 'item3', 2],
        ['user2', 'item4', 3],
    ]
    ratings_df = pd.DataFrame(ratings)
    ratings_df
    dataset = Dataset.load_from_df(ratings_df, reader=Reader(rating_scale=[0, 5]))

     

    print(dataset.raw_ratings)

     

     

    construct_trainset.

    construct_trainset 는 Training Dataset 으로 사용할 메모리 상의 데이터들을 Dataset 객체로 변환하는 함수입니다.

    from surprise import Dataset, Reader
    
    ratings = [
        ['user1', 'item1', 1, None],
        ['user1', 'item2', 2, None],
        ['user1', 'item3', 3, None],
        ['user1', 'item4', 4, None],
        ['user2', 'item1', 5, None],
        ['user2', 'item2', 1, None],
        ['user2', 'item3', 2, None],
        ['user2', 'item4', 3, None],
    ]
    
    dataset = Dataset(reader=Reader(rating_scale=[0, 5]))
    trainset = dataset.construct_trainset(ratings)

     

    to_inner_uid.

    Trainset 객체는 to_inner_uid 함수를 가집니다.

    to_inner_uid 함수는 Raw User Id 를 Encoded User Id 로 변환시켜주는 Mapper 함수입니다.

    Trainset 객체는 학습을 위해서 내부적으로 사용자 정보를 숫자로 인코딩하게 되는데요.

    원본 데이터와 인코딩된 데이터를 위한 Mapper 함수로 생각하시면 됩니다.

    trainset.to_inner_uid('user1')
    --> 0
    
    trainset.to_inner_uid('user2')
    --> 1
    
    trainset.to_inner_uid('user3')
    --> ValueError: User user3 is not part of the trainset.

     

    to_inner_iid.

    to_inner_uid 함수와 마찬가지로 to_inner_iid 는 원본의 상품 아이디를 인코딩된 상품 아이디로 변환해주는 Mapper 함수입니다.

    아마 to_inner_iid 를 풀어 쓰면 "to inner item id" 가 될 것 같네요.

     

    trainset.to_inner_iid('item1')
    --> 0
    
    trainset.to_inner_iid('item10')
    --> ValueError: Item item10 is not part of the trainset.

     

    to_raw_uid.

    to_raw_uid 함수는 to_inner_uid 의 Inverse Mapper 함수입니다.

    즉, 인코딩된 User Id 를 원본 User Id 로 변환시키는 기능을 수행합니다.

    trainset.to_raw_uid(0)
    --> 'user1'
    
    trainset.to_raw_uid(1)
    --> 'user2'
    
    trainset.to_raw_uid(2)
    --> ValueError: 2 is not a valid inner id.

     

    to_raw_iid.

    to_raw_iid 는 to_raw_uid 와 마찬가지로 상품 아이디에 대한 디코딩을 수행합니다.

     

    trainset.to_raw_iid(0)
    --> 'item1'
    
    trainset.to_raw_iid(10)
    --> ValueError: 10 is not a valid inner id.

     

     

    ir , ur (Item Ratings, User Ratings)

    Trainset 객체는 ir 이라는 변수를 가집니다.

    ir 는 Item Ratings 의 약자로 List 와 Dictionary 를 함께 취하는 자료형입니다.

     

    Trainset.ir 는 아래와 같은 형태를 취하는데요.

    특정 Item 에 평점을 매긴 User 과 Rating 정보를 List 형식으로 취합니다.

    trainset.ir

     

    구체적인 예시로 아래와 같이 user1, user2 그리고 item1, item2, item3 에 대한 Ratings 데이터셋이 존재한다고 가정합니다.

    이 상태에서 Trainset 를 생성하게 되면 ir 의 값은 아래와 같이 생성됩니다.

    from surprise import Dataset, Reader
    import pandas as pd 
    
    ratings = [
        ('user1', 'item1', 4),
        ('user1', 'item2', 2),
        ('user2', 'item3', 3)
    ]
    
    dataset = Dataset.load_from_df(pd.DataFrame(ratings), reader=Reader(rating_scale=[1, 5]))
    trainset = dataset.build_full_trainset()
    print(f"trainset.ur : {trainset.ur}")
    print(f"trainset.ir : {trainset.ir}")
    trainset.ur : defaultdict(<class 'list'>, {0: [(0, 4.0), (1, 2.0)], 1: [(2, 3.0)]})
    trainset.ir : defaultdict(<class 'list'>, {0: [(0, 4.0)], 1: [(0, 2.0)], 2: [(1, 3.0)]})

     

    ur 변수에서 확인할 수 있는 정보는

    - 0번 User 는 0번 Item 에 4 의 Rating 을 주었고, 1번 Item 에 2 의 Rating 을 주었습니다.

    - 1번 User 는 2번 Item 에 3 의 Rating 을 주었습니다.

    여기서 0번 User 는 'user1' 에 해당하며, 1 번 User 는 'user2' 에 해당합니다.

     

    ir 변수에서 확인할 수 있는 정보는

    - 0 번 Item 은 0 번 User 에 의해서 4 의 Rating 을 받았고,

    - 1 번 Item 은 0 번 User 에 의해서 2 의 Rating 을 받았고,

    - 2 번 Item 은 1 번 User 에 의해서 3 의 Rating 을 받았습니다.

     

     

    construct_testset.

    construct_testset 함수는 Ratings 데이터로부터 테스트를 위한 데이터셋을 생성합니다.

    Dataset 와 Trainset 은 별도의 Class 가 존재하지만, Testset 은 단순한 파이썬 리스트 객체입니다.

    from surprise import Dataset, Reader
    
    ratings = [
        ['user1', 'item1', 1, None],
        ['user1', 'item2', 2, None],
        ['user1', 'item3', 3, None],
        ['user1', 'item4', 4, None],
        ['user2', 'item1', 5, None],
        ['user2', 'item2', 1, None],
        ['user2', 'item3', 2, None],
        ['user2', 'item4', 3, None],
    ]
    
    dataset = Dataset(reader=Reader(rating_scale=[0, 5]))
    trainset = dataset.construct_trainset(ratings)
    testset = dataset.construct_testset(ratings)
    
    
    type(trainset)
    --> surprise.trainset.Trainset
    
    type(dataset)
    --> surprise.dataset.Dataset
    
    type(testset)
    --> list

     

     

    build_full_trainset.

    build_full_trainset 은 모든 Dataset 을 Trainset 으로 변환하는 함수입니다.

    즉, Testset 으로 Split 하지 않습니다.

    일반적인 머신러닝의 예측 모델의 경우에는 새로운 데이터가 추가되어도 예측이 가능합니다.

    Linear Regression 을 예로 들면, 새로운 데이터가 추가되더라고 데이터의 Feature 들을 활용하여 Output 을 예측할 수 있습니다.

    반면 Matrix Factorization 의 경우에는 새로운 User 또는 Item 은 예측할 수가 없습니다.

    왜냐하면 이어지는 글에서 또다시 설명드리겠지만,

    User 와 Item 자체가 곧 Feature 이기에 새로운 User 와 Item 은 학습된 feature 에 존재하지 않죠.

    그래서 Cold Start 문제가 야기됩니다.

     

    만약 Dataset 을 Trainset 과 Testset 으로 분할한 이후에 Trainset 으로만 학습된다면,

    Trainset 에서 누락된 Testset 의 User 와 Item 은 예측 과정에서 활용할 수 없게 되는 문제가 있습니다.

    사실 Dataset 의 양이 많다면 이렇게 누락되는 경우는 드물긴하지만,

    그렇지 않는 현실적인 상황이 존재하기 때문에 build_full_trainset 함수가 제공됩니다.

    그래서 어느 정도 Parameter 들이 최적화된다면 ( n_factors, reg_all, biased 등 )

    build_full_trainset 함수를 통해 Testset 으로 데이터가 누락되는 경우를 차단합니다.

     

    build_full_trainset 의 사용법은 아래와 같습니다.

    from surprise import Dataset
    dataset = Dataset.load_builtin("ml-100k")
    trainset = dataset.build_full_trainset()

     

     

     

     

    반응형
Designed by Tistory.