1. 为什么需要"手撕"机器学习与深度学习题目

在技术面试和实际工作中,我们常常会遇到需要现场推导机器学习(ML)和深度学习(DL)算法的情况。这种"手撕"能力不仅考察对基础理论的掌握程度,更是评估工程师能否将数学公式转化为实际代码的关键指标。

我经历过多次大厂面试,发现90%的候选人会在白板推导环节暴露出理论短板。一个典型的例子是,当被要求从零实现一个简单的神经网络时,多数人只能写出框架代码,却无法正确推导反向传播的矩阵运算过程。这种差距往往决定了面试的成败。

2. 机器学习基础题精讲

2.1 线性回归的完整推导

让我们从一个最基础的例子开始:实现线性回归的闭式解(closed-form solution)。很多面试者知道公式 (X^T X)^{-1} X^T y,但说不清楚为什么这样设计。

推导过程:

  1. 定义损失函数:J(θ) = 1/2 (Xθ - y)^T (Xθ - y)
  2. 对θ求导并令导数为零: ∇J(θ) = X^T (Xθ - y) = 0
  3. 解得:θ = (X^T X)^{-1} X^T y

面试陷阱:

  • 当X^T X不可逆时怎么办?(提示:加入λI)
  • 为什么用最小二乘而不是绝对值误差?(提示:可导性)

2.2 逻辑回归的梯度推导

这是面试最高频的题目之一。关键在于理解sigmoid函数导数的特殊性质:σ' = σ(1-σ)。

def logistic_gradient(X, y, w):
    """
    X: (n_samples, n_features)
    y: (n_samples,)
    w: (n_features,)
    """
    z = np.dot(X, w)
    h = 1 / (1 + np.exp(-z))  # sigmoid
    gradient = np.dot(X.T, (h - y)) / len(y)
    return gradient

常见错误:

  • 忘记sigmoid函数的导数特性
  • 没有对梯度进行归一化(除以样本数)

3. 深度学习核心题解析

3.1 实现一个简单的神经网络

假设我们需要实现一个单隐层的神经网络,面试官通常会考察以下能力:

class TwoLayerNet:
    def __init__(self, input_size, hidden_size, output_size):
        self.W1 = np.random.randn(input_size, hidden_size) * 0.01
        self.b1 = np.zeros(hidden_size)
        self.W2 = np.random.randn(hidden_size, output_size) * 0.01
        self.b2 = np.zeros(output_size)
    
    def forward(self, X):
        self.z1 = np.dot(X, self.W1) + self.b1
        self.a1 = np.maximum(0, self.z1)  # ReLU
        self.z2 = np.dot(self.a1, self.W2) + self.b2
        exp_scores = np.exp(self.z2 - np.max(self.z2, axis=1, keepdims=True))
        self.probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
        return self.probs

反向传播的关键点:

  1. softmax的梯度计算
  2. ReLU的非线性特性
  3. 参数初始化的技巧(小随机数)

3.2 手写Self-Attention

Transformer是当前面试的热点,下面是一个简化版的self-attention实现:

def scaled_dot_product_attention(Q, K, V):
    """
    Q: (batch_size, seq_len, d_k)
    K: (batch_size, seq_len, d_k)
    V: (batch_size, seq_len, d_v)
    """
    d_k = Q.shape[-1]
    scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(d_k)
    attention = torch.softmax(scores, dim=-1)
    return torch.matmul(attention, V)

常见考察点:

  • 为什么要除以√d_k?(防止点积过大导致softmax梯度消失)
  • 多头注意力的并行计算如何实现?

4. 优化算法与调参技巧

4.1 从零实现Adam优化器

Adam是深度学习中最常用的优化器之一,其核心在于动量(momentum)和自适应学习率:

class Adam:
    def __init__(self, lr=0.001, beta1=0.9, beta2=0.999, eps=1e-8):
        self.lr = lr
        self.beta1 = beta1
        self.beta2 = beta2
        self.eps = eps
        self.m = None
        self.v = None
        self.t = 0
    
    def update(self, params, grads):
        if self.m is None:
            self.m = [np.zeros_like(p) for p in params]
            self.v = [np.zeros_like(p) for p in params]
        
        self.t += 1
        for i, (p, g) in enumerate(zip(params, grads)):
            self.m[i] = self.beta1 * self.m[i] + (1 - self.beta1) * g
            self.v[i] = self.beta2 * self.v[i] + (1 - self.beta2) * (g ** 2)
            m_hat = self.m[i] / (1 - self.beta1 ** self.t)
            v_hat = self.v[i] / (1 - self.beta2 ** self.t)
            p -= self.lr * m_hat / (np.sqrt(v_hat) + self.eps)

调参经验:

  • β1通常保持0.9不变
  • β2可以尝试0.98或0.999
  • 学习率需要根据任务调整,一般从3e-4开始尝试

5. 实战中的特殊场景处理

5.1 处理类别不平衡问题

当遇到正负样本比例悬殊的情况(如1:100),可以采用以下策略:

  1. 重采样(oversampling/undersampling)
  2. 类别权重(class_weight)
  3. 修改损失函数(focal loss)
class FocalLoss(nn.Module):
    def __init__(self, alpha=0.25, gamma=2.0):
        super().__init__()
        self.alpha = alpha
        self.gamma = gamma
    
    def forward(self, inputs, targets):
        BCE_loss = F.binary_cross_entropy_with_logits(inputs, targets, reduction='none')
        pt = torch.exp(-BCE_loss)
        focal_loss = self.alpha * (1-pt)**self.gamma * BCE_loss
        return focal_loss.mean()

5.2 模型部署时的量化技巧

在移动端部署时,模型量化能显著减少内存占用和加速推理:

# 训练后动态量化
model = torch.quantization.quantize_dynamic(
    model, {torch.nn.Linear}, dtype=torch.qint8
)

# 量化感知训练
model.qconfig = torch.quantization.get_default_qat_qconfig('fbgemm')
torch.quantization.prepare_qat(model, inplace=True)
# ...正常训练...
quantized_model = torch.quantization.convert(model.eval(), inplace=False)

注意事项:

  • 量化会导致约1-3%的精度损失
  • 某些操作(如LSTM)需要特殊处理
  • 部署前必须测试量化模型的实际推理速度

更多推荐