文档

自定义策略

更新时间:

自定义召回、过滤、排序定义类似,这里一块介绍。

自定义过滤

增加自定义 Filter 需要以下步骤

  1. 实现自定义 Filter 类,实现 IFilter 接口

 // IFilter 接口定义
 type IFilter interface {
        // 主要实现 Filter 方法,传入的数据在 filterData.Data 里找到
        Filter(filterData *FilterData) error
 }
 
 type MyFilter struct {
}

func (f *MyFilter) Filter(data *filter.FilterData) error {
    fmt.Println("my filter")
    items := filterData.Data.([]*module.Item)  
    newItems := make([]*module.Item, 0)
    // 处理items, 符合条件的加入到 newItems中
    ...
    filterData.Data = newItems
    return nil
}
  1. 在 server 启动前,注册自定义的 Filter

  pairec.AddStartHook(func() error {
        filter.RegisterFilter("myfilter", &MyFilter{})
        return nil
    })
  1. 修改配置,增加 filter 的访问,这里是配置时场景 + filter 列表的形式,场景可以设置具体的值,也可以设置默认值 default

"FilterNames":  {"default":  ["myfilter", "item_exposure_filter"]}

自定义召回

和过滤类似,需要实现 recall 接口

type Recall interface {
    GetCandidateItems(user *module.User, context *context.RecommendContext) []*module.Item
}

然后使用 RegisterRecall 进行注册。 在场景类别的配置中,可以设置相应的 recall 名称。

precall "gitlab.alibaba-inc.com/pai_biz_arch/pairec/service/recall"
...
precall.RegisterRecall("LiveRoomRecall", recall.NewLiveRoomRecall())

自定义 Sort 支持

和 filter 类似, sort 也使用同样的方式实现自定义

  1. 实现自定义 Sort 类

type ISort interface {   
    // 实现 sort 接口,数据在 sortData.Data 里
    Sort(sortData *SortData) error
 }
 
 type MySort struct {                                                                                                                                                                                                                                                                                                             
}                                                                                                                                                                                                                                                                                                                                

func (s *MySort) Sort(data *sort.SortData) error {    
    fmt.Println("my sort")
    items := sortData.Data.([]*module.Item)
    // sort 具体逻辑处理
    ...
    sortData.Data = items
    return nil                                                                                                                                                                                                                                                                                                                   
}

  1. 在 server 启动前,注册自定义的 Sort

   pairec.AddStartHook(func() error {                                                                         
        sort.RegisterSort("mysort", &MySort{})                                                                                                                      
        return nil                                                                                    
    })
  1. 修改配置,增加 Sort 的访问

"SortNames":   {"default":   ["mysort", "item_score"]}

提权操作自定义

很多情况下模型打分之后,用户需要自定义策略实现对模型得分的提降权操作,用户需要提供自定义的策略逻辑。需要实现 boostFunc 定义。

type boostFunc func(score float64, user *module.User, item *module.Item, context *context.RecommendContext) float64

import "prank gitlab.alibaba-inc.com/pai_biz_arch/pairec/service/rank"

// 用户自定义
func BoostScore(score float64, user *module.User, item *module.Item, context *context.RecommendContext) float64 {
	//编辑提权
	vTagId, err := item.IntProperty("vtag_id")
	if err == nil && vTagId == 20214 {
		return score * 1.3
	}
	return score
}
// main函数里, AddStartHook 进行注册
prank.SetBoostFunc(rank.BoostScore)

特征自定义加载

特征加载过程,增加了自定义特征加载的支持。除了在配置中FeatureConfs 中加载特征,也支持自定义的 function 加载特征。

加载特征function 需要实现

type LoadFeatureFunc func(user *module.User, items []*module.Item, context *context.RecommendContext)

通过

func RegisterLoadFeatureFunc(sceneName string, f LoadFeatureFunc)

进行注册。 这个是分场景注册。

特征工程自定义处理

在特征加载完成之后,很多时候会做特征处理的工作,比如生成新的特征,生成组合特征,特征处理时,可能会有 user 和 item 特征的综合处理等等。在特征加载之后,引擎也提供一些预定义的特征处理算子用于特征工程,在不满足的情况下,可以自定义处理实现。

特征工程的自定义处理也是通过自定义的 function 来实现的。

特征工程 function 定义

type FeatureFunc func(user *module.User, items []*module.Item, context *context.RecommendContext) []*module.Item

通过

func RegisterFeatureFunc(sceneName string, f FeatureFunc)

进行注册。 这个也是分场景的注册。

举例:

定义一个特征处理 function

// 这里还可以决定进一步减少返回的 item 数量
func MyFeatureFunc(user *module.User, items []*module.Item, context *context.RecommendContext) []*module.Item {
	if len(items) < 400 {
		return items
	}
  
  // 特征的处理
  
  return items[:400]
}

在 main.go 里进行注册

// pfeature "gitlab.alibaba-inc.com/pai_biz_arch/pairec/service/feature"
pairec.AddStartHook(func() error {
    // feed 是场景名称
    pfeature.RegisterFeatureFunc("feed", feature.MyFeatureFunc)
    return nil
})

自定义 Rank(算法调用)

目前也支持了自定义 rank 流程。rank 需要实现 IRank 接口才能注册到流程中。包含两部分, 一个是过滤出想自定义调用算法的 item, 然后对过滤出来的 item 调用自定义 rank function。

IRank 接口定义如下:

type IRank interface {
	// Filter the custom rank of item
	Filter(User *module.User, item *module.Item, context *context.RecommendContext) bool

	Rank(User *module.User, items []*module.Item, requestData []map[string]interface{}, context *context.RecommendContext)
}

然后通过

func RegisterRank(sceneName string, ranks ...IRank)

根据每个场景注册不同的自定义的 rank。 注意: 这里 ranks 可以设置多个,可以针对不同的 item, 调用不同的 rank 。

这里举个简单的例子说明:

type MyRank struct {
    index int
}

func NewMyRank() *MyRank {
    return &MyRank{
        index: 0,
    }
}
func (r *MyRank) Filter(User *module.User, item *module.Item, context *context.RecommendContext) bool {
    r.index++

    item.AddProperty("other", "other")
    if r.index%2 == 0 {
        item.AddProperty("index", r.index)
        return true
    }
    return false
}

func (r *MyRank) Rank(User *module.User, items []*module.Item, requestData []map[string]interface{}, context *context.RecommendContext) {
    fmt.Println("rank len", len(items))
    for _, item := range items {
        if f, err := item.FloatProperty("index"); err == nil {
            item.Score = float64(f * 10)
        }
    }

    r.index = 0
}

如果没有匹配到自定义 Rank 的 item 列表,还是会调用 RankConf 里的模型配置。