[알고리즘] 영화 추천 시스템 코드

2020. 4. 6. 22:36노트/Python : 프로그래밍

 

추천시스템의 분류

1. 컨텐츠 기반 추천 시스템

2. 협업 기반 추천 시스템

    2-1. 메모리 기반(KNN) 

        2-1-1. 사용자 기반 : 비슷한 유형의 사용자를 찾음 

        2-1-2. 아이템 기반 : 어떤 영화를 본 관객들이 다른 영화를 본다. (영향도)

    2-2. 잠재요인 기반(Latent factor) 

 

  • 컨텐츠 기반 추천시스템

      사용자가 선호하는 아이템을 같은 장르( 컨텐츠 ) 에 기반하여 추천하는 시스템 

 

  • 협업 기반 추천 시스템 
    • 메모리 기반 

  영화1 영화2 영화3 영화4
사용자1 4 ? 3 ?
사용자2 3 2 ? 5
사용자3 ? 3 2 2

   다음과 같은 사용자 - 아이템 평점 행렬(Matrix)를 구성하여 구현해야한다. 

   사용자 기반 협업 필터링과 아이템 기반 협업 필터링이 있다. 

 

  • 사용자 기반 협업 필터링 
    User1과 User2는 itemA 부터 itemC까지 평점이 비슷하다. 
    그래서 비슷한 유형의 사람이라고 보고, 
    User1에게 itemD를 주면 User2의 3점과 비슷하게 줄 것이고, 
    User2에게 itemE를 주면 User1의 1점과 비슷하게 줄 것이다. 

 

  • 아이템 기반 협업 필터링 
    itemA와 itemB는 평점 분포가 유사하기 때문에 유사한 아이템으로 보고, 
    User4에게는 itemA를 추천하고 , 
    User5에게는 itemB를 추천한다. 

 

 

데이터

 

영화평점.xlsx
0.01MB

 

 

코드

# 실무 적용하기 좋음

 

데이터 준비

#데이터 불러오기
moviedata=pd.read_excel("영화평점.xlsx",header=1, index_col=0, dtypes=float)

#영화X이름 행렬로 만들기위해 Transpose
moviedata=pd.DataFrame(data=moviedata.T)

#데이터 프레임 딕셔너리 구조로 변경
moviedata_dic=moviedata.to_dict()
moviedata_dic

 

딕셔너리 nan 값 제거 

# dictionary에 nan값 함수 
def dropna(data): # name : i , movie : j 
    for i in data:
        for j in data[i]:
            name=i
            movie=j
            value=data[i][j]
        data[i]={movie: value for movie, value in data[i].items() if pd.isnull(value)==False}
    return data

# 딕셔너리 nan 값 제거 
moviedata_dic_drop=dropna(moviedata_dic)
moviedata_dic_drop

 

피어슨 상관계수 함수 구현

 

  • 왼쪽의 식을 오른쪽 식으로 변형하여 함수를 구현함 

    * 주의사항: 분모에 0을 나누지 않도록 임의 상수값 0.00001 정도를 추가함 

                     cnt 값도 누적한 값으로 나눌 수 있도록 식 들여쓰기 주의 

#피어슨 상관계수
def sim_pearson(data, n1, n2): 
    #구현
    sumX=0
    sumY=0
    sumSqX=0 # x 제곱합 
    sumSqY=0 # y 제곱합 
    sumXY=0 #XY 합
    global cnt # 영화 갯수
    cnt =0
    for i in data[n1]:
        if i in data[n2]:
            sumX+=data[n1][i]
            sumY+=data[n2][i]
            sumSqX+=pow(data[n1][i],2)
            sumSqY+=pow(data[n2][i],2)
            sumXY+=(data[n1][i])*(data[n2][i])
            cnt+=1
            global num # 전역변수 선언
            global den # 전역변수 선언
            num=sumXY-((sumX*sumY)/cnt)
            den= (sumSqX-(pow(sumX,2)/cnt))*(sumSqY-(pow(sumY,2)/cnt))
    return num/sqrt(den+0.00001) # 분모=0방지

 

나와 상관계수가 높게 나온 사람 출력 함수 구현 

def top_match(data, name, rank=3, simf=sim_pearson):
    #sim_pearson함수를 simf라는 이름으로 사용하겠다.    
    #구현부분 
    simList=[] #유사한 영화들이 저장될 리스트
    for i in data:
        if name!=i: #자기 자신을 제외
            simList.append((simf(data, name, i),i))
    simList.sort() #오름차순
    simList.reverse() #역순(내림차순)
    return simList[:rank]
    
    # 1.나와 가장 상관계수가 높게 나온 사람? 5명출력
print(top_match(moviedata_dic_drop,"엄다연",5))



내가 보지 않은 영화에 대해 예상 평점이 높은 순으로 추천 함수 구현 

def recommendation(data,person,simf=sim_pearson ):
    res=top_match(data, person, len(data))
#     print(res)
    simSum=0 #상관계수(유사도)의 합
    score_dic={} #예상평점 총합을 저장하기 위한 dic
    sim_dic={} # 유사도 합을 저장하기 위한 dic 
    myList=[]
    for sim, name in res: 
        if sim<0 : continue # 유사도가 양수인 경우만 처리를 하도록 하겠음 
        for movie in data[name]:
            if movie not in data[person]:
# name이 본 movie를 person이 보지 않았다면... => 추천해야하는 영화 대상이 됌
                simSum+=sim*data[name][movie]
                score_dic.setdefault(movie,0) #key가 없으면 초기화하지만 key가 있으면 냅둔다.
                score_dic[movie]+=simSum
                sim_dic.setdefault(movie,0)
                sim_dic[movie]+=sim
            simSum=0 # 영화변경 -> 0으로 초기화 
    for key in score_dic: #기준이 안본 영화의 제목들이 들어감 
        score_dic[key]=score_dic[key]/sim_dic[key] # 평점총합 / 유사도 총합
        myList.append((score_dic[key],key))
    myList.sort()
    myList.reverse()
    return myList
    
# 2.내가 안본 영화 중에서 추천 점수가 가장 높은 순으로 영화제목과 예상평점을 출력
recommendation(moviedata_dic_drop, "엄다연")