特征构造:组合特征、交叉特征、多项式特征

一句话概述

特征构造(Feature Construction)是特征工程中最具创造力的环节——它通过数学变换、组合和交互,从原始特征中生成新的、更有信息量的特征。组合特征(如两个数值特征的比值)、交叉特征(如类别特征之间的笛卡尔积)、多项式特征(如 x²、x₁x₂)可以显著提升模型对非线性关系的捕捉能力。在深度学习中,特征构造被神经网络自动完成;在传统 ML 中,特征构造往往是「从 80 分到 95 分」的关键。

💡 核心要点:① 特征构造通过发掘特征间的交互效应来弥补线性模型的非线性表达能力不足 ② 交叉特征是推荐系统和 CTR 预估中最重要的特征工程技巧 ③ 多项式特征让线性模型能够拟合非线性边界 ④ 特征构造需要领域知识,但也应警惕特征爆炸

教学与演示

一、多项式特征

是什么:多项式特征(Polynomial Features)是将原始特征的幂次和交互项作为新特征加入模型。例如,对于两个特征 x₁ 和 x₂,二阶多项式特征包括:x₁、x₂、x₁²、x₂²、x₁x₂。多项式特征使得线性模型能够在扩增后的特征空间中拟合非线性决策边界。

大白话 线性模型只能画直线,但加上多项式特征后,它就能画曲线了。比如加上 x² 就能拟合抛物线,加上 x₁x₂ 就能拟合倾斜的椭圆。这就像给一个只会走直线的人配了一副「曲面镜」——透过镜子看,弯的也变成直的了。

为什么:线性模型的核心假设是目标变量与特征之间存在线性关系。但在现实中,很多关系是非线性的(如「年龄对收入的影响呈倒 U 形」、「房价与面积的平方成正比」)。多项式特征通过特征空间的非线性变换,将非线性问题转化为线性问题——在扩增后的特征空间中,原问题的非线性决策边界变成了超平面。这等价于使用多项式核函数。

怎么做

import numpy as np

# ========== 多项式特征生成演示 ==========
np.random.seed(42)

def polynomial_features_demo():
    """演示多项式特征的生成和作用"""
    # 原始特征:x1, x2(2个特征)
    X = np.array([
        [1, 2],
        [3, 4],
        [5, 6],
    ])
    print("=== 多项式特征生成 ===")
    print(f"原始特征 (3个样本, 2个特征):")
    print(X)
    print()
    
    # 生成二阶多项式特征(包括交互项)
    # 包括: 1, x1, x2, x1², x1·x2, x2²
    def generate_poly_features(X, degree=2, include_bias=True):
        """
        生成多项式特征
        X: (n_samples, n_features) 原始特征矩阵
        degree: 多项式的阶数
        include_bias: 是否包含常数项(全1列)
        """
        n_samples, n_features = X.shape
        features = []
        feature_names = []
        
        if include_bias:
            features.append(np.ones(n_samples))  # 常数项(偏置)
            feature_names.append("1")
        
        for d in range(1, degree + 1):
            # 生成所有 d 次幂的组合
            # 使用组合索引生成所有阶数组合
            for combo in np.ndindex(*([n_features] * d)):
                # 检查组合是否满足总次数 = d
                if sum(combo) != d:
                    continue
                new_feat = np.ones(n_samples)
                name_parts = []
                for feat_idx, power in enumerate(combo):
                    if power > 0:
                        new_feat *= X[:, feat_idx] ** power
                        if power == 1:
                            name_parts.append(f"x{feat_idx+1}")
                        else:
                            name_parts.append(f"x{feat_idx+1}^{power}")
                if name_parts:  # 不是零次幂
                    features.append(new_feat)
                    feature_names.append("·".join(name_parts))
        
        return np.column_stack(features), feature_names
    
    X_poly, names = generate_poly_features(X, degree=2)
    print(f"二阶多项式特征 (3个样本, {len(names)}个特征):")
    print("特征名:", names)
    print(X_poly)
    print()
    
    print("特征数量增长: 2 →", len(names))
    print("公式: p 个特征, d 次多项式 → C(p+d, d) = (p+d)!/(p!·d!) 个特征")
    
    # ===== 演示多项式特征如何让线性模型拟合非线性 =====
    print("\n=== 多项式特征的实际效果 ===")
    # 生成非线性数据:y = 2x² - 3x + 1 + noise
    n_samples = 100
    x = np.linspace(-3, 3, n_samples).reshape(-1, 1)
    y = 2 * x.ravel()**2 - 3 * x.ravel() + 1 + np.random.randn(n_samples) * 2
    
    # 方法1:只用原始特征 x(线性回归,欠拟合)
    X_simple = np.column_stack([np.ones(n_samples), x.ravel()])  # [1, x]
    w_simple = np.linalg.lstsq(X_simple, y, rcond=None)[0]
    y_pred_simple = X_simple @ w_simple
    mse_simple = np.mean((y - y_pred_simple) ** 2)
    
    # 方法2:使用多项式特征 [1, x, x²](完美拟合)
    X_poly_full = np.column_stack([np.ones(n_samples), x.ravel(), x.ravel()**2])
    w_poly = np.linalg.lstsq(X_poly_full, y, rcond=None)[0]
    y_pred_poly = X_poly_full @ w_poly
    mse_poly = np.mean((y - y_pred_poly) ** 2)
    
    print(f"只用 x(线性回归): MSE = {mse_simple:.2f}")
    print(f"用 x 和 x²(多项式回归): MSE = {mse_poly:.2f}")
    print(f"提升: {(mse_simple - mse_poly) / mse_simple * 100:.1f}%")
    print(f"拟合的公式: y = {w_poly[2]:.2f}x² + {w_poly[1]:.2f}x + {w_poly[0]:.2f}")
    print(f"真实公式: y = 2x² - 3x + 1")

polynomial_features_demo()
\text{多项式特征的数量}\(\text{features} = \binom{p + d}{d} = \frac{(p+d)!}{p! \cdot d!}\)
大白话 多项式特征就像给模型配了一副「度数可调」的眼镜——x² 让它能看到抛物线,x₁x₂ 让它能看到两个特征之间的「化学反应」。但度数太高(阶数太大)会导致特征爆炸,模型反而看不清了。

什么用:多项式特征在物理建模(如运动轨迹拟合)、经济学(如科布-道格拉斯生产函数)、工业过程控制(如响应面分析)等领域有广泛应用。在 AI 中,多项式特征是线性模型突破线性限制的最简单方法——在推荐系统中,用户年龄的平方项可以捕捉「年龄对购买力的倒 U 形影响」。

哪些坑:特征数量随阶数和原始特征数呈组合爆炸增长——p=100 个特征,d=2 时就产生约 5000 个特征。这会导致过拟合和计算困难。解决方案:① 只对重要的特征做多项式变换,② 使用 L1 正则化(Lasso)自动筛选,③ 使用核方法(隐式多项式变换,无需显式生成特征)。

二、交叉特征

是什么:交叉特征(Cross Features)是指将两个或多个类别特征(或离散化后的数值特征)做笛卡尔积,生成新的组合特征。例如,将「性别」(男/女)和「年龄段」(青年/中年/老年)交叉,得到 6 个组合特征(男-青年、男-中年、...)。交叉特征捕捉了特征之间的交互效应——「男性中年」可能有与「男」或「中年」单独不同的行为模式。

大白话 交叉特征就像在问「同时满足条件 A 和条件 B 会怎样」。比如「北京」和「冬天」单独看没什么特别,但「北京+冬天」组合在一起,可能意味着「雾霾严重」,这对预测口罩销量非常关键。

为什么:在推荐系统和 CTR 预估中,单一特征的信息量有限,但特征组合往往蕴含着强烈的信号。例如:「用户 ID=123」和「商品类别=电子产品」单独看都很弱,但「用户 123 + 电子产品」这个组合可能意味着该用户经常购买电子产品,这对推荐非常有价值。交叉特征是 FM(因子分解机)和 DeepFM 等模型的核心思想来源。

怎么做

import numpy as np

# ========== 交叉特征生成演示 ==========
def cross_features_demo():
    """演示交叉特征的生成和编码"""
    print("=== 交叉特征(Cross Features)===")
    
    # 模拟用户行为数据
    n_samples = 1000
    np.random.seed(42)
    
    # 类别特征
    gender = np.random.choice(['男', '女'], n_samples)  # 性别(2类)
    city = np.random.choice(['北京', '上海', '广州', '深圳'], n_samples)  # 城市(4类)
    age_group = np.random.choice(['青年', '中年', '老年'], n_samples)  # 年龄段(3类)
    
    # 模拟 CTR(点击率)——不同交叉组合有不同的点击率
    def get_ctr(g, c, a):
        """模拟真实的 CTR:不同组合有不同的点击率"""
        base = 0.05
        # 交叉效应
        if g == '男' and c == '北京' and a == '青年':
            return 0.15  # 北京男青年 CTR 高(可能是程序员)
        elif g == '女' and c == '上海' and a == '中年':
            return 0.12  # 上海中年女性 CTR 高
        elif g == '男' and c == '深圳' and a == '青年':
            return 0.13
        else:
            return base + np.random.uniform(-0.02, 0.02)
    
    ctr = np.array([get_ctr(g, c, a) for g, c, a in zip(gender, city, age_group)])
    clicks = np.random.binomial(1, ctr)  # 模拟点击事件
    
    # 分析交叉特征的效应
    print("各特征单独的平均 CTR:")
    print(f"  性别: 男={clicks[gender=='男'].mean():.3f}, 女={clicks[gender=='女'].mean():.3f}")
    print(f"  城市: 北京={clicks[city=='北京'].mean():.3f}, 上海={clicks[city=='上海'].mean():.3f}")
    
    print("\n交叉特征「性别 × 城市 × 年龄段」的 CTR:")
    from collections import defaultdict
    cross_ctr = defaultdict(list)
    for g, c, a, clk in zip(gender, city, age_group, clicks):
        cross_ctr[(g, c, a)].append(clk)
    
    # 显示 CTR 最高和最低的交叉组合
    avg_ctr = {k: np.mean(v) for k, v in cross_ctr.items()}
    sorted_ctr = sorted(avg_ctr.items(), key=lambda x: x[1], reverse=True)
    
    print("  CTR 最高的 3 个组合:")
    for (g, c, a), ctr_val in sorted_ctr[:3]:
        print(f"    {g}-{c}-{a}: CTR={ctr_val:.3f}")
    print("  CTR 最低的 3 个组合:")
    for (g, c, a), ctr_val in sorted_ctr[-3:]:
        print(f"    {g}-{c}-{a}: CTR={ctr_val:.3f}")
    
    print(f"\n交叉特征总数: {2 * 4 * 3} = 24 个组合")
    print("如果对24个组合做 One-Hot 编码,会生成 24 个特征")
    print("这在工业级数据中会爆炸(如100万用户 × 10万商品 = 100亿组合)")
    print("解决方案:使用 FM 或 Embedding 来隐式学习交叉效应")

cross_features_demo()
\text{FM 中的交叉特征表示}\(\hat{y} = w_0 + \sum_{i=1}^{n} w_i x_i + \sum_{i=1}^{n} \sum_{j=i+1}^{n} \langle v_i, v_j \rangle x_i x_j\)
大白话 交叉特征就像「拼图」——单独看每个拼图块(特征)没什么信息,但把两块拼在一起(交叉),可能就看到了图案的关键部分。但如果你有 100 万用户和 10 万商品,不可能显式生成 100 亿个交叉特征——这时候 FM 的嵌入向量技巧就派上用场了。

什么用:交叉特征是推荐系统和 CTR 预估中最核心的特征工程技巧。Google 的 Wide & Deep 模型中,「Wide」部分就是显式的交叉特征;DeepFM 和 xDeepFM 等模型进一步用神经网络隐式学习高阶交叉特征。在金融风控中,交叉特征(如「交易金额 × 交易时间」)能捕捉到异常模式。

哪些坑:类别特征交叉会导致特征爆炸——p 个特征,每个有 k 个类别,理论上有 O(k^p) 种组合。解决方案:① 只交叉重要的特征,② 使用特征哈希(Feature Hashing),③ 使用 FM/FFM 等嵌入方法隐式学习交叉效应,④ 降维(如 PCA)后再交叉。

三、组合特征与数学变换

是什么:组合特征(Combined Features)是数值特征之间的数学运算结果,包括:比值(如「身高/体重」= BMI)、差值(如「离店时间 − 到店时间」= 停留时长)、乘积(如「价格 × 购买量」= 总消费额)、对数变换(log(x+1))、分箱(将连续值离散化为区间)等。这些变换常常能揭示原始特征无法直接表达的规律。

大白话 原始特征就像「食材」,组合特征就是「烹饪」——把几种食材搭配在一起,味道可能完全不同。比如「身高」和「体重」单独看都不如「BMI=体重/身高²」有信息量。好的特征构造能「点石成金」。

为什么:组合特征的价值在于利用领域知识或数学直觉,将原始特征转化为更「线性」的形式。例如,房价预测中「面积」和「房间数」的比值(每间房的平均面积)比单独两个特征更有预测力。对数变换可以将乘法关系转化为加法关系(log(ab) = log(a) + log(b)),使线性模型能捕捉乘法效应。

怎么做

import numpy as np

# ========== 组合特征生成演示 ==========
np.random.seed(42)

def combined_features_demo():
    """演示各种组合特征的生成方法"""
    n_samples = 200
    
    # 模拟用户购物数据
    price = np.random.uniform(10, 500, n_samples)  # 商品价格
    quantity = np.random.randint(1, 10, n_samples)  # 购买数量
    user_age = np.random.randint(18, 70, n_samples)  # 用户年龄
    session_duration = np.random.uniform(1, 60, n_samples)  # 浏览时长(分钟)
    pages_viewed = np.random.randint(1, 20, n_samples)  # 浏览页面数
    
    # 真实购买金额(标签):由组合特征决定
    total_amount = price * quantity  # 总消费 = 价格 × 数量
    # 添加年龄效应:中年用户消费更多(倒U形)
    age_effect = -0.5 * (user_age - 40)**2 / 100 + 10
    # 浏览深度效应:浏览越多,购买意愿越强
    browse_effect = 2 * np.sqrt(pages_viewed)
    # 最终标签(加噪声)
    y = total_amount * 0.01 + age_effect + browse_effect + np.random.randn(n_samples) * 5
    
    print("=== 组合特征构造 ===")
    print("原始特征: price, quantity, user_age, session_duration, pages_viewed (5个)")
    print()
    
    # 构造新特征
    features = {}
    
    # 1. 乘法组合:总消费金额
    features['total_spend'] = price * quantity
    print("1. 乘法组合: total_spend = price × quantity")
    print(f"   相关系数 with y: {np.corrcoef(features['total_spend'], y)[0,1]:.3f}")
    
    # 2. 比值组合:平均每页浏览时长
    features['time_per_page'] = session_duration / pages_viewed
    print("2. 比值组合: time_per_page = session_duration / pages_viewed")
    print(f"   相关系数 with y: {np.corrcoef(features['time_per_page'], y)[0,1]:.3f}")
    
    # 3. 多项式:年龄的平方项(捕捉倒U形效应)
    features['age_squared'] = user_age ** 2
    print("3. 多项式: age_squared = user_age²")
    print(f"   相关系数 with y: {np.corrcoef(features['age_squared'], y)[0,1]:.3f}")
    
    # 4. 对数变换:压缩长尾分布
    features['log_price'] = np.log(price + 1)
    print("4. 对数变换: log_price = log(price + 1)")
    print(f"   相关系数 with y: {np.corrcoef(features['log_price'], y)[0,1]:.3f}")
    
    # 5. 分箱(离散化):将年龄分为3个年龄段
    age_bins = [0, 30, 50, 100]
    features['age_bucket'] = np.digitize(user_age, age_bins) - 1
    print("5. 分箱: age_bucket = [青年(0-30), 中年(30-50), 老年(50+)]")
    print(f"   各年龄段平均 y: 青年={y[features['age_bucket']==0].mean():.1f}, "
          f"中年={y[features['age_bucket']==1].mean():.1f}, "
          f"老年={y[features['age_bucket']==2].mean():.1f}")
    
    # 6. 差值组合
    features['browse_efficiency'] = pages_viewed / session_duration
    print("6. 效率指标: browse_efficiency = pages_viewed / session_duration")
    print(f"   相关系数 with y: {np.corrcoef(features['browse_efficiency'], y)[0,1]:.3f}")
    
    print(f"\n总结: 从 5 个原始特征构造了 6 个新特征,大幅丰富了特征空间")
    print("其中 total_spend 和 age_squared 对标签的预测力最强")

combined_features_demo()
\text{对数变换线性化}\(y = a \cdot x_1^{b_1} \cdot x_2^{b_2} \quad \Rightarrow \quad \log y = \log a + b_1 \log x_1 + b_2 \log x_2\)
大白话 特征构造的「黄金法则」是:如果你能直觉地感受到两个特征之间有「化学反应」,那就试着创造出它们的组合。比如「价格」和「数量」乘起来就是「总消费」——这个新特征比两个原始特征加起来更有预测力。

什么用:在 AI 工业中,特征构造是数据科学家最有价值的工作之一。在信用评分中,「收入/负债比」比「收入」和「负债」单独更有预测力;在用户行为分析中,「页面停留时间/浏览页面数」反映了浏览深度;在物联网中,「振动频率 × 振幅」可能比单一传感器读数更能预示设备故障。

哪些坑:特征构造容易过度——构造太多的特征会导致特征空间爆炸、模型过拟合。应该有选择性地构造——基于领域知识、EDA 发现或模型反馈来构造最有价值的特征。构造的特征应该具有可解释性,避免「黑箱」特征。

四、特征构造的实战策略

是什么:实战中的特征构造遵循「探索-构造-验证」循环:① 探索性数据分析(EDA)发现特征之间的关系和潜在模式,② 基于发现构造新特征,③ 用模型验证新特征是否有效(如特征重要性、交叉验证得分提升)。循环往复,直到边际收益递减到可接受的阈值。

大白话 特征构造没有「标准答案」——它更像是一门手艺。高手和新手的区别在于:高手能从数据中「嗅」出哪些特征组合可能有用,然后用实验验证。这需要大量的实践和领域知识积累。

为什么:特征构造的有效性取决于数据的内在结构。有些数据集特征构造能带来巨大提升(如存在强非线性关系时),有些数据集则收效甚微(如特征已经是线性关系时)。了解数据、了解业务,是做好特征构造的前提。

怎么做

import numpy as np

# ========== 特征构造实战策略总结 ==========
def feature_construction_strategy():
    """总结特征构造的实战策略"""
    print("=== 特征构造实战策略 ===")
    print()
    
    strategies = [
        ("数值特征变换", [
            "对数变换: log(x+1) — 处理长尾分布",
            "平方根变换: sqrt(x) — 处理右偏分布",
            "Box-Cox 变换 — 使数据接近正态分布",
            "分箱: 将连续值离散化为区间",
            "标准化: (x-μ)/σ — 使不同特征可比",
        ]),
        ("数值特征组合", [
            "加减: x1+x2, x1-x2 — 总和与差值",
            "乘除: x1*x2, x1/x2 — 比值与交互",
            "多项式: x², x³, x1*x2 — 捕捉非线性",
            "统计量: 按组聚合的 mean/max/min/std",
            "时间差: 日期2 - 日期1 = 时间间隔",
        ]),
        ("类别特征处理", [
            "One-Hot 编码 — 无序类别特征",
            "Label Encoding — 有序类别特征",
            "Target Encoding — 用目标变量均值编码",
            "Count Encoding — 用出现频率编码",
            "交叉特征: cat1 × cat2 — 捕捉交互效应",
        ]),
        ("业务特征构造", [
            "基于业务公式: 如 BMI = 体重/身高²",
            "基于时间窗口: 如「过去7天购买次数」",
            "基于统计: 如「用户购买金额的Z-score」",
            "基于比率: 如「已用额度/总额度」",
            "基于趋势: 如「最近3个月的增长率」",
        ]),
    ]
    
    for category, examples in strategies:
        print(f"【{category}】")
        for ex in examples:
            print(f"  • {ex}")
        print()
    
    print("特征构造的黄金法则:")
    print("  1. 先理解业务,再构造特征")
    print("  2. 每个新特征都应该有直觉或理论的支撑")
    print("  3. 构造后立即验证(特征重要性、交叉验证)")
    print("  4. 警惕特征泄漏(不能使用未来信息)")
    print("  5. 在训练集上计算统计量,在测试集上应用")

feature_construction_strategy()
大白话 特征构造的本质是「帮模型做它做不到的事」。线性模型不会算平方,你就帮它算好 x² 放进去;树模型不会做除法,你就帮它算好 BMI 放进去。好的特征构造,就是把你的领域知识「翻译」成模型能理解的语言。

概念关系图谱

特征类型生成方式适用模型主要作用风险
多项式特征幂次+交互线性模型拟合非线性特征爆炸
交叉特征类别笛卡尔积FM/DeepFM捕捉交互稀疏性
组合特征数学运算所有模型发掘新信息过度构造
分箱特征连续值离散化线性模型非线性信息损失
对数变换函数变换线性模型正态化丢失零值

重点答疑

Q1: 特征构造和特征选择有什么区别?

特征构造是「创造」新特征(如 x²、x₁+x₂),特征选择是「筛选」已有特征(挑出最重要的子集)。两者的顺序通常是:先构造新特征(扩充特征空间),再选择特征(精简特征空间)。特征构造依赖领域知识和创造力,特征选择更依赖统计和算法。

Q2: 多项式特征的阶数(degree)应该选多大?

通常 degree=2 就足够大多数场景。degree=3 时特征数量会显著增加(p 个特征时约为 O(p³)),且容易过拟合。选择原则:① 如果数据有明显的非线性关系(如散点图呈 U 形),用 degree=2;② 配合 L1 正则化(Lasso),让模型自动选择重要的多项式项;③ 在验证集上比较不同 degree 的性能,选择最佳的。

Q3: 交叉特征(Cross Features)与特征交互(Feature Interaction)是一回事吗?

不完全相同。交叉特征是显式地生成新的组合特征(如「男-北京」),特征交互是模型自动学习特征之间的交互效应(如 FM 的嵌入向量内积、GBDT 的树结构自然捕捉交互)。交叉特征是特征工程层面的概念,特征交互是模型层面的概念。深度学习和树模型能自动学习特征交互,线性模型则需要显式的交叉特征。

章节单词汇总

英文音标术语/释义
Polynomial Features/ˌpɑːliˈnoʊmiəl ˈfiːtʃərz/多项式特征;原始特征的幂次和交互
Cross Features/krɔːs ˈfiːtʃərz/交叉特征;类别特征的笛卡尔积
Feature Interaction/ˈfiːtʃər ˌɪntərˈækʃən/特征交互;特征之间的联合效应
Discretization/dɪsˌkriːtɪˈzeɪʃən/离散化;将连续值划分为区间
Log Transform/lɔːɡ trænsˈfɔːrm/对数变换;压缩长尾分布
Target Encoding/ˈtɑːrɡɪt ɪnˈkoʊdɪŋ/目标编码;用目标变量均值编码类别
Feature Hashing/ˈfiːtʃər ˈhæʃɪŋ/特征哈希;用哈希函数压缩特征空间
FM/ˌefˈem/Factorization Machine;因子分解机

面试练习

Q1 [单选] 多项式特征的主要作用是什么?

  • A. 减少特征数量
  • B. 让线性模型能够拟合非线性关系
  • C. 提高模型训练速度
  • D. 减少过拟合风险
解答:多项式特征(如 x²、x₁x₂)通过特征空间的非线性变换,使线性模型在扩增后的特征空间中能够拟合非线性决策边界。这是线性模型突破线性限制的最简单方法。

Q2 [单选] 对于 p=10 个特征,生成二阶多项式特征(包括交互项),会生成多少个特征?

  • A. 20 个
  • B. 55 个
  • C. 66 个(包括常数项)
  • D. 100 个
解答:C(10+2, 2) = C(12, 2) = 66。包括:1 个常数项 + 10 个一次项 + 10 个平方项 + 45 个交叉项 = 66 个。如果不包括常数项则是 65 个。

Q3 [多选] 以下哪些是特征构造的常用方法?

  • A. 对数变换(log transform)
  • B. 分箱(Binning/Discretization)
  • C. 特征交叉(如性别 × 城市)
  • D. PCA 降维
解答:对数变换、分箱和特征交叉都是特征构造方法。PCA 是特征提取(降维),不是特征构造——它生成的是主成分,而非基于原始特征的组合。

Q4 [单选] 交叉特征(Cross Features)在推荐系统中最主要的作用是什么?

  • A. 减少特征数量
  • B. 捕捉特征之间的交互效应(如用户 × 商品的匹配度)
  • C. 提高模型训练速度
  • D. 使特征变得更稀疏
解答:交叉特征的主要价值是捕捉特征之间的交互效应。在推荐系统中,「用户 × 商品」的交叉特征能捕捉到特定用户对特定商品的偏好模式,这是单一特征无法表达的。

Q5 [单选] 特征构造中,对数变换(log(x+1))主要用于什么场景?

  • A. 增加特征数量
  • B. 处理长尾分布(右偏数据),使数据更接近正态分布
  • C. 将分类特征转为数值
  • D. 增加数据的方差
解答:对数变换是处理长尾分布(如收入、点击量等具有极端值的特征)的常用方法,通过压缩大值、拉伸小值使数据更接近正态分布,便于线性模型学习。

Q6 [多选] 以下哪些是特征构造中需要注意的问题?

  • A. 特征爆炸(太多特征导致过拟合)
  • B. 特征泄漏(使用了未来信息)
  • C. 构造的特征可能高度相关(多重共线性)
  • D. 特征构造总是能提高模型性能
解答:特征构造的主要风险是特征爆炸、特征泄漏和多重共线性。特征构造不一定提高性能——如果构造的特征没有信息量,反而可能引入噪声。

Q7 [单选] FM(因子分解机)如何解决交叉特征的特征爆炸问题?

  • A. 只保留最重要的交叉特征
  • B. 通过低维嵌入向量的内积来隐式表示交叉特征,避免显式生成
  • C. 使用 PCA 降维
  • D. 随机删除一部分交叉特征
解答:FM 为每个特征学习一个低维嵌入向量 v_i,用 v_i·v_j 的内积来表示特征 i 和 j 的交互权重。这样就将 O(p²) 的显式交叉特征参数降为 O(p·k),其中 k 是嵌入维度(通常 k << p)。

Q8 [单选] 在特征构造中,「分箱」(Binning)的主要目的是什么?

  • A. 增加特征数量
  • B. 将连续特征转化为类别特征,捕捉非线性效应
  • C. 减少数据噪声
  • D. 使数据标准化
解答:分箱将连续值离散化到固定区间,使线性模型能够捕捉阶梯式的非线性效应。分箱后的特征通常需要 One-Hot 编码才能被模型使用。

Q9 [多选] 以下哪些是特征构造的「业务特征」例子?

  • A. BMI = 体重 / 身高²
  • B. 负债率 = 总负债 / 总资产
  • C. 过去7天购买次数(时间窗口聚合)
  • D. 对数变换 log(x+1)
解答:BMI、负债率、时间窗口聚合都是基于业务知识构造的特征。对数变换是数学变换,不属于业务特征。

Q10 [单选] 特征构造中「特征泄漏」是指什么?

  • A. 特征值中有缺失值
  • B. 在构造特征时使用了测试集或未来信息
  • C. 特征值超出了正常范围
  • D. 特征之间有相关性
解答:特征泄漏是指在构造特征时,不小心使用了测试集的信息或未来信息。例如,用全量数据的均值来做目标编码(而非只用训练集),或者用「购买后」的行为来预测「购买前」的事件。特征泄漏会导致模型在测试集上表现异常好,但上线后效果差。