Browse Source

Merge branch 'dev-xym-add-model' of algorithm/ad-engine into master

xueyiming 2 days ago
parent
commit
bdaf990619

+ 22 - 9
ad-engine-commons/src/main/java/com/tzld/piaoquan/ad/engine/commons/helper/DnnCidDataHelper.java

@@ -36,32 +36,45 @@ public class DnnCidDataHelper {
 
     private static final String cidPath = "fengzhoutian/pai_model_trained_cids/";
 
+    private static final String modelVersionV2Path = "fengzhoutian/pai_model_trained_cids/model_version_v2.json";
+
 
     // 提供获取CID集合的方法
     // 全局变量,存储CSV文件中的数据
     @Getter
     private volatile static Set<Long> cidSet = Collections.emptySet();
 
+    @Getter
+    private volatile static Set<Long> cidSetV2 = Collections.emptySet();
+
     // 服务启动时初始化数据
     @PostConstruct
     public void init() {
-        updateCidSet();
+        Set<Long> cidSet1 = updateCidSet(cidPath, modelVersionPath);
+        cidSet = Collections.unmodifiableSet(cidSet1);
+
+        Set<Long> cidSet2 = updateCidSet(cidPath, modelVersionV2Path);
+        cidSetV2 = Collections.unmodifiableSet(cidSet2);
     }
 
     // 每10分钟更新一次数据
     @Scheduled(fixedRate = 10 * 60 * 1000)
     public void scheduledUpdate() {
-        updateCidSet();
+        Set<Long> cidSet1 = updateCidSet(cidPath, modelVersionPath);
+        cidSet = Collections.unmodifiableSet(cidSet1);
+
+        Set<Long> cidSet2 = updateCidSet(cidPath, modelVersionV2Path);
+        cidSetV2 = Collections.unmodifiableSet(cidSet2);
     }
 
     // 更新CID集合的方法
-    private synchronized void updateCidSet() {
+    private Set<Long> updateCidSet(String cidPath, String modelVersionPath) {
         try {
             log.info("start init cid data");
-            String modelVersion = readCsvPathFromOss();
+            String modelVersion = readCsvPathFromOss(modelVersionPath);
             if (StringUtils.isEmpty(modelVersion)) {
                 log.error("modelVersion is null");
-                return;
+                return Collections.emptySet();
             }
             String csvPath = cidPath + modelVersion;
 
@@ -105,19 +118,19 @@ public class DnnCidDataHelper {
             } else {
                 log.error("not found csv file");
             }
-            // 使用volatile保证可见性,一次性替换整个集合
-            cidSet = Collections.unmodifiableSet(newCidSet);
-            log.info("cid set init success size={}", cidSet.size());
+            log.info("cid set init success size={}", newCidSet.size());
+            return newCidSet;
         } catch (Exception e) {
             log.error("update cid error", e);
             // 发生异常时保持原数据不变
         }
+        return Collections.emptySet();
     }
 
     /**
      * 从OSS配置文件中读取CSV路径(第一行)
      */
-    private String readCsvPathFromOss() {
+    private String readCsvPathFromOss(String modelVersionPath) {
         try {
             // 检查配置文件是否存在
             if (!ossClient.doesObjectExist(BUCKET_NAME, modelVersionPath)) {

+ 3 - 0
ad-engine-commons/src/main/java/com/tzld/piaoquan/ad/engine/commons/score/ScorerUtils.java

@@ -30,6 +30,8 @@ public final class ScorerUtils {
     public static String XGBOOST_SCORE_CONF_20240909 = "ad_score_config_xgboost_20240909.conf";
     public static String XGBOOST_SCORE_CONF_20241105 = "ad_score_config_xgboost_20241105.conf";
     public static String PAI_SCORE_CONF_20250214 = "ad_score_config_pai_20250214.conf";
+    public static String PAI_SCORE_CONF_20250804 = "ad_score_config_pai_20250804.conf";
+
 
     public static void warmUp() {
         log.info("scorer warm up ");
@@ -41,6 +43,7 @@ public final class ScorerUtils {
         ScorerUtils.init(XGBOOST_SCORE_CONF_20240909);
         ScorerUtils.init(XGBOOST_SCORE_CONF_20241105);
         ScorerUtils.init(PAI_SCORE_CONF_20250214);
+        ScorerUtils.init(PAI_SCORE_CONF_20250804);
     }
 
     private ScorerUtils() {

+ 210 - 0
ad-engine-commons/src/main/java/com/tzld/piaoquan/ad/engine/commons/score/model/PAIModelV2.java

@@ -0,0 +1,210 @@
+package com.tzld.piaoquan.ad.engine.commons.score.model;
+
+import com.aliyun.openservices.eas.predict.http.HttpConfig;
+import com.aliyun.openservices.eas.predict.http.PredictClient;
+import com.aliyun.openservices.eas.predict.request.TFDataType;
+import com.aliyun.openservices.eas.predict.request.TFRequest;
+import com.aliyun.openservices.eas.predict.response.TFResponse;
+import com.tzld.piaoquan.recommend.feature.domain.ad.base.AdRankItem;
+import org.apache.commons.collections4.MapUtils;
+import org.apache.commons.lang.math.NumberUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.CollectionUtils;
+
+import java.util.*;
+
+
+public class PAIModelV2 {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(PAIModelV2.class);
+
+    private PAIModelV2() {
+    }
+
+    private static final PAIModelV2 model;
+
+    public static PAIModelV2 getModel() {
+        return model;
+    }
+
+    private static final PredictClient client;
+
+    static {
+        model = new PAIModelV2();
+        client = new PredictClient(new HttpConfig());
+        client.setEndpoint("1894469520484605.vpc.cn-hangzhou.pai-eas.aliyuncs.com");
+        client.setToken("ZTZiMWY2ZWJmN2M1NmZkYjZkMDA2ZjkzM2Q2ODNlOTc1YWIwODA0OQ==");
+        client.setModelName("ad_rank_dnn_v11_easyrec_prod");
+    }
+
+    private static final String[] sparseUserStrFeatures = {
+            "brand", "region", "city", "cate1", "cate2", "user_cid_click_list", "user_cid_conver_list",
+            "user_vid_return_tags_2h", "user_vid_return_tags_1d", "user_vid_return_tags_3d", "user_vid_return_tags_7d",
+            "user_vid_return_tags_14d", "root_source_scene", "root_source_channel", "title_split", "user_vid_share_tags_1d",
+            "user_vid_share_tags_14d", "user_vid_return_cate1_14d", "user_vid_return_cate2_14d", "user_vid_share_cate1_14d",
+            "user_vid_share_cate2_14d", "user_conver_ad_class", "user_layer_class"
+    };
+
+    private static final String[] sparseUserLongFeatures = {
+            "vid", "apptype", "is_first_layer", "user_has_conver_1y"
+    };
+
+    private static final String[] sparseSceneLongFeatures = {
+            "hour", "hour_quarter"
+    };
+
+    private static final String[] sparseAdLongFeatures = {
+            "cid", "adid", "adverid",
+            "user_adverid_view_3d", "user_adverid_view_7d", "user_adverid_view_30d",
+            "user_adverid_click_3d", "user_adverid_click_7d", "user_adverid_click_30d",
+            "user_adverid_conver_3d", "user_adverid_conver_7d", "user_adverid_conver_30d",
+            "user_skuid_view_3d", "user_skuid_view_7d", "user_skuid_view_30d",
+            "user_skuid_click_3d", "user_skuid_click_7d", "user_skuid_click_30d",
+            "user_skuid_conver_3d", "user_skuid_conver_7d", "user_skuid_conver_30d"
+    };
+
+    private static final String[] sparseAdStrFeatures = {
+            "profession"
+    };
+
+    private final String[] userFeatures = {
+            "viewAll", "clickAll", "converAll", "incomeAll", "ctr_all", "ctcvr_all", "cvr_all", "ecpm_all"
+    };
+
+    private final String[] itemFeatures = {
+            "actionstatic_click", "actionstatic_ctcvr", "actionstatic_ctr", "actionstatic_view", "b2_12h_click", "b2_12h_conver",
+            "b2_12h_conver_x_ctcvr", "b2_12h_conver_x_log_view", "b2_12h_ctcvr", "b2_12h_ctr", "b2_12h_cvr", "b2_12h_ecpm",
+            "b2_1d_click", "b2_1d_conver", "b2_1d_conver_x_ctcvr", "b2_1d_conver_x_log_view", "b2_1d_ctcvr", "b2_1d_ctr",
+            "b2_1d_cvr", "b2_1d_ecpm", "b2_3d_click", "b2_3d_conver", "b2_3d_conver_x_ctcvr", "b2_3d_conver_x_log_view",
+            "b2_3d_ctcvr", "b2_3d_ctr", "b2_3d_cvr", "b2_3d_ecpm", "b2_3h_click", "b2_3h_conver", "b2_3h_conver_x_ctcvr",
+            "b2_3h_conver_x_log_view", "b2_3h_ctcvr", "b2_3h_ctr", "b2_3h_cvr", "b2_3h_ecpm", "b2_6h_click", "b2_6h_conver",
+            "b2_6h_conver_x_ctcvr", "b2_6h_conver_x_log_view", "b2_6h_ctcvr", "b2_6h_ctr", "b2_6h_cvr", "b2_6h_ecpm", "b2_7d_click",
+            "b2_7d_conver", "b2_7d_conver_x_ctcvr", "b2_7d_conver_x_log_view", "b2_7d_ctcvr", "b2_7d_ctr", "b2_7d_cvr",
+            "b2_7d_ecpm", "b3_12h_click", "b3_12h_conver", "b3_12h_conver_x_ctcvr", "b3_12h_ctcvr", "b3_12h_ctr", "b3_12h_cvr",
+            "b3_12h_ecpm", "b3_1d_click", "b3_1d_conver", "b3_1d_conver_x_ctcvr", "b3_1d_conver_x_log_view", "b3_1d_ctcvr",
+            "b3_1d_ctr", "b3_1d_cvr", "b3_1d_ecpm", "b3_3d_click", "b3_3d_conver", "b3_3d_conver_x_ctcvr",
+            "b3_3d_conver_x_log_view", "b3_3d_ctcvr", "b3_3d_ctr", "b3_3d_cvr", "b3_3d_ecpm", "b3_3h_click", "b3_3h_conver",
+            "b3_3h_conver_x_ctcvr", "b3_3h_ctcvr", "b3_3h_ctr", "b3_3h_cvr", "b3_3h_ecpm", "b3_6h_click", "b3_6h_conver_x_ctcvr",
+            "b3_6h_ctcvr", "b3_6h_ctr", "b3_6h_cvr", "b3_6h_ecpm", "b3_7d_click", "b3_7d_conver", "b3_7d_conver_x_ctcvr",
+            "b3_7d_conver_x_log_view", "b3_7d_ctcvr", "b3_7d_ctr", "b3_7d_cvr", "b3_7d_ecpm", "b4_12h_click",
+            "b4_12h_conver_x_ctcvr", "b4_12h_conver_x_log_view", "b4_12h_ctcvr", "b4_12h_ctr", "b4_12h_cvr", "b4_12h_ecpm",
+            "b4_1d_click", "b4_1d_conver_x_ctcvr", "b4_1d_conver_x_log_view", "b4_1d_ctcvr", "b4_1d_ctr", "b4_1d_cvr", "b4_1d_ecpm",
+            "b4_3d_click", "b4_3d_conver_x_ctcvr", "b4_3d_conver_x_log_view", "b4_3d_ctcvr", "b4_3d_ctr", "b4_3d_cvr", "b4_3d_ecpm",
+            "b4_3h_click", "b4_3h_conver_x_ctcvr", "b4_3h_conver_x_log_view", "b4_3h_ctcvr", "b4_3h_ctr", "b4_3h_cvr", "b4_3h_ecpm",
+            "b4_6h_click", "b4_6h_conver_x_ctcvr", "b4_6h_conver_x_log_view", "b4_6h_ctcvr", "b4_6h_ctr", "b4_6h_cvr", "b4_6h_ecpm",
+            "b4_7d_click", "b4_7d_conver", "b4_7d_conver_x_ctcvr", "b4_7d_conver_x_log_view", "b4_7d_ctcvr", "b4_7d_ctr",
+            "b4_7d_cvr", "b4_7d_ecpm", "b5_12h_click", "b5_12h_conver", "b5_12h_conver_x_ctcvr", "b5_12h_ctcvr", "b5_12h_ctr",
+            "b5_12h_cvr", "b5_12h_ecpm", "b5_1d_click", "b5_1d_conver", "b5_1d_conver_x_ctcvr", "b5_1d_conver_x_log_view",
+            "b5_1d_ctcvr", "b5_1d_ctr", "b5_1d_cvr", "b5_1d_ecpm", "b5_3d_click", "b5_3d_conver", "b5_3d_conver_x_ctcvr",
+            "b5_3d_conver_x_log_view", "b5_3d_ctcvr", "b5_3d_ctr", "b5_3d_cvr", "b5_3d_ecpm", "b5_3h_click", "b5_3h_conver_x_ctcvr",
+            "b5_3h_conver_x_log_view", "b5_3h_ctcvr", "b5_3h_ctr", "b5_3h_cvr", "b5_3h_ecpm", "b5_6h_click",
+            "b5_6h_conver_x_log_view", "b5_6h_ctcvr", "b5_6h_ctr", "b5_6h_cvr", "b5_6h_ecpm", "b5_7d_click", "b5_7d_conver",
+            "b5_7d_conver_x_ctcvr", "b5_7d_conver_x_log_view", "b5_7d_ctcvr", "b5_7d_ctr", "b5_7d_cvr", "b5_7d_ecpm", "b6_7d_click",
+            "b6_7d_conver", "b6_7d_conver_x_log_view", "b6_7d_ctcvr", "b6_7d_ctr", "b6_7d_cvr", "b6_7d_ecpm", "b7_14d_ctr",
+            "b7_7d_click", "b7_7d_conver_x_ctcvr", "b7_7d_conver_x_log_view", "b7_7d_ctcvr", "b7_7d_ctr", "b7_7d_cvr", "b7_7d_ecpm",
+            "b8_12h_click", "b8_12h_conver_x_ctcvr", "b8_12h_ctcvr", "b8_12h_ctr", "b8_12h_cvr", "b8_12h_ecpm", "b8_1d_click",
+            "b8_1d_conver", "b8_1d_conver_x_ctcvr", "b8_1d_conver_x_log_view", "b8_1d_ctcvr", "b8_1d_ctr", "b8_1d_cvr",
+            "b8_1d_ecpm", "b8_3d_click", "b8_3d_conver", "b8_3d_conver_x_ctcvr", "b8_3d_ctcvr", "b8_3d_ctr", "b8_3d_cvr",
+            "b8_3d_ecpm", "b8_3h_click", "b8_3h_conver_x_ctcvr", "b8_3h_ctcvr", "b8_3h_ctr", "b8_3h_cvr", "b8_3h_ecpm",
+            "b8_6h_click", "b8_6h_conver_x_ctcvr", "b8_6h_ctcvr", "b8_6h_ctr", "b8_6h_cvr", "b8_6h_ecpm", "b8_7d_click",
+            "b8_7d_conver_x_ctcvr", "b8_7d_conver_x_log_view", "b8_7d_ctcvr", "b8_7d_ctr", "b8_7d_cvr", "b8_7d_ecpm", "cpa",
+            "d1_feature_12h_conver", "d1_feature_12h_ctcvr", "d1_feature_12h_ctr", "d1_feature_12h_cvr", "d1_feature_12h_ecpm",
+            "d1_feature_1d_conver", "d1_feature_1d_ctcvr", "d1_feature_1d_ctr", "d1_feature_1d_cvr", "d1_feature_1d_ecpm",
+            "d1_feature_3d_conver", "d1_feature_3d_ctcvr", "d1_feature_3d_ctr", "d1_feature_3d_ecpm", "d1_feature_3h_conver",
+            "d1_feature_3h_ctcvr", "d1_feature_3h_ctr", "d1_feature_3h_cvr", "d1_feature_3h_ecpm", "d1_feature_6h_ctcvr",
+            "d1_feature_6h_ctr", "d1_feature_6h_ecpm", "d1_feature_7d_conver", "d1_feature_7d_ctcvr", "d1_feature_7d_ctr",
+            "d1_feature_7d_cvr", "d1_feature_7d_ecpm", "e1_tags_14d_avgscore", "e1_tags_14d_maxscore", "e1_tags_3d_avgscore",
+            "e1_tags_3d_maxscore", "e1_tags_7d_avgscore", "e1_tags_7d_maxscore", "e2_tags_14d_avgscore", "e2_tags_14d_maxscore",
+            "e2_tags_3d_avgscore", "e2_tags_3d_maxscore", "e2_tags_7d_avgscore", "e2_tags_7d_maxscore", "timediff_conver",
+            "timediff_view", "vid_rank_ctcvr_14d", "vid_rank_ctcvr_1d", "vid_rank_ctcvr_3d", "vid_rank_ctcvr_7d",
+            "vid_rank_ctr_14d", "vid_rank_ctr_1d", "vid_rank_ctr_3d", "vid_rank_ctr_7d", "vid_rank_ecpm_14d", "vid_rank_ecpm_1d",
+            "vid_rank_ecpm_3d", "vid_rank_ecpm_7d"
+    };
+
+
+    public List<Float> score(final List<AdRankItem> items,
+                             final Map<String, String> userFeatureMap,
+                             final Map<String, String> sceneFeatureMap) {
+        try {
+            TFRequest request = new TFRequest();
+
+            for (String feature : sparseUserStrFeatures) {
+                String v = userFeatureMap.getOrDefault(feature, "");
+                request.addFeed(feature, TFDataType.DT_STRING, new long[]{1}, new String[]{v});
+            }
+
+            for (String feature : sparseUserLongFeatures) {
+                long v = NumberUtils.toLong(userFeatureMap.getOrDefault(feature, "0"), 0);
+                request.addFeed(feature, TFDataType.DT_INT64, new long[]{1}, new long[]{v});
+            }
+
+            for (String feature : sparseSceneLongFeatures) {
+                long v = NumberUtils.toLong(sceneFeatureMap.getOrDefault(feature, "0"), 0);
+                request.addFeed(feature, TFDataType.DT_INT64, new long[]{1}, new long[]{v});
+            }
+
+
+            for (String feature : userFeatures) {
+                double v = NumberUtils.toDouble(userFeatureMap.getOrDefault(feature, "0.0"), 0.0);
+                request.addFeed(feature.toLowerCase(), TFDataType.DT_DOUBLE, new long[]{1}, new double[]{v});
+            }
+            Map<String, double[]> doubleFeed = new HashMap<>();
+            Map<String, long[]> longFeed = new HashMap<>();
+            Map<String, String[]> strFeed = new HashMap<>();
+            for (int i = 0; i < items.size(); i++) {
+                for (String feature : itemFeatures) {
+                    String key = feature.replace("_x_", "*").replace("_view", "(view)");
+                    double[] doubles = doubleFeed.computeIfAbsent(feature, k -> new double[items.size()]);
+                    if (MapUtils.isEmpty(items.get(i).getFeatureMap())) {
+                        doubles[i] = 0.0;
+                        continue;
+                    }
+                    double v = NumberUtils.toDouble(items.get(i).getFeatureMap().getOrDefault(key, "0.0"), 0.0);
+                    doubles[i] = v;
+                }
+
+                for (String feature : sparseAdLongFeatures) {
+                    long[] longs = longFeed.computeIfAbsent(feature, k -> new long[items.size()]);
+                    if (MapUtils.isEmpty(items.get(i).getFeatureMap())) {
+                        longs[i] = 0L;
+                        continue;
+                    }
+                    long v = NumberUtils.toLong(items.get(i).getFeatureMap().getOrDefault(feature, "0"), 0L);
+                    longs[i] = v;
+                }
+
+                for (String feature : sparseAdStrFeatures) {
+                    String[] strs = strFeed.computeIfAbsent(feature, k -> new String[items.size()]);
+                    if (MapUtils.isEmpty(items.get(i).getFeatureMap())) {
+                        strs[i] = "";
+                        continue;
+                    }
+                    String v = items.get(i).getFeatureMap().getOrDefault(feature, "");
+                    strs[i] = v;
+                }
+            }
+            for (Map.Entry<String, double[]> entry : doubleFeed.entrySet()) {
+                request.addFeed(entry.getKey(), TFDataType.DT_DOUBLE, new long[]{items.size()}, entry.getValue());
+            }
+
+            for (Map.Entry<String, long[]> entry : longFeed.entrySet()) {
+                request.addFeed(entry.getKey(), TFDataType.DT_INT64, new long[]{items.size()}, entry.getValue());
+            }
+
+            for (Map.Entry<String, String[]> entry : strFeed.entrySet()) {
+                request.addFeed(entry.getKey(), TFDataType.DT_STRING, new long[]{items.size()}, entry.getValue());
+            }
+            request.addFetch("probs");
+            TFResponse response = client.predict(request);
+            List<Float> result = response.getFloatVals("probs");
+            if (!CollectionUtils.isEmpty(result)) {
+                return result;
+            }
+        } catch (Exception e) {
+            LOGGER.error("PAIModel score error", e);
+        }
+        return new ArrayList<>(Collections.nCopies(items.size(), 0.0f));
+    }
+
+}

+ 6 - 0
ad-engine-server/src/main/resources/ad_score_config_pai_20250804.conf

@@ -0,0 +1,6 @@
+scorer-config = {
+  pai-score-config = {
+    scorer-name = "com.tzld.piaoquan.ad.engine.service.score.scorer.PAIScorerV2"
+    scorer-priority = 99
+  }
+}

+ 89 - 0
ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/scorer/PAIScorerV2.java

@@ -0,0 +1,89 @@
+package com.tzld.piaoquan.ad.engine.service.score.scorer;
+
+
+import com.tzld.piaoquan.ad.engine.commons.score.AbstractScorer;
+import com.tzld.piaoquan.ad.engine.commons.score.ScoreParam;
+import com.tzld.piaoquan.ad.engine.commons.score.ScorerConfigInfo;
+import com.tzld.piaoquan.ad.engine.commons.score.model.PAIModelV2;
+import com.tzld.piaoquan.recommend.feature.domain.ad.base.AdRankItem;
+import com.tzld.piaoquan.recommend.feature.domain.ad.base.UserAdFeature;
+import org.apache.commons.collections4.CollectionUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+public class PAIScorerV2 extends AbstractScorer {
+
+    private final static Logger LOGGER = LoggerFactory.getLogger(PAIScorerV2.class);
+
+
+    public PAIScorerV2(ScorerConfigInfo configInfo) {
+        super(configInfo);
+    }
+
+    @Override
+    public List<AdRankItem> scoring(final ScoreParam param,
+                                    final UserAdFeature userAdFeature,
+                                    final List<AdRankItem> rankItems) {
+        throw new NoSuchMethodError();
+    }
+
+    public List<AdRankItem> scoring(final Map<String, String> sceneFeatureMap,
+                                    final Map<String, String> userFeatureMap,
+                                    final List<AdRankItem> rankItems) {
+        if (CollectionUtils.isEmpty(rankItems)) {
+            return rankItems;
+        }
+
+        long startTime = System.currentTimeMillis();
+
+        List<AdRankItem> result = rankByJava(sceneFeatureMap, userFeatureMap, rankItems);
+
+        LOGGER.debug("ctr ranker time java items size={}, time={} ", result != null ? result.size() : 0,
+                System.currentTimeMillis() - startTime);
+
+        return result;
+    }
+
+    private List<AdRankItem> rankByJava(final Map<String, String> sceneFeatureMap,
+                                        final Map<String, String> userFeatureMap,
+                                        final List<AdRankItem> items) {
+        long startTime = System.currentTimeMillis();
+        PAIModelV2 model = PAIModelV2.getModel();
+        // 所有都参与打分,按照ctr排序
+        multipleCtrScore(items, userFeatureMap, sceneFeatureMap, model);
+
+        // debug log
+        if (LOGGER.isDebugEnabled()) {
+            for (int i = 0; i < items.size(); i++) {
+                LOGGER.debug("before enter feeds model predict ctr score [{}] [{}]", items.get(i), items.get(i));
+            }
+        }
+
+        Collections.sort(items);
+
+        LOGGER.debug("ctr ranker java execute time: [{}]", System.currentTimeMillis() - startTime);
+        LOGGER.debug("[ctr ranker time java] items size={}, cost={} ", items != null ? items.size() : 0,
+                System.currentTimeMillis() - startTime);
+        return items;
+    }
+
+    private void multipleCtrScore(final List<AdRankItem> items,
+                                  final Map<String, String> userFeatureMap,
+                                  final Map<String, String> sceneFeatureMap,
+                                  final PAIModelV2 model) {
+
+        List<Float> score = model.score(items, userFeatureMap, sceneFeatureMap);
+        LOGGER.debug("PAIScorer score={}", score);
+        for (int i = 0; i < items.size(); i++) {
+            Double pro = Double.valueOf(score.get(i));
+            items.get(i).setLrScore(pro);
+            items.get(i).getScoreMap().put("ctcvrScore", pro);
+        }
+    }
+
+
+}

+ 246 - 18
ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/RankStrategyBy683.java

@@ -2,6 +2,9 @@ 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.DnnCidDataHelper;
+import com.tzld.piaoquan.ad.engine.commons.param.RankRecommendRequestParam;
 import com.tzld.piaoquan.ad.engine.commons.score.ScoreParam;
 import com.tzld.piaoquan.ad.engine.commons.score.ScorerUtils;
 import com.tzld.piaoquan.ad.engine.commons.thread.ThreadPoolFactory;
@@ -9,8 +12,6 @@ import com.tzld.piaoquan.ad.engine.commons.util.*;
 import com.tzld.piaoquan.ad.engine.service.entity.CorrectCpaParam;
 import com.tzld.piaoquan.ad.engine.service.entity.GuaranteeView;
 import com.tzld.piaoquan.ad.engine.service.feature.Feature;
-import com.tzld.piaoquan.ad.engine.commons.dto.AdPlatformCreativeDTO;
-import com.tzld.piaoquan.ad.engine.commons.param.RankRecommendRequestParam;
 import com.tzld.piaoquan.recommend.feature.domain.ad.base.AdRankItem;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.CollectionUtils;
@@ -45,21 +46,33 @@ public class RankStrategyBy683 extends RankStrategyBasic {
     @Value("${word2vec.exp:694}")
     private String word2vecExp;
 
+
     // FIXME(zhoutian): 可能需要独立配置
     @ApolloJsonValue("${rank.score.weight.680:{}}")
     private Map<String, Double> weightMap;
 
+    /**
+     * 人群分层&创意的权重
+     * 格式:{layer_creativeId: weight}
+     */
+    @ApolloJsonValue("${rank.score.weight.layer.and.creative:{}}")
+    private Map<String, Double> layerAndCreativeWeightMap;
+
     @ApolloJsonValue("${rank.score.neg_sample_rate:0.01}")
     Double negSampleRate;
 
+    Set<String> sparseFeatureSet;
+
+
     @PostConstruct
     public void afterInit() {
         this.readBucketFile();
+        this.initSparseFeatureNames();
     }
 
+
     @Override
     public List<AdRankItem> adItemRank(RankRecommendRequestParam request, ScoreParam scoreParam) {
-
         Map<String, Double> weightParam = ObjUtil.nullOrDefault(weightMap, new HashMap<>());
 
 
@@ -80,6 +93,7 @@ public class RankStrategyBy683 extends RankStrategyBasic {
         // feature1
         Feature feature = this.getFeature(scoreParam, request);
 
+
         Map<String, Map<String, String>> userFeature = feature.getUserFeature();
         Map<String, Map<String, String>> videoFeature = feature.getVideoFeature();
         Map<String, Map<String, Map<String, String>>> allAdVerFeature = feature.getAdVerFeature();
@@ -87,7 +101,6 @@ public class RankStrategyBy683 extends RankStrategyBasic {
         Map<String, Map<String, Map<String, String>>> allSkuFeature = feature.getSkuFeature();
         Map<String, String> reqFeature = this.getReqFeature(scoreParam, request);
 
-
         Map<String, String> userFeatureMap = new HashMap<>();
         Map<String, String> c1Feature = userFeature.getOrDefault("alg_mid_feature_ad_action", new HashMap<>());
         List<TupleMapEntry<Tuple5>> midActionList = this.handleC1Feature(c1Feature, userFeatureMap);
@@ -103,6 +116,37 @@ public class RankStrategyBy683 extends RankStrategyBasic {
         Map<String, String> e1Feature = userFeature.getOrDefault("alg_mid_feature_return_tags", new HashMap<>());
         Map<String, String> e2Feature = userFeature.getOrDefault("alg_mid_feature_share_tags", new HashMap<>());
 
+        Map<String, String> g1Feature = userFeature.getOrDefault("mid_return_video_cate", new HashMap<>());
+        Map<String, String> g2Feature = userFeature.getOrDefault("mid_share_video_cate", new HashMap<>());
+
+
+        userFeatureMap.put("brand", reqFeature.getOrDefault("brand", ""));
+        userFeatureMap.put("region", reqFeature.getOrDefault("region", ""));
+        userFeatureMap.put("city", reqFeature.getOrDefault("city", ""));
+        userFeatureMap.put("vid", reqFeature.getOrDefault("vid", ""));
+        userFeatureMap.put("apptype", reqFeature.getOrDefault("apptype", ""));
+        userFeatureMap.put("is_first_layer", reqFeature.getOrDefault("is_first_layer", ""));
+        userFeatureMap.put("root_source_scene", reqFeature.getOrDefault("root_source_scene", ""));
+        userFeatureMap.put("root_source_channel", reqFeature.getOrDefault("root_source_channel", ""));
+
+
+        userFeatureMap.put("cate1", d3Feature.get("merge_first_level_cate"));
+        userFeatureMap.put("cate2", d3Feature.get("merge_second_level_cate"));
+        userFeatureMap.put("user_vid_return_tags_2h", e1Feature.getOrDefault("tags_2h", null));
+        userFeatureMap.put("user_vid_return_tags_1d", e1Feature.getOrDefault("tags_1d", null));
+        userFeatureMap.put("user_vid_return_tags_3d", e1Feature.getOrDefault("tags_3d", null));
+        userFeatureMap.put("user_vid_return_tags_7d", e1Feature.getOrDefault("tags_7d", null));
+        userFeatureMap.put("user_vid_return_tags_14d", e1Feature.getOrDefault("tags_14d", null));
+        userFeatureMap.put("title_split", d3Feature.getOrDefault("title_split", null));
+        userFeatureMap.put("user_vid_share_tags_1d", e2Feature.getOrDefault("tags_1d", null));
+        userFeatureMap.put("user_vid_share_tags_14d", e2Feature.getOrDefault("tags_14d", null));
+        userFeatureMap.put("user_vid_return_cate1_14d", g1Feature.getOrDefault("cate1_14d", null));
+        userFeatureMap.put("user_vid_return_cate2_14d", g1Feature.getOrDefault("cate2_14d", null));
+        userFeatureMap.put("user_vid_share_cate1_14d", g2Feature.getOrDefault("cate1_14d", null));
+        userFeatureMap.put("user_vid_share_cate2_14d", g2Feature.getOrDefault("cate2_14d", null));
+
+        userFeatureMap.put("user_layer_class", reqFeature.getOrDefault("user_layer_class", null));
+
         Map<String, String> sceneFeatureMap = this.handleSceneFeature(ts);
         long time1 = System.currentTimeMillis();
 
@@ -133,7 +177,6 @@ public class RankStrategyBy683 extends RankStrategyBasic {
                     } else {
                         adRankItem.getExt().put("isApi", "1");
                     }
-
                     adRankItem.getExt().put("recallsources", dto.getRecallSources());
                     adRankItem.getExt().put("correctCpaMap", JSONObject.toJSONString(correctCpaMap.get(dto.getAdId())));
                     adRankItem.getExt().put("correctionFactor", correctCpaMap.get(dto.getAdId()).getCorrectionFactor());
@@ -144,7 +187,7 @@ public class RankStrategyBy683 extends RankStrategyBasic {
                     Map<String, String> b1Feature = cidFeature.getOrDefault("alg_cid_feature_basic_info", new HashMap<>());
 
                     Map<String, Map<String, String>> adVerFeature = allAdVerFeature.getOrDefault(dto.getAdVerId(), new HashMap<>());
-
+                    Map<String, Map<String, String>> skuFeature = allSkuFeature.getOrDefault(String.valueOf(dto.getSkuId()), new HashMap<>());
                     Map<String, String> d1Feature = cidFeature.getOrDefault("alg_cid_feature_vid_cf", new HashMap<>());
 
                     this.handleB1Feature(b1Feature, cidFeatureMap, cidStr);
@@ -153,6 +196,17 @@ public class RankStrategyBy683 extends RankStrategyBasic {
                     this.handleC1UIFeature(midTimeDiffMap, actionStaticMap, cidFeatureMap, cidStr);
                     this.handleD1Feature(d1Feature, cidFeatureMap);
                     this.handleD2Feature(vidRankMaps, cidFeatureMap, cidStr);
+                    this.handleH1AndH2Feature(skuFeature, adVerFeature, cidFeatureMap);
+                    cidFeatureMap.put("cid", dto.getCreativeId() != null ? String.valueOf(dto.getCreativeId()) : "");
+                    cidFeatureMap.put("adid", dto.getAdId() != null ? String.valueOf(dto.getAdId()) : "");
+                    cidFeatureMap.put("adverid", dto.getAdVerId() != null ? dto.getAdVerId() : "");
+                    cidFeatureMap.put("profession", dto.getProfession() != null ? dto.getProfession() : "");
+                    //DNN模型没训练过的cid才不传入广告相关的稀疏特征
+                    if (CollectionUtils.isNotEmpty(DnnCidDataHelper.getCidSetV2()) && !DnnCidDataHelper.getCidSetV2().contains(adRankItem.getAdId())) {
+                        cidFeatureMap.put("cid", "");
+                        cidFeatureMap.put("adid", "");
+                        cidFeatureMap.put("adverid", "");
+                    }
                     return adRankItem;
                 } finally {
                     cdl1.countDown();
@@ -226,27 +280,38 @@ public class RankStrategyBy683 extends RankStrategyBasic {
         long time4 = System.currentTimeMillis();
         // 打分排序
         // getScorerPipeline
-        List<AdRankItem> result = ScorerUtils.getScorerPipeline(ScorerUtils.XGBOOST_SCORE_CONF_683).scoring(sceneFeatureMap, userFeatureMap, adRankItems);
+        List<AdRankItem> result = ScorerUtils.getScorerPipeline(ScorerUtils.PAI_SCORE_CONF_20250804).scoring(sceneFeatureMap, userFeatureMap, adRankItems);
         long time5 = System.currentTimeMillis();
-
-        // calibrate score for negative sampling
+        // calibrate score for negative sampling or cold start
         for (AdRankItem item : result) {
             double originalScore = item.getLrScore();
             double calibratedScore = originalScore / (originalScore + (1 - originalScore) / negSampleRate);
+            // 该创意尚未在模型中训练,打分不可靠
+            if (CollectionUtils.isNotEmpty(DnnCidDataHelper.getCidSetV2()) && !DnnCidDataHelper.getCidSetV2().contains(item.getAdId())) {
+                Map<String, Map<String, String>> cidFeature = allCidFeature.getOrDefault(String.valueOf(item.getAdId()), new HashMap<>());
+                Map<String, String> b3Feature = cidFeature.getOrDefault("alg_cid_feature_cid_action", new HashMap<>());
+                double view = Double.parseDouble(b3Feature.getOrDefault("ad_view_14d", "0"));
+                double conver = Double.parseDouble(b3Feature.getOrDefault("ad_conversion_14d", "0"));
+                double smoothCxr = NumUtil.divSmoothV1(conver, view, 1.64);
+                //模型打分和统计计算取打分更低的
+                calibratedScore = Math.min(smoothCxr, calibratedScore);
+            }
             item.setLrScore(calibratedScore);
             item.getScoreMap().put("originCtcvrScore", originalScore);
             item.getScoreMap().put("modelCtcvrScore", calibratedScore);
             item.getScoreMap().put("ctcvrScore", calibratedScore);
         }
 
-        calculateCtcvrScore(result, request, scoreParam, null, reqFeature);
+        calculateCtcvrScore(result, request, scoreParam, "dnn", reqFeature);
         // loop
         double cpmCoefficient = weightParam.getOrDefault("cpmCoefficient", 0.9);
         boolean isGuaranteeType = false;
+        // 查询人群分层信息
+        String peopleLayer = Optional.of(reqFeature)
+                .map(f -> f.get("layer"))
+                .map(s -> s.replace("-炸", ""))
+                .orElse(null);
         for (AdRankItem item : result) {
-            if (isGuaranteedFlow && item.getExt().get("isGuaranteed") != null && (boolean) item.getExt().get("isGuaranteed")) {
-                isGuaranteeType = true;
-            }
             double bid = item.getCpa();
             if (scoreParam.getExpCodeSet().contains(correctCpaExp1) || scoreParam.getExpCodeSet().contains(correctCpaExp2)) {
                 Double correctionFactor = (Double) item.getExt().get("correctionFactor");
@@ -254,9 +319,16 @@ public class RankStrategyBy683 extends RankStrategyBasic {
                 bid = bid * correctionFactor;
             }
             item.getScoreMap().put("ecpm", item.getLrScore() * bid * 1000);
+            if (isGuaranteedFlow && item.getExt().get("isGuaranteed") != null && (boolean) item.getExt().get("isGuaranteed")) {
+                isGuaranteeType = true;
+            }
+
+            String layerAndCreativeWeightMapKey = getLayerAndCreativeWeightMapKey(peopleLayer, String.valueOf(item.getAdId()));
+            // 人群分层&创意的权重
+            double layerAndCreativeWeight = getLayerAndCreativeWeight(layerAndCreativeWeightMapKey);
             double scoreCoefficient = creativeScoreCoefficient.getOrDefault(item.getAdId(), 1d);
             double guaranteeScoreCoefficient = getGuaranteeScoreCoefficient(isGuaranteedFlow, item.getExt());
-            double score = item.getLrScore() * bid * scoreCoefficient * guaranteeScoreCoefficient;
+            double score = item.getLrScore() * bid * scoreCoefficient * guaranteeScoreCoefficient * layerAndCreativeWeight;
             item.getScoreMap().put("guaranteeScoreCoefficient", guaranteeScoreCoefficient);
             item.getScoreMap().put("cpa", item.getCpa());
             item.getScoreMap().put("cpm", item.getCpm());
@@ -265,6 +337,7 @@ public class RankStrategyBy683 extends RankStrategyBasic {
             item.getScoreMap().put("scoreCoefficient", scoreCoefficient);
             item.getFeatureMap().putAll(userFeatureMap);
             item.getFeatureMap().putAll(sceneFeatureMap);
+
             // 没有转化回传的广告主,使用后台配置的CPM
             if (noApiAdVerIds.contains(item.getAdVerId())) {
                 score = item.getCpm() * cpmCoefficient / 1000;
@@ -277,8 +350,23 @@ public class RankStrategyBy683 extends RankStrategyBasic {
 
         if (CollectionUtils.isNotEmpty(result)) {
             AdRankItem top1Item = result.get(0);
+            List<String> participateCompetitionType = new ArrayList<>();
+            participateCompetitionType.add("engine");
             top1Item.getExt().put("isGuaranteeType", isGuaranteeType);
+            if (isGuaranteeType) {
+                participateCompetitionType.add("guarantee");
+            }
+            top1Item.getExt().put("participateCompetitionType", StringUtils.join(participateCompetitionType, ","));
+            Double modelCtcvrScore = top1Item.getScoreMap().get("modelCtcvrScore");
+            Double ctcvrScore = top1Item.getScoreMap().get("ctcvrScore");
+            if (scoreParam.getExpCodeSet().contains(checkoutEcpmExp)) {
+                top1Item.getExt().put("ecpm", ctcvrScore * top1Item.getCpa() * 1000);
+                top1Item.getExt().put("filterEcpm", filterEcpm);
+            } else {
+                top1Item.getExt().put("ecpm", modelCtcvrScore * top1Item.getCpa() * 1000);
+            }
             putMetaFeature(top1Item, feature, reqFeature, sceneFeatureMap, request);
+            top1Item.getExt().put("model", "dnn");
         }
         long time6 = System.currentTimeMillis();
         log.info("cost={}, getFeature={}, handleFeature={},  similar={}, bucketFeature={}, getScorerPipeline={}, " +
@@ -289,6 +377,34 @@ public class RankStrategyBy683 extends RankStrategyBasic {
         return result;
     }
 
+    /**
+     * 获取人群分层和创意的权重
+     *
+     * @param key
+     * @return
+     */
+    private Double getLayerAndCreativeWeight(String key) {
+        if (StringUtils.isBlank(key)) {
+            return 1d;
+        }
+        return layerAndCreativeWeightMap.getOrDefault(key, 1d);
+    }
+
+    /**
+     * 获取人群分层和创意的权重key
+     *
+     * @param layer
+     * @param creativeId
+     * @return
+     */
+    private String getLayerAndCreativeWeightMapKey(String layer, String creativeId) {
+        if (StringUtils.isBlank(layer) || StringUtils.isBlank(creativeId)) {
+            return null;
+        }
+        return layer + "_" + creativeId;
+    }
+
+
     private void handleB1Feature(Map<String, String> b1Feature, Map<String, String> cidFeatureMap, String cid) {
         cidFeatureMap.put("cid_" + cid, "0.1");
         // if (StringUtils.isNotBlank(b1Feature.get("adid"))) {
@@ -389,6 +505,15 @@ public class RankStrategyBy683 extends RankStrategyBasic {
 
     private List<TupleMapEntry<Tuple5>> handleC1Feature(Map<String, String> c1Feature, Map<String, String> featureMap) {
 
+        //用户近1年内是否有转化
+        if (c1Feature.containsKey("user_has_conver_1y") && c1Feature.get("user_has_conver_1y") != null) {
+            featureMap.put("user_has_conver_1y", c1Feature.get("user_has_conver_1y"));
+        }
+        //用户历史转化过品类
+        if (c1Feature.containsKey("user_conver_ad_class") && c1Feature.get("user_conver_ad_class") != null) {
+            featureMap.put("user_conver_ad_class", c1Feature.get("user_conver_ad_class"));
+        }
+
         // 用户特征
         List<TupleMapEntry<Tuple5>> midActionList = new ArrayList<>();
         if (c1Feature.containsKey("action")) {
@@ -416,7 +541,23 @@ public class RankStrategyBy683 extends RankStrategyBasic {
         featureMap.put("ctcvr_all", String.valueOf(NumUtil.div(converAll, viewAll)));
         featureMap.put("cvr_all", String.valueOf(NumUtil.div(clickAll, converAll)));
         featureMap.put("ecpm_all", String.valueOf(NumUtil.div(incomeAll * 1000, viewAll)));
-
+        if (CollectionUtils.isNotEmpty(midActionList)) {
+            List<String> cidClickList = new ArrayList<>();
+            List<String> cidConverList = new ArrayList<>();
+            for (TupleMapEntry<Tuple5> tupleMapEntry : midActionList) {
+                String cid = tupleMapEntry.key;
+                String click = tupleMapEntry.value.f2;
+                String conver = tupleMapEntry.value.f3;
+                if (Objects.equals(click, "1")) {
+                    cidClickList.add(cid);
+                }
+                if (Objects.equals(conver, "1")) {
+                    cidConverList.add(cid);
+                }
+            }
+            featureMap.put("user_cid_click_list", String.join(",", cidClickList));
+            featureMap.put("user_cid_conver_list", String.join(",", cidConverList));
+        }
         return midActionList;
     }
 
@@ -497,6 +638,33 @@ public class RankStrategyBy683 extends RankStrategyBasic {
         }
     }
 
+    private void handleH1AndH2Feature(Map<String, Map<String, String>> skuFeature,
+                                      Map<String, Map<String, String>> adVerFeature,
+                                      Map<String, String> cidFeatureMap) {
+        Map<String, String> h1Feature = adVerFeature.getOrDefault("alg_mid_feature_adver_action", new HashMap<>());
+        Map<String, String> h2Feature = skuFeature.getOrDefault("alg_mid_feature_sku_action", new HashMap<>());
+        List<String> timeList = Arrays.asList("3d", "7d", "30d");
+        List<Tuple2<Map<String, String>, String>> featureList = Arrays.asList(
+                new Tuple2<>(h1Feature, "adverid"),
+                new Tuple2<>(h2Feature, "skuid")
+        );
+        for (Tuple2<Map<String, String>, String> tuple2 : featureList) {
+            Map<String, String> feature = tuple2.f1;
+            String prefix = tuple2.f2;
+            for (String time : timeList) {
+                String timeValue = feature.getOrDefault(time, "");
+                if (StringUtils.isNotEmpty(timeValue)) {
+                    String[] split = timeValue.split(",");
+                    cidFeatureMap.put("user" + "_" + prefix + "_" + "view" + "_" + time, split[0]);
+                    cidFeatureMap.put("user" + "_" + prefix + "_" + "click" + "_" + time, split[1]);
+                    cidFeatureMap.put("user" + "_" + prefix + "_" + "conver" + "_" + time, split[2]);
+                }
+            }
+        }
+
+
+    }
+
     private void handleD3AndB1Feature(Map<String, String> d3Feature, String cTitle, Map<String, String> featureMap,
                                       ScoreParam scoreParam) {
         if (MapUtils.isEmpty(d3Feature) || !d3Feature.containsKey("title") || StringUtils.isEmpty(cTitle)) {
@@ -608,7 +776,7 @@ public class RankStrategyBy683 extends RankStrategyBasic {
         }
         synchronized (this) {
             String bucketFile = "20250217_ad_bucket_688.txt";
-            InputStream resourceStream = RankStrategyBy683.class.getClassLoader().getResourceAsStream(bucketFile);
+            InputStream resourceStream = this.getClass().getClassLoader().getResourceAsStream(bucketFile);
             if (resourceStream != null) {
                 try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceStream))) {
                     Map<String, double[]> bucketsMap = new HashMap<>();
@@ -640,10 +808,72 @@ public class RankStrategyBy683 extends RankStrategyBasic {
         }
     }
 
+    private void initSparseFeatureNames() {
+        this.sparseFeatureSet = new HashSet<String>() {{
+            add("brand");
+            add("region");
+            add("city");
+            add("vid");
+            add("cate1");
+            add("cate2");
+            add("cid");
+            add("adid");
+            add("adverid");
+            add("user_cid_click_list");
+            add("user_cid_conver_list");
+            add("user_vid_return_tags_2h");
+            add("user_vid_return_tags_1d");
+            add("user_vid_return_tags_3d");
+            add("user_vid_return_tags_7d");
+            add("user_vid_return_tags_14d");
+            add("apptype");
+            add("hour");
+            add("hour_quarter");
+            add("root_source_scene");
+            add("root_source_channel");
+            add("is_first_layer");
+            add("title_split");
+            add("profession");
+            add("user_vid_share_tags_1d");
+            add("user_vid_share_tags_14d");
+            add("user_vid_return_cate1_14d");
+            add("user_vid_return_cate2_14d");
+            add("user_vid_share_cate1_14d");
+            add("user_vid_share_cate2_14d");
+            add("user_has_conver_1y");
+            add("user_conver_ad_class");
+            add("user_adverid_view_3d");
+            add("user_adverid_click_3d");
+            add("user_adverid_conver_3d");
+            add("user_adverid_view_7d");
+            add("user_adverid_click_7d");
+            add("user_adverid_conver_7d");
+            add("user_adverid_view_30d");
+            add("user_adverid_click_30d");
+            add("user_adverid_conver_30d");
+            add("user_skuid_view_3d");
+            add("user_skuid_click_3d");
+            add("user_skuid_conver_3d");
+            add("user_skuid_view_7d");
+            add("user_skuid_click_7d");
+            add("user_skuid_conver_7d");
+            add("user_skuid_view_30d");
+            add("user_skuid_click_30d");
+            add("user_skuid_conver_30d");
+            add("user_layer_class");
+        }};
+    }
+
     private Map<String, String> featureBucket(Map<String, String> featureMap) {
         Map<String, String> newFeatureMap = new ConcurrentHashMap<>(featureMap.size());
         for (Map.Entry<String, String> entry : featureMap.entrySet()) {
             String name = entry.getKey();
+            if (this.sparseFeatureSet.contains(name)) {
+                if (entry.getValue() != null) {
+                    newFeatureMap.put(name, entry.getValue());
+                }
+                continue;
+            }
             double score = Double.parseDouble(entry.getValue());
             // 注意:0值、不在分桶文件中的特征,会被过滤掉。
             if (score > 1E-8) {
@@ -657,8 +887,6 @@ public class RankStrategyBy683 extends RankStrategyBasic {
                 }
             }
         }
-
         return newFeatureMap;
     }
-
 }