3Idiots-2014-Kaggle 比赛源码走读

3Idiots-2014-Kaggle比赛源码走读

  最近在研究ffm,自然要找回3Idiots在2014年kaggle比赛的解决方案。主要是研究他们的特征提取方案,写下此文记录走读大佬代码的过程,目的是方便以后查询,免得忘记。

3Idiots代码下载

git clone https://github.com/guestwalk/kaggle-2014-criteo.git

特征取值统计

  utils/count.py对训练集tr.csv 26个离散型特征(C1~C26)各自不同的取值进行了统计,包括每个值出现次数(Total)、每个值对应的label为0和1的次数(Neg和Pos),写入fc.trva.t10.txt

列名如下

Field,Value,Neg,Pos,Total,Ratio

GBDT特征准备

  converters/pre-a.py将训练集tr.csv转换成gbdt需要的数据格式。分成两部分,数值类特征(I1~I13)被转到tr.gbdt.dense,离散型特征(C1~C26)作者设置了26个特殊的取值,然后遍历csv的每一行,找出每一行中包含的特殊取值,把他们的序号(0~25)写到tr.gbdt.sparse.

数值类特征(I1~I13)处理部分如下,可见仅做了null值补全而已。

...
for j in range(1, 14):
    val = row['I{0}'.format(j)]
    if val == '':
        val = -10 
    feats.append('{0}'.format(val))
f_d.write(row['Label'] + ' ' + ' '.join(feats) + '\n')
...

离散型特征(C1~C26)处理如下,首先各行全部处理成field-value,例如C9-a73ee510这样,然后逐行找出target_cat_feats中包含的field-value,将他们的index写入tr.gbdt.sparse

#These features are dense enough (they appear in the dataset more than 4 million times), so we include them in GBDT
target_cat_feats = ['C9-a73ee510', 'C22-', 'C17-e5ba7672', 
                    'C26-', 'C23-32c7478e', 'C6-7e0ccccf', 
                    'C14-b28479f6', 'C19-21ddcdc9', 'C14-07d13a8f', 
                    'C10-3b08e48b', 'C6-fbad5c96', 'C23-3a171ecb', 
                    'C20-b1252a9d', 'C20-5840adea', 'C6-fe6b92e5', 
                    'C20-a458ea53', 'C14-1adce6ef', 'C25-001f3601', 
                    'C22-ad3062eb', 'C17-07c540c4', 'C6-', 
                    'C23-423fab69', 'C17-d4bb7bd8', 'C2-38a947a1', 
                    'C25-e8b83407', 'C9-7cc72ec2']

...
cat_feats = set()
for j in range(1, 27):
    field = 'C{0}'.format(j)
    key = field + '-' + row[field]
    cat_feats.add(key)

feats = []
for j, feat in enumerate(target_cat_feats, start=1):
    if feat in cat_feats:
        feats.append(str(j))
f_s.write(row['Label'] + ' ' + ' '.join(feats) + '\n')
...

GBDT训练

  solvers/gbdt/gbdt接收tr.gbdt.densetr.gbdt.sparsete.gbdt.densete.gbdt.sparse,训练好模型,然后输出叶子结点编号到te.gbdt.outtr.gbdt.out,都取30棵树,最深层数是7,因此一棵树叶子编号最多是 2 7

ffm特征准备

  ffm用到的特征包括三个方面:数值类特征(I1~I13)、离散型特征(C1~C26)和GBDT输出的叶子结点。

  • 数值类特征 numeric features (13)
    数值类特征(I1~I13),大于2的数值进行转化:
    v log ( v ) 2

common.py/gen_feats函数对数值型特征(I1~I13)的处理:

for j in range(1, 14):
    field = 'I{0}'.format(j)
    value = row[field]
    if value != '':
        value = int(value)
        if value > 2:
            value = int(math.log(float(value))**2)
        else:
            value = 'SP'+str(value)
    key = field + '-' + str(value)
    feats.append(key)
  • 离散型特征 categorical features (26)
    首先read_freqent_feats函数读取fc.trva.t10.txt存储出现次数大于10次的离散型特征取值(以C3-xxxx这样的形式)
    离散型特征(C1~C26),出现次数少于10次的,编码成同一个特殊值{field}less,否则保持原值。

converters/pre-b.py对离散值特征的处理:

frequent_feats = read_freqent_feats(args['threshold']) # 10
...
for feat in gen_feats(row):
    field = feat.split('-')[0]
    type, field = field[0], int(field[1:])
    # 离散特征,出现次数少于10次的
    if type == 'C' and feat not in frequent_feats:
        feat = feat.split('-')[0]+'less' # 可见将编码成同一值
    if type == 'C':
        field += 13
    feats.append((field, feat))
  • GBDT特征 (30)
    直接使用.
for i, feat in enumerate(line_gbdt.strip().split()[1:], start=1):
    field = i + 39
    feats.append((field, str(i)+":"+feat)) # e.g. 40:127

最终ffm格式的value总是取1,feature采用hash并取模的方式生成。

converters/pre-b.pygen_hashed_fm_feats的处理方式:

def gen_hashed_fm_feats(feats, nr_bins):
    # 可见value总是1,即使特征中有连续值
    feats = ['{0}:{1}:1'.format(field-1, hashstr(feat, nr_bins)) for (field, feat) in feats]
    return feats

参考资料

github代码 https://github.com/guestwalk/kaggle-2014-criteo/
3 Idiots’ solution PDF
libffm PPT
深入FFM原理与实践

猜你喜欢

转载自blog.csdn.net/songbinxu/article/details/80347656