|
@@ -0,0 +1,148 @@
|
|
|
|
|
+package com.tzld.piaoquan.recommend.server.service.recall.strategy;
|
|
|
|
|
+
|
|
|
|
|
+import com.tzld.piaoquan.recommend.server.model.Video;
|
|
|
|
|
+import com.tzld.piaoquan.recommend.server.service.filter.FilterParam;
|
|
|
|
|
+import com.tzld.piaoquan.recommend.server.service.filter.FilterResult;
|
|
|
|
|
+import com.tzld.piaoquan.recommend.server.service.filter.FilterService;
|
|
|
|
|
+import com.tzld.piaoquan.recommend.server.service.rank.bo.UserShareReturnProfile;
|
|
|
|
|
+import com.tzld.piaoquan.recommend.server.service.rank.bo.VideoAttrSRBO;
|
|
|
|
|
+import com.tzld.piaoquan.recommend.server.service.recall.FilterParamFactory;
|
|
|
|
|
+import com.tzld.piaoquan.recommend.server.service.recall.RecallParam;
|
|
|
|
|
+import com.tzld.piaoquan.recommend.server.service.recall.RecallStrategy;
|
|
|
|
|
+import com.tzld.piaoquan.recommend.server.util.FeatureUtils;
|
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
+import org.apache.commons.collections4.CollectionUtils;
|
|
|
|
|
+import org.apache.commons.math3.util.Pair;
|
|
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
|
+import org.springframework.beans.factory.annotation.Qualifier;
|
|
|
|
|
+import org.springframework.data.redis.core.RedisTemplate;
|
|
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
|
|
+
|
|
|
|
|
+import java.util.*;
|
|
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * V565 实验专属:UserCate1RecallStrategy 的 topN=100 变体。
|
|
|
|
|
+ * 与原版共用同一 Redis key 前缀,仅拉取更多 cate 达到扩大召回量的效果。
|
|
|
|
|
+ * 使用独立 PUSH_FORM 避免污染其他实验组。
|
|
|
|
|
+ */
|
|
|
|
|
+@Component
|
|
|
|
|
+@Slf4j
|
|
|
|
|
+public class UserCate1RecallStrategyV565 implements RecallStrategy {
|
|
|
|
|
+ private final String CLASS_NAME = this.getClass().getSimpleName();
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private FilterService filterService;
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ @Qualifier("redisTemplate")
|
|
|
|
|
+ public RedisTemplate<String, String> redisTemplate;
|
|
|
|
|
+
|
|
|
|
|
+ public static final int topN = 100;
|
|
|
|
|
+ public static final String PUSH_FORM = "recall_strategy_user_cate1_v565";
|
|
|
|
|
+ public static final String redisKeyPrefix = "merge_cate_recall:cate1";
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public String pushFrom() {
|
|
|
|
|
+ return PUSH_FORM;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public List<Video> recall(RecallParam param) {
|
|
|
|
|
+ List<Video> videosResult = new ArrayList<>();
|
|
|
|
|
+ try {
|
|
|
|
|
+ UserShareReturnProfile userProfile = param.getUserProfile();
|
|
|
|
|
+ if (null != userProfile && null != userProfile.getC1_s()) {
|
|
|
|
|
+ List<String> keys = getRedisKey(userProfile.getC1_s());
|
|
|
|
|
+ if (!keys.isEmpty()) {
|
|
|
|
|
+ List<String> values = redisTemplate.opsForValue().multiGet(keys);
|
|
|
|
|
+ List<Long> ids = recall(param.getVideoId(), values);
|
|
|
|
|
+ Map<Long, Double> scoresMap = FilterParamFactory.positionScores(ids);
|
|
|
|
|
+ FilterParam filterParam = FilterParamFactory.create(param, ids, pushFrom(), scoresMap);
|
|
|
|
|
+ FilterResult filterResult = filterService.filter(filterParam);
|
|
|
|
|
+ if (filterResult != null && CollectionUtils.isNotEmpty(filterResult.getVideoIds())) {
|
|
|
|
|
+ for (Long vid : filterResult.getVideoIds()) {
|
|
|
|
|
+ Video video = new Video();
|
|
|
|
|
+ video.setVideoId(vid);
|
|
|
|
|
+ video.setRovScore(scoresMap.getOrDefault(vid, 0.0));
|
|
|
|
|
+ video.setPushFrom(pushFrom());
|
|
|
|
|
+ videosResult.add(video);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("recall is wrong in {}, error={}", CLASS_NAME, e);
|
|
|
|
|
+ }
|
|
|
|
|
+ return videosResult;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private List<Long> recall(Long headVid, List<String> values) {
|
|
|
|
|
+ List<Long> vidList = new ArrayList<>();
|
|
|
|
|
+ if (null != values && !values.isEmpty()) {
|
|
|
|
|
+ Set<Long> hits = new HashSet<>();
|
|
|
|
|
+ hits.add(headVid);
|
|
|
|
|
+ List<Pair<Long, Double>> list = new ArrayList<>();
|
|
|
|
|
+ for (String value : values) {
|
|
|
|
|
+ if (null != value && !value.isEmpty()) {
|
|
|
|
|
+ String[] cells = value.split("\t");
|
|
|
|
|
+ if (2 == cells.length) {
|
|
|
|
|
+ List<Long> ids = Arrays.stream(cells[0].split(",")).map(Long::valueOf).collect(Collectors.toList());
|
|
|
|
|
+ List<Double> scores = Arrays.stream(cells[1].split(",")).map(Double::valueOf).collect(Collectors.toList());
|
|
|
|
|
+ if (!ids.isEmpty() && ids.size() == scores.size()) {
|
|
|
|
|
+ for (int i = 0; i < ids.size(); ++i) {
|
|
|
|
|
+ long id = ids.get(i);
|
|
|
|
|
+ double score = scores.get(i);
|
|
|
|
|
+ if (hits.contains(id)) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ hits.add(id);
|
|
|
|
|
+ list.add(Pair.create(id, score));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!list.isEmpty()) {
|
|
|
|
|
+ list.sort(Comparator.comparingDouble(o -> -o.getSecond()));
|
|
|
|
|
+ for (Pair<Long, Double> pair : list) {
|
|
|
|
|
+ vidList.add(pair.getFirst());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return vidList;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private List<String> getRedisKey(Map<String, VideoAttrSRBO> map) {
|
|
|
|
|
+ List<String> keys = new ArrayList<>();
|
|
|
|
|
+ if (null != map) {
|
|
|
|
|
+ List<Pair<String, Double>> cateList = getOrderedList(map);
|
|
|
|
|
+ for (Pair<String, Double> pair : cateList) {
|
|
|
|
|
+ keys.add(String.format("%s:%s", redisKeyPrefix, pair.getFirst()));
|
|
|
|
|
+ if (keys.size() >= topN) {
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return keys;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private List<Pair<String, Double>> getOrderedList(Map<String, VideoAttrSRBO> map) {
|
|
|
|
|
+ List<Pair<String, Double>> pairList = new ArrayList<>();
|
|
|
|
|
+ if (null != map) {
|
|
|
|
|
+ for (Map.Entry<String, VideoAttrSRBO> entry : map.entrySet()) {
|
|
|
|
|
+ String name = entry.getKey();
|
|
|
|
|
+ VideoAttrSRBO videoAttrSRBO = entry.getValue();
|
|
|
|
|
+ if (null != videoAttrSRBO && videoAttrSRBO.getRu() > 0) {
|
|
|
|
|
+ long sharePv = videoAttrSRBO.getSp();
|
|
|
|
|
+ long returnUv = videoAttrSRBO.getRu();
|
|
|
|
|
+ double score = FeatureUtils.plusSmooth(returnUv, sharePv, 5);
|
|
|
|
|
+ Pair<String, Double> pair = Pair.create(name, score);
|
|
|
|
|
+ pairList.add(pair);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (pairList.size() > 1) {
|
|
|
|
|
+ pairList.sort(Comparator.comparingDouble(o -> -o.getSecond()));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return pairList;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|