目录

零基础实现高质量数据挖掘(模型分析篇)

参考:零基础实现高质量数据挖掘(数据分析篇)- 1.1 上手难度

零基础?

某些意义上来说,这个标题是有一些“标题党”了,有些朋友可能会抬杠说不是“零基础”吗,怎么点进来一看还是需要会写python,那我不会写python是不是就不用看了?如果我python已经很精通做得很好了是不是也不用看了?而且如果不会学了也不一定学得会,投入产出比太低了是不是不太划算?

目前“零基础”解决方案是不需要学习太多python语法的,基本上是类似“命令行”的参数化解决方案,全部需要写的可能也几句代码来形成一份通用的“配置文件”,运行后实现所有分析内容自动写入报告后输出,虽然说起来要写代码却基本上能实现“零基础”入门,

从历史培训实践来看,基本没有学习难度,小白也能快速上手,投入产出非常惊人,具体案例在本章的最后一节1.4 案例展示中会进行演示

建模案例可见本节1.4 案例展示中的:1.4.1 评分卡建模1.4.2 机器学习建模,另外,写这一篇章时升级了jupyterlab的环境,启用了新的cdn并使用nginx路径分流,所以本文的截图中看到的web路径会和上一章不一样

参考:零基础实现高质量数据挖掘(数据分析篇)- 1.2 业界对比

优势是什么?

目前数据挖掘类的项目,无论是营销、风控还是别的场景,工具链都非常丰富,除了python还可以用SAS\R\SPSS等等,不想写代码还可以买市面上的商用模型平台,例如阿里的PAI平台,或者Salford Systems的SPM等等,都不用写代码,直接拖拉拽点击就行,为什么我还要使用这个工具呢?

针对以上问题,按市场行情和业界实践,这里按类别梳理了对比的表格:

平台类解决方案(PXX平台、图X平台等)单机商用解决方案(SXX/SXXX等)直接使用开源模块(c/c++/python/java/r等)我的解决方案(基于开源模块二次开发与封装)
价格高,主要面向企业用户中,部分面向个人用户无,开源低,主要面向个人用户
操作难度低,面向非编程人员,主要使用点击和拖拉拽中,可使用点击和拖拉拽,或自定义脚本开发高,主要面向编程人员,直接撰写代码需要控制大量细节中,参数化封装配置文件,可融合自定义脚本开发
分析颗粒度粗糙,基于组件封装不可进行细节调整,部分平台能支持较为细致的代码融合开发一般,页面点击或脚本自定义细致,基于源代码开发细致,模块化参数控制或基于源代码开发
开发成本中,拖拉拽点击配置,一般可加载复用,如涉及脚本自定义开发则开发成本较高中,拖拉拽点击配置,一般可加载复用,如涉及脚本自定义开发则开发成本较高高,debug开发成本不可控制,且不同场景通用性较低低,参数化封装后,配置文件一次配置可多次重复使用
硬件要求高,一般需要服务器部署配置各类服务项中,一般单机点击安装包即可低,基于源代码任意安装和开发中,单机安装conda/python配合pip即可
一体化程度高,通常能同时支持特征生成和模型部署等全流程操作中,一般不包含特征工程,可导出模型文件和分析结果低,基于源代码自行开发中,不包含特征生成,针对分析本身,导出对应的模型文件和分析结果
扩展性低,一体化平台一般不支持非官方自定义扩展中,不支持扩展或官方插件扩建需要加钱,或只能使用特定脚本扩展高,可基于任意生态任意扩展融合高,可基于python生态任意扩展融合

我的解决方案非常适用于以下几类场景:

场景示例
  1. 我既不想写代码,也不会写代码,我就想知道数据效果怎么样
  2. 按规矩的方式仔细做分析要好久,我想知道有没有深入分析的必要性
  3. 重头到尾整合报告太累了,我需要一个工具一键生成常见的报告
  4. 代码我都会写,原理我也都懂,只是现在没时间慢慢搞
  5. 学过一遍之后剩下的都是重复操作,我想避免无意义的内卷,留点时间干别的

简单总结来说,我的解决方案可以在较低的开销下,参数化模块化常用分析结果,帮助分析师绕开繁琐重复的日常操作,可快速生成分析结果并反复参数化调优,在维持高质量分析的基础上大幅降低分析成本

参考:零基础实现高质量数据挖掘(数据分析篇)- 1.3 数据适用性

支持的数据量?

现在都是大数据时代,如果数量太大python处理的了吗?使用python是不是不合适?

如果是数据量足够大的情况下(PB起步,淘宝、微信,或流水日志等等),一般也没有太多可以选择的空间,基本都是各类hdfs分布式数据库进行存储和操作,例如阿里的odps、常见的hive\hadoop\spark套件等等,这个维度也不是各类单机分析软件的处理领域,

从业界实践来看,一般很多分析确实没有那么大的数据量,即使数据量真的很大,在具体设计方案上和实施层面中也有很多解决的办法和优化的方式,举例如下:

数据量较大时的处理案例
  1. 从统计学抽样方式进行数据切片并将一次任务转化为多次进行数据降维
  2. 较为严格的在时间层面上定义观察期和表现期来缩小可用数据范围
  3. 较好的进行前置分析,对数据进行合理的预分群等进行样本拆分
  4. 前置spark等分析工具预处理降低后期细颗粒度分析时的数据宽度
  5. 实现内嵌spark等底层分布式解决方案,如xgboostlightgbm
  6. 直接基于分布式服务器环境搭建jupyterhub等工具,内存理论上无上限

还是使用一样的案例数据文件和同样的jupyter环境搭建python环境管理方式,代码的数据读取和加载方式也和零基础实现高质量数据挖掘(数据分析篇)- 1.4 案例展示中的一样:

import pandas as pd
# 导入我自定义的模块
import sys
sys.path.append("./code_libs")
import alpha_tools as at
# 导入数据
df_ = pd.read_csv("application_record.csv")
df_response = pd.read_csv("credit_record.csv")
# 添加“年龄”变量
df_['Age'] = -(df_['DAYS_BIRTH']) // 365
# 定义标签和数据
df_["target"] = df_["ID"].map(df_response.groupby("ID")["STATUS"].apply(
    lambda x: 1 if ({"2", "3", "4", "5"} & set(x)) else 2 if ({"1"} & set(x)) else 0))
df_data = df_[df_["target"].notnull()]
# 划分数据集
from sklearn.model_selection import train_test_split
df_train, df_test = train_test_split(
    df_data, test_size=0.3, stratify=df_data["target"], random_state=42)
# 进行分析
   at.Analysis.data_flow(df_train, "./数据分析demo/v1/v1.xlsx", test_data=df_test, response="target")

下面分别展示两个参数化模型分析的案例

评分卡建模是非常经典的风控建模流程,这里继承零基础实现高质量数据挖掘(数据分析篇)- 5.7 分箱搜索和过滤数据分析报告v12.xlsx的分析参数,展示一下参数化评分卡建模流程,参考代码如下:

# 首先定义一下之前data_flow用过的参数,便于后续直接调用
data_params = {
    "response": "target",  # 基础定义
    "split_col_name": "birth_year", "use_train_time": True,  # 时间切片
    "add_info": "ID",  # 主键添加
    "enable_single_threshold": False,  # 关闭单一值过滤
    # enable_iv_limit=True, iv_threshold=[0.01, np.inf],  # 暂不启用iv过滤
    "customized_groups": {
        "OCCUPATION_TYPE": [
            "Cleaning staff|Cooking staff|Drivers|Laborers|Low-skill Laborers|Security staff|Waiters/barmen staff",
            "Accountants|Core staff|HR staff|Medicine staff|Private service staff|Realty agents|Sales staff|Secretaries",
            "Managers|High skill tech staff|IT staff",
        ],
        "CNT_CHILDREN": [-np.inf, 0, np.inf],
    },
    "exclude_column": ["ID", "weight"],  # 排除权重
    "min_group_percent": 0,
}
at.Analysis.model_on_data(
    df_train, "./模型分析demo/v12/v12.xlsx", test_data=df_test,
    data_flow={
        **data_params,
        **{
            "save_or_return": False  # 跳过csv的io读写提升性能
        },
    },
    model_flow={
        # 默认流程即为评分卡建模,采用statsmodels的逻辑回归
        "model_type_path": "statsmodels.api.Logit"
    }
)
评分卡建模案例
评分卡建模案例

真正意义上实现分钟级别的评分卡建模流程,主要报告文件可见:模型分析报告v12.xlsxmodel_on_data流程会自动继承了第一阶段data_flow生成的数据分析报告v12.xlsx文件并在报告中合并,包含了数据分析与模型分析等,目录如下:

假设使用原始数据进行机器学习建模,这里使用lightgbm,参考代码如下:

at.Analysis.model_flow(
    df_train, "./模型分析demo/v1-lgb/v1-lgb.xlsx", test_data=df_test, 
    # 目标定义 & 切片定义
    response="target", split_col_name="birth_year", use_train_time=True,
    exclude_column=["ID", "weight"],  # 排除权重
    add_info="ID",  # 主键添加
    # 指定使用训练的class并不进行打分转化
    model_type_path="lightgbm.sklearn.LGBMClassifier",
    convert_score=False,
)
评分卡建模案例
机器学习建模案例

和评分卡类似,也能得到一系列分析报告,主要报告可见:模型分析报告v1-lgb.xlsx,和评分卡建模model_on_data流程相比,仅使用model_flow未能继承data_flow且不存在Data_v1-lgb.xlsx文件,因此报告内容会相对少一些,不过常见的分析内容都是覆盖的:

如果修改model_flowmodel_on_data,并入参合适的data_flow参数,即可在data_flow生成的数据集上使用lightgbm进行建模

整体来说可以参考零基础实现高质量数据挖掘(数据分析篇)- 2 基础定义中的介绍,

数据挖掘子模块实在太多了,按建模目标可以分为:分类、回归、聚类问题,按主题还可以分为:营销、风控、欺诈、图像识别、文本翻译、自然语言处理,而其中的分类也可以有二分类、多分类,不同的主题有不同的分析链路和分析范式,如果没有领域志向,很难把分析元素固定和泛化,从上面的案例也可以看到,本文涉及的参数化工具包主要是针对二分类问题的,业界常用的场景有金融领域的风险识别、营销响应、欺诈识别等等,这里都按风险识别进行举例

差异在于除分类问题外,模型分析这里另外还支持处理回归问题

数据定义方面,和零基础实现高质量数据挖掘(数据分析篇)- 2.1 数据定义中必填项完全一致:

主要需要定义的必填项是train_dataoutput_name参数,其他参数包括test_data等全都是选填项,

       # 必填项
       # train_data                        必填,训练集,pd.DataFrame数据类型
       # output_name                       必填,绑定的各类输出的基本名称,需要以.xlsx结尾

其中入参train_dataNone时会尝试读取本地环境的文件,因为可能前一步data_flow把数据存成csv文件没有直接内容入参到model_flow,不过一般正常还是推荐显式入参比较好,

基础定义上则是基本一致:

    # 基础定义
    # test_data                         选填,默认None,对应的测试数据或验证数据
    # task_type                         选填,默认"binary",任务类型,可条件性修改
    # exclude_column                    选填,默认None,排除的数据的列,支持str和list
    # flow_data_type                    选填,默认"WOE_",可选"ORI_",数据选取的关键字
    # xlsx_must_exists                  选填,默认False,output_name关联的excel不存在时会报错
    # sample_weight_name                选填,默认None,输入后来指定使用某一列的数值调整样本权重
    # auto_convert_dtype                选填,默认True,将train_data和test_data每列数据类型转化一致
    # search_recover_path               选填,默认True,当data为None时候,是否在搜索时包含recover路径
    # slient_performance_warning        选填,默认True,建模时忽略pd.errors.PerformanceWarning的warning

其中task_type支持"binary""regression",如果是"regression"时不会启用和检测标签定义"binary"的取值情况,可用通过__metrics__全局参数来查看分类回归在评估方式上输出的差异:

分类回归评价差异
分类回归评价差异

具体形式上,以之前的模型分析报告v1-lgb.xlsx报告为例,会自动生成大量的报告表格信息:

分类器报告表格示例
分类器报告表格示例

同样也会自动生成大量的报告图片信息:

分类器报告图片示例
分类器报告图片示例

而有关建模定义的最基本参数主要如下:

    # 模型定义
    # model_type_path                   选填,默认"statsmodels.api.Logit",采用标准逻辑回归
    # auto_set_params                   选填,默认True,按预处理和模型特性等自动填充模型参数
    # auto_resize_score                 选填,默认True,检测分数与目标关系,部分评估进行尺度调整
    # slient_model_warn                 选填,默认True,忽略训练预测时,因调用三方模块生成的消息

首选主要通过修改model_type_path来指定使用不同类型的模型,支持的模型范围可以通过全局定义来查看,目前支持的范围如下:

参数化建模支持范围
参数化建模支持范围

另外还支持基于自定义切片交叉验证的超参数搜索、基于模型算法前置的变量筛选、自定义变量、自定义入参、自定义模型、跳过建模步骤进行模型评估等等,这些后续会逐步展开

自动尺度调整
虽然比较少见,但参数auto_resize_score用来在结果评估时自动进行尺度调整,如当roc_auc_score结果在[0, 0.5)之间时,会按p1 = 100 / (p1 + 1)暂时自动调整模型p1结果,使得auc结果重新映射到[0.5, 1],不需要时入参auto_resize_scoreFalse即可

模型的数据预处理一般来说会涉及缺失值处理和离散值处理,主要参数如下:

    # 缺失值处理
    # nan_mode                          选填,默认None,可选min/max/mean/median
    # allow_nan                         选填,默认False,是否允许有缺失值,自动调整

    # 离散值处理
    # allow_discrete                    选填,默认False,是否允许有离散值,自动调整项
    # one_hot_xgboost                   选填,默认False,当模型为xgboost时,所有离散值OH处理
    # one_hot_catboost                  选填,默认False,当模型为catboost时,所有离散值OH处理
    # max_discrete_num                  选填,默认100,支持的离散类型变量的唯一值的上限的个数
其他数据预处理方式
  1. 取值替换与极端值平滑处理:使用data_flow的数据预处理部分并搭配使用save_ori输出预处理后的结果,即可在model_flowmodel_on_data中实现预处理
  2. 数据提前分箱:使用data_flow的数据分箱部分并搭配使用save_woe_data输出预处理后的结果,即可在model_flowmodel_on_data中实现预处理
  3. 基于离散值与分箱的One-Hot编码:和上一条相同,save_woe_data改成save_one_hot即可
  4. 其他预处理:按需搜索,参考使用pandas实现或基于sklearn实现,网上教程极多,不一一举例

常用的模型是带有自动推断的,比如常见的lightgbmxgboost等树模型,会在__pattern__中根据官方文档预设缺失和离散的处理能力:

常用缺失离散预设值
常用缺失离散预设值
自动推断示例
  1. 入参的model_type_path会按全局预设的__pattern__["allow_nan"/"allow_discrete"]自动调整allow_discreteallow_nan的取值
  2. 建模数据选取时,会按flow_data_type的可能取值:"WOE_"(默认值)和"ORI_",进行依次搜索,当入参的建模数据columns中没有"ORI_"/"WOE_"标识,会按数据自动搜索、自动分类离散值和数值型,并根据allow_discrete保留或删除离散值,根据allow_nan保留或填充缺失值
  3. 虽然目前sklearn的小部分模型开始支持缺失值和离散值入模,但大部分默认sklearn的模型都是不支持的,因此如果指定sklearn的模型,默认还是按不可缺失、不支持离散进行训练
  4. 如果model_type_path入参模型支持allow_nanallow_discrete,但数据并没有缺失值或离散值,则会在数据检测完后,把allow_nanallow_discrete调整为False,以避免后续需要allow_nanallow_discrete调整为False才能允许的过滤器失效
  5. 任何自动推断都不会对原始数据造成影响,会按模型适用范围建立子数据集来进行数据操作避免影响后续的其他操作

回归时不涉及标签定义,二分类时可参考:零基础实现高质量数据挖掘(数据分析篇)- 2.3 标签定义

二分类任务最重要的一步就是定义目标变量,在分析时采用1和0来表示,一般来说,会使用1代表有表现的数据(逾期客户、响应客户、目标客户等),使用0代表无表现的数据(正常客户、沉默客户等)

       # 基础定义
       # ind                               选填,默认2,响应中的不确定值
       # bad                               选填,默认1,响应中的negative
       # good                              选填,默认0,响应中的positive
       # response                          选填,默认为"target",数据的标签列
中间状态的标记处理?

不过如果大家项目做的多就会发现很多时候是需要有一个中间状态的体现的,比如:

  1. 逾期分析时按逾期天数计算滚动率发现,>=30天的逾期天数为目标客户(标记1)
  2. 和<=3天的逾期天数为正常客户(标记0)
  3. 那么(3, 30)之间的客户应该怎么处理呢?(标记?)
不合适的处理方式

有些朋友说,我分析的时候把这些数据删了,不考虑不就行了?

直接删除数据其实是不建议的,因为如果你删除了这些特殊状态的客户,可能01分析的时候没有太大影响,但是是会影响到全量综合测算的(因为会影响分母大小),比如按01分析一条规则的测算命中率是3%,把2加进去的时候可能实际命中率就只有2.5%了,还没有正式投产测算就发生偏移是非常不专业的做法

还有些朋友说,我就不设置中间状态只保留01,把中间状态设成0或1,不就没这个问题了?

这种情况也是不建议的,01的定义是会直接影响到后续有效性和稳定性的,不恰当的01设置可能会导致分析的目标紊乱出现分析偏差(如:分箱切分点阈值偏移),况且谁也不能保证永远没有特殊状态的存在(比如我把未激活的客户当作2来评估分布)

合适的处理方式

所以我设计的模块虽然针对二分类,但标签定义默认是能包含012三种状态的,2可以配合各种不同状态(未激活、沉默、被拒绝、营销未响应等)中间状态的数据集进行测算和展示,如下:

中间状态客户示例
中间状态客户示例

中间状态仅做展示并不影响使用01的进行核心测算和分析

模板太单一?

可能还有朋友抬杠说我不做风险逾期分析你这个报告的模板不适合我,太单一了只有ind/good/bad

自定义报告输出

这个模板其实也可以改的,主要通过修改全局模板,简单配置如下:

修改目标变量展示名称
修改目标变量展示名称

这样所有对应的bad/good/ind等名称都会发生改变,如下图所示:

报告示例(修改目标变量名称)
报告示例(修改目标变量名称)

具体可以下载文件查看:数据分析报告vx.xlsx,参数调整范围也可查看6.1 报告格式调整

二分类时的基础定义完全一致,当task_type入参regression时,不会检测response中的内容,会直接按回归的方式进行建模和评估,如下:

at.Analysis.model_flow(
    df_train, "./模型分析demo/v2-lr/v2-lr.xlsx", test_data=df_test,
    # 目标定义 & 切片定义
    response="target", split_col_name="birth_year", use_train_time=True,
    exclude_column=["ID", "weight", "DAYS_BIRTH"],  # 排除权重
    add_info="ID",  # 主键添加
    task_type="regression",  # 调整为 regression
    # 非分类器会自动把 convert_score 调整为 False
    model_type_path="sklearn.linear_model.LinearRegression",
)
回归建模示例
回归建模示例

可以得到模型分析报告:模型分析报告v2-lr.xlsx,模型回归的报告内容会少一些,这和模型评价维度、适用面等因素有关,

当然,如果涉及到模板调整,则入参除at.dt.__excel__外,额外添加到at.mt.__excel__,分别对应调整data_flowmodel_flow的输出报告

参考:零基础实现高质量数据挖掘(数据分析篇)- 2.4 数据切片

数据切片是可选的参数,但因为实在是太基础了,所以提前说一下,细心的朋友可能会发现上面展示的报告内容里面有好多Random1/2/3是怎么回事?比如这一页:

切片示例(部分)
切片示例(部分)

上面报告当中的Random1/2/3其实是代码生成的数据切片的一种,上图的具体含义是在计算相关性时,除了按整体数据量计算,还把数据集分为Random1/2/3三片,每片数据分别计算相关性,最后汇总展示,

那么来看一下数据切片设定的参数定义:

       # 数据切片相关
       # split_col_name                    选填,默认None,可选时间序列或离散值,用来对数据进行分割
       # is_time_series                    选填,默认True,当输入非响应列时,按时间序列预先分割处理
       # use_train_time                    选填,默认False,处理时间时,使用开发时间区间来映射全局
       # max_split_part                    选填,默认10,支持的数据切片的上限,过大会导致性能问题
       # format_time_edge                  选填,默认False,当处理时间时,时间区间两端展示为无穷
       # stratified_split                  选填,默认False,等量分割数据抽样时,是否使用分层抽样
       # split_data_method                 选填,默认3,可选"M"/"2W-SUN",int时使用等量分割
       # split_random_state                选填,默认None,数据在按时间划分抽样时的随机种子
       # use_response_split                选填,默认True,当split未指定时采用response

这个模块这部分的默认参数与引用中的定义一致,差异点在于stratified_split默认值是True,原理是一般建模会更多倾向于默认进行分层抽样来避免样本上的数据倾斜,数据切片后会展现在模型报告的多个地方,下面举例说明,

如在报告的模型分析报告v12.xlsx变量评估 - 方差膨胀系数页面可以看到VIF和对应的切片情况:

整体与切片的VIF
整体与切片的VIF

而在变量评估 - 相关性矩阵页面可以看到完整的相关性矩阵和pvalue,也是包含切片的展示:

入模变量相关性系数与pvalue
入模变量相关性系数与pvalue

同时也会在模型的排序和分布的稳定性上加以观察,如页面模型评估 - 排序稳定性模型评估 - 稳定性汇总等页面:

排序稳定性
排序稳定性

分布稳定性
分布稳定性

切片的概念贯穿全文,信息量较大,其他涉及的图表等后续模块再做展示

参考:零基础实现高质量数据挖掘(数据分析篇)- 2.5 多数据映射

上述test_data的定义为Nonedataframe,实战中会面临多个数据集的测算需求,如:验证、测试、样本外、全量等不同维度,考虑到这些需求,实际上test_data是支持list入参的,参数如下:

       # alpha_tools.Analysis参数
       # train_name                        选填,默认None,train_data的名称,入参str并和test_names联动
       # test_names                        选填,默认None,test_data(s)的名称,入参str/list并和test_data(s)联动
       # auto_dir_cn                       选填,默认True,根据train_name和test_names自动修改train_test_dir_cn值

只是test_datalist时,需要同时指定train_nametest_names,不然会报错,参考用例如下:

at.Analysis.data_flow(
     df_train, "./数据分析demo/v2-more/v2-more.xlsx",
       # 同时入参两个数据集:测试、全量
       test_data=[df_test, df_data], train_name="开发", test_names=["测试", "全量"],
       response="target", split_col_name="birth_year", use_train_time=True,
   )
多数据映射
多数据映射

可一次性得到多个数据集的分析结果报告,并会基于output_name自动添加后缀名,如上图展示的数据分析报告v2-more-开发-测试.xlsx数据分析报告v2-more-开发-测试2.xlsx

具体实现上其实是通过配置文件还原的方式来操作,后面章节6.3 配置映射和还原会展开描述,至于多份文件如何合并与浏览?也会在后面章节6.5 报告合并与数据合并中具体展开描述

涉及参数与data_flow完全一致,可以在model_on_data多数据映射时,对多个候选数据集默认选取已经入模的数据指标,降低无效性能开销,比如输入:

at.Analysis.model_on_data(
    df_train, "./模型分析demo/v1-scorecard-more/v1-scorecard-more.xlsx",
    # 同时入参两个数据集:测试、全量
    test_data=[df_test, df_data], train_name="开发", test_names=["测试", "全量"],
    data_flow={
        **data_params,
        **{
            "save_or_return": False  # 跳过csv的io读写提升性能
        },
    },
    model_flow={
        # 默认流程即为评分卡建模,采用statsmodels的逻辑回归
        "model_type_path": "statsmodels.api.Logit"
    }
)
多数据映射-评分卡
多数据映射-评分卡

或是应用在model_flow上,参数也和最开始的示例一样,写起来会更简洁一些:

at.Analysis.model_flow(
    df_train, "./模型分析demo/v1-lgb-more/v1-lgb-more.xlsx", 
    test_data=[df_test, df_data], train_name="开发", test_names=["测试", "全量"],
    # 目标定义 & 切片定义
    response="target", split_col_name="birth_year", use_train_time=True,
    exclude_column=["ID", "weight"],  # 排除权重
    add_info="ID",  # 主键添加
    # 指定使用训练的class并不进行打分转化
    model_type_path="lightgbm.sklearn.LGBMClassifier",
    convert_score=False,
)
多数据映射-机器学习
多数据映射-机器学习

获取的样例文件可见:

和前文类似,配置还原与报告合并请见:5.3 配置映射和还原5.5 报告合并与数据合并

入参与data_flow是一样的,还是通过sample_weight_name参数实现,只是这个模块是通过将sample_weight_name的入参传递给模型fit函数中的sample_weight参数来间接实现,

如果model_flow底层调用的建模依赖模块不支持权重调整,则传递的参数在model_flow环节无效,不过也要综合来看,比如默认的statsmodels.api.Logit建模类的fit函数并没有sample_weight参数,但如果是data_flow已经入参sample_weight_name调整过woe数据权重了,相当于已经间接实现了权重调整的目的了,statsmodels.api.Logit本身并不能在建模环节支持权重调整也没有太大的影响,

另外关于class_weight没有单独实现,感觉单独实现有些冗余,有需求的请按需要的class_weight自行添加sample_weight_name并指定入参

参考:零基础实现高质量数据挖掘(数据分析篇)- 2.7 数据项翻译

一般来说直接使用原始数据分析即可,也可以直接修改原始dataframe.columns来调整报告内容,不过总有一些情况要求既保留原始数据内容,又体现内容转换,因此做了这个翻译方面的定义,具体内容如下:

       # 数据翻译
       # var_dict_path                     选填,默认None,变量翻译的字典路径
       # var_series_name                   选填,默认"var_name",变量文件字典中待翻译变量的列名称
       # var_explain_name                  选填,默认"var_explain",变量文件字典中变量解释的列名称

举例来说,假如我自定义了一份字段翻译.xlsx并放在同一路径下,里面包含的内容如下:

数据翻译
数据翻译

在使用的时候直接指定参数即可:

at.Analysis.data_flow(
       df_train, "./数据分析demo/v2-rename/v2-rename.xlsx", test_data=df_test, response="target",
       split_col_name="birth_year",
       var_dict_path="./字段翻译.xlsx", var_series_name="ori_name", var_explain_name="new_name"
)

可在输出结果数据分析报告v2-rename.xlsx相应的位置看到已经完成了翻译标注:

数据翻译1
数据翻译1
数据翻译2
数据翻译2

其中变量评估 - 翻译页会把字段翻译.xlsx中匹配到的行的所有列全部粘贴保留

这一部分和data_flow用法完全一样,差异点在于翻译结果出现的页面不同,非重点内容不展开描述了

参考:零基础实现高质量数据挖掘(数据分析篇)- 2.8 高性能实现

除了底层混用python/c/c++实现高性能计算函数,在变量分析的流程是默认开启并发的,能同时平行加速处理多个变量缩短整体分析时间,涉及的参数如下:

       # 并发相关
       # multi_num                         选填,默认8,多进程或多线程的并发数
       # chunk_size                        选填,默认1,多进程时每个进程包含的并发数
       # enable_lock                       选填,默认True,并发锁,可间接调整并发运行状况
       # enable_multi                      选填,默认True,是否启用多进程或多线程加速计算

参数比较简单不再展开描述,其他实践中的一些简单备注事项如下:

备注事项
  1. 如果机器内核数量小于multi_num,会把multi_num调整为实际内核数量
  2. windows因为python本来的深坑,如果是在windowsipython环境下运行,将默认关闭多进程,需要windows的多进程运算,请在if __name__ == '__main__': main()下执行
  3. 环境变量NUMEXPR_MAX_THREADS不存在时,默认设置为str(min(8, os.cpu_count()))
  4. 为避免multiprocessing导致的环境问题,在启用enable_multi时,会自动调用threadpoolctl.threadpool_limits并限制limits=1

参数是完全一样的,混合编程的实践也是完全一样的,只是模型分析的并发多通过模型本身参数n_jobs来间接实现,而其他如相关性筛选等功能的并发,因为底层的numpy并发性能已经很好了,因此也多是多线程实现,不太会有data_flow面临的multiprocessing多进程的问题

这一部分的内容和之前的零基础实现高质量数据挖掘(数据分析篇)- 4 数据过滤和筛选存在较大的差异,主要体现在之前的数据过滤和筛选主要是基于单指标的特性来进行,而模型分析这里主要是通过多变量的模型分析来筛选,下面展开描述

目前主要通过算法实现的过滤有以下几步:相关性筛选重要性筛选正则化筛选逐步回归筛选变量数量限制,因线性建模与机器学习建模的差异,预设了筛选的顺序和范围,参数如下:

    # 模型定义
    # filter_variable                   选填,默认True,是否对读取数据的变量进行算法筛选
    # is_inherit_tree_model             选填,默认True,传递树模型model_type_path到limit_var_method和imp_filter_method
    
    # 自定义筛选顺序和范围
    # linear_filter_order               选填,默认["corr", "zero", "lasso", "stepwise", "rank"],线性建模的筛选顺序
    # model_filter_order                选填,默认["corr", "zero", "rank"],自定义建模流程的筛选顺序

其中filter_variable是全局定义的筛选开启开关,不开启filter_variablelinear_filter_ordermodel_filter_order均不会生效,

但开启filter_variable后,未在linear_filter_ordermodel_filter_order中列出的筛选算法也不会生效,即使列出如果筛选器内部关闭的话也不会生效,

默认开启is_inherit_tree_model是指当用自定义树模型训练时,使用对应的树模型进行后续的重要性和变量数量筛选,适用于比如lightgbm模型训练变量名报错时进行连带切换模型的场景,

如果有需求可以通过调整linear_filter_ordermodel_filter_order来实现,比如我们查看模型报告模型分析报告v1-lgb.xlsx 变量评估 - 排除详情页,发现原有为importance-zero的有两个指标:

重要性筛选排除
重要性筛选排除

并在变量评估 - 重要性非零筛选页面查看确认了该事实:

重要性筛选详情
重要性筛选详情

此时可以通过输入:

at.Analysis.model_flow(
    df_train, "./模型分析demo/v3-lgb/v3-lgb.xlsx", test_data=df_test, 
    # 目标定义 & 切片定义
    response="target", split_col_name="birth_year", use_train_time=True,
    exclude_column=["ID", "weight"],  # 排除权重
    add_info="ID",  # 主键添加
    # 指定使用训练的class并不进行打分转化
    model_type_path="lightgbm.sklearn.LGBMClassifier",
    convert_score=False,
    model_filter_order=["corr", "rank"],
)
手动调整模型过滤器范围
手动调整模型过滤器范围

则新的模型报告模型分析报告v3-lgb.xlsx 变量评估 - 排除详情页就不会再有importance-zero原因了,

另外还需要注意的是,线性筛选器:相关性筛选正则化筛选逐步回归筛选,这三类筛选器底层计算不支持缺失值、离散值,因此需要在allow_nanallow_discreteFalse时才能生效,这样就会有几种情况:

线性筛选器生效范围
  1. 入参的model_type_path模型不支持缺失值、离散值:能无缝衔接使用这三类筛选器,如评分卡建模
  2. 入参的model_type_path模型支持缺失值、离散值,则:
    1. 指定的入模变量范围存在缺失值、离散值:会自动跳过这三类筛选器,如范例中的机器学习建模(存在离散值)
    2. 指定的入模变量范围不存在缺失值、离散值:数据集检测完成后把allow_nanallow_discrete调整为False,在启用这三类筛选器时仍然会生效,这一点在之前的章节2.2 数据预处理中也有提到过

相关性筛选涉及的参数如下:

    # 相关性筛选
    # enable_corr_filter                选填,默认True,是否对全局变量进行相关性筛选
    # corr_filter_limit                 选填,默认0.65,启用相关性筛选时的变量筛选阈值
    # corr_pvalue_limit                 选填,默认None,建议可选范围0.001/0.01/0.05/0.1
    # slient_corr_warn                  选填,默认True,忽略相关性分析输出的warnings消息
    # select_corr_by                    选填,默认["iv_train", False],使用的列和是否升序
    # corr_method                       选填,默认"pearsonr",可选"kendalltau"/"spearmanr"

默认开启,在之前评分卡报告模型分析报告v12.xlsx变量评估 - 排除详情页面可以看到有两个指标是因为相关性过滤被排除的:

相关性排除示例-评分卡
相关性排除示例-评分卡

通过查看变量评估 - 相关性筛选页面,可以看到具体被排除的系数等信息:

相关性排除详情-评分卡
相关性排除详情-评分卡

相关性筛选是需要有一个序列指示筛选方向和优先级的,当两个指标比较结果超过筛选阈值时,按序列保留优先级较高的指标,在model_flow流程时,会根据select_corr_by通过读取数据分析报告中的信息并排序做为序列,如果报告文件不存在、或报告内变量完全不匹配数据集时,默认会按变量名称的字符串排序做为筛选方向,

那上面机器学习的报告模型分析报告v1-lgb.xlsx为什么没有相关性筛选的分析结果?因为按3.1 筛选顺序和范围所述,lightgbm支持离散值而数据存在离散值,因此相关性筛选做为线性筛选器失效了,日志里也是有体现的:

线性筛选器自动调整示例
线性筛选器自动调整示例

可以通过排除离散值的方式来启用相关性筛选,输入如下:

discrete_cols = df_train.dtypes[df_train.dtypes == object].index.tolist()
at.Analysis.model_flow(
    df_train, "./模型分析demo/v4-lgb/v4-lgb.xlsx", test_data=df_test, 
    # 目标定义 & 切片定义
    response="target", split_col_name="birth_year", use_train_time=True,
    exclude_column=["ID", "weight"] + discrete_cols,  # 排除权重和离散值
    add_info="ID",  # 主键添加
    # 指定使用训练的class并不进行打分转化
    model_type_path="lightgbm.sklearn.LGBMClassifier",
    convert_score=False,
)
lightgbm排除离散值建模示例
lightgbm排除离散值建模示例

则能在模型报告模型分析报告v4-lgb.xlsx的对应页面看到相关性筛选已经生效了:

相关性排除示例-机器学习
相关性排除示例-机器学习
关于VIF
VIF计算的逻辑过于性能浪费、不易优化或并发,且VIF结果可以通过调整相关性筛选阈值来间接控制,因此不再单独做为筛选器

这个筛选器比较简单,主要参数有:

    # 重要性筛选
    # zero_imp_filter                   选填,默认True,是否对全局变量进行重要性非零筛选
    # imp_filter_method                 选填,默认"lightgbm.sklearn.LGBMClassifier"
    # imp_method_parameter              选填,默认{},继承"tree_default_params"

用树模型提前训练一下,把特征重要性为0的变量提前排除,一般会排除一些特别没有增益效果的变量,如3.1 筛选顺序和范围中展示的排除项,有一个应该是单一值为1的变量,

imp_method_parameter继承__pattern__["tree_default_params"]中的参数,

默认tree_default_params参数
默认tree_default_params参数

默认用lightgbm主要是考虑到适用范围比较广,同时支持缺失值、离散值

这个筛选器也很简单,只有一个参数:

    # 正则化筛选
    # enable_lasso_filter               选填,默认False,使用lasso对变量进行降维

在某些特定线性场景下可能会比较有效果,原理是通过sklearn.linear_modelLogisticRegression进行penalty="l1"的拟合,并排除拟合后coef=0的变量,适用面有限,默认也没有开启

使用逐步回归筛选是线性模型常见的操作方式,控制的参数也比较多,如下:

    # 逐步回归模式
    # enable_stepwise                   选填,默认True,是否启用逐步回归
    # stepwise_method                   选填,默认"forward"前向逐步回归,可选"backward"
    # forward_round_num                 选填,默认3,forward回归时遍历所有变量的次数,不可超过10

    # 逐步回归显著性筛选
    # enable_pvalue_filter              选填,默认True,是否启用显著性筛选
    # pvalue_filter_limit               选填,默认0.05,显著性筛选时的筛选阈值

    # 逐步回归模型系数筛选
    # enable_coef_negative              选填,默认{"WOE_": True, "ORI_": None, "": None},控制coef的方向
    # check_coef_consistency            选填,默认{"WOE_": False, "ORI_": True, "": True},变量coef一致性检验

    # 线性模型参数
    # use_fstatistic_pvalue             选填,默认True,使用sklearn线性模型时,使用ftest的pvalue替换
逐步回归算法
  • 前向forward:变量预排序后,从第一个变量开始,每次添加一个变量进行一轮回归,回归结束检测pvaluecoef,检测完成后保留通过检测的变量,变量全部检测完后视forward_round_num情况从头开始重复添加变量
  • 后向backward:所有变量都放在一起,进行一轮回归,回归结束检测pvaluecoef,检测完成后保留通过检测的变量,再进行一轮回归,直至所有变量都满足检测条件后退出
变量预排序
需要注意的是,enable_stepwise开启时,尤其是stepwise_mode"forward"时,变量预排序是有价值的,会倾向于使用select_corr_by参数来进行变量排序,如3.2 相关性筛选所述,如果报告文件不存在、或报告内变量完全不匹配数据集时,则预排序会按变量名称排序进行
检测pvalue和coef说明
  • pvalue检测:通过参数enable_pvalue_filter启用,并限制pvalue的取值需<=参数pvalue_filter_limit,不满足条件则排除
  • coef负向检测:通过入参enable_coef_negative约束coef方向需要为负,不满足条件则排除,enable_coef_negative可选True/False/None,代表:约束负项、正向、不约束,如果data_flow不修改bad/good定义,则无需修改此处默认值
  • coef一致性检测:通过入参check_coef_consistency开启或关闭coef一致性检测,指逐步回归建模获取的coef变量系数需和单变量建模得到的coef系数方向一致,不一致则排除,隐含意义是变量效果上的独立性并未因多变量融合而发生改变
pvalue和coef的备注
  • 参数enable_coef_negativecheck_coef_consistency入参dict的主键"WOE_"/"ORI_"/""分别指数据集columns包含"WOE_"/"ORI_"/""的情况,分别适用不同建模场景
  • 参数check_coef_consistency仅在model_type_pathstatsmodel.apisklearn.linear_model时生效,因为需要计算对比使用同样的线性,所以model_type_path也必须是线性模型
  • 在计算pvalue时,如果是model_type_pathstatsmodel.api则默认使用原生提供的pvalue,但如果是sklearn.linear_model,因没有原生的pvalue,且手动重新算pvalue比较影响性能,因此默认参数use_fstatistic_pvalue改用sklearn.feature_selectionf_classiff_regression来计算pvalue,方差分析与线性回归的关系可自行搜索,如果不认可建议调整use_fstatistic_pvalueFalse,或只使用statsmodel.api进行线性分析
为什么woe线性回归需要约束coef为负?
data_flow中的woe默认按log(good=0/bad=1)计算,woe取值越大代表客户越靠近good,因此在model_flow中按默认bad=1 & good=0拟合后coef系数应该是negative,客户越goodnegative减的越多,才能符合越good越接近0的线性方程逻辑

1.4.1 评分卡建模中提到的woe建模场景,这里再写一个基于原始数据建模的案例,假如我希望基于原始值进行广义线性回归,缺失值用最小值填充,并先经过相关性筛选、逐步回归筛选,相关性筛选基于数据分析的排序,逐步回归需要启用一致性检验,则可以这样写:

at.Analysis.model_on_data(
    df_train, "./模型分析demo/v5-gls/v5-gls.xlsx", test_data=df_test,
    data_flow={
        **data_params,
        **{
            "save_ori": True,  # 数据集仅保留原始数据,会自动添加"ORI_"
            "save_or_return": False  # 跳过csv的io读写提升性能
        },
    },
    model_flow={
        # 更换模型,非discrete模型会自动关闭convert_score
        "model_type_path": "statsmodels.api.GLS",
        "pvalue_filter_limit": 0.5,  # 变量较少,此处调整pvalue阈值避免无变量
        # stepwise时按ORI_,enable_coef_negative取None跳过,check_coef_consistency取True开启
        "model_filter_order": ["corr", "stepwise"],
    }
)
GLS建模示例
GLS建模示例

则可在报告模型分析报告v5-gls.xlsx变量评估 - 排除详情变量评估 - 相关性筛选变量评估 - 逐步回归筛选这几个页面查看过滤器生效的详情:

广义线性回归示例
广义线性回归示例

当然这里也可以直接使用model_flow,只不过这样的话在相关性筛选和逐步回归时,就只能把变量名字符串排序做为优先级了

这个筛选器也很简单,也是通过树模型进行筛选,和3.3 重要性筛选中按0排除不同,数量筛选是按重要性排序,保留前limit_var_num个重要的变量入模,参数如下:

    # 变量数量限制
    # limit_var_num                     选填,默认20,通过树模型重要性,限制最终进入模型的变量的个数
    # limit_var_method                  选填,默认"lightgbm.sklearn.LGBMClassifier",需要树模型
    # limit_method_parameter            选填,默认{},继承"tree_default_params"

当然了,limit_method_parameter同样继承__pattern__["tree_default_params"]中的参数

2.1 数据模型定义中涉及的常规模型参数,线性模型额外会多几个常用的参数,如下:

    # 线性模型参数
    # auto_add_intercept                选填,默认True,当检测为statsmodels模型时自动添加截距项
    # auto_singular_matrix              选填,默认True,statsmodels时,自动处理奇异矩阵不可逆问题
    # display_linear_pvalue             选填,默认True,使用sklearn线性模型时,是否展示模型pvalue

自动添加截距和自动展示pvalue不再展开,参数auto_singular_matrix是为了避免statsmodels可能引发的np.linalg.LinAlgError,即奇异矩阵矩阵不可逆,原理是使用np.linalg.pinv暂时替换np.linalg.inv来解决这个问题,有兴趣可阅读拓展链接

另外,在VIF计算时,也会自动添加截距,参数如下:

    # 线性模型参数
    # vif_include_intercept             选填,默认True,VIF值计算包含截距,statsmodels作者推荐包含截距
    # vif_display_intercept             选填,默认False,vif_include_intercept为True时,展示是否包含截距

这也是statsmodels作者在代码的问题反馈中推荐的操作方式

在之前2.1 数据模型定义中已经介绍了如何通过入参model_type_path来实现模型切换,这里再进一步介绍一些自定义建模的参数,主要包括以下三项:

    # 模型定义
    # customized_variables              选填,默认[],设定变量列表来定制建模,入参后跳过变量筛选步骤
    # customized_parameter              选填,默认{},建模方法的入参字典,可配合参数搜索来做定制使用
    # model_func_definition             选填,默认None,默认自动按model_type_path配置调整,不建议入参

有些建模场景可能需要锁定入参变量,此时可以通过customized_variables入参实现,此时不会再进行筛选和搜索(当然exclude_column还是会生效的),会直接使用customized_variables中的变量建模,比如我希望比较离散值数据在one-hot前后用lightgbm建模,模型表现是否会存在差异,案例如下,

首先customized_variables入参实现lightgbm基于离散值原始值建模,代码如下:

discrete_cols = df_train.dtypes[df_train.dtypes == object].index.tolist()
at.Analysis.model_flow(
    df_train, "./模型分析demo/v6-lgb/v6-lgb.xlsx", test_data=df_test, 
    # 目标定义 & 切片定义
    response="target", split_col_name="birth_year", use_train_time=True,
    exclude_column=["ID", "weight"],  # 排除权重和离散值
    add_info="ID",  # 主键添加
    # 指定使用训练的class并不进行打分转化
    model_type_path="lightgbm.sklearn.LGBMClassifier",
    convert_score=False,
    customized_variables=discrete_cols,
)
自定义变量建模示例
自定义变量建模示例

得到模型分析报告:模型分析报告v6-lgb.xlsx,再是基于one-hot的取值进行分析:

not_discrete_cols = df_train.dtypes[df_train.dtypes != object].index.tolist()
at.Analysis.model_on_data(
    df_train, "./模型分析demo/v6-lgb-oh/v6-lgb-oh.xlsx", test_data=df_test,
    data_flow={
        **data_params,
        **{
            "save_one_hot": True,  # 数据集仅保留原始数据,会自动添加"ORI_"
            "save_or_return": False,  # 跳过csv的io读写提升性能
            "exclude_column": ["ID", "weight"] + not_discrete_cols,  # 排除权重和数值变量
        },
    },
    model_flow={
        "response": "target", "split_col_name": "birth_year", "use_train_time": True,
        "exclude_column": ["ID", "weight"],  # 排除权重和离散值
        "add_info": "ID",  # 主键添加
        # 指定使用训练的class并不进行打分转化
        "model_type_path": "lightgbm.sklearn.LGBMClassifier", "convert_score": False,
        "filter_variable": False, # 数据one-hot之后变量名会变化,data_flow已经限制范围了,直接设置filter_variables能达到一样的效果
    }
)
自定义变量建模One-Hot示例
自定义变量建模One-Hot示例

得到基于one-hot建模的模型分析报告:模型分析报告v6-lgb-oh.xlsx,简单对比两份报告的模型评估 - 指标汇总页面,结果上还是有些轻微差异的:

有无one-hot预处理lightgbm训练对比
有无one-hot预处理lightgbm训练对比

左边是离散值直接建模,右边是离散值one-hot以后建模,结果上看,似乎one-hot以后train效果更好了但test效果更差了,可能lightgbm的离散值处理算法在这个数据集上的处理比单纯的one-hot效果要好一点点吧,但算法优势上体现的也不多毕竟差异也很有限,

当然还可以尝试用data_flow的其他分箱算法预处理数据集再和lightgbm对比,这里篇幅有限不再展开

除自定义模型外,还可以通过customized_parameter来自定义建模入参,在这之前的lightgbm训练案例使用的都是模型默认预置的参数,这里我们尝试按官方文档入参看一下结果会有什么不同:

at.Analysis.model_flow(
    df_train, "./模型分析demo/v7-lgb/v7-lgb.xlsx", test_data=df_test, 
    # 目标定义 & 切片定义
    response="target", split_col_name="birth_year", use_train_time=True,
    exclude_column=["ID", "weight"],  # 排除权重
    add_info="ID",  # 主键添加
    # 指定使用训练的class并不进行打分转化
    model_type_path="lightgbm.sklearn.LGBMClassifier",
    convert_score=False,
    customized_parameter={
        "n_estimators": 200  #  默认100,尝试增加迭代次数
    }
)
自定义参数建模示例
自定义参数建模示例

从模型报告模型分析报告v7-lgb.xlsx来看,结论基本上是大力出奇迹:

lightgbm增大n_estimators后对比
lightgbm增大n_estimators后对比

翻倍迭代次数,虽然对稳定性没什么帮助,但性能上确实会比默认要好上一点点。。。

另外,参数customized_parameter入参list时会自动进行模型参数搜索,超参数搜索坑也很大,可通过查阅4.4 模型参数搜索来了解我在解决方案上的架构设计

这一部分比较高阶,适用于已有的model_type_path入参模型不满足建模需求时,支持通过model_func_definition入参进行高阶自定义建模,入参模板通过调用at.mt.Api.get_model_template()函数获取,示例函数参考如下:

自定义函数建模示例
自定义函数建模示例
如何自定义模型?
  1. 需要保持模板函数的入参与出参,并替换函数主体内容,生成train_ptest_p后对外传递
  2. 检测到params = {}时会联动超参数搜索,因此不建议修改,一旦修改就不会动态超参数搜索了
  3. 其他部分,包括template_fit_modeltemplate_use_model等都需要修改成自定义函数,默认仅是示例
  4. 自定义模型的allow_nanallow_discrete不会再动态调整,会按入参对数据进行预处理,请谨慎入参
  5. 因为自定义代码的搜索域有限,所有自定义代码包括import,都需要在model_func_definition引用的函数内定义,函数外定义再引用是找不到的,如果自定义建模的流程代码量大,请先自建模块,在函数内引入模块即可

这里看一个真实案例,比如我想要使用神经网络建模,这里使用torchskorch来减少代码数量,首先先安装依赖:

# 首先安装torch,网速太慢的话请先下载再安装,不同平台自行修改安装命令,miniconda建议pip安装,安装命令来自:https://pytorch.org/get-started/locally/
# windows 我的笔记本有GPU
pip3 install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu113
# linux 但我的VPS只有CPU
pip3 install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cpu
# 再安装skorch,来自:https://github.com/skorch-dev/skorch
pip install -U skorch

然后进行自定义建模函数run_torch_net的定义:

def run_torch_net(train_data, train_response, train_all, test_all, kwargs):
    # 当 params = {} 时会自动使用 customized_parameter 来调参
    params = {}

    import numpy as np
    import torch
    import torch.nn.functional as F
    from skorch import NeuralNetClassifier
    
    class Classifier(torch.nn.Module):
        def __init__(self):
            super(Classifier, self).__init__()
            var_num = len(train_data.columns)
            self.first_layer = torch.nn.Linear(var_num, var_num * 2)
            self.second_layer = torch.nn.Linear(var_num * 2, var_num * 4)
            self.final_layer = torch.nn.Linear(var_num * 4, 2)

        def forward(self, x_batch):
            X = self.first_layer(x_batch)
            # X = F.relu(X)
            X = torch.sigmoid(X)

            X = self.second_layer(X)
            X = torch.sigmoid(X)
            # X = F.relu(X)
            X = F.dropout(X, 0.15)

            X = self.final_layer(X)
            # X = F.relu(X)
            X = torch.sigmoid(X)
            return F.softmax(X, dim=1)

    net = NeuralNetClassifier(
        module=Classifier,
        criterion=torch.nn.CrossEntropyLoss,
        optimizer=torch.optim.Adam,
        # train_split=skorch.dataset.CVSplit(cv=5, stratified=True),
        # verbose=0,
        **params  # 把params注入到这里
    )
    # 使用模型函数fit拟合数据来训练模型,torch网络需要float32/int64
    net.fit(
        train_data.values.astype(np.float32),
        train_response.values.astype(np.int64)
    )
    # 通过查看 net.classes_ 来确认 p1的index的位置
    train_p = net.predict_proba(train_all.values.astype(np.float32))[:, 1]
    if test_all is not None:
        test_p = net.predict_proba(test_all.values.astype(np.float32))[:, 1]
    else:
        test_p = None

    # 将模型对象缓存,会尝试把model_obj保存为pkl文件,并尝试解析成pmml模型文件
    kwargs["model_obj"] = net
    return train_p, test_p, kwargs

最后在model_flow中入参上面定义好的run_torch_net即可:

not_discrete_cols = df_train.dtypes[df_train.dtypes != object].index.tolist()
at.Analysis.model_flow(
    df_train, "./模型分析demo/v8-torch/v8-torch.xlsx", test_data=df_test, 
    # 目标定义 & 切片定义
    response="target", split_col_name="birth_year", use_train_time=True,
    exclude_column=["ID", "weight"],  # 排除权重
    add_info="ID",  # 主键添加
    model_func_definition=run_torch_net,
    customized_variables=not_discrete_cols,
    customized_parameter={
        "max_epochs": 10,
        "lr": 0.1,
    },
    precision=30,
)
自定义模型建模示例
自定义模型建模示例

虽然从模型报告模型分析报告v8-torch.xlsx的结果来看,模型性能很差,但作为案例的流程是通畅的,可以以这样的方式充分融合python语法的优势来支持任意建模范式

注意
这一部分难度较高,不建议新手尝试,使用时需要有相当水平的编程能力

这里的交叉验证是指使用2.4 数据切片中定义好的切片进行N折交叉验证的过程,涉及的参数如下:

    # 交叉验证相关
    # use_larger_training               选填,默认True,默认在N折时使用分割后较大的数据集来进行训练,正常不调整
    # enable_train_fold                 选填,默认True,是否在切片存在时,使用切片进行N折交叉验证
    # cv_inherit_range                  选填,默认False,交叉验证分段计算时是否继承建模的分段点

训练时会使用已经确定的变量和模型参数进行,确保差异点仅在数据集不同,会为每一份模型报告默认开启,比如在模型分析报告v1-lgb.xlsx的目录可以看到大量交叉验证相关的图和表:

交叉验证结果目录
交叉验证结果目录

页面较多不一一截图展示了,可自行浏览,这些内容会从不同维度来评价模型

数据集选择
一般来说交叉验证是使用分割后数据量较多的一个数据集训练,较少的数据集验证,但有一个技巧是使用较少的数据集来训练,有一种说法是如果数据量较大细微的数据特征不容易体现出来,如果有这方面的需求,可以通过调整参数use_larger_training来实现

参数搜索是通过4.2.2 自定义参数入参customized_parameter来实现的,同时也继承了4.3 模型交叉训练中的模型训练机制,最基础的用例如下:

at.Analysis.model_flow(
    df_train, "./模型分析demo/v9-lgb/v9-lgb.xlsx", test_data=df_test, 
    # 目标定义 & 切片定义
    response="target", split_col_name="birth_year", use_train_time=True,
    exclude_column=["ID", "weight"],  # 排除权重
    add_info="ID",  # 主键添加
    # 指定使用训练的class并不进行打分转化
    model_type_path="lightgbm.sklearn.LGBMClassifier",
    convert_score=False,
    customized_parameter={
        "n_estimators": [10, 20, 30, 40, 50],
        "learning_rate": [0.05, 0.08, 0.10]
    }
)
参数搜索入参示例
参数搜索入参示例

默认是使用网格搜索,会在报告模型分析报告v9-lgb.xlsx模型评估 - 参数搜索汇总模型评估 - 参数搜索绘图页面看到参数搜索相关的信息:

参数搜索结果示例
参数搜索结果示例

从页面模型评估 - 最优搜索参数可知,默认网格搜索下目前的最优参数为:{"n_estimators": 10, "learning_rate": 0.08}

就像训练模型需要有目标函数一样,参数搜索需要有一个具体的方向,相关参数如下:

    # 模型参数搜索
    # cache_same_params                 选填,默认True,交叉验证搜索时缓存相同参数的模型搜索结果,不建议调整
    # model_search_target               选填,默认{"binary": "roc_auc_score", "regression": "mean_squared_error"}
    # model_search_params               选填,默认{"binary": ["mean / std", -1], "regression": ["mean * std", 0]}
选择搜索方向

这里主要用到的参数有model_search_targetmodel_search_params这两个,解读如下:

  • 参数model_search_target:这个参数主要用来确认模型性能评价的标准,可选范围参考__cv_metrics__["binary_func_range"]__cv_metrics__["regression_func_range"]
  • 参数model_search_params:因为继承了4.3 模型交叉训练中的训练方式,因此每次搜索会生成多个结果,需要基于这些结果进行聚合并取最大或最小,有两个元素组成:
    • 聚合函数范围在__cv_metrics__["eval_func_range"]中定义,包含:"mean / std""mean * std""mean""std""max""min"
    • 方向性在__cv_metrics__["func_range_place"]中定义,包含:0-10代表最小,-1代表最大
默认入参解读
  • 针对二分类问题:取模型roc_auc_score结果,并取交叉验证结果的mean/std的最大值
  • 针对回归问题:取模型mean_squared_error结果,并取交叉验证结果的mean*std的最小值

当然了,有些业界的做法,认为机器学习建模的逻辑是只要max足够大,即使衰减了也会足够大,就可以不考虑稳定性了,假如我希望基于ksmax,那就可以改成这样的入参:

at.Analysis.model_flow(
    df_train, "./模型分析demo/v10-lgb/v10-lgb.xlsx", test_data=df_test, 
    # 目标定义 & 切片定义
    response="target", split_col_name="birth_year", use_train_time=True,
    exclude_column=["ID", "weight"],  # 排除权重
    add_info="ID",  # 主键添加
    # 指定使用训练的class并不进行打分转化
    model_type_path="lightgbm.sklearn.LGBMClassifier",
    convert_score=False,
    customized_parameter={
        "n_estimators": [10, 20, 30, 40, 50],
        "learning_rate": [0.05, 0.08, 0.10]
    },
    model_search_target={"binary": "ks"},
    model_search_params={"binary": ["max", -1]}
)
搜索方向入参示例
搜索方向入参示例

从得到的模型报告模型分析报告v10-lgb.xlsx来看,最优参数变成了{"n_estimators": 50, "learning_rate": 0.10}

页面模型评估 - 指标汇总上的结果显示,模型结果确实是优于模型分析报告v9-lgb.xlsx的,这可能不仅仅是model_search_targetmodel_search_params调整的原因,也有可能是因为customized_parameter的搜索域较小,且没启用算法参数搜索

在搜索方向之外,还涉及到的模型搜索的参数有:

    # 模型参数搜索
    # model_search_method               选填,默认"grid",参数搜索方式,参考范围__pattern__["search_method_range"]
    # early_stop_iteration              选填,默认50,交叉验证搜索时最后N轮迭代无提升则提前终止搜索,适用于所有算法
    # min_iteration_number              选填,默认2,model_search_number和early_stop_iteration允许的最小搜索迭代次数
    # stop_certainty_search             选填,默认False,是否对确定性搜索算法启用early_stop_iteration,指"order"/"grid"

网格搜索做为常见的搜索算法,搜索虽然全,但比较大缺点就是时间复杂度太高了,先尝试一下加大搜索域:

at.Analysis.model_flow(
    df_train, "./模型分析demo/v11-lgb/v11-lgb.xlsx", test_data=df_test, 
    # 目标定义 & 切片定义
    response="target", split_col_name="birth_year", use_train_time=True,
    exclude_column=["ID", "weight"],  # 排除权重
    add_info="ID",  # 主键添加
    # 指定使用训练的class并不进行打分转化
    model_type_path="lightgbm.sklearn.LGBMClassifier",
    convert_score=False,
    customized_parameter={
        "n_estimators": list(range(5, 200, 10)),
        "learning_rate": [i/100 for i in range(5, 15, 1)],
    }
)
网格搜索入参示例
网格搜索入参示例

从模型结果报告模型分析报告v11-lgb.xlsx模型评估 - 指标汇总来看效果甚至还不如模型分析报告v9-lgb.xlsx,一方面是搜索域仅仅只有两个参数,另外搜索方向也是偏向稳定的方向,还有可能和用到的模型特性也有关系,

但即便如此,目前按grid计算就需要:20(n_estimators)*10(learning_rate)*3(birth_year切片) = 600 次建模计算,这还是在lightgbm高性能的前提下实现的,搜索参数范围继续扩展性能压力会很大,因此grid仅适用于小范围参数搜索

提前停止搜索
默认会为所有算法开启early_stop_iteration,即在最后的N轮搜索内没有性能提升则终止,默认还为确定性的搜索算法gridorder启用了stop_certainty_search来避免提前停止搜索,如果有需要可自行开启

顺序搜索会按customized_parameter定义的list参数的顺序按从上到下、从左到右的顺序来进行搜索的方法,每个参数列表搜索完成后,选取列表中效果最优的参数,参与下一个参数的搜索过程,涉及的参数有:

    # auto_search_loop                  选填,默认3,自动cv参数循环的重复次数,"order"/"order-random"时启用
    # auto_search_limit                 选填,默认5,auto_search_loop的上限值,"order"/"order-random"时启用

这次把搜索域拓展到了5个参数,搜索方向也调整成max,代码如下:

at.Analysis.model_flow(
    df_train, "./模型分析demo/v12-lgb/v12-lgb.xlsx", test_data=df_test, 
    # 目标定义 & 切片定义
    response="target", split_col_name="birth_year", use_train_time=True,
    exclude_column=["ID", "weight"],  # 排除权重
    add_info="ID",  # 主键添加
    # 指定使用训练的class并不进行打分转化
    model_type_path="lightgbm.sklearn.LGBMClassifier",
    convert_score=False,
    customized_parameter={
        "n_estimators": list(range(5, 200, 10)),
        "learning_rate": [i/100 for i in range(5, 15, 1)],
        "max_depth": [3, 4, 5, -1],
        "min_child_samples": list(range(20, 55, 5)),
        "min_child_weight": [i/1000 for i in range(1, 11, 1)],
    },
    model_search_method="order",
    model_search_params={"binary": ["max", -1]}
)
顺序搜索入参示例
顺序搜索入参示例

目前模型训练总次数就变成:(20+10+4+7+10)*3 = 153,和之前的600次相比大大减少,

lightgbm顺序搜索对比示例
lightgbm顺序搜索对比示例

模型分析报告v12-lgb.xlsx来看效果提升明显,实现了较少的搜索次数找到了一个局部最优解,效果和模型分析报告v7-lgb.xlsx也挺接近的,而且因为用了max搜索,树模型的特性就是这样的,暴力迭代会过拟合,但至少衰减后底线也会高一点

虽然前需的顺序搜索已经有不错的效果, 但因为搜索只集中在确定的点上,这里来使用随机搜索来进行随机搜索,参数如下:

    # 模型参数搜索
    # model_search_number               选填,默认0.75,float为"grid"的百分比,int为迭代数,不适用于"order"/"grid"
    # random_search_number              选填,默认True,在数值参数的上下限随机搜索入参数值,不适用于"order"/"grid"
    # random_number_precision           选填,默认3,当启用random_search_number时,限制float保留的小数点位数,不影响int

默认是会按customized_parameter入参定义的grid次数的model_search_number(float)的百分比取训练次数,或直接按model_search_number(int)定义的次数来随机搜索,

需要注意的是,此时early_stop_iteration是默认生效的,注意调整,继续延用上面的案例,微调后如下:

at.Analysis.model_flow(
    df_train, "./模型分析demo/v13-lgb/v13-lgb.xlsx", test_data=df_test, 
    # 目标定义 & 切片定义
    response="target", split_col_name="birth_year", use_train_time=True,
    exclude_column=["ID", "weight"],  # 排除权重
    add_info="ID",  # 主键添加
    # 指定使用训练的class并不进行打分转化
    model_type_path="lightgbm.sklearn.LGBMClassifier",
    convert_score=False,
    customized_parameter={
        "n_estimators": list(range(5, 200, 10)),
        "learning_rate": [i/100 for i in range(5, 15, 1)],
        "max_depth": [3, 4, 5, -1],
        "min_child_samples": list(range(20, 55, 5)),
        "min_child_weight": [i/1000 for i in range(1, 11, 1)],
    },
    model_search_method="random",
    model_search_params={"binary": ["max", -1]},
)
随机搜索入参示例
随机搜索入参示例

从模型报告模型分析报告v13-lgb.xlsx来看,最终效果上其实也差不多,不过random的搜索范围更有弹性,说明很有可能在这个特定的数据集和搜索域内lightgbm的效果差不多到极限了

除了上面实现的算法,超参数搜索开源的模块也很多,底层同时还集成了hyperoptscikit-optimize,同时还可以使用model_search_methodsearch_method_params参数来进一步定制调参:

    # 模型参数搜索
    # search_method_params              选填,默认{},当入参model_search_method使用"hyperopt"/"skopt"函数时的额外入参    
参数介绍

model_search_method可选的参数有:

search_method_params用在微调函数,比如当model_search_method="gp"时,希望修改底层调用的gp_minimizeacq_optimizer,则可设置search_method_params={"acq_optimizer": "EI"},这样就间接实现了bayesian-optimization

现在尝试改用邻近算法,并使用tpe搜索参数试试看效果,样例如下:

at.Analysis.model_flow(
    df_train, "./模型分析demo/v14-knn/v14-knn.xlsx", test_data=df_test, 
    # 目标定义 & 切片定义
    response="target", split_col_name="birth_year", use_train_time=True,
    exclude_column=["ID", "weight"],  # 排除权重
    add_info="ID",  # 主键添加
    # 指定使用训练的class并不进行打分转化
    model_type_path="sklearn.neighbors.KNeighborsClassifier",
    convert_score=False,
    customized_parameter={
        "n_neighbors": [3, 4, 5, 6, 7, 8],
        "weights": ['uniform', 'distance'],
        "leaf_size": [20, 30, 40, 50],
    },
    model_search_method="tpe",
    model_search_params={"binary": ["max", -1]},
)
集成模块入参示例
集成模块入参示例

从模型结果模型分析报告v14-knn.xlsx来看,和之前相比结果上没太大的波动,应该是这个数据集就这个效果了吧

从最开始1.4.1 评分卡建模中的模型分析报告v12.xlsx就能看到里面的结果是带有分数转化的,评分卡定义主要涉及的参数如下:

    # 评分卡相关
    # odds                              选填,默认1/15,None时为样本好坏比
    # precision                         选填,默认3,convert_score时默认为0
    # cut_method                        选填,默认"quantile",可配置"cumsum"
    # display_ks                        选填,默认20,汇总展示的计算参数配置
    # base_score                        选填,默认600,打分卡转换时的基础分数
    # double_score                      选填,默认50,打分卡转换时分数的翻倍分数
    # include_right                     选填,默认True,数值分组时的边界,默认含右侧值
    # convert_score                     选填,默认True,是否将模型预测的概率转化为评分卡结果
    # keep_raw_score                    选填,默认True,在convert_score为True时,是否保留原始分数
    # save_score_desc                   选填,默认True,输出打分卡的生成逻辑到表格,会包含详细信息
    # auto_valid_convert                选填,默认True,检测convert_score为True时是否可行,并自动调整参数

这些参数主要用在评分卡转化上,可能会需要修改的参数有base_scoredouble_scoreodds,其他的参数就建议不要修改了

注意事项
  • convert_scoreTrueauto_valid_convertTrue时,会自动校验是否满足评分卡转化的条件
    1. model_type_path定义的模型为statsmodels范围时,必须为statsmodels.discrete.discrete_model.BinaryModel才允许开启convert_score
    2. 不满足第一条时,model_type_path必须有predict_proba函数,才允许开启convert_score
    3. 如果是4.2.3 自定义模型,因model_type_path重置,永远不会开启convert_score
  • 完成上述检测后convert_score仍然为True时,则会调整precision0,优化评分输出和展示
    • 如果在训练阶段的运行中,分数转化后发现出现infnan等情况,也会把convert_score重置为False
    • 如果是测试或交叉验证阶段,出现上述情况会直接报错,并显示日志建议调整convert_scoreFalse
  • 评分卡的precision会在每个指标的打分上就生效,最后模型报告中的总分为每项小分round之后再sum的结果,与keep_raw_score提到的严格按概率转化的总分会有细微差异,实测这些差异对模型性能基本无影响

再来说一下怎么来实现交叉矩阵,这个一般用在评估模型更替上的影响,也用在决策评估上,参数如下:

    # 交叉矩阵分析
    # swap_compare_name                 选填,默认None,输入一列或多列与打分或概率进行交叉项分析
    # swap_length_limit                 选填,默认30,限制swap名称和离散值个数长度,避免绘图异常
    # maximum_swap_item                 选填,默认9,最大支持swap_item的数量,过多会导致性能问题
    # swap_precision                    选填,默认3,存在swap时的精度,不会随convert_score修改

参数swap_compare_nameadd_info类似,支持入参strlist,如果是入参list其实是实现了多次的swap分析,需要注意的是用在swap_compare_name中的指标会自动加入exclude_column,这里还是用原来的评分卡模型进行示例:

df_train, df_test = df_train.copy(), df_test.copy()
df_train["Age-swap"], df_test["Age-swap"] = df_train["Age"].copy(), df_test["Age"].copy()
df_train["sample1"] = np.random.rand(df_train.index.size)
df_train["sample2"] = np.random.uniform(0, 1, df_train.index.size)
df_test["sample1"] = np.random.rand(df_test.index.size)
df_test["sample2"] = np.random.uniform(0, 1, df_test.index.size)
at.Analysis.model_on_data(
    df_train, "./模型分析demo/v15/v15.xlsx", test_data=df_test,
    data_flow={
        **data_params, **{
            "save_or_return": False,  # 跳过csv的io读写提升性能
            "add_info": ["ID", "sample1", "sample2", "Age-swap"],  # model_flow才能读到
        },
    },
    model_flow={
        "split_col_name": "birth_year", "use_train_time": True,
        "exclude_column": ["ID", "weight"],  # 排除权重
        "swap_compare_name": ["sample1", "sample2", "Age-swap"],  # 直接伪造数据假设这三个维度需要swap
    }
)
swap入参示例
swap入参示例

可在模型分析报告v15.xlsx模型应用 - 交叉矩阵 - 散点图模型应用 - 交叉矩阵 - 数量分布 模型应用 - 交叉矩阵 - 百分比分布看到交叉矩阵相关的多类分析图表:

交叉矩阵分析示例
交叉矩阵分析示例

模型应用 - 交叉矩阵 - 散点图中,还会包含Pearson相关性Distance相关性,有助于横向对比和辅助决策

另外,还存在一些特殊的场景,比如模型横向对比或模型监控等需要,只需评估无需建模,则可以参数:

    # 模型定义
    # exists_proba_name                 选填,默认None,指定后会跳过建模过程,直接进行评估与绘图

指定该参数后,建模相关的参数都不会生效,比如:

df_train, df_test = df_train.copy(), df_test.copy()
df_train["sample1"] = np.random.rand(df_train.index.size)
df_train["sample2"] = np.random.uniform(0, 1, df_train.index.size)
df_test["sample1"] = np.random.rand(df_test.index.size)
df_test["sample2"] = np.random.uniform(0, 1, df_test.index.size)
at.Analysis.model_flow(
    df_train, "./模型分析demo/v16/v16.xlsx", test_data=df_test, 
    # 目标定义 & 切片定义
    response="target", split_col_name="birth_year", use_train_time=True,
    exclude_column=["ID", "weight"],  # 排除权重
    add_info="ID",  # 主键添加
    exists_proba_name="Age",  # 假设此处Age是需要进行评估的模型结果
    swap_compare_name=["sample1", "sample2"],  # 直接伪造数据假设这两个维度需要swap
)
非建模评估示例
非建模评估示例

模型报告可见模型分析报告v16.xlsx,有点像是把模型结果当做单变量进行分析,不过可同时叠加交叉矩阵、时间切片、更详尽的图表等,尤其适用于模型监控等场景

参数与零基础实现高质量数据挖掘(数据分析篇)- 6.1 报告格式调整一样:

输出文档数据分析报告v12.xlsx中的大量格式和颜色都是可以进行修改的,涉及到的自动格式调整参数如下:

       # 自动格式调整
       # add_excel_table_dir               选填,默认True,给输出的excel表格自动添加目录
       # train_test_dir_cn                 选填,默认None,即["开发", "验证"],train和test中文
       # deal_unused_input                 选填,默认"error",处理无效入参的方式,可选"warning"
       # text_wrap_trigger                 选填,默认36,在auto_formatting时对超过长度的单元格自动换行
       # auto_formatting                   选填,默认True,自动添加条件格式、设置单元格格式、添加表头格式
       # data_bar_cols                     选填,默认["rate", "woe", "corr"],条件格式的列,支持str或list
       # format_header                     选填,默认True,自定义输出表的表头格式,参数通过查看__header__来更改
       # hidden_tables                     选填,默认None,自定义输出文件中,需要隐藏的表格的名称,支持str或list
       # hidden_cols                       选填,默认["var_name", "var_new"],输出多表格时自动隐藏的列,支持str或list

主要包含:自动目录添加、入参校验、自动条件格式、长文本自动换行,自动单元格格式、自动生成数据栏、自动隐藏不必要的表和列等,

同时也支持手动修改默认表格输出的参数,默认参数在at.dt.__excel__这个位置可见:

正常情况下无需修改,如无必要不建议修改,不恰当的入参可能会引发意料之外的报错

默认值均一致,除参数data_bar_cols有调整:

    # data_bar_cols                     选填,默认["rate", "woe", "vif", "score", "corr"],默认条件格式的列

全局参数位置改成at.mt.__excel__,同样也不建议调整

零基础实现高质量数据挖掘(数据分析篇)- 6.2 数据文件输出中类似,不仅仅会输出csv文件,还会输出模型部署的代码,包括sqlpmml等,

首先是涉及报告内容输出调整的参数:

    # 分析输出相关
    # drawing                           选填,默认True,输出可视化绘图结果
    # add_info                          选填,默认None,添加列,支持str和list
    # save_vif                          选填,默认True,计算变量的vif输出结果
    # save_corr                         选填,默认True,输出变量的相关性系数表的结果
    # save_json                         选填,默认True,将计算结果配置文件输出为json
    # add_woe_iv                        选填,默认False,是否在表中展示分组的woe和iv值
    # add_ind_ks                        选填,默认True,计算将不确定值视为good的ks数值
    # add_cap_info                      选填,默认True,是否在表中展示组内占比的百分比列
    # add_cum_left                      选填,默认True,是否在表中展示累计与剩余目标占比
    # add_cum_info                      选填,默认True,是否在表中展示累计个数与累计占比
    # add_cum_odds                      选填,默认True,是否在结果表中展示累计好坏客户比
    # add_gini_lift                     选填,默认True,是否在表中展示gini和lift的计算值

然后是涉及到模型文件输出的参数:

    # 模型文件输出
    # save_model_pkl                    选填,默认True,将计算的模型的结果存储为pkl文件导出
    # save_model_pmml                   选填,默认True,使用sklearn2pmml来转化模型为pmml模型
    # save_score_pmml                   选填,默认True,解析评分卡分数并转化为对应的pmml文件
    # save_stepwise_pkl                 选填,默认False,输出符合筛选的逐步回归的所有变量组合
    # save_cv_model_pkl                 选填,默认False,将enable_train_fold的模型结果存为pkl文件
    # save_cv_search_pkl                选填,默认False,将交叉验证中间过程的搜索参数等保存为pkl文件
文件输出依赖
  1. 参数save_model_pmml调用的sklearn2pmml底层需要依赖java,而save_score_pmml不需要java支持
  2. 另外在drawing时,底层调用的matplotlib也需要中文字体安装,需提前配置

关于评分卡逻辑,除了可以使用save_score_pmml把模型输出为pmml文件外,还可以输出模型逻辑对应的sqlpython部署文件:

    # 分数输出相关
    # save_score_py                     选填,默认True,解析分数为对应的py部署逻辑
    # save_score_sql                    选填,默认True,解析分数为对应的sql部署逻辑
    # sql_has_boolean                   选填,默认True,是否按bool值转化score中的字符串
    # else_score_value                  选填,默认-999999,模型打分逻辑不包含时的异常分值

最后是需要保存模型打分结果到文件时的参数:

    # 模型评分输出
    # save_total_score                  选填,默认False,输出输入数据的总分以及概率到csv文件
    # save_detail_score                 选填,默认False,输出输入数据的明细打分结果到csv文件
    # save_raw_dataframe                选填,默认False,输出输入数据的筛选原始数据到csv文件

参数都比较简单,不一一举例了,按需入参即可

执行pmml文件

请参考https://github.com/jpmml/jpmml-evaluator,安装java,下载需要的jar包,使用命令行即可执行最基础的模型打分操作,比如:

java -cp pmml-evaluator-example-executable-xxx.jar org.jpmml.evaluator.example.EvaluationExample --model model.pmml --input input.csv --output output.csv

只看参数和用法的话,与零基础实现高质量数据挖掘(数据分析篇)- 6.3 配置映射和还原是完全一样的:

除了常规输出的csvxlsx文件外,还会输出配置文件jsonpkl,用来避免配置文件的重复输入,涉及的主要参数有:

       # 从配置文件还原
       # recover_from_json                 选填,默认None,输入对应的.json文件来从对应的json映射结果
       # recover_from_pkl                  选填,默认True,默认从pkl完整映射上次计算结果,overwrite时失效
       # overwrite_json                    选填,默认False,使运行时的入参设置覆盖读取的json文件的对应设置

例如之前数据分析报告v12.xlsx搭配的配置文件有Data_v12.jsonData_v12_data.pkl,如果打开可以看到里面主要都是之前定义的参数和分析过程中的缓存对象:

配置文件的主要使用方式举例如下:

at.Analysis.data_flow(
       df_train, "./数据分析demo/v12-rec/v12-rec.xlsx", test_data=df_test,
       recover_from_json="./数据分析demo/v12/Data_v12.json"
)
从配置文件还原
从配置文件还原

从结果文件数据分析报告v12-rec.xlsx来看,和之前数据分析报告v12.xlsx效果相同的情况下,节省了很多笔墨

配置恢复备注
  1. 参数recover_from_pkl用在仅100%恢复之前的配置文件,且不进行覆写调整的场景下使用
  2. 有效使用recover_from_pkl时,不再会经过判断和过滤,因此不生成变量评估 - 排除详情页面
  3. 如果需要基于配置文件进行调整,不建议修改json文件(可能会打破jsonpkl文件之间的数据关联),推荐直接入参需要调整的参数
  4. 只要覆写入参和之前不同,就会自动启用overwrite_json,但只有当判定覆写入参影响训练结果时,才会自动禁用recover_from_pkl,此时并非100%还原历史数据,而是基于当下数据和历史配置重新训练
  5. overwrite_json的对象为list时,且入参为"add_info""node_info"时,会对参数进行合并去重而不是覆写
  6. overwrite_json的对象为dict时,进行update操作而不是覆写
  7. 一开始自定义的customized_groups除非入参覆盖不然都会还原,由算法生成入参的customized_groups会在判定覆写入参影响训练结果时被替换为重新训练以后的值

data_flow的处理逻辑类似,如果检测到覆写的参数会影响训练结果时,就会禁用recover_from_pkl并重新训练,用例如下:

at.Analysis.model_flow(
    df_train, "./模型分析demo/vr-torch/vr-torch.xlsx", test_data=df_test, 
    recover_from_json="./模型分析demo/v8-torch/Model_v8-torch.json"
)
从配置文件还原
从配置文件还原

即可节约一堆定义的代码,按之前报告模型分析报告v8-torch.xlsx的参数定义来执行,从而得到模型报告模型分析报告vr-torch.xlsx,需要注意的是,因为pytorch是无法直接保存成pkl文件的,针对这个案例的recover_from_pkl是无法生效的

此部分与零基础实现高质量数据挖掘(数据分析篇)- 6.4 多数据映射和还原的架构一致:

在上面6.3 配置映射和还原这节中介绍了配置文件的还原,但是如2.5 多数据映射所述,现实使用时情况会比较复杂,这里再介绍一下多数据映射情况下的还原方式,主要参数如下:

       # alpha_tools.Analysis参数
       # recover_path                      选填,默认None,入参str需要以.xlsx结尾,根据输出文件还原json和pkl配置
       # recover_search                    选填,默认True,按train_name和test_names入参联动调整recover_path路径取值
       # only_recover_json                 选填,默认False,在使用recover_path还原时是否忽略pkl,只按json文件还原配置

这里来看一个稍微复杂的案例,其中output_name2处与直接修改5.7 分箱搜索和过滤处的cut_method入参效果相同:

# 使用ori_name配置映射到output_name1
# 再使用output_name1配置映射到output_name2(这一步train_name&test_names需要1&2一致)
ori_name = "./数据分析demo/v12/v12.xlsx"
output_name1 = "./数据分析demo/v12-rec-more/v12-one.xlsx"
output_name2 = "./数据分析demo/v12-rec-more/v12-other.xlsx"
# 按配置历史数据100%还原
at.Analysis.data_flow(
       df_train, output_name1,
       test_data=[df_test, df_data], train_name="开发", test_names=["测试", "全量"],
       recover_path=ori_name
)
# 继承配置并按"tree"重新训练
at.Analysis.data_flow(
       df_train, output_name2,
       test_data=[df_test, df_data], train_name="开发", test_names=["测试", "全量"],
       recover_path=output_name1, cut_method="tree"
)
# 打包结果
at.dt.Api.zip_dir_files("./数据分析demo/v12-rec-more.zip", "./数据分析demo/v12-rec-more")
多数据映射与还原
多数据映射与还原

这样就实现了多文件的映射与还原,打包文件详情可以自行下载v12-rec-more.zip查看

参数在一样的同时,多了一项继承的union_variable,参考如下:

    # alpha_tools.Analysis.model_on_data参数
    # union_variable                    选填,默认False,仅恢复入模参数指标,True时在recover_path还原时恢复全量指标

用法也是一样的,样例如下:

ori_name = "./模型分析demo/v4-lgb/v4-lgb.xlsx"
new_output = "./模型分析demo/vr-lgb/vr-lgb.xlsx"
at.Analysis.model_flow(
    df_train, new_output,
    test_data=[df_test, df_data], train_name="开发", test_names=["测试", "全量"],
    recover_path=ori_name, 
    # 继承 v4 并进行参数搜索,映射到多个数据集
    customized_parameter={
        "n_estimators": [10, 20, 30, 40, 50],
        "learning_rate": [0.05, 0.08, 0.10]
    }
)
# 打包结果
at.dt.Api.zip_dir_files("./模型分析demo/vr-lgb.zip", "./模型分析demo/vr-lgb")
多数据映射与还原
多数据映射与还原

这样和模型分析报告v9-lgb.xlsx的入参是一致的,压缩文件可下载vr-lgb.zip查看

这一部分同样也可以参考零基础实现高质量数据挖掘(数据分析篇)- 6.5 报告合并与数据合并中的说明:

在上面5.2 分箱算法和生成这节中,我们看到很多时候是需要横向对比多个报告的,因此存在针对多份报告和数据进行自动合并的需求,实现了既可以合并报告内的页面,也可以将各个分析的数据集移动并重命名到同一路径,主要通过以下两个函数实现:

报告合并函数说明
报告合并函数说明

横向合并需要手动指定,比如之前5.2 分箱算法和生成这节中使用方式为:

at.Report.create_data_report(
       "./数据分析demo/v9-算法对比/v9-算法对比.xlsx", [
           "./数据分析demo/v9-cumsum/v9-cumsum.xlsx", "./数据分析demo/v9-quantile/v9-quantile.xlsx",
           "./数据分析demo/v9-linspace/v9-linspace.xlsx", "./数据分析demo/v9-kmeans/v9-kmeans.xlsx",
           "./数据分析demo/v9-bestks/v9-bestks.xlsx", "./数据分析demo/v9-tree/v9-tree.xlsx",
           "./数据分析demo/v9-chimerge/v9-chimerge.xlsx", "./数据分析demo/v9-ratemerge/v9-ratemerge.xlsx"
       ], ["cumsum", "quantile", "linspace", "kmeans", "bestks", "tree", "chimerge", "ratemerge"]
)
文件横向合并
文件横向合并

最后得到的效果和对应的分析,也在5.2 分箱算法和生成这节中有所展示:

算法效果对比
算法效果对比(DAYS_EMPLOYEDOCCUPATION_TYPE

而针对2.5 多数据映射6.4 多数据映射和还原中提到的多数据情况,需要进行纵向合并,简单输入:

at.Report.create_data_report2(
       "./数据分析demo/v12-纵向合并/v12-纵向合并.xlsx", "./数据分析demo/v12-rec-more/v12-one.xlsx",
       train_name="开发", test_names=["测试", "全量"]
)
文件纵向合并
文件纵向合并

即可实现多数据映射层面的纵向文件合并,效果如下:

纵向合并效果示例
纵向合并效果示例

另外如果涉及到横向合并多个纵向数据集的报告,则可输入:

at.Report.create_data_report2(
       "./数据分析demo/v12-纵横合并/v12-纵横合并.xlsx",
       {
           "plan1": "./数据分析demo/v12-rec-more/v12-one.xlsx",
           "plan2": "./数据分析demo/v12-rec-more/v12-other.xlsx"
       },
       train_name="开发", test_names=["测试", "全量"]
)
文件纵横合并
文件纵横合并

实现多方案多数据集综合交叉对比,排序后的效果截图如下:

纵横合并效果示例
纵横合并效果示例

简单来看,部分数据上tree算法还是有些许提升效果的,

小技巧
  1. 报告合并的sheetname范围和csv搜索的范围均在在__excel__中定义

  2. 如果是在win环境下,会多一页附录,原理是使用pywin32调用excel把文件做为附件插入

报告附录页windows
报告附录页(windows

最后附上以上三类报告合并的样例文件:

压缩输出合并文档
压缩输出合并文档

只要把create_data_report替换成create_model_report即可,

报告合并函数说明
报告合并函数说明

其实如果单次跑的快的话,这部分的功能用到的机会也少很多,这里把上面几个案例的报告提取一下:

at.Report.create_model_report(
    "./模型分析demo/vm-模型对比/vm-模型对比.xlsx", [
        "./模型分析demo/v1-lgb/v1-lgb.xlsx", "./模型分析demo/v2-lr/v2-lr.xlsx",
        "./模型分析demo/v3-lgb/v3-lgb.xlsx", "./模型分析demo/v4-lgb/v4-lgb.xlsx",
        "./模型分析demo/v5-gls/v5-gls.xlsx", "./模型分析demo/v6-lgb/v6-lgb.xlsx",
        "./模型分析demo/v6-lgb-oh/v6-lgb-oh.xlsx", "./模型分析demo/v7-lgb/v7-lgb.xlsx",
        "./模型分析demo/v8-torch/v8-torch.xlsx", "./模型分析demo/v9-lgb/v9-lgb.xlsx",
        "./模型分析demo/v10-lgb/v10-lgb.xlsx", "./模型分析demo/v11-lgb/v11-lgb.xlsx",
        "./模型分析demo/v12-lgb/v12-lgb.xlsx", "./模型分析demo/v13-lgb/v13-lgb.xlsx",
        "./模型分析demo/v14-knn/v14-knn.xlsx",
    ], [
        "v1-lgb", "v2-lr", "v3-lgb", "v4-lgb", "v5-gls",
        "v6-lgb", "v6-lgb-oh", "v7-lgb", "v8-torch", "v9-lgb",
        "v10-lgb", "v11-lgb", "v12-lgb", "v13-lgb", "v14-knn",
    ]
)
文件合并示例
文件合并示例

从合并以后的模型对比文件vm-模型对比.xlsx来看,

模型对比结果汇总
模型对比结果汇总

怎么说呢,效果还是要看数据啦,数据永远是算法的天花板,算法可以不断逼近却永远无法突破

和上一篇文章一样,提供完整的工作簿:零门槛模型分析.ipynb,对应的html也一并提供:零门槛模型分析.html

如何重新运行整个ipynb文件

某些场景下,比如模块有新特征更新、数据有更新等,ipynb中的所有代码需要重新执行,网上能搜索到很多方法,这里列几个参考方案如下:

  1. 登录jupyter服务的前端web页面后,打开ipynb文件后使用Ctrl + A全选并执行,但如果是基于远程vps,且网络状况不佳很可能会执行一半中断或页面展示不全,推荐本地服务环境使用
  2. 登录后台并使用screen等,在ipython中运行,比如:%run <notebook>.ipynb,这个方式比上面好点,不会受网络影响、有详细的交互信息,但执行后只会更新模型文件,不会更新ipynb文件本身,推荐在只需要更新结果文件时使用
  3. 还是基于screen,执行方式改为终端jupyter nbconvert --execute --clear-output <notebook>.ipynb,和上一条相比的优点是通过--clear-output覆盖更新ipynb文件的输出信息,缺点是运行过程中没有ipython交互的日志信息,推荐在确认无代码错误并需要同时更新ipynb输出信息时使用

本篇信息量比上一篇大很多,全部的代码就不重复粘贴了,请按需仔细阅读各章节内容,有兴趣请邮件或留言咨询