본문 바로가기

Etc

파이썬 협업필터링(Collaborative Filtering), 추천 알고리즘 - 2

파이썬 협업필터링(Collaborative Filtering), 추천 알고리즘 - 2


이전 포스팅에서 사용된 피타고라스 공식을 이용한 유사도도출은 2차원, 즉 비교대상이 2개로 한정되는 한계가 있다.
비교대상이 2개라니, 실제 추천 알고리즘을 구현하고자 하는 데이터에서는 사실상 사용될 일이 없다고 할 수 있다. 

다차원에서의 거리를 구해 비교대상이 몇 개로 늘어나든 하나의 함수로 비교할 수 있어야 하고, 그에 따른 유사도를 구할 수 있어야 한다.

다차원간 거리를 구하는 데 사용되는 공식들은
  • Euclidean distance
  • City-block(Manhattan) distance
  • Minkowski distance
  • Cosine distance
  • Jaccard's distance
등등이 있는데, 여기서는 Euclidean distance( 유클리디안 거리공식) 을 사용해본다.

유클리디안 거리공식

공식은 다음과 같다. 나도 문돌이라 시그마같은 기호만 나오면 손발이 떨린다.
그러나 기본적인 틀은 피타고라스 공식과 유사하다. 
i번째 비교대상 아이템의 값인 (xi-yi)의 제곱을 모두 구해 더한 다음 제곱근을 취하는 방식이다. 

그럼 이제 유클리디안 거리공식을 통해 공통된 영화 개수가 2개 이상이 되어도 거리를 구할 수 있게 되었다.


1. 유클리디안 거리공식을 통한 다차원에서의 두 사람간 거리구하기

위 공식을 함수 sim_distance(data, name1, name2)로 정의한다. 
파라미터의 data는 전 포스팅에 사용한 critics 딕셔너리고, name1name2가 비교대상이 될 것이다.

이중 딕셔너리 구조이므로 critics[name] 안에서 for문이 돈다면 딕셔너리 안에 존재하는 value값인 {'guardians of the galaxy 2':5,'christmas in august':4,'boss baby':1.5} 이 나오고, 여기서 다시 key값인 영화 이름을 이용해 평점을 꺼내올 수 있다. 

이를 함수로 만들면 아래와 같다: (pow는 제곱을 구하는 메서드)
1
2
3
4
5
6
7
>> def sim_distance(data, name1, name2):
    sum=0
    for i in data[name1]:
        if i in data[name2]: #같은 영화를 봤다면
            sum+=pow(data[name1][i]- data[name2][i],2)
        
    return 1/(1+sqrt(sum))
cs

혹시나 같은 영화를 보지 않아 엉뚱한 값이 도출될 수 있으므로 조건문을 통해 같은 영화를 봤을 때만 합을 누적하도록 제한했다.
합을 모두 구했다면 저번 피타고라스공식을 사용했을 때와 마찬가지로 정규화를 통해 변수를 일정 범위내로 재설정하고 리턴한다.

위 함수를 사용해 chs과 leb의 유사도를 구하면:
1
2
3
>> sim_distance(critics, 'chs','leb')
 
0.2402530733520421
cs
이제 아이템이 몇 개가 되든지간에 두 사람간 유사도를 구할 수 있게 되었다.

2. 1.에서 도출한 함수를 통해 전체 데이터에서 거리가 가장 가까운 사람 구하기

이제 두 사람간 유사도를 구할 수 있으니 한 단계 더 나아가 딕셔너리 전체를 돌며 유사도가 가장 비슷한 사람을 구해본다.
지금은 4명뿐이지만 사람이 몇 명이 되든 적용하도록 새로운 함수를 만들어보자. 

top_match(data, name, index, sim_function) 

파라미터의 
-data는 딕셔너리
-name은 기준이 될 사람
-index는 몇 위까지 출력할 것인지
-sim_function은 위에서 구해 놓은 sim_distance 함수가 될 것이다.

함수가 작동하는 과정은 다음과 같다. 
  1. 반복문을 data의 사람수만큼 돌린다.
  2. (유사도, 이름)을 튜플로 묶어 리스트에 추가한다. 출력하면 [(점수,이름),(점수,이름)..]의 형태가 될 것이다.
  3. 보기 쉽게 내림차순 정렬 후 파라미터로 주어진 index 숫자만큼 인덱싱하고 return한다.
1
2
3
4
5
6
7
8
9
def top_match(data, name, index=3, sim_function=sim_distance):
    li=[]
    for i in data:
        if name!=i: #자기 자신은 제외한다
            li.append((sim_function(data,name,i),i)) # 유사도, 이름을 튜플에 묶어 리스트에 추가한다
    li.sort() #오름차순 정렬
    li.reverse() #내림차순 정렬
    
    return li[:index]
cs

이미 위에서 정의한 sim_distance에 '같은 영화를 봤을 때'라는 조건이 붙어 있기 때문에 여기서는 자기 자신만 제외하는 조건만 붙으면 된다.

출력

1
2
3
4
5
>> top_match(critics, 'chs')
 
[(0.4721359549995794'hhd'),
(0.2402530733520421'leb'),
(0.2402530733520421'kmh')]
cs

하나의 함수로 여러개의 아이템을 한 번에 비교했고, chs과 hhd의 거리가 가장 가까운 것을 확인할 수 있다. 

3. barplot을 통한 시각화

이제 시각화를 위해 아래와 같이 bar plot을 사용해보자.


그래프를 위해 matplotlib.pyplot을 import한 뒤, 플롯을 그리기 위한 함수 barchart(data, labels)를 정의한다. 
x축이 유사도가 되고, y축은 이름이 되는 가로 bar플롯이다.

파라미터의 data는 점수가 들어있는 리스트고, labels는 이름이 들어있는 리스트가 될 것이다.
1
2
3
4
5
6
7
8
>> import matplotlib.pyplot as plt
>> def barchart(data, labels): # data, labels는 list형태로 사용
    positions = range(len(data))
    plt.barh(positions, data, height=0.5,color='r'#가로
    plt.yticks(positions, labels)
    plt.xlabel('similarity'#x축
    plt.ylabel('name'#y축
    plt_show #출력
cs

함수를 정의했으니 파라미터로 들어갈 data(점수), label(이름)를 구해보자.

먼저 위의 top_match()에서 구한 리스트
1
2
3
[(0.4721359549995794'hhd'),
(0.2402530733520421'leb'),
(0.2402530733520421'kmh')]

cs

에서 반복문을 통해 이름과 점수를 분리한다. 
각 0번째 인덱스를 점수로, 1번째 인덱스를 이름으로 분리했다.
1
2
3
4
5
6
>> score= []
    names= []
 
    for i in li:
        score.append(i[0])
        names.append(i[1])
cs

점수가 담긴 리스트 score:
1
2
3
>> score
 
[0.47213595499957940.24025307335204210.2402530733520421]
cs

이름이 담긴 리스트 names:
1
2
3
>> names
 
['hhd''leb''kmh']
cs

barplot 출력:
1
barchart(score,names)
cs

아래는 시각화된 barplot이다.

이번 포스팅에서는 유클리디안 거리공식을 통해 다차원간 거리를 도출하고, 그에 따른 유사도를 구했다.

마지막이 될 다음 글에서는 correlation analysis(상관분석) 을 통해 실제로 예상평점을 구해보고 그에 따른 영화추천 알고리즘을 구현해보자.