FFM模型在点击率预估中的应用实践

FFM模型在点击率预估中的应用实践这篇文章,将主要讲述FFM模型在CTR预估中的应用。

大家好,又见面了,我是你们的朋友全栈君。

最近任务重,时间紧,跳票了两个月,真是抱歉。

近期参加了kesci平台上的云脑机器学习训练营,接触到了FFM模型,因此这篇文章,将主要讲述FFM模型在CTR预估中的应用。

看这篇文章之前,如果对FFM模型完全没了解的,建议先看一下FFM的原理介绍:深入FFM原理与实践

FFM(Field-aware Factorization Machine)模型是FM(Factorization Machine)的升级版模型,美团点评技术团队在站内CTR/CVR的预估上使用了该模型,取得了不错的效果。

数据集是一个外国电商网站的用户浏览记录,大家可以在kesci平台上下载,也可以注册kesci账号直接在平台上运行:KASANDR Data Set

##导入需要用到的库
import pandas as pd
import numpy as np
import datetime
from sklearn import preprocessing
import ffm
from sklearn.metrics import roc_auc_score
from sklearn import preprocessing
%matplotlib inline
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')

画图分析时间特征:

###画图分析时间特征
data = pd.read_csv('../input/kasandr/de_cb_camp01/sample_train.csv',sep='\t',index_col='Unnamed: 0')
pltdata = data.copy()
pltdata['date'] = pltdata['utcdate'].apply(lambda x: x.split(' ')[0])
pltdata['hour'] = pltdata['utcdate'].apply(lambda x: x.split(' ')[1].split(':')[0])
plt.figure(figsize = [12,6])
pltdata.groupby('date')['rating'].mean().plot()
plt.show()
plt.figure(figsize = [12,6])
pltdata.groupby('hour')['rating'].mean().plot()
plt.show()

结果显示:

FFM模型在点击率预估中的应用实践

FFM模型在点击率预估中的应用实践

可以看到2016-06-14号的数据明显异常,所以在应用模型时直接弃用了这一天的数据;另外时间段上可以看到工作时间和非工作时间的浏览数是明显不同的。

添加时间特征:

##添加时间特征,即行为发生在每天的哪个时间段,以及根据是否工作时间创建特征(划分阈值由上图2得到)
data['hour'] = data['utcdate'].apply(lambda x: x.split(' ')[1].split(':')[0]).astype(int)
data['off_work'] = data['hour'].apply(lambda x: 1 if x >= 17 or x <= 1 else 0)

接下来,添加用户及商品相关的特征:

##这部分添加的特征有用户历史浏览数,用户历史浏览的商品数,用户历史浏览的种类数,offerid历史被浏览次数,offerid历史被点击次数
##文中出现%i变量的原因是:我原来是想对时间滑窗构建特征,比如在前1,3,5,7天内用户即商品的相关统计特征,但由于平台资源限制,这里只做了前7天的相关特征;
print ('start adding features...')
def add_feat(data,date1,date2,date3,date4,i):
    train_data = data[(data['utcdate'] >= date1) & (data['utcdate'] <= date2)]
    train_label = data[(data['utcdate'] >= date3) & (data['utcdate'] <= date4)]
    
    ###userid相关特征
    label_uid = train_label['userid'].unique()
    train_userid = train_data[train_data['userid'].isin(label_uid)]
    
    browse = train_userid.groupby(['userid'])['offerid'].count().reset_index()    ##用户历史浏览数
    browse.columns = ['userid','browse%d'%i]
    train_label = train_label.merge(browse,on=['userid'],how='left')
    
    
    subdata1 = train_userid.drop_duplicates(['userid','merchant'])             ##用户历史浏览的商品数
    merchant = subdata1.groupby(['userid'])['merchant'].count().reset_index()
    merchant.columns = ['userid','merchant%d'%i]
    train_label = train_label.merge(merchant,on=['userid'],how='left')
    
    
    subdata2 = train_userid.drop_duplicates(['userid','category'])             ##用户历史浏览的种类数
    category = subdata2.groupby(['userid'])['category'].count().reset_index()
    category.columns = ['userid','category%d'%i]
    train_label = train_label.merge(category,on=['userid'],how='left')
    
    
    ###offerid相关特征
    label_offerid = train_label['offerid'].unique()
    train_offer = train_data[train_data['offerid'].isin(label_offerid)]
    
    offerid_num = train_offer.groupby(['offerid'])['userid'].count().reset_index()  ##offerid历史被浏览次数
    offerid_num.columns = ['offerid','offerid_num%d'%i]
    train_label = train_label.merge(offerid_num,on=['offerid'],how='left')
    
    
    offer_rating = train_offer.groupby(['offerid'])['rating'].sum().reset_index()  ##offerid历史被点击次数
    offer_rating.columns = ['offerid','offer_rating%d'%i]
    train_label = train_label.merge(offer_rating,on=['offerid'],how='left')
    
    
    train_label[['browse%d'%i,'merchant%d'%i,'category%d'%i,'offerid_num%d'%i,'offer_rating%d'%i]] = train_label[['browse%d'%i,'merchant%d'%i,'category%d'%i,'offerid_num%d'%i,'offer_rating%d'%i]].fillna(0)
    return train_label

##滑窗构造样本
train1 = add_feat(data,'2016-06-01','2016-06-07','2016-06-08','2016-06-09',i=0)
train2 = add_feat(data,'2016-06-02','2016-06-08','2016-06-09','2016-06-10',i=0)
train3 = add_feat(data,'2016-06-03','2016-06-09','2016-06-10','2016-06-11',i=0)
train4 = add_feat(data,'2016-06-04','2016-06-10','2016-06-11','2016-06-12',i=0)
test = add_feat(data,'2016-06-05','2016-06-11','2016-06-12','2016-06-13',i=0)    
train = pd.concat([train1,train2,train3,train4],axis=0)  
len_train = train.shape[0]
all_data = pd.concat([train,test],axis=0)  
print ('finish adding features...')

特征工程做完之后,就是对数据格式的转换(转换成FFM模型需要的格式:“field_id:feat_id:value”),以及使用模型进行训练了:

###将数据格式转换为FFM模型需要的格式,分别对类别型和数值型数据做处理,数值型数据必须做归一化处理,而且处理时训练集和测试集必须在同个
###变换空间内,我一开始是对训练集和测试集分别归一化后,导致结果非常差;修正后效果提升很多。
def pd_to_ffm(df):
    field_dict = dict(zip(df.columns,range(len(df.columns))))
    ffm = pd.DataFrame()
    idx = 0
    t = df.dtypes.to_dict()  
    for col in df.columns:
        col_type = t[col]
        if col_type.kind ==  'O':  ##category数据
            col_value = df[col].unique()
            feat_dict = dict(zip(col_value,range(idx,idx+len(col_value))))
            se = df[col].apply(lambda x: (field_dict[col],feat_dict[x],1))
            ffm = pd.concat([ffm,se],axis=1)
            idx += len(col_value)
        elif col_type.kind == 'i':  ##数值型数据
            min_max_scaler = preprocessing.MinMaxScaler()   ##归一化处理
            df[col] = min_max_scaler.fit_transform(df[col])
            
            si = df[col].apply(lambda x: (field_dict[col],field_dict[col],x))
            ffm = pd.concat([ffm,si],axis=1)
    return ffm

print ('starting FFM...')
train_y = train['rating'].values
test_y = test['rating'].values
all_example = all_data[all_data.columns.difference(['countrycode','utcdate','rating'])]
def data_str_int(df):
    for col in df.columns:
        #if col in ['userid','offerid','category','merchant','date','hour','off_work']:
        if col in ['userid','offerid','category','merchant']:
            df[col] = df[col].map(str)
        else:
            df[col] = df[col].map(int)
    return df


all_example = data_str_int(all_example)  
all_X = pd_to_ffm(all_example)
train_X = all_X[:len_train]
test_X = all_X[len_train:]

ffm_train_data = ffm.FFMData(train_X.values, train_y)
ffm_test_data = ffm.FFMData(test_X.values, test_y)

# train the model for 20 iterations

n_iter = 20

model = ffm.FFM(eta=0.1, lam=0.0001, k=4)
model.init_model(ffm_train_data)

for i in range(n_iter):
    print('iteration %d, ' % i, end='')
    model.iteration(ffm_train_data)

    train_y_pred = model.predict(ffm_train_data)
    train_auc = roc_auc_score(train_y, train_y_pred)
    test_y_pred = model.predict(ffm_test_data)
    test_auc = roc_auc_score(test_y, test_y_pred)
    print('train auc %.4f' % train_auc,'test auc %.4f' % test_auc) 

模型训练及预测结果:

starting FFM...
iteration 0, train auc 0.9363 test auc 0.7920
iteration 1, train auc 0.9761 test auc 0.7962
iteration 2, train auc 0.9901 test auc 0.7977
iteration 3, train auc 0.9961 test auc 0.7980
iteration 4, train auc 0.9984 test auc 0.7994
iteration 5, train auc 0.9993 test auc 0.8005
iteration 6, train auc 0.9996 test auc 0.8011
iteration 7, train auc 0.9998 test auc 0.8012
iteration 8, train auc 0.9999 test auc 0.8014
iteration 9, train auc 0.9999 test auc 0.8016
iteration 10, train auc 0.9999 test auc 0.8016
iteration 11, train auc 1.0000 test auc 0.8016
iteration 12, train auc 1.0000 test auc 0.8015
iteration 13, train auc 1.0000 test auc 0.8012
iteration 14, train auc 1.0000 test auc 0.8012
iteration 15, train auc 1.0000 test auc 0.8012
iteration 16, train auc 1.0000 test auc 0.8011
iteration 17, train auc 1.0000 test auc 0.8009
iteration 18, train auc 1.0000 test auc 0.8007
iteration 19, train auc 1.0000 test auc 0.8005

可以看到,迭代10次左右就开始收敛了。

划重点:数值型特征必须先进行归一化,且必须保证训练集和测试集在同个变换空间内。

本文只是介绍对FFM模型的简单应用,在特征工程上没有特别的花费功夫,适合初学者了解这个模型的使用。

最后,安利一个同学的方案,做的很详细:云脑-电商推荐系统(特征工程部分)

参考:

深入FFM原理与实践 

点击率预估算法:FM与FFM

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/144981.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • Python线程指南[通俗易懂]

    Python线程指南[通俗易懂]本文介绍了Python对于线程的支持,包括“学会”多线程编程需要掌握的基础以及Python两个线程标准库的完整介绍及使用示例。注意:本文基于Python2.4完成;如果看到不明白的词汇请记得百度谷

  • 计算机组成原理期末复习90分以上选择填空大题总考点

    同学,你好!若觉得有用请点赞或关注~以后会发布更多有用的内容。2019-07-24更新:插入了一张“高分喷雾”。20…

  • AWS EC2文件上传[通俗易懂]

    AWS EC2文件上传[通俗易懂]AWSEC2申请配置、文件上传、nginx安装部署、tomcat安装和项目部署、域名绑定AWSEC2服务器申请配置我这里是参考简书的一篇博客:利用AWS的EC2来搭建属于自己的VPN服务器(MAC平台)在步骤4搭建vpn服务器之前都是可以通用的。非常的详细。文件上传经过以上配置之后应该了解到,使用ssh命令访问aws服务器是会用到其提供的秘钥文件的(我这里是serverK…

  • MySQL索引的使用实例

    MySQL索引的使用实例前言这是我听老师讲课做的笔记,考试要看的。这是视频地址作者:陈运智关注我的csdn博客,更多Linux笔记知识还在更新本人只在csdn写博客配套这篇文章观看效果更佳MySQL索引的使用实例一.慢查询日志二.查询分析器——explain三.索引的基本使用四.复合索引五.覆盖索引一.慢查询日志//查看是否开启慢查询日志mysql>showvariableslike’%slow%’;//临时开启慢查询日志mysql>setglobalslow_q

  • GitHub 热榜:轻量级无 Agent 的自动化运维平台!「建议收藏」

    GitHub 热榜:轻量级无 Agent 的自动化运维平台!「建议收藏」大家好,我是JackTian。作为一名运维工程师,大家都知道。早在几年前,偏传统运维,以cacti、nagios为主流,到后来的zabbix、Prometheus、Open-Falcon等,也是现在大多数企业用的偏多的运维监控平台。甚至有些企业,都是自主研发。不管是自主研发还是用开源的,其最终目的都是为了提高日常运维工作效率。那么,今天杰哥给大家推荐一款GitHub热榜开源运维平台——spug。这款开源运维平台是:面向中小型企业设计的轻量级无Agent的自动化运维平台,其主要功能

  • 学习java的好书及视频推荐

    学习java的好书及视频推荐转载来自:点击打开链接要想在java领域成为大牛,除了不断进行项目实战以外,还要不断的进行进修和学习,以下将本人学习java多年使用的好书和一些好的视频推荐给大家,这些书和视频都是本人在网络找了很久,后来又经过实践证明的好书和视频。希望对大家学习java有帮助首先,是书的推荐:1学习java,java基础,1.0 入门:HeadFirstJava(

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号