1 rdna 是什么
推荐系统一般除了会返回推荐的结果 id 列表以外,还会返回一些辅助参数,例如算法 id,也可以称为推荐的 rdna,可以用于描述该推荐结果的「来龙去脉」。比如说,推荐流程经过了召回、混排和排序三个阶段,而每个阶段都可能会有 ab 实验,为了标识所属阶段和实验,都会给实验分配一个 id,用于追踪推荐的完整过程。
{
"status": true,
"result": [
{
"rdna": 1150982,
"itemId": "69",
"itemType": "Item",
"debug": {
"rtime": 1544502577084
}
},
{
"rdna": 1150982,
"itemId": "0",
"itemType": "Item",
"debug": {
"rtime": 1544502577084
}
},
{
"rdna": 1150982,
"itemId": "88",
"itemType": "Item",
"debug": {
"rtime": 1544502577084
}
}
]
}
传入相应的三个参数,可以返回一个 Long 类型的 rdna 值,而在分析过程中,也可以通过返回值的 rdna 解析出 recallId,mixId 和 rerankId,以此来区分业务。rdna 的实现其实很简单,主要运用了位运算。也可以用 python 或者其他语言来实现。
# 该样例类的参数为召回 id,混排 id 和 重排 id,并且都是在0到1023间的整数
case class DNA(recallId: Int, mixId: Int, rerankId: Int) {
require(recallId < 1024, s"Invalid recall id: [ $recallId ] > 1024 or < 0.")
require(mixId < 1024, s"Invalid mix id: [ $mixId ] > 1024 or < 0.")
require(rerankId < 1024, s"Invalid rerank id: [ $rerankId ] > 1024 or < 0.")
def dnaToLong: Long = {
recallId.toLong + (mixId << 10).toLong + (rerankId << 20).toLong
}
}
# 该对象通过传入一个 dna 数值,分别取得召回 id,混排 id 和 重排 id
object DNA {
def apply(dnaCode: Long): DNA = {
val rc = dnaCode & 1023
val m = dnaCode >> 10 & 1023
val rr = dnaCode >> 20 & 1023
new DNA(rc.toInt, m.toInt, rr.toInt)
}
}
2 算法ID如何转rdna
考虑一个召回,混排,精排的组合为(6, 0, 7),如何转换成为 rdna。
# 首先计算 recallId
recallId.toLong = 6
# 然后计算 mixId
(mixId << 10).toLong = 0
# 最后计算 rerankId
(rerankId << 20).toLong = 7340032
# 最后的 rdna 是通过将以上三部分的结果相加得到的
rdna = 6 + 0 + 7340032 = 7340038
3 rnda如何转算法id
根据 rdna 获得召回,混排,精排的组合。仍然使用实例1的数据:
# 以下是解析 rdna 为7340038的过程
# 十进制化为二进制
7340038 => 100 0110 0000 0000 0000 0000 1000
recallId = dnaCode & 1023
= 7340038 & 1023
= 100 0110 0000 0000 0000 0000 1000 & 11 1111 1111
= 6
mixId = dnaCode >> 10 & 1023
= 7340038 >> 10 & 1023
= 7168 & 1023
= 0
rerankId = dnaCode >> 20 & 1023
= 7340038 >> 20 & 1023
= 7 & 1023
= 7
解析点击、行为日志的时候,可以通过注册 Hive 的 UDF 函数来解析不同的推荐业务的 rdna,从而实现报表的开发。
4 脚本解析
提供一个样例脚本,用于在 shell 模式下方便解析,其实就是一个关于位运算的 shell 脚本。
#!/bin/bash
function longtordna() {
RECALL=$1
MIX=$2
RERANK=$3
RDNA=$((RECALL + (MIX << 10) + (RERANK << 20))) echo "Input racall: $RECALL, mix: $MIX and rerank $RERANK, convert to rdna: $RDNA." } function rdnatolong() { RDNA=$1 echo "Input rdna: $RDNA to convert to recall: $((RDNA & 1023)), mix: $((RDNA >> 10 & 1023)) and rerank: $((RDNA >> 20 & 1023))." } function usage() { cat << EOF Usage: ./rdna.sh longtordna recallId mixId rerankId ./rdna.sh rdnatolong rdna ./rdna.sh help EOF } case "$1:$2:$3" in longtordna:*) longtordna "${@:2}" ;; rdnatolong:*) rdnatolong $2 ;; h|help) usage ;; *) usage exit 0 esac