DeepFM(2) 数据集处理

一、数据集

Kaggle的一个竞赛数据集:
Preprocess Criteo dataset. This dataset was used for the Display Advertising
Challenge (https://www.kaggle.com/c/criteo-display-ad-challenge).

二、数据集描述

第1列就是标签(点击与否用0/1表示,1为点击,0为没点击)。后面的2-40列数据(共39列)是我们的特征变量(指标),也就是原文paper里面提到的“n-field”的概念,这里的n是39。
然后数据内又分了前13列是连续型变量,后26列是类别型变量。

三、训练集生成

1、连续变量和类别变量特征:[1,13],[14,39]。

continous_features = range(1, 14) # [1,...13]
categorial_features = range(14, 40) # [14,...39]

将39列的每一列做为独立的输入之后再合并,所以根据每一列建立索引字典。建立好之后根据索引字典将原始数据(上面的数据视图呈现那样)映射到数字空间,即每个值都代表着索引字典里面的索引,可以根据索引找到原来的值。

2、feature_sizes:每个特征的长度,用于构建索引。

首先要知道每个类别特征的特征个数。连续特征的特征个数为1。因为在特征索引中,不同的连续值对应一个特征位,类别特征每种情况对应一个特征位。
1、类别特征维度统计

categorial_features = [14,...39]
num_feature = len(categorial_features)
dicts = []    
for i in range(0, num_feature):
    dicts.append(collections.defaultdict(int)) # defaultdict(int) 如果key不存在的时候,则赋值为int类型,值0。

with open('train.txt', 'r') as f: # 读取文件,遍历行,针对所有类别特征,如果有值。则以该值为key的字典的value加1。
     for line in f:
         features = line.rstrip('\n').split('\t')
         for i in range(0, num_feature):
             if features[categorial_features[i]] != '':
                 dicts[i][features[categorial_features[i]]] += 1

得到每个类别特征的个数统计之后,进行后续处理。dicts[i]表示第i个类别特征的统计字典。

for i in range(0, num_feature):
    dicts[i] = filter(lambda x: x[1] >= 2,
                                  dicts[i].items())  # 过滤掉只出现过一次的类别特征。
    dicts[i] = sorted(dicts[i], key=lambda x: (-x[1], x[0])) ## 第二个元素的负数, 如果第二个元素相等,则基于第1个元素。皆为正序
    vocabs, _ = list(zip(*dicts[i])) # vocabs:tuple. 第i种类别特征的所有情况组成的元组。
    dicts[i] = dict(zip(vocabs, range(1, len(vocabs) + 1))) # 得到 {特征:索引}的字典。索引从1开始。
    dicts[i]['<unk>'] = 0 # 赋值未知特征为0,即出现次数少于2次的当做0处理。

dict_size = [len(self.dicts[idx]) for idx in range(0, self.num_feature)] # 26个类别特征,每个特征包含多少个不同的类别。
sizes = [1] * len(continous_features) + dict_sizes
sizes = [str(i) for i in sizes] # sizes 保存了所有特征的维度。
feature_sizes.write(','.join(sizes)) # 保存为feature_sizes.txt。

四、构建feature_index 和 feature_value

前面得到了特征长度,可以构建最后的特征索引和特征值。根据每一列建立索引字典。

with open('train.txt', 'r') as f:
    for line in f:
        features = line.rstrip('\n').split('\t') # 同样是首先得到原始数据的特征列表。
        continous_vals = [] # 连续特征转换之后的值
        for i in range(0, len(continous_features)):       
            val = features[continous_features[i]] # 第i个连续特征的原始值
            if val == '': # 原始数据是有存在一些缺少值的,对缺失值采取的手段是填0处理
                value= 0.0
            else:
                val = float(val)
            continous_vals.append("{0:.6f}".format(val).rstrip('0')
                                    .rstrip('.'))
        categorial_vals = [] # 类别特征转换之后的值,其值为索引。
        for i in range(0, len(categorial_features)):
            key = features[categorial_features[i]] # 第i个类别特征的原始值
            if key not in dicts[i]:
               res = dicts[i]['<unk>']
            else:
               res = dicts[i][key]
           
            categorial_vals.append(str(res)) # 类别特征转换后的值为其每个类别中的索引。

        continous_vals = ','.join(continous_vals)
        categorial_vals = ','.join(categorial_vals)
        label = features[0]
        out_train.write(','.join([continous_vals, categorial_vals, label]) + '\n') # 保存起来。

总结

最终得到了两个文件
1、feature_sizes.txt
每个特征的维度:连续特征都为1,类别特征为其类别数,相当于每个field的特征数,用于初始化deepfm模型。

1,1,1,1,1,1,1,1,1,1,1,1,1,10,22,7,6,7,7,3,9,3,5,6,7,7,8,5,7,10,14,4,4,7,3,9,8,11,5

2、train.txt
每行长度为\(13+26+1=40\),13个连续特征为其原始值,26个类别特征,为其类别索引。最后一位为label。

1,1,5,0,1382,4,15,2,181,1,2,0,2,2,3,0,0,1,1,0,3,1,0,0,0,0,3,0,0,1,5,1,3,0,0,2,0,1,0,0
2,0,44,1,102,8,2,2,4,1,1,0,4,2,14,0,0,1,3,0,1,1,0,0,0,0,1,0,0,2,0,1,2,0,0,2,0,1,0,1
posted @ 2021-03-31 11:58  木叶流云  阅读(835)  评论(0编辑  收藏  举报