working_helen

[모델링] 인코딩 기반 추천 시스템 본문

deep daiv./추천시스템 toy project

[모델링] 인코딩 기반 추천 시스템

HaeWon_Seo 2023. 8. 10. 15:59

 추천 시스템의 첫번째 구현 방법으로 인코딩 기반 추천 시스템을 구현하였다.

 

1. 인코딩 기반 추천시스템 과정

2. 도서 벡터화

  1) CountVectorizer

  2) One-hot encoding

3. 유사도 측정

4. 추천 리스트 작성

5. 한계 및 개선방안


 

1. 인코딩 기반 추천시스템 과정

[구성]
키워드에서 각 단어의 등장 여부를 1과 0의 값으로 두고 도서를 벡터화

→ 도서 간 코사인 유사도 계산
→ 유사도 기반 베스트셀러 추천 리스트 작성

벡터화 :
CountVectorizer 이용 / One-hot encoding
유사도 : 코사인 유사도

[결과]
- One-hot encoding 기반 DTM 선택
- 가중합 코사인 유사도 이용 (4 : 5 : 1 비율)
- 최종 추천 함수 find_sim_book 정의

3가지 document-term matrix(DTM)와 3가지 유사도 행렬

사용한 칼럼 document-term matrix 유사도 행렬
'책소개 키워드 수정본' book_mat_keyword
book_mat
cosine_sim
cosine_sim_re
'키워드' book_mat_keyword_1
book_mat_1
cosine_sim_1
cosine_sim_re_1
'상품명' book_mat_keyword_2
book_mat_2
cosine_sim_2
cosine_sim_re_2

- `book_mat_keyword`와 `cosine_sim`은 CountVectorizer 벡터화를 사용한 경우 변수명

- `book_mat`과 `cosine_sim_re`는 One-hot encoding을 사용한 경우 변수명

 

 

 

2. 도서 벡터화

방법 1) CountVectorizer

: CountVectorizer 클래스는 주어진 문서에 각각의 단어가 몇 번 등장하는지 count하여 DTM를 만든다.

참고 post : 2023.08.08 - [deep daiv./추천시스템 미니 프로젝트] - [모델링] CountVectorizer / 한글 형태소 분석기 KoNLPy

 

- 본 프로젝트에서 사용하는 Dataframe의 경우 키워드 리스트에 각 단어가 중복되어 존재하지 않기 때문에 CountVectorizer를 적용하면 키워드에 존재하는 경우엔 1을, 그렇지 않은 경우엔 0의 값을 가지는 One-hot encoding과 같은 결과의 벡터화를 진행할 수 있다. 

from sklearn.feature_extraction.text import CountVectorizer

# '키워드' DTM 생성
count_vectorizer_1 = CountVectorizer()
book_mat_keyword_1 = count_vectorizer_1.fit_transform(best['키워드'])

book_mat_keyword_1.toarray()

- 문제점 : CountVectorizer를 실행하면 이미 완료된 키워드 토큰화가 다시 토큰화되는 문제가 발생한다.

 

 

방법 2) One-hot encoding

: One-hot encoding은 범주의 개수와 같은 크기의 벡터에 대하여 0과 1로 어떤 범주에 속하는지 표현하는 인코딩이다. 문장에서 특정 단어의 출현 여부를 0과 1로 표현하여 단어 임베딩을 진행할 수 있다.

참고 post : 2023.08.01 - [deep daiv./추천시스템 스터디] - [추천시스템 스터디] 1주차 : 콘텐츠 기반 필터링

 

- 책소개 키워드 수정본', '키워드', '상품명' 3가지 열에 대하여 각 열에 존재하는 모든 단어를 정리한 단어 사전을 만든다.

- 각 도서에 대하여 단어 사전의 단어가 존재하는지 확인해 0과 1로 값을 부여하는 One-hot encoding 함수를 정의한다.

- 3가지 열과 각각의 단어사전을 입력해 vectorize 함수를 통한 인코딩을 진행한다.

# 단어 사전 만들기
word=list()
for i in range(len(best2['키워드'])):
    keyword_list = best2.loc[i, '키워드']
    word += keyword_list
word_dic = list(set(word))

# One-hot encoding 수행하는 함수 정의
def vectorize(df, col, word_dic):
    df_matrix = pd.DataFrame()

    for i in range(df.shape[0]):
        tmp = []
        for voca in word_dic:
            tmp.append(df.loc[i, col].count(voca))
        df_matrix = pd.concat([df_matrix,  pd.DataFrame(tmp).T])
        #df_matrix.append(tmp)
    return df_matrix

# One-hot encoding 수행
df_matrix = vectorize(best2, '키워드', word_dic)
사용한 칼럼 단어 사전 document-term matrix
'책소개 키워드 수정본' book_word_dic book_mat
'키워드' word_dic book_mat_1
'상품명' title_word_dic book_mat_2

- 문제점 : One-hot encoding은 범주가 너무 넓거나 복잡하면 매우 sparse한 형태의 고차원 벡터로 표현되기 때문에 메모리 낭비 및 계산 복잡도가 커지는 단점이 있다. 실제 분석 과정에서 One-hot encoding을 이용한 벡터화 과정이 가장 오랜 시간이 소요되었다.

 

 

 

3. 유사도 측정

- 유사도 종류 중 코사인 유사도를 사용

- cosine_similarity 클래스를 이용해 코사인 유사도를 계산한다.

from sklearn.metrics.pairwise import cosine_similarity

# cosine_similarity(DTM 행렬 입력)
cosine_sim = cosine_similarity(book_mat) # 책소개 키워드 기반 유사도
cosine_sim_1 = cosine_similarity(book_mat_1) # 키워드 기반 유사도
cosine_sim_2 = cosine_similarity(book_mat_2) # 상품명 기반 유사도

 

 

 

4. 추천 리스트 작성

- 주어진 title에 대하여 코사인 유사도가 높은 순부터 내림차순 정렬한 후 상위 n개를 추출하는 함수를 정의한다.

- 세 가지의 코사인 유사도에 가중치를 설정하고, 가중합 유사도를 최종 유사도 판단의 기준 값으로 사용했다.

- 가중치 : '책소개 키워드 수정본' : '키워드' : '상품명' =  4 : 5 : 1

  • 세 가중치의 합이 1이 되도록 설정
  • 상품명의 경우 길이가 너무 짧고 책의 내용과는 상관없는 내용일 수 도 있기에 책의 특성을 많이 담고 있지 않다고 판단해 가장 적은 가중치를 부여함
  • '키워드'의 경우 교보문고에서 직접 부여한 키워드기 때문에 다른 변수보다도 책의 특성을 많이 반영하고 있다 판단해 가장 많은 가중치를 부여함

추천 함수 find_sim_book(best, sim, query_index, top_k)

- 도서 Dataframe, 유사도 행렬, 궁금한 도서 index, 추천 유사 도서 개수를 입력

- best에서부터 index도서와 유사한 top_k개의 도서를 sim 유사도를 기반으로 추천

# 유사도 가중치 적용
sim = 0.4*cosine_sim + 0.5*cosine_sim_1 + 0.1*cosine_sim_2

# 추천 함수 정의
def find_sim_book(best, sim, query_index, top_k):
    # query 인덱스에 대한 유사도 값을 가져옴
    query_similarity = sim[query_index]

    # 코사인 유사도 값을 기준으로 유사도가 높은 순서대로 정렬된 인덱스를 얻음
    sorted_indices = np.argsort(query_similarity)[::-1]

    print(f'검색한 콘텐츠 : {best["상품명"].iloc[query_index]}')
    print('\n출력한 콘텐츠')
    print(sorted_indices)
    print(best['상품명'].iloc[sorted_indices[1:top_k +1]])


# 궁금한 도서의 index, 유사 도서 개수 입력
query_index = 0
top_k = 10

# 추천 함수 실행
find_sim_book(best, sim, query_index, top_k)

 

 

 

 

5. 한계 및 개선 방안

결론 : "동일한 키워드를 얼마나 많이 포함하고 있느냐"를 기준으로 하는 추천 시스템(함수) 구현

 

  • 단어의 빈도 (등장 여부)만을 따지고 의미이나 포함 관계 등을 따지지 못함

- 의미상으론 매우 유사한 두 단어여도 단어 자체가 다르다는 이유로 두 도서 간 유사도에 기여하지 못하는 경우 발생

- 예를 들어, '투자자'와 '투자'의 경우 매우 근접한 단어이지만 단어 자체는 다르기 때문에 한 도서가 '투자자'를, 다른 도서가 '투자'를 포함하고 있다면 이 두 단어는 두 도서의 유사도를 증가시키는 방향으로 기여하지 못하게 됨

- 유사도 측정에 있어서 정확도 감소

 

  • 빈도수가 너무 적은 단어들이 존재

- EDA 결과에서 키워드로 한번만 나오는 단어들이 많았음

- One-hot encoding의 결과 sparse한 고차원 DTM이 생성되면서 계산 효율이 떨어지고 실행에 많은 시간이 소요

 

 

=> 개선 방안

 

  •  추출된 키워드에 대한 전처리 과정 진행

: 너무 빈도수가 적은 키워드는 제외하거나 유사성이 높은 다른 키워드로 대체하는 방법으로 sparsity 문제를 해소한다. 혹은 기존의 키워드를 바탕으로 상위 키워드를 정의하여 학습과 분류가 더 용의한 데이터 형태로 대체하는 방법을 쓴다.

수많은 단어 속에서 키워드를 분류하고 대체하는 방법에 대한 추가 학습이 필요하다.

 

  • 단어의 의미도 유사도 측정에 반영할 수 있는 벡터화 방법을 사용

: 단어의 빈도 (등장 여부)만 따지는 것이 아니라 의미성 가까운 단어들에 대해 유사성을 높게 부여하는 벡터화 방법을 사용한다. 현재로선 Word2Vec를 이용한 단어 유사도 측정으로 유사한 단어에 대한 정보를 반영하는 방법을 생각해보았다.

 

 

 

 

 

 

Jupyter Notebook

ver 0. '책소개 키워드 수정본' 사용