共计 8292 个字符,预计需要花费 21 分钟才能阅读完成。
1. 什么是机器学习的特色工程
一个实在场景的机器学习利用个别会蕴含两个主体流程,即 特色工程 和机器学习模型 (以下简称 模型)。大家对模型肯定很理解,平时也是接触的最多的,比方从经典的逻辑回归、决策树模型,到近几年大火的深度学习模型,都是聚焦于如何开发高质量的模型。对于特色工程,可能大家绝对关注较少。然而大家肯定据说过坊间风闻的一句”名言“:数据和特色决定了机器学习的下限,而模型和算法只是迫近这个下限而已。由此可见,对于特色工程的重要性大家早有共识。
一句话来定义特色工程:应用特定的畛域常识,从原始数据中抽取有用的特色信息。这里强调了特定的畛域常识(domain knowledge),也就是说特色抽取并不是一个标准化过程,而是基于不同的场景有不同的教训和方法论。举个简略的例子,对于实时举荐零碎来说,原始数据可能只是用户实时打入的搜寻关键字,如“洗衣机”,以及相应的寄存于数据库中的用户和商品数据表格。那么为了更好的进行实时举荐,能够思考如下更有意义的特色:
- 该用户过来一年购买最多的家电品牌
- 该用户过来三年在大家电类的生产上的均匀消费水平
- 过来一小时平台上打折力度 7 折以上,合乎该用户性别和年龄组的用户所购买量排名前三的洗衣机型号
通过下面的例子能够看到,特色能够做的相当简单,并且能够具备十分高的时效性。那么如何依据特定场景,抽取好的特色,这就是数据科学家须要的涵养,同时须要装备足够弱小的工具,能力做好特色工程。本教程抛砖引玉,来让大家意识如何在实践中做特色工程。
2. 特色工程开发利器 – OpenMLDB
工欲善必先利其器,在介绍特色工程算法之前,咱们先有必要来认识一下特色工程的开发和部署工具。依据教训,咱们粗略的把他们分类,并且总结了各自的优缺点。
开发工具 | 入门门槛 | 性能反对 | 工程化落地 |
---|---|---|---|
Python | 低,数据科学家广泛应用 | 性能实现较为自在,通过 Python 编程,根本能够实现任何运算脚本 | 较难。Python 程序运行广泛效率较低,能够进行离线开发,然而性能个别无奈满足线上实时要求,另外用户须要本人解决如高可用等生产级个性。 |
通用数据库 | 中,基于 SQL 进行开发 | 没有针对特色工程做优化,某些特色的实现较为顺当,或者实现当前运行效率较低。 | 可承受。然而对于反对不太好的特征函数,其性能或者性能上可能无奈满足生产环境需要。 |
混合开发,比方离线应用 Python,线上应用数据库或者 C++ | 十分高,须要两组技能栈团队开发保护 | 通过开发和肯定的定制化,能够满足性能需要。 | 能够落地,然而老本较高。除了开发经营老本以外,还须要解决线上线下一致性问题,保障离线和在线成果统一。 |
OpenMLDB | 中,基于 SQL 进行开发 | 针对特色工程优化,基于规范 SQL 进行扩大,高效反对特色工程罕用的计算方法。 | 可低成本高效落地。基于 SQL 开发,实现开发即上线,人造解决性能和线上线下一致性问题。 |
从下面的表格中总结能够看到,OpenMLDB 在性能和工程化落地方面都具备独特的劣势,特地对于实时性高的时序特色计算,OpenMLDB 有不少的针对性优化。如果心愿进一步理解 OpenMLDB,能够浏览相干 介绍文档,以及咱们的开源 GitHub 代码仓库:
https://github.com/4paradigm/OpenMLDBgithub.com/4paradigm/OpenMLDB
在本系列教程中,咱们将会基于 OpenMLDB 的 SQL 语法,来实际演示如何基于 SQL 开发特色工程脚本。你能够通过浏览咱们的文档 – OpenMLDB 疾速上手,来理解如何试用 OpenMLDB(举荐基于 docker 镜像,通过单机版来疾速试用);你也能够在这里找到咱们 残缺的产品阐明文档。
3. 从 0 到 1,特色工程实际
咱们将会分高低两篇零碎的介绍特色工程罕用的解决办法,本篇将会偏重单表特色解决,下一篇咱们将会聚焦更为简单的多表特色计算。本文应用在金融畛域广泛应用的反欺诈作为理论案例进行形容。
3.1. 基本概念
3.1.1. 主表和副表
主表 是特色抽取的主体数据表。直观上能够了解主表为带有模型训练所须要的标签(label)列的数据表格。在特色工程过程中,会对主表的每一行进行特色计算,最终生成对应的 特色宽表。例如,上面这张用户交易表(以下代指为数据表 t1),是本文所述案例的主表。
Field | Type | Description | |
---|---|---|---|
id | BIGINT | 样本 ID, 每一条样本领有惟一 ID | |
uid | STRING | 用户 ID | |
mid | STRING | 商户 ID | |
cardno | STRING | 卡号 | |
trans_time | TIMESTAMP | 交易工夫 | |
trans_amt | DOUBLE | 交易金额 | |
trans_type | STRING | 交易类型 | |
province | STRING | 省份 | |
city | STRING | 城市 | |
cardno | STRING | 卡号 | |
label | BOOL | 样本 label, true | false |
除了主表以外,数据库中可能还存在着存储相干辅助信息的数据表格,能够通过 join 操作和主表进行拼接,这些表格称为 副表(留神副表可能有多张)。比方咱们能够有一张副表存储着商户流水历史记录。在做特色工程过程中,把主表和副标的信息拼接起来,能够取得更为有价值的信息。
对于多表的特色工程,咱们将在本系列的下篇具体介绍,敬请期待。本篇文章介绍的特色工程均基于主表。
3.1.2. 特色分类
在深刻探讨特色构建细节之前,咱们须要对目前机器学习下罕用的特色进行分类,从构建特色数据集以及聚合形式上看,机器学习罕用的特色蕴含四种:
- 主表单行特色:对主表的一列或者多列进行表达式和函数加工计算。
- 主表窗口时序特色:对主表构建时序窗口,在窗口内进行时序特色加工。
- 副表单行特色:以后主表行从副表中匹配一条记录并拼接,而后对拼接后的数据行进行单行特色加工。
- 副表多行聚合特色:以后主表行从副表中匹配多条记录,对多条记录进行特色加工。
本文上篇将会着重介绍主表单行特色和主表窗口时序特色,稍后推出的下篇将会具体开展介绍副表单行特色以及副表多行聚合特色。
3.2. 主表单行特色
对以后主表行的一列或者多列进行表达式和函数加工计算。次要蕴含以下几种形式。
列直取
主表的某些列,间接就能够作为特色参加模型训练。
SELECT uid, trans_type FROM t1;
多列加工
主表的若干列能够通过表达式或者函数加工成一个特色,如以下例子把 province 和 city 拼接到一起成为一个字符串作为一个特色。
SELECT concat(province, city) as province_city FROM t1
单行函数加工
通过零碎内置函数或者 UDF 进行计算加工,如以下例子抽取了天、小时、分钟作为特色。
SELECT
day(trans_time) as f_trans_day,
hour(trans_time) as f_trans_hour,
minute(trans_time) as f_trans_minute FROM t1;
其余相干函数也包含数值特色计算(如 ceiling
)、字符串特色计算(如 substr
)等。
3.3. 主表窗口时序特色
在很多场景下,更为罕用的特色构建伎俩是基于时序窗口的特色构建,比方交易数据、用户行为等都是带有工夫戳的时序数据。结构主表窗口时序特色须要实现两个步骤:
- 步骤一:定义时序窗口
- 步骤二:在时序窗口上结构特色
3.3.1. 步骤一:定义时序窗口
咱们既能够通过工夫区间(如一个月),也能够通过窗口内的行数(如 100 条),去定义一个具体的时序窗口大小。时序窗口的最根本定义形式:
window window_name as (PARTITION BY partition_col ORDER BY order_col ROWS_RANGE|ROWS BETWEEN StartFrameBound AND EndFrameBound)
其中,最根本的不可或缺的语法元素包含:
PARTITION BY partition_col
: 示意窗口依照partition_col
列分组ORDER BY order_col
: 示意窗口依照order_col
列进行排序ROWS_RANGE
: 示意窗口按工夫滑动;ROWS
示意窗口类型是按条数滑动-
StartFrameBound
: 示意该窗口的上界。在 OpenMLDB 中,个别咱们能够定义窗口上界为:UNBOUNDED PRECEDING
: 无上界。time_expression PRECEDING
: 如果是工夫窗口,能够定义工夫偏移,如30d PRECEDING
示意窗口上界为 以后行的工夫 -30 天。number PRECEDING
: 如果是条数窗口,能够定义条数偏移。如,100 PRECEDING
示意窗口上界为的以后行的前 100 行。
-
EndFrameBound
: 示意该工夫窗口的下界。在 OpenMLDB 中,个别咱们能够定义窗口下界为:CURRENT ROW
:以后行time_expression PRECEDING
: 肯定的工夫偏移,如1d PRECEDING
。这示意窗口下界为 以后行的工夫 - 1 天。number PRECEDING
: 如果是条数窗口,能够定义条数偏移。如,1 PRECEDING
示意窗口上界为的以后行的前 1 行。
-
配置窗口上下界时,请留神:
- OpenMLDB 目前无奈反对以后行当前的工夫作为上界和下界。如
1d FOLLOWING
。换言之,咱们只能解决历史工夫窗口。这也根本满足大部分的特色工程的利用场景。 - OpenMLDB 的下界工夫必须 >= 上界工夫
- OpenMLDB 的下界条数必须 <= 上界条数
- OpenMLDB 目前无奈反对以后行当前的工夫作为上界和下界。如
更多语法和个性能够参考 OpenMLDB 窗口参考手册
时序窗口举例
对于后面所述的交易表 t1
,咱们定义两个工夫窗口和两个条数窗口。每一个样本行的窗口都是按用户 ID (uid
)分组,按交易工夫 (trans_time
)排序。相干的窗口举例如下:
- w1d: 用户最近一天的窗口
-- 用户最近一天的窗口, 蕴含以后行到最近 1 天以内的数据行 window w1d as (PARTITION BY uid ORDER BY trans_time ROWS_RANGE BETWEEN 1d PRECEDING AND CURRENT ROW)
样本 9 的 w1d 窗口蕴含了三行数据。别离是样本 6,8,9。这三条数据落在样本 9 的工夫窗口内[2022-02-07 12:00:00, 2022-02-08 12:00:00]。
- w1d_10d: 用户 1 天以前和最近 10 天的窗口
-- 用户 1d~10d 的窗口,蕴含 1 天以前,10 天以内的数据行 window w1d_10d as (PARTITION BY uid ORDER BY trans_time ROWS_RANGE BETWEEN 10d PRECEDING AND 1d PRECEDING)
样本 9 的 w1d_10d 窗口蕴含了三行数据。别离是样本 1,3,4。这三条数据落在样本 9 的工夫窗口内[2022-01-29 12:00:00, 2022-02-07 12:00:00]。
- w0_1: 用户最近 0~1 行窗口
-- 用户最近 1 行窗口,蕴含前一行和以后行 window w0_1 as (PARTITION BY uid ORDER BY trans_time ROWS BETWEEN 1 PRECEDING AND CURRENT ROW)
样本 10 的 w0_1 窗口蕴含了 2 行数据。别离是样本 7 和样本 10。
- w2_10: 用户最近 2~10 行窗口
-- 用户最近 2~10 行窗口,蕴含前 2~10 行 window w2_10 as (PARTITION BY uid ORDER BY trans_time ROWS BETWEEN 10 PRECEDING AND 2 PRECEDING)
样本 10 的 w2_10 窗口蕴含了 2 行数据。别离是样本 2 和样本 5。
3.3.2. 步骤二:多行聚合函数加工
定义好工夫窗口当前,咱们能够做工夫窗口内的多行聚合函数计算。
简略聚合统计
聚合函数目前反对:count(), sum(), max(), min(), avg(),示例如下。
SELECT
-- 最近 30 天总交易金额总额 sum(trans_amt) over w30d as w30d_sum_trans_amt,
-- 最近 30 天的最大交易金额 max(trans_amt) over w30d as w30d_max_trans_amt,
-- 最近 30 天的单次均匀交易金额 avg(trans_amt) over w30d as w30d_avg_trans_amt,
-- 最近 30 天的总交易次数 count(trans_amt) over w30d as w30d_count_trans_amt
FROM t1
window w30d as (PARTITION BY uid ORDER BY trans_time ROWS_RANGE BETWEEN 30d PRECEDING AND CURRENT ROW);
过滤后聚合统计
先对数据集按条件过滤后,而后进行简略统计。函数形如 xxx_where
:
xxx_where(col, filter_condition) over w
- 参数
col
:参加聚合计算的列。 - 参数
filter_condition
:数据的过滤条件表达式。
目前可反对的带有 _where
类型的聚合函数为:count_where, sum_where, avg_where, max_where, min_where。
相干示例如下:
SELECT
-- 最近 30 天 POS 交易总金额 sum_where(trans_amt, trans_type = "POS") over w30d as w30d_sum_pos_trans_amt,
-- 最近 30 天的最大 POS 交易金额 max_where(trans_amt, trans_type = "POS") over w30d as w30d_max_pos_trans_amt,
-- 最近 30 天的单次均匀 POS 交易金额 avg_where(trans_amt, trans_type = "POS") over w30d as w30d_avg_pos_trans_amt,
-- 最近 30 天的 POS 交易总次数 count_where(trans_amt, trans_type = "POS") over w30d as w30d_count_pos_trans_amt
FROM t1
window w30d as (PARTITION BY uid ORDER BY trans_time ROWS_RANGE BETWEEN 30d PRECEDING AND CURRENT ROW);
分组后聚合统计
对数据集按某一列进行分组,而后分组统计,统计后果保留为形如 "k1:v1,k2:v2,k3:v3"
的的字符串。
函数形如 xxx_cate
:
xxx_cate(col, cate) over w
- 参数
col
:参加聚合计算的列。 - 参数
cate
:分组列。
目前反对的带有 _cate 后缀的聚合函为:count_cate, sum_cate, avg_cate, max_cate, min_cate
相干示例如下:
SELECT
-- 最近 30 天的各城市的总交易次数, "beijing:10,shanghai:3" count_cate(trans_amt, city) over w30d as w30d_city_count_trans_amt,
-- 最近 30 天的各城市的总交易额, "beijing:100,shanghai:30" sum_cate(trans_amt, city) over w30d as w30d_city_sum_trans_amt,
-- 最近 30 天的各城市的均匀交易额, "beijing:10,shanghai:10" avg_cate(trans_amt, city) over w30d as w30d_city_avg_trans_amt,
-- 最近 30 天的各城市的最大交易额, "beijing:30,shanghai:15" max_cate(trans_amt, city) over w30d as w30d_city_max_trans_amt,
-- 最近 30 天的各城市的最小交易额, "beijing:5,shanghai:5" min_cate(trans_amt, city) over w30d as w30d_city_max_trans_amt
FROM t1
window w30d as (PARTITION BY uid ORDER BY trans_time ROWS_RANGE BETWEEN 30d PRECEDING AND CURRENT ROW);
过滤后再分组聚合统计
先对窗口按条件过滤后, 而后对数据集按某一列进行分组,而后分组统计,统计后果保留为形如 "k1:v1,k2:v2,k3:v3"
的的字符串。
函数形如 xxx_cate_where
:
xxx_cate_where(cate, col, filter_condition) over w
- 参数
cate
: 分组列。 - 参数
col
:参加聚合计算的列。 - 参数
filter_condition
:数据的过滤条件表达式
目前可反对的过滤后再分组聚合统计函数为:count_cate_where, sum_cate_where, avg_cate_where, max_cate_where, min_cate_where
相干示例如下:
SELECT
-- 最近 30 天的各城市的 POS 交易次数, "beijing:5,shanghai:2" count_cate_where(trans_amt, city, trans_type = "POS") over w30d as w30d_city_count_pos_trans_amt,
-- 最近 30 天的各城市的 POS 交易总额, "beijing:60,shanghai:25" sum_cate_where(trans_amt, city, trans_type = "POS") over w30d as w30d_city_sum_pos_trans_amt,
-- 最近 30 天的各城市的 POS 均匀交易额, "beijing:12,shanghai:12.5" avg_cate_where(trans_amt, city, trans_type = "POS") over w30d as w30d_city_avg_pos_trans_amt,
-- 最近 30 天的各城市的 POS 最大交额额, "beijing:30,shanghai:15" max_cate_where(trans_amt, city, trans_type = "POS") over w30d as w30d_city_count_pos_trans_amt,
-- 最近 30 天的各城市的 POS 最小交易额, "beijing:5,shanghai:10" min_cate_where(trans_amt, city, trans_type = "POS") over w30d as w30d_city_count_pos_trans_amt
FROM t1
window w30d as (PARTITION BY uid ORDER BY trans_time ROWS_RANGE BETWEEN 30d PRECEDING AND CURRENT ROW);
按类型列进行频率统计
通常,咱们对类型特色会进行频率的统计。例如,咱们可能须要统计各个类别中,最高频次的类型,最高频的类型的频度占比等。
Top ratio 特色fz_top1_ratio
:求窗口内某个分类 count 最大的 count 数占窗口总数据的比例。
SELECT
-- 最近 30 天的交易次数最大的城市的交易次数占比 fz_top1_ratio(city) over w30d as top_city_ratio
FROM t1
window w30d as (PARTITION BY uid ORDER BY trans_time ROWS_RANGE BETWEEN 30d PRECEDING AND CURRENT ROW);
Top N 特色fz_topn_frequency(col, top_n)
: 求取窗口内某个分类频率最高的 N 个分类
SELECT
-- 最近 30 天的交易次数最大的 2 个城市, "beijing,shanghai" fz_topn_frequency(city, 2) over w30d as top_city_ratio
FROM t1
window w30d as (PARTITION BY uid ORDER BY trans_time ROWS_RANGE BETWEEN 30d PRECEDING AND CURRENT ROW);
4. 扩大浏览和下篇预报
- 开源机器学习数据库 OpenMLDB v0.4.0 产品介绍
- 想疾速试用 OpenMLDB 来开始写特色计算脚本?快来看一下 OpenMLDB 疾速上手
- 残缺的 SQL 语法参考 中国镜像站点,国内站点
- 在本系列的下篇中,咱们将会介绍更为简单弱小,但同时也是理论生产环境中更为罕用的多表特色计算,感兴趣的小伙伴千万不要错过。