4. 客户基础特征构建:客户类型、行业、地区、风险等级、开户时长、账户状态等静态特征编码与归一化

客户基础特征是 AML 规则引擎中最稳定、最易获取的一类特征。它们通常来自 KYC(Know Your Customer)数据,在客户生命周期内变化频率低,因此被称为“静态特征”。本章将系统讲解如何对客户类型、行业、地区、风险等级、开户时长、账户状态等核心静态特征进行编码与归一化,使其成为规则模型可用的数值化输入。

4.1 静态特征分类与原始数据形态

在开始特征工程之前,需要明确各类静态特征的原始数据类型。不同数据类型决定了后续编码与归一化的技术路线。

特征名称 原始数据类型 常见取值示例 特征工程目标
客户类型 类别型(无序) 个人、企业、个体工商户、政府机构 One-Hot / Label Encoding
行业 类别型(有序/无序) 金融、房地产、珠宝、贸易、科技 Target Encoding / Frequency Encoding
地区 类别型(有序/无序) 北京、上海、深圳、纽约、伦敦 One-Hot / 风险等级映射
风险等级 有序类别型 低风险、中风险、高风险、极高风险 Ordinal Encoding
开户时长 数值型(连续) 30天、180天、365天、1095天 归一化 / 分箱
账户状态 类别型(有序) 正常、休眠、冻结、销户 Ordinal Encoding
特征工程原则:类别型特征优先考虑业务语义与模型兼容性。对于树模型(如 XGBoost、LightGBM),Label Encoding 通常足够;对于线性模型或神经网络,必须使用 One-Hot Encoding 或 Embedding。

4.2 类别型特征的编码方法

4.2.1 One-Hot Encoding(独热编码)

适用于无序类别型特征,如客户类型、地区。将每个类别扩展为一个独立的二元特征(0/1)。

# Python 示例:使用 pandas 进行 One-Hot Encoding
import pandas as pd

df = pd.DataFrame({
    'customer_type': ['个人', '企业', '个体工商户', '个人', '企业'],
    'region': ['北京', '上海', '深圳', '北京', '广州']
})

# One-Hot Encoding
df_encoded = pd.get_dummies(df, columns=['customer_type', 'region'], prefix=['type', 'region'])
print(df_encoded.columns.tolist())
# 输出:['type_个人', 'type_企业', 'type_个体工商户', 'region_北京', 'region_上海', 'region_深圳', 'region_广州']
注意维度爆炸:当类别基数较大时(如地区有 300+ 个取值),One-Hot Encoding 会导致特征维度急剧膨胀。此时建议先进行低频类别合并(如将出现次数 < 100 的类别归为“其他”),或改用 Target Encoding。

4.2.2 Ordinal Encoding(有序编码)

适用于有序类别型特征,如风险等级、账户状态。编码时需保持业务顺序,避免模型学到错误的排序关系。

风险等级 业务顺序 Ordinal 编码值
低风险 1 1
中风险 2 2
高风险 3 3
极高风险 4 4
# Python 示例:手动映射有序编码
risk_mapping = {'低风险': 1, '中风险': 2, '高风险': 3, '极高风险': 4}
df['risk_level_encoded'] = df['risk_level'].map(risk_mapping)

status_mapping = {'正常': 1, '休眠': 2, '冻结': 3, '销户': 4}
df['account_status_encoded'] = df['account_status'].map(status_mapping)

4.2.3 Target Encoding(目标编码)

适用于高基数类别特征,如行业、地区。用目标变量(如是否涉洗钱)的统计量(均值、中位数)替换原始类别。

# Python 示例:Target Encoding(需配合交叉验证防止过拟合)
from category_encoders import TargetEncoder

encoder = TargetEncoder(cols=['industry', 'region'])
df['industry_target_enc'] = encoder.fit_transform(df['industry'], df['is_aml_flag'])
df['region_target_enc'] = encoder.fit_transform(df['region'], df['is_aml_flag'])
行业特征工程实战建议:AML 场景中,某些行业(如珠宝、房地产、虚拟货币)天然具有更高的洗钱风险。建议在 Target Encoding 基础上叠加“行业风险权重”作为辅助特征,权重由合规专家根据 FATF 指引设定。

4.3 数值型特征的归一化与分箱

4.3.1 开户时长归一化

开户时长(Account Age)是连续数值型特征,取值范围可能从 0 天到数十年。直接输入模型会导致量纲差异,需进行归一化。

归一化方法 公式 适用场景 输出范围
Min-Max 归一化 (x - min) / (max - min) 特征值有明确上下界 [0, 1]
Z-Score 标准化 (x - μ) / σ 特征值分布近似正态 无界(均值 0,标准差 1)
分箱(Binning) 将连续值离散化为区间 需要捕捉非线性效应 类别型
# Python 示例:开户时长归一化与分箱
from sklearn.preprocessing import MinMaxScaler, StandardScaler
import numpy as np

# 原始数据
account_age_days = np.array([30, 180, 365, 730, 1095, 1825, 3650]).reshape(-1, 1)

# Min-Max 归一化
scaler_minmax = MinMaxScaler()
age_minmax = scaler_minmax.fit_transform(account_age_days)

# Z-Score 标准化
scaler_std = StandardScaler()
age_zscore = scaler_std.fit_transform(account_age_days)

# 分箱(业务含义分箱)
bins = [0, 90, 365, 730, 1825, np.inf]
labels = ['新开户', '短期', '中期', '长期', '老客户']
df['account_age_bin'] = pd.cut(df['account_age_days'], bins=bins, labels=labels)

4.3.2 账户状态与开户时长的交叉特征

单一特征往往不足以刻画风险。将账户状态与开户时长组合,可以构建更有区分度的交叉特征。

客户静态特征编码与归一化流程 客户类型 行业 地区 风险等级 开户时长 账户状态 One-Hot Encoding Target Encoding Ordinal Encoding Min-Max / Z-Score 归一化后的数值特征矩阵(输入模型) 特征工程输出:每个客户对应一个固定长度的数值向量,供规则引擎或机器学习模型使用

4.4 特征归一化的业务考量

在 AML 场景中,归一化不仅仅是数学变换,还需要结合业务含义进行决策。

  • Min-Max 归一化:适用于开户时长、交易金额等有明确业务上下界的特征。例如,开户时长上限可设为 3650 天(约 10 年),超过该值的客户统一视为“老客户”。
  • Z-Score 标准化:适用于分布相对对称的特征。若开户时长分布严重右偏(大量新客户 + 少量老客户),建议先做对数变换再标准化。
  • 分箱(Binning):当特征与目标变量存在非线性关系时(如新开户和极老客户风险较高,中期客户风险较低),分箱可以捕捉这种 U 型关系。
实战案例:某银行 AML 项目中发现,开户时长在 0-30 天的客户涉洗钱概率是 30-365 天客户的 3.2 倍。通过将开户时长分箱为“新开户(0-30天)”、“短期(31-365天)”、“中期(366-1095天)”、“长期(1096天以上)”四个区间,模型 AUC 提升了 0.07。

4.5 特征编码后的维度管理与存储

完成编码与归一化后,需要将特征存储为模型可读取的格式。推荐使用稀疏矩阵存储 One-Hot 编码后的高维特征,以节省内存。

# Python 示例:使用稀疏矩阵存储 One-Hot 编码结果
from scipy.sparse import hstack, csr_matrix
from sklearn.preprocessing import OneHotEncoder

# 原始类别特征
cat_features = df[['customer_type', 'region']].values

# One-Hot 编码(返回稀疏矩阵)
encoder = OneHotEncoder(sparse_output=True, handle_unknown='ignore')
cat_encoded = encoder.fit_transform(cat_features)

# 数值特征(已归一化)
num_features = csr_matrix(df[['account_age_scaled', 'risk_level_encoded']].values)

# 合并为最终特征矩阵
final_features = hstack([cat_encoded, num_features])
print(f"最终特征矩阵形状: {final_features.shape}")
# 输出:最终特征矩阵形状: (100000, 45)

4.6 本章小结

特征 推荐编码方法 归一化/后处理 注意事项
客户类型 One-Hot Encoding 无需归一化 类别数少(通常 < 10),直接独热
行业 Target Encoding + 行业风险权重 无需归一化 高基数时合并低频类别
地区 One-Hot Encoding 或 风险等级映射 无需归一化 可结合 FATF 高风险地区列表
风险等级 Ordinal Encoding Min-Max 到 [0,1] 保持业务顺序,不可逆序
开户时长 分箱 + Ordinal Encoding Min-Max 或 Z-Score 注意长尾分布,建议对数变换
账户状态 Ordinal Encoding 无需归一化 冻结/销户状态需特殊处理