推荐算法(一)——FM因式分解机
目录
- 1 介绍
- 2 原理
- 3 代码
- 4 总结
- 写在最后
1 介绍
FM(Factorization Machines,因子分解机)早在2010年提出,作为逻辑回归模型的改进版,拟解决在稀疏数据的场景下模型参数难以训练的问题。并且考虑了特征的二阶交叉,弥补了逻辑回归表达能力差的缺陷。
FM 作为推荐算法广泛应用于推荐系统及计算广告领域,通常用于预测点击率 CTR(click-through rate)和转化率 CVR(conversion rate)。
2 原理
逻辑回归为普通的线性模型,优点是复杂度低、方便求解,但缺点也很明显,没有考虑特征之间的交叉,表达能力有限。
FM在线性模型的基础上添加了一个多项式,用于描述特征之间的二阶交叉。
- nnn 表示一个样本的特征的个数(类别特征 onehot 之后的维度),两两交互可得到 n(n−1)/2n(n-1)/2n(n−1)/2 个交叉项;
- wijw_{ij}wij 是组合对应的权重,用于表征这对组合的重要性,多项式要学习的参数即为 n(n−1)/2n(n-1)/2n(n−1)/2 个 www 系数。
2.1 存在的问题:
参数 wijw_{ij}wij 学习困难,因为对 wijw_{ij}wij 进行更新时,求得的梯度对应为 xixjx_{i}x_{j}xixj,当且仅当 xix_{i}xi 与 xjx_{j}xj 都非0时参数才会得到更新。
但是经过 onehot 处理的数据非常稀疏,能够保证两者都非0的组合较少,导致大部分参数 www 难以得到充分训练。
2.2 解决方法:
作者对每个特征分量 xix_{i}xi 引入 kkk 维(k<<n) 辅助向量 vi=(vi1,vi2,...,vik)v_{i}=(v_{i1},v_{i2},...,v_{ik})vi=(vi1,vi2,...,vik),每个特征对应一个总共 nnn 个向量,然后利用向量内积的结果 vivjTv_{i}v_{j}^{T}vivjT 来表示原来的组合参数 wijw_{ij}wij.
于是,原式变成了如下形式:(尖括号表示内积)
这样要学习的参数从 n(n−1)/2n(n-1)/2n(n−1)/2 个 wijw_{ij}wij 系数变成了元素个数为 n∗kn*kn∗k 的 VVV 矩阵,因为 k<<nk<<nk<<n, 所以该做法降低了训练复杂度。
此外,引入辅助向量削弱了参数间的独立性,因为对于 xix_{i}xi 的隐向量 viv_{i}vi,任何包含 xix_{i}xi 的特征组合,只要 xix_{i}xi 本身不为0,都可对 viv_{i}vi 进行更新,同理每个隐向量都能得到充分的学习,这样就解决了数据稀疏带来的难以训练问题。
2.3 复杂度分析
为了进一步降低算法复杂度,对多次项进行如下化简:
对需要训练的参数 θ\thetaθ 求梯度得:
重点关注 vifv_{if}vif 的梯度,vifv_{if}vif 表示 xix_{i}xi 的隐向量,因为梯度项 ∑j=1nvj,fxj\sum_{j=1}^{n} v_{j, f} x_{j}∑j=1nvj,fxj 中不包含 iii,只与 fff 有关,因此只要一次性求出所有的 fff 的 ∑j=1nvj,fxj\sum_{j=1}^{n} v_{j, f} x_{j}∑j=1nvj,fxj 的值(复杂度 O(nk)O(nk)O(nk)),在求每个参数的梯度时都可复用该值。
当已知 ∑j=1nvj,fxj\sum_{j=1}^{n} v_{j, f} x_{j}∑j=1nvj,fxj 时计算每个参数梯度的复杂度是 O(1)O(1)O(1),因此训练FM模型的复杂度也是 O(nk)O(nk)O(nk)。
化简之后,FM的复杂度从 O(n2k)O(n^{2}k)O(n2k) 降到线性的 O(nk)O(nk)O(nk),更利于上线使用。
2.4 扩展到多维 FM:
将二阶交叉项转换成多阶交叉项,进一步提升模型的表达能力。跟二阶交叉项相同,多阶交叉项也可从 O(nlk)O(n^{l}k)O(nlk) 复杂度降到线性的 O(nk)O(nk)O(nk),具有非常好的性质。
3 代码
理论结合代码食用更佳, 代码中会加入充分注释,以易理解。(整体代码可参考Github仓库)
FM 层代码:
(将 FM 封装成 Layer,随后在搭建 Model 时直接调用即可)
import tensorflow as tf
import tensorflow.keras.backend as Kclass FM_layer(tf.keras.layers.Layer):def __init__(self, k, w_reg, v_reg):super(FM_layer, self).__init__()self.k = k # 隐向量vi的维度self.w_reg = w_reg # 权重w的正则项系数self.v_reg = v_reg # 权重v的正则项系数def build(self, input_shape): # 需要根据input来定义shape的变量,可在build里定义)self.w0 = self.add_weight(name='w0', shape=(1,), # shape:(1,)initializer=tf.zeros_initializer(),trainable=True,)self.w = self.add_weight(name='w', shape=(input_shape[-1], 1), # shape:(n, 1)initializer=tf.random_normal_initializer(), # 初始化方法trainable=True, # 参数可训练regularizer=tf.keras.regularizers.l2(self.w_reg)) # 正则化方法self.v = self.add_weight(name='v', shape=(input_shape[-1], self.k), # shape:(n, k)initializer=tf.random_normal_initializer(),trainable=True,regularizer=tf.keras.regularizers.l2(self.v_reg))def call(self, inputs, **kwargs):# inputs维度判断,不符合则抛出异常if K.ndim(inputs) != 2:raise ValueError("Unexpected inputs dimensions %d, expect to be 2 dimensions" % (K.ndim(inputs)))# 线性部分,相当于逻辑回归linear_part = tf.matmul(inputs, self.w) + self.w0 #shape:(batchsize, 1)# 交叉部分——第一项inter_part1 = tf.pow(tf.matmul(inputs, self.v), 2) #shape:(batchsize, self.k)# 交叉部分——第二项inter_part2 = tf.matmul(tf.pow(inputs, 2), tf.pow(self.v, 2)) #shape:(batchsize, k)# 交叉结果inter_part = 0.5*tf.reduce_sum(inter_part1 - inter_part2, axis=-1, keepdims=True) #shape:(batchsize, 1)# 最终结果output = linear_part + inter_partreturn tf.nn.sigmoid(output) #shape:(batchsize, 1)
定义好了 FM 层,模型搭建就简单了,Model 代码如下:
class FM(tf.keras.Model):def __init__(self, k, w_reg=1e-4, v_reg=1e-4):super(FM, self).__init__()self.fm = FM_layer(k, w_reg, v_reg) # 调用写好的FM_layerdef call(self, inputs, training=None, mask=None):output = self.fm(inputs) # 输入FM_layer得到输出return output
数据处理代码: 实验数据点我
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_splitdef create_criteo_dataset(file_path, test_size=0.3):data = pd.read_csv(file_path)dense_features = ['I' + str(i) for i in range(1, 14)] # 数值特征sparse_features = ['C' + str(i) for i in range(1, 27)] # 类别特征# 缺失值填充data[dense_features] = data[dense_features].fillna(0)data[sparse_features] = data[sparse_features].fillna('-1')# 归一化(数值特征)data[dense_features] = MinMaxScaler().fit_transform(data[dense_features])# onehot编码(类别特征)data = pd.get_dummies(data)#数据集划分X = data.drop(['label'], axis=1).valuesy = data['label']X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size)return (X_train, y_train), (X_test, y_test)
模型训练代码:
from model import FM
from utils import create_criteo_datasetimport tensorflow as tf
from tensorflow.keras import optimizers, losses, metrics
from sklearn.metrics import accuracy_scoreif __name__ == '__main__':file_path = 'data\train.txt' # 修改为自己的路径(X_train, y_train), (X_test, y_test) = create_criteo_dataset(file_path, test_size=0.2)k = 8 w_reg = 1e-5v_reg = 1e-5model = FM(k, w_reg, v_reg)optimizer = optimizers.SGD(0.01)summary_writer = tf.summary.create_file_writer('.\tensorboard') # tensorboard可视化文件路径for epoch in range(100):with tf.GradientTape() as tape:y_pre = model(X_train) # 前馈得到预测值loss = tf.reduce_mean(losses.binary_crossentropy(y_true=y_train, y_pred=y_pre)) # 与真实值计算loss值print('epoch: {} loss: {}'.format(epoch, loss.numpy())) grad = tape.gradient(loss, model.variables) # 根据loss计算模型参数的梯度optimizer.apply_gradients(grads_and_vars=zip(grad, model.variables)) # 将梯度应用到对应参数上进行更新# 需要tensorboard记录的变量(不需要可视化可将该模块注释掉)with summary_writer.as_default():tf.summary.scalar("loss", loss, step=epoch)#评估pre = model(X_test)pre = [1 if x>0.5 else 0 for x in pre]print("AUC: ", accuracy_score(y_test, pre)) # AUC: 0.772
tensorboard的可视化结果如下: (可继续加大 epoch 数)
4 总结
看到这,相信你对 FM 也有了基本的了解。下面是对优缺点的总结:
优点:
- 将二阶交叉特征考虑进来,提高模型的表达能力;
- 引入隐向量,缓解了数据稀疏带来的参数难训练问题;
- 模型复杂度保持为线性,并且改进为高阶特征组合时,仍为线性复杂度,有利于上线应用。
缺点:
- 虽然考虑了特征的交叉,但是表达能力仍然有限,不及深度模型;
- 同一特征 xix_{i}xi 与不同特征组合使用的都是同一隐向量 viv_{i}vi,违反了特征与不同特征组合可发挥不同重要性的事实。
下篇文章将会是 FM 算法的改进版本 FFM,敬请期待…
写在最后
一直都想开一个推荐系统专栏,记录一些经典推算法的原理介绍及代码实践。但忙于学业,一直拖到了现在才产出专栏的第一篇文章,之后陆续发表其他算法的文章.
如果你想系统的学习推荐算法,原理可参考我的知乎专栏,代码的实现可参考Github仓库。
码字不易,读完此文的你,如果感觉有收获,就请点个赞吧~
推荐算法(一)——FM因式分解机相关推荐
- 【推荐算法】FM模型:Factorization Machines
1.线性回归 在介绍FM之前,我们先简单回顾以下线性回归. 回归分析是一种预测性的建模技术,它研究的是因变量(目标)和自变量(预测器)之间的关系.这种技术通常用于预测分析,时间序列模型以及发现变量之间 ...
- fm算法 c语言,推荐算法之—FM
1.什么是FM算法 FM即Factor Machine,因子分解机 2.为什么需要FM 1).特征组合是许多机器学习建模过程中遇到的问题,如果对特征直接建模,很有可能忽略掉特征与特征之间的关联信息,一 ...
- 因式分解机(Factorization Machines,FM )
FM优化(0):因式分解机(Factorization Machines,FM ) FM优化(1):基于地理因式分解法的POI推荐排序算法(Rank-GeoFM) 前言 FM背景 FM模型 FM算法详 ...
- 【深度推荐算法】DataWhale组队学习Task01: DeepCrossing
DeepCrossing 动机 将深度学习架构应用于推荐系统中的模型2016年由微软提出, 完整的解决了特征工程.稀疏向量稠密化, 多层神经网络进行优化目标拟合等一系列深度学习再推荐系统的应用问题. ...
- 「网易云音乐」歌单推荐算法
推荐算法可以通过APP表现进行反推,不会100%准确,但应该也八九不离十. 推荐依据 推荐算法不是无根之水,它必须借助用户的数据才可以进行推荐.即使是冷启动,也需要获得你的微博.微信(你的登录方式)或 ...
- 杨鹏谈世纪佳缘推荐算法:基于Spark GraphX,弃GBDT和LR用FM
杨鹏谈世纪佳缘推荐算法:基于Spark GraphX,弃GBDT和LR用FM 发表于2015-09-30 09:53| 1447次阅读| 来源CSDN| 2 条评论| 作者杨鹏 机器学习推荐算法 ...
- 推荐算法---FM,协同过滤
文章目录 目录 1.FM算法产生背景 2.FM算法模型 3.FM算法VS其他算法 4.推荐算法总结 目录 1.FM算法产生背景 在传统的线性模型如LR中,每个特征都是独立的,如果需要考虑特征与特征直接 ...
- (一)因式分解机(Factorization Machine,FM)原理及实践
因子分解机(Factorization Machine),是由Konstanz大学(德国康斯坦茨大学)Steffen Rendle(现任职于Google)于2010年最早提出的,旨在解决大规模稀疏数据 ...
- FM(Factorization Machine)因式分解机 与 TensorFlow实现 详解
1,线性回归(Linear Regression) 线性回归,即使用多维空间中的一条直线拟合样本数据,如果样本特征为: \[x = ({x_1},{x_2},...,{x_n})\] 模型假设函数如下 ...
最新文章
- Akka源码分析-Remote-发消息
- 从复旦博士生到985高校副教授,贫穷使人深知摆脱自卑的艰难
- 5G UE — SIM Card
- 现实世界的Windows Azure:采访Figlo的全球合作伙伴支持经理Nathan Brouwer
- Servlet的部分request请求处理
- NIO详解(九):Selector详解
- 2018-1-20:左移和右移运算以及和(无符号右移)的区别以及位运算的小题目...
- iphone ios 如何开发升级适配 iphone5 4inch
- Django框架之DRF get post put delete 使用简单示例 (利用序列化反序列化)
- Anisotropic gauss filter
- Ubuntu 中设置源的几种方法
- 【渝粤题库】陕西师范大学165202 战略管理 作业(专升本)
- aws s3 獲取所有文件_Url从Amazon S3获取文件
- (二十二)【模电】(波形的发生与信号的转换)电压比较器
- 微信公众号授权H5页面
- Chrome插件安装时出现的“程序包无效”问题
- 解决vmware下虚拟机关机重启ip改变
- Gitlab Runner
- 使用 arp-scan 快速扫描局域网 IP -> raspberry pi ssh vnc
- Android wifi carlife,CarLife 可以通过WIFI和Android