搜广推校招面经一百零四

虾皮推荐算法一面

一、看你简历中写到了DeepFM,讲一下FM怎么实现的

FM 的核心思想就是用向量内积来学习特征之间的 二阶交互(如性别 × 购买记录)

FM通过以下公式优化交叉项的计算:
∑ i = 1 n ∑ j = i + 1 n ⟨ v i , v j ⟩ x i x j = 1 2 ( ( ∑ i = 1 n v i x i ) 2 − ∑ i = 1 n ( v i x i ) 2 ) \sum_{i=1}^{n} \sum_{j=i+1}^{n} \langle v_i, v_j \rangle x_i x_j = \frac{1}{2} \left( \left( \sum_{i=1}^{n} v_i x_i \right)^2 - \sum_{i=1}^{n} (v_i x_i)^2 \right) i=1nj=i+1nvi,vjxixj=21 (i=1nvixi)2i=1n(vixi)2
这种优化将计算复杂度从 O ( n 2 ) O(n^2) O(n2) 降低到 O ( n ) O(n) O(n),极大地提高了计算效率。

import torch
import torch.nn as nn
class FM(nn.Module):
    def __init__(self, field_dims, embed_dim=8):
        """
        参数:
        - field_dims: List[int],每个类别特征的取值范围大小。
        - embed_dim: int,嵌入向量维度。
        """
        super().__init__()
        self.num_fields = len(field_dims)
        self.embed_dim = embed_dim
        self.offsets = nn.Parameter(
            torch.tensor([0] + field_dims[:-1]).cumsum(dim=0), requires_grad=False
        )
        total_dim = int(sum(field_dims))
        
        # 一阶权重
        self.linear_weight = nn.Embedding(total_dim, 1)
        self.bias = nn.Parameter(torch.zeros(1))
        
        # 嵌入向量
        self.embedding = nn.Embedding(total_dim, embed_dim)

    def forward(self, x_cat):
        # x_cat: [B, F]
        x = x_cat + self.offsets  # 对齐到大 embedding 空间
        # 一阶项
        linear_part = self.linear_weight(x).sum(dim=1) + self.bias  # [B,1]
        # 二阶项
        v = self.embedding(x)  # [B,F,E]
        sum_of_emb = v.sum(dim=1)  # [B,E]
        square_of_sum = sum_of_emb * sum_of_emb
        sum_of_square = (v * v).sum(dim=1)
        data = v * v
        second_order = 0.5 * (square_of_sum - sum_of_square).sum(dim=1, keepdim=True)
        return linear_part + second_order  # [B,1]


if __name__ == "__main__":
    field_dims = [1000, 500, 100]
    model = FM(field_dims, embed_dim=8)
    # batch 输入
    x_cat = torch.tensor([[1, 2, 3], [4, 5, 6]], dtype=torch.long)
    logit = model(x_cat)
    prob = torch.sigmoid(logit)
    print("Logits:", logit)
    print("Pred prob:", prob)

二、介绍一下常用的优化器。

神经网络的优化器(Optimizer)是用于更新模型参数(如权重和偏差)的算法,核心目标是最小化损失函数:

2.1 基础的优化器

  • 随机梯度下降(SGD, Stochastic Gradient Descent)
    • 公式: θ = θ − η ⋅ ∇ L ( θ ) \theta = \theta - \eta \cdot \nabla L(\theta) θ=θηL(θ)
      • η \eta η:学习率
      • ∇ L ( θ ) \nabla L(\theta) L(θ):损失函数 𝐿(𝜃) 对参数 𝜃 的梯度

2.2 带动量的优化算法

  • 动量梯度下降(Momentum SGD)
    • 公式: v t = β v t − 1 + ( 1 − β ) ∇ L ( θ )   θ = θ − η v t v_t = \beta v_{t-1} + (1-\beta) \nabla L(\theta) \\ \ \\ \theta = \theta - \eta v_t vt=βvt1+(1β)L(θ) θ=θηvt
    • 通过指数加权平均(EMA)的方法,缓解震荡问题;
    • 在陡峭方向收敛更快,在平坦方向减小震荡
  • Nesterov 动量(NAG, Nesterov Accelerated Gradient)
    • 公式 v t = β v t − 1 + ( 1 − β ) ∇ L ( θ − η v t − 1 )   θ = θ − η v t v_t = \beta v_{t-1} + (1 - \beta) \nabla L(\theta - \eta v_{t-1}) \\ \ \\ \theta = \theta - \eta v_t vt=βvt1+(1β)L(θηvt1) θ=θηvt
    • 计算梯度时使用“提前一步”的权重,收敛更快
    • 比普通 Momentum 具有更好的泛化能力

2.3 自适应学习率优化算法

  • Adagrad(Adaptive Gradient)

    • 公式: G t = G t − 1 + ∇ L ( θ ) ⊙ ∇ L ( θ )   θ = θ − η G t + ϵ ∇ L ( θ ) G_t = G_{t-1} + \nabla L(\theta) \odot \nabla L(\theta) \\ \ \\ \theta = \theta - \frac{\eta}{\sqrt{G_t} + \epsilon} \nabla L(\theta) Gt=Gt1+L(θ)L(θ) θ=θGt +ϵηL(θ)
    • 特点
      • 自适应调整不同参数的学习率
      • 主要问题是随着时间推移,学习率不断减小,最终趋近于 0
  • RMSprop(Root Mean Square Propagation)

    • 公式:
      G t = β G t − 1 + ( 1 − β ) ∇ L ( θ ) ⊙ ∇ L ( θ )   θ = θ − η G t + ϵ ∇ L ( θ ) G_t = \beta G_{t-1} + (1 - \beta) \nabla L(\theta) \odot \nabla L(\theta) \\ \ \\ \theta = \theta - \frac{\eta}{\sqrt{G_t} + \epsilon} \nabla L(\theta) Gt=βGt1+(1β)L(θ)L(θ) θ=θGt +ϵηL(θ)
    • 特点
      • 解决了 Adagrad 学习率衰减过快的问题
      • 适用于时序任务
  • Adam(Adaptive Moment Estimation)
    m t = β 1 m t − 1 + ( 1 − β 1 ) ∇ L ( θ )   v t = β 2 v t − 1 + ( 1 − β 2 ) ∇ L ( θ ) ⊙ ∇ L ( θ )   m t ^ = m t 1 − β 1 t , v t ^ = v t 1 − β 2 t   θ = θ − η v t ^ + ϵ m t ^ m_t = \beta_1 m_{t-1} + (1 - \beta_1) \nabla L(\theta) \\ \ \\ v_t = \beta_2 v_{t-1} + (1 - \beta_2) \nabla L(\theta) \odot \nabla L(\theta) \\ \ \\ \hat{m_t} = \frac{m_t}{1 - \beta_1^t}, \quad \hat{v_t} = \frac{v_t}{1 - \beta_2^t} \\ \ \\ \theta = \theta - \frac{\eta}{\sqrt{\hat{v_t}} + \epsilon} \hat{m_t} mt=β1mt1+(1β1)L(θ) vt=β2vt1+(1β2)L(θ)L(θ) mt^=1β1tmt,vt^=1β2tvt θ=θvt^ +ϵηmt^

  • AdamW(Weight Decay Adam)

    • 公式:
      θ = θ − η ( m t ^ v t ^ + ϵ + λ θ ) \theta = \theta - \eta \left( \frac{\hat{m_t}}{\sqrt{\hat{v_t}} + \epsilon} + \lambda \theta \right) θ=θη(vt^ +ϵmt^+λθ)
    • 特点:
      • AdamW 在 Adam 的基础上增加了 权重衰减(Weight Decay)

3.4. 其他优化算法

  • AdaDelta
    进一步改进 RMSprop,使学习率自适应变化
    Δ θ t = − Δ θ t − 1 2 + ϵ G t + ϵ ∇ L ( θ ) \Delta \theta_t = - \frac{\sqrt{\Delta \theta_{t-1}^2 + \epsilon}}{\sqrt{G_t + \epsilon}} \nabla L(\theta) Δθt=Gt+ϵ Δθt12+ϵ L(θ)
  • Nadam(Nesterov-accelerated Adam)
    结合 Adam 和 NAG 的优化方法:
    m t = β 1 m t − 1 + ( 1 − β 1 ) ∇ L ( θ )   m t ^ = m t 1 − β 1 t   θ = θ − η ( β 1 m t ^ + ( 1 − β 1 ) ∇ L ( θ ) v t ^ + ϵ ) m_t = \beta_1 m_{t-1} + (1 - \beta_1) \nabla L(\theta) \\ \ \\ \hat{m_t} = \frac{m_t}{1 - \beta_1^t} \\ \ \\ \theta = \theta - \eta \left( \frac{\beta_1 \hat{m_t} + (1 - \beta_1) \nabla L(\theta)}{\sqrt{\hat{v_t}} + \epsilon} \right) mt=β1mt1+(1β1)L(θ) mt^=1β1tmt θ=θη(vt^ +ϵβ1mt^+(1β1)L(θ))
  • Lion(EvoLved Sign Momentum)
    m t = β 1 m t − 1 + ( 1 − β 1 ) ∇ L ( θ )   θ = θ − η ( sign ( m t ) + λ θ ) m_t = \beta_1 m_{t-1} + (1 - \beta_1) \nabla L(\theta) \\ \ \\ \theta = \theta - \eta (\text{sign}(m_t) + \lambda \theta) mt=β1mt1+(1β1)L(θ) θ=θη(sign(mt)+λθ)
优化器适用场景优点缺点
SGD适用于小型数据集计算简单,稳定震荡大,收敛慢
Momentum适用于梯度震荡较大的情况解决震荡问题,加速收敛需要调参(β 值)
NAG适用于快速收敛的场景提前更新,收敛更快计算量略大
Adagrad适用于稀疏数据自适应学习率学习率下降过快
RMSprop适用于非平稳目标平稳收敛,适用于 RNN需要调 β
Adam适用于大多数任务结合 Momentum 和 RMSprop泛化能力较差
AdamW适用于大规模深度网络加入 Weight Decay,提高泛化计算量略大
Nadam适用于 NLP 任务结合 Adam 和 NAG计算量大

三、谈到对回归任务做多分桶,是如何实现的

为每个样本根据分桶规则(等频、等距)打上分桶标签,输出分类结果和回归结果

3.1. 后处理校正(最常用),强行约束结果

3.2. 用分类结果指导回归

y final = ∑ c p ( c ) ⋅ center ( c ) + Δ y reg y_{\text{final}} = \sum_c p(c) \cdot \text{center}(c) + \Delta y_{\text{reg}} yfinal=cp(c)center(c)+Δyreg

  • p ( c ) p(c) p(c) :分类预测的概率
  • center ( c ) \text{center}(c) center(c) :桶 c c c 的中心值
  • Δ y reg \Delta y_{\text{reg}} Δyreg :回归分支的修正项
import torch
import torch.nn as nn
import torch.optim as optim
from sklearn.preprocessing import KBinsDiscretizer
from sklearn.model_selection import train_test_split
import numpy as np

# ===== 1. 造数据 =====
np.random.seed(42)
X = np.random.rand(500, 5)
y = 10 * X[:, 0] + 5 * X[:, 1]**2 + np.random.randn(500)

# ===== 2. 分桶 =====
n_bins = 5
discretizer = KBinsDiscretizer(n_bins=n_bins, encode="ordinal", strategy="quantile")
y_class = discretizer.fit_transform(y.reshape(-1, 1)).astype(int).ravel()

# train/test
X_train, X_test, y_class_train, y_class_test, y_reg_train, y_reg_test = train_test_split(
    X, y_class, y, test_size=0.2, random_state=42
)

# 转 tensor
X_train = torch.tensor(X_train, dtype=torch.float32)
X_test  = torch.tensor(X_test, dtype=torch.float32)
y_class_train = torch.tensor(y_class_train, dtype=torch.long)
y_class_test  = torch.tensor(y_class_test, dtype=torch.long)
y_reg_train   = torch.tensor(y_reg_train, dtype=torch.float32).view(-1,1)
y_reg_test    = torch.tensor(y_reg_test, dtype=torch.float32).view(-1,1)

# ===== 3. 定义 MLP 模型 =====
class MultiTaskMLP(nn.Module):
    def __init__(self, input_dim, hidden_dim, n_classes):
        super().__init__()
        # 共享层
        self.shared = nn.Sequential(
            nn.Linear(input_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, hidden_dim),
            nn.ReLU()
        )
        # 分类头
        self.class_head = nn.Linear(hidden_dim, n_classes)
        # 回归头
        self.reg_head = nn.Linear(hidden_dim, 1)

    def forward(self, x):
        h = self.shared(x)
        class_out = self.class_head(h)   # logits
        reg_out = self.reg_head(h)       # continuous
        return class_out, reg_out

# ===== 4. 初始化模型 =====
model = MultiTaskMLP(input_dim=5, hidden_dim=64, n_classes=n_bins)
criterion_cls = nn.CrossEntropyLoss()
criterion_reg = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)

# ===== 5. 训练 =====
epochs = 100
for epoch in range(epochs):
    model.train()
    optimizer.zero_grad()
    out_cls, out_reg = model(X_train)
    loss_cls = criterion_cls(out_cls, y_class_train)
    loss_reg = criterion_reg(out_reg, y_reg_train)
    loss = loss_cls + loss_reg  # 联合loss

    loss.backward()
    optimizer.step()

    if (epoch+1) % 10 == 0:
        print(f"Epoch {epoch+1}/{epochs}, Loss={loss.item():.4f}, "
              f"Cls={loss_cls.item():.4f}, Reg={loss_reg.item():.4f}")

# ===== 6. 评估 =====
model.eval()
with torch.no_grad():
    pred_cls, pred_reg = model(X_test)
    acc = (pred_cls.argmax(1) == y_class_test).float().mean().item()
    mse = criterion_reg(pred_reg, y_reg_test).item()

print("桶分类准确率:", acc)
print("连续值预测 MSE:", mse)

3.3. 给回归头的loss添加约束项()

def consistency_loss(pred_reg, pred_cls, bin_edges):
    pred_bucket = torch.argmax(pred_cls, dim=1)
    bin_edges = torch.tensor(bin_edges, dtype=torch.float32, device=pred_reg.device)
    low = bin_edges[pred_bucket]
    high = bin_edges[pred_bucket + 1]
    y_val = pred_reg.squeeze(1)
    # relu : max(0,x)
    penalty = torch.relu(low - y_val) + torch.relu(y_val - high)  
    return penalty.mean()

# ===== 5. 训练 =====
bin_edges = discretizer.bin_edges_[0]  # 取第 0 个特征的 bin_edges
print(bin_edges)
epochs = 100
for epoch in range(epochs):
    model.train()
    optimizer.zero_grad()
    out_cls, out_reg = model(X_train)
    loss_cls = criterion_cls(out_cls, y_class_train)
    loss_reg = criterion_reg(out_reg, y_reg_train)
    loss_cons = consistency_loss(out_reg, out_cls, bin_edges) # 约束项
    loss = loss_cls + loss_reg + loss_cons
    
    loss.backward()
    optimizer.step()

    if (epoch+1) % 10 == 0:
        print(f"Epoch {epoch+1}/{epochs}, Loss={loss.item():.4f}, "
              f"Cls={loss_cls.item():.4f}, Reg={loss_reg.item():.4f}")

# ===== 6. 评估 =====
model.eval()
with torch.no_grad():
    pred_cls, pred_reg = model(X_test)
    acc = (pred_cls.argmax(1) == y_class_test).float().mean().item()
    mse = criterion_reg(pred_reg, y_reg_test).item()

print("桶分类准确率:", acc)
print("连续值预测 MSE:", mse)

四、除了梯度下降,还有哪些参数求解算法?

在机器学习和深度学习中,除了梯度下降(Gradient Descent)之外,还有多种用于参数求解的算法,具体如下:

方法类别是否迭代是否适合大规模是否可导典型应用
正规方程可导线性回归
牛顿法可导小型回归或分类问题
L-BFGS可导逻辑回归、神经网络
坐标下降可导/分段导Lasso、稀疏模型
ALS不要求可导推荐系统、矩阵分解
EM算法中等不要求GMM、隐马尔可夫模型
遗传算法/PSO不可导也可黑盒优化、组合优化问题

4.1. 用牛顿法和梯度下降法求解 sqrt(x)

见【搜广推校招面经八十

### 关于目标检测的试经验分享 #### 准备基础知识的重要性 目标检测作为计算机视觉中的重要分支,在过程中通常会涉及基础理论和实际应用两方。为了更好地应对目标检测方向的技术试,应聘者需掌握扎实的基础知识,包括但不限于卷积神经网络(CNN)、常见目标检测算法(如 YOLO、Faster R-CNN 和 SSD),以及这些模型的核心原理和技术特点[^1]。 #### 实施细节的关注 某些技术试官可能会深入探讨具体实现细节,因此建议提前熟悉主流框架(如 TensorFlow 或 PyTorch)下的目标检测模型搭建过程。例如,可以尝试自己动手实现一个简单的 Faster R-CNN 模型,并理解其 Region Proposal Network (RPN) 的工作机制及其优化方法。 以下是基于 Python 使用 PyTorch 构建简单目标检测器的一个代码片段: ```python import torch from torchvision.models.detection import fasterrcnn_resnet50_fpn def load_model(num_classes=91): model = fasterrcnn_resnet50_fpn(pretrained=True) in_features = model.roi_heads.box_predictor.cls_score.in_features model.roi_heads.box_predictor = FastRCNNPredictor(in_features, num_classes) return model model = load_model() ``` #### 时间管理和沟通技巧 除了技术能力外,良好的时间管理能力和清晰表达也是成功通过试的关键因素之一。系统化地安排好每一轮试环节,及时接收并确认试通知;同时利用试评估工具反馈个人表现以便改进下一次的表现[^2]。 #### 工作环境适应力考察 部分公司可能还会关注候选人的团队协作精神及对公司文化的认同感。比如金山办公软件以其较为轻松自由的工作氛围著称,即便是在正式场合中也可能允许员工穿便服上班,体现了企业内部开放包容的态度[^3]。 #### 失败案例分析与成长反思 即使经历失败也不要气馁,每一次挫折都是宝贵的学习机会。一位求职者曾因准备不足而未能顺利通过阿里巴巴集团针对应届毕业生开展的一轮电话初筛考试,但他从中认识到持续学习更新专业知识体系的重要性,并决定调整策略重新出发[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值