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的核心思想:让模型更关注那些被分错的样本

具体做法是这样的:

  1. 先训练一个弱分类器(比如决策树桩)
  2. 找出被分错的样本
  3. 提高这些样本的权重
  4. 再训练下一个分类器
  5. 重复,直到达到指定轮数

嗯,这里要注意:AdaBoost本身是一个集成方法,但我们可以借用它的「难度权重」思想,用在单一模型上。

我曾经在一个信贷评分卡项目里试过这个思路。当时有个客户群体,收入低但还款意愿强,模型老是误杀。我手动给这批样本加了1.5倍的权重,模型就学乖了。

小技巧: 如果你不想用AdaBoost那么复杂的流程,可以先用一个简单模型(比如逻辑回归)跑一遍,把预测概率和真实标签对比,找出那些「预测概率接近0.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天的客户,虽然都是「坏客户」,但严重程度完全不同。你总不能给他们一样的权重吧?

自定义权重函数,就是让你根据样本的业务属性来分配权重。比如:

  • 逾期天数越长,权重越大
  • 金额越大的样本,权重越大
  • 近期样本比历史样本权重更大

我建议你这样做:

  1. 先确定业务上哪些维度重要(逾期天数、金额、时间等)
  2. 给每个维度设计一个权重函数(线性、指数、分段等)
  3. 把这些权重组合起来,得到最终权重
避坑指南: 我曾经在自定义权重时,把逾期天数的权重设成了指数函数。结果逾期90天的样本权重是逾期30天的1000倍,模型直接崩了。后来改成对数函数,才稳定下来。记住:权重差异不要太大,否则模型训练会不稳定。

代码示例:

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 三种策略的对比

说了这么多,咱们用一张表总结一下:

策略 适用场景 优点 缺点
基于类别比例 类别不平衡严重,但样本质量均匀 简单、快速、效果好 忽略样本内部差异
基于样本难度 存在大量「边界样本」 自适应、关注难点 计算复杂、可能过拟合
自定义权重 业务规则明确、多维度考量 灵活、可解释性强 需要业务经验、调参麻烦

下面这张图,展示了三种权重策略的核心逻辑:

权重设置策略核心逻辑 样本权重设置 基于类别比例 少数类权重 = 多数类/少数类 所有同类样本权重相同 适用:类别不平衡,样本均匀 基于样本难度 分错样本权重增大 AdaBoost思想迭代更新 适用:边界样本多 自定义权重函数 逾期天数、金额、时间 业务规则驱动 适用:多维度业务场景 核心原则:权重差异不宜过大,避免模型过拟合 建议:先试类别比例权重,再根据业务调整

最后说一句:权重设置没有银弹。我个人的经验是,先试类别比例权重,如果效果不满意,再结合样本难度或自定义权重。别一上来就搞复杂的,简单方法往往最靠谱。

一句话总结: 权重是给模型「划重点」的工具。划得好,模型学得快;划得不好,模型学歪了。多试、多调、多验证。

专注资料整理