|
@@ -1,84 +1,200 @@
|
|
|
package com.tzld.piaoquan.recommend.server.implement;
|
|
|
|
|
|
|
|
|
+import com.tzld.piaoquan.recommend.feature.domain.video.base.RequestContext;
|
|
|
+import com.tzld.piaoquan.recommend.feature.domain.video.base.UserFeature;
|
|
|
+import com.tzld.piaoquan.recommend.server.common.ThreadPoolFactory;
|
|
|
import com.tzld.piaoquan.recommend.server.common.base.RankItem;
|
|
|
-import com.tzld.piaoquan.recommend.server.framework.candidiate.Candidate;
|
|
|
import com.tzld.piaoquan.recommend.server.framework.common.User;
|
|
|
-import com.tzld.piaoquan.recommend.server.framework.merger.MergeUtils;
|
|
|
-import com.tzld.piaoquan.recommend.server.framework.merger.SimilarityUtils;
|
|
|
-import com.tzld.piaoquan.recommend.server.framework.merger.StrategyQueue;
|
|
|
-import com.tzld.piaoquan.recommend.server.framework.recaller.BaseRecaller;
|
|
|
-import com.tzld.piaoquan.recommend.server.framework.recaller.provider.RedisBackedQueue;
|
|
|
-import com.tzld.piaoquan.recommend.server.framework.userattention.UserAttentionExtractorPipeline;
|
|
|
-import com.tzld.piaoquan.recommend.server.framework.userattention.UserAttentionExtractorUtils;
|
|
|
-import com.tzld.piaoquan.recommend.server.framework.utils.RedisSmartClient;
|
|
|
import com.tzld.piaoquan.recommend.server.gen.recommend.RecommendRequest;
|
|
|
+import com.tzld.piaoquan.recommend.server.model.MachineInfo;
|
|
|
+import com.tzld.piaoquan.recommend.server.model.RecommendParam;
|
|
|
+import com.tzld.piaoquan.recommend.server.model.Video;
|
|
|
+import com.tzld.piaoquan.recommend.server.service.RecommendService;
|
|
|
+import com.tzld.piaoquan.recommend.server.service.flowpool.FlowPoolConstants;
|
|
|
+import com.tzld.piaoquan.recommend.server.service.rank.RankParam;
|
|
|
+import com.tzld.piaoquan.recommend.server.service.recall.RecallParam;
|
|
|
+import com.tzld.piaoquan.recommend.server.service.recall.RecallResult;
|
|
|
+import com.tzld.piaoquan.recommend.server.service.recall.RecallStrategy;
|
|
|
+import com.tzld.piaoquan.recommend.server.service.recall.strategy.FlowPoolWithLevelRecallStrategy;
|
|
|
+import com.tzld.piaoquan.recommend.server.service.recall.strategy.FlowPoolWithLevelScoreRecallStrategy;
|
|
|
+import com.tzld.piaoquan.recommend.server.service.recall.strategy.FlowPoolWithScoreRecallStrategy;
|
|
|
+import com.tzld.piaoquan.recommend.server.service.score.ScoreParam;
|
|
|
+import com.tzld.piaoquan.recommend.server.service.score.ScorerUtils;
|
|
|
+import com.tzld.piaoquan.recommend.server.util.CommonCollectionUtils;
|
|
|
+import org.apache.commons.collections4.CollectionUtils;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+import org.springframework.beans.BeansException;
|
|
|
+import org.springframework.context.ApplicationContext;
|
|
|
+import org.springframework.context.ApplicationContextAware;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
+import javax.annotation.PostConstruct;
|
|
|
import javax.annotation.Resource;
|
|
|
-import java.util.ArrayList;
|
|
|
-import java.util.HashMap;
|
|
|
-import java.util.List;
|
|
|
-import java.util.Map;
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
+import java.util.*;
|
|
|
+import java.util.concurrent.CountDownLatch;
|
|
|
+import java.util.concurrent.ExecutorService;
|
|
|
+import java.util.concurrent.Future;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
@Service
|
|
|
-public class FlowPoolRecommendPipeline {
|
|
|
+public class FlowPoolRecommendPipeline implements ApplicationContextAware {
|
|
|
|
|
|
- public static final String MERGE_CONF = "flow_merge_config.conf";
|
|
|
+ private final Logger log = LoggerFactory.getLogger(FlowPoolRecommendPipeline.class);
|
|
|
|
|
|
- public static final String PREFIX = "";
|
|
|
+ private final static Map<String, RecallStrategy> strategyMap = new HashMap<>();
|
|
|
|
|
|
@Resource
|
|
|
- private RedisSmartClient client;
|
|
|
+ private RecommendService recommendService;
|
|
|
|
|
|
- private List<RankItem> feedByRec(final RecommendRequest requestData,
|
|
|
- final int requestIndex,
|
|
|
- final User userInfo) {
|
|
|
- int recallNum = 200;
|
|
|
+ private ApplicationContext applicationContext;
|
|
|
|
|
|
- // Step 1: Attention extraction
|
|
|
- long timestamp = System.currentTimeMillis();
|
|
|
- UserAttentionExtractorPipeline attentionExtractorPipeline = UserAttentionExtractorUtils.getAtttentionPipeline(UserAttentionExtractorUtils.BASE_CONF);
|
|
|
- attentionExtractorPipeline.extractAttention(requestData, userInfo);
|
|
|
-
|
|
|
- // Step 2: create top queue
|
|
|
- StrategyQueue topQueue = MergeUtils.createTopQueue(MERGE_CONF, "top-queue");
|
|
|
+ private ExecutorService pool = ThreadPoolFactory.recallPool();
|
|
|
|
|
|
+ @PostConstruct
|
|
|
+ public void init() {
|
|
|
+ Map<String, RecallStrategy> type = applicationContext.getBeansOfType(RecallStrategy.class);
|
|
|
+ for (Map.Entry<String, RecallStrategy> entry : type.entrySet()) {
|
|
|
+ RecallStrategy value = entry.getValue();
|
|
|
+ strategyMap.put(value.getClass().getSimpleName(), value);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- // Step 3: Candidate
|
|
|
- Map<String, Candidate> candidates = new HashMap<String, Candidate>();
|
|
|
- topQueue.candidate(candidates, recallNum, userInfo, requestData, 0, 0);
|
|
|
|
|
|
+ private List<Video> feedByRec(final RecommendRequest requestData,
|
|
|
+ final int recommendType) {
|
|
|
+ List<RecallStrategy> strategies = new ArrayList<>();
|
|
|
|
|
|
- // Step 4: Recalling & Basic Scoring
|
|
|
- RedisBackedQueue queueProvider = new RedisBackedQueue(client, 1000L);
|
|
|
+ RecommendParam param = recommendService.genRecommendParam(requestData, recommendType);
|
|
|
+ RecallParam recallParam = recommendService.convertToRecallParam(param);
|
|
|
+ RecallResult recallResult = getRecallResult(strategies, recallParam);
|
|
|
|
|
|
+ RankParam rankParam = recommendService.convertToRankParam(param, recallResult);
|
|
|
+ return mergeAndRankFlowPoolRecall(rankParam);
|
|
|
+ }
|
|
|
|
|
|
- BaseRecaller recaller = new BaseRecaller(queueProvider);
|
|
|
- List<RankItem> items = recaller.recalling(requestData, userInfo, requestIndex, new ArrayList<>(candidates.values()));
|
|
|
+ public List<Video> mergeAndRankFlowPoolRecall(RankParam param) {
|
|
|
+ List<Video> quickFlowPoolVideos = sortFlowPoolByThompson(param, FlowPoolConstants.QUICK_PUSH_FORM);
|
|
|
+ if (CollectionUtils.isNotEmpty(quickFlowPoolVideos)) {
|
|
|
+ return quickFlowPoolVideos;
|
|
|
+ } else {
|
|
|
+ return sortFlowPoolByThompson(param, FlowPoolConstants.PUSH_FORM);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
+ public List<Video> sortFlowPoolByThompson(RankParam param, String pushFrom) {
|
|
|
+
|
|
|
+ //初始化 userid
|
|
|
+ UserFeature userFeature = new UserFeature();
|
|
|
+ userFeature.setMid(param.getMid());
|
|
|
+
|
|
|
+ // 初始化RankItem
|
|
|
+ Optional<RecallResult.RecallData> data = param.getRecallResult().getData().stream()
|
|
|
+ .filter(d -> d.getPushFrom().equals(pushFrom))
|
|
|
+ .findFirst();
|
|
|
+ List<Video> videoList = data.get().getVideos();
|
|
|
+ if (videoList == null) {
|
|
|
+ return Collections.emptyList();
|
|
|
+ }
|
|
|
+ List<RankItem> rankItems = new ArrayList<>();
|
|
|
+ for (int i = 0; i < videoList.size(); i++) {
|
|
|
+ RankItem rankItem = new RankItem(videoList.get(i));
|
|
|
+ rankItems.add(rankItem);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 初始化上下文参数
|
|
|
+ ScoreParam scoreParam = convert(param);
|
|
|
+ List<RankItem> rovRecallScore = ScorerUtils.getScorerPipeline(ScorerUtils.FLOWPOOL_CONF)
|
|
|
+ .scoring(scoreParam, userFeature, rankItems);
|
|
|
+
|
|
|
+ if (rovRecallScore == null) {
|
|
|
+ return Collections.emptyList();
|
|
|
+ }
|
|
|
+
|
|
|
+ return CommonCollectionUtils.toList(rovRecallScore, i -> {
|
|
|
+ // hard code 将排序分数 赋值给video的sortScore
|
|
|
+ Video v = i.getVideo();
|
|
|
+ v.setSortScore(i.getScore());
|
|
|
+ return v;
|
|
|
+ });
|
|
|
+ }
|
|
|
|
|
|
- // Step 4: Advance Scoring
|
|
|
- timestamp = System.currentTimeMillis();
|
|
|
-// ScorerPipeline scorerPipeline = getScorerPipeline(requestData);
|
|
|
-// items = scorerPipeline.scoring(requestData, userInfo, requestIndex, items);
|
|
|
+ protected ScoreParam convert(RankParam param) {
|
|
|
+ ScoreParam scoreParam = new ScoreParam();
|
|
|
|
|
|
+ scoreParam.setMid(param.getMid());
|
|
|
|
|
|
- // Step 5: Merger
|
|
|
- MergeUtils.distributeItemsToMultiQueues(topQueue, items);
|
|
|
- topQueue.merge(recallNum * 3, userInfo, requestData, requestIndex, 0);
|
|
|
+ // TODO hardcode 为了兼容写入逻辑
|
|
|
+ RequestContext context = new RequestContext();
|
|
|
+ context.setApptype(param.getAppType() + "");
|
|
|
|
|
|
- // step 6:多样性融合
|
|
|
- List<RankItem> mergeItems = topQueue.getItems();
|
|
|
- MergeUtils.diversityRerank(mergeItems, SimilarityUtils.getIsSameUserTagOrCategoryFunc(), recallNum, 6, 2);
|
|
|
+ // TODO 地域转换
|
|
|
+ context.setRegion(param.getProvince());
|
|
|
+ context.setCity(param.getCity());
|
|
|
|
|
|
+ Calendar calendar = Calendar.getInstance();
|
|
|
+ context.setWeek((calendar.get(Calendar.DAY_OF_WEEK) + 6) % 7 + "");
|
|
|
+ context.setDay(new SimpleDateFormat("yyyyMMdd").format(calendar.getTime()));
|
|
|
+ context.setHour(new SimpleDateFormat("HH").format(calendar.getTime()));
|
|
|
|
|
|
- // Step 6: Global Rank & subList
|
|
|
- // TODO: Global Rank
|
|
|
+ MachineInfo machineInfo = param.getMachineInfo();
|
|
|
+ if (machineInfo != null) {
|
|
|
+ context.setMachineinfo_brand(machineInfo.getBrand());
|
|
|
+ context.setMachineinfo_model(machineInfo.getModel());
|
|
|
+ context.setMachineinfo_platform(machineInfo.getPlatform());
|
|
|
+ context.setMachineinfo_sdkversion(machineInfo.getSdkVersion());
|
|
|
+ context.setMachineinfo_system(machineInfo.getSystem());
|
|
|
+ context.setMachineinfo_wechatversion(machineInfo.getWechatVersion());
|
|
|
+ }
|
|
|
|
|
|
+ scoreParam.setRequestContext(context);
|
|
|
+ return scoreParam;
|
|
|
+ }
|
|
|
|
|
|
- return items;
|
|
|
+ private RecallResult getRecallResult(List<RecallStrategy> strategies, RecallParam param) {
|
|
|
+ if (param.getFlowPoolAbtestGroup().equals(FlowPoolConstants.EXPERIMENTAL_FLOW_SET_LEVEL)) {
|
|
|
+ strategies.add(strategyMap.get(FlowPoolWithLevelRecallStrategy.class.getSimpleName()));
|
|
|
+ } else if (param.getFlowPoolAbtestGroup().equals(FlowPoolConstants.EXPERIMENTAL_FLOW_SET_LEVEL_SCORE)) {
|
|
|
+ strategies.add(strategyMap.get(FlowPoolWithLevelScoreRecallStrategy.class.getSimpleName()));
|
|
|
+ } else {
|
|
|
+ strategies.add(strategyMap.get(FlowPoolWithScoreRecallStrategy.class.getSimpleName()));
|
|
|
+ }
|
|
|
+
|
|
|
+ CountDownLatch cdl = new CountDownLatch(strategies.size());
|
|
|
+ List<Future<RecallResult.RecallData>> recallResultFutures = new ArrayList<>();
|
|
|
+ for (final RecallStrategy strategy : strategies) {
|
|
|
+ Future<RecallResult.RecallData> future = pool.submit(() -> {
|
|
|
+ List<Video> result = strategy.recall(param);
|
|
|
+ cdl.countDown();
|
|
|
+ return new RecallResult.RecallData(strategy.pushFrom(), result);
|
|
|
+ });
|
|
|
+ recallResultFutures.add(future);
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ cdl.await(3000, TimeUnit.MILLISECONDS);
|
|
|
+ } catch (InterruptedException e) {
|
|
|
+ log.error("recall error", e);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ List<RecallResult.RecallData> results = new ArrayList<>();
|
|
|
+ for (Future<RecallResult.RecallData> f : recallResultFutures) {
|
|
|
+ try {
|
|
|
+ RecallResult.RecallData data = f.get();
|
|
|
+ results.add(data);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("future get error ", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return new RecallResult(results);
|
|
|
}
|
|
|
|
|
|
|
|
|
+ @Override
|
|
|
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
|
|
+ this.applicationContext = applicationContext;
|
|
|
+ }
|
|
|
}
|