字节推荐算法
一、SVM 原理与推导
1.1. 问题定义
给定训练集:
{ ( x 1 , y 1 ) , ( x 2 , y 2 ) , . . . , ( x n , y n ) } , x i ∈ R d , y i ∈ { − 1 , + 1 } \{(x_1, y_1), (x_2, y_2), ..., (x_n, y_n)\}, \quad x_i \in \mathbb{R}^d, \quad y_i \in \{-1, +1\} {(x1,y1),(x2,y2),...,(xn,yn)},xi∈Rd,yi∈{−1,+1}
目标是找到一个线性分类器:
f ( x ) = sign ( w T x + b ) f(x) = \text{sign}(w^T x + b) f(x)=sign(wTx+b)
使得所有样本被正确分类,且分类间隔最大。
1.2. 几何间隔与函数间隔
几何间隔定义为:
γ i = y i ( w T x i + b ) ∥ w ∥ \gamma_i = \frac{y_i (w^T x_i + b)}{\|w\|} γi=∥w∥yi(wTxi+b)
最大化几何间隔:
max γ = min i γ i \max \gamma = \min_i \gamma_i maxγ=iminγi
约定几何间隔为 1,转换为约束:
y i ( w T x i + b ) ≥ 1 y_i (w^T x_i + b) \geq 1 yi(wTxi+b)≥1
1.3. 原始优化问题(线性可分)
将最大间隔问题转化为优化问题:
min w , b 1 2 ∥ w ∥ 2 subject to y i ( w T x i + b ) ≥ 1 , i = 1 , . . . , n \min_{w,b} \quad \frac{1}{2} \|w\|^2 \\ \text{subject to} \quad y_i (w^T x_i + b) \geq 1, \quad i = 1, ..., n w,bmin21∥w∥2subject toyi(wTxi+b)≥1,i=1,...,n
1.4. 对偶问题推导(拉格朗日乘子法)
构造拉格朗日函数:
L ( w , b , α ) = 1 2 ∥ w ∥ 2 − ∑ i = 1 n α i [ y i ( w T x i + b ) − 1 ] L(w, b, \alpha) = \frac{1}{2} \|w\|^2 - \sum_{i=1}^n \alpha_i [y_i (w^T x_i + b) - 1] L(w,b,α)=21∥w∥2−i=1∑nαi[yi(wTxi+b)−1]
对 w , b w, b w,b 求偏导并令其为 0:
- ∂ L ∂ w = 0 ⇒ w = ∑ i α i y i x i \frac{\partial L}{\partial w} = 0 \Rightarrow w = \sum_i \alpha_i y_i x_i ∂w∂L=0⇒w=∑iαiyixi
- ∂ L ∂ b = 0 ⇒ ∑ i α i y i = 0 \frac{\partial L}{\partial b} = 0 \Rightarrow \sum_i \alpha_i y_i = 0 ∂b∂L=0⇒∑iαiyi=0
代入得到对偶问题:
max α ∑ i = 1 n α i − 1 2 ∑ i , j α i α j y i y j x i T x j subject to ∑ i = 1 n α i y i = 0 , α i ≥ 0 \max_\alpha \sum_{i=1}^n \alpha_i - \frac{1}{2} \sum_{i,j} \alpha_i \alpha_j y_i y_j x_i^T x_j \\ \text{subject to} \quad \sum_{i=1}^n \alpha_i y_i = 0, \quad \alpha_i \geq 0 αmaxi=1∑nαi−21i,j∑αiαjyiyjxiTxjsubject toi=1∑nαiyi=0,αi≥0
1.5. 支持向量与决策函数
求得 α i \alpha_i αi 后,有些 α i > 0 \alpha_i > 0 αi>0 的样本为支持向量(support vectors)。
最终分类器为:
f ( x ) = sign ( ∑ i ∈ S V α i y i x i T x + b ) f(x) = \text{sign}\left(\sum_{i \in SV} \alpha_i y_i x_i^T x + b\right) f(x)=sign(i∈SV∑αiyixiTx+b)
1.6. 软间隔 SVM(线性不可分)
引入松弛变量 (\xi_i),允许部分样本被错分:
min w , b , ξ 1 2 ∥ w ∥ 2 + C ∑ i = 1 n ξ i \min_{w,b,\xi} \quad \frac{1}{2} \|w\|^2 + C \sum_{i=1}^n \xi_i minw,b,ξ21∥w∥2+C∑i=1nξi
subject to y i ( w T x i + b ) ≥ 1 − ξ i , ξ i ≥ 0 \text{subject to} \quad y_i (w^T x_i + b) \geq 1 - \xi_i, \quad \xi_i \geq 0 subject toyi(wTxi+b)≥1−ξi,ξi≥0
其中 C C C 为惩罚因子,控制间隔与误差的权衡。
1.7. 核方法(非线性 SVM)
使用核函数将数据映射到高维空间,实现非线性可分的分类:
f ( x ) = sign ( ∑ i ∈ S V α i y i κ ( x i , x ) + b ) f(x) = \text{sign}\left(\sum_{i \in SV} \alpha_i y_i \kappa(x_i, x) + b\right) f(x)=sign(i∈SV∑αiyiκ(xi,x)+b)
常见核函数:
- 线性核: κ ( x i , x j ) = x i T x j \kappa(x_i, x_j) = x_i^T x_j κ(xi,xj)=xiTxj
- 多项式核: ( x i T x j + c ) d (x_i^T x_j + c)^d (xiTxj+c)d
- RBF 核(高斯核): exp ( − ∥ x i − x j ∥ 2 2 σ 2 ) \exp\left(-\frac{\|x_i - x_j\|^2}{2\sigma^2}\right) exp(−2σ2∥xi−xj∥2)
二、XGBoost 和 LightGBM 中的树构建与加速机制详解
2.1. 最优分裂节点的选择
2.1.1 分裂点选择目标
目标是最大化增益(Gain):
Gain = 1 2 [ G L 2 H L + λ + G R 2 H R + λ − ( G L + G R ) 2 H L + H R + λ ] − γ \text{Gain} = \frac{1}{2} \left[ \frac{G_L^2}{H_L + \lambda} + \frac{G_R^2}{H_R + \lambda} - \frac{(G_L + G_R)^2}{H_L + H_R + \lambda} \right] - \gamma Gain=21[HL+λGL2+HR+λGR2−HL+HR+λ(GL+GR)2]−γ
其中:
- G L , H L G_L, H_L GL,HL:左子树的梯度和 Hessian
- G R , H R G_R, H_R GR,HR:右子树的梯度和 Hessian
- λ \lambda λ:正则项
- γ \gamma γ:分裂代价
遍历每一个特征的候选划分点,计算 Gain,选择最大 Gain 的分裂点。
2.2. 加速技巧对比
项目 | XGBoost | LightGBM |
---|---|---|
分裂方式 | 预排序(pre-sorted)、近似算法(histogram) | 直方图 (histogram) |
特点 | 精度高但计算慢 | 速度快,内存友好 |
增益计算 | 精确计算梯度与 Hessian | 离散 bin 上聚合统计量估算 |
2.2.1. 预排序(Pre-sorted)机制(XGBoost 早期)
原理(排序耗时,缓存效率差):
- 每个特征提前对样本排序(一次性预处理)。
- 每次分裂时只需线性扫一遍候选点,计算增益。
2.2.2. 直方图优化(Histogram-based)(LGB)
原理:
- 将连续特征离散为有限数量的 bins(桶)。
- 在每个 bin 上统计梯度和 Hessian 累加值。
- 枚举 bins 而不是每个样本,提高效率。
- XGBoost 中的
tree_method=hist
- LightGBM 默认使用 histogram 构建
2.3. 缺失值处理
(1)训练时的处理方式:
1. XGBoost 的做法:
- 在每个特征分裂时,尝试将缺失值分到左或右子树两种情况,计算哪种情况下 Gain 更高。
- 将缺失值统一分到“默认方向”。
2. LightGBM 的做法:
- 将缺失值看成一个独立的 bin,参与增益比较。
- 根据缺失值所在 bin 的增益选择其应去的方向。
(2)预测时的处理:
- XGB:根据训练中学到的“缺失值默认方向”将缺失样本分配下去。所以预测时无需补全缺失值。
- LGB:仍然看作独立的bin
三、有了解过设计模式吗?
设计模式大致分为 三大类,共23 种经典模式
3.1. 创建型模式(Creational Patterns)- 解决“对象如何创建”的问题
模式名称 | 作用 |
---|---|
Singleton(单例) | 保证一个类只有一个实例 |
Factory Method(工厂方法) | 把实例化交给子类 |
Abstract Factory(抽象工厂) | 创建一系列相关或相互依赖的对象 |
Builder(建造者) | 分步骤构建复杂对象 |
Prototype(原型) | 通过拷贝已有实例创建新对象 |
3.2. 结构型模式(Structural Patterns)- 解决“类或对象如何组合”的问题
模式名称 | 作用 |
---|---|
Adapter(适配器) | 连接接口不兼容的类 |
Bridge(桥接) | 分离抽象和实现,使其独立变化 |
Composite(组合) | 统一处理部分和整体对象 |
Decorator(装饰器) | 动态添加功能,不改变结构 |
Facade(外观) | 提供统一接口,简化子系统使用 |
Flyweight(享元) | 共享对象,减少内存使用 |
Proxy(代理) | 控制对目标对象的访问 |
3.3. 行为型模式(Behavioral Patterns)- 解决“对象间如何通信、如何协作”的问题
模式名称 | 作用 |
---|---|
Strategy(策略) | 封装可互换的算法 |
Observer(观察者) | 实现一对多的通知机制 |
Command(命令) | 封装请求为对象 |
Chain of Responsibility(责任链) | 顺序传递请求直到被处理 |
State(状态) | 状态转换由对象自己控制 |
Template Method(模板方法) | 父类定义算法骨架,子类实现细节 |
Iterator(迭代器) | 顺序访问聚合对象元素 |
Mediator(中介者) | 降低对象间耦合,由中介协调通信 |
Memento(备忘录) | 保存和恢复对象状态 |
Visitor(访问者) | 在不修改类的情况下添加操作 |
Interpreter(解释器) | 定义语言语法,构建解释器解释表达式 |
四、解释装饰器模式(Decorator Pattern)
装饰器模式是一种结构型设计模式,允许在不改变原有类结构的情况下,动态地给对象添加功能。它通过将原始对象包装在装饰器对象中,达到扩展功能的目的。
本质:使用“对象包装”替代“类继承”来扩展功能。
4.1. 实际中的应用
场景 | 应用方式 |
---|---|
Python 函数装饰器 | @decorator 语法,动态增强函数功能 |
Java I/O 流体系 | BufferedReader 包装 InputStreamReader 等 |
日志记录 | 在函数执行前后加日志 |
权限控制 | 在执行逻辑前检查权限 |
缓存机制 | 给函数增加缓存能力 |
五、46. 全排列
- 代码:
按照答案每个位置该选的位置。
class Solution:
def permute(self, nums):
n = len(nums)
ans = []
path = [0] * n # 所有的排列长度都是一样的
def dfs(i, s): # 从答案的角度,第i个位置该从剩余s中选择哪一个
if i == n:
ans.append(path.copy())
return
for x in s:
path[i] = x
dfs(i+1, s-{x})
dfs(0, set(nums))
return ans