本文為您介紹改進版swing相似度計算算法原理,包括工具包下載、工具包詳細參數說明以及常見問題等。
改進版swing算法
改進1:限定common neighbour數量
原版的swing算法對于物品的同時被觸達的用戶數量過少的情況,并不適用。從統計學的角度來看,數據量過少,會導致結果的誤差過大。也就是說當同時觸達兩個物品的用戶數量過少時,這時候swing計算得出的結果誤差會比較大。
舉個極端的例子:
如下圖所示,有A、B、C三個視頻,X1到X10共10個皇馬死忠球迷,只關注音樂的音樂愛好者Y。皇馬死忠球迷會看與皇馬相關的內容,不管是熱門視頻C,還是中低頻視頻A,他們都會觀看,可以假定他們都看了200個關于皇馬的視頻。其中有一個球迷X1,被其他朋友推薦了一首冷門音樂B,聽完發現不喜歡這種暗黑系古典音樂,還是喜歡比較激昂的皇馬隊歌A。音樂愛好者Y會聽各種音樂,不管是A,還是B,都會播放。那么在這種情況下,由于視頻A、B的共現用戶量過少(只有2個),從而導致swing算法得出的分數s(A,B)=0.33 > s(A,C)=0.22
。但是很明顯,用戶在觀看視頻A的時候,推薦視頻C比推薦視頻B要好。
解法方法:問題是由兩個視頻的同時被看的用戶數量過少導致的,所以我們需要根據同時觀看過兩個視頻的用戶數量來對結果進行處理。我們在swing的基礎上,往其公式中增加了指示函數 Id(.) 來達到去噪的目的。同時,引入行為權重來打壓過熱的用戶或物品。改進后的公式如下:?
改進2:支持場景i2i
場景i2i —— 基于swing算法學習全網點擊與場景點擊的共現,傾向于預測用戶在場景內的點擊,形式化表示如下:
下圖給出了全網i2i與場景i2i的結構圖,與全網i2i相比,場景i2i不考慮無場景心智(場景內無點擊)的用戶,只保留用戶與場景內點擊的邊。
改進版swing部署
下載算法包到本地:swing-1.0.jar。
如果下載不了,拷貝上述鏈接到瀏覽器打開。
在MaxCompute項目空間下添加Jar包資源。
備注:同一項目空間只需要部署一次。
輸入輸出格式
輸入表(支持分區表)至少包含兩列:
user_id: bigint/string類型(代碼不檢查具體類型)的用戶ID或session id(推薦)
item_list: string類型的物品列表,物品與物品之間的分隔符為英文分號(;),每個物品用bigint類型的ID表示
item序列由分號分割,每個點擊物品由至少3個字段構成,即item_id,norm,timestamp,scene
,其中item_id需要在開頭。
norm代表物品近期的熱度(比如,統計該item被多少個用戶點擊過),即當前item的模,用于懲罰超級熱門的item,解決“哈利波特效應”問題。如果不知道norm如何計算,或者數據本身“哈利波特效應”不嚴重,可以不填這一列。
“哈利波特效應”通常是指一種現象,即一個非常受歡迎和廣泛知名的項目(比如《哈利波特》系列書籍或電影)會在推薦列表中占據主導地位,導致其它項目難以獲得同等的關注。
備注:norm值僅跟當前物品相關,表示物品的全局熱度,跟當前用戶無關;在整個訓練數據集里,同一物品的norm值必須要在多條記錄里保持一致。
timestamp遵循 %Y%m%d%H%M%S
(如:20190805223205) 的格式,如不需要可以用同一個timestamp填充。應按照點擊時間順序由遠至近組織item_list。
scene是可選字段,為用戶行為所在的場景,用于支持場景i2i。
user_id | item_list |
12031602 | 558448406561,137,20190805223205;585456515773,39397,20190806170331;10200442969,81,20190807223820 |
3954442742 | 658448406561,137,20190805223206;485456515773,39397,20190806170335 |
注意:同一條record中的item_list不要有重復的item_id, 建議 <user, item> pair每天只保留一個, 輸入表中的user_id的實際內容處理成 concat(user_id, date) 作為一個虛擬的session_id。
輸出表格式(支持分區列):
item_id bigint類型的錨定物品ID
similar_items 相似物品列表
其中similar_items形如item_id1,score1,coccur1,ori_score1;item_id2,score2,coccur2,ori_score2;...
其中,ori_score1 是原始相似度分;score1是最大值歸一之后的分數;coccur1是共現次數。
注意:輸出表需要預先創建好,column類型不能搞錯,column name可自定義。
結果示例:
item_id | similar_items |
1084315 | 7876717,0.000047,2,0.003601;6929557,0.000250,2,0.019373;1084342,0.000780,4,0.060325;1089552,0.000963,4,0.074516;1083467,0.008233,5,0.637016;66042,0.012925,6,1.000000 |
1090195 | 1090172,0.015136,1,1.000000 |
參考命令行
在DataWorks上新建 ODPS MR 節點(ODPS SQL類型的節點可能會報錯),使用如下命令提交Job
jar [<GENERIC_OPTIONS>] <MAIN_CLASS> [ARGS];
-conf <configuration_file> Specify an application configuration file
-resources <resource_name_list> file\table resources used in mapper or reducer, seperate by comma
-classpath <local_file_list> classpaths used to run mainClass
-D<name>=<value> Property value pair, which will be used to run mainClass
ARGS: <in_table/input_partition> <out_table/output_partition>
舉例如下:
公有云(彈外)用戶的命令:
##@resource_reference{"swing-1.0.jar"}
jar -resources swing-1.0.jar
-classpath swing-1.0.jar
-DtopN=150
-Dmax.user.behavior.count=500
-Dcommon.user.number.threshold=0
-Dmax.user.per.item=600
-Ddebug.info.print.number=10
-Dalpha1=5
-Dalpha2=1
-Dbeta=0.3
-Dodps.stage.mapper.split.size=1
com.alibaba.algo.PaiSwing
swing_click_input_table/ds=${bizdate}
swing_output/ds=${bizdate}
;
注意:完整代碼包括第一行的注釋; -D<key>=<value>指定參數值,-D后面不能有空格
彈內用戶的命令:
jar -resources swing-1.0.jar
-classpath http://schedule@{env}inside.cheetah.alibaba-inc.com/scheduler/res?id=XXXXX
-DtopN=150
-Dmax.user.behavior.count=500
-Dcommon.user.number.threshold=0
-Dmax.user.per.item=600
-Ddebug.info.print.number=10
-Dalpha1=5
-Dalpha2=1
-Dbeta=0.3
-Dodps.stage.mapper.split.size=1
com.alibaba.algo.PaiSwing
swing_click_input_table/ds=${bizdate}
swing_output/ds=${bizdate}
;
彈內classpath的獲取方式:
在DataWorks里右擊swing資源包,點擊“歷史版本”獲取文件路徑(http開頭)
參數說明
參數名稱 | 參數描述 | 參數類型 |
common.user.number.threshold | 同時訪問的用戶數量(過濾力度的設定),設置過大會導致剩下的結果過少,該參數需要根據具體業務場景進行調整探索 | 默認為0 |
max.user.per.item | 每個物品使用多少個用戶的點擊序列來計算k近鄰 | 整數,默認值為700 |
max.user.behavior.count | 每個用戶的最長序列長度,如果超過該長度會對最近進行截斷保留 | 整數,默認值為600 |
debug.info.print.number | 輸出debug信息的記錄數 | 整數,默認值為10 |
alpha1 | swing算法參數,見公式[1] | 整數,默認值為5 |
beta | swing算法參數,見公式[1] | 實數,默認值為0.3 |
alpha2 | swing算法參數,見公式[1] | 整數,默認值為1 |
user.column.name | 用戶或session ID的列名 | 字符串,默認值:"user_id" |
item.list.column.name | {物品ID,Norm}列表字段的列名 | 字符串,默認值:"item_list" |
topN | 每個trigger物品保留的k近鄰數目 | 整數,默認值為200 |
odps.stage.mapper.split.size | 【控制并發數】每個mapper處理的數據量 | 整數,單位M,默認值256 |
odps.stage.reducer.num | 【控制并發數】計算物品Pair相似度的reducer的數量 | 整數,默認值為200 |
item.delimiter | input table中item list的分隔符 | 默認值英文分號 |
item.field.delimiter | input table中item info的分隔符 | 默認值英文逗號 |
pos_norm | 物品熱度所對應字段,從0開始,在上述樣例中1 | 整數,默認值為1 |
pos_time | pos_time timestamp對應的字段編號,從0開始,在上述樣例中為2 | 整數,默認值為2 |
pos_scene | 場景名對應的字段編號,從0開始 | 整數,默認值為3 |
target.scene.name | 目標場景名,用于場景i2i建模 | 默認為全網i2i |
max.time.span | 認為兩個物品存在鄰居關系的最長點擊間隔天數 | 整數,默認值為1 |
do_supplement_by_adamic_adar | 當相似item數量不足topN時,是否嘗試用Adamic/Adar算法補足 | boolean, 默認值為true |
FQA
1. java.lang.ClassCastException: com.aliyun.odps.io.LongWritable cannot be cast to com.aliyun.odps.io.Text
FAILED: ODPS-0123131:User defined function exception - Traceback:
java.lang.ClassCastException: com.aliyun.odps.io.LongWritable cannot be cast to com.aliyun.odps.io.Text
at com.aliyun.odps.udf.impl.batch.TextBinary.put(TextBinary.java:55)
at com.aliyun.odps.udf.impl.batch.BaseWritableSerde.put(BaseWritableSerde.java:20)
at com.aliyun.odps.udf.impl.batch.BatchUDTFCollector.collect(BatchUDTFCollector.java:54)
at com.aliyun.odps.udf.UDTF.forward(UDTF.java:164)
at com.aliyun.odps.mapred.bridge.LotTaskUDTF.collect(LotTaskUDTF.java:62)
at com.aliyun.odps.mapred.bridge.LotReducerUDTF$ReduceContextImpl.write(LotReducerUDTF.java:167)
at com.aliyun.odps.mapred.bridge.LotReducerUDTF$ReduceContextImpl.write(LotReducerUDTF.java:162)
at com.aliyun.odps.mapred.bridge.LotReducerUDTF$ReduceContextImpl.write(LotReducerUDTF.java:151)
at com.alibaba.algo.Paiswing$swingI2IReducer.reduce(Paiswing.java:346)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.aliyun.odps.mapred.bridge.utils.MapReduceUtils.runReducer(MapReduceUtils.java:160)
at com.aliyun.odps.mapred.bridge.LotReducerUDTF.run(LotReducerUDTF.java:330)
at com.aliyun.odps.udf.impl.batch.BatchStandaloneUDTFEvaluator.run(BatchStandaloneUDTFEvaluator.java:53)
輸出表的item_id一定要是bigint類型,不能是string類型。注意create table
語句的schema信息。