自定义策略
自定义召回、过滤、排序定义类似,这里一块介绍。
自定义过滤
增加自定义 Filter 需要以下步骤
实现自定义 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
}
在 server 启动前,注册自定义的 Filter
pairec.AddStartHook(func() error {
filter.RegisterFilter("myfilter", &MyFilter{})
return nil
})
修改配置,增加 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 也使用同样的方式实现自定义
实现自定义 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
}
在 server 启动前,注册自定义的 Sort
pairec.AddStartHook(func() error {
sort.RegisterSort("mysort", &MySort{})
return nil
})
修改配置,增加 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 里的模型配置。