3. 打分函数类型与应用:线性打分、非线性打分、多维度打分

聊到打分函数,很多新手容易把它想成“一个公式打天下”。但我做了这么多年,发现事情没那么简单。

说白了,你得根据你的业务场景和数据特性,选择合适的打分函数。选错了,模型再复杂也是白搭。

这一节我们就来拆开看看,这三种最主流的打分函数分别长什么样,什么时候用,怎么用。

核心观点:打分函数没有绝对的好坏,只有合不合适。线性打分是基线,非线性打分处理复杂关系,多维度打分解决多目标冲突。

打分函数三大类型:核心逻辑 输入特征向量 线性打分 score = Σ(wᵢ·xᵢ) + b 非线性打分 score = σ( Σ(wᵢ·xᵢ) + b ) 多维度打分 score = Σ(αⱼ · fⱼ(x)) 适用场景: · CTR预估基线模型 · 简单排序、阈值判定 · 特征已做充分交叉 适用场景: · DNN / Tree模型输出层 · 处理特征交叉、高阶关系 · 打分需平滑概率化 适用场景: · 多目标推荐、权衡 · 业务规则复杂组合 · 多模型分数融合 最终预测分数

3.1 线性打分:最朴实,也最稳定

线性打分,就是最基础的加权求和。你想想看,把每个特征乘上权重,再加一个偏置,完事。

score = w1*x1 + w2*x2 + ... + wn*xn + b

这个形式简单吧?但简单不代表没用。我个人习惯,每次做新项目,先拿线性打分跑一遍基线。为什么?

  • 可解释性强:每个特征的权重w就是它的贡献度。你能跟业务方说,“这个特征的权重是0.3,它的影响力占了三成。”
  • 训练稳定:梯度计算简单,几乎没有收敛问题。我在项目中遇到过,用线性模型做CTR预估的初始版本,线上效果稳定,没有震荡。
  • 推理极快:没有激活函数,没有矩阵乘法,就是几个乘法加法,适合高并发场景。

不过它也有天花板。线性打分假设特征之间是独立的,不会自动捕捉交叉。所以当你发现线性模型效果不升反降时,就需要考虑非线性能力了。

我的经验:如果你的数据已经是经过深度特征工程(比如做了很多交叉特征、多项式扩展),线性打分往往就够用了。别急着上复杂模型,它可能只是一个简单的线性问题。

举个例子,一个简单的点击率预估场景。使用Python快速实现:

class LinearScorer:
    def __init__(self, weights, bias=0.0):
        self.weights = weights
        self.bias = bias
    
    def predict(self, features):
        score = self.bias
        for i, w in enumerate(self.weights):
            score += w * features[i]
        return score

# 假设3个特征:用户活跃度、广告质量分、出价
weights = [0.4, 0.35, 0.25]
scorer = LinearScorer(weights, bias=-0.1)
score = scorer.predict([0.8, 0.6, 0.9])
print(f"预估点击率: {score:.4f}")

3.2 非线性打分:更能打,也更危险

当线性打分不够用时,就该上非线性了。说白了,就是在线性求和外面套一个“激活函数”。

score = activation_function( w1*x1 + w2*x2 + ... + wn*xn + b )

常见的激活函数就那么几种:Sigmoid、ReLU、Tanh。每个都有自己的脾气。

激活函数 输出范围 适合场景 注意问题
Sigmoid (0, 1) 概率化、二分类 梯度消失,饱和区
ReLU [0, +∞) 深度网络隐层 神经元死亡 (Dead ReLU)
Tanh (-1, 1) 需负值输出场景 梯度消失依然存在

我记得有一次做推荐系统的重排序,直接用线性打分输出不做限制,结果分数超出预期范围,导致排序逻辑混乱。后来换成了tanh做非线性变换,把分数缩放到(-1,1)之间,才稳定下来。

看个实际代码例子,非线性打分的实现:

import math

def sigmoid(x):
    return 1.0 / (1.0 + math.exp(-x))

def relu(x):
    return max(0.0, x)

class NonLinearScorer:
    def __init__(self, weights, bias, activation='sigmoid'):
        self.weights = weights
        self.bias = bias
        self.activation = activation
    
    def predict(self, features):
        linear_sum = self.bias
        for i, w in enumerate(self.weights):
            linear_sum += w * features[i]
        if self.activation == 'sigmoid':
            return sigmoid(linear_sum)
        elif self.activation == 'relu':
            return relu(linear_sum)
        else:
            return linear_sum

scorer = NonLinearScorer([0.4, 0.35, 0.25], -0.1, 'sigmoid')
score = scorer.predict([0.8, 0.6, 0.9])
print(f"软打分 (概率): {score:.4f}")

避坑指南:我曾经接过一个case,同学用ReLU做打分层的激活,结果所有负样本的分数都变成0。原因很简单:ReLU把所有负输入砍掉了。如果你的训练数据里有大量负样本,建议用sigmoid或leaky ReLU。

3.3 多维度打分:把鸡蛋放在多个篮子里

很多真实场景,你关心的不只是“这个点击率高不高”,你还关心“用户是否愿意付费”、“会不会投诉”、“是不是新用户”。你想想看,一个维度打分解决不了这种多目标权衡。

多维度打分,就是把复杂目标拆成几个独立的维度,每个维度有自己的打分函数(可以是线性也可以是非线性),最后加权融合。

final_score = α₁ * score₁ + α₂ * score₂ + ... + αₘ * scoreₘ

我在项目中遇到过电商推荐场景,需要同时考虑:点击概率、购买概率、好评概率、发货时效。每个维度各自用一个模型打分,最后人工定权重组合。

看一个典型的多维度打分结构:

class MultiDimScorer:
    """
    多维度打分器,每个维度有独立的打分函数
    """
    def __init__(self, dim_weights, scorers):
        self.dim_weights = dim_weights  # 维度权重列表
        self.scorers = scorers          # 维度打分器列表
    
    def predict(self, features_dict):
        """
        features_dict: 每个维度对应的特征字典
        """
        final_score = 0.0
        for i, (weight, scorer) in enumerate(zip(self.dim_weights, self.scorers)):
            features = features_dict.get(f'dim_{i}', [])
            dim_score = scorer.predict(features)
            final_score += weight * dim_score
            print(f"维度{i}: 权重={weight:.2f}, 分数={dim_score:.4f}")
        return final_score

# 实例化:点击率、购买率、满意度三个维度
scorers = [
    NonLinearScorer([0.5, 0.3], 0.0, 'sigmoid'),   # 点击率
    NonLinearScorer([0.7, 0.2], -0.1, 'sigmoid'),  # 购买率
    LinearScorer([0.4, 0.4, 0.2], 0.3)              # 满意度
]
multi_scorer = MultiDimScorer(
    dim_weights=[0.4, 0.4, 0.2],
    scorers=scorers
)
# 模拟特征输入
features = {
    'dim_0': [0.9, 0.7],
    'dim_1': [0.6, 0.8],
    'dim_2': [0.7, 0.5, 0.6]
}
final = multi_scorer.predict(features)
print(f"最终多维度分数: {final:.4f}")

关键点:多维度打分的权重αⱼ不是学出来的,是业务定的。你在上线前,一定要和产品、运营对齐权重。我就经历过,明明技术指标最优,但业务方觉得不对,反复调了几轮权重才稳住。

3.4 三种打分的对比总结

一下子讲了三种,我们用一个表格收一下。这样你以后看项目代码,一眼就知道该用哪个。

对比维度 线性打分 非线性打分 多维度打分
数学复杂度 低 中 高(组合)
可解释性 高 中(注意激活函数) 中(维度可解释)
处理特征交叉 弱(需人工交叉) 强(自动捕捉) 中(依赖维度拆分)
训练稳定性 极高 中(受激活影响) 中(各维度独立训练)
典型应用 LR、FM模型输出 DNN、GBDT输出 多目标重排序

说到这,你是不是觉得豁然开朗了?其实挑选打分函数,核心就是看你的目标和数据。没有银弹,但你知道了这些差异,至少知道第一步该往哪个方向走。