前言:由於近期項目上在開發一個銷售管理系統,里面涉及到一個基於用戶的產品給推薦算法,之前也對推薦系統有比較系統地了解,因此本文及接下來的幾篇文章將詳細推薦系統的思想及其多中實現方法,本篇將主要介紹基於系統過濾的推薦系統及其Python實現。
1、協同過濾思想
協同過濾(collabrotive filtering)是商品銷售(尤其是網店)常用的推薦方法,分為基於用戶(user-based)和基於商品(item-based)兩種情況,整體思想是在已經有銷售記錄的數據庫中,從用戶購買的歷史商品數據中找到用戶或產品的相似度,然后對用戶做推薦。如果選擇基於用戶的系統過濾,則找到用戶的相似度,對一個特定的用戶,可以選擇將他最相似的K個用戶所購買的且該用戶未購買的N中商品推薦給他;若是基於商品的推薦,則找到商品之間的相似度,然后對購買了該商品的用戶,推薦其未購買的相似商品中的其他商品,協同過濾的主要思想是聚類(無論是基於用戶還是基於商品)。
2、相似度度量方法
上面提到要計算用戶/商品之間的相似度,那么怎么度量兩個用戶/商品之間的相似度呢?在轉載的文章《漫談:機器學習中距離和相似性度量方法》中,談到了多種度量距離和相似性的方法,在機器學習中,距離和相似性是比較相關的概念,一般來說距離越大,相似性與低,反之相似性越高。在推薦系統的相似度度量中,常常用到以下三種相似度度量:
1)閔可夫斯基距離(Minkowski/歐式距離):$$distance(X,Y)=\left ( \sum {i=1}^{n}\left | x{i}-y_{i}^{} \right | ^{p}\right )^{^{\frac{1}{p}}}$$
2)傑卡德相似系數(Jaccard):$$J(A,B)=\frac{\left | A\bigcap B \right |}{\left | A\bigcup B \right |}$$
3)余弦相似度:$$\cos \left ( \theta \right )=\frac{a^{T}b}{\left | a \right |\left | b \right |}$$
4)Pearson相關系數:$$\rho_{XY}=\frac{cov\left ( X,Y \right )}{\rho _{X}\rho {Y}}$$
5)相對熵(K-L距離):$$D\left ( p||q \right )=\sum {x}p\left ( x \right )\log \frac{p\left ( x \right )}{q\left ( x \right )}=E{p\left ( x \right )}\log \frac{p\left ( x \right )}{q\left ( x \right )}$$
6)Hellinger距離:$$D\left ( p||q \right )=\frac{2}{1-\alpha ^{2}}\left ( 1-\int p\left ( x \right )^{\frac{1+\alpha }{2}}q\left ( x \right )^{\frac{1-\alpha }{2}}d{x} \right )$$
3、幾種相似度的python實現
上面列舉了六種相似度度量(並未列舉完所有的相似度度量),各種適合的場景不太一致,但是這些相似度之間又有一定意義上的相關性(如果敢興趣可以推導一下),在這六種相似度中,閔可夫斯基距離、傑卡德相似度、余弦相似度用的頻率較高,下面是這幾種相似度的簡單實現:
import numpy as np
def euclidea_sim(x,y):
assert len(x) == len(y)
dis = np.linalg.norm(x-y)
sim = 1/(1+dis)
return sim
def jaccard_sim(x,y):
assert len(x) == len(y)
x,y = np.array(x).astype(bool),np,array(y).astype(bool)
return sum(x*y)/sum(x+y)
def cosine_sim(x,y):
assert len(x) == len(y)
sum_x_y = np.dot(x,y)
return sum_x_y/np.linalg.norm(x)/np.linalg.norm(y)
# 以上用python簡單地實現了三種相似度,供下面提供推薦使用
上面用python實現了常用的三種相似度度量方法。注意,這里的余弦相似度的取值范圍為[-1,1],可以通過簡單變化將各種相似度都轉換在[0,1]之間(比如余弦相似度乘以0.5+0.5處理),這里不做過多處理。下面將從銷售中的記錄數據(如下表),完整地實現一個簡單的推薦系統(之所以說簡單,是因為沒有過多地考慮業務場景,而且僅僅用最基本的系統過濾方法)。
客戶ID | 購買日期 | 產品類別 | 產品ID | 購買數量 |
客戶46 | 2017/2/15 | 類別18 | 產品17 | 3 |
客戶118 | 2017/4/7 | 類別15 | 產品7 | 1 |
客戶9 | 2017/4/12 | 類別17 | 產品1 | 15 |
客戶74 | 2016/11/27 | 類別28 | 產品13 | 17 |
客戶248 | 2017/4/24 | 類別19 | 產品15 | 1 |
客戶271 | 2017/4/25 | 類別6 | 產品20 | 8 |
客戶220 | 2017/4/1 | 類別20 | 產品10 | 5 |
客戶94 | 2017/3/23 | 類別32 | 產品11 | 14 |
import numpy as np
import pandas as pd
#pandas庫清理數據特別方便,因此本次用到該庫
from pandas import DataFrame
# 加載數據,數據放在本地磁盤
self_file = "F:\\python_jupyter\\sell_record.csv"
sell_record = pd.read_csv(self_file, sep=',',header=0,encoding='gbk')
# 客戶可能多次購買同一種產品,因此把客戶對產品匯總
sell_pivot = sell_record.pivot_table(values='購買數量',index='客戶ID',columns='產品ID',aggfunc=sum,fill_value=0)
#先計算相似度矩陣
def sim_mat(sell_group,sim=euclidea_sim):
# 定義一個全零相似度矩陣
#
sim_matrix = np.zeros((sell_group.shape[0],sell_group.shape[0]),dtype=float)
sim_matrix = DataFrame(sim_matrix,index=sell_group.index,columns=sell_group.index)
for index in sell_group.index:
for column in sell_group.index:
sim_matrix.ix[index,column] = sim(sell_group.ix[index,:],sell_group.ix[column,:])
return sim_matrix
def recommendation(sim_mat,customer,n_sim_customer,n_product,sell_record):
'''
paramer:
sim_mat:相似度矩陣
customer:需要推薦的客戶
n_sim_customer:相似客戶數
n_product:推薦的產品數
sell_record:客戶購買的產品列表,每條數據為一個行,每列數據為一個產品統計的客戶產品購買量
'''
try:
k_similar = sim_mat.sort_values(customer,axis=0,ascending=False)[:n_sim_customer]
except:
print('該用戶還未購買過我公司產品,可為他推薦熱門產品')
return None
# 找到k個相似用戶購買的所有產品
recom_product = sell_record.ix[k_similar.index,:].astype(bool).sum(axis=0)
recom_product = recom_product[recom_product>0].sort_values(axis=0,ascending=False).index
count_ = 0
recom_list = []
# while count_ < n_product:
for i in recom_product:
if sell_record[i][customer] > 0:
continue
else:
recom_list.append(i)
count_ += 1
if count_ >= n_product:
return recom_list
return recom_list
# sim_mat[customer].
上面代碼就是簡單的基於客戶(user-based)推薦系統簡單實現,可以對上面代碼進行簡單地修改就能直接應用於基於商品(item-based)的推薦。該代碼未考慮具體業務場景(購買產品度量、相似度選擇、生成推薦商品候選列表對各個商品與該用戶的相似度乘積求和)及執行效率,具體應用時可以對這些方面加以改進。下面是對客戶10做推薦
sim = sim_mat(sell_pivot)
recommendation(sim,"客戶10",20,5,sell_pivot)
['產品11', '產品9', '產品3', '產品4', '產品16']