deepFM代码TensorFlow版阅读笔记

更新时间:2020-05-13 10:53:46点击次数:327次
1、fit

#dfm = DeepFM(**dfm_params)
dfm.fit(Xi_train_, Xv_train_, y_train_, Xi_valid_, Xv_valid_, y_valid_)
1
2
输入:
训练集:
Xi_train_存放的是特征对应的索引
Xv_train_ 存放的是特征的具体的值
y_train_是label
验证集 Xi_valid_, Xv_valid_, y_valid_

功能:feed数据,并训练

具体实现:
epoch 从0 到 设置值:

①shuffle
以相同的规律打乱Xi_train, Xv_train, y_train

②将所有数据按照batch大小feed_dict
通过feed_dict喂入每一个batch的数据,进行训练和传播

③评估训练集 测试集,并保存auc

④refit fit 训练集和测试集

def fit(self, Xi_train, Xv_train, y_train,
            Xi_valid=None, Xv_valid=None, y_valid=None,
            early_stopping=False, refit=False):
            
        has_valid = Xv_valid is not None
        for epoch in range(self.epoch):
            t1 = time()
            self.shuffle_in_unison_scary(Xi_train, Xv_train, y_train)
            total_batch = int(len(y_train) / self.batch_size)
            for i in range(total_batch):
                #获取一个batch大小数据
                Xi_batch, Xv_batch, y_batch = self.get_batch(Xi_train, Xv_train, y_train, self.batch_size, i)
                #通过feed_dict喂入每一个batch的数据,进行训练和传播
                self.fit_on_batch(Xi_batch, Xv_batch, y_batch)

            # evaluate training and validation datasets #评估训练集、验证集
            #训练集预测结果 auc
            train_result = self.evaluate(Xi_train, Xv_train, y_train) 
            self.train_result.append(train_result)
            if has_valid:
                #测试集预测结果 auc
                valid_result = self.evaluate(Xi_valid, Xv_valid, y_valid)
                self.valid_result.append(valid_result)
            
            #verbose=True、False 是否输出中中间的过程(debug日志)
            if self.verbose > 0 and epoch % self.verbose == 0:
                if has_valid:
                    print("[%d] train-result=%.4f, valid-result=%.4f [%.1f s]"
                        % (epoch + 1, train_result, valid_result, time() - t1))
                else:
                    print("[%d] train-result=%.4f [%.1f s]"
                        % (epoch + 1, train_result, time() - t1))
                    
            #有验证集,并且early_stopping设置为True,并且valid_result auc 后面无个递增、递减,就跳出(过早停止)
            if has_valid and early_stopping and self.training_termination(self.valid_result):
                break

                
        # fit a few more epoch on train+valid until result reaches the best_train_score
        if has_valid and refit: #refit fit 训练集和测试集
            #获取最佳epoch 和 最佳auc
            if self.greater_is_better:
                best_valid_score = max(self.valid_result)
            else:
                best_valid_score = min(self.valid_result)
            best_epoch = self.valid_result.index(best_valid_score)
            best_train_score = self.train_result[best_epoch]
            
            Xi_train = Xi_train + Xi_valid
            Xv_train = Xv_train + Xv_valid
            y_train = y_train + y_valid
            for epoch in range(100):
                self.shuffle_in_unison_scary(Xi_train, Xv_train, y_train)
                total_batch = int(len(y_train) / self.batch_size)
                for i in range(total_batch):
                    Xi_batch, Xv_batch, y_batch = self.get_batch(Xi_train, Xv_train, y_train,
                                                                self.batch_size, i)
                    self.fit_on_batch(Xi_batch, Xv_batch, y_batch)
                # check
                train_result = self.evaluate(Xi_train, Xv_train, y_train)
                if abs(train_result - best_train_score) < 0.001 or \
                    (self.greater_is_better and train_result > best_train_score) or \
                    ((not self.greater_is_better) and train_result < best_train_score):
                    break

2、evaluate 代码如下

def evaluate(self, Xi, Xv, y):
        """
        :param Xi: list of list of feature indices of each sample in the dataset
        :param Xv: list of list of feature values of each sample in the dataset
        :param y: label of each sample in the dataset
        :return: metric of the evaluation
        """
        y_pred = self.predict(Xi, Xv)
        #eval_metric=roc_auc_score
        return self.eval_metric(y, y_pred)

3、predict

def predict(self, Xi, Xv):
        """
        :param Xi: list of list of feature indices of each sample in the dataset
        :param Xv: list of list of feature values of each sample in the dataset
        :return: predicted probability of each sample
        """
        # dummy y
        dummy_y = [1] * len(Xi)
        batch_index = 0
        Xi_batch, Xv_batch, y_batch = self.get_batch(Xi, Xv, dummy_y, self.batch_size, batch_index)
        y_pred = None
        while len(Xi_batch) > 0:
            num_batch = len(y_batch)
            feed_dict = {self.feat_index: Xi_batch,
                         self.feat_value: Xv_batch,
                         self.label: y_batch,
                         self.dropout_keep_fm: [1.0] * len(self.dropout_fm),
                         self.dropout_keep_deep: [1.0] * len(self.dropout_deep),
                         self.train_phase: False}
            batch_out = self.sess.run(self.out, feed_dict=feed_dict)

            if batch_index == 0:
                y_pred = np.reshape(batch_out, (num_batch,))
            else:
                #concatenate axis=0 默认一维数组拼接
                y_pred = np.concatenate((y_pred, np.reshape(batch_out, (num_batch,))))

            batch_index += 1
            Xi_batch, Xv_batch, y_batch = self.get_batch(Xi, Xv, dummy_y, self.batch_size, batch_index)

        return y_pred

4、predict训练集验证集,计算gino_norm 和 每epoch训练集和测试集的auc

        #验证集上y_pred
        y_train_meta[valid_idx,0] = dfm.predict(Xi_valid_, Xv_valid_)
        #测试集上y_pred
        y_test_meta[:,0] += dfm.predict(Xi_test, Xv_test)

        #gini_norm评估函数 gini_norm(真实值, 预测值)
        gini_results_cv[i] = gini_norm(y_valid_, y_train_meta[valid_idx])
        gini_results_epoch_train[i] = dfm.train_result #训练集auc
        gini_results_epoch_valid[i] = dfm.valid_result #测试集auc

gini_norm计算如下:

import numpy as np

#是将预测概率从小到大排的,所以我们希望实际值中的0尽可能出现在前面,因此Normalization的Gini系数越大,分类效果越好。

def gini(actual, pred):
    assert (len(actual) == len(pred))
    #actual、pred、索引号(都是一维的),变成三列。
    #all的大小为 len(actual)行3列, 第一列是真实值、第二列是预测值、第三列为索引号
    all = np.asarray(np.c_[actual, pred, np.arange(len(actual))], dtype=np.float)
    #lexsort对多个序列进行排序;排序时优先照顾靠后的列。先对最后一列排序
    #all 预测值从大到小排序,索引号列从小到大排序
    all = all[np.lexsort((all[:, 2], -1 * all[:, 1]))]
    totalLosses = all[:, 0].sum() #正样本的个数
    giniSum = all[:, 0].cumsum().sum() / totalLosses

    giniSum -= (len(actual) + 1) / 2.
    return giniSum / len(actual)

def gini_norm(actual, pred):
    return gini(actual, pred) / gini(actual, actual)

例子:

pred = [0.9, 0.3, 0.8, 0.75, 0.65, 0.6, 0.78, 0.7, 0.05, 0.4, 0.4, 0.05, 0.5, 0.1, 0.1]
actual = [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
all = np.c_[actual, pred, np.arange(len(actual))]
all = all[np.lexsort((all[:, 2], -1 * all[:, 1]))]
totalLosses = all[:, 0].sum()
giniSum = all[:, 0].cumsum().sum() / totalLosses
giniSum -= (len(actual) + 1) / 2.
print(giniSum / len(actual))
#0.18888888888888894


橙色区域的面积,就是我们得到的Normalization的Gini系数。

这里,由于我们是将预测概率从小到大排的,所以我们希望实际值中的0尽可能出现在前面,因此Normalization的Gini系数越大,分类效果越好。

5、保存预测结果到文件
文件内容如下:


# save result
    if dfm_params["use_fm"] and dfm_params["use_deep"]:
        clf_str = "DeepFM"
    elif dfm_params["use_fm"]:
        clf_str = "FM"
    elif dfm_params["use_deep"]:
        clf_str = "DNN"
    #gini_norm的均值,方差
    print("%s: %.5f (%.5f)"%(clf_str, gini_results_cv.mean(), gini_results_cv.std()))
    filename = "%s_Mean%.5f_Std%.5f.csv"%(clf_str, gini_results_cv.mean(), gini_results_cv.std())
    #将测试集上的预测结果存到文件 id  y_pred
    _make_submission(ids_test, y_test_meta, filename)

def _make_submission(ids, y_pred, filename="submission.csv"):
    #保存,预测结果
    pd.DataFrame({"id": ids, "target": y_pred.flatten()}).to_csv(
        os.path.join(config.SUB_DIR, filename), index=False, float_format="%.5f")
1
2
3
4
6、将epoch的训练集和测试集上的gini存到fig中

fig文件夹下,可以看到三张图片

_plot_fig(gini_results_epoch_train, gini_results_epoch_valid, clf_str)
1
def _plot_fig(train_results, valid_results, model_name):
    colors = ["red", "blue", "green"]
    xs = np.arange(1, train_results.shape[1]+1)
    plt.figure()
    legends = []
    for i in range(train_results.shape[0]):
        plt.plot(xs, train_results[i], color=colors[i], linestyle="solid", marker="o")
        plt.plot(xs, valid_results[i], color=colors[i], linestyle="dashed", marker="o")
        legends.append("train-%d"%(i+1))
        legends.append("valid-%d"%(i+1))
    plt.xlabel("Epoch")
    plt.ylabel("Normalized Gini")
    plt.title("%s"%model_name)
    plt.legend(legends)
    plt.savefig("./fig/%s.png"%model_name)
    plt.close()

本站文章版权归原作者及原出处所有 。内容为作者个人观点, 并不代表本站赞同其观点和对其真实性负责,本站只提供参考并不构成任何投资及应用建议。本站是一个个人学习交流的平台,网站上部分文章为转载,并不用于任何商业目的,我们已经尽可能的对作者和来源进行了通告,但是能力有限或疏忽,造成漏登,请及时联系我们,我们将根据著作权人的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。

  • 项目经理 点击这里给我发消息
  • 项目经理 点击这里给我发消息
  • 项目经理 点击这里给我发消息
  • 项目经理 点击这里给我发消息