이 글은 제가 작업했던 내용을 정리하기 위해 수기 형식으로 작성 된 글입니다.
2022.12.09 - [[신.만.추]] - 신입이 만드는 추천시스템-1(개요)
2022.12.09 - [[신.만.추]] - 신입이 만드는 추천시스템-2(데이터 수집, 스크래핑)
2022.12.09 - [[신.만.추]] - 신입이 만드는 추천시스템-3(셀레니움 최소화)
2022.12.09 - [[신.만.추]] - 신입이 만드는 추천시스템-4(한국어 전처리 및 워드임베딩)
2022.12.10 - [[신.만.추]] - 신입이 만드는 추천시스템-5(아이템 벡터화)
2022.12.10 - [[신.만.추]] - 신입이 만드는 추천시스템-6(웹서버 구축)
- 플라스크로 단일서버 구축하기
- 검색성능 향상
- 포트포워딩으로 외부 접속 허용하기
- uWSGI와 gunicorn
저번에 작성했던 글에서 플라스크로 단일서버를 구축하였다. 로컬로만 구축했기 때문에 외부접속 허용을 위해서는 포트포워딩을 진행해야 한다. 하지만 그 이전에 검색성능 관련한 피드백을 반영하겠다.
2. 검색성능 향상
검색성능 관련한 피드백 내용은 아래와 같다.
‘검색키워드와 유사도 기반으로 검색된 리스트의 내용이 상반되는 결과가 나오는 부분이 있다.’
‘검색키워드가 전혀 포함되지 않은 경우에도 결과로 나온다.’
사실 이 부분은 추천시스템에 대한 내 역량부족이라고 생각한다.
사실 단순 워드임베딩만으로 추천시스템을 완벽하게 만들 순 없을 것이라고 생각했었다.
그래서 조금이라도 추천 리스트의 질을 높이기 위해 택한 방법이 텍스트 키워드의 카운트 가중치였다.
하지만 텍스트가 직접적으로 해당되는 것이 아니라, 가중평균으로 아이템의 벡터가 만들어지기 때문에 실제적으로 키워드 검색을 통해 나오는 결과가 안좋을 수 있다고 판단했다.
이를 보완하기 위해서 생각한 방법은 아래와 같다.
- 아이템의 텍스트 데이터에서 검색 키워드를 포함하는 아이템에 가중치 부여
- 가중치의 계산은 텍스트 데이터에서 검색 키워드를 카운트하여 계산
- 계산된 카운트에 테스트를 통한 추가가중치 부여
식으로 표현하자면 이렇게 표현할 수 있을 것 같다.
생각보다 단순한 점수산정 방법이지만, 위와 같은 방법으로 점수를 산정한 후 리스트를 노출하였을 때 내부적으로 검색성능이 이전보다 훨씬 괜찮아졌다는 평을 들었다.
적용한 코드는 아래와 같다.
# 서버 라우팅 분리
@app.route("/")
def hello():
return "hello flask"
@app.route('/rec', methods = ['POST'])
def rec():
if request.method == 'POST':
res = { "resultCode" : "200",
"resultMessage": "성공",
"data" : []}
keyword_dict = request.json
keyword = keyword_dict["keyword"]
keyword = keyword .replace('\\','')
keyword = keyword .replace('‘','')
keyword = keyword .replace('’','')
keyword = re.sub(r"[^a-zA-Z0-9가-힣]",' ',keyword)
keyword = ' '.join(re.split(r"\\s+", keyword))
keyword = keyword.strip()
keyword = keyword.split(' ')
min_range = keyword_dict["min_range"]
max_range = keyword_dict["max_range"]
if max_range==0:
temp_df = creator_df[(creator_df['구독자수']>=min_range)]
else:
temp_df = creator_df[(creator_df['구독자수']>=min_range) & (creator_df['구독자수']<=max_range)]
# 일반적으로 중요한 키워드는 앞쪽에 적기때문에 앞쪽에 가중치 부여
keyword_cnt = len(keyword)
weight_list = [6,3]
if keyword_cnt>=3:
for i in range(keyword_cnt-len(weight_list)):
weight_list.append(1)
# 키워드 벡터화
word_vecs = np.zeros(200)
sum_weight = 0
for word_idx in range(len(keyword)):
word_vec = model.wv[keyword[word_idx]]
weight = weight_list[word_idx]
sum_weight += weight
word_vecs+=word_vec.dot(weight)
# keyword 가중평균 구하기
target_vector = word_vecs/sum_weight
cosine_matrix = cosine_similarity([target_vector],vec_stack)
cos_idx = np.where(cosine_matrix[0])[0]
cos_val = cosine_matrix[0][np.where(cosine_matrix[0])]
creator_name = [y_idx2name[idx] for idx in cos_idx]
zip_seq_val = list(zip(creator_name,cos_val))
sim_df = pd.DataFrame(zip_seq_val,columns=['채널명','유사도'])
temp_copy = temp_df.copy()
temp_copy.loc[:,('키워드 카운트')] = 0
for search_idx in range(len(keyword)):
temp_copy.loc[:,('키워드 카운트')] = (temp_copy.loc[:,('키워드 카운트')]+temp_copy['영상제목'].str.count(search_key[search_idx])*weight_list[search_idx]+temp_copy['영상제목'].str.count(search_key['키워드'])*weight_list[search_idx])
temp_copy = temp_copy[['채널명','키워드 카운트']]
temp_df = pd.merge(left = temp_copy, right = temp_df , how = "inner", on = "채널명")
sim_df = pd.merge(left = sim_df , right = temp_df, how = "inner", on = '채널명')
zip_seq_val = sim_df[['채널명','구독자수','유사도','키워드 카운트']].values.tolist()
del temp_copy
del sim_df
temp_zip = []
w1 = 120
w2 = 1.5
for zip_idx in range(len(zip_seq_val)):
name = zip_seq_val[zip_idx][0]
sub = zip_seq_val[zip_idx][1]
x1 = zip_seq_val[zip_idx][2]
x2 = zip_seq_val[zip_idx][3]
score = int((x1*w1)+(x2*w2))
temp_zip.append([seq,sub,score])
temp_zip.sort(key = lambda x : (x[2]), reverse = True)
for v in zip_seq_val:
res['data'].append({'creatorName':v[0], 'subscribers':v[1], 'score':v[2]})
return jsonify(res)
if __name__ == "__main__":
app.run()
하지만 위의 산정방법도 어느정도 문제를 내포하고 있다.
단순 검색 키워드 카운트가 점수에 포함되기 때문에 특정 키워드가 계속해서 포함 된 텍스트 데이터를 가진 아이템의 경우는 점수가 높게 나오게 된다.
이부분은 사실 TF-IDF를 적용하게 되면 해결 될 수 있는 부분이라고 생각하지만, 추가적으로 TF-IDF를 적용한다면 반대로 전체 텍스트 데이터의 양이 적으면서 해당 검색 키워드가 포함되어 있다면 높은 점수를 받을 것으로 예상되어 고민 되는 부분이다.
해당 부분은 차후에 엘라스틱서치를 다루면서 고민했던 부분에 대해서 서술하겠다.
'[신.만.추]' 카테고리의 다른 글
신입이 만드는 추천시스템-9(WSGI) (0) | 2022.12.10 |
---|---|
신입이 만드는 추천시스템-8(포트포워딩) (0) | 2022.12.10 |
신입이 만드는 추천시스템-6(웹서버 구축) (0) | 2022.12.10 |
신입이 만드는 추천시스템-5(아이템 벡터화) (0) | 2022.12.10 |
신입이 만드는 추천시스템-4(한국어 전처리 및 워드임베딩) (0) | 2022.12.09 |