3、权重设置策略:基于类别比例的权重、基于样本难度的权重(如AdaBoost思想)、自定义权重函数
好,咱们接着聊权重设置。说实话,权重这玩意儿,是处理不平衡问题的「最后一公里」。前面你用了再好的采样方法,到了建模这一步,权重没设对,照样白搭。
我个人习惯把权重策略分成三类:基于类别比例的、基于样本难度的、以及自定义的。咱们一个一个说。
3.1 基于类别比例的权重
这个最直观。说白了就是:少数类样本,每个顶好几个用。
比如你的正负样本比是1:99,那正样本的权重就设成99,负样本设成1。这样模型在训练时,正样本的「嗓门」就大了,不会被负样本淹没。
具体怎么算?常见的有两种:
- 逆频率法:权重 = 总样本数 / (类别数 × 该类样本数)
- 平衡模式:权重 = 多数类样本数 / 该类样本数
我在项目中遇到过一个问题:直接用逆频率法,结果模型在少数类上过拟合了。为什么?因为少数类样本太少,权重太大,模型死记硬背了那几个样本。后来我加了个截断,把权重上限设为10,效果就好了很多。
代码实现也很简单,以逻辑回归为例:
from sklearn.linear_model import LogisticRegression
# 方法1:直接用class_weight参数
model = LogisticRegression(class_weight='balanced')
# 方法2:手动计算权重
from sklearn.utils.class_weight import compute_class_weight
weights = compute_class_weight('balanced', classes=[0,1], y=y_train)
weight_dict = {0: weights[0], 1: weights[1]}
model = LogisticRegression(class_weight=weight_dict)
3.2 基于样本难度的权重(AdaBoost思想)
这个就高级一点了。类别比例权重是「一刀切」——所有正样本权重一样,所有负样本权重一样。但现实是,有些正样本很容易分对,有些很难。你想想看,是不是该给难样本更大的权重?
这就是AdaBoost的核心思想:让模型更关注那些被分错的样本。
具体做法是这样的:
- 先训练一个弱分类器(比如决策树桩)
- 找出被分错的样本
- 提高这些样本的权重
- 再训练下一个分类器
- 重复,直到达到指定轮数
嗯,这里要注意:AdaBoost本身是一个集成方法,但我们可以借用它的「难度权重」思想,用在单一模型上。
我曾经在一个信贷评分卡项目里试过这个思路。当时有个客户群体,收入低但还款意愿强,模型老是误杀。我手动给这批样本加了1.5倍的权重,模型就学乖了。
代码示例:
import numpy as np
from sklearn.tree import DecisionTreeClassifier
# 模拟AdaBoost的权重更新过程
def adaboost_weights(X, y, n_estimators=10):
n_samples = len(y)
weights = np.ones(n_samples) / n_samples
for i in range(n_estimators):
model = DecisionTreeClassifier(max_depth=1)
model.fit(X, y, sample_weight=weights)
pred = model.predict(X)
# 计算错误率
error = np.sum(weights * (pred != y)) / np.sum(weights)
# 更新权重:分错的样本权重增加
alpha = 0.5 * np.log((1 - error) / (error + 1e-10))
weights *= np.exp(alpha * (pred != y))
weights /= np.sum(weights)
return weights
3.3 自定义权重函数
前面两种方法,说白了都是「通用方案」。但实际业务中,往往需要你根据业务逻辑来定制权重。
举个例子:在风控场景中,逾期30天的客户和逾期90天的客户,虽然都是「坏客户」,但严重程度完全不同。你总不能给他们一样的权重吧?
自定义权重函数,就是让你根据样本的业务属性来分配权重。比如:
- 逾期天数越长,权重越大
- 金额越大的样本,权重越大
- 近期样本比历史样本权重更大
我建议你这样做:
- 先确定业务上哪些维度重要(逾期天数、金额、时间等)
- 给每个维度设计一个权重函数(线性、指数、分段等)
- 把这些权重组合起来,得到最终权重
代码示例:
def custom_weight_function(df):
"""
自定义权重函数
参数:
df: 包含特征的数据框,必须有 'overdue_days' 和 'amount' 列
返回:
权重数组
"""
# 逾期天数权重:对数函数,避免差异过大
overdue_weight = np.log1p(df['overdue_days'])
# 金额权重:标准化后线性映射到[0.5, 2.0]
amount = df['amount']
amount_norm = (amount - amount.min()) / (amount.max() - amount.min())
amount_weight = 0.5 + 1.5 * amount_norm
# 时间权重:近期样本权重更大
# 假设有 'date' 列,越近权重越大
from datetime import datetime
today = datetime.now()
days_diff = (today - df['date']).dt.days
time_weight = np.exp(-days_diff / 365) # 一年衰减到0.37
# 组合权重
final_weight = overdue_weight * amount_weight * time_weight
# 归一化到[0.5, 2.0]区间
final_weight = 0.5 + 1.5 * (final_weight - final_weight.min()) / (final_weight.max() - final_weight.min())
return final_weight
3.4 三种策略的对比
说了这么多,咱们用一张表总结一下:
| 策略 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 基于类别比例 | 类别不平衡严重,但样本质量均匀 | 简单、快速、效果好 | 忽略样本内部差异 |
| 基于样本难度 | 存在大量「边界样本」 | 自适应、关注难点 | 计算复杂、可能过拟合 |
| 自定义权重 | 业务规则明确、多维度考量 | 灵活、可解释性强 | 需要业务经验、调参麻烦 |
下面这张图,展示了三种权重策略的核心逻辑:
最后说一句:权重设置没有银弹。我个人的经验是,先试类别比例权重,如果效果不满意,再结合样本难度或自定义权重。别一上来就搞复杂的,简单方法往往最靠谱。