从框架的角度看,推荐系统基本可以分为数据层、触发层、融合过滤层和排序层。数据层包括数据生成和数据存储,主要是利用各种数据处理工具对原始日志进行清洗,处理成格式化的数据,落地到不同类型的存储系统中,供下游的算法和模型使用。候选集触发层主要是从用户的历史行为、实时行为、地理位置等角度利用各种触发策略产生推荐的候选集(召回
)。候选集融合和过滤层有两个功能,一是对出发层产生的不同候选集进行融合,提高推荐策略的覆盖度和精度;另外还要承担一定的过滤职责,从产品、运营的角度确定一些人工规则,过滤掉不符合条件的item。排序层主要是利用机器学习的模型对触发层筛选出来的候选集进行重排序。
首先将客户上报过来的数据进行数据清洗,检查数据的一致性,处理无效值和缺失值等,去除脏数据,处理成格式化数据存储到不同类型的存储系统中。对于用户行为日志和推荐日志由于随时间积累会越来越大,一般存储在分布式文件系统(HDFS),即Hive表中,当需要的时候可以下载到本地进行离线分析
。对于物品信息一般存储在MySQL中
,但是对于业务数据,越来越多的客户导致物品信息表(item_info)越来越大,所以同时也会保存在Hive表和HBase中
,Hive可以方便离线分析时操作,但实时程序读取的时候Hive表的实时性较差,所以同时也会写一份放在HBase中供实时程序读取。对于各个程序模块生成的结果,有进程同步关系的程序一般会使用Redis作为缓冲存储,生产者会把信息写到redis中供消费者使用。候选集生成是从用户的历史行为、实时行为、利用各种策略和算法生成推荐的候选集。同时点击反馈会根据用户的实时操作对候选集进行实时的调整,对于部分新用户和历史行为不太丰富的用户,由于候选集太小,需要一些替补策略进行补充。候选集融合规则过滤主要有两个功能,一是对生成的候选集进行融合,提高推荐策略的覆盖度和精度;另外还需根据产品、运营的角度确定一些人为的规则,过滤掉不符合条件的item,重排序主要是利用机器学习的模型对融合后的候选集进行重排序。
同时,对与候选集触发和重排序两层而言,为了效果迭代是需要频繁修改的两层,因此需要支持ABtest。为了支持高效率的迭代,我们对候选集触发和重排序两层进行了解耦,这两层的结果是正交的,因此可以分别进行对比试验,不会相互影响。同时在每一层的内部,我们会根据用户将流量划分为多份,支持多个策略同时在线对比。
排序模型分为非线性模型和线性模型,非线性模型能较好的捕捉特征中的非线性关系,但训练和预测的代价相对线性模型要高一些,这也导致了非线性模型的更新周期相对要长。相较而言,线性模型对特征的处理要求比较高(LR对特征要求较高),需要凭借领域知识和经验人工对特征做一些先期处理,但因为线性模型简单,在训练和预测时效率较高。因此在更新周期上也可以做的更短,还可以结合业务做一些在线学习的尝试。
关键特征
求解发生概率的最大值
目的:求解发生概率最大值
步骤:1.写出似然函数,2.对似然函数求log对数,3.对似然函数求导数,4.得到最优解
机器学习三要素:
求解极大似然目标函数
极大似然估计的例子图解
# 给定初始值 , xOld记录上一步的x值 , xNew下一步迭代的x值
xOld = 0
xNew = 6
# 步长
epa = 0.01
# 可接受误差
precision = 0.00001
# 定义原函数
def f(x):
return x ** 4 - 3 * x ** 3 + 2
# 定义导函数
def f_prime(x):
return 4 * x ** 3 - 9 * x ** 2
# 主函数
if __name__ == '__main__':
# 循环直到函数值之差满足最小误差
while abs(f(xNew) - f(xOld)) > precision:
xOld = xNew
xNew = xOld - epa * f_prime(xOld)
# 输出极小值点
print("最小值点为 : ", xNew, "最小值为 : ", f(xNew))
# 最小值点为 : 2.2489469258218673 最小值为 : -6.542957528732806
# 定义原函数
def f(x):
return x ** 3.0 - 2.0
# 定义导函数
def df(x):
return 3.0 * x ** 2.0
# 定义迭代值
def g(x):
return x - f(x) / df(x)
# 定义初始值
x = 1.0
# 定义误差
r = 1.0
# 循环100次
for i in range(100):
# 迭代值赋值
x1 = g(x);
# 误差赋值
r = abs(x1 - x)
# 可接受误差
if r < 1e-10:
print("step : % d " % i)
break
# 更新下一步起始位置
x = x1
# 显示迭代步骤
print("step : %d , x = %f" % (i, x))
print("remaind error = %f" % r)
print("x = %f" % x)
print("check f(x) = %f , the result is %r" % (f(x), f(x) == 0))
# step : 0 , x = 1.333333
# step : 1 , x = 1.263889
# step : 2 , x = 1.259933
# step : 3 , x = 1.259921
# step : 4 , x = 1.259921
# step : 5
# remaind error = 0.000000
# x = 1.259921
# check f(x) = 0.000000 , the result is True
#导入数据
from sklearn.datasets import load_iris
iris=load_iris()
#数据的基础属性信息
print(iris.data)
print(iris.target)
#建立模型
X=iris.data
y=iris.target
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.2,random_state=22)
from sklearn.linear_model import LogisticRegression
lr=LogisticRegression(solver='newton-cg')
lr.fit(X_train,y_train)
#模型检验
print("lr model in trainset score:",lr.score(X_train,y_train))
print("lr model in testset score:",lr.score(X_test,y_test))
# lr model in trainset score: 0.9416666666666667
# lr model in testset score: 0.9666666666666667
GBDT+LR实战
GBDT形成结果通过OneHot编码形成没有线性关系的独热编码
再通过LR输出0-1之间的概率值
sklearn中https://scikit-learn.org/stable/auto_examples/ensemble/plot_feature_transformation.html
测试代码
import numpy as np
np.random.seed(10)
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import (RandomTreesEmbedding, RandomForestClassifier,
GradientBoostingClassifier)
from sklearn.preprocessing import OneHotEncoder
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_curve
from sklearn.pipeline import make_pipeline
n_estimator = 10
X, y = make_classification(n_samples=80000)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5)
X_train, X_train_lr, y_train, y_train_lr = train_test_split(
X_train, y_train, test_size=0.5)
# Supervised transformation based on gradient boosted trees
grd = GradientBoostingClassifier(n_estimators=n_estimator)
grd_enc = OneHotEncoder()
grd_lm = LogisticRegression(solver='lbfgs', max_iter=1000)
grd.fit(X_train, y_train)
grd_enc.fit(grd.apply(X_train)[:, :, 0])
grd_lm.fit(grd_enc.transform(grd.apply(X_train_lr)[:, :, 0]), y_train_lr)
y_pred_grd_lm = grd_lm.predict_proba(grd_enc.transform(grd.apply(X_test)[:, :, 0]))[:, 1]
fpr_grd_lm, tpr_grd_lm, _ = roc_curve(y_test, y_pred_grd_lm)
# # The gradient boosted model by itself
# y_pred_grd = grd.predict_proba(X_test)[:, 1]
# fpr_grd, tpr_grd, _ = roc_curve(y_test, y_pred_grd)
plt.plot(fpr_grd_lm,tpr_grd_lm)
plt.show()
**每个样本都经过整体的每棵树的决定,并以每棵树的一片叶子结束。**通过将这些叶的特征值设置为1并将其他特征值设置为0来对样本进行编码。
然后,所得到的transformer学习数据的监督的,稀疏的,高维的分类嵌入。
http://scikit-learn.org/stable/modules/generated/sklearn.metrics.auc.html#sklearn.metrics.auc
http://scikit-learn.org/stable/modules/generated/sklearn.metrics.roc_auc_score.html#sklearn.metrics.roc_auc_score
http://scikit-learn.org/stable/auto_examples/ensemble/plot_feature_transformation.html
算法背景 : acebook发表了一篇介绍将GBDT+LR模型用于其广告推荐系统的论文
LR模型有以下特点:
GBDT作为一种常用的树模型,可天然地对原始特征进行特征划分、特征组合和特征选择,并得到高阶特征属性和非线性映射。从而可将GBDT模型抽象为一个特征处理器,通过GBDT分析原始特征获取到更利于LR分析的新特征。这也正是GBDT+LR模型的核心思想——利用GBDT构造的新特征来训练LR模型。
算法原理及实现
算法流程& 代码简单实现
From sklearn.model import train_test_split
X_gbdt,X_lr,y_gbdt,y_lr= train_test_split(X,y,test_size=0.5)
from sklearn.ensemble import GradientBoostingClassifier
gbdt = GradientBoostingClassifier()
gbdt.fit(X_gbdt,y_gbdt)
leaves = gbdt.apply(X_lr)[:,:,0]
from sklearn.preprocessing import OneHotEncoder
featutes_trans =OneHotEncoder.fit_transform(leaves)
from sklearn.linear_model import LogisticRegression
lr= LogisticRegression()
lr.fit(features_trans,y_lr)
lr.predict(features_trans)
lr.predict_proba(features_trans)[:,1]
调参方法简述 :
构建了模型框架后,模型中的函数参数调整也是必不可少的。对模型参数的适当调整,往往可以有效提升模型的效果。
由于GBDT+LR模型无法整体使用GridSearchCV函数,所以调参时
使用sklearn.cross_validation中的StratifiedKFold方法,将数据集进行k折交叉切分,然后以auc值为模型评估指标,对混合模型进行调参。
调参时的重点为GradientBoostingClassifier函数,可用如下图所示的调参顺序进行调参。
其中,n_estimators和learning_rate应该联合调参。
模型效果展示
我们分别使用LR模型和GBDT+LR模型对样本数据集进行学习,通过模型所得的auc值和ks值,来评估和比较模型的效果。
简介
CTR估计也就是广告点击率预估,计算广告训练与平滑思想说明了是用LR算法对于预测的有效性。LR(Logistic Regression)是广义线性模型,与传统线性模型相比,**LR通过Logit变换将函数值映射到0~1区间,映射后的函数就是CTR的预估值。**LR模型十分适合并行化,因此对于大数据的训练十分有效。但是对于线性模型而言,学习能力是有限的,因此需要大量的特征工程预先分析出有效的特征或者是特征组合,从而去间接的增强LR的非线性学习能力。
特征组合,是通过特征的一些线性叠加或者非线性叠加得到一个新的特征,可以有效的提高分类效果。常见的特征组合方式有笛卡尔积方式。为了降低人工组合特征的工作量,FaceBook提出了一个自动特征提取的方式GBDT+LR。
GBDT是梯度提升决策树,首先会构造一个决策树,首先在已有的模型和实际样本输出的残差上再构造一颗决策树,不断地进行迭代。每一次迭代都会产生一个增益较大的分类特征,因此GBDT树有多少个叶子节点,得到的特征空间就有多大,并将该特征作为LR模型的输入。
核心问题
(1)建树采用ensemble决策树?
一棵树的区分性是具有一定的限制的,但是多棵树可以获取多个具有区分度的特征组合,而且GBDT的每一棵树都会学习前面的树的不足。
(2)建树算法为什么采用GBDT而不是RF?
对于GBDT而言,前面的树,特征分裂主要体现在对多数样本的具有区分度的特征;后面的树,主要体现的是经过前面n棵树,残差依然比较大的少数样本。优先选用在整体上具有区分度的特征,再选用针对少数样本有区分度的特征。
代码实现
import pandas as pd
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import GradientBoostingClassifier,RandomForestClassifier
import xgboost as xgb
from xgboost.sklearn import XGBClassifier
from sklearn.preprocessing import OneHotEncoder
from sklearn.metrics import roc_curve,roc_auc_score,confusion_matrix,classification_report
#1.随机生成数据集
np.random.seed(10)
X,y = make_classification(n_samples=1000,n_features=30)
#2.切分数据
X_train,X_test,y_train,y_test = train_test_split(X,y,random_state=223,test_size=0.5)
X_train,X_train_lr,y_train,y_train_lr = train_test_split(X_train,y_train,random_state=223,test_size=0.2)
#4.网格搜索
#5.训练模型
#5.1 RandomForest + LogisticRegression
def RF_LR():
# Rf: 训练模型
rf = RandomForestClassifier(n_estimators=100, max_depth=4)#n_estimators:树的数目
rf.fit(X_train, y_train)
rf_result = rf.apply(X_train)#apply得到叶子节点的索引
#onehot编码
ohe = OneHotEncoder()
ohe.fit(rf_result)
# 利用RF模型获取以X_train_lr为输入的叶子节点的索引值, 并对其进行one-hot编码
X_train_leaf_ohe = ohe.transform(rf.apply(X_train_lr))
#LR: 训练模型
lr = LogisticRegression(C=0.1, penalty="l2",multi_class='auto')
lr.fit(X_train_leaf_ohe, y_train_lr)
#LR: 预测
y_pred = lr.predict_proba(ohe.transform(rf.apply(X_test)))[:, 1]
#模型评估
fpr, tpr, _ = roc_curve(y_test, y_pred)
auc = roc_auc_score(y_test, y_pred)
print("RandomForest + LogisticRegression :\n", auc)
return fpr,tpr
#5.2 XGBoost + LogisticRegression
def XGB_LR():
# XGBoost: 训练模型
# nthread: 并行度
# n_estimators: Number of boosted trees to fit 要拟合树的数目
# colsample_bytree:Subsample ratio of columns when constructing each tree
XGB = xgb.XGBClassifier(nthread=4, learning_rate=0.08, n_estimators=100,
colsample_bytree=0.5)
XGB.fit(X_train, y_train)
XGB_result = XGB.apply(X_train)
# onehot编码
ohe = OneHotEncoder()
ohe.fit(XGB_result)
X_train__ohe = ohe.transform(XGB.apply(X_train_lr))
# X_train__ohe = ohe.transform(rf_result)
# LR: 训练模型
lr = LogisticRegression(C=0.1, penalty="l2",multi_class='auto')
lr.fit(X_train__ohe, y_train_lr)
# LR: 预测
# y_pred的shape = [n_samples, n_classes]
y_pred = lr.predict_proba(ohe.transform(XGB.apply(X_test)))[:, 1]
# 模型评估
fpr, tpr, _ = roc_curve(y_test, y_pred)
auc = roc_auc_score(y_test, y_pred)
print("XGBoost + LogisticRegression :\n", auc)
return fpr,tpr
#5.3 GradientBoostingClassifier+LR
def GBDT_LR():
# GBDT: 训练模型
# n_estimators:迭代次数
gbdt = GradientBoostingClassifier(n_estimators=100)
gbdt.fit(X_train, y_train)
gbdt_result = gbdt.apply(X_train) # 3维:shape (n_samples, n_estimators, n_classes)
# onehot编码
ohe = OneHotEncoder()
ohe.fit(gbdt_result[:, :, 0]) # gbdt_result[:,:,0]获取GBDT
# print(ohe.fit(gbdt_result[:,:,0]))
X_train__ohe = ohe.transform(gbdt.apply(X_train_lr)[:, :, 0])
# LR: 训练模型
lr = LogisticRegression(C=0.1, penalty="l2",multi_class='auto')
lr.fit(X_train__ohe, y_train_lr)
# LR: 预测
# y_pred的shape = [n_samples, n_classes]
y_pred = lr.predict_proba(ohe.transform(gbdt.apply(X_test)[:, :, 0]))[:, 1]
# 模型评估
fpr, tpr, _ = roc_curve(y_test, y_pred)
auc = roc_auc_score(y_test, y_pred)
print("GBDT + LogisticRegression :\n", auc)
return fpr,tpr
#5.4 LR
def LR():
# LR: 训练模型
lr = LogisticRegression(C=0.1, penalty="l2",multi_class='auto')
lr.fit(X_train, y_train)
# LR: 预测
# y_pred的shape = [n_samples, n_classes]
y_pred = lr.predict_proba(X_test)[:, 1]
# 模型评估
fpr, tpr, _ = roc_curve(y_test, y_pred)
auc = roc_auc_score(y_test, y_pred)
print("LogisticRegression :\n", auc)
return fpr, tpr
#5.4 XGBoost
def XGBoost():
# XGB: 训练模型
XGB = xgb.XGBClassifier(nthread=4, learning_rate=0.08, n_estimators=100,
colsample_bytree=0.5)
XGB.fit(X_train, y_train)
# XGB: 预测
y_pred = XGB.predict_proba(X_test)[:, 1]
# 模型评估
fpr, tpr, _ = roc_curve(y_test, y_pred)
auc = roc_auc_score(y_test, y_pred)
print("XGBoost :\n", auc)
return fpr, tpr
# 主函数
if __name__ == '__main__':
fpr_rf_lr,tpr_rf_lr = RF_LR()
fpr_xgb_lr,tpr_xgb_lr = XGB_LR()
fpr_gbdt_lr, tpr_gbdt_lr = GBDT_LR()
fpr_lr, tpr_lr = LR()
fpr_xgb, tpr_xgb = XGBoost()
# plt.figure(1)
plt.xlim(0,0.2)
plt.ylim(0.8,1)
plt.plot([0, 1], [0, 1], "k--")
plt.plot(fpr_rf_lr, tpr_rf_lr, label="RF+LR")
plt.plot(fpr_xgb_lr, tpr_xgb_lr, label="XGB+LR")
plt.plot(fpr_gbdt_lr, tpr_gbdt_lr, label="GBDT+LR")
plt.plot(fpr_lr, tpr_lr, label="LR")
plt.plot(fpr_xgb, tpr_xgb, label="XGBoost")
plt.xlabel("False positive rate")
plt.ylabel("True positive rate")
plt.legend(loc="best")
plt.show()
# # gbc = GradientBoostingClassifier(loss='exponential',criterion='friedman_mse',presort='auto')
# gbc = GradientBoostingClassifier(loss='deviance',criterion='friedman_mse',presort='auto')
# gbc.fit(X_train,y_train)
#
#
#6.测试数据
# y_pred = gbc.predict(X_test)
#
#7. 模型检测
# print("classification report is: \n", classification_report(y_test,y_pred))
https://mp.weixin.qq.com/s?__biz=MzI1ODM5MTI4Nw%3D%3D&chksm=ea09a6badd7e2fac05f9886746bd717bc7e53503906728337b72cd1b95cd2faa4e186e79b9cd&idx=1&mid=2247486242&scene=21&sn=3723bc28c36e0c779bb20aa3f1c92b23
https://blog.csdn.net/lilyth_lilyth/article/details/48032119
https://blog.csdn.net/asdfghjkl1993/article/details/78606268
https://blog.csdn.net/TwT520Ly/article/details/79769705
#1.导入数据并进行简单的数据探索
import os
data_path = os.path.join(".", "train_small.csv")
import pandas as pd
ctr_data1 = pd.read_csv(data_path)
#2.数据的简单描述信息
print(ctr_data1.shape)
# print ctr_data.head()
# print ctr_data.describe()
print (ctr_data1.columns)
print ("="*100)
training_Set=ctr_data1.drop(['id','site_id', 'app_id', 'device_id', 'device_ip', 'site_domain',
'site_category', 'app_domain', 'app_category', 'device_model'], axis=1)
ctr_data=training_Set.values #numpy--ndarry
#2.对数据进行处理和分析
from sklearn.model_selection import train_test_split
X=ctr_data[:,1:]
print (X.shape)
y=ctr_data[:,0]
print (y.shape)
X_train, X_test, y_train, y_test=train_test_split(X,y,test_size=0.22,random_state=33)
print (X_train.shape)
print (y_train.shape)
# #3.引入机器学习算法
from sklearn.linear_model import LogisticRegression
# lr=LogisticRegression()
# 0 0.83 1.00 0.91 18240
# 1 0.00 0.00 0.00 3760
#
# avg / total 0.69 0.83 0.75 22000
lr=LogisticRegression(C=0.1, penalty= 'l1')
# precision recall f1-score support
#
# 0 0.83 1.00 0.91 18240
# 1 0.40 0.00 0.00 3760
#
# avg / total 0.76 0.83 0.75 22000
lr.fit(X_train,y_train)
# #4.模型预测
y_pred=lr.predict(X_test)
print (y_pred)
# # #5.模型校验
print( lr.score(X_train,y_train))
print (lr.score(X_test,y_test))
from sklearn.metrics import confusion_matrix
print( confusion_matrix(y_test,y_pred))
from sklearn.metrics import classification_report
print( classification_report(y_test,y_pred))
# #6.保存模型
from sklearn.externals import joblib
joblib.dump(lr,filename="Ctr_Predict.pkl")
# #8.按照要求写入对应的csv文件
import numpy as np
import pandas as pd
ctr_data2=pd.read_csv("test.csv")
ctr_data3=ctr_data2.drop(['click','site_id', 'app_id', 'device_id', 'device_ip', 'site_domain',
'site_category', 'app_domain', 'app_category', 'device_model'], axis=1)
print( ctr_data3)
ids=ctr_data3.values[0:,0]
y_pred_test=lr.predict(ctr_data3.values[0:,1:])
# # # print ids
submit=np.concatenate((ids.reshape(len(ids),1),y_pred_test.reshape(len(y_pred_test),1)),axis=1)
df=pd.DataFrame(submit)
df.to_csv("submit.csv", header=['id', 'click'], index=False)
区别 :
CTR预估数据特点:
LR,FTRL。线性模型有个致命的缺点:无法提取高阶的组合特征(线性y=w0+w1+w2等)。
LR最大的缺点就是无法组合特征,依赖于人工的特征组合,这也直接使得它表达能力受限,基本上只能处理线性可分或近似线性可分的问题
。
FM模型
FM通过隐向量latent vector做内积
来表示组合特征,从理论上解决了低阶和高阶组合特征提取的问题。但是实际应用中受限于计算复杂度,一般也就只考虑到2阶交叉特征。
后面有进行了改进 , 提出了FFM , 增加了Field的概念
CNN模型的缺点是:偏向于学习相邻特征的组合特征。 RNN模型的缺点是:比较适用于有序列(时序)关系的数据。
FNN : 先使用预先训练好的FM,得到隐向量,然后作为DNN的输入来训练模型。缺点在于:受限于FM预训练的效果。
随后提出了PNN,PNN为了捕获高阶组合特征,在embedding layer和first hidden layer之间增加了一个product layer
。根据product layer使用内积、外积、混合
分别衍生出IPNN, OPNN, PNN
三种类型。
无论是FNN还是PNN , 都避免不了 : 对于低阶的组合特征,学习到的比较少。而前面我们说过,低阶特征对于CTR也是非常重要的。
为了同时学习低阶和高阶组合特征,提出了Wide&Deep模型。它混合了一个线性模型(Wide part)和Deep模型(Deep part)。这两部分模型需要不同的输入,而Wide part部分的输入,依旧依赖人工特征工程。
这些模型普遍都存在两个问题:
DeepFM在Wide&Deep的基础上进行改进,成功解决了这两个问题,并做了一些改进,其优势/优点如下:
FNN使用预训练的FM来初始化DNN,然后只有Deep部分,不能学习低阶组合特征。
FNN缺点 :
PNN:为了捕获高阶特征。PNN在第一个隐藏层和embedding层之间,增加了一个product layer。
PNN缺点:
Wide & Deep设计的初衷是想同时学习低阶和高阶组合特征,但是wide部分需要领域知识进行特征工程。
Wide&Deep缺点 : 需要特征工程提取低阶组合特征
DeepFM优点 :
上述东西太多太杂 , 记住最核心的 :
超参数建议
超参数 | 建议 | 备注 |
---|---|---|
激活函数 | 1.IPNN使用tanh ; 2,其余使用ReLU | |
学习方法 | Adam | |
Dropout | 0.6~0.9 | |
隐藏层数量 | 3~5 , 根据实际数据大小调整 | |
网络形状 | constant , 一共有四种 : 固定、增长、下降、菱形 | PS:constant效果最好 , 就是隐藏层每一层的神经元的数量相同 |