Jdata大資料競賽總結
競賽概述:
本次大賽以京東商城真實的使用者、商品和行為資料(脫敏後)為基礎,參賽隊伍需要通過資料探勘的技術和機器學習的演算法,構建使用者購買商品的預測模型,輸出高潛使用者和目標商品的匹配結果,為精準營銷提供高質量的目標群體。同時,希望參賽隊伍能通過本次比賽,挖掘資料背後潛在的意義,為電商使用者提供更簡單、快捷、省心的購物體驗。資料介紹:
符號定義:S:提供的商品全集;
P:候選的商品子集(JData_Product.csv),P是S的子集;
U:使用者集合;
A:使用者對S的行為資料集合;
C:S的評價資料。
訓練資料部分:
提供2016-02-01到2016-04-15日使用者集合U中的使用者,對商品集合S中部分商品的行為、評價、使用者資料;提供部分候選商品的資料P。
選手從資料中自行組成特徵和資料格式,自由組合訓練測試資料比例。
預測資料部分:
2016-04-16到2016-04-20使用者是否下單P中的商品,每個使用者只會下單一個商品;抽取部分下單使用者資料,A榜使用50%的測試資料來計算分數;B榜使用另外50%的資料計算分數(計算準確率時剔除使用者提交結果中user_Id與A榜的交集部分)。
1、使用者資料
2、商品資料
3、評價資料
任務描述:
參賽者需要使用京東多個品類下商品的歷史銷售資料,構建演算法模型,預測使用者在未來5天內,對某個目標品類下商品的購買意向。對於訓練集中出現的每一個使用者,參賽者的模型需要預測該使用者在未來5天內是否購買目標品類下的商品以及所購買商品的SKU_ID。評測演算法將針對參賽者提交的預測結果,計算加權得分。
評分標準:
參賽者提交的結果檔案中包含對所有使用者購買意向的預測結果。對每一個使用者的預測結果包括兩方面:1、該使用者2016-04-16到2016-04-20是否下單P中的商品,提交的結果檔案中僅包含預測為下單的使用者,預測為未下單的使用者,無須在結果中出現。若預測正確,則評測演算法中置label=1,不正確label=0;
2、如果下單,下單的sku_id (只需提交一個sku_id),若sku_id預測正確,則評測演算法中置pred=1,不正確pred=0。
對於參賽者提交的結果檔案,按如下公式計算得分:
Score=0.4*F11 + 0.6*F12
此處的F1值定義為:
F11=6*Recall*Precise/(5*Recall+Precise)
F12=5*Recall*Precise/(2*Recall+3*Precise)
其中,Precise為準確率,Recall為召回率.
F11是label=1或0的F1值,F12是pred=1或0的F1值.
資料清洗:
比賽的題目是高潛使用者的購買意向的預測,從機器學習的角度來講我們可以認為這是一個二分類的任務.那麼我們就是嘗試去構建自己的正負樣本.由於我們拿到的是原始資料,裡面存在很多噪聲,因而第一步我們先要對資料清洗,比如說:
- 去掉只有購買記錄的使用者(沒有可用的歷史瀏覽等記錄來預測使用者將來的購買意向)
- 去掉瀏覽量很大而購買量很少的使用者(惰性使用者或爬蟲使用者)
- 去掉最後5(7)天沒有記錄(互動)的商品和使用者
- ......
為了能夠進行上述清洗,在此首先構造了簡單的使用者(user)行為特徵和商品(item)行為行為特徵,對應於兩張表user_table和item_table
- user_table
user_id(使用者id),age(年齡),sex(性別),
user_lv_cd(使用者級別),browse_num(瀏覽數),
addcart_num(加購數),delcart_num(刪購數),
buy_num(購買數),favor_num(收藏數),
click_num(點選數),buy_addcart_ratio(購買加購轉化率),
buy_browse_ratio(購買瀏覽轉化率),
buy_click_ratio(購買點選轉化率),
buy_favor_ratio(購買收藏轉化率)
- item_table特徵包括:
sku_id(商品id),attr1,attr2,
attr3,cate,brand,browse_num,
addcart_num,delcart_num,
buy_num,favor_num,click_num,
buy_addcart_ratio,buy_browse_ratio,
buy_click_ratio,buy_favor_ratio,
comment_num(評論數),
has_bad_comment(是否有差評),
bad_comment_rate(差評率)
探索高潛使用者的行為:
比賽的題目是高潛使用者購買意向預測, 那麼理解清楚什麼是高潛使用者對於資料分析,特徵抽取,以及之後的建立模型有著至關重要的作用.
簡單來講,作為訓練集的高潛使用者應該具有以下特徵:
- 必須有購買行為
- 對一個商品購買和其他互動行為(瀏覽,點選,收藏等)時間差應該多於一天
因為根據賽題,我們需要預測未來5天的購買情況,那麼如果使用者對某商品在同一天完成所有的互動行為(包括購買),
我們無法從這種交易中指導未來的預測.
特徵工程:
使用者相關特徵:
主要根據使用者資料集,對使用者原本的年齡、性別、使用者等級,採用獨熱編碼。
def convert_age(age_str):
if age_str == u'-1':
return 0
elif age_str == u'15歲以下':
return 1
elif age_str == u'16-25歲':
return 2
elif age_str == u'26-35歲':
return 3
elif age_str == u'36-45歲':
return 4
elif age_str == u'46-55歲':
return 5
elif age_str == u'56歲以上':
return 6
else:
return -1
user = pd.read_csv(user_path, encoding='gbk')
user['age'] = user['age'].map(convert_age)
age_df = pd.get_dummies(user["age"], prefix="age")
sex_df = pd.get_dummies(user["sex"], prefix="sex")
user_lv_df = pd.get_dummies(user["user_lv_cd"], prefix="user_lv_cd")
user = pd.concat([user['user_id'], age_df, sex_df, user_lv_df], axis=1)
商品相關特徵:
根據商品資料集和評論資料集,對商品屬性特徵a1、a2、a3,和評論數量comment_num,進行獨熱編碼
product = pd.read_csv(product_path)
attr1_df = pd.get_dummies(product["a1"], prefix="a1")
attr2_df = pd.get_dummies(product["a2"], prefix="a2")
attr3_df = pd.get_dummies(product["a3"], prefix="a3")
product = pd.concat([product[['sku_id', 'cate', 'brand']], attr1_df, attr2_df, attr3_df], axis=1)
comments = comments[(comments.dt >= comment_date_begin) & (comments.dt < comment_date_end)]
df = pd.get_dummies(comments['comment_num'], prefix='comment_num')
comments = pd.concat([comments, df], axis=1) # type: pd.DataFrame
comments = comments[['sku_id', 'has_bad_comment', 'bad_comment_rate', 'comment_num_1', 'comment_num_2', 'comment_num_3', 'comment_num_4']]
提取商品在某段時間內的瀏覽購買轉化率,加入購物車購買轉化率,收藏購買轉化率,點選購買轉化率,可展現該商品在近期的人氣與熱門程度,方便我們預測該商品是否為使用者可能購買的高潛商品。
actions = get_actions(start_date, end_date)
df = pd.get_dummies(actions['type'], prefix='action')
actions = pd.concat([actions['sku_id'], df], axis=1)
actions = actions.groupby(['sku_id'], as_index=False).sum()
actions['product_action_1_ratio'] = actions['action_4'] / actions['action_1']
actions['product_action_2_ratio'] = actions['action_4'] / actions['action_2']
actions['product_action_3_ratio'] = actions['action_4'] / actions['action_3']
actions['product_action_5_ratio'] = actions['action_4'] / actions['action_5']
actions['product_action_6_ratio'] = actions['action_4'] / actions['action_6']
使用者行為相關特徵:
提取從起始日期start_date到截止日期end_date內的行為資料集,對使用者行為型別type做one-hot編碼,然後使用聚合函式groupby,對user_id,sku_id進行聚合分組,對組內的其他特徵進行相加,即可統計出在此期間使用者對商品的各種行為型別的累計互動次數。
這個函式方便我們後面採用劃窗方式,多次提取不同時間段的使用者行為累計特徵。
actions = actions[(actions.time >= start_date) & (actions.time < end_date)]
actions = actions[['user_id', 'sku_id', 'type']]
df = pd.get_dummies(actions['type'], prefix='%s-%s-action' % (start_date, end_date))
actions = pd.concat([actions, df], axis=1) # type: pd.DataFrame
actions = actions.groupby(['user_id', 'sku_id'], as_index=False).sum()
提取按時間衰減的累計行為特徵,使用匿名函式lambda提取出每條行為資料發生時間與截止日期的相隔天數,然後對相隔天數取反,作為對數函式的指數。這樣若發生互動行為的資料離我們要預測的日期離得越遠,那麼它對我們的預測所佔的權重指數就越小。
actions = actions[(actions.time >= start_date) & (actions.time < end_date)]
df = pd.get_dummies(actions['type'], prefix='action')
actions = pd.concat([actions, df], axis=1) # type: pd.DataFrame
#近期行為按時間衰減
actions['weights'] = actions['time'].map(lambda x: datetime.strptime(end_date, '%Y-%m-%d') - datetime.strptime(x, '%Y-%m-%d %H:%M:%S'))
#actions['weights'] = time.strptime(end_date, '%Y-%m-%d') - actions['datetime']
actions['weights'] = actions['weights'].map(lambda x: math.exp(-x.days))
actions['action_1'] = actions['action_1'] * actions['weights']
actions['action_2'] = actions['action_2'] * actions['weights']
actions['action_3'] = actions['action_3'] * actions['weights']
actions['action_4'] = actions['action_4'] * actions['weights']
actions['action_5'] = actions['action_5'] * actions['weights']
actions['action_6'] = actions['action_6'] * actions['weights']
actions = actions.groupby(['user_id', 'sku_id', 'cate', 'brand'], as_index=False).sum()
通過提取使用者的點選購買轉化率,加入購物車後購買轉化率,以及瀏覽購買轉換率,我們可以更深刻的刻畫該使用者是否我們要尋找的高潛購買使用者物件
actions = get_actions(start_date, end_date)
df = pd.get_dummies(actions['type'], prefix='action')
actions = pd.concat([actions['user_id'], df], axis=1)
actions = actions.groupby(['user_id'], as_index=False).sum()
actions['user_action_1_ratio'] = actions['action_4'] / actions['action_1']
actions['user_action_2_ratio'] = actions['action_4'] / actions['action_2']
actions['user_action_3_ratio'] = actions['action_4'] / actions['action_3']
actions['user_action_5_ratio'] = actions['action_4'] / actions['action_5']
actions['user_action_6_ratio'] = actions['action_4'] / actions['action_6']
我們已經完成特徵工程部分,接下來主要就是從特徵工程生成的許多特徵中選出有用的特徵,然後對模型引數進行調優。
我們使用xgboost模型對使用者模型進行建模,然後由於xgboost是基於樹模型的分類器,那麼在建樹的過程中也就自動完成了對特徵的選擇。
模型調優:
我們使用xgboost自帶的交叉驗證函式,先大約確定最優迭代次數。param = {'eta' : 0.1, 'max_depth': 3, 'seed':27,
'min_child_weight': 1, 'gamma': 0, 'subsample': 0.8, 'colsample_bytree': 0.8,
'scale_pos_weight': 1, 'objective': 'binary:logistic','eval_metric':'auc'}
bst=xgb.cv( param, dtrain, 500,nfold=5,early_stopping_rounds=100)
bst
然後再使用sklearn的格子搜尋,類似以下程式碼,然後我們更改param_grid的值,即可確定每個引數的最優值。
param_test1 = {
'max_depth':[1,3,5,7]
}
gsearch1 = GridSearchCV(estimator = XGBClassifier( learning_rate =0.1, n_estimators=178, max_depth=5,
min_child_weight=1, gamma=0, subsample=0.8, colsample_bytree=0.8,
objective= 'binary:logistic', nthread=4, scale_pos_weight=1, seed=27),
param_grid = param_test1, scoring='roc_auc', n_jobs=4, iid=False, cv=StratifiedKFold(training_data.label,n_folds=5))
gsearch1.fit(dtrain_x,training_data.label)
gsearch1.grid_scores_, gsearch1.best_params_, gsearch1.best_score_
當我們確定好所有超引數時,我們用這些引數作為xgboost模型的引數,然後訓練出我們的預測模型,此時我們可以呼叫xgboost自帶的得分函式檢視各個特徵的重要性,方便我們確定哪些特徵比較重要,哪些特徵被選做分裂節點的次數少。然後我們可以試著重新選擇特徵集,不斷重複上面的調優步驟,直到結果比較符合我們期望。
feature_score = bst.get_score()
feature_score = sorted(feature_score.items(), key=lambda x:x[1],reverse=True)
feature_score
總結思考:
本次比賽最終排名:174 / 4240 比賽收穫:- 完整的完成了一次大資料比賽,對資料探勘整體流程有了更深的理解。
- 對python常見的進行資料處理的函式更加熟悉。
- 對模型調優、特徵選擇的重要性有深刻認識。例如,同樣的特徵資料集,若引數調優得當,能有10%的提升;同時,若特徵選的好,則有20%的提升。
- 若有更多時間,應該可以從特徵組合和模型融合方面入手,繼續改善模型效果。