来源:https://blog.csdn.net/qq_23269761/article/details/81366939,如有不妥,请随时联系沟通,谢谢~

0.疯狂安利一个博客

FM的前世今生: 
https://tracholar.github.io/machine-learning/2017/03/10/factorization-machine.html#%E7%BB%BC%E8%BF%B0

1.FM 与 DNN和embedding的关系

先来复习一下FM 
 
 
对FM模型进行求解后,对于每一个特征xi都能够得到对应的隐向量vi,那么这个vi到底是什么呢?

想一想Google提出的word2vec,word2vec是word embedding方法的一种,word embedding的意思就是,给出一个文档,文档就是一个单词序列,比如 “A B A C B F G”, 希望对文档中每个不同的单词都得到一个对应的向量(往往是低维向量)表示。比如,对于这样的“A B A C B F G”的一个序列,也许我们最后能得到:A对应的向量为[0.1 0.6 -0.5],B对应的向量为[-0.2 0.9 0.7] 。

所以结论就是: 
FM算法是一个特征组合以及降维的工具,它能够将原本因为one-hot编码产生的稀疏特征,进行两两组合后还能做一个降维!!降到多少维呢?就是FM中隐因子的个数k

2.FNN

利用FM做预训练实现embedding,再通过DNN进行训练 
 
这样的模型则是考虑了高阶特征,而在最后sigmoid输出时忽略了低阶特征本身。

3.DeepFM

鉴于上述理论,目前新出的很多基于深度学习的CTR模型都从wide、deep(即低阶、高阶)两方面同时进行考虑,进一步提高模型的泛化能力,比如DeepFM。 
参考博客:https://blog.csdn.net/zynash2/article/details/79348540 
 
可以看到,整个模型大体分为两部分:FM和DNN。简单叙述一下模型的流程:借助FNN的思想,利用FM进行embedding,之后的wide和deep模型共享embedding之后的结果。DNN的输入完全和FNN相同(这里不用预训练,直接把embedding层看作一层的NN),而通过一定方式组合后,模型在wide上完全模拟出了FM的效果(至于为什么,论文中没有详细推导,本文会稍后给出推导过程),最后将DNN和FM的结果组合后激活输出。

需要着重强调理解的时模型中关于FM的部分,究竟时如何搭建网络计算2阶特征的 
**划重点:**embedding层对于DNN来说时在提取特征,对于FM来说就是他的2阶特征啊!!!!只不过FM和DNN共享embedding层而已。

4.DeepFM代码解读

先放代码链接: 
https://github.com/ChenglongChen/tensorflow-DeepFM 
数据下载地址: 
https://www.kaggle.com/c/porto-seguro-safe-driver-prediction

4.0 项目目录

 
data:存储训练数据与测试数据 
output/fig:用来存放输出结果和训练曲线 
config:数据获取和特征工程中一些参数的设置 
DataReader:特征工程,获得真正用于训练的特征集合 
main:主程序入口 
mertics:定义了gini指标作为评价指标 
DeepFM:模型定义

4.1 整体过程

推荐一篇此数据集的EDA分析,看过可以对数据集的全貌有所了解: 
https://blog.csdn.net/qq_37195507/article/details/78553581

  • 1._load_data()
  1. def _load_data():

  2. dfTrain = pd.read_csv(config.TRAIN_FILE)

  3. dfTest = pd.read_csv(config.TEST_FILE)

  4. def preprocess(df):

  5. cols = [c for c in df.columns if c not in ["id", "target"]]

  6. df["missing_feat"] = np.sum((df[cols] == -1).values, axis=1)

  7. df["ps_car_13_x_ps_reg_03"] = df["ps_car_13"] * df["ps_reg_03"]

  8. return df

  9. dfTrain = preprocess(dfTrain)

  10. dfTest = preprocess(dfTest)

  11. cols = [c for c in dfTrain.columns if c not in ["id", "target"]]

  12. cols = [c for c in cols if (not c in config.IGNORE_COLS)]

  13. X_train = dfTrain[cols].values

  14. y_train = dfTrain["target"].values

  15. X_test = dfTest[cols].values

  16. ids_test = dfTest["id"].values

  17. cat_features_indices = [i for i,c in enumerate(cols) if c in config.CATEGORICAL_COLS]

  18. return dfTrain, dfTest, X_train, y_train, X_test, ids_test, cat_features_indices

首先读取原始数据文件TRAIN_FILE,TEST_FILE 
preprocess(df)添加了两个特征分别是missing_feat【缺失特征个数】与ps_car_13_x_ps_reg_03【两个特征的乘积】 
返回: 
dfTrain, dfTest :所有特征都存在的Dataframe形式 
X_train, X_test:删掉了IGNORE_COLS的ndarray格式 【X_test后面都没有用到啊】 
y_train: label 
ids_test:测试集的id,ndarray 
cat_features_indices:类别特征的特征indices

  • 利用X_train, y_train 进行了K折均衡交叉验证切分数据集
  • DeepFM参数设置
  • 2._run_base_model_dfm
  1. def _run_base_model_dfm(dfTrain, dfTest, folds, dfm_params):

  2. fd = FeatureDictionary(dfTrain=dfTrain, dfTest=dfTest,

  3. numeric_cols=config.NUMERIC_COLS,

  4. ignore_cols=config.IGNORE_COLS)

  5. data_parser = DataParser(feat_dict=fd)

  6. Xi_train, Xv_train, y_train = data_parser.parse(df=dfTrain, has_label=True)

  7. Xi_test, Xv_test, ids_test = data_parser.parse(df=dfTest)

  8. dfm_params["feature_size"] = fd.feat_dim

  9. dfm_params["field_size"] = len(Xi_train[0])

  10. y_train_meta = np.zeros((dfTrain.shape[0], 1), dtype=float)

  11. y_test_meta = np.zeros((dfTest.shape[0], 1), dtype=float)

  12. _get = lambda x, l: [x[i] for i in l]

  13. gini_results_cv = np.zeros(len(folds), dtype=float)

  14. gini_results_epoch_train = np.zeros((len(folds), dfm_params["epoch"]), dtype=float)

  15. gini_results_epoch_valid = np.zeros((len(folds), dfm_params["epoch"]), dtype=float)

  16. for i, (train_idx, valid_idx) in enumerate(folds):

  17. Xi_train_, Xv_train_, y_train_ = _get(Xi_train, train_idx), _get(Xv_train, train_idx), _get(y_train, train_idx)

  18. Xi_valid_, Xv_valid_, y_valid_ = _get(Xi_train, valid_idx), _get(Xv_train, valid_idx), _get(y_train, valid_idx)

  19. dfm = DeepFM(**dfm_params)

  20. dfm.fit(Xi_train_, Xv_train_, y_train_, Xi_valid_, Xv_valid_, y_valid_)

  21. y_train_meta[valid_idx,0] = dfm.predict(Xi_valid_, Xv_valid_)

  22. y_test_meta[:,0] += dfm.predict(Xi_test, Xv_test)

  23. gini_results_cv[i] = gini_norm(y_valid_, y_train_meta[valid_idx])

  24. gini_results_epoch_train[i] = dfm.train_result

  25. gini_results_epoch_valid[i] = dfm.valid_result

  26. y_test_meta /= float(len(folds))

  27. # save result

  28. if dfm_params["use_fm"] and dfm_params["use_deep"]:

  29. clf_str = "DeepFM"

  30. elif dfm_params["use_fm"]:

  31. clf_str = "FM"

  32. elif dfm_params["use_deep"]:

  33. clf_str = "DNN"

  34. print("%s: %.5f (%.5f)"%(clf_str, gini_results_cv.mean(), gini_results_cv.std()))

  35. filename = "%s_Mean%.5f_Std%.5f.csv"%(clf_str, gini_results_cv.mean(), gini_results_cv.std())

  36. _make_submission(ids_test, y_test_meta, filename)

  37. _plot_fig(gini_results_epoch_train, gini_results_epoch_valid, clf_str)

  38. return y_train_meta, y_test_meta

经过 
DataReader中的FeatureDictionary 
这个对象中有一个self.feat_dict属性,长下面这个样子:

{'missing_feat': 0, 'ps_ind_18_bin': {0: 254, 1: 255}, 'ps_reg_01': 256, 'ps_reg_02': 257, 'ps_reg_03': 258}

DataReader中的DataParser

  1. class DataParser(object):

  2. def __init__(self, feat_dict):

  3. self.feat_dict = feat_dict #这个feat_dict是FeatureDictionary对象实例

  4. def parse(self, infile=None, df=None, has_label=False):

  5. assert not ((infile is None) and (df is None)), "infile or df at least one is set"

  6. assert not ((infile is not None) and (df is not None)), "only one can be set"

  7. if infile is None:

  8. dfi = df.copy()

  9. else:

  10. dfi = pd.read_csv(infile)

  11. if has_label:

  12. y = dfi["target"].values.tolist()

  13. dfi.drop(["id", "target"], axis=1, inplace=True)

  14. else:

  15. ids = dfi["id"].values.tolist()

  16. dfi.drop(["id"], axis=1, inplace=True)

  17. # dfi for feature index

  18. # dfv for feature value which can be either binary (1/0) or float (e.g., 10.24)

  19. dfv = dfi.copy()

  20. for col in dfi.columns:

  21. if col in self.feat_dict.ignore_cols:

  22. dfi.drop(col, axis=1, inplace=True)

  23. dfv.drop(col, axis=1, inplace=True)

  24. continue

  25. if col in self.feat_dict.numeric_cols:

  26. dfi[col] = self.feat_dict.feat_dict[col]

  27. else:

  28. dfi[col] = dfi[col].map(self.feat_dict.feat_dict[col])

  29. dfv[col] = 1.

  30. #dfi.to_csv('dfi.csv')

  31. #dfv.to_csv('dfv.csv')

  32. # list of list of feature indices of each sample in the dataset

  33. Xi = dfi.values.tolist()

  34. # list of list of feature values of each sample in the dataset

  35. Xv = dfv.values.tolist()

  36. if has_label:

  37. return Xi, Xv, y

  38. else:

  39. return Xi, Xv, ids

这里Xi,Xv都是二位数组,可以将dfi,dfv存在csv文件中看一下长什么样子,长的很奇怪【可能后面模型需要吧~】 
dfi:value值为特征index,也就是上文中feat_dict属性保存的值 

dfv:如果是数值变量,则保持原本的值,如果是分类变量,则value为1 

4.2 模型架构

  1. def _init_graph(self):

  2. self.graph = tf.Graph()

  3. with self.graph.as_default():

  4. tf.set_random_seed(self.random_seed)

  5. self.feat_index = tf.placeholder(tf.int32, shape=[None, None],

  6. name="feat_index") # None * F

  7. self.feat_value = tf.placeholder(tf.float32, shape=[None, None],

  8. name="feat_value") # None * F

  9. self.label = tf.placeholder(tf.float32, shape=[None, 1], name="label") # None * 1

  10. self.dropout_keep_fm = tf.placeholder(tf.float32, shape=[None], name="dropout_keep_fm")

  11. self.dropout_keep_deep = tf.placeholder(tf.float32, shape=[None], name="dropout_keep_deep")

  12. self.train_phase = tf.placeholder(tf.bool, name="train_phase")

  13. self.weights = self._initialize_weights()

  14. # model

  15. self.embeddings = tf.nn.embedding_lookup(self.weights["feature_embeddings"],

  16. self.feat_index) # None * F * K

  17. #print(self.weights["feature_embeddings"]) shape=[259,8] n*k个隐向量

  18. #print(self.embeddings) shape=[?,39,8] f*k 每个field取出一个隐向量[这不是FFM每个field取是在取非0量,减少计算]

  19. feat_value = tf.reshape(self.feat_value, shape=[-1, self.field_size, 1])

  20. #print(feat_value) shape=[?,39*1] 某一个样本的39个Feature值

  21. self.embeddings = tf.multiply(self.embeddings, feat_value) #multiply在有一个维度不同时,较少的维度会自行扩展

  22. #print(self.embeddings) shape=[?,39*8]

  23. # 所以这个multiply之后得到的矩阵是Vixi,方便以后进行<Vi,Vj>*xi*xj=<Vi*xi,Vj*xj>的计算,后面的计算FM被简化为了

  24. # sum_square part-square_sum part的形式,采用上面multiply的形式更方便啊!

  25. # ---------- first order term ----------

  26. self.y_first_order = tf.nn.embedding_lookup(self.weights["feature_bias"], self.feat_index) # None * F * 1

  27. self.y_first_order = tf.reduce_sum(tf.multiply(self.y_first_order, feat_value), 2) # None * F

  28. self.y_first_order = tf.nn.dropout(self.y_first_order, self.dropout_keep_fm[0]) # None * F

  29. # ---------- second order term ---------------

  30. # sum_square part

  31. self.summed_features_emb = tf.reduce_sum(self.embeddings, 1) # None * K

  32. self.summed_features_emb_square = tf.square(self.summed_features_emb) # None * K

  33. # square_sum part

  34. self.squared_features_emb = tf.square(self.embeddings)

  35. self.squared_sum_features_emb = tf.reduce_sum(self.squared_features_emb, 1) # None * K

  36. # second order

  37. self.y_second_order = 0.5 * tf.subtract(self.summed_features_emb_square, self.squared_sum_features_emb) # None * K

  38. self.y_second_order = tf.nn.dropout(self.y_second_order, self.dropout_keep_fm[1]) # None * K

  39. # ---------- Deep component ----------

  40. self.y_deep = tf.reshape(self.embeddings, shape=[-1, self.field_size * self.embedding_size]) # None * (F*K)

  41. self.y_deep = tf.nn.dropout(self.y_deep, self.dropout_keep_deep[0])

  42. for i in range(0, len(self.deep_layers)):

  43. self.y_deep = tf.add(tf.matmul(self.y_deep, self.weights["layer_%d" %i]), self.weights["bias_%d"%i]) # None * layer[i] * 1

  44. if self.batch_norm:

  45. self.y_deep = self.batch_norm_layer(self.y_deep, train_phase=self.train_phase, scope_bn="bn_%d" %i) # None * layer[i] * 1

  46. self.y_deep = self.deep_layers_activation(self.y_deep)

  47. self.y_deep = tf.nn.dropout(self.y_deep, self.dropout_keep_deep[1+i]) # dropout at each Deep layer

  48. # ---------- DeepFM ----------

  49. if self.use_fm and self.use_deep:

  50. concat_input = tf.concat([self.y_first_order, self.y_second_order, self.y_deep], axis=1)

  51. elif self.use_fm:

  52. concat_input = tf.concat([self.y_first_order, self.y_second_order], axis=1)

  53. elif self.use_deep:

  54. concat_input = self.y_deep

  55. self.out = tf.add(tf.matmul(concat_input, self.weights["concat_projection"]), self.weights["concat_bias"])

不知道为什么这篇代码把FM写的看起来很复杂。人家复杂是有原因的!!避免了使用one-hot编码后的大大大矩阵 
其实就是embedding层Deep和FM共用了隐向量【feature_size*k】矩阵

所以这个实现的重点在embedding层啊,这里的实现方式通过Xi,Xv两个较小的矩阵【n*field】注意这里field不是FFM中的F,而是未one-hot编码前的Feature数量。 

根据内积的公式我们可以得到

推荐系统算法总结(三)——FM与DNN DeepFM相关推荐

  1. FM系列算法解读(FM+FFM+DeepFM)

    综述 在计算广告中,CTR是非常重要的一环.对于特征组合来说,业界通用的做法主要有两大类:FM系列和Tree系列.这里我们来介绍一下FM系列. 在传统的线性模型中,每个特征都是独立的,如果需要考虑特征 ...

  2. 个人总结:推荐算法 从MF(LFM) 到 FM FFM WideDeep DeepFM

    FM 在推荐系统中,经常会碰到电影评分这样高度稀疏的数据,在之前的个人总结:推荐算法篇(附协同过滤等) 综述的基于模型的协同过滤中,提到了FunkSVD(LFM,Latent Factor Model ...

  3. 推荐系统算法系列(一):FM算法

    一.FM算法背景 在计算广告和推荐系统中,CTR预估是非常重要的一个环节,判断一个商品的是否进行推荐需要根据CTR预估的点击率来进行. 在进行CTR预估时,除了单特征外,往往要对特征进行组合,对于特征 ...

  4. 推荐系统入门(三):矩阵分解MF因子分解机FM(附代码)

    推荐系统入门(三):矩阵分解MF&因子分解机FM(附代码) 目录 推荐系统入门(三):矩阵分解MF&因子分解机FM(附代码) 一. 矩阵分解MF 1. 隐含语义分析技术 1.1 隐语义 ...

  5. mf模型 svd++_推荐系统算法(MF、FM、CF、SVD、LFM、SVD++、TItemCF、timeSVD++、模型融合)...

    为什么需要矩阵分解?(matrix factorization model) 协同过滤可以解决我们关注的很多问题,但是仍然有一些问题存在,比如: 物品之间存在相关性,信息量并不随着向量维度增加而线性增 ...

  6. 深度探索推荐系统算法在工业界如何应用

    由于近些年深度学习技术的飞速发展,大力加速推动了AI在互联网以及传统各个行业的商业化落地,尤其是推荐系统.计算广告等领域.由于推荐系统与提升用户量以及商业化变现有着密不可分的联系,各大公司都放出了众多 ...

  7. 推荐系统中特征交叉模型之——DeepWide/DeepFM/NFM

    前言 上篇文章中我们引入了推荐系统中特征交叉的概念,以及介绍了一些常见的特征交叉方法,这篇文章我们将详细地讨论一下推荐系统中特征交叉地模型,他们的特点,以及他们为什么会这样.本文中介绍的模型有Wide ...

  8. 推荐系统算法原理:向量空间和欧几里得距离的应用

    概述 现在我们使用的一些APP都有智能推荐功能,例如抖音能够根据你的口味推荐符合你口味的短视频,网易云音乐每天会有大约三十首歌曲推荐给你,其中的私人FM功能则可以无限推荐符合你口味的歌曲,各种购物AP ...

  9. 推荐系统lambda架构学习笔记之推荐系统算法(二)

    推荐系统算法 随着机器学习技术的逐渐发展与完善,推荐系统也逐渐运用机器学习的思想来进行推荐.将机器学习应用到推荐系统中的方案真是不胜枚举.以下对Model-Based CF算法做一个大致的分类: 基于 ...

最新文章

  1. python把数据写入excel_Python读写sqlite3数据库的方法并且将数据写入Excel的实例详解...
  2. html编辑完后扩展名是,【填空题】使用文本编辑器编辑完HTML后,扩展名可以是 或 。...
  3. 进入工程制图闪退_工程整体一
  4. python代码太长_Python 太糟糕了?开发者总结了 8 大原因
  5. 支撑数千家天猫商家CRM业务,数云高弹性数据库如何做
  6. [凡文]Docker+Jenkins+Gradle+GitLab在Linux服务端自动化构建Android包
  7. Android关于超图Gis地图的配置流程与许可文件获取方法
  8. linux服务端下的c++ udp socket demo
  9. Neo4j AuraDB免费版——Data Importer
  10. SOLIDWORKS工程图教程:SOLIDWORKS2019工程图新增功能
  11. 朴素贝叶斯算法——拼写检查器
  12. 开发与研发:区别很大(上)
  13. c语言英语教学大纲,C语言教学大纲(《大学C语言实用教程》)
  14. 美团饿了么外卖返利小程序公众号搭建外卖返利分销系统代cps源码
  15. linux内核和发行版本的关系,简述Linux内核和Linux发行版的区别
  16. 三招轻松辨别微博含水量!提升广告主投放效果
  17. linux系统如何关闭触控板,在Deepin Linux 15.7系统中开启或关闭触摸板的方法
  18. [转] “嫁给我是你一生的赌注,我怎么舍得让你输”
  19. 面试常见几种排序算法 Java代码总结
  20. 【基于Arduino自动水位指示器和控制器】

热门文章

  1. 10年老电脑如何提速_告别求人!3个方法教你怎样让你的电脑快的嗖嗖的!
  2. Linux中常见服务对应的端口号
  3. python和按键精灵自动化测试_按键精灵对APP自动化测试(下)
  4. python中xpath_Python爬虫之Xpath语法
  5. Fiddler对手机抓包
  6. 微型计算机最早出现在第三代计算机中,微型计算机最早出现在第三代计算机中。...
  7. android wear2.9新功能,Android Wear 2.0确认2月9日正式登场
  8. 依赖注入的三种方式_一起学Spring之三种注入方式及集合类型注入
  9. 如何得出数组里最大_相邻两数的最大差值(超巧妙解法)
  10. fastdfs redis java,大文件上传_断点续传_文件分片传输_fastdfs_前后端一站式解决方案...