算法代码实现

Metric

// Some code
class Metrics:
    def cal_auc(self, y_pred, y_true):
        """
        y_true: 1D- list
        y_pred: 1D-list
        auc = (number of positive pred > negative pred pair) / all pair conditions
        """
        pos_ls= []
        neg_ls = []
        for i in range(len(y_pred)):
            if y_true[i] != 0:
                pos_ls.append(i)
            else:
                neg_ls.append(i)

        cnt = 0
        for p in pos_ls:
            for n in neg_ls:
                if y_pred[p] > y_pred[n]:
                    cnt += 1
        auc = cnt/(len(neg_ls) * len(pos_ls))
        return auc
    def mrr(self, y_pred, y_true, topN=3):
        """
        Mean Reciprocal Rank
        强调的是用户的需求项在模型推荐列表中的位置,越靠前越佳。
        MRR = 1/S sum_i (1/pi), pi = i^th item rank position, S= sample amount
        pi = 打分的商品列表中第i个商品所在的排序后列表的位置。 如果第i个商品不在排序后的列表的topN
        的位置, 那么就是 1/pi=0
        y_pred: 1D- list
        y_true: 1D - list  with target item 
        """
        # 倒排
        rank = len(y_pred) - np.argsort(np.array(y_pred)) -1
        print("mrr rank:",rank,y_pred)
        topN_rank = rank.squeeze()[:topN]
        mrr = 0
        for i in y_true:
            if  i not in topN_rank:
                pi=0
            else:
                pi = 1/ (1+topN_rank.tolist()[i])
            mrr += pi
        return mrr
    def ndcg(self, y_preds, y_trues, K=5):
        """
        inputs: 
            y_preds: N*L
            y_trues: N*L
        y_pred仅仅用来算dcg的序列, 不需要它的score 绝对值!!
        normalize discount cumulative gain
        ndcg = dcg/idcg
        dcg = sum_i^N ( rel_i / log_2(i+1) )
        1. 先把y_pred 倒排,取topK, 以及找他们对应的ground truth score
        2. 用ground truth score 作为rel, 以及用pred排的序的位置rank作为 分母算dcg
        3. 算idcg
        4. ndcg = mean_i^N dcg/idcg 对N条样本的ndcg求均值
        """
        def dcg(pred, K):
            pred= pred[:K] # topK  ground truth relevence scores
            dcg=sum([ s/(np.log2(2+ i)) for i, s in enumerate(pred) ])
            return dcg
        res = 0
        for y_pred, y_true in zip(y_preds, y_trues):
            sorted_true_rel = [ x for _, x in sorted(zip(y_pred,y_true), reverse=True) ]
            dcg_val = dcg(sorted_true_rel, K)
            ideal_true_rel = sorted(y_true, reverse=True)
            idcg_val = dcg(ideal_true_rel, K)
            print("idcg: ", idcg_val)
            if idcg_val >0:
                ndcg = dcg_val/idcg_val 
            else:
                ndcg = 0
            res += ndcg
        res /= len(y_preds)
        return res
    def recall(self, y_pred, y_true, k):
        """
        recall = TP / (TP + FN)
        """
        def rc(pred, lb,k):
            sorted_lb = [ l for p, l in sorted(zip(pred, lb))]
            topk = sorted_lb[:k]
            return sum(topk)/sum(lb)
        res= []
        for pred, lb in zip(y_pred,y_true):
            res.append(rc(pred, lb, k))
        #  average recall
        return sum(res)/len(res)
    
    def precision(self, y_pred, y_true, K):
        def pk(pred, lb,k):
            sorted_lb = [ l for p, l in sorted(zip(pred, lb))]
            topk = sorted_lb[:k]
            return sum(topk), sum(topk)/k
        res= []
        x=0
        for pred, lb in zip(y_pred,y_true):
            num, pk_v = pk(pred, lb, K)
            res.append(pk_v)
            x+= num
        #  average recall
        return sum(res)/len(res), x/(len(y_pred)*K)
    
    def hitrate(self, y_pred, y_true, K):
        """
        hitrate@k: 分用户推荐的item的top k 是否有ground truth
        用户力度
        input:
            y_pred: N*L  N= number of user
            y_true: N*L
        https://cran.r-project.org/web/packages/recometrics/vignettes/Evaluating_recommender_systems.html
        """
        def hit(pred, lb, k):
            res = []
            for i, (p, l) in enumerate(sorted(zip( pred, lb), reverse=True)):
                if i <k:
                    res.append(l)
            return sum(res)>0
        hr = []
        for p, l in zip(y_pred, y_true):
            hr.append(hit(p, l,K))
        return sum(hr)/len(hr)
    
mt=Metrics()
print("metric auc:",mt.cal_auc([0.8,0.1,0.4,0.5,0.6,0.1],[1,0,1,0,0,0]))
print("metric mrr:",mt.mrr([0.8,0.1,0.4,0.5,0.6,0.1],[3, 2],4))
print("metric ndcg:",mt.ndcg( [[0.8,0.1,0.4,0.5,0.6,0.1], [0.8,0.1,0.4,0.5,0.6,0.1]],[[1,0,1,0,0,0], [1,0,1,1,0,0]] ,4))
print("metric hitrate:",mt.hitrate( [[0.8,0.1,0.4,0.5,0.6,0.1], [0.8,0.1,0.4,0.5,0.6,0.1]],[[0,0,0,0,0,1], [1,0,1,1,0,0]] ,4))
print("metric recall:",mt.recall( [[0.8,0.1,0.4,0.5,0.6,0.1], [0.8,0.1,0.4,0.5,0.6,0.1]],[[0,0,0,0,0,1], [1,0,1,1,0,0]] ,10))
print("metric precision:",mt.precision( [[0.8,0.1,0.4,0.5,0.6,0.1], [0.8,0.1,0.4,0.5,0.6,0.1]],[[0,0,0,0,0,1], [1,0,1,1,0,0]] ,10))

Last updated

Was this helpful?