版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_35564813/article/details/85464337
摘要
FM被广泛应用在CTR,但是FFM在一些世界范围的CTR竞赛表现好于目前存在的模型。作者实现了相关代码,并与一些竞争模型进行了全面的分析。实验证明FFM在某些分类问题上非常有用。
介绍
FFM
对于这个例子来说,FM的隐向量表示应该为:
在FM中,每个特征只有一个隐向量来学习隐性的影响。拿ESPN来做例子, 被用来学习隐性的与Nike和Male的影响,但是因为Nike和Male是属于不同的领域的,那么使用同一个 可能不太合适。
在FFM中,每个特征都有一些隐性的向量,取决于其他特征的所属领域。比如对于上述例子,FFM的隐向量表示为:
所以其数学模型为:
整个算法步骤:
优化
使用AdaGrad优化方法,自适应优化学习速率,对频繁变化的参数以更小的步长进行更新,而稀疏的参数以更大的步长进行更新。
梯度:
AdaGrad:
G的初始值为1,避免分母为0,过大
缺陷
仅针对二次项,FM有nk个参数,而FFM有nfk个参数。而且FFM无法利用化简,所以计算复杂度为 .所以这需要较大的硬件资源来提供运算。
Impact of Parameter
-
k值不需要太大,没有什么提升
-
和 需要着重调整
Early Stopping
防止过拟合
代码
import tensorflow as tf
import numpy as np
import os
input_x_size = 20
field_size = 2
vector_dimension = 3
# 使用SGD,每一个样本进行依次梯度下降,更新参数
batch_size = 1
all_data_size = 1000
alpha = 0.01
MODEL_SAVE_PATH = "TFModel"
MODEL_NAME = "FFM"
def createTwoDimensionWeight(input_x_size,field_size,vector_dimension): #初始化w2
weights = tf.truncated_normal([input_x_size,field_size,vector_dimension]) #默认生成均值为0,标准差为1的[input_x_size,field_size,vector_dimension]维度的张量
tf_weights = tf.Variable(weights)
return tf_weights
def computation(input_x,input_x_field,TwoWeights):
thirdValue=tf.Variable(0.0,dtype=tf.float32)
input_shape=input_x_size
for i in range(input_shape-1):
featureIndex1 = i #对应每个x1的特征序号
fieldIndex1 = int(input_x_field[i]) #对应特征x1的field序号
for j in range(i + 1, input_shape):
featureIndex2 = j #对应每个x2的特征序号
fieldIndex2 = int(input_x_field[j]) #对应特征x2的field序号
vectorLeft = tf.convert_to_tensor([[featureIndex1, fieldIndex2, i] for i in range(vector_dimension)]) #转换成张量
weightLeft = tf.gather_nd(TwoWeights, vectorLeft) #取对应位置的值,只不过是在张量上
weightLeftAfterCut = tf.squeeze(weightLeft) #消除维度为1的shape
vectorRight = tf.convert_to_tensor([[featureIndex2, fieldIndex1, i] for i in range(vector_dimension)])
weightRight = tf.gather_nd(TwoWeights, vectorRight)
weightRightAfterCut = tf.squeeze(weightRight)
tempValue = tf.reduce_sum(tf.multiply(weightLeftAfterCut, weightRightAfterCut))
indices2 = [i]
indices3 = [j]
xi = tf.squeeze(tf.gather_nd(input_x, indices2))
xj = tf.squeeze(tf.gather_nd(input_x, indices3))
product = tf.reduce_sum(tf.multiply(xi, xj))
secondItemVal = tf.multiply(tempValue, product)
tf.assign(thirdValue, tf.add(thirdValue, secondItemVal))
return thirdValue
def gen_data():
labels = [-1,1]
y = [np.random.choice(labels,1)[0]for _ in range(all_data_size)] #表示在【-1,1】中选择一个数,后面加【0】是为了取值,而不是保持array类型
x_field = [i // 10 for i in range(input_x_size)]
x = np.random.randint(0,2,size=(all_data_size,input_x_size))
return x,y,x_field
if __name__=='__main__':
global_step = tf.Variable(0, trainable=False)
trainx, trainy, trainx_field = gen_data()
input_x = tf.placeholder(tf.float32, [input_x_size])
input_y = tf.placeholder(tf.float32)
lambda_v = tf.constant(0.001, name='lambda_v')
weight = createTwoDimensionWeight(input_x_size, # 创建二次项的权重变量
field_size,
vector_dimension) # n * f * k
y_ = computation(input_x, trainx_field, weight)
l2_norm = tf.reduce_sum(tf.multiply(lambda_v, tf.pow(weight, 2)))
loss = tf.log(1 + tf.exp(-input_y * y_)) + l2_norm
train_step = tf.train.AdagradOptimizer(learning_rate=alpha,initial_accumulator_value=1).minimize(loss)
saver = tf.train.Saver(max_to_keep=1) #只保留最后的一个模型
max_acc=100000
is_train=False
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for j in range(10):
if is_train:
for t in range(all_data_size):
input_x_batch = trainx[t]
input_y_batch = trainy[t]
predict_loss, _, steps = sess.run([loss, train_step,global_step],
feed_dict={input_x: input_x_batch, input_y: input_y_batch})
print("After {step} training step(s) , loss on training batch is {predict_loss} "
.format(step=steps, predict_loss=predict_loss))
global_step+=1
if predict_loss<max_acc:
max_acc=predict_loss
saver.save(sess, os.path.join(MODEL_SAVE_PATH, MODEL_NAME), global_step=steps)
else:
model_file = tf.train.latest_checkpoint(MODEL_SAVE_PATH+'/')
saver.restore(sess, model_file)
for t in range(all_data_size):
val_loss, yhat = sess.run([loss, y_], feed_dict={input_x: trainx[t],input_y: trainy[t]})
print("loss on training batch is {predict_loss} ,prediction is {yhat},real y is {y}"
.format(predict_loss=val_loss,yhat=yhat,y=trainy[t]))
上述代码在重新导入model以后,运行的所得到的yhat均不变,可能有一些问题,但是总体思路应该是没错的,如果有大佬能帮忙解决一下这个问题就更好了。