Skip to content

Latest commit

 

History

History
289 lines (217 loc) · 8.25 KB

File metadata and controls

289 lines (217 loc) · 8.25 KB

数据及处理

数据集介绍

数据集使用 csv 格式存储,其中各个字段内容如下:

  • id : ad identifier
  • click : 0/1 for non-click/click
  • hour : format is YYMMDDHH, so 14091123 means 23:00 on Sept. 11, 2014 UTC.
  • C1 : anonymized categorical variable
  • banner_pos
  • site_id
  • site_domain
  • site_category
  • app_id
  • app_domain
  • app_category
  • device_id
  • device_ip
  • device_model
  • device_type
  • device_conn_type
  • C14-C21 : anonymized categorical variables

特征提取

下面我们会简单演示几种特征的提取方式。

原始数据中的特征可以分为以下几类:

  1. ID 类特征(稀疏,数量多)
  • id
  • site_id
  • app_id
  • device_id
  1. 类别类特征(稀疏,但数量有限)
  • C1
  • site_category
  • device_type
  • C14-C21
  1. 数值型特征转化为类别型特征
  • hour (可以转化成数值,也可以按小时为单位转化为类别)

类别类特征

类别类特征的提取方法有以下两种:

  1. One-hot 表示作为特征
  2. 类似词向量,用一个 Embedding 将每个类别映射到对应的向量

ID 类特征

ID 类特征的特点是稀疏数据,但量比较大,直接使用 One-hot 表示时维度过大。

一般会作如下处理:

  1. 确定表示的最大维度 N
  2. newid = id % N
  3. 用 newid 作为类别类特征使用

上面的方法尽管存在一定的碰撞概率,但能够处理任意数量的 ID 特征,并保留一定的效果[2]。

数值型特征

一般会做如下处理:

  • 归一化,直接作为特征输入模型
  • 用区间分割处理成类别类特征,稀疏化表示,模糊细微上的差别

特征处理

类别型特征

类别型特征有有限多种值,在模型中,我们一般使用 Embedding将每种值映射为连续值的向量。

这种特征在输入到模型时,一般使用 One-hot 表示,相关处理方法如下:

class CategoryFeatureGenerator(object):
    '''
    Generator category features.

    Register all records by calling ~register~ first, then call ~gen~ to generate
    one-hot representation for a record.
    '''

    def __init__(self):
        self.dic = {'unk': 0}
        self.counter = 1

    def register(self, key):
        '''
        Register record.
        '''
        if key not in self.dic:
            self.dic[key] = self.counter
            self.counter += 1

    def size(self):
        return len(self.dic)

    def gen(self, key):
        '''
        Generate one-hot representation for a record.
        '''
        if key not in self.dic:
            res = self.dic['unk']
        else:
            res = self.dic[key]
        return [res]

    def __repr__(self):
        return '<CategoryFeatureGenerator %d>' % len(self.dic)

CategoryFeatureGenerator 需要先扫描数据集,得到该类别对应的项集合,之后才能开始生成特征。

我们的实验数据集[3]已经经过shuffle,可以扫描前面一定数目的记录来近似总的类别项集合(等价于随机抽样), 对于没有抽样上的低频类别项,可以用一个 UNK 的特殊值表示。

fields = {}
for key in categorial_features:
    fields[key] = CategoryFeatureGenerator()

def detect_dataset(path, topn, id_fea_space=10000):
    '''
    Parse the first `topn` records to collect meta information of this dataset.

    NOTE the records should be randomly shuffled first.
    '''
    # create categorical statis objects.

    with open(path, 'rb') as csvfile:
        reader = csv.DictReader(csvfile)
        for row_id, row in enumerate(reader):
            if row_id > topn:
                break

            for key in categorial_features:
                fields[key].register(row[key])

CategoryFeatureGenerator 在注册得到数据集中对应类别信息后,可以对相应记录生成对应的特征表示:

record = []
for key in categorial_features:
    record.append(fields[key].gen(row[key]))

本任务中,类别类特征会输入到 DNN 中使用。

ID 类特征

ID 类特征代稀疏值,且值的空间很大的情况,一般用模操作规约到一个有限空间, 之后可以当成类别类特征使用,这里我们会将 ID 类特征输入到 LR 模型中使用。

class IDfeatureGenerator(object):
    def __init__(self, max_dim):
        '''
        @max_dim: int
            Size of the id elements' space
        '''
        self.max_dim = max_dim

    def gen(self, key):
        '''
        Generate one-hot representation for records
        '''
        return [hash(key) % self.max_dim]

    def size(self):
        return self.max_dim

IDfeatureGenerator 不需要预先初始化,可以直接生成特征,比如

record = []
for key in id_features:
    if 'cross' not in key:
        record.append(fields[key].gen(row[key]))

交叉类特征

LR 模型作为 Wide & Deep model 的 wide 部分,可以输入很 wide 的数据(特征空间的维度很大), 为了充分利用这个优势,我们将演示交叉组合特征构建成更大维度特征的情况,之后塞入到模型中训练。

这里我们依旧使用模操作来约束最终组合出的特征空间的大小,具体实现是直接在 IDfeatureGenerator 中添加一个 gen_cross_feature 的方法:

def gen_cross_fea(self, fea1, fea2):
    key = str(fea1) + str(fea2)
    return self.gen(key)

比如,我们觉得原始数据中, device_idsite_id 有一些关联(比如某个 device 倾向于浏览特定 site), 我们通过组合出两者组合来捕捉这类信息。

fea0 = fields[key].cross_fea0
fea1 = fields[key].cross_fea1
record.append(
    fields[key].gen_cross_fea(row[fea0], row[fea1]))

特征维度

Deep submodel(DNN)特征

feature dimention
app_category 21
site_category 22
device_conn_type 5
hour 24
banner_pos 7
Total 79

Wide submodel(LR)特征

Feature Dimention
id 10000
site_id 10000
app_id 10000
device_id 10000
device_id X site_id 1000000
Total 1,040,000

输入到 PaddlePaddle 中

Deep 和 Wide 两部分均以 sparse_binary_vector 的格式 [1] 输入,输入前需要将相关特征拼合,模型最终只接受 3 个 input, 分别是

  1. dnn input ,DNN 的输入
  2. lr input , LR 的输入
  3. click , 标签

拼合特征的方法:

def concat_sparse_vectors(inputs, dims):
    '''
    concaterate sparse vectors into one

    @inputs: list
        list of sparse vector
    @dims: list of int
        dimention of each sparse vector
    '''
    res = []
    assert len(inputs) == len(dims)
    start = 0
    for no, vec in enumerate(inputs):
        for v in vec:
            res.append(v + start)
        start += dims[no]
    return res

生成最终特征的代码如下:

# dimentions of the features
categorial_dims = [
    feature_dims[key] for key in categorial_features + ['hour']
]
id_dims = [feature_dims[key] for key in id_features]

dense_input = concat_sparse_vectors(record, categorial_dims)
sparse_input = concat_sparse_vectors(record, id_dims)

record = [dense_input, sparse_input]
record.append(list((int(row['click']), )))
yield record

参考文献

  1. https://github.com/PaddlePaddle/Paddle/blob/develop/doc/api/v1/data_provider/pydataprovider2_en.rst
  2. Mikolov T, Deoras A, Povey D, et al. Strategies for training large scale neural network language models[C]//Automatic Speech Recognition and Understanding (ASRU), 2011 IEEE Workshop on. IEEE, 2011: 196-201.
  3. https://www.kaggle.com/c/avazu-ctr-prediction/data