简单实现x的n次方pta_TF2.0实现DeepFM并部署

前文介绍过DeepFM模型理论与其运算过程,本文主要介绍通过TF2.0实现一个自定义deepfm模型,并通过tf serving部署遇到的一些问题。 简单实现x的n次方pta_TF2.0实现DeepFM并部署_第1张图片 1、FM部分 因为FM是通过隐向量做内积来表达组合特征,因此也可以看作一种embedding,特征之间的关联性通过embedding vector的内积来表示。 简单实现x的n次方pta_TF2.0实现DeepFM并部署_第2张图片 简单实现x的n次方pta_TF2.0实现DeepFM并部署_第3张图片 简单实现x的n次方pta_TF2.0实现DeepFM并部署_第4张图片 如上面FM部分网络图,其中标红的1处是计算embedding(Vi*Xi);2处是FM的一阶部分 (W i *X i )注意网络中是直接连接在原始输入上,而不是embedding层上;3处是FM的二阶部分(Xi ·X j ),是连接在embedding层上的。 如果把一阶部分的权重也看成1维的embedding,那么这里加上特征的embedding,这里就需要2个embedding,分别记为:first_weights、feature_embeddings。前面的理论文章介绍过,模型的输入需要feature_index (为了方便取模型特征的权重和embedding) 、feature_value (实际的特征值) 两部分,那么:

FM一次项部分计算:

        # step1: FM 一阶部分 none * f * 1        first_weights = self.first_weights(feature_index)        # Wi*Xi, tf.math.multiply 是对应元素相乘,  none * f * 1        first_weight_value =tf.math.multiply(first_weights, feature_value)        # first_weight_value维度从none * f * 1降维为 none * f        #只是降维,可以理解为concat(w1*x1, ..., wn*xn), 并不是sum(w1*x1, ..., wn*xn)        fm_first_output = tf.math.reduce_sum(first_weight_value, axis=2) # none * f

FM二次项部分计算:

前面文章DeepFM模型理论与其运算过程,介绍过二阶部分运算的推导过程,不清楚的可以看前面文章,二次项的计算如下:

61ec130018cbe216bac705579ba775be.png

        # step2: FM 二阶部分 none * f * k        feature_emb = self.feature_embeddings(feature_index) # none * f * k        # Vi*Xi。tf.math.multiply广播(none * f * k) * (none * f * 1) = (none * f * k)        feature_emb_value = tf.math.multiply(feature_emb, feature_value) # none * f * k        # sum(Vi*Xi)**2, none * k        sumed_feature_emb = tf.math.reduce_sum(feature_emb_value, axis=1) # none * k        interaction_part1 = tf.math.pow(sumed_feature_emb, 2) # none * k        # sum((Vi*Xi)**2), none * k        squared_feature_emb = tf.math.pow(feature_emb_value, 2) # none * f * k        interaction_part2 = tf.math.reduce_sum(squared_feature_emb, axis=1) # none * k        # sum(Vi*Vj*Xi*Xj) none * k        fm_second_output = 0.5 * tf.math.subtract(interaction_part1, interaction_part2) # none * k
2、Deep部分与结果输出

简单实现x的n次方pta_TF2.0实现DeepFM并部署_第5张图片

Deep部分很简单,与FM共享二次项部分的embedding层,再加几层全连接:
        # step3: deep部分        # 输入形状变换为 none * (f * k)        deep_feature = tf.reshape(feature_emb_value, (-1, self.num_field * self.embedding_size))        deep_feature = tf.keras.layers.Dropout(self.dropout_deep)(deep_feature)        # 最终输出:none * layer_sizes[-1]        for i in range(len(self.layer_sizes)):            deep_feature = getattr(self, 'dense_' + str(i))(deep_feature)            deep_feature = getattr(self, 'bn_' + str(i))(deep_feature)            deep_feature = getattr(self, 'activation_' + str(i))(deep_feature)            deep_feature = getattr(self, 'dropout_' + str(i))(deep_feature)        # 所有中间特征合并:none * (f + k + layer_sizes[-1])        concat_input = tf.concat((fm_first_output, fm_second_output, deep_feature), axis=1)        # 最终输出        output = self.output_layer(concat_input) #  at_bias']))
3、遇到的一些问题 3.1、模型输入的数值类型 模型的输入数值类型,需要进行tf.cast转换,不然会报错:

简单实现x的n次方pta_TF2.0实现DeepFM并部署_第6张图片

    # 训练的train data & train label, 并统一数据格式,不然tf.math.multiply会报错    index = tf.cast(data_index[cols].values, tf.float32)    value = tf.cast(data_value[cols].values, tf.float32)

3.2、模型输入的数据形状

模型的输入需要feature_index、feature_value,刚开始在自定义model的前向传播时,是直接定义2个输入,训练没问题,但模型save时会报错:

    # 前向传播    @tf.function    def call(self, feature_index, feature_value):        # 为了矩阵运算,需要对feature_value进行从内升维,从none * f 到 none * f * 1        # 之所以是f不是one-hot后的n,是因为这里进行了压缩,离散的值都是1,主要是通过feature_index索引决定的

0c85a04a6a7adba2fb99c3cae738cb14.png

简单实现x的n次方pta_TF2.0实现DeepFM并部署_第7张图片

把模型的输入格式改为list格式[feature_index, feature_value]作为输入后,模型可以训练、预测和保存后重新load进行预测,但是在进行tf serving部署进行预测时,遇到问题预测不成功:

    # 前向传播    @tf.function    def call(self, inputs):        feature_index, feature_value = inputs        # 为了矩阵运算,需要对feature_value进行从内升维,从none * f 到 none * f * 1        # 之所以是f不是one-hot后的n,是因为这里进行了压缩,离散的值都是1,主要是通过feature_index索引决定的

0bad8c7eb5d596d1928704e67706e8f8.png

简单实现x的n次方pta_TF2.0实现DeepFM并部署_第8张图片

为了能进行tf serving部署预测,又尝试了把模型输入feature_index, feature_value拼接为一个整体作为输入,从而模型可以保存并可以通过tf serving部署预测:

index_value = tf.concat([index, value], axis=-1)
    # 前向传播    @tf.function    def call(self, inputs):        feature_index = inputs[:, :self.num_field]        feature_value = inputs[:, self.num_field:]

a7e19b61cc620e8ebba423731d317eac.png

3.3、其他

在自定复杂模型时,如果自己没有自定义predict方法时,model.predict(x)会报错,预测要用model(x),或者model.call(x)进行预测:

    # model_deepfm.call(feature_index_value)    model_deepfm(feature_index_value)

自定义类模型时,起码要有两部分初始化部分__init__和前向运算部分call。@tf.function是把函数转换为计算图,以图执行模式运行可以提高运算速度:

class DeepFM(tf.keras.Model):    def __init__(self, num_field, num_feature):        super().__init__()        # xxx    # 前向传播    @tf.function    def call(self, inputs):        # xxx

4、总结

本文主要分享了在学习TF2.0过程中,参考其他一些资料,实现自定义deepfm,并通过tf serving进行部署中遇到的一些问题。代码中的方法可能不是最优方法或存在一些问题,需要详细代码和交流的朋友可以私信。

本次实现的deepfm,是通过自定义model类,即class DeepFM(tf.keras.Model)实现的,好处是对于初学者来说,前向运算过程比较清晰,但需要同时自定义模型的训练方法。

其实也可以通过自定义层:class FMLayer(tf.keras.layers.Layer)来实现,这样就不需要再自定义model类,从而可以用原始model类的一些方法,如 model.fit。git上的DeepCTR项目就是用的这种方法,有兴趣的朋友可以试试。

参考资料:

《简明的TensorFlow2》

https://arxiv.org/pdf/1703.04247.pdf

https://zhuanlan.zhihu.com/p/32563337

http://www.manongjc.com/article/33337.html

https://www.cnblogs.com/wkang/p/9881921.html

https://blog.csdn.net/maqunfi/article/details/99635620

https://mp.weixin.qq.com/s/QrO48ZdP483TY_EnnWFhsQ

https://mp.weixin.qq.com/s/4bw9GxQ10iyC08H0zYbSmw

https://github.com/yongqyu/DeepFM-tf2

https://github.com/shenweichen/DeepCTR

https://github.com/JianzhouZhan/Awesome-RecSystem-Models

https://github.com/princewen/tensorflow_practice/tree/master/recommendation

你可能感兴趣的:(简单实现x的n次方pta)