ソースを参照

增加 均值score排序因子

yangxiaohui 11 ヶ月 前
コミット
66986f8356

+ 1 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/common/enums/RankStrategyEnum.java

@@ -8,6 +8,7 @@ import java.util.Objects;
 public enum RankStrategyEnum {
     ArticleRankV2("ArticleRankV2", "ArticleRankV2", "rankV2Strategy"),
     ArticleRankV3("ArticleRankV3", "ArticleRankV3", "rankV3Strategy"),
+    ArticleRankTest("ArticleRankTest", "ArticleRankTest", "rankTestStrategy"),
 
     default_strategy("ArticleRankV1", "默认策略", "defaultRankStrategy"),
     ;

+ 1 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/rank/RankItem.java

@@ -20,4 +20,5 @@ public class RankItem {
                 : scoreMap.get(strategy);
     }
 
+
 }

+ 212 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/rank/strategy/RankTestStrategy.java

@@ -0,0 +1,212 @@
+package com.tzld.longarticle.recommend.server.service.rank.strategy;
+
+
+import com.tzld.longarticle.recommend.server.model.Content;
+import com.tzld.longarticle.recommend.server.model.ContentHisPublishArticle;
+import com.tzld.longarticle.recommend.server.service.AccountContentPoolConfigService;
+import com.tzld.longarticle.recommend.server.service.AccountIndexAvgViewCountService;
+import com.tzld.longarticle.recommend.server.service.rank.RankItem;
+import com.tzld.longarticle.recommend.server.service.rank.RankParam;
+import com.tzld.longarticle.recommend.server.service.rank.RankResult;
+import com.tzld.longarticle.recommend.server.service.rank.RankStrategy;
+import com.tzld.longarticle.recommend.server.service.score.ScoreParam;
+import com.tzld.longarticle.recommend.server.service.score.ScoreResult;
+import com.tzld.longarticle.recommend.server.service.score.ScoreService;
+import com.tzld.longarticle.recommend.server.service.score.strategy.*;
+import com.tzld.longarticle.recommend.server.util.CommonCollectionUtils;
+import com.tzld.longarticle.recommend.server.util.JSONUtils;
+import com.tzld.longarticle.recommend.server.util.TitleSimilarCheckUtil;
+import com.tzld.longarticle.recommend.server.util.MathUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+
+/**
+ * @author yangxiaohui
+ */
+@Service
+@Slf4j
+public class RankTestStrategy implements RankStrategy {
+
+    @Autowired
+    private ScoreService scoreService;
+    @Autowired
+    AccountIndexAvgViewCountService accountIndexAvgViewCountService;
+    @Autowired
+    private AccountContentPoolConfigService accountContentPoolConfigService;
+
+    public RankResult rank(RankParam param) {
+
+        log.info("RankParam {}", JSONUtils.toJson(param));
+        ScoreResult scoreResult = scoreService.score(convertToScoreParam(param));
+        log.info("ScoreResult {}", JSONUtils.toJson(scoreResult));
+
+        Map<String, Map<String, Double>> scoreMap = scoreResult.getScoreMap();
+
+        double toutiaoAvgViewCount = accountIndexAvgViewCountService.getAvgReadCount(param.getGhId(), 1);
+        List<RankItem> items = CommonCollectionUtils.toList(param.getContents(), c -> {
+            RankItem item = new RankItem();
+            item.setContent(c);
+            item.setScoreMap(scoreMap.get(c.getId()));
+            double score = 2 * item.getScore(SimilarityStrategy.class.getSimpleName())
+                    + item.getScore(ViewMultiplierStrategy.class.getSimpleName());
+            double showViewCountSum = 0D;
+            double avgViewCountSum = 0D;
+            for (ContentHisPublishArticle hisItem : item.getContent().getHisPublishArticleList()) {
+                if (hisItem.isInnerAccount() && hisItem.getShowViewCount() > 0 && hisItem.getAvgViewCount() > 0) {
+                    showViewCountSum += hisItem.getShowViewCount();
+                    avgViewCountSum += hisItem.getAvgViewCount();
+                }
+            }
+            Map<String, Double> thisScoreMap = item.getScoreMap();
+            double viewCountRate = 0D; // 设置默认值
+            if (avgViewCountSum > 0) {
+                viewCountRate = showViewCountSum / avgViewCountSum;
+            }
+            thisScoreMap.put("showViewCountSum", showViewCountSum);
+            thisScoreMap.put("avgViewCountSum", avgViewCountSum);
+            thisScoreMap.put("viewCountRate", viewCountRate);
+            double viewCountRateW = MathUtils.sigmoid(avgViewCountSum, 0.0005, toutiaoAvgViewCount);
+            double viewCountRateScore = 0;
+            if (viewCountRate > 0) {
+                viewCountRateScore = (Math.min(viewCountRate, 5) - 1D) * viewCountRateW;
+            }
+            thisScoreMap.put("viewCountRateW", viewCountRateW);
+            thisScoreMap.put("viewCountRateScore", viewCountRateScore);
+            item.setScoreMap(thisScoreMap);
+            item.setScore(viewCountRateScore );
+
+            return item;
+        });
+
+
+
+
+        // 1 排序
+        Collections.sort(items, (o1, o2) -> -Double.compare(o1.getScore(), o2.getScore()));
+
+        // 3 ITEM按照内容池分组
+        Map<String, List<RankItem>> itemMap = new HashMap<>();
+        for (RankItem item : items) {
+            List<RankItem> data = itemMap.computeIfAbsent(item.getContent().getContentPoolType(), k -> new ArrayList<>());
+            data.add(item);
+        }
+
+        log.info("SortResult {}", JSONUtils.toJson(items));
+        // 2 相似去重
+        List<Content> contents = CommonCollectionUtils.toList(items, RankItem::getContent);
+        contents = deduplication(contents);
+        log.info("Deduplication {}", JSONUtils.toJson(contents));
+
+        // 3 文章按照内容池分组
+        Map<String, List<Content>> contentMap = new HashMap<>();
+        for (Content c : contents) {
+            List<Content> data = contentMap.computeIfAbsent(c.getContentPoolType(), k -> new ArrayList<>());
+            data.add(c);
+        }
+        log.info("ContentMap {}", JSONUtils.toJson(contentMap));
+        // 4 选文章
+        List<Content> result = new ArrayList<>();
+        String[] contentPools = accountContentPoolConfigService.getContentPools(param.getAccountName());
+
+
+        // 头、次
+        if (StringUtils.equals(contentPools[0], contentPools[1])) {
+            List<Content> pool = contentMap.get(contentPools[0]);
+            Integer level = accountContentPoolConfigService.getLevelByContentPool(contentPools[0]);
+            if (level == 1) {
+                if (CollectionUtils.isNotEmpty(pool)) {
+                    result.add(pool.get(0));
+                    if (pool.size() > 2) {
+                        result.add(pool.get(2));
+                    }
+                } else {
+                    // level2 兜底
+                    pool = contentMap.get(accountContentPoolConfigService.getContentPoolByLevel(2));
+                    if (CollectionUtils.isNotEmpty(pool)) {
+                        result.add(pool.get(0));
+                        if (pool.size() > 1) {
+                            result.add(pool.get(1));
+                        }
+                    }
+                }
+            } else if (level == 2) {
+                if (CollectionUtils.isNotEmpty(pool)) {
+                    pool = pool.subList(0, Math.max(10, pool.size()));
+                    Collections.shuffle(pool);
+                    result.add(pool.get(0));
+                    if (pool.size() > 1) {
+                        result.add(pool.get(1));
+                    }
+                }
+            }
+        } else {
+            // 配置错误 兜底
+            List<Content> pool1 = contentMap.get(contentPools[0]);
+            List<Content> pool2 = contentMap.get(contentPools[1]);
+            if (CollectionUtils.isNotEmpty(pool1)) {
+                result.add(pool1.get(0));
+                if (CollectionUtils.isNotEmpty(pool2)) {
+                    result.add(pool2.get(0));
+                }
+            } else if (CollectionUtils.isNotEmpty(pool2)) {
+                result.add(pool2.get(0));
+                if (pool2.size() > 1) {
+                    result.add(pool2.get(1));
+                }
+            }
+        }
+
+        // 3-8
+        List<Content> pool = contentMap.get(contentPools[2]);
+        if (CollectionUtils.isNotEmpty(pool)) {
+            Collections.shuffle(pool);
+            result.addAll(pool.subList(0, Math.min(pool.size(), param.getSize() - result.size())));
+        }
+
+        if (result.size() < param.getSize()) {
+            // 兜底
+            pool = param.getBackup();
+            pool = deduplication(pool);
+            log.info("Backup Deduplication {}", JSONUtils.toJson(pool));
+            Collections.shuffle(pool);
+            result.addAll(pool.subList(0, Math.min(pool.size(), param.getSize() - result.size())));
+        }
+
+        return new RankResult(result);
+    }
+
+    private ScoreParam convertToScoreParam(RankParam param) {
+        ScoreParam scoreParam = new ScoreParam();
+        scoreParam.setGhId(param.getGhId());
+        scoreParam.setAccountName(param.getAccountName());
+        scoreParam.setContents(param.getContents());
+        scoreParam.setStrategy(param.getStrategy());
+        return scoreParam;
+    }
+
+    private List<Content> deduplication(List<Content> contents) {
+        List<String> titles = new ArrayList<>();
+        List<Content> result = new ArrayList<>();
+        // 遍历所有列表
+        for (Content c : contents) {
+            if (similarity(c.getTitle(), titles)) {
+                continue;
+            } else {
+                result.add(c);
+                titles.add(c.getTitle());
+            }
+        }
+
+        return result;
+    }
+
+    private boolean similarity(String title, List<String> titles) {
+        return TitleSimilarCheckUtil.isDuplicateContent(title, titles);
+    }
+
+}

+ 25 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/util/MathUtils.java

@@ -0,0 +1,25 @@
+package com.tzld.longarticle.recommend.server.util;
+
+public class MathUtils {
+    // Sigmoid函数实现
+    public static double sigmoid(double x, double k, double m) {
+        return 1.0 / (1.0 + Math.exp(-k * (x - m)));
+    }
+
+    public static void main(String[] args) {
+        double[] xValues = {0, 25000, 50000, 75000, 100000};  // 示例阅读量
+
+        // 测试不同的 k 值
+        double[] kValues = {0.0005, 0.0001, 0.00005};  // 不同的 k 值
+        double m = 100;  // 中值
+
+        for (double k : kValues) {
+            System.out.println("k = " + k);
+            for (double x : xValues) {
+                double sigmoidValue = sigmoid(x, k, m);
+                System.out.printf("x = %.2f, Sigmoid = %.4f%n", x, sigmoidValue);
+            }
+            System.out.println();
+        }
+    }
+}