特征工程系列:自动化特征构造

0x00 前言

数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限而已。由此可见, 特征工程在机器学习中占有相当重要的地位
。在实际应用当中,可以说特征工程是机器学习成功的关键。
那特征工程是什么?
特征工程是利用数据领域的相关知识来创建能够使机器学习算法达到最佳性能的特征的过程。
特征工程又包含了 Data PreProcessing(数据预处理)、Feature Extraction(特征提取)、Feature Selection(特征选择)和 Feature construction(特征构造)等子问题,本章内容主要讨论特征构造的方法。

创造新的特征是一件十分困难的事情,需要丰富的专业知识和大量的时间。机器学习应用的本质基本上就是特征工程。
——Andrew Ng

0x01 自动化特征构造介绍

目前,很多机器学习项目的模型选择开始转向自动化,而特征工程仍然主要以人工为主。自动化特征工程旨在通过从数据集中自动创建候选特征,且从中选择若干最佳特征进行训练的一种方式。自动化特征工程工具包有 Feature Tools 和 tsfresh 等,以下以 Feature Tools 为例进行说明。
Feature Tools 是执行自动化功能工程的框架。它擅长将时态和关系数据集转换为机器学习的特征矩阵。

  • 项目地址:https://docs.featuretools.com/
  • 代码地址:https://github.com/WillKoehrsen/automated-feature-engineering/blob/master/walk_through/Automated_Feature_Engineering.ipynb

Feature Tools 使用一种称为深度特征合成(Deep Feature Synthesis,DFS)的算法,该算法遍历通过关系数据库的模式描述的关系路径,深度特征合成叠加多个转换和聚合操作,这在特征工具的词库中被称为特征基元,以便通过分布在多张表内的数据来构造新的特征。与机器学习中的大多数方法一样,这是建立在简单概念基础之上的复杂方法。

0x02实体和实体集

特征工具的前两个概念的是「实体」和「实体集」。一个实体就是一张表(或是 Pandas 中的一个 DataFrame(数据框))。一个实体集是一组表以及它们之间的关联。将一个实体集看成另一种 Python 数据结构,并带有自己的方法和属性。

0x03表的关联

考虑两张表之间「关联」的最好方法是类比父子之间的关联。这是一种一对多的关联:每个父亲可以有多个儿子。对表来说,每个父亲对应一张父表中的一行,但是子表中可能有多行对应于同一张父表中的多个儿子。
例如,在我们的数据集中,clients 数据框是 loans 数据框的一张父表。每个客户只对应 clients 表中的一行,但是可能对应 loans 表中的多行。同样,loans 表是 payments 表的一张父表,因为每项贷款可以有多项支付。父亲通过共享变量与儿子相关联。当我们执行聚合操作的时候,我们根据父变量对子表进行分组,并计算每个父亲的儿子的统计量。

0x04特征基元

  • 聚合:根据父与子(一对多)的关联完成的操作,也就是根据父亲分组并计算儿子的统计量。一个例子就是根据 client_id 对 loan 表分组并找到每个客户的最大贷款额。

  • 转换:对一张表中一或多列完成的操作。一个例子就是取一张表中两列之间的差值或者取一列的绝对值。

在特征工具中单独使用这些基元或者叠加使用这些基元可以构造新的特征。以下是特征工具中一些特征基元的列表,也可以自定义特征基元。

0x05深度特征合成

深度特征只是叠加多个基元构造的一个特征,而 dfs 只是构造这些特征的过程的名称。深度特征的深度是构造这个特征所需的基元数量。

例如,MEAN( payments.payment_amount
)列是深度为 1 的特征,因为它是使用单个聚合操作构造的。LAST(loans(MEAN( payments.payment_amount
))是一个深度为 2 的特征,它是由两个叠加的聚合操作构造的:MEAN 列之上的 LAST(最近的)列。这表示每个客户最近的贷款平均支付额。

0x06实现程序

加载数据

import pandas as pd

import numpy as np

import featuretools as ft

import warnings

warnings.filterwarnings('ignore')


# 加载数据 clients = pd.read_csv('data/clients.csv', parse_dates = ['joined']) loans = pd.read_csv('data/loans.csv', parse_dates = ['loan_start', 'loan_end']) payments = pd.read_csv('data/payments.csv', parse_dates = ['payment_date'])

clients 数据:

loans 数据:

payments 数据:

创建实体和实体集

# 创建一个空的实体集

es = ft.EntitySet(id = 'clients')


#clients指定索引为client_id,时间索引为joined es = es.entity_from_dataframe(entity_id = 'clients', dataframe = clients, index = 'client_id', time_index = 'joined')
# payments建议一个索引payment_id,指定missed是一个类别特性,时间索引为payment_date。 es = es.entity_from_dataframe(entity_id = 'payments', dataframe = payments, variable_types = {'missed': ft.variable_types.Categorical}, make_index = True, index = 'payment_id', time_index = 'payment_date') # loans指定索引为loan_id,repaid是一个类别特性,时间索引为loan_start es = es.entity_from_dataframe(entity_id = 'loans', dataframe = loans, variable_types = {'repaid': ft.variable_types.Categorical}, index = 'loan_id', time_index = 'loan_start')

添加实体关系

# 通过client_id 关联clients和loans实体 r_client_previous = ft.Relationship(es['clients']['client_id'], es['loans']['client_id']) es = es.add_relationship(r_client_previous)
# 通过loan_id 关联payments和loans实体 r_payments = ft.Relationship(es['loans']['loan_id'], es['payments']['loan_id']) es = es.add_relationship(r_payments)

聚合特征,指定聚合和转换函数生成新特征

# 聚合特征,通过指定聚合agg_primitives和转换trans_primitives生成新特征 features, feature_names = ft.dfs(entityset = es, target_entity = 'clients', agg_primitives = ['mean', 'max', 'percent_true', 'last'], trans_primitives = ['years', 'month', 'subtract', 'divide'])

聚合特征,并生成新特征

除了手动指定聚合和转换特征基元之外,我们还可以让 featuretools 自动生成许多新功能。我们通过进行相同的 ft.dfs 函数调用来完成此操作,但不传入任何基元。我们只需设置 max_depth 参数, featuretools 将自动尝试许多特征基元的所有组合到有序深度。

#聚合特征,并生成新特征

features, feature_names = ft.dfs(entityset = es, target_entity = 'clients')

0x0FF 总结

1.特征构造

特征构造是一个非常耗时的过程,因为每个新的特征通常需要几步才能构造,特别是当使用多张表的信息时。我们可以将特征构造的操作分为两类:“转换”和“聚合”。
很多机器学习比赛都是直接给出了训练集(特征+类标),我们可以对给出的特征进行“转换”操作,构造更多的特征。而在实际的工作中,很多时候我们都没有现成的特征,需要自己进行“聚合”操作从多个原始数据表中构造出模型所需要的特征。
例如,用户行为数据表中每条记录为某个用户的一次浏览行为或一次点击行为,我们需要通过“聚合”操作构造出用户的行为特征(如:用户最近一次浏览的时长、用户最近一次登录的点击次数等特征),然后再使用“转换”操作来构造更多特征,最后再使用这些特征训练模型。

2.特征选择

经过各种方法构造特征,我们收获了大量特征,但同时带来了另一个问题:特征太多了。尽管在拟合一个模型之前很难说哪些特征是重要的,但很可能不是所有这些特征都与我们想要训练的模型的任务相关。此外,拥有太多特征可能会导致模型性能不佳,因为较无益的特征会淹没那些更重要的特征。
特征过多问题以维度灾难著称。随着特征数量的上升(数据维度增长),模型越来越难以学习特征与目标之间的映射关系。事实上,让模型表现良好所需的数据量与特征数量成指数关系。

至于如何进行特征选择,详情可参看 《特征工程系列:特征筛选的原理与实现(上)》
《特征工程系列:特征筛选的原理与实现(下)》

3.自动构造特征

即使是具有相当领域知识的人,在制作新功能时也会受到想象力的限制(更不用说时间)了。自动化特征工程不受这些因素的限制(而是受到计算时间的限制),并为特征创建提供了良好的起点。
这个过程不会完全消除人类对特征工程的贡献,因为人类仍然可以使用领域知识和机器学习专业知识来选择最重要的特征或从自动深度特征合成建议的那些构建新特征。

参考文献

[1] https://machinelearning-notes.readthedocs.io/zh_CN/latest/feature/%E7%89%B9%E5%BE%81%E5%B7%A5%E7%A8%8B%E2%80%94%E2%80%94%E6%97%B6%E9%97%B4.html
[2] https://www.cnblogs.com/nxf-rabbit75/p/11141944.html#_nav_12
[3] https://gplearn.readthedocs.io/en/stable/examples.html#symbolic-classifier
[4] 利用 gplearn 进行特征工程. https://bigquant.com/community/t/topic/120709
[5] Practical Lessons from Predicting Clicks on Ads at Facebook. https://pdfs.semanticscholar.org/daf9/ed5dc6c6bad5367d7fd8561527da30e9b8dd.pdf
[6] Feature Tools:可自动构造机器学习特征的Python库. https://www.jiqizhixin.com/articles/2018-06-21-2
[7] 各种聚类算法的系统介绍和比较. https://blog.csdn.net/abc200941410128/article/details/78541273