TensorFlow实现FM

Posted earendil

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TensorFlow实现FM相关的知识,希望对你有一定的参考价值。

看了网上的一些用tf实现的FM,很多都没有考虑FM实际使用中数据样本稀疏的问题。

我在实现的时候使用 embedding_lookup_sparse来解决这个问题。

对于二阶部分,由于embedding_lookup_sparse没法计算 和的平方 和 平方的和,我参考embedding_lookup_sparse中sum和mean两种实现,自己写了一下。不过数据输入部分还需要改一下,改用dataset会更好。

代码如下:

import tensorflow as tf
from tensorflow.python.ops import math_ops
from tensorflow.python.framework import dtypes
from tensorflow.python.ops import array_ops
import random
import numpy as np
from sklearn import metrics

class Args():
    feature_size=925
    field_size=15
    embedding_size = 20
    epoch = 3
    batch_size = 2000
    learning_rate = 0.001
    l2_reg_rate = 0.001
    checkpoint_dir = "./model"
    is_training = True

class FMmodel():
    def __init__(self):
        self.feature_sizes = Args.feature_size
        self.field_size = Args.field_size
        self.embedding_size = Args.embedding_size
        self.l2_reg_rate = Args.l2_reg_rate
        self.epoch = Args.epoch
        self.learning_rate = Args.learning_rate
        self.weight = {}
        self.model_path = Args.checkpoint_dir
        self.batch_size = Args.batch_size

    def build_model(self,is_warm_up=False):
        self.x1_index = tf.sparse_placeholder(tf.int64,name="x1_index")
        self.x1_value = tf.sparse_placeholder(tf.float32,name="x1_value")
        self.labels = tf.placeholder(tf.float32,name="labels",shape=[None,1])
        init_randomW = tf.random_normal_initializer(mean=0.0, stddev=0.05, seed=None, dtype=tf.float32)
        init_randomV = tf.random_normal_initializer(mean=0.0, stddev=0.00001, seed=None, dtype=tf.float32)
        #特征向量
        self.weight["feature_weight"] = tf.get_variable(
            shape =[self.feature_sizes,self.embedding_size],
            name=feature_weight,
            initializer=init_randomV
        )

        #一次项中的W系数
        self.weight["feature_first"] = tf.get_variable(
            shape=[self.feature_sizes,1],
            initializer=init_randomW,
            name=feature_first)

        self.weight["bais"] = tf.get_variable(shape=[1,1],initializer=tf.constant_initializer(0.0),name="bais")

        #[batch_size,1] 线性部分的计算结果 xi*wi求和
        self.line_part1 = tf.nn.embedding_lookup_sparse(self.weight["feature_first"],
                                                        sp_ids=self.x1_index,sp_weights=self.x1_value,combiner=sum)
        self.line_part1_shape = tf.shape(self.line_part1)
        #[batch*embedding_size]
        self.embedding_part1_sum_square = tf.nn.embedding_lookup_sparse(self.weight["feature_weight"],
                                                      sp_ids=self.x1_index,sp_weights=self.x1_value,combiner=sum)

        #[batch_size,embeding_size]
        ids_1 = self.x1_index.values

        self.ids1,self.idx1 = tf.unique(ids_1)

        self.weight_1 = self.x1_value.values

        self.weight_1 = tf.reshape(self.weight_1,[-1,1])

        if self.weight_1.dtype != dtypes.float32:
            self.weight_1 = math_ops.cast(self.weight_1,dtypes.float32)

        #[batch_size,embedding_size]
        self.embedding_1 = tf.nn.embedding_lookup(self.weight["feature_weight"],ids=self.ids1)

        self.new_embedding_1 = tf.gather(self.embedding_1,self.idx1)

        #[batch_value_count,embedding_size]
        self.embedding_weight_part1 =tf.multiply(self.weight_1,self.new_embedding_1)

        self.embedding_weight_part1_square = tf.square(self.embedding_weight_part1)


        self.segment_ids_1 = self.x1_index.indices[:, 0]

        if self.segment_ids_1.dtype != dtypes.int32:
            self.segment_ids_1 = math_ops.cast(self.segment_ids_1, dtypes.int32)

        self.embeddings_square_sum1 = tf.math.segment_sum(
            self.embedding_weight_part1_square,self.segment_ids_1)

        self.ess1_shape = tf.shape(self.embeddings_square_sum1)
        #[batch_size,1]
        self.y1_v = 0.5*tf.reduce_sum(tf.subtract(self.embedding_part1_sum_square,self.embeddings_square_sum1),1)
        self.y1_v = tf.reshape(self.y1_v,[-1,1])
        self.y1 = tf.add(tf.add(self.line_part1,self.y1_v),self.weight["bais"])

        self.o1 = tf.sigmoid(self.y1)
        self.loss = tf.losses.log_loss(labels=self.labels,predictions=self.o1)
        self.error = tf.reduce_mean(self.loss)
        # with tf.name_scope("loss"):
        #     tf.summary.scalar("loss", self.error)

        self.opt = tf.train.AdamOptimizer().minimize(self.error)
        self.session = tf.Session()
        self.init = tf.group(tf.global_variables_initializer())
        if is_warm_up:
            self.saver = tf.train.Saver(tf.global_variables())
            self.saver.restore(self.session, self.model_path)
        else:
            self.session.run(self.init)

    def predict(self,file_name):
        result_list = []
        for x1_index, x1_value, true_labels in self.load_data(file_name,is_train=False):
            predict1 = self.session.run([self.o1],feed_dict={
                self.x1_value:x1_value,
                self.x1_index:x1_index
            })
            # print(len(predict1))
            # print(len(predict1[0]))
            # print(true_labels.shape)
            for i in range(len(predict1[0])):
                result_list.append((true_labels[i][0],predict1[0][i]))
            print(len(result_list))
        with open("./data/result.txt",w) as file1:
            for tp in result_list:
                file1.write(str(tp[0])+","+str(tp[1][0])+"\n")

    def save(self,sess,path):
        saver = tf.train.Saver()
        saver.save(sess,save_path=path)

    def restore(self,sess,path):
        saver = tf.train.Saver()
        saver.restore(sess,save_path=path)

    def train(self,train_data_file):
        index=0
        for x1_index,x1_value,true_labels in self.load_data(train_data_file):#ids_1,ids_2,weight_1,weight_2,
            if(len(true_labels)<2):
                #print("###$$$$$$ : "+str(len(true_labels)))
                continue
            my_o1,myerror,_=self.session.run([self.o1,self.error,self.opt],feed_dict={
                self.x1_index : x1_index,
                self.x1_value : x1_value,
                self.labels:true_labels
            })
            index+=1
            # if(index%1000==0):
            #     for i in range(len(my_o1)):
            #         print(str(my_o1[i])+" : "+str(true_labels[i]))
            #y_t = true_labels.reshape([-1])
            #y_p = np.asarray(my_o1,dtype=float).reshape([-1])
            print(metrics.roc_auc_score(true_labels,my_o1))

            #print(my_o1)

        self.save(self.session,self.model_path)

        self.session.close()

    def load_data(self,file_name,epoch=3,is_train=True):
        def __parse_line(line):
            tokens = line.split("#")[0].split()
            assert len(tokens)>=2, "Ill-formatted line: {}".format(line)
            label = float(tokens[0])
            uid = tokens[1]
            mid = tokens[2]
            kv_pairs = [kv.split(":") for kv in tokens[3:]]
            features = {k: float(v) for (k,v) in kv_pairs}
            #print(type(features))
            qid = uid
            return qid,features,label

        def __encoder_line(sample):
            qid = sample[0]
            features = sample[1]
            label = sample[2]
            features_arr = []
            for key in features.keys():
                features_arr.append(str(key)+":"+str(features[key]))
            return str(label)+" "+"qid:"+str(qid)+" "+" ".join(features_arr)

        def __gen_sparse_tensor(sample_list):
            # 生成batch_size数据
            # 根据sample_pair_list生成一个batch_size的训练样本
            sample_index = 0
            tensor_x1_index_ids = []
            tensor_x1_index_value = []

            tensor_x1_value_ids = []
            tensor_x1_value_values = []
            label_list = []
            for sample in sample_list:
                x1_feature = sample[0]
                label_list.append([float(sample[1])])
                tmpIndex = 0
                for key in x1_feature.keys():
                    tensor_x1_index_ids.append([sample_index, tmpIndex])
                    tensor_x1_index_value.append(int(key))

                    tensor_x1_value_ids.append([sample_index, tmpIndex])
                    tensor_x1_value_values.append(float(x1_feature[key]))
                    tmpIndex += 1
                sample_index+=1
            x1_index = tf.SparseTensorValue(indices=tensor_x1_index_ids,values=tensor_x1_index_value,
                                       dense_shape=[len(sample_list),self.feature_sizes])
            x1_value = tf.SparseTensorValue(indices=tensor_x1_value_ids,values=tensor_x1_value_values,
                                       dense_shape=[len(sample_list),self.feature_sizes])
            #print("AHAHAHAHA : "+str(len(sample_list)))
            return x1_index,x1_value,np.asarray(label_list,dtype=np.float32)

        def __gen_train_data(file_name):
            new_file_name  = file_name+"_train_data"
            with open(file_name,r) as filer:
                with open(new_file_name,w) as filew:
                    sample_list = []
                    now_qid = None
                    for l in filer:
                        qid, features, label = __parse_line(l)
                        if now_qid is None or now_qid==qid:
                            now_qid = qid
                            sample_list.append((qid,features,label))
                        else:
                            sorted_sample_list = sorted(sample_list,key=lambda x:x[2],reverse=True)
                            for sample in sorted_sample_list:
                                sample_str = __encoder_line(sample)
                                filew.write(sample_str+"\n")
                            sample_list = []
                            now_qid = qid
                            sample_list.append((qid, features, label))

            return new_file_name

        if is_train:
            new_file_name ="./data/new_final_train_data.txt" # __gen_train_data(file_name)
            print("process data")
            sample_list = []
            while epoch>0:
                epoch-=1
                with open(new_file_name,r) as filer:
                    for l in filer:
                        qid,features,label = __parse_line(l)
                        #print(len(sample_list))
                        if len(sample_list)<self.batch_size*10:
                            sample_list.append((features,label))
                        else:
                            random.shuffle(sample_list)
                            start = 0
                            end = len(sample_list)
                            while (start < end):
                                tmpEnd = min(end, start + self.batch_size)
                                sub_list = sample_list[start:tmpEnd]
                                x1_index, x1_value,labels = __gen_sparse_tensor(sub_list)  # ids_1,ids_2,weight_1,weight_2,
                                if(labels.sum()<1):
                                    start += self.batch_size
                                    continue
                                yield (x1_index, x1_value,labels)  # ids_1,ids_2,weight_1,weight_2,
                                start += self.batch_size
                            sample_list = []
                            sample_list.append((features, label))
        else:
            with open(file_name, r) as filer:
                sample_list = []
                for l in filer:
                    qid, features, label = __parse_line(l)
                    # print(len(sample_list))
                    if len(sample_list) < self.batch_size:
                        sample_list.append((features, label))
                    else:
                        start = 0
                        end = len(sample_list)
                        while (start < end):
                            tmpEnd = min(end, start + self.batch_size)
                            sub_list = sample_list[start:tmpEnd]
                            x1_index, x1_value, labels = __gen_sparse_tensor(sub_list)  # ids_1,ids_2,weight_1,weight_2,
                            yield (x1_index, x1_value, labels)  # ids_1,ids_2,weight_1,weight_2,
                            start += self.batch_size
                        sample_list = []
                        sample_list.append((features, label))


if __name__ =="__main__":
    fm = FMmodel()
    fm.build_model(is_warm_up=True)
    #fm.train("./data/new_final_train_data.txt")
    fm.predict("./data/test.data")

 

以上是关于TensorFlow实现FM的主要内容,如果未能解决你的问题,请参考以下文章

19推荐系统12FM——TensorFlow2实现

19推荐系统13FNN——TensorFlow2实现

6.4 tensorflow2实现FNN推荐系统——Python实战(第二篇)

6.3 tensorflow2实现FNN推荐系统——Python实战

将libFM模型变换成tensorflow可serving的形式

手写数字识别——基于全连接层和MNIST数据集