【推薦算法】從零開始做推薦(二)——TopK推薦的評價指標,計算原理與樣例

前言

  推薦系統的評價指標在不同類型的推薦場景下,選用的不同。有些推薦的評價指標並非完全出自推薦系統,而是從搜索算法,信息檢索,機器學習等相關領域沿用過來,因此網上有些對評價指標的解釋並非完全以推薦系統的角度進行,這讓我會在學習的時候產生困惑,特此做出一些整理,力求完全用推薦系統的角度解釋,並給出計算的具體流程。

  如果你對本系列(未寫完,持續更新中)感興趣,可接以下傳送門:
  【推薦算法】從零開始做推薦(一)——認識數據
  【推薦算法】從零開始做推薦(二)——推薦系統的評價指標,計算原理與實現樣例
  【推薦算法】從零開始做推薦(三)——傳統矩陣分解的TopK推薦實戰
  【推薦算法】從零開始做推薦(四)——python Keras框架 利用Embedding實現矩陣分解TopK推薦
  【推薦算法】從零開始做推薦(五)——貝葉斯個性化排序矩陣分解 (BPRMF) 推薦實戰
  【推薦算法】從零開始做推薦(六)——貝葉斯性化排序矩陣分解 (BPRMF) 的Tensorflow版

TopK 推薦評價指標

TopK推薦

  定義:TopK推薦顧名思義,首先推薦給用戶的項目數是K個,其次,這些推薦依據用戶對項目的“評分”進行從高到低排序展示給用戶。
  舉例:打開淘寶後給你推薦的TopK個商品;
  特點:將推薦視爲分類問題,如給定一個用戶,其將要訪問的項目作爲其分類的標籤,而對該用戶的TopK推薦即爲模型對該用戶的多分類結果。
  因此TopK推薦的評價指標也大多爲分類問題指標沿用演化而來。

Precision 精確度,Recall 召回率

  我最初瞭解它們,是出自機器學習分類問題裏經典的混淆矩陣。網上有部分解釋也是沿用混淆矩陣,但這會讓我對於TopKTopK推薦直觀理解會造成困難,因此直接以推薦算法角度給出計算公式:
Precision@K=i=1NR(i)T(i)i=1NR(i).(1)Recall@K=i=1NR(i)T(i)i=1NT(i).(2) \begin {aligned} Precision@K = \frac { \sum_{i=1}^N|R(i)∩T(i)|}{\sum_{i=1}^N|R(i)|} ….…(1)\\ Recall@K = \frac {\sum_{i=1}^N|R(i)∩T(i)|}{\sum_{i=1}^N|T(i)|}….…(2) \end{aligned}

  下面對公式(1)(2)中的變量進行解釋.
  ii:第ii次推薦。
  R(i)R(i):第ii次推薦中,推薦的項目列表。
  T(i)T(i):第ii次推薦中,用戶真實訪問的項目列表。
  KKTopKTopK推薦中推薦列表的長度。
  NN:總推薦次數。
  大多數場景下,對每一個用戶進行一次TopKTopK推薦,因此nn常爲測試集中的用戶,NN常爲測試集中的用戶數量。
  實際上,Precision@KPrecision@KRecall@KRecall@K的分子都是單次推薦時命中數量的累加,區別在分母,Recall@KRecall@K的分母要按實際情況進行累加,但Precision@KPrecision@K的分母是推薦列表長度的累加,可以很輕鬆的得出:i=1NR(i)=N×K\sum_{i=1}^N|R(i)|=N×K
  注意Precision@KPrecision@KRecall@KRecall@K的計算都是先累加再相除,不能寫成先相除再累加。
  思考Precision@KPrecision@KRecall@KRecall@K的值域在每次推薦都命中的情況下一定爲1嗎?這受R(i)R(i)T(i)T(i)的長度影響。對每次推薦而言,R(i)=K|R(i)|=K是定長的,而T(i)|T(i)|是變長的。
  當任意的T(i)|T(i)|都小於R(i)=K|R(i)|=K時,有max(R(i)T(i))=T(i)i=1NT(i)<i=1NR(i)max(|R(i)∩T(i)|)=|T(i)|, \sum_{i=1}^N|T(i)|<\sum_{i=1}^N|R(i)|,在每次都命中的前提下,:
Precision@K=i=1NR(i)T(i)i=1NR(i)=i=1NT(i)i=1NR(i)<1Recall@K=i=1NR(i)T(i)i=1NT(i)=i=1NT(i)i=1NT(i)=1Precision@K = \frac { \sum_{i=1}^N|R(i)∩T(i)|}{\sum_{i=1}^N|R(i)|}= \frac { \sum_{i=1}^N|T(i)|}{\sum_{i=1}^N|R(i)|}<1\\Recall@K = \frac {\sum_{i=1}^N|R(i)∩T(i)|}{\sum_{i=1}^N|T(i)|}=\frac { \sum_{i=1}^N|T(i)|}{\sum_{i=1}^N|T(i)|}=1
  同理,當任意的T(i)|T(i)|都大於R(i)=K|R(i)|=K時,在每次都命中的前提下,有Precision@K=1,Recall@K<1Precision@K =1,Recall@K <1,僅當任意的|T(i)|T(i)|都等於R(i)=K|R(i)|=K時,在每次都命中的前提下,Precision@K=1,Recall@K=1Precision@K =1,Recall@K =1
  疑惑:可以看出,要使Precision@KPrecision@KRecall@KRecall@K達到1的條件是非常苛刻的,實際推薦場景中,很可能這兩個指標的最大值都達不到1。那這會對其作爲評價指標產生負面影響嗎?歡迎各路大神與我交流。

F1值

  有了Precision@KPrecision@KRecall@KRecall@KF1F1值的計算也就順水推舟地過來了,和機器學習中的公式完全一樣。F1值是完全基於Precision@KPrecision@KRecall@KRecall@K的,是一個綜合考慮兩者的評價指標,感興趣的可以看這裏
F1@K=2Precision@KRecall@KPrecision@K+Recall@K.(3)F1@K=\frac{2*Precision@K*Recall@K}{Precision@K+Recall@K}….…(3)

命中率(Hit Ratio,HR)

  聽名字很好理解,但實際上概念很容易搞混。剛好看到一篇文章(TKDE-2019)有用命中率,這裏以它的計算方法爲準。
在這裏插入圖片描述
  可以看出他這裏分子是推薦命中的用戶數,分母的是測試集中用戶數,其對每一個用戶推薦一次,因此準確而言分母應爲推薦次數。

hr(i)={1,R(i)T(i)0,R(i)T(i)=.(4)HR@K=i=1Nhr(i)N.(5)hr(i)=\left\{ \begin{array}{rcl} 1 & ,R(i)∩T(i)\not=\varnothing\\ 0 &, R(i)∩T(i)=\varnothing \end{array} \right.….…(4)\\ HR@K=\frac {\sum_{i=1}^Nhr(i)}{N}….…(5)
  可以看出,HR@KHR@KRecall@KRecall@K的計算思路有些相似,實際上,HR@KHR@K也是衡量召回關係的指標。個人感覺HR@KHR@K的計算方式目前仍是模糊的,甚至部分博客將HR@KHR@K寫成Recall@KRecall@K的計算方式,因此不是很建議使用。

平均到數排名(Mean Reciprocal Rank,MRR)

  源自搜索領域,其中Reciprocal Rank(RR)指第一個正確答案在TopK推薦列表裏的排名的倒數。MRR即爲多次推薦RR的平均值。
MRR@K=1Ni=1N1rank(i).(6)MRR@K=\frac1N\sum_{i=1}^N\frac{1}{rank(i)}….…(6)
  其中rank(i)rank(i)表示第ii次推薦中,第一個命中的項目,在TopK推薦列表裏的次序,若沒命中,則rank(i)rank(i)\rightarrow∞,即1rank(i)=0\frac{1}{rank(i)}=0

平均精度均值(Mean Average Precision,MAP)

  同樣,我們首先得知道Average Precision(AP)怎麼計算(自己總結的,慎用):
AP(i)={j=1hitsijrank(hitsi(j))T(i),hitsi00,hitsi=0.(7) AP(i)=\left\{ \begin{array}{rcl} \frac{\sum_{j=1}^{|hits_i|}\frac{j}{rank(hits_i(j))}}{|T(i)|} & ,|hits_i|\not=0\\ 0 &, |hits_i|=0 \end{array} \right.….…(7)

  其中hitsi|{hits}_i|表示第ii次推薦中,命中的項目個數;hitsi(j)hits_i(j)表示在第ii次推薦中命中的第jj個項目;rank(item)rank(item)表示itemitemR(i)R(i)中的位置。
  MAP@KMAP@K即爲APAP的平均值,其中NN同樣爲總推薦次數:
 ⁣MAP@K=1Ni=1NAP(i).(8)\negthinspace MAP@K=\frac1N\sum_{i=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=1KreljDCGi=j=1K2relj1log2(rank(hitsi(j))+1)NDCGi=DCGiIDCGiNDCG@K=1Ni=1NNDCGiCG=\sum_{j=1}^Krel_j\\ DCG_i=\sum_{j=1}^K\frac{2^{rel_j}-1}{log_2(rank(hits_i(j))+1)}\\ NDCG_i=\frac{DCG_i}{IDCG_i}\\ NDCG@K=\frac1N\sum_{i=1}^NNDCG_i

  hitsi(j)ijhits_i(j)表示第i次推薦命中的第j個項目;
  rank(item)itemR(i)(1)rank(item)表示item在R(i)中的位置(從1開始編號)。
  以下爲個人推算,不保證正確性,歡迎討論!!!
  之所以說在評分推薦系統裏常用NDCG,是因爲這個relrel表示相關性,指的就是項目與用戶的相關性,也就是評分。現在挪到TopK裏,那命中了就是相關,未命中就應該是無關,若未命中時,設rank(hitsi(j))+rank(hits_i(j))\rightarrow+∞,此時log2(rank(hitsi(j))+1)+,1log2(rank(hitsi(j))+1)0log_2{(rank(hits_i(j))+1)}\rightarrow+∞,\frac{1}{log_2{(rank(hits_i(j))+1)}}\rightarrow0,則DCG可改寫成:
DCGi=j=1hitsi211log2(rank(hitsi(j))+1)=j=1hitsi1log2(rank(hitsi(j))+1)DCG_i=\sum_{j=1}^{|hits_i|}\frac{2^{1}-1}{log_2(rank(hits_i(j))+1)}=\sum_{j=1}^{|hits_i|}\frac{1}{log_2(rank(hits_i(j))+1)}
  那麼問題來了,IDCGiIDCG_i怎麼求?按照原理,IDCGiIDCG_i即爲最優的排序,則命中的全要在前面,若10個裏面命中五個,則序號應爲12345。則有如下公式:
IDCGi=j=1hitsi1log2(j+1)IDCG_i=\sum_{j=1}^{|hits_i|}\frac{1}{log_2(j+1)}
  照着原公式進行推導,則有:
NDCG@K=1Ni=1NNDCGi=NDCG@K=1Ni=1NDCGiIDCGi=1Ni=1Nj=1hitsi1log2(rank(hitsi(j))+1)j=1hitsi1log2(j+1)NDCG@K=\frac1N\sum_{i=1}^NNDCG_i=NDCG@K=\frac1N\sum_{i=1}^N\frac{DCG_i}{IDCG_i}=\frac1N\sum_{i=1}^N\frac{\sum_{j=1}^{|hits_i|}\frac{1}{log_2(rank(hits_i(j))+1)}}{\sum_{j=1}^{|hits_i|}\frac{1}{log_2(j+1)}}
  可以發現,TKDE那篇文中似乎並沒有計算IDCG,不知道這裏面有什麼說法沒。

樣例

  假設在給用戶推薦的場景中,總共對3個用戶進行了Top@5Top@5推薦,情況分別如下:
  R=[[2,5,1,3,9],[6,2,0,12,8],[1,6,7,11,2]]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]]T=[[3,10,7,21],[15,0,5,2,13],[19]]
則相應的指標計算如下:
  Precision@5=1+2+05+5+5=315=0.2Precision@5 =\frac{1+2+0}{5+5+5}=\frac{3}{15}=0.2
  Recall@5=1+2+04+5+1=310=0.3Recall@5=\frac{1+2+0}{4+5+1}=\frac{3}{10}=0.3
  F1@5=20.20.30.2+0.3=0.120.5=0.24F1@5=\frac{2*0.2*0.3}{0.2+0.3}=\frac{0.12}{0.5}=0.24
  HR@5=1+1+03=230.667HR@5=\frac{1+1+0}{3}=\frac{2}{3}\approx0.667
  MRR@5=14+12+03=14=0.25MRR@5=\frac{\frac14+\frac12+0}{3}=\frac{1}{4}=0.25
  MAP@5=144+12+235+013=717200.09861MAP@5=\frac{\frac{\frac14}{4}+\frac{\frac12+\frac23}{5}+\frac01}{3}=\frac{71}{720}\approx0.09861
  NDCG@5=13(1log2(4+1)1log2(1+1)+1log2(2+1)+1log2(3+1)1log2(1+1)+1log2(2+1)+0)=13(log22log25+log23+22(1+log23)+0)0.37470NDCG@5=\frac{1}{3}*(\frac{\frac{1}{log_2{(4+1)}}{}}{\frac{1}{log_2{(1+1)}}}+\frac{\frac{1}{log_2{(2+1)}}+\frac{1}{log_2{(3+1)}}}{\frac{1}{log_2{(1+1)}}+\frac{1}{log_2{(2+1)}}}+0)=\frac13*(\frac{log_22}{log_25}+\frac{log_23+2}{2*(1+log_23)}+0)\approx0.37470
  用代碼也寫了個,當作驗算,效果如下:

# -*- coding: utf-8 -*-
"""
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@KRecall@KF1@KHR@KPrecision@K、Recall@K、F1@K、HR@K,第二類指標更關心找到的這些項目,是否放在用戶更顯眼的位置裏,即強調“順序性”,分別爲MRR@KMAP@KNDCG@KMRR@K、MAP@K、NDCG@K
  實際上推薦系統裏還有一些評價指標,從評分預測的角度的RMSE,衡量多樣性的ILS等,這些指標並非在TopK推薦裏應用,就不一一細講了。在下一章,將進行傳統矩陣分解的實戰。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章