1. 研究背景

Airbnb的搜索排名任务是从数千个房源列表中为用户提供有序推荐。最初使用手动设计的评分函数,后被梯度提升决策树(GBDT)模型取代,带来显著改进,但随着时间推移收益趋于平稳。为寻求突破,团队开始尝试将神经网络应用于搜索排名系统。

2. 模型演变

2.1简单神经网络(Simple NN)

最初尝试复杂架构失败后,上线了一个简单的单隐藏层神经网络,含32个全连接ReLU激活单元,输入与GBDT相同特征,训练目标为最小化L2回归损失(预订房源效用为1.0,未预订为0.0)。该模型虽预订效果与GBDT持平,但验证了神经网络管道的生产就绪性。

2.2 LambdaRank神经网络(Lambdarank NN)

结合神经网络与LambdaRank思想,将训练目标从回归转为成对偏好形式,通过优化预订房源与未预订房源的得分差异交叉熵损失,并根据交换房源位置对NDCG的影响加权成对损失,直接优化NDCG。

2.3决策树/因子分解机神经网络(Decision Tree/Factorization Machine NN)

在主排名模型为神经网络时,研究了其他模型,如GBDT模型的迭代、预测房源预订概率的因子分解机(FM)模型。受相关神经网络架构启发,新模型结合了这三个模型的优势,将FM模型预测作为特征输入神经网络,GBDT模型中树的叶节点索引作为分类特征。

2.4深度神经网络(Deep NN)

通过将训练数据量扩大10倍并采用2隐藏层的深度神经网络,简化了模型复杂性。典型配置为输入层195个特征(扩展分类特征为嵌入后),第一个隐藏层127个全连接ReLU,第二个隐藏层83个全连接ReLU。

3. 失败模型案例分析

3.1房源ID(Listing ID)

尝试将房源ID作为特征,通过嵌入学习房源向量表示,但由于Airbnb房源受物理世界限制,数据稀疏,导致过拟合,该方法在测试集上未提升NDCG。

3.2多任务学习(Multi-task learning)

为解决房源ID过拟合问题,构建多任务学习模型,同时预测预订和长浏览概率,共享房源ID嵌入,但在线测试时长浏览量大幅增加,预订量不变。原因是长浏览与预订虽相关,但受多种复杂因素影响,如高端高价房源、长描述难解析房源等,使得基于长浏览预测预订具有挑战性。

4. 特征工程

4.1特征归一化

最初直接使用GBDT模型特征训练神经网络时,因特征未正确归一化导致损失饱和。神经网络对特征数值敏感,需将特征值限制在较小范围,多数分布在{-1, 1}区间,中位数映射为0。根据特征分布选择合适变换,如类似正态分布用,类似幂律分布用

4.2特征分布

4.2.1发现错误

处理海量特征样本时,特征分布的平滑性有助于发现错误,如货币相关错误或价格记录错误在分布中表现为异常尖峰。

4.2.2促进泛化

深度神经网络中,层输出分布越来越平滑,下层特征的平滑分布有助于上层对未见值正确插值,从而促进模型泛化。通过将测试集中特征值缩放(如价格翻倍、三倍等)并观察NDCG变化,发现模型性能稳定,表明模型具有良好泛化能力。为确保输入特征平滑分布,对一些特征需特殊处理,如房源地理位置(经纬度),通过计算与地图中心偏移并取对数,使分布平滑,同时将原始经纬度转换为与地图中心偏移的特征,可让模型学习基于距离的全局属性,而非特定地理位置属性。

4.2.3检查特征完整性

部分特征分布不平滑可能暗示模型缺失特征,如房源占用率分布异常,经调查发现受最低入住时长要求影响,添加平均入住时长特征后,占用率分布变平滑。有时特征在一维不平滑但在高维可能变平滑,需思考模型是否能获取这些维度特征,必要时添加。

4.3高基数分类特征

除房源ID外,还尝试了其他高基数分类特征,如通过将查询中的城市和房源对应的S2单元格哈希映射为整数作为分类特征,输入神经网络并通过反向传播学习嵌入,能有效编码基于城市查询的社区位置偏好,且效果优于GBDT模型中复杂的工程化处理方式。

5. 系统工程优化

5.1数据格式与处理

GBDT模型使用CSV格式训练数据,TensorFlow模型沿用此管道时,因解析CSV和通过feed dict复制数据耗时,导致GPU利用率低。将训练数据转换为Protobufs格式并使用Dataset后,训练速度提升17倍,GPU利用率提高到约90%,使大规模训练数据成为可能。

5.2静态特征重构

许多房源属性特征很少变化,读取这些特征造成输入瓶颈。将房源ID作为分类特征,准静态房源特征打包为不可训练嵌入,通过房源ID索引,嵌入存储在GPU内存中,减少了磁盘I/O和训练数据量,提高了训练速度,使模型能考虑用户过去交互房源的更多细节。

5.3 Java神经网络库

2017年初将TensorFlow模型部署到生产时,Java栈中缺乏高效评分解决方案,需在Java和其他语言间来回转换数据,引入高延迟。为满足搜索严格延迟要求,创建了自定义Java神经网络评分库,虽目前效果良好,但未来可能会重新审视以寻找更好替代方案。

6. 超参数调整

6.1丢弃法(Dropout)

最初认为Dropout对神经网络正则化重要,但尝试不同方式均导致离线指标轻微下降。目前认为Dropout类似数据增强技术,在引入随机场景无效时会干扰模型,尝试添加手工制作的噪声形状虽使离线NDCG提升约1%,但在线性能无显著改善。

6.2初始化(Initialization)

最初将所有权重和嵌入初始化为零,发现效果最差。目前网络权重使用Xavier初始化,嵌入使用{-1, 1}范围内的随机均匀初始化。

6.3学习率(Learning rate)

面对众多学习率策略,发现Adam优化器默认设置效果难以超越,目前使用LazyAdamOptimizer变体,在训练大嵌入时速度更快。

6.4批大小(Batch size)

批大小对训练速度有显著影响,但对模型影响难以把握。虽未遵循相关建议,但选择固定批大小200对当前模型有效。

7. 特征重要性评估

7.1评分分解(Score Decomposition)

在GBDT模型中,部分依赖工具可分析特征重要性,但在神经网络中,尝试分解网络最终得分以确定单个输入节点贡献时,因无法清晰分离非线性激活(如ReLU)中特定输入节点的影响而失败。

7.2消融测试(Ablation Test)

通过逐个删除特征、重新训练模型并观察性能差异来评估特征重要性,但由于特征冗余,删除单个特征对性能影响类似模型重训练时的噪声,难以准确判断特征重要性。

7.3排列测试(Permutation Test)

受随机森林排列特征重要性启发,对测试集中特征值随机排列后观察模型性能变化,预期重要特征受扰动后性能下降更大。但因假设特征相互独立(实际不独立,如房间数量与价格、入住人数、设施等相关),导致结果不合理,不过该测试可确定部分不重要特征。

7.4 TopBot分析(TopBot Analysis)

使用自定义的TopBot工具,通过比较排名顶部和底部房源的特征值分布,了解模型如何利用不同特征值范围。如价格在顶部房源分布偏向低值,表明模型对价格敏感,而评论数量分布相似,表明模型未充分利用评论信息,为进一步研究提供方向。

8. 回顾与展望

深度学习应用之旅从乐观开始,期望替换GBDT模型获得巨大收益,但初期离线指标倒退。后来意识到深度学习需系统扩展,涉及重新思考整个系统。虽过程曲折,但深度学习带来了在线性能提升,且改变了团队关注重点,从特征工程转向更高层次问题,如优化目标和用户表示。团队认为深度学习值得推荐,且感觉在该领域才刚起步。

参考

  • https://www.jianshu.com/p/cb8c3b834135