Programming/Projects

[지난 프로젝트도 다시보자] '무비플레이리스트(무플리)' - 1. 편집거리 알고리즘, 코사인 분석

리버김 2022. 11. 9.

1. 코사인 유사도 분석

https://wikidocs.net/24603

 

1) 코사인 유사도(Cosine Similarity)

BoW에 기반한 단어 표현 방법인 DTM, TF-IDF, 또는 뒤에서 배우게 될 Word2Vec 등과 같이 단어를 수치화할 수 있는 방법을 이해했다면 이러한 표현 방법에 대해서 …

wikidocs.net

class MovieListSerializer(serializers.ModelSerializer):

    class Meta:
        model = Movie
        fields = ('pk', 'words',)

@api_view(['GET'])
def similar_movie(request, movie_pk):
    movies = get_list_or_404(Movie)
    serializer = MovieListSerializer(movies, many=True)
    
    idx = []
    for i in range(len(serializer.data)):
        if movie_pk == serializer.data[i]['pk']:
            idx.append(i)
            break

    xMovie = [data.get('words') for data in serializer.data]
    result = recommend_movies_names(xMovie, idx, serializer)
    final_movie = [get_object_or_404(Movie, pk=i) for i in result]
    final_serializer = UserChoiceSimilarMovieSerializer(final_movie, many=True)
    
    return Response(final_serializer.data)

# 추천 알고리즘
def recommend_movies_names(xMovie, idx, movies):
    
    # 불용어 제거
    countVec = CountVectorizer(max_features=10000, stop_words='english')

    # 영화 키워드 벡터라이징
    dataVectors = countVec.fit_transform(xMovie).toarray()

    # 코사인 유사도
    similarity = cosine_similarity(dataVectors)
    
    # 유사도 내림차순 5개 영화의 인덱스
    idx_collection = []
    for i in idx:
        distances = similarity[i]
        listofMovies = sorted(list(enumerate(distances)), reverse=True, key=lambda x:x[1])[1:7]
        idx_collection.extend(listofMovies)
 
    # 인덱스를 pk로 바꾸기
    pk_collection = []
    for idx in idx_collection:
        pk_collection.append(movies.data[idx[0]]['pk'])

    return pk_collection

2. 편집거리 알고리즘

편집 거리 알고리즘이란, 두 문자열의 유사도를 판단하는 알고리즘이다. 유사도를 판단하는 기준은, 어떠한 문자열을 삽입,삭제,변경을 몇 번이나 해서 바꿀수 있는지를 계산하여 그 최소값을 구해, 그 값을 유사도 판단의 척도로 다룬다.

 

프로젝트 무플리에서 편집거리 알고리즘은 어떤 영화를 검색했을 때, 그 영화와 제목을 비교하여 가장 가까운 상위 5개의 영화를 출력해주는 데 쓰였다.

from jellyfish import jaro_winkler_similarity

@api_view(['GET'])
def search_movie(request, movie_name):
    movies = get_list_or_404(Movie)
    serializer = MovieSearchSerializer(movies, many=True)
    serializer = serach(serializer.data, movie_name)
    return Response(serializer[:5])

# 편집거리 알고리즘
def serach(lst, keyword):
    fetch_data = []
    for data in lst:
        tmp = {'pk': 0, 'title': '', 'poster_path':'', 'similarity':''}
        tmp['pk'] = data['pk']; tmp['title'] = data['title']; tmp['poster_path'] = data['poster_path']
        tmp['similarity'] = jaro_winkler_similarity(keyword, data['title'])
        fetch_data.append(tmp)
    fetch_data.sort(key=lambda x : -x['similarity'])
    return fetch_data

search 함수에는 영화들의 리스트와 유저가 검색한 영화 한 개가 인자로 들어간다. 그리고 영화의 pk, 제목, 포스터 정보, 유사도를 tmp 딕셔너리에 담는다. 그리고 각 영화 데이터를 담아주고 similiary의 경우 import해 놓은 편집거리 알고리즘 함수에 담에 계산한 후 담는다. lamda함수를 사용해 similarity가 '높은' 순으로 담고, 유저에게 최종적으로 index 0~4까지 다섯 개의 영화를 return해준다. 

 

댓글