【推薦系統】基於標簽的推薦算法(SimpleTagBased,NormTagBased,TagBased-TFIDF)


本篇主要介紹基於標簽的推薦算法,涉及了3個原理較簡單的計算方法(Simple Tag-based、Normal Tag-based、Tag-based-Tfidf ),以及python代碼實現。

1.概述

1.1 如何定義用戶畫像

用戶畫像即是對用戶行為特征的總結歸納和描述,以更好的提升業務質量。
用戶畫像的關鍵步驟:

  1. 定義全局的用戶唯一標識id(例如身份證、手機號、用戶id等)
  2. 給用戶打標簽(用戶標簽,消費標簽,行為標簽,內容分析)
  3. 根據標簽為為業務帶來不同階段的收益。在用戶生命周期的3個階段:獲客、粘客、留客 中,根據各類的用戶數據對用戶進行標簽化、用戶畫像等,盡最大限度對用戶進行留存。

1.2 如何給用戶打標簽

用戶畫像 即描述用戶的行為特征,可抽象為標簽。 那么如何給用戶打標簽呢?

典型的方式有:

  • PGC:專家生產,通過工作人員進行打標簽(質量高,數據規范)
  • UGC:用戶生產 (質量低,數據不規范)

標簽是對高維事物的抽象(降維),所以可以利用聚類、降維的方法進行物品標簽的生成。目前主流的聚類方法有:Kmeans,EM,DBScan,層級聚類,PCA等等。

1.3 如何給用戶推薦標簽

當用戶u給物品i打標簽時,可以給用戶推薦和物品i相關的標簽,方法如下:

  • 方法1:給用戶u推薦整個系統最熱門的標簽
  • 方法2:給用戶u推薦物品i上最熱門的標簽
  • 方法3:給用戶u推薦他自己經常使用的標簽
  • 將方法2和3進行加權融合,生成最終的標簽推薦結果

2.基於標簽的算法

2.1 Simple Tag-based

其中 user_tags[u,t] 表示【用戶u使用標簽t的次數】,tag_items[t, i] 表示【物品i被打上標簽t的次數】

2.2 Norm Tag-based

Simple Tag-based 使用次數的絕對值,在很多情況下不能真實的反映用戶的個性化需求。 比如可能某個用戶尤其喜歡打標簽,所以本身打標簽的次數就比較多,或者某個標簽可能非常熱門,可能每個物品都打上這個熱門標簽,導致最終的score很大。 因此對Simple Tag-based 做歸一化改進,即Norm Tag-based 算法。

其中 user_tags[u] 表示【用戶u打標簽的總次數】,tag_items[t] 表示【標簽t被使用的總次數】

2.3 Tag-based TFIDF

同樣的,如果一個tag很熱門,會導致給熱門標簽過大的權重,不能反應用戶個性化的興趣。
TF-IDF的定義:

  • TF:Term Frequency,詞頻=單詞次數/文檔中總單詞數
    一個單詞的重要性和它在文檔中出現的次數呈正比

  • IDF:Inverse Document Frequency,逆向文檔頻率=log(文檔總數/(單詞出現的文檔數+1))
    一個單詞在文檔中的區分度。這個單詞出現的文檔數越少,區分度越大,IDF越大

這里借鑒TF-IDF的思想,使用tag_users[t]表示標簽t被多少個不同的用戶使用

3.代碼實現

基本結構的定義:

  • 用戶打標簽的記錄【user_id, item_id, tag】
  • 用戶打過的標簽【user_id, tag】
  • 用戶打過標簽的item【user_id, item】
  • 打上某標簽的item【tag, item】
  • 同樣打過某標簽的用戶【tag, user_id】
# 使用SimpleTagBased算法對Delicious2K數據進行推薦
# 原始數據集:https://grouplens.org/datasets/hetrec-2011/
# 數據格式:userID  bookmarkID  tagID timestamp
import random
import math
import operator
import pandas as pd

file_path = "./user_taggedbookmarks-timestamps.dat"
# 字典類型,保存了user對item的tag,即{userid: {item1:[tag1, tag2], ...}}
records = {}
# 訓練集,測試集
train_data = dict()
test_data = dict()
# 用戶標簽,商品標簽
user_tags = dict()
tag_items = dict()
user_items = dict()

# 使用測試集,計算精確率和召回率
def precisionAndRecall(N):
    hit = 0
    h_recall = 0
    h_precision = 0
    for user,items in test_data.items():
        if user not in train_data:
            continue
        # 獲取Top-N推薦列表
        rank = recommend(user, N)
        for item,rui in rank:
            if item in items:
                hit = hit + 1
        h_recall = h_recall + len(items)
        h_precision = h_precision + N
    #print('一共命中 %d 個, 一共推薦 %d 個, 用戶設置tag總數 %d 個' %(hit, h_precision, h_recall))
    # 返回准確率 和 召回率
    return (hit/(h_precision*1.0)), (hit/(h_recall*1.0))

# 對單個用戶user推薦Top-N
def recommend(user, N):
    recommend_items=dict()
    # 對Item進行打分,分數為所有的(用戶對某標簽使用的次數 wut, 乘以 商品被打上相同標簽的次數 wti)之和
    tagged_items = user_items[user]     
    for tag, wut in user_tags[user].items():
        #print(self.user_tags[user].items())
        for item, wti in tag_items[tag].items():
            if item in tagged_items:
                continue
            #print('wut = %s, wti = %s' %(wut, wti))
            
            
            if item not in recommend_items:
                recommend_items[item] = wut * wti
            else:
                recommend_items[item] = recommend_items[item] + wut * wti
    return sorted(recommend_items.items(), key=operator.itemgetter(1), reverse=True)[0:N]

# 使用測試集,對推薦結果進行評估
def testRecommend():
    print("推薦結果評估")
    print("%3s %10s %10s" % ('N',"精確率",'召回率'))
    for n in [5,10,20,40,60,80,100]:
        precision,recall = precisionAndRecall(n)
        print("%3d %10.3f%% %10.3f%%" % (n, precision * 100, recall * 100))
        
# 數據加載
def load_data():
    print("開始數據加載...")
    df = pd.read_csv(file_path, sep='\t')
    for i in range(len(df)):
        uid = df['userID'][i]
        iid = df['bookmarkID'][i]
        tag = df['tagID'][i]
        # 鍵不存在時,設置默認值{}
        records.setdefault(uid,{})
        records[uid].setdefault(iid,[])
        records[uid][iid].append(tag)
    print("數據集大小為 %d." % (len(df)))
    print("設置tag的人數 %d." % (len(records)))
    print("數據加載完成\n")

# 將數據集拆分為訓練集和測試集
def train_test_split(ratio, seed=100):
    random.seed(seed)
    for u in records.keys():
        for i in records[u].keys():
            # ratio比例設置為測試集
            if random.random()<ratio:
                test_data.setdefault(u,{})
                test_data[u].setdefault(i,[])
                for t in records[u][i]:
                    test_data[u][i].append(t)
            else:
                train_data.setdefault(u,{})
                train_data[u].setdefault(i,[])
                for t in records[u][i]:
                    train_data[u][i].append(t)        
    print("訓練集樣本數 %d, 測試集樣本數 %d" % (len(train_data),len(test_data)))

# 設置矩陣 mat[index, item] = 1
def addValueToMat(mat, index, item, value=1):
    if index not in mat:
        mat.setdefault(index,{})
        mat[index].setdefault(item,value)
    else:
        if item not in mat[index]:
            mat[index][item] = value
        else:
            mat[index][item] += value


# 使用訓練集,初始化user_tags, tag_items, user_items
def initStat():
    records=train_data
    for u,items in records.items():
        for i,tags in items.items():
            for tag in tags:
                #print tag
                # 用戶和tag的關系
                addValueToMat(user_tags, u, tag, 1)
                # tag和item的關系
                addValueToMat(tag_items, tag, i, 1)
                # 用戶和item的關系
                addValueToMat(user_items, u, i, 1)
    print("user_tags, tag_items, user_items初始化完成.")
    print("user_tags大小 %d, tag_items大小 %d, user_items大小 %d" % (len(user_tags), len(tag_items), len(user_items)))

# 數據加載
load_data()
# 訓練集,測試集拆分,20%測試集
train_test_split(0.2)
initStat()
testRecommend()

開始數據加載...
數據集大小為 437593.
設置tag的人數 1867.
數據加載完成

訓練集樣本數 1860, 測試集樣本數 1793
user_tags, tag_items, user_items初始化完成.
user_tags大小 1860, tag_items大小 36884, user_items大小 1860
推薦結果評估
  N        精確率        召回率
  5      0.829%      0.355%
 10      0.633%      0.542%
 20      0.512%      0.877%
 40      0.381%      1.304%
 60      0.318%      1.635%
 80      0.276%      1.893%
100      0.248%      2.124%

參考鏈接:https://blog.csdn.net/weixin_41578567/article/details/110049018


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM