ソースを参照

copc新增v3实验

fanjinyang 1 週間 前
コミット
ef7e9550bc

+ 143 - 0
ad-engine-commons/src/main/java/com/tzld/piaoquan/ad/engine/commons/helper/CopcV3DataHelper.java

@@ -0,0 +1,143 @@
+package com.tzld.piaoquan.ad.engine.commons.helper;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.tzld.piaoquan.ad.engine.commons.redis.AlgorithmRedisHelper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * COPC v3 系数数据缓存。
+ */
+@Slf4j
+@Component
+public class CopcV3DataHelper {
+
+    private static final String REDIS_KEY = "ad:engine:strategy:clib:copc_v3";
+    private static final long REFRESH_INTERVAL_MILLIS = 5 * 60 * 1000L;
+    private static final String EDGE_KEY_PREFIX = "edge:";
+    private static final String LANDING_KEY_PREFIX = "landing";
+    private static final int BUCKET_COUNT = 6;
+
+    @Autowired
+    protected AlgorithmRedisHelper algRedisHelper;
+
+    /**
+     * Redis value 中同时包含系数和版本等元数据,因此统一保留为字符串。
+     */
+    private static volatile Map<String, String> copcMap = Collections.emptyMap();
+
+    @PostConstruct
+    public void init() {
+        refreshCopcMap();
+    }
+
+    @Scheduled(fixedRate = REFRESH_INTERVAL_MILLIS)
+    public void scheduledUpdate() {
+        refreshCopcMap();
+    }
+
+    public static String getValue(String key) {
+        return key == null ? null : copcMap.get(key);
+    }
+
+    public static Map<String, String> getCopcMap() {
+        return copcMap;
+    }
+
+    /**
+     * 按分数所在分桶获取 landing 维度的 COPC 加权系数。
+     *
+     * <p>分桶规则为:{@code score <= edge:1} 属于第 1 桶,依次类推;
+     * 大于 {@code edge:5} 的分数属于第 6 桶。</p>
+     */
+    public static Double getLandingCoefficient(Double score, Integer landingPageType, String targetingConversion) {
+        if (!isFinite(score) || landingPageType == null || targetingConversion == null
+                || targetingConversion.trim().isEmpty()) {
+            return null;
+        }
+
+        int bucketNo = getBucketNo(score);
+        if (bucketNo == 0) {
+            return null;
+        }
+        String key = String.format("%s:%s:%s:%s", LANDING_KEY_PREFIX, targetingConversion,
+                landingPageType, bucketNo);
+        return getFiniteDoubleValue(key);
+    }
+
+    private static int getBucketNo(double score) {
+        for (int bucketNo = 1; bucketNo < BUCKET_COUNT; bucketNo++) {
+            Double edge = getFiniteDoubleValue(EDGE_KEY_PREFIX + bucketNo);
+            if (edge == null) {
+                return 0;
+            }
+            if (score <= edge) {
+                return bucketNo;
+            }
+        }
+        return BUCKET_COUNT;
+    }
+
+    private static Double getFiniteDoubleValue(String key) {
+        String value = getValue(key);
+        if (value == null || value.trim().isEmpty()) {
+            return null;
+        }
+        try {
+            Double result = Double.valueOf(value);
+            return isFinite(result) ? result : null;
+        } catch (NumberFormatException e) {
+            return null;
+        }
+    }
+
+    private static boolean isFinite(Double value) {
+        return value != null && !value.isNaN() && !value.isInfinite();
+    }
+
+    private void refreshCopcMap() {
+        Map<String, String> map = loadCopcMap();
+        if (map == null) {
+            return;
+        }
+        copcMap = Collections.unmodifiableMap(map);
+        log.info("update COPC v3 data success size={}", copcMap.size());
+    }
+
+    /**
+     * @return 解析失败时返回 {@code null},以保留上一次成功加载的数据。
+     */
+    private Map<String, String> loadCopcMap() {
+        try {
+            String value = algRedisHelper.get(REDIS_KEY);
+            if (value == null || value.trim().isEmpty()) {
+                return Collections.emptyMap();
+            }
+
+            JSONObject jsonObject = JSON.parseObject(value);
+            if (jsonObject == null) {
+                log.error("COPC v3 redis value is not a JSON object");
+                return null;
+            }
+
+            Map<String, String> map = new HashMap<>(jsonObject.size());
+            for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
+                if (entry.getValue() != null) {
+                    map.put(entry.getKey(), String.valueOf(entry.getValue()));
+                }
+            }
+            return map;
+        } catch (Exception e) {
+            log.error("update COPC v3 data error", e);
+            return null;
+        }
+    }
+}

+ 29 - 9
ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/RankStrategyBy865.java

@@ -3,6 +3,7 @@ package com.tzld.piaoquan.ad.engine.service.score.strategy;
 import com.alibaba.fastjson.JSONObject;
 import com.ctrip.framework.apollo.spring.annotation.ApolloJsonValue;
 import com.tzld.piaoquan.ad.engine.commons.dto.AdPlatformCreativeDTO;
+import com.tzld.piaoquan.ad.engine.commons.helper.CopcV3DataHelper;
 import com.tzld.piaoquan.ad.engine.commons.helper.CreativeUserLayerDataHelperV2;
 import com.tzld.piaoquan.ad.engine.commons.helper.DnnCidDataHelper;
 import com.tzld.piaoquan.ad.engine.commons.param.RankRecommendRequestParam;
@@ -43,7 +44,7 @@ public class RankStrategyBy865 extends RankStrategyBasic {
 
     /**
      * 空 Map 常量,避免频繁创建空 HashMap
-     */
+    */
     private static final Map<String, String> EMPTY_STRING_MAP = Collections.emptyMap();
     private static final Map<String, Map<String, String>> EMPTY_NESTED_MAP = Collections.emptyMap();
 
@@ -54,6 +55,9 @@ public class RankStrategyBy865 extends RankStrategyBasic {
     @Value("${word2vec.exp:694}")
     private String word2vecExp;
 
+    @Value("${rank.score.copc.v3.switch:false}")
+    private Boolean copcV3Enabled;
+
     @ApolloJsonValue("${rank.score.params.865:{}}")
     private Map<String, String> paramsMap;
 
@@ -190,6 +194,7 @@ public class RankStrategyBy865 extends RankStrategyBasic {
                     adRankItem.setAgentId(dto.getAgentId());
                     adRankItem.setProfession(dto.getProfession());
                     adRankItem.setLandingPageType(dto.getLandingPageType());
+                    adRankItem.setTargetingConversion(dto.getTargetingConversion());
                     adRankItem.setRandom(random.nextInt(1000));
                     if (noApiAdVerIds.contains(dto.getAdVerId())) {
                         adRankItem.getExt().put("isApi", "0");
@@ -1116,19 +1121,34 @@ public class RankStrategyBy865 extends RankStrategyBasic {
                                      double minVal, double maxVal) {
         try {
             String layer = reqFeature.get("layer_l4");
-            for (AdRankItem item : items) {
-                String cid = String.valueOf(item.getAdId());
-                Double diff = CreativeUserLayerDataHelperV2.getCopc(modelName, cid, layer);
-                if (null != diff) {
-                    double newDiff = Math.min(Math.max(minVal, diff), maxVal);
-                    if (newDiff < minValid || newDiff > maxValid) {
-                        double ctcvr = item.getLrScore() * newDiff;
+            if (Boolean.TRUE.equals(copcV3Enabled)) {
+                for (AdRankItem item : items) {
+                    Double coefficient = CopcV3DataHelper.getLandingCoefficient(item.getLrScore(),
+                            item.getLandingPageType(), item.getTargetingConversion());
+                    log.info("CopcV3DataHelper originScore={}, pageType={},target={},result={}",item.getLrScore(), item.getLandingPageType(),item.getTargetingConversion(),coefficient);
+                    if (coefficient != null) {
+                        double ctcvr = item.getLrScore() * coefficient;
+                        item.getScoreMap().put("copcV3Coefficient", coefficient);
                         item.getScoreMap().put("cidCtcvrScore", ctcvr);
                         item.getScoreMap().put("ctcvrScore", ctcvr);
-                        item.getScoreMap().put("copcDiff", diff);
                         item.setLrScore(ctcvr);
                     }
                 }
+            }else {
+                for (AdRankItem item : items) {
+                    String cid = String.valueOf(item.getAdId());
+                    Double diff = CreativeUserLayerDataHelperV2.getCopc(modelName, cid, layer);
+                    if (null != diff) {
+                        double newDiff = Math.min(Math.max(minVal, diff), maxVal);
+                        if (newDiff < minValid || newDiff > maxValid) {
+                            double ctcvr = item.getLrScore() * newDiff;
+                            item.getScoreMap().put("cidCtcvrScore", ctcvr);
+                            item.getScoreMap().put("ctcvrScore", ctcvr);
+                            item.getScoreMap().put("copcDiff", diff);
+                            item.setLrScore(ctcvr);
+                        }
+                    }
+                }
             }
         } catch (Exception e) {
             log.error("calibCidScore error", e);