易企秀推荐系统曝光控制

大家应该知道广告有一种计算收益的方式叫CPM,简单讲就是通过不断扩大曝光量来提升广告收益的计算方式;同样这种方式也适用于推荐系统;业务在不断提升,而商品的展示位相对又是比较固定的,为了收益最大化,必须要充分利用商品展示栏位,最大化优质商品的曝光。

数据分析

易企秀PC商城90%以上的流量来自搜索推荐,每天PC端H5商品总曝光次数800w次,去重后的商品有16.8w个,占总商品的84%,咋一看这个数字挺可喜的,可进一步分析发现问题所在;首先将商品曝光人数分别限制在2人或5人以上时,那么有效的商品曝光数是9.4w和6w,分别占比47%和30%,曝光占比一下子下滑了50%;我们继续调大人数到100,此时曝光占比跌至2%;一天中PC端活跃人数6w,很明显绝大部分商品只有极小部分人可以看到,长尾问题可见一斑;有很多优质商品得不到展示的机会,没有曝光就不可能有后续的点击和购买动作,那么定义一个好的曝光控制策略将是多么的重要。

架构

image.png
  • 商品索引库

结合商品属性数据及用户行为特征数据,通过hadoop离线计算平台算出用户商品特征关系存入ES,该数据为商品候选结果集,为推荐系统召回模块提供商品检索、筛选及底层排序支持。


image.png
  • 策略库

通过管理后台,产品可轻松添加推荐策略;策略库存放推荐系统使用的策略信息,如召回策略、排序策略、打散策略、分流策略、分群策略、配比策略等。


image.png
  • 标签库

基于业务数据及用户行为数据,通过制定运营规则及算法模型离线计算得到;标签库主要存放商品、作品及用户的标签,如用户属性标签、用户兴趣标签,作品用途标签、商品风格标签及商品行业标签等。


image.png
  • CTR模型库

定期通过spark任务提取业务日志与用户行为日志构建商品点击预估模型,CTR模型用来对商品进行二次排序,将用户可能点击的商品优先返回。

  • 实时特征库

实时特征库的构建主要为了弥补离线特征在时效性方面的不足;特征库记录用户与商品实时产生的互动数据,如曝光、点击、购买,依据用户未来对商品兴趣程度判断:已点击 > 已曝光 > 已购买 ,参照兴趣程度及数据量分别设置小时、日、周的曝光限制,如用户已购买的商品本周内将不会重复推荐。

用户上下文特征库

重点来了,实时特征库我们选择ES进行存储,理由是便于特征检索及排序;用户动作的上下文环境数据结构如下:

  • 创建索引库自动生成模板
PUT _template/uplog_temp
{  
    "template" : "uplog_*",
    "order":1,
      "mappings" : {
      "dynamic" : "false",
      "dynamic_templates" : [
        {
          "strings" : {
            "match_mapping_type" : "string",
            "mapping" : {
              "doc_values" : false,
              "norms" : false,
              "type" : "keyword"
            }
          }
        }
      ],
      "date_detection" : false,
      "properties" : {
        "pid" : {    ##动作对象
          "type" : "keyword"
        },
        "platform" : {
          "type" : "keyword"
        },
        "position" : {  ## 位置信息
          "type" : "text",
          "index_options" : "docs",
          "analyzer" : "ik_max_word",
          "search_analyzer" : "ik_smart"
        },
        "mark" : {  ## 备注
          "type" : "text",
          "index_options" : "docs",
          "analyzer" : "ik_max_word",
          "search_analyzer" : "ik_smart"
        },
        "product" : {
          "type" : "keyword"
        },
        "time" : {
          "type" : "date",
          "format" : "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
        },
        "feature_type" : {
          "type" : "integer"  ##用户动作  1:点击;2:曝光;3:购买 ;4:升级会员...
        },
        "u_i" : {  ## 不能为空
          "type" : "keyword"
        },
        "s_i" : {
          "type" : "keyword"   ## 标记上下文环境
        }
      }
    },
    "settings" : {
      "index" : {
        "codec" : "best_compression",
        "search" : {
          "slowlog" : {
            "level" : "info",
            "threshold" : {
              "fetch" : {
                "info" : "500ms"
              },
              "query" : {
                "info" : "1s"
              }
            }
          }
        },
        "refresh_interval" : "60s",
        "number_of_shards" : "1",
        "translog" : {
          "flush_threshold_size" : "2024mb",
          "sync_interval" : "1200s",
          "durability" : "async"
        },
        "merge" : {
          "scheduler" : {
            "max_thread_count" : "2"
          }
        },
        "store" : {
          "type" : "niofs"
        },
        "number_of_replicas" : "0"
      }
    } 
} 

因为曝光日志较多(千万日志量),所以分库存储只保留1天,其它上下文日志保留7天(每天一个索引库),超过对应天数的日志会执行delete操作,执行删除索引库的动作比delete_by_query性能要高很多

  • 通过spark脚本同步离线日志到ES进行测试(上线后切streaming日志)
#!/bin/bash
  export SPARK_HOME=/data/work/spark-2.4.4
  export PATH=$SPARK_HOME/bin:$PATH

source /etc/profile

exec  spark-shell    --master yarn --name log2es  --num-executors 5 --packages org.elasticsearch:elasticsearch-spark-20_2.11:7.3.0  <<!EOF

import org.elasticsearch.spark.sql._
val df=spark.read.parquet("/data/merge/tracker/202003/31")
df.registerTempTable("log")

#曝光日志
sql("select u_i,s_i,e_d.product_id pid, 'h5' product,os_2 platform,tk_id position,'2' as feature_type, from_unixtime(cast(s_t/1000 as long),'yyyy-MM-dd HH:mm:ss') time  from log where e_t='element_view' and length(u_i)>4 and length(e_d.product_id)>4 and e_d.type=2").saveToEs("uplog_view_20200331",Map("es.nodes"->""))
#其它行为上下文日志
sql("select u_i,s_i,e_d.product_id pid, 'h5' product,os_2 platform,tk_id position,'1' as feature_type, from_unixtime(cast(s_t/1000 as long),'yyyy-MM-dd HH:mm:ss') time  from log where e_t='element_click' and length(u_i)>4 and length(e_d.product_id)>4 and e_d.type=2").saveToEs("uplog_ctx_20200331",Map("es.nodes"->""))


spark.stop
!EOF
  • 数据预览
      "_source" : {
          "u_i" : "ff80808159905690015991716a1d007e",
          "s_i" : "f69604159905001585499443PGXGRB",
          "pid" : "1237715",
          "product" : "h5",
          "platform" : "PC",
          "position" : "search-69604-96046-1585499443PGXGRB-687-11",
          "feature_type" : "1",
          "time" : "2020-03-30 00:52:11"
        }
  • 例如获取某用户当天推荐曝光次数高的200个商品进行屏蔽
GET uplog_view_20200330/_search
{
  "size": 0,
   
  "query": {
    "bool": {
      "filter": {
        "term": {
            "u_i": {
              "value": "ff80808159905690015991716a1d007e"
            }
          }
      },
      "must": [
        {
          "match": {
            "position": "re"
          }
        }
      ]
    }
  },
  "aggregations": {
    "field1": {
      "terms": {
        "field": "pid",
        "size": 200
      }
    }
  }
}

通过实时的用户上下文日志也可快速区分用户当前兴趣变化及身份变化情况,配合运营规则及时合理调配资源以提升用户活跃及留存

商品曝光控制

商品曝光控制主要分为两种:节假日&兴趣预判

  • 节假日
    已过节假日及热点商品,理论上热度已经减少,需要在推荐系统召回模块进行降权甚至过滤处理;
    通过离线任务每天对昨日新增商品进行标签化处理,标记出有节假日性质的商品,整个过程程序通过算法规则自动完成的无需人工干预。
    推荐系统会在数据召回时先获取全年中不在最近一个月的节日类清单,基于此清单在获取ES候选结果集时对商品进行排除或降权处理。


    image.png
  • 兴趣预判
    基于实时特征库的行为日志,结合一定的分析策略来预判用户未来一段时间内对该商品的兴趣程度;首先设计类电商平台有区别于传统电商平台,例如某用户今天购买了一个模板商品,那么很大程度上近期该用户是不会再重复购买类似商品的,所以推荐系统需要在各推荐位自动屏蔽该商品继续曝光给该用户,通过选择其它商品曝光不仅能充分利用推荐位展示更多信息,同时更能拓展用户兴趣需求;
    在商品召回模块,程序会根据用户id从实时特征库抽取出当前用户最近触达到的Top商品,基于此清单在获取ES候选结果集时对商品进行排除。
    注:
    根据不同用户的活跃程度,每个用户日志量有大有小,在获取用户日志信息时需要进行Top限制,防止因数据量太大影响服务性能(建表时已根据type和time进行排序)
    该屏蔽策略并不会永久屏蔽商品,根据用户兴趣预判,最长屏蔽1周,最短屏蔽1天

兜底策略

当召回模块没有提取到足够的数据时需要触发兜底策略:

  • 热点
    基于业务日志及用户行为日志,汇总求出商品近7日销量作为热点候选商品,每天通过hadoop离线数据平台进行计算,将热点score已标签方式更新到商品标签库,更新之前需要先将所有热点商品score致为0。
  • 运营干预
    运营可根据时下热点及促销模式指定一部分商品,并以标签的形式更新到商品标签库。

其它阅读平台,欢迎收藏

掘金
简书
Segment

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容