본문 바로가기
파이썬

파이썬 프로젝트(1) : 펀딩 추천 시스템 - userbase, contentbase

by 스노위13 2022. 9. 14.

1. 교육처 : 넥스트아이티교육센터(대전)

2. 프로젝트 내용 : 이전에 Spring 과제에서 만들었던 펀딩 사이트의 데이터를 활용하여 추천 시스템을 만들었다. 사용자가 찜한 펀딩 데이터를 기반으로 userbase와 contentbase로 유저가 좋아할 만한 펀딩을 추천하도록 했다. 여기서 만든 추천 리스트를 이후에 flask를 사용해서 홈 화면으로 보내주었다.

3. 과정 중 특이점 : contentbase에서 자신이 찜한 펀딩에 대한 개인 선호도가 없어서 비슷한 카테고리만 추천을 하는 부분이 아쉬웠고 다음번에는 좀 더 데이터가 다양한 내용으로 만들어야겠다고 생각했다. 최소한 별점이라도 있었다면 조금 더 좋았을 텐데... 팀프로젝트에서는 이 부분을 좀 더 신경써야겠다.

4. 화면 및 코드


1) DB에 더미데이터 만들기

회원이 찜을 했을 때 저장하는 mem_like 테이블에 더미데이터를 넣어서 사용자가 찜한 데이터를 임의로 만들었다 
TRUNC(DBMS_RANDOM.VALUE(1,405))를 활용해서 랜덤하게 숫자를 넣고
CONNECT BY LEVEL <= 40 으로 들어갈 갯수를 조절하였다. 한 번에 40개씩 넣을 수 있어서 완전 편했음 

INSERT INTO mem_like(like_no, mem_id, fu_no)
SELECT fu_seq.nextval,'qwer',TRUNC(DBMS_RANDOM.VALUE(1,405)) AS RANDOM_NUM
FROM dual
CONNECT BY LEVEL <= 40;


2) userbase : 자신과 비슷한 펀딩을 고른 유저가 찜한 펀딩 중 내가 찜한 펀딩을 제외한 나머지로 리스트를 만든다. 

import pandas as pd
import mydb
from sklearn.metrics.pairwise import cosine_similarity

def get_user_recomment(userId):
    db = mydb.Mydb()

    sql = """
    SELECT
    mem_id  , a.fu_no   , fu_title  , fu_cate, fu_like
    FROM mem_like a, funding b
    WHERE a.fu_no = b.fu_no
    """

    df_funding = pd.read_sql(sql, con=db.conn)
    df_funding.rename(columns={'MEM_ID': 'userId'}, inplace=True)
    df_funding.rename(columns={'FU_NO': 'fu_no'}, inplace=True)
    df_funding.rename(columns={'FU_TITLE': 'title'}, inplace=True)
    df_funding.rename(columns={'FU_CATE': 'category'}, inplace=True)
    df_funding.rename(columns={'FU_LIKE': 'like'}, inplace=True)

    user_funding = df_funding.pivot_table('like', index='userId', columns='title')
    user_funding.fillna(0, inplace=True)

    user_base_metric = cosine_similarity(user_funding)
    user_base_metric_df = pd.DataFrame(data=user_base_metric, index=user_funding.index
                                       ,columns=user_funding.index)

    def get_user_base(id, df_fundings, user_base_metric_df):
        sim_user = user_base_metric_df[id].sort_values(ascending=False)[:3]
        user_like_funding = df_funding[df_funding['userId'] == userId]
        user_like = set(user_like_funding['fu_no'].values.tolist())
        id_list = sim_user.index.tolist()[1:]
        data=[]
        for i in id_list:
            print(f'user: {i}')
            item = get_user_item(i, id, df_fundings)
            data = data + item
        set_item = set(data) - user_like
        return set_item

    def get_user_item(id, userId, df_funding):
        funding_list = df_funding[df_funding['userId'] == id]
        best = funding_list.sort_values(by='like', ascending=False)[:4]
        return best['fu_no'].values.tolist()

    id = userId
    result = get_user_base(str(id), df_funding, user_base_metric_df)
    print(f'userbase: {result}')

    funosql = """
    SELECT fu_title, (fu_like)/10 as fu_like, fu_no, fu_cate, fu_amount, fu_img
    FROM funding
    WHERE fu_no= :1
    """

    temp = []

    for i in result:
        user_recommend = db.get_select_param(funosql, [i])
        temp.append(user_recommend[0])

    print(temp)
    result = pd.DataFrame(temp, columns=['title', 'like', 'fu_no', 'category', 'cnt', 'fu_img'])
    return result

 

3) contentbase : 내가 찜한 펀딩과 비슷한 펀딩을 가지고 와서 리스트를 만든다. 별점이 없어서 찜한 펀딩 내에서 선호도를 구별해내지 못한 점이 아쉬웠다. 

import pandas as pd
import mydb
from sklearn.metrics.pairwise import cosine_similarity
from sklearn.feature_extraction.text import CountVectorizer

def get_cate_recomment(userId):
    db = mydb.Mydb()

    def get_imdbs_score(p_x, p_m, p_c):
        p_v = p_x['cnt']
        p_r = p_x['like']
        return (p_v / (p_v + p_m) * p_r) + (p_m / (p_m + p_v) * p_c)

    sql = """
    SELECT fu_title , (fu_like)/10 as fu_like , fu_no , fu_cate, fu_amount, fu_img
    FROM   funding
    """
    df_funding = pd.read_sql(sql, con=db.conn)
    df_funding.fillna(0, inplace=True)

    df_funding.rename(columns={'FU_TITLE': 'title'}, inplace=True)
    df_funding.rename(columns={'FU_LIKE': 'like'}, inplace=True)
    df_funding.rename(columns={'FU_NO': 'fu_no'}, inplace=True)
    df_funding.rename(columns={'FU_AMOUNT': 'cnt'}, inplace=True)
    df_funding.rename(columns={'FU_CATE': 'category'}, inplace=True)
    df_funding.rename(columns={'FU_IMG': 'fu_img'}, inplace=True)

    m = df_funding['cnt'].quantile(0.1)
    m_data = df_funding.copy().loc[df_funding['cnt'] >= m]
    c = m_data['like'].mean()

    m_data['score'] = m_data.apply(get_imdbs_score, args=(m, c), axis=1)
    m_data['category'] = m_data['category']
    m_data.reset_index(drop=True, inplace=True)
    count_vetor = CountVectorizer(ngram_range=(1, 1))
    category_vector = count_vetor.fit_transform(m_data['category'])
    AA = category_vector.toarray()
    gensim_sim = cosine_similarity(category_vector, category_vector).argsort()[:, ::-1]

    def get_contentbase_funding(data, d_sim, title, top=20):
        target_movie_index = data[data['title'] == title].index.values
        sim_index = d_sim[target_movie_index, :top].reshape(-1)
        sim_index = sim_index[sim_index != target_movie_index]
        result = data.iloc[sim_index].sort_values('score', ascending=False)[:9]
        print(f'contentbase : {result}')
        return result

    titlesql = """
    SELECT rnum,fu_title
    FROM (SELECT rownum as rnum, fu_title
            FROM  (SELECT  fu_title
                    FROM mem_like a , funding b
                    WHERE a.fu_no = b.fu_no
                    AND mem_id = :1
                    AND fu_cate = (SELECT fu_cate
                                    FROM (SELECT fu_cate , count(fu_cate) as caterank
                                            FROM  mem_like a , funding b
                                            WHERE a.fu_no = b.fu_no
                                            AND a.mem_id = :1 
                                            group by fu_cate
                                            ORDER BY caterank desc)
                                            WHERE rownum < 2)   
                    order by DBMS_RANDOM.RANDOM               
                    ) a
            ) b
    WHERE rnum=1
    """
    ranktitle = db.get_select_param(titlesql, [userId])
    ranktitle = ranktitle[0]
    text = ranktitle[1]

    temp = get_contentbase_funding(m_data, gensim_sim, text)
    result = temp.sample(frac=1).reset_index(drop=True)
    return result

댓글