搜广推校招面经九十三

字节懂车帝一面

一、NDCG(Normalized Discounted Cumulative Gain)的计算

NDCG 是信息检索和排序任务中常用的评价指标,用于衡量模型预测的排序质量与真实相关性排序的一致程度。

1.1. DCG@k(Discounted Cumulative Gain)

DCG@k=∑i=1krelilog⁡2(i+1) \text{DCG@k} = \sum_{i=1}^{k} \frac{rel_i}{\log_2(i+1)} DCG@k=i=1klog2(i+1)reli

  • relirel_ireli:第 iii 个位置上的真实相关性分数(如 0, 1, 2,…)
  • iii:排序中的位置索引(从 1 开始)

1.2. IDCG@k(Ideal DCG)

IDCG@k=∑i=1krelisortedlog⁡2(i+1) \text{IDCG@k} = \sum_{i=1}^{k} \frac{rel_i^{\text{sorted}}}{\log_2(i+1)} IDCG@k=i=1klog2(i+1)relisorted

  • 将相关性标签从高到低排序得到理想的排序列表

1.3. NDCG@k(归一化 DCG)

NDCG@k=DCG@kIDCG@k \text{NDCG@k} = \frac{\text{DCG@k}}{\text{IDCG@k}} NDCG@k=IDCG@kDCG@k

1.4. 示例计算

假设有5个项目,真实相关性如下:
真实相关性: [3, 2, 3, 0, 1]
模型预测排序对应的标签值: [0, 3, 2, 3, 1]

步骤 1:计算 DCG@5

DCG@5=0log⁡2(1+1)+3log⁡2(2+1)+2log⁡2(3+1)+3log⁡2(4+1)+1log⁡2(5+1) \text{DCG@5} = \frac{0}{\log_2(1+1)} + \frac{3}{\log_2(2+1)} + \frac{2}{\log_2(3+1)} + \frac{3}{\log_2(4+1)} + \frac{1}{\log_2(5+1)} DCG@5=log2(1+1)0+log2(2+1)3+log2(3+1)2+log2(4+1)3+log2(5+1)1
≈0+31.58496+22+32.32193+12.58496≈4.5722 \approx 0 + \frac{3}{1.58496} + \frac{2}{2} + \frac{3}{2.32193} + \frac{1}{2.58496} \approx 4.5722 0+1.584963+22+2.321933+2.5849614.5722

步骤 2:计算 IDCG@5(真实相关性排序:[3,3,2,1,0])

IDCG@5=3log⁡2(1+1)+3log⁡2(2+1)+2log⁡2(3+1)+1log⁡2(4+1)+0log⁡2(5+1)≈6.3235 \text{IDCG@5} = \frac{3}{\log_2(1+1)} + \frac{3}{\log_2(2+1)} + \frac{2}{\log_2(3+1)} + \frac{1}{\log_2(4+1)} + \frac{0}{\log_2(5+1)} \approx 6.3235 IDCG@5=log2(1+1)3+log2(2+1)3+log2(3+1)2+log2(4+1)1+log2(5+1)06.3235

步骤 3:NDCG@5

NDCG@5=4.57226.3235≈0.723 \text{NDCG@5} = \frac{4.5722}{6.3235} \approx 0.723 NDCG@5=6.32354.57220.723

二、逻辑回归为什么用 BCE 损失?什么是 BCE 损失?

逻辑回归用于二分类任务,模型输出的是一个概率值 y^i=σ(wTxi)\hat{y}_i = \sigma(w^T x_i)y^i=σ(wTxi),表示样本为正类yi=1y_i = 1yi=1的概率。
逻辑回归从概率建模角度出发,假设每个标签服从 伯努利分布
yi∼Bernoulli(y^i) y_i \sim \text{Bernoulli}(\hat{y}_i) yiBernoulli(y^i)

伯努利分布的概率密度函数(PMF)

P(yi∣y^i)=y^iyi⋅(1−y^i)1−yi P(y_i \mid \hat{y}_i) = \hat{y}_i^{y_i} \cdot (1 - \hat{y}_i)^{1 - y_i} P(yiy^i)=y^iyi(1y^i)1yi
这意味着:

  • yi=1y_i = 1yi=1P=y^iP = \hat{y}_iP=y^i
  • yi=0y_i = 0yi=0P=1−y^iP = 1 - \hat{y}_iP=1y^i

2.1. 最大似然估计推导 BCE 损失

  • 我们要学习的是模型参数 θ=(w,b)θ=(w,b)θ=(w,b),目标是最大化所有样本标签出现的联合概率:
  • 用**最大似然估计(MLE)**来估计模型参数 θ=(w,b)\theta = (w, b)θ=(w,b)
  • 对所有样本的联合概率为:
    L(θ)=∏i=1Ny^iyi(1−y^i)1−yi \mathcal{L}(\theta) = \prod_{i=1}^N \hat{y}_i^{y_i} (1 - \hat{y}_i)^{1 - y_i} L(θ)=i=1Ny^iyi(1y^i)1yi
    对数似然函数为:
    log⁡L=∑i=1N[yilog⁡(y^i)+(1−yi)log⁡(1−y^i)] \log \mathcal{L} = \sum_{i=1}^{N} \left[ y_i \log(\hat{y}_i) + (1 - y_i)\log(1 - \hat{y}_i) \right] logL=i=1N[yilog(y^i)+(1yi)log(1y^i)]
    取负号得到损失函数:
    LBCE=−∑i=1N[yilog⁡(y^i)+(1−yi)log⁡(1−y^i)] \mathcal{L}_{\text{BCE}} = -\sum_{i=1}^{N} \left[ y_i \log(\hat{y}_i) + (1 - y_i)\log(1 - \hat{y}_i) \right] LBCE=i=1N[yilog(y^i)+(1yi)log(1y^i)]
    这就是 Binary Cross-Entropy(BCE,二元交叉熵)损失函数

三、为什么二分类问题不用 MAE 损失

在二分类任务中,我们通常使用 交叉熵损失(Cross Entropy Loss) 而不是 MAE(Mean Absolute Error),主要原因如下:

3.1. MAE 不适合概率输出的优化

  • MAE 是对预测值和真实值之间的绝对差值求平均:
    MAE=1N∑i=1N∣yi−y^i∣ MAE = \frac{1}{N} \sum_{i=1}^{N} |y_i - \hat{y}_i| MAE=N1i=1Nyiy^i
  • 对于二分类,真实值 yi∈{0,1}y_i \in \{0, 1\}yi{0,1},预测值 y^i∈[0,1]\hat{y}_i \in [0, 1]y^i[0,1]
  • MAE 对所有误差的梯度是常数(不是关于预测概率的非线性函数),这会导致在梯度下降过程中更新缓慢甚至停滞,不利于模型学习。

3.2. 梯度问题

MAE 的梯度恒定(要么是 1 要么是 -1),不会提供置信度信息:

  • 无论预测是 0.01 还是 0.49,MAE 对标签为 0 的样本的梯度是一样的。这会导致优化过程缺乏方向性,不利于模型快速收敛。

四、分类任务一定没法用MAE loss么?

4.1. ordinal regression 或者 Soft Classification

如果分类任务的label:类别有序、可数值化的分类问题。例如评分,美学等级。
在这种情况下,每个类别有明确的数值含义,用 softmax 后的加权期望值(soft-argmax)来回归一个“期望标签”,再用 MAE/MSE 来优化,是合理的
具体方案:

import torch
import torch.nn.functional as F

# 模拟网络输出 logits
logits = torch.tensor([[2.0, 1.0, 0.5, -1.0]], requires_grad=True)  # shape: [batch, num_classes]
true_label = torch.tensor([0.0])                     # shape: [batch]

# softmax 变成概率
probs = F.softmax(logits, dim=1)                     # shape: [batch, 4]

# soft-argmax 得到数值输出(概率期望)
label_values = torch.tensor([0.0, 1.0, 2.0, 3.0])    # 类别编号
pred_value = torch.sum(probs * label_values, dim=1)  # shape: [batch]

# 计算 MAE loss
loss = F.l1_loss(pred_value, true_label)

五、决策树了解么?介绍一下ID3 与 C4.5

5.1. ID3 算法

ID3(Iterative Dichotomiser 3)是最早的决策树算法之一,由 Quinlan 于1986年提出。
选择 信息增益(Information Gain) 最大的特征来进行划分。

5.1.1. 信息增益计算公式

设数据集为 DDD,特征为 AAA

  • 数据集的熵:
    Ent(D)=−∑k=1Kpklog⁡2pk Ent(D) = - \sum_{k=1}^{K} p_k \log_2 p_k Ent(D)=k=1Kpklog2pk
  • 特征 A 的条件熵:
    Ent(D∣A)=∑v=1V∣Dv∣∣D∣Ent(Dv) Ent(D|A) = \sum_{v=1}^{V} \frac{|D^v|}{|D|} Ent(D^v) Ent(DA)=v=1VDDvEnt(Dv)
  • 信息增益:
    Gain(D,A)=Ent(D)−Ent(D∣A) Gain(D, A) = Ent(D) - Ent(D|A) Gain(D,A)=Ent(D)Ent(DA)
    • pkp_kpk:在数据集 D 中,第 k 类样本所占的比例
    • ∣D∣|D|D:数据集 D 的总样本数量
    • ∣Dv∣|D^v|Dv:数据集特征A为v的样本数量

5.1.2. 特点

  • 偏好选择取值多的特征(容易过拟合)
  • 不支持连续值处理(需离散化)
  • 不支持剪枝(可能导致冗余)

5.2. C4.5 算法

C4.5 是 ID3 的改进版本,同样由 Quinlan 提出。

5.2.1. 核心改进

  • 使用 信息增益率(Gain Ratio) 作为划分标准
  • 支持 连续属性处理
  • 支持 剪枝(如错误率剪枝)
  • 可处理缺失值

5.2.2. 信息增益率计算公式

  • 先计算特征A的信息增益 Gain(D, A)
  • 再计算特征 A 的固有信息(Intrinsic Value):
    IV(A)=−∑v=1V∣Dv∣∣D∣log⁡2(∣Dv∣∣D∣) IV(A) = - \sum_{v=1}^{V} \frac{|D^v|}{|D|} \log_2 \left(\frac{|D^v|}{|D|}\right) IV(A)=v=1VDDvlog2(DDv)
  • 信息增益率:
    GainRatio(D,A)=Gain(D,A)IV(A) GainRatio(D, A) = \frac{Gain(D, A)}{IV(A)} GainRatio(D,A)=IV(A)Gain(D,A)

5.2.3. 特点

  • 弥补了 ID3 偏向取值多特征的缺点
  • 处理能力更强,适用性更广
  • 仍可能有偏向问题(对取值较少但划分效果不佳的特征)

5.2.4. 处理连续特征的方式

基本思路

将连续特征值转化为二元划分:

“特征 A <= t” 和 “特征 A > t”,
其中 ttt 是一个划分阈值。

选取两个相邻样本值之间的中点作为候选划分点(t):
ti=ai+ai+12 t_i = \frac{a_i + a_{i+1}}{2} ti=2ai+ai+1
在这个例子中,候选划分点为 t :

24, 27, 29.5, 32.5, 37.5
对每个划分点 ( t ),将数据划分成两部分:
  • A≤tA \leq tAt
  • A>tA > tA>t
计算每个划分点对应的:
  • 信息增益率(C4.5)
  • 或基尼指数(CART)
选择最优划分点 t∗t^*t,使得划分后:
  • 信息增益率最大(C4.5)
  • 或基尼指数最小(CART)

六、25. K 个一组翻转链表

搜广推校招面经九十三_第1张图片

class Solution:
    def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
        n = 0
        cur = head
        while cur:
            n += 1
            cur = cur.next
        dummy = ListNode()
        dummy.next = head
        p0 = dummy
        while n >= k:
            n -= k
            pre = None
            cur = p0.next
            for _ in range(k):
                nxt = cur.next
                cur.next = pre
                pre = cur
                cur = nxt
            nxt = p0.next
            p0.next.next = cur
            p0.next = pre
            p0 = nxt
        return dummy.next

你可能感兴趣的:(搜广推面经,机器学习,人工智能,python,算法,推荐算法,pytorch,搜索算法)