베이스라인이나 공부해보자.

순서는 아래로 하나하나씩 한줄씩 공부해보자.

preprocessing.py 부터 보는 것이 좋겠습니다.

데이터가 어떻게 전처리되는지 이해하는 것이 첫 단계입니다 메타데이터(감독, 장르, 작가 등)가 어떻게 처리되는지 볼 수 있습니다

datasets.py

전처리된 데이터가 어떤 형태로 모델에 입력되는지 이해할 수 있습니다 데이터 로딩 및 배치 생성 과정을 볼 수 있습니다

modules.py & models.py

모델의 기본 구성 요소와 전체 아키텍처를 이해할 수 있습니다

trainers.py & run_train.py

실제 학습이 어떻게 이루어지는지 볼 수 있습니다

inference.py

학습된 모델이 어떻게 추천을 생성하는지 이해할 수 있습니다

utils.py

보조함수


preprocessing.py

import pandas as pd  

def main():
    genres_df = pd.read_csv("../data/train/genres.tsv", sep="\t")
    
    # genres_df의 "genre" 컬럼을 수치형으로 변환합니다
    # pd.factorize는 문자열 카테고리를 0부터 시작하는 정수로 인코딩합니다
    # 예: 'Action' -> 0, 'Drama' -> 1, 'Comedy' -> 2 등
    # array는 변환된 숫자값들의 배열, index는 원래 장르명들의 리스트입니다
    array, index = pd.factorize(genres_df["genre"])
    
    # 변환된 숫자값들을 다시 genres_df의 "genre" 컬럼에 할당합니다
    genres_df["genre"] = array
    
    # groupby를 사용하여 각 영화(item)별로 장르 리스트를 만들고
    # 이를 JSON 형태로 저장합니다
    # 예: {"1": [0,1,2], "2": [1,3]} -> 영화 1번은 0,1,2번 장르를, 영화 2번은 1,3번 장르를 가짐
    genres_df.groupby("item")["genre"].apply(list).to_json(
        "data/Ml_item2attributes.json"
    )

if __name__ == "__main__":
    main()

우선은 뭐 별거 없긴하다. 그냥 genres파일을 숫자값으로 인코딩해서 json형식으로 변환한건데

인코딩해서 수치 데이터로 만들어서 딥러닝 모델에 처리가능하게 한건가? Embedding 레이어를 위하여? 근데 이게 인코딩이 순서에 따른 인코딩아닌가? 각 장르별 상관관계가 가까운 순서대로 인코딩하는게 낫지 않을까?

one-hot encoding으로 바꾸는게 낫지않을까? 근데 그러면 희소 벡터 많이 생겨서 연산량 많아질 거 같긴한데 좀 더 나으려나?

그리고 다른 director나 title같은건 인코딩 못할거같은데 어카지? 굳이 인코딩을 해야하나? 좀 자주 나오는 얘들로만 전처리해서 인코딩해버릴까?

해볼거

  1. genres를 원-핫인코딩으로 변경
  2. 다른 파일들 차원의 저주 안나도록 전처리해서 인코딩해보기

datasets.py

얜 좀 코드가 길다. 다는 못 쓸거 같고 아래와 같이 두개의 class가 있는데

# PyTorch의 Dataset 클래스를 상속받는 사전학습용 데이터셋 클래스

class PretrainDataset(Dataset):


# SASRec 모델을 위한 데이터셋 클래스
class SASRecDataset(Dataset):

PretrainDataset 클래스의 주요 특징:

  1. 데이터 준비
    • 시퀀스 마스킹 (일부 아이템을 마스크로 대체)
    • 세그먼트 예측을 위한 데이터 준비
    • 부정 샘플링 (학습에 사용할 부정 예제 생성)
  2. 데이터 처리
    • 패딩 (모든 시퀀스를 동일한 길이로 맞춤)
    • 속성 정보 변환 (원-핫 인코딩)
    • 텐서 변환 (PyTorch 모델 입력용)
  3. 검증
    • 모든 시퀀스 길이가 올바른지 확인
    • 데이터 형식이 올바른지 검증

SASRecDataset 클래스의 주요 특징:

  1. 데이터 타입별(train/valid/test/submission) 다른 처리 방식 적용
  2. 시계열 특성을 고려한 입력/타겟 분리
  3. 부정 샘플링을 통한 학습 데이터 보강
  4. 테스트 시 별도의 부정 샘플 사용 가능

사실 위에가 뭔 소린지 모르겠음. ㅋㅋ


PretrainDataset class가 실제로 어떻게 동작하는가?

user,item,time
11,4643,1230782529
11,170,1230782534
11,531,1230782539
11,616,1230782542
11,2140,1230782563
11,2722,1230782583
...

이런 데이터가 들어왔다고 치면

  1. 시퀀스 데이터 구성
User 11의 시청 시퀀스:
[4643 → 170 → 531 → 616 → 2140 → 2722 → 2313 → 2688 → 2428 → 3113 → 1591 → 2600 → 8169 → 2572]
  1. split_sequence() -> 시퀀스를 부분 시퀀스로 분할합니다. 예를 들어 max_len이 5라고 가정하면:
첫번째 부분: [4643]
두번째 부분: [4643, 170]
세번째 부분: [4643, 170, 531]
네번째 부분: [4643, 170, 531, 616]
다섯번째 부분: [4643, 170, 531, 616, 2140]
...
  1. getitem() -> 부분 시퀀스 [4643, 170, 531, 616, 2140]를
# A) Masked Item Prediction
# 원본
sequence = [4643, 170, 531, 616, 2140]

# 마스킹 확률(mask_p)이 0.2라고 가정
masked_sequence = [4643, MASK, 531, MASK, MASK]

# 부정 샘플링 (사용자가 보지 않은 다른 영화 ID)
neg_items = [4643, 999, 531, 888, 777]
# B) Segment Prediction
# 랜덤하게 길이 2의 세그먼트를 선택했다고 가정
원본: [4643, 170, 531, 616, 2140]
start_id = 1 (랜덤 선택)

masked_segment = [4643, MASK, MASK, 616, 2140]
pos_segment = [MASK, 170, 531, MASK, MASK]  # 실제 세그먼트
neg_segment = [MASK, 999, 888, MASK, MASK]  # 다른 무작위 세그먼트
# C) Attribute Prediction
# 각 영화의 속성을 원-핫 인코딩으로 변환
# 속성 크기가 5라고 가정
4643의 속성: [1, 0, 1, 0, 1]  # 예: 액션(1), SF(3), 스릴러(5) 장르
170의 속성:  [0, 1, 1, 0, 0]  # 예: 드라마(2), SF(3) 장르
531의 속성:  [1, 1, 0, 0, 1]
...

attributes = [
    [1, 0, 1, 0, 1],  # 4643 영화의 속성
    [0, 1, 1, 0, 0],  # 170 영화의 속성
    [1, 1, 0, 0, 1],  # 531 영화의 속성
    ...
]
  1. 모든 시퀀스의 길이를 동일하게 맞추기 위해 패딩처리를 해줍니다.
# 사용자 A의 시청 기록
[4643, 170, 531]  # 길이 3

# 사용자 B의 시청 기록
[4643, 170, 531, 616, 2140]  # 길이 5

# 사용자 C의 시청 기록
[4643, 170, 531, 616, 2140, 2722, 2313]  # 길이 7

위와 같은 시청 기록을

# 사용자 A의 시청 기록 (패딩 후)
[0, 0, 0, 0, 4643, 170, 531]  # 앞쪽에 0으로 패딩

# 사용자 B의 시청 기록 (패딩 후)
[0, 0, 4643, 170, 531, 616, 2140]  # 앞쪽에 0으로 패딩

# 사용자 C의 시청 기록 (패딩 필요 없음)
[4643, 170, 531, 616, 2140, 2722, 2313]  # 이미 길이가 7

다른 길이의 시퀀스들을 딥러닝 모델을 위해 같은 크기로 맞춰줍니다.

  1. 데이터를 최종적으로 반환해줍니다.

    return (
        torch.tensor(attributes),              # 영화 속성 정보
        torch.tensor(masked_sequence),         # 마스킹된 시퀀스
        torch.tensor(pos_items),              # 원본 시퀀스
        torch.tensor(neg_items),              # 부정 샘플 시퀀스
        torch.tensor(masked_segment),         # 마스킹된 세그먼트
        torch.tensor(pos_segment),            # 실제 세그먼트
        torch.tensor(neg_segment)             # 부정 세그먼트
    )
    

SASRecDataset class가 어떻게 동작하는가?

user_id=11의 시청 기록:
[4643, 170, 531, 616, 2140, 2722, 2313, 2688, 2428, 3113, 1591, 2600, 8169, 2572]

이 데이터가 각 데이터 타입(“train”, “valid”, “test”, “submission”)별로 어떻게 처리되는지 살펴보겠습니다.

  1. “train” 모드
# data_type="train"일 때
input_ids  = [4643, 170, 531, 616, 2140, 2722, 2313, 2688, 2428, 3113, 1591]     # 마지막 3개 제외
target_pos = [170, 531, 616, 2140, 2722, 2313, 2688, 2428, 3113, 1591, 2600]     # input_ids에서 한 칸씩 밀린 것
target_neg = [999, 888, 777, 666, 555, 444, 333, 222, 111, 000, 123]             # 랜덤 샘플링된 부정 예시
answer = [0]  # 학습시에는 사용안함
  1. “valid” 모드
# data_type="valid"일 때
input_ids  = [4643, 170, 531, 616, 2140, 2722, 2313, 2688, 2428, 3113, 1591, 2600]  # 마지막 2개 제외
target_pos = [170, 531, 616, 2140, 2722, 2313, 2688, 2428, 3113, 1591, 2600, 8169]  # input_ids에서 한 칸씩 밀린 것
target_neg = [999, 888, 777, 666, 555, 444, 333, 222, 111, 000, 123, 456]           # 랜덤 샘플링된 부정 예시
answer = [8169]  # 검증용 정답
  1. “test” 모드

    # data_type="test"일 때
    input_ids  = [4643, 170, 531, 616, 2140, 2722, 2313, 2688, 2428, 3113, 1591, 2600, 8169]  # 마지막 1개 제외
    target_pos = [170, 531, 616, 2140, 2722, 2313, 2688, 2428, 3113, 1591, 2600, 8169, 2572]  # input_ids에서 한 칸씩 밀린 것
    target_neg = [999, 888, 777, 666, 555, 444, 333, 222, 111, 000, 123, 456, 789]            # 랜덤 샘플링된 부정 예시
    answer = [2572]  # 테스트용 정답
    
  2. “submission” 모드

# data_type="submission"일 때
input_ids  = [4643, 170, 531, 616, 2140, 2722, 2313, 2688, 2428, 3113, 1591, 2600, 8169, 2572]  # 전체 시퀀스
target_pos = [4643, 170, 531, 616, 2140, 2722, 2313, 2688, 2428, 3113, 1591, 2600, 8169, 2572]  # 사용 안함
target_neg = [999, 888, 777, 666, 555, 444, 333, 222, 111, 000, 123, 456, 789, 321]             # 랜덤 샘플링
answer = []  # 답 없음

패딩 처리 (max_len=7 가정)

# 예: train 모드의 경우
원본 input_ids = [4643, 170, 531, 616, 2140, 2722, 2313, 2688, 2428, 3113, 1591]

# 1. 패딩 추가
input_ids  = [0, 0, 0, 0, 4643, 170, 531, 616, 2140, 2722, 2313, 2688, 2428, 3113, 1591]
target_pos = [0, 0, 0, 0, 170, 531, 616, 2140, 2722, 2313, 2688, 2428, 3113, 1591, 2600]
target_neg = [0, 0, 0, 0, 999, 888, 777, 666, 555, 444, 333, 222, 111, 000, 123]

# 2. 최대 길이(7)로 자르기 (뒤에서부터 7개만 유지)
input_ids  = [2688, 2428, 3113, 1591]
target_pos = [2428, 3113, 1591, 2600]
target_neg = [222, 111, 000, 123]

최종 반환

return (
    torch.tensor(11),                # user_id
    torch.tensor(input_ids),         # 입력 시퀀스
    torch.tensor(target_pos),        # 긍정 타겟
    torch.tensor(target_neg),        # 부정 타겟
    torch.tensor(answer),            # 정답
)
  1. 데이터 타입에 따라 다르게 시퀀스를 자릅니다:
    • train: 마지막 3개제외, validation과 test를 위해 제외
    • valid: 마지막 2개 제외, 모델 성능 검증에 사용. 8169를 맞추는지 확인
    • test: 마지막 1개 제외, 최종 모델 테스트에 사용. 2572를 맞추는지 확인
    • submission: 실제 예측할 때는 모든 데이터를 사용하여 다음 영화를 추천
  2. 각 입력 아이템에 대해 다음 아이템을 예측하도록 구성됩니다 (target_pos)
  3. 부정적 예시(target_neg)를 각 위치마다 생성합니다

PretrainDataset 클래스

# 사전학습(Pre-training)을 위한 데이터 처리
목적: BERT처럼 마스킹과 세그먼트 예측을 통한 사전학습

return (
    torch.tensor(attributes),         # 영화 속성 정보
    torch.tensor(masked_sequence),    # 마스킹된 시퀀스
    torch.tensor(pos_items),         # 원본 시퀀스
    torch.tensor(neg_items),         # 부정 샘플 시퀀스
    torch.tensor(masked_segment),    # 마스킹된 세그먼트
    torch.tensor(pos_segment),       # 실제 세그먼트
    torch.tensor(neg_segment)        # 부정 세그먼트
)

SASRecDataset 클래스

# 실제 추천을 위한 데이터 처리
목적: 다음 영화 추천을 위한 순차적 학습

return (
    torch.tensor(user_id),           # 사용자 ID
    torch.tensor(input_ids),         # 입력 시퀀스
    torch.tensor(target_pos),        # 실제 다음 아이템
    torch.tensor(target_neg),        # 부정 샘플 아이템
    torch.tensor(answer)             # 정답 (valid/test용)
)

주요 차이점:

  • PretrainDataset:
    • 영화의 속성 정보도 포함
    • BERT 스타일의 마스킹 사용
    • 세그먼트 단위 예측도 수행
    • run_pretrain.py에서 사용
  • SASRecDataset:
    • 사용자 ID 포함
    • train/valid/test/submission 모드 구분
    • 순차적 예측에 초점
    • run_train.py에서 사용