Aromajoin Project

강화학습 (Reinforce Learning) 추천 알고리즘

qkrwnsmir 2024. 10. 13. 17:31

Matrix Factorization도 추천방식의 하나지만, 강화학습방법또한 점차 강력해지는 좋은 알고리즘이다.

 

강화학습에서 주요 개념은 다음과 같다:

 

에이전트(Agent): 추천 시스템이 사용자를 대신하여 추천을 제공하는 역할.

상태(State): 사용자가 현재 시청하는 동영상 및 사용 중인 향기 등의 정보.

행동(Action): 추천할 향기나 동영상을 선택하는 것.

보상(Reward): 사용자가 추천된 향기/동영상에 대해 좋아요(positive) 또는 싫어요(negative)를 남기는 등의 피드백.

 

즉 간단하게 요약하면 이것저것 다해보다가 맞으면 + 틀리면 - 를 받고 점차 맞는길을 알아가는 것이다.

 

바로 아로마 플레이어에 적용할 방법을 알아보자.

 

강화학습 적용에 필요한 요소

 

상태(State):

사용자가 현재 시청하는 동영상 ID (video_id)

사용자가 현재 사용 중인 향기 ID (aroma_id)

사용자 정보 (user_serial)

행동(Action):

향기 추천: 사용자가 현재 시청하는 동영상에서 어떤 향기를 추천할지 결정.

동영상 추천: 시청한 동영상 및 향기 패턴을 바탕으로 다른 동영상을 추천.

보상(Reward):

사용자가 추천받은 동영상을 시청하거나 추천받은 향기를 사용하는지에 따라 보상을 설정.

예를 들어, 추천한 향기에 대해 좋아요(1)를 남기면 양의 보상, 싫어요(-1)를 남기면 음의 보상.

 

강화학습을 위한 환경 설정

 

상태: 현재 사용자 정보, 시청 중인 동영상, 사용 중인 향기.

행동: 향기 또는 동영상을 추천하는 선택.

보상: 추천받은 동영상이나 향기에 대해 사용자가 좋아요/싫어요로 반응.

 


 

위의 백그라운드를 토대로 알고리즘을 설계해본다.

 

Q-Learning 알고리즘을 적용한 추천 시스템

 

1. Q 테이블 초기화: 가능한 모든 상태와 행동에 대해 Q 값을 초기화한다.

2. 에이전트가 행동을 선택: 현재 상태에서 가장 높은 Q 값을 가진 행동을 선택한다(탐험과 활용의 균형 필요).

3. 보상 수집: 사용자가 추천받은 콘텐츠에 대한 반응으로 보상을 받는다.

4. Q 값 업데이트: 새로운 Q 값을 업데이트 한다.

5. 반복: 에이전트는 지속적으로 상태를 갱신하고, 행동을 선택하여 보상을 최대화하는 방향으로 학습을 진행한다.

 

import numpy as np
import random

# Q 테이블 초기화: 상태(사용자, 향기, 동영상)와 행동(추천)을 매핑
# 여기서는 간단히 사용자, 동영상, 향기 각각 5개의 상태/행동을 가정
Q_table = np.zeros((5, 5, 5))  # 사용자, 동영상, 향기 (5개씩 가정)

# 행동(Action): 향기나 동영상 추천 (0~4) 중 하나
actions = [0, 1, 2, 3, 4]

# 학습 하이퍼파라미터
alpha = 0.1  # 학습률
gamma = 0.6  # 할인율
epsilon = 0.1  # 탐험 비율 (epsilon-greedy 탐험)

# 보상 테이블 (가정된 보상, 실제로는 사용자 반응 기반으로 설정)
# 예: 좋아요를 누르면 +1, 싫어요면 -1
reward_table = np.array([
    [1, -1, 1, -1, 1],  # 동영상 1에 대한 향기 사용 반응
    [-1, 1, 1, -1, 1],  # 동영상 2에 대한 향기 사용 반응
    [1, 1, -1, 1, -1],  # 동영상 3에 대한 향기 사용 반응
    [-1, 1, -1, 1, 1],  # 동영상 4에 대한 향기 사용 반응
    [1, 1, -1, -1, 1]   # 동영상 5에 대한 향기 사용 반응
])

# Q-Learning 알고리즘
def q_learning(user, video, aroma, episodes=1000):
    for _ in range(episodes):
        # 현재 상태: 사용자, 동영상, 향기
        state = (user, video, aroma)
        
        # 탐험 또는 활용 선택 (epsilon-greedy)
        if random.uniform(0, 1) < epsilon:
            action = random.choice(actions)  # 탐험: 무작위 행동 선택
        else:
            action = np.argmax(Q_table[state])  # 활용: 최대 Q 값을 가진 행동 선택

        # 보상 계산
        reward = reward_table[video, action]

        # 새로운 상태 (동영상과 향기가 업데이트될 수 있음)
        new_video = random.choice(actions)
        new_aroma = action  # 행동을 통해 추천된 향기로 변경

        # Q 값 업데이트
        old_value = Q_table[state]
        next_max = np.max(Q_table[new_video, new_aroma])
        
        # Q-Learning 업데이트 공식
        Q_table[state] = old_value + alpha * (reward + gamma * next_max - old_value)

        # 상태 업데이트
        state = (user, new_video, new_aroma)

# 사용자, 동영상, 향기 초기 상태
user = 0  # user_serial을 0으로 가정 (5명의 사용자가 있다고 가정)
video = 1  # video_id를 1로 가정 (5개의 동영상 중 하나)
aroma = 2  # aroma_id를 2로 가정 (5개의 향기 중 하나)

# Q-Learning 실행
q_learning(user, video, aroma, episodes=1000)

# 최종 Q 테이블 출력
print("학습된 Q 테이블:")
print(Q_table)

 

코드를 살펴보면

탐험과 활용이란게 나오는데 탐험은 무작위로 시도해보는것, 활용은 학습한 정보에 기반해 최적이라 생각되는 행동을 선택하는것 이다.

 

탐험과 활용을 적절히 조절하는 것이 매우 중요다. 이를 위해 가장 일반적으로 사용하는 방법은 epsilon-greedy 방법이다.

 

Epsilon-Greedy 방법

 

Epsilon-Greedy는 확률적으로 탐험활용을 선택하는 방법.

Epsilon(ε)은 탐험의 비율을 나타내며, 0과 1 사이의 값을 가짐. 예를 들어, ε가 0.1이라면, 10%의 확률로 탐험을 하고 90%의 확률로 활용을 하게 된다.

탐험: ε 확률로 랜덤하게 행동을 선택합니다.

활용: 1-ε 확률로 현재 Q 값이 가장 높은 행동을 선택합니다.

 

즉 초기에는 탐험을 많이 하는것이 좋으므로 ε 값을 높게 설정하여 탐험을 많이한다.

학습이 진행될수록 줄여서 활용을 늘리면 된다.

 

 

여기서 추천한 영상을 누를때마다 수시로 학습을 시킬지, 따로 테이블을 만들어 주기적으로 학습을 시킬지 선택해야한다.

회의를 거친 결과 주로 사용될 알고리즘은 MatrixFactorization 이므로 주기적 update를 하는것이 서버의 성능이나 가격적 측면에서 유효할 것 같다.

-- 사용자-향기/동영상 상호작용 저장 테이블 예시
CREATE TABLE user_interactions (
    interaction_id SERIAL PRIMARY KEY,
    user_serial VARCHAR(25),   -- 사용자 식별자
    video_id VARCHAR(25),      -- 동영상 식별자
    aroma_id VARCHAR(25),      -- 향기 식별자
    action VARCHAR(10),        -- 행동: 'like', 'dislike', 'view', 'use'
    action_value INT,          -- 행동 값: 예를 들어 좋아요=1, 싫어요=-1, 시청/사용 시간(초 단위)
    action_timestamp TIMESTAMP -- 행동 발생 시간
);