前言
推薦系統的評價指標在不同類型的推薦場景下,選用的不同。有些推薦的評價指標並非完全出自推薦系統,而是從搜索算法,信息檢索,機器學習等相關領域沿用過來,因此網上有些對評價指標的解釋並非完全以推薦系統的角度進行,這讓我會在學習的時候產生困惑,特此做出一些整理,力求完全用推薦系統的角度解釋,並給出計算的具體流程。
如果你對本系列(未寫完,持續更新中)感興趣,可接以下傳送門:
【推薦算法】從零開始做推薦(一)——認識數據
【推薦算法】從零開始做推薦(二)——推薦系統的評價指標,計算原理與實現樣例
【推薦算法】從零開始做推薦(三)——傳統矩陣分解的TopK推薦實戰
【推薦算法】從零開始做推薦(四)——python Keras框架 利用Embedding實現矩陣分解TopK推薦
【推薦算法】從零開始做推薦(五)——貝葉斯個性化排序矩陣分解 (BPRMF) 推薦實戰
【推薦算法】從零開始做推薦(六)——貝葉斯性化排序矩陣分解 (BPRMF) 的Tensorflow版
TopK 推薦評價指標
TopK推薦
定義:TopK推薦顧名思義,首先推薦給用戶的項目數是K個,其次,這些推薦依據用戶對項目的“評分”進行從高到低排序展示給用戶。
舉例:打開淘寶後給你推薦的TopK個商品;
特點:將推薦視爲分類問題,如給定一個用戶,其將要訪問的項目作爲其分類的標籤,而對該用戶的TopK推薦即爲模型對該用戶的多分類結果。
因此TopK推薦的評價指標也大多爲分類問題指標沿用演化而來。
Precision 精確度,Recall 召回率
我最初瞭解它們,是出自機器學習分類問題裏經典的混淆矩陣。網上有部分解釋也是沿用混淆矩陣,但這會讓我對於TopK推薦直觀理解會造成困難,因此直接以推薦算法角度給出計算公式:
Precision@K=∑i=1N∣R(i)∣∑i=1N∣R(i)∩T(i)∣….…(1)Recall@K=∑i=1N∣T(i)∣∑i=1N∣R(i)∩T(i)∣….…(2)
下面對公式(1)(2)中的變量進行解釋.
i:第i次推薦。
R(i):第i次推薦中,推薦的項目列表。
T(i):第i次推薦中,用戶真實訪問的項目列表。
K:TopK推薦中推薦列表的長度。
N:總推薦次數。
大多數場景下,對每一個用戶進行一次TopK推薦,因此n常爲測試集中的用戶,N常爲測試集中的用戶數量。
實際上,Precision@K和Recall@K的分子都是單次推薦時命中數量的累加,區別在分母,Recall@K的分母要按實際情況進行累加,但Precision@K的分母是推薦列表長度的累加,可以很輕鬆的得出:∑i=1N∣R(i)∣=N×K。
注意:Precision@K和Recall@K的計算都是先累加再相除,不能寫成先相除再累加。
思考:Precision@K和Recall@K的值域在每次推薦都命中的情況下一定爲1嗎?這受R(i)和T(i)的長度影響。對每次推薦而言,∣R(i)∣=K是定長的,而∣T(i)∣是變長的。
當任意的∣T(i)∣都小於∣R(i)∣=K時,有max(∣R(i)∩T(i)∣)=∣T(i)∣,∑i=1N∣T(i)∣<∑i=1N∣R(i)∣,在每次都命中的前提下,:
Precision@K=∑i=1N∣R(i)∣∑i=1N∣R(i)∩T(i)∣=∑i=1N∣R(i)∣∑i=1N∣T(i)∣<1Recall@K=∑i=1N∣T(i)∣∑i=1N∣R(i)∩T(i)∣=∑i=1N∣T(i)∣∑i=1N∣T(i)∣=1。
同理,當任意的∣T(i)∣都大於∣R(i)∣=K時,在每次都命中的前提下,有Precision@K=1,Recall@K<1,僅當任意的|∣T(i)∣都等於∣R(i)∣=K時,在每次都命中的前提下,Precision@K=1,Recall@K=1。
疑惑:可以看出,要使Precision@K和Recall@K達到1的條件是非常苛刻的,實際推薦場景中,很可能這兩個指標的最大值都達不到1。那這會對其作爲評價指標產生負面影響嗎?歡迎各路大神與我交流。
F1值
有了Precision@K和Recall@K,F1值的計算也就順水推舟地過來了,和機器學習中的公式完全一樣。F1值是完全基於Precision@K和Recall@K的,是一個綜合考慮兩者的評價指標,感興趣的可以看這裏。
F1@K=Precision@K+Recall@K2∗Precision@K∗Recall@K….…(3)
命中率(Hit Ratio,HR)
聽名字很好理解,但實際上概念很容易搞混。剛好看到一篇文章(TKDE-2019)有用命中率,這裏以它的計算方法爲準。
可以看出他這裏分子是推薦命中的用戶數,分母的是測試集中用戶數,其對每一個用戶推薦一次,因此準確而言分母應爲推薦次數。
hr(i)={10,R(i)∩T(i)=∅,R(i)∩T(i)=∅….…(4)HR@K=N∑i=1Nhr(i)….…(5)
可以看出,HR@K與Recall@K的計算思路有些相似,實際上,HR@K也是衡量召回關係的指標。個人感覺HR@K的計算方式目前仍是模糊的,甚至部分博客將HR@K寫成Recall@K的計算方式,因此不是很建議使用。
平均到數排名(Mean Reciprocal Rank,MRR)
源自搜索領域,其中Reciprocal Rank(RR)指第一個正確答案在TopK推薦列表裏的排名的倒數。MRR即爲多次推薦RR的平均值。
MRR@K=N1i=1∑Nrank(i)1….…(6)
其中rank(i)表示第i次推薦中,第一個命中的項目,在TopK推薦列表裏的次序,若沒命中,則rank(i)→∞,即rank(i)1=0。
平均精度均值(Mean Average Precision,MAP)
同樣,我們首先得知道Average Precision(AP)怎麼計算(自己總結的,慎用):
AP(i)={∣T(i)∣∑j=1∣hitsi∣rank(hitsi(j))j0,∣hitsi∣=0,∣hitsi∣=0….…(7)
其中∣hitsi∣表示第i次推薦中,命中的項目個數;hitsi(j)表示在第i次推薦中命中的第j個項目;rank(item)表示item在R(i)中的位置。
MAP@K即爲AP的平均值,其中N同樣爲總推薦次數:
MAP@K=N1i=1∑NAP(i)….…(8)
歸一化折損累計增益(Normalized Discounted Cumulative Gain,NDCG)
這個指標我最在博客上看到的都是關於評分推薦上的,而非TopK推薦,同樣還是看到的那篇論文,剛好擺了出來,於是在此我也想記錄下來。
可仔細一檢查公式,發現文中的公式似乎是錯誤的,當一次推薦命中多個時,該公式只計算了一次,因此猜想是少了個sigma求和。
想完全照抄顯然是不行了,只能再百度各路NDCG計算方法,從頭開始講起。NDCG的完全概念和計算方法可參照這裏。下面根據評分類型的NDCG嘗試推導TopK類型的NDCG。
先擺出按本文定義的變量,CG,DCG,IDCG的原計算公式。
CG(Cummulative Gain)表示累積增益;
DCG(Discounted Cummulative Gain), “Discounted”有打折,折扣的意思,這裏指的是對於排名靠後推薦結果的推薦效果進行“打折處理”;
IDCG表示推薦系統某一用戶返回的最好推薦結果列表, 即假設返回結果按照相關性排序, 最相關的結果放在最前面;
CG=j=1∑KreljDCGi=j=1∑Klog2(rank(hitsi(j))+1)2relj−1NDCGi=IDCGiDCGiNDCG@K=N1i=1∑NNDCGi
hitsi(j)表示第i次推薦命中的第j個項目;
rank(item)表示item在R(i)中的位置(從1開始編號)。
以下爲個人推算,不保證正確性,歡迎討論!!!
之所以說在評分推薦系統裏常用NDCG,是因爲這個rel表示相關性,指的就是項目與用戶的相關性,也就是評分。現在挪到TopK裏,那命中了就是相關,未命中就應該是無關,若未命中時,設rank(hitsi(j))→+∞,此時log2(rank(hitsi(j))+1)→+∞,log2(rank(hitsi(j))+1)1→0,則DCG可改寫成:
DCGi=j=1∑∣hitsi∣log2(rank(hitsi(j))+1)21−1=j=1∑∣hitsi∣log2(rank(hitsi(j))+1)1
那麼問題來了,IDCGi怎麼求?按照原理,IDCGi即爲最優的排序,則命中的全要在前面,若10個裏面命中五個,則序號應爲12345。則有如下公式:
IDCGi=j=1∑∣hitsi∣log2(j+1)1
照着原公式進行推導,則有:
NDCG@K=N1i=1∑NNDCGi=NDCG@K=N1i=1∑NIDCGiDCGi=N1i=1∑N∑j=1∣hitsi∣log2(j+1)1∑j=1∣hitsi∣log2(rank(hitsi(j))+1)1
可以發現,TKDE那篇文中似乎並沒有計算IDCG,不知道這裏面有什麼說法沒。
樣例
假設在給用戶推薦的場景中,總共對3個用戶進行了Top@5推薦,情況分別如下:
R=[[2,5,1,3,9],[6,2,0,12,8],[1,6,7,11,2]]
T=[[3,10,7,21],[15,0,5,2,13],[19]]
則相應的指標計算如下:
Precision@5=5+5+51+2+0=153=0.2
Recall@5=4+5+11+2+0=103=0.3
F1@5=0.2+0.32∗0.2∗0.3=0.50.12=0.24
HR@5=31+1+0=32≈0.667
MRR@5=341+21+0=41=0.25
MAP@5=3441+521+32+10=72071≈0.09861
NDCG@5=31∗(log2(1+1)1log2(4+1)1+log2(1+1)1+log2(2+1)1log2(2+1)1+log2(3+1)1+0)=31∗(log25log22+2∗(1+log23)log23+2+0)≈0.37470
用代碼也寫了個,當作驗算,效果如下:
"""
Created on Wed Mar 25 18:40:33 2020
@author: Yang Lechuan
"""
import math
R=[[2,5,1,3,9],[6,2,0,12,8],[1,6,7,11,2]]
T=[[3,10,7,21],[15,0,5,2,13],[19]]
def cal_indicators(rankedlist, testlist):
HITS_i = 0
sum_precs = 0
AP_i = 0
len_R = 0
len_T = 0
MRR_i = 0
HR_i = 0
DCG_i = 0
IDCG_i = 0
NDCG_i = 0
ranked_score = []
for n in range(len(rankedlist)):
if rankedlist[n] in testlist:
HITS_i += 1
sum_precs += HITS_i / (n + 1.0)
IDCG_i += 1.0/math.log2((HITS_i)+1)
DCG_i += 1.0/math.log2((rankedlist.index(rankedlist[n])+1)+1)
if MRR_i == 0:
MRR_i = 1.0/(rankedlist.index(rankedlist[n])+1)
else:
ranked_score.append(0)
if HITS_i > 0:
AP_i = sum_precs/len(testlist)
HR_i = 1
NDCG_i = DCG_i/IDCG_i
len_R = len(rankedlist)
len_T = len(testlist)
return AP_i,len_R,len_T,MRR_i,HITS_i,HR_i,NDCG_i
AP = 0
PRE = 0
REC = 0
MRR = 0
HITS = 0
HR = 0
sum_R = 0
sum_T = 0
F1 = 0
NDCG = 0
N = len(R)
K = len(R[0])
for i in range(N):
AP_i,len_R,len_T,MRR_i,HITS_i,HR_i,NDCG_i = cal_indicators(R[i],T[i])
AP += AP_i
sum_R += len_R
sum_T += len_T
MRR += MRR_i
HITS += HITS_i
HR += HR_i
NDCG += NDCG_i
PRE = HITS/(sum_R*1.0)
REC = HITS/(sum_T*1.0)
F1 = 2*PRE*REC/(PRE+REC)
HR = HR/(N*1.0)
MRR = MRR/(N*1.0)
MAP = AP/(N*1.0)
NDCG = NDCG/(N*1.0)
print('評價指標如下:')
print('PRE@',K,':',PRE)
print('REC@',K,':',REC)
print('F1@',K,':',F1)
print('HR@',K,':',HR)
print('MRR@',K,':',MRR)
print('MAP@',K,':',MAP)
print('NDCG@',K,':',NDCG)
可以看出和手算的一致。
總結
在TopK推薦中,本文共總結了7種指標,實際上這些指標可以分爲兩類,第一類指標關心用戶想要的,我有沒有推薦到,強調語文裏的“準確性”,分別爲Precision@K、Recall@K、F1@K、HR@K,第二類指標更關心找到的這些項目,是否放在用戶更顯眼的位置裏,即強調“順序性”,分別爲MRR@K、MAP@K、NDCG@K。
實際上推薦系統裏還有一些評價指標,從評分預測的角度的RMSE,衡量多樣性的ILS等,這些指標並非在TopK推薦裏應用,就不一一細講了。在下一章,將進行傳統矩陣分解的實戰。