一、數據集
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
