小红书推荐算法
一、BN(Batch Normalization)在训练和测试的区别
Batch Normalization(批归一化,BN)是一种加速深度神经网络训练的技术,它通过对每个 mini-batch 计算均值和方差来归一化输入特征,从而稳定训练过程,减少梯度消失/梯度爆炸问题。
1.1. 训练阶段
在训练过程中,BN 采用 mini-batch 统计信息 进行归一化:
- 计算方式 :
- 计算当前 mini-batch 的均值和方差:
μ B = 1 m ∑ i = 1 m x i \mu_B = \frac{1}{m} \sum_{i=1}^{m} x_i μB=m1i=1∑mxi
σ B 2 = 1 m ∑ i = 1 m ( x i − μ B ) 2 \sigma_B^2 = \frac{1}{m} \sum_{i=1}^{m} (x_i - \mu_B)^2 σB2=m1i=1∑m(xi−μB)2 - 归一化:
x ^ i = x i − μ B σ B 2 + ϵ \hat{x}_i = \frac{x_i - \mu_B}{\sqrt{\sigma_B^2 + \epsilon}} x^i=σB2+ϵ xi−μB - 进行缩放和平移:
y i = γ x ^ i + β y_i = \gamma \hat{x}_i + \beta yi=γx^i+β
- 计算当前 mini-batch 的均值和方差:
- 关键点 :
- 训练时,每个 batch 都会 单独计算均值和方差。
- 由于 mini-batch 之间的统计信息不同,BN 计算的均值和方差会有波动。
- 为了测试时稳定 ,BN 维护一个 全局均值和方差 ,通过滑动平均 的方式更新:
μ global ← ( 1 − α ) ⋅ μ global + α ⋅ μ B \mu_{\text{global}} \leftarrow (1 - \alpha) \cdot \mu_{\text{global}} + \alpha \cdot \mu_B μglobal←(1−α)⋅μglobal+α⋅μB
σ global 2 ← ( 1 − α ) ⋅ σ global 2 + α ⋅ σ B 2 \sigma^2_{\text{global}} \leftarrow (1 - \alpha) \cdot \sigma^2_{\text{global}} + \alpha \cdot \sigma^2_B σglobal2←(1−α)⋅σglobal2+α⋅σB2 - 其中 α \alpha α 是动量系数(通常设为 0.9 或 0.99)。
1.2. 测试阶段
在测试阶段,BN 不再依赖 mini-batch 统计,而是使用 训练过程中累积的全局均值和方差:
- 计算方式 :
- 使用全局均值和方差:
x ^ i = x i − μ global σ global 2 + ϵ \hat{x}i = \frac{x_i - \mu{\text{global}}}{\sqrt{\sigma^2_{\text{global}} + \epsilon}} x^i=σglobal2+ϵ xi−μglobal - 进行缩放和平移:
y i = γ x ^ i + β y_i = \gamma \hat{x}_i + \beta yi=γx^i+β
- 使用全局均值和方差:
- 关键点 :
- 不再计算 batch 统计量 ,而是使用训练时累积的全局均值和方差。
- 这样可以保证测试阶段的稳定性,避免 batch 之间的分布变化导致不一致性。
1.3. 训练和测试的关键区别
训练阶段 | 测试阶段 | |
---|---|---|
归一化方式 | 用当前 mini-batch 的均值和方差 | 用全局均值和方差 |
统计信息计算 | 每个 batch 单独计算 | 直接使用训练时累积的全局均值和方差 |
统计信息是否更新 | 通过滑动平均更新全局均值和方差 | 不更新(固定全局均值和方差) |
计算公式 | μ B , σ B 2 \mu_B, \sigma_B^2 μB,σB2 来归一化 | μ global , σ global 2 \mu_{\text{global}}, \sigma^2_{\text{global}} μglobal,σglobal2 来归一化 |
适用场景 | 训练时,batch 统计可能波动 | 测试时,保证稳定性 |
1.4. BN 训练和测试不一致的问题
(1) 训练和测试分布不匹配
- 在训练时,每个 mini-batch 计算的均值和方差会有一定波动,而测试时使用的是固定的全局均值和方差。
- 这可能导致训练和测试阶段的特征分布不同,影响模型性能。
(2) BN 在小 batch size 下的影响
- 若 batch size 很小(例如 m = 2 m=2 m=2 或 m = 4 m=4 m=4),计算的均值和方差可能波动较大,导致 BN 训练时不稳定。
- 解决方法:
- 使用 Group Normalization(GN):在通道维度上进行归一化,避免 batch size 过小的问题。
- 调整 BN 的 momentum 参数 :降低 α \alpha α,让全局均值和方差更稳定。
二、Dropout在训练和测试的区别
见【搜广推校招面经二十七】
三、ID 类特征如何处理、连续值分桶的原因?
推荐系统中,用户 ID、物品 ID、类别 ID 等 ID 类特征 不能直接用于模型,需要进行特征工程。
(1) One-Hot 编码
- 方法:为每个 ID 创建一个独立的维度,属于该 ID 的样本对应位置设为 1,其他为 0。
- 缺点 :
- 维度极高,存储和计算成本大(如百万级别 ID)。
- 过于稀疏,无法挖掘 ID 之间的相似性。
- 适用场景:类别较少时可用,如少量离散标签。
(2) Embedding 向量化
- 方法 :
- 通过 Embedding 层 将 ID 映射为低维稠密向量。
- Embedding 维度通常为
log(n) ~ sqrt(n)
(n 为 ID 种类数)。 - 训练过程中,Embedding 通过梯度更新,学习 ID 之间的相似性。
- 优点 :
- 解决高维稀疏问题,降低计算复杂度。
- 允许 ID 之间的特征共享,提高泛化能力。
- 适用场景:用户 ID、商品 ID、类别 ID、品牌等离散特征。
(3) 统计特征(Target Encoding / 频次统计)
- 方法 :
- 计算 ID 在历史数据上的某些统计值,如点击率、转化率、均值等。
- 例如
item_id
的点击率:
C T R ( i t e m ) = 点击次数 曝光次数 + λ CTR(item) = \frac{\text{点击次数}}{\text{曝光次数} + \lambda} CTR(item)=曝光次数+λ点击次数 - 这里的 λ \lambda λ 是平滑系数,避免数据稀疏问题。
- 优点 :
- 直接利用历史行为信息,有一定的业务可解释性。
- 缺点 :
- 需要对新 ID 做平滑处理,否则可能无法处理冷启动问题。
- 可能会引入信息泄露(Target Leakage),需要 K 折交叉验证。
3.2. 连续值分桶的原因
(1) 处理长尾分布
- 连续值(如消费金额、活跃天数)可能呈 长尾分布,极值影响较大。
- 通过 分桶(Binning),可以减少异常值的影响,提高模型的稳健性。
(2) 提高模型的表达能力
- 线性模型 只能学习 线性关系 ,但许多连续变量对目标的影响可能是 非线性的。
- 通过 分桶 + One-Hot/Embedding 处理,可以让模型学习非线性特征。
(3) 降低计算复杂度
- 连续值直接输入可能导致特征维度过大(特别是高精度浮点数)。
- 分桶后,连续特征变为有限个类别特征,减少计算量。
(5) 适用于深度学习
- 在深度学习模型(如 DNN、Wide & Deep)中,连续值经过 分桶 + Embedding 处理,可以提升模型的泛化能力。
3.3 分桶(Binning)的常见方法
(1) 等宽分桶(Equal Width Binning)
- 方法 :按照固定宽度进行分桶,例如
[0, 10]
,[10, 20]
。 - 缺点:如果数据分布不均匀,部分桶可能数据较少,影响学习。
(2) 等频分桶(Equal Frequency Binning)
- 方法 :保证每个桶中的样本数相同,例如按 百分位数(quantile) 进行划分。
- 优点:适用于长尾分布数据,每个桶的样本量均衡。
(3) 哈希分桶(Hash Binning)在推荐系统中的应用
Ⅰ. 为什么使用哈希分桶?
在处理 ID 类特征 (如用户 ID、商品 ID)或 高基数离散特征 (如 IP 地址、设备型号)时,直接使用 One-Hot 或 Embedding 会带来 维度爆炸 和 存储开销过大 的问题。
哈希分桶(Hash Binning)是一种 高效降维 和 避免存储过大的方法,常用于推荐系统和大规模机器学习任务。
Ⅱ. 哈希分桶的基本方法
哈希分桶(Hash Binning)主要基于哈希函数,将原始 ID 或类别特征映射到固定数量的桶中。
(1) 哈希映射(Hashing Trick)
-
通过哈希函数
h(x)
将 高维类别特征 映射到 固定数量的桶(buckets) :
b u c k e t = h ( I D ) m o d B bucket = h(ID) \mod B bucket=h(ID)modB其中:
h(ID)
是哈希函数(如MurmurHash
,MD5
,SHA-256
)。B
是桶的数量(通常B << N
,即桶数远小于 ID 总数)。
-
这样,即使 ID 总数 N 很大 ,最终的桶数
B
仍然是有限的,避免了 维度爆炸。
(2) 哈希 Embedding
- 传统 Embedding 需要为每个 ID 训练一个独立向量:
- 问题 :如果 ID 种类过多(如 1 亿用户),Embedding 需要 大量存储。
- 通过 哈希分桶 + Embedding :
- 先用哈希分桶减少 ID 维度,再为每个桶学习 Embedding。
- 优点:减少存储需求,计算更加高效。
- 缺点 :可能不同 ID 被映射到同一桶,导致 哈希冲突(Hash Collision)。
四、手撕 sqrt(x)一牛顿迭代法、梯度下降法
4.1. 牛顿迭代法(Newton's Method)
4.1.1 基本思想
牛顿迭代法(Newton's Method)是一种用于求解非线性方程 f ( x ) = 0 f(x) =0 f(x)=0 的数值方法,基于 泰勒展开 的近似,利用导数信息进行快速逼近。
对于一个函数 f ( x ) f(x) f(x),使用一阶泰勒展开:
f ( x ) ≈ f ( x n ) + f ′ ( x n ) ( x − x n ) f(x) \approx f(x_n) + f'(x_n) (x - x_n) f(x)≈f(xn)+f′(xn)(x−xn)
令 f ( x ) = 0 f(x) = 0 f(x)=0,得到更新公式:
x n + 1 = x n − f ( x n ) f ′ ( x n ) x_{n+1} = x_n - \frac{f(x_n)}{f'(x_n)} xn+1=xn−f′(xn)f(xn)
这个公式表示在点 x n x_n xn 处找到 切线 ,并用它的零点作为下一个迭代值。
4.1.2 用牛顿法求解平方根
假设我们要求 y = x y = \sqrt{x} y=x ,可以转化为方程:
f ( y ) = y 2 − x = 0 f(y) = y^2 - x = 0 f(y)=y2−x=0
求导数:
f ′ ( y ) = 2 y f'(y) = 2y f′(y)=2y
代入牛顿迭代公式:
y n + 1 = y n − y n 2 − x 2 y n y_{n+1} = y_n - \frac{y_n^2 - x}{2y_n} yn+1=yn−2ynyn2−x
化简:
y n + 1 = 1 2 ( y n + x y n ) y_{n+1} = \frac{1}{2} \left( y_n + \frac{x}{y_n} \right) yn+1=21(yn+ynx)
这就是牛顿迭代法求平方根的核心公式。
4.1.3 牛顿法特点
优点
- 二次收敛 :每次迭代误差缩小 平方倍,非常快。
- 适用于非线性方程:可以求解平方根、对数、三角函数等复杂函数的零点。
- 数值稳定:一般情况下比梯度下降更稳定。
缺点
- 需要计算 导数 f ′ ( x ) f'(x) f′(x),有时比较复杂。
- 依赖初值,如果初始点选得不好,可能收敛慢或不收敛。
4.2. 梯度下降法(Gradient Descent)
4.2.1 基本思想
梯度下降法(Gradient Descent)是一种 优化方法 ,用于找到函数的最小值。核心思想是:
- 计算函数的 梯度(导数)。
- 沿着 梯度的相反方向 更新变量,使得函数值下降。
假设目标函数是:
f ( y ) = y 2 − x f(y) = y^2 - x f(y)=y2−x
其梯度(导数)为:
∇ f ( y ) = 2 y \nabla f(y) = 2y ∇f(y)=2y
按照梯度下降的更新规则:
y n + 1 = y n − α ⋅ ∇ f ( y n ) y_{n+1} = y_n - \alpha \cdot \nabla f(y_n) yn+1=yn−α⋅∇f(yn)
其中 α \alpha α 是学习率(learning rate),控制步长大小。
4.2.2 用梯度下降法求平方根
如果要求 y = x y = \sqrt{x} y=x ,可以改写目标函数:
f ( y ) = ( y 2 − x ) 2 f(y) = (y^2 - x)^2 f(y)=(y2−x)2
对 y y y 求导:
d d y f ( y ) = 2 ( y 2 − x ) ⋅ 2 y = 4 y ( y 2 − x ) \frac{d}{dy} f(y) = 2(y^2 - x) \cdot 2y = 4y(y^2 - x) dydf(y)=2(y2−x)⋅2y=4y(y2−x)
更新公式:
y n + 1 = y n − α ⋅ 4 y n ( y n 2 − x ) y_{n+1} = y_n - \alpha \cdot 4y_n (y_n^2 - x) yn+1=yn−α⋅4yn(yn2−x)
4.2.3 梯度下降法特点
优点
- 适用于 任意可微函数 ,可以用来求解 最优化问题(如机器学习中的参数训练)。
- 简单易实现,只需要计算一阶导数。
缺点
- 收敛速度慢 ,相比牛顿法通常需要 更多迭代 才能逼近正确值。
- 需要 调整学习率 α \alpha α:
- 学习率太大:可能震荡或发散。
- 学习率太小:收敛速度很慢。
- 可能 陷入局部最优,特别是在非凸优化问题中。
4.3. 牛顿法 vs 梯度下降法
方法 | 适用场景 | 计算复杂度 | 收敛速度 | 依赖初值 | 适用范围 |
---|---|---|---|---|---|
牛顿法 | 求解方程零点 | 需要计算二阶导数 | 二次收敛(快) | 需要好初值,否则可能不收敛 | 适用于数值计算 |
梯度下降 | 求解最优化问题 | 只需计算一阶导数 | 线性收敛(慢) | 对初值不敏感,但收敛速度受学习率影响 | 适用于机器学习和深度学习 |
4.4. python代码实现
python
# 使用牛顿法求解 sqrt(x)
def sqrt_newton(x, tol=1e-6, max_iter=100):
if x < 0:
raise ValueError("Cannot compute the square root of a negative number")
y = x # 初始猜测值
for _ in range(max_iter):
y_next = 0.5 * (y + x / y) # 牛顿迭代公式
if abs(y_next - y) < tol:
break
y = y_next
return y
# 使用梯度下降法求解 sqrt(x)
def sqrt_gradient_descent(x, alpha=0.01, tol=1e-6, max_iter=10000):
if x < 0:
raise ValueError("Cannot compute the square root of a negative number")
y = x / 2 # 初始猜测值
for _ in range(max_iter):
gradient = 4 * y * (y**2 - x) # 计算梯度
y_next = y - alpha * gradient # 梯度下降更新
if abs(y_next - y) < tol:
break
y = y_next
return y
# 测试
x = 25
print(f"牛顿法计算 sqrt({x}) = {sqrt_newton(x)}")
print(f"梯度下降法计算 sqrt({x}) = {sqrt_gradient_descent(x)}")
print(f"math.sqrt 计算 sqrt({x}) = {x ** 0.5}") # 验证