教你復現頂會論文網絡結構(五)--NFM模型網絡結構

參考文章:
TensorFlow 2.0 implementation of NFM
Reference:
Neural Factorization Machines for Sparse Predictive Analytics
https://www.jianshu.com/p/4e65723ee632

概述

該系列主要是復現一些經典的網絡結構與頂會論文的網絡結構,我一開始看論文,以爲看到網絡結構和了解結構原理後,就完全學到了這篇論文的精髓,誰知,等到自己想要用這個網絡結構時,無法打通理解與代碼復現的這一步,這就導致我在科研或者工作時的束手無措,因此,我就決定探究如何將一篇論文中的結構和論文中的公式用代碼的形式復現出來。
深度學習框架:tensorflow2.0 ,numpy。
語言:python。
復現的代碼全部在:https://github.com/Snail110/recsys。

0.介紹

FM和深度網絡DNN的結合也就成爲了CTR預估問題中主流的方法。有關FM和DNN的結合有兩種主流的方法,並行結構和串行結構。兩種結構的理解以及實現如下表所示:

結構 描述 常見模型
並行結構 FM部分和DNN部分分開計算,只在輸出層進行一次融合得到結果 DeepFM,DCN,Wide&Deep
串行結構 將FM的一次項和二次項結果(或其中之一)作爲DNN部分的輸入,經DNN得到最終結果 PNN,NFM,AFM

1.網絡結構

該部分主要是將論文中公式與結構圖對應起來,理解每一個公式的含義以及網絡結構圖中每一部分的輸入輸出。
模型圖
首先,當你看完一篇論文並理解了論文的主要思想後,需要嘗試着將網絡結構與論文中的每一步的數學公式一一對應上,在心中或者圖片上協商每一個環節的數學公式,然後考慮用深度學習框架來實現。
首先這篇論文中有數學公式(1),(2),(3)對應着網絡模型。
然後需要一步一步的將公式對應到網絡模型中,

公式(1)

在這裏插入圖片描述

輸出層是若干層的全連接層,經過多次的deep layers 獲取深度特徵。

公式(2)

Bi-Interaction Layer名字挺高大上的,其實它就是計算FM中的二次項的過程,因此得到的向量維度就是我們的Embedding的維度。最終的結果是:

公式(3)

Hidden Layers就是我們的DNN部分,將Bi-Interaction Layer得到的結果接入多層的神經網絡進行訓練,從而捕捉到特徵之間複雜的非線性關係。

在進行多層訓練之後,將最後一層的輸出求和同時加上一次項和偏置項,就得到了我們的預測輸出:
在這裏插入圖片描述

2.代碼復現

該部分主要是按照網絡結構圖,用代碼的方式實現。在代碼實現的過程中,我們需要緊緊結合數學公式體會其中的含義以及如何用代碼來實現這些數學公式。

class BiInteraction(tf.keras.layers.Layer):
    def __init__(self, Units=1, **kwargs):
        self.units = Units
        super(BiInteraction, self).__init__(**kwargs)

    def build(self, input_shape):
        input_dim = input_shape[2]
        # self.W = self.add_weight(shape=(input_dim, self.units), initializer='random_normal', trainable=True)
        # self.b = self.add_weight(shape=(input_dim, self.units), initializer='random_normal', trainable=True)
        self.linearlayer = tf.keras.layers.Dense(input_dim, activation='relu', use_bias=True)

    def call(self, input):
        # sum-square-part
        self.summed_features_emb = tf.reduce_sum(input,1) # None * K
        # print("self.summed_features_emb:",self.summed_features_emb.get_shape())
        self.summed_features_emb_square = tf.square(self.summed_features_emb) # None * K
        # square-sum-part
        self.squared_features_emb = tf.square(input)
        self.squared_sum_features_emb = tf.reduce_sum(self.squared_features_emb,1) # None * K

        # second order
        self.y_second_order = 0.5 * tf.subtract(self.summed_features_emb_square,self.squared_sum_features_emb) # None * K
        # print("y_second_order:",self.y_second_order.get_shape()) # 128 * 10
        output = self.linearlayer(self.y_second_order)
        return output

class NFM(tf.keras.Model):
    def __init__(self, num_feat, num_field, dropout_deep, deep_layer_sizes, embedding_size=10):
        super().__init__()
        self.num_feat = num_feat  # F =features nums
        self.num_field = num_field  # N =fields of a feature
        self.dropout_deep = dropout_deep

        # Embedding 這裏採用embeddings層因此大小爲F* M F爲特徵數量,M爲embedding的維度
        feat_embeddings = tf.keras.layers.Embedding(num_feat, embedding_size,
                                                    embeddings_initializer='uniform')  # F * M
        self.feat_embeddings = feat_embeddings

        # fc layer
        self.deep_layer_sizes = deep_layer_sizes
        # 神經網絡方面的參數
        for i in range(len(deep_layer_sizes)):
            setattr(self, 'dense_' + str(i), tf.keras.layers.Dense(deep_layer_sizes[i]))
            setattr(self, 'batchNorm_' + str(i), tf.keras.layers.BatchNormalization())
            setattr(self, 'activation_' + str(i), tf.keras.layers.Activation('relu'))
            setattr(self, 'dropout_' + str(i), tf.keras.layers.Dropout(dropout_deep[i]))
        self.bilayer = BiInteraction(1)
        # last layer
        self.fc = tf.keras.layers.Dense(1, activation=None, use_bias=True)

        self.linearlayer = tf.keras.layers.Dense(deep_layer_sizes[-1], activation='relu', use_bias=True)

    def call(self, feat_index, feat_value):
        # call函數接收輸入變量
        # embedding part  feat_index = inputs爲輸入 feat_embeddings爲一個layer。
        feat_embedding_0 = self.feat_embeddings(feat_index)  # Batch * N * M
        #         print(feat_value.get_shape())
        feat_embedding = tf.einsum('bnm,bn->bnm', feat_embedding_0, feat_value)

        y_deep = self.attentionlayer(feat_embedding)
        y_linear = self.linearlayer(tf.reduce_sum(feat_embedding,1))

        for i in range(len(self.deep_layer_sizes)):
            y_deep = getattr(self, 'dense_' + str(i))(y_deep)
            y_deep = getattr(self, 'batchNorm_' + str(i))(y_deep)
            y_deep = getattr(self, 'activation_' + str(i))(y_deep)
            y_deep = getattr(self, 'dropout_' + str(i))(y_deep)
        y = y_deep + y_linear
        output = self.fc(y)

        return output

3.總結

你看,通過這樣一步步將公式與代碼對應起來,就好實現多了,對於不同的計算公式採用不同的函數需要多看文檔,這樣纔可以選用正確的api。
最後,如果需要獲取全部代碼,請看下我的github上倉庫:https://github.com/Snail110/recsys
這裏面是用tensorflow2.0框架來寫。如果覺得我實現的還不錯,記得給我一個星星哦。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章