|
|
@@ -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.CreativeUserLayerDataHelper;
|
|
|
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;
|
|
|
@@ -83,6 +84,9 @@ public class RankStrategyBy886 extends RankStrategyBasic {
|
|
|
@Override
|
|
|
public List<AdRankItem> adItemRank(RankRecommendRequestParam request, ScoreParam scoreParam) {
|
|
|
Map<String, Double> weightParam = ObjUtil.nullOrDefault(weightMap, new HashMap<>());
|
|
|
+
|
|
|
+
|
|
|
+ Map<Long, Double> creativeScoreCoefficient = getCreativeScoreCoefficient();
|
|
|
Set<String> noApiAdVerIds = getNoApiAdVerIds();
|
|
|
|
|
|
long ts = System.currentTimeMillis() / 1000;
|
|
|
@@ -186,21 +190,19 @@ public class RankStrategyBy886 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");
|
|
|
} else {
|
|
|
adRankItem.getExt().put("isApi", "1");
|
|
|
}
|
|
|
- adRankItem.getExt().put("recallsources", dto.getRecallSources());
|
|
|
- fillAdRankItemExt(adRankItem, dto);
|
|
|
- // 886 的竞价系数来自当前广告候选,缺失时使用中性值以兼容历史请求。
|
|
|
+ // 883 的竞价系数来自当前广告候选,缺失时使用中性值以兼容历史请求。
|
|
|
adRankItem.getScoreMap().put("fRankCoefficient", normalizeCoefficient(dto.getFRankCoefficient()));
|
|
|
adRankItem.getScoreMap().put("rcRankCoefficient", normalizeCoefficient(dto.getRcRankCoefficient()));
|
|
|
adRankItem.getScoreMap().put("bidCoefficient", normalizeCoefficient(dto.getBidCoefficient()));
|
|
|
- // 计价系数不参与排序,但需要进入排序日志,供曝光计价链路回溯。
|
|
|
adRankItem.getScoreMap().put("pricingCoefficient", normalizeCoefficient(dto.getPricingCoefficient()));
|
|
|
+ adRankItem.getExt().put("recallsources", dto.getRecallSources());
|
|
|
+ fillAdRankItemExt(adRankItem, dto);
|
|
|
adRankItem.getExt().put("correctCpaMap", JSONObject.toJSONString(correctCpaMap.get(dto.getAdId())));
|
|
|
adRankItem.getExt().put("correctionFactor", correctCpaMap.get(dto.getAdId()).getCorrectionFactor());
|
|
|
setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt(), isGuaranteedFlow, reqFeature);
|
|
|
@@ -322,86 +324,131 @@ public class RankStrategyBy886 extends RankStrategyBasic {
|
|
|
if (CollectionUtils.isEmpty(adRankItems)) {
|
|
|
log.error("adRankItems is empty");
|
|
|
}
|
|
|
- List<AdRankItem> result = ScorerUtils.getScorerPipeline(ScorerUtils.PAI_SCORE_CONF_20250804).scoring(sceneFeatureMap, userFeatureMap, adRankItems);
|
|
|
+ List<AdRankItem> result = ScorerUtils.getScorerPipeline(ScorerUtils.PAI_SCORE_CONF_20250214).scoring(sceneFeatureMap, userFeatureMap, adRankItems);
|
|
|
if (CollectionUtils.isEmpty(result)) {
|
|
|
log.error("scoring result is empty");
|
|
|
}
|
|
|
long time5 = System.currentTimeMillis();
|
|
|
+ int viewLimit = NumberUtils.toInt(paramsMap.getOrDefault("viewLimit", "3000"));
|
|
|
// calibrate score for negative sampling or cold start
|
|
|
for (AdRankItem item : result) {
|
|
|
- double scanScore = item.getScoreMap().getOrDefault("scanScore", 0.0);
|
|
|
- double addWechatScore = item.getScoreMap().getOrDefault("addWechatScore", 0.0);
|
|
|
- double conversionScore = item.getScoreMap().getOrDefault("conversionScore", 0.0);
|
|
|
- double calibratedScanScore = scanScore / (scanScore + (1 - scanScore) / negSampleRate);
|
|
|
- double calibratedConversionScore = conversionScore / (conversionScore + (1 - conversionScore) / negSampleRate);
|
|
|
- item.getScoreMap().put("calibratedScanScore", calibratedScanScore);
|
|
|
- item.getScoreMap().put("calibratedConversionScore", calibratedConversionScore);
|
|
|
- double calibratedScore = 0.0;
|
|
|
- if (Objects.equals(item.getLandingPageType(), 3) && Objects.equals(item.getTargetingConversion(), "10019")) {
|
|
|
- calibratedScore = calibratedScanScore;
|
|
|
- } else if (Objects.equals(item.getLandingPageType(), 3) && Objects.equals(item.getTargetingConversion(), "10004")) {
|
|
|
- calibratedScore = calibratedScanScore * addWechatScore;
|
|
|
- } else {
|
|
|
- calibratedScore = calibratedConversionScore;
|
|
|
+ double originalScore = item.getLrScore();
|
|
|
+ double calibratedScore = originalScore / (originalScore + (1 - originalScore) / negSampleRate);
|
|
|
+ // 该创意尚未在模型中训练,打分不可靠
|
|
|
+ Map<String, Map<String, String>> cidFeature = allCidFeature.getOrDefault(String.valueOf(item.getAdId()), EMPTY_NESTED_MAP);
|
|
|
+ Map<String, String> b3Feature = cidFeature.getOrDefault("alg_cid_feature_cid_action", EMPTY_STRING_MAP);
|
|
|
+ double view3Day = Double.parseDouble(b3Feature.getOrDefault("ad_view_3d", "0"));
|
|
|
+ if ((CollectionUtils.isNotEmpty(DnnCidDataHelper.getCidSet()) && !DnnCidDataHelper.getCidSet().contains(item.getAdId()))
|
|
|
+ || view3Day <= viewLimit) {
|
|
|
+ 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);
|
|
|
+ smoothCxr = this.getDefaultCxr(smoothCxr);
|
|
|
+ //模型打分和统计计算取打分更低的
|
|
|
+ item.getScoreMap().put("cvcvrItemValue", 1.0);
|
|
|
+ if (smoothCxr <= calibratedScore) {
|
|
|
+ calibratedScore = smoothCxr;
|
|
|
+ item.getScoreMap().put("cvcvrItemValue", 2.0);
|
|
|
+ }
|
|
|
}
|
|
|
+ item.setLrScore(calibratedScore);
|
|
|
+ item.getScoreMap().put("originCtcvrScore", originalScore);
|
|
|
item.getScoreMap().put("modelCtcvrScore", calibratedScore);
|
|
|
item.getScoreMap().put("ctcvrScore", calibratedScore);
|
|
|
- item.setLrScore(calibratedScore);
|
|
|
}
|
|
|
|
|
|
- // 886 的 eCPM 与排序分分开:eCPM 表示竞价基础值,score 表示叠加 851 外围权重后的排序值。
|
|
|
+ String calibModelName = paramsMap.getOrDefault("calibModelName", "dnnV3");
|
|
|
+ calculateCtcvrScore(result, request, scoreParam, calibModelName, reqFeature);
|
|
|
+
|
|
|
+ double minValidCopc = NumberUtils.toDouble(paramsMap.getOrDefault("minValidCopc", "0.8"));
|
|
|
+ double maxValidCopc = NumberUtils.toDouble(paramsMap.getOrDefault("maxValidCopc", "10"));
|
|
|
+ double minCopc = NumberUtils.toDouble(paramsMap.getOrDefault("minCopc", "0.2"));
|
|
|
+ double maxCopc = NumberUtils.toDouble(paramsMap.getOrDefault("maxCopc", "2.5"));
|
|
|
+ calibrationCidCtcvr(result, calibModelName, reqFeature, minValidCopc, maxValidCopc, minCopc, maxCopc);
|
|
|
+ if (CollectionUtils.isEmpty(result)) {
|
|
|
+ log.error("calculateCtcvrScore result is empty");
|
|
|
+ }
|
|
|
+ // 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);
|
|
|
+
|
|
|
+ // 控制曝光参数
|
|
|
+ String expOldKey = paramsMap.getOrDefault("expOldKey", "ad_view_yesterday");
|
|
|
+ double expOldThreshold = NumberUtils.toDouble(paramsMap.getOrDefault("expOldThreshold", "1000"));
|
|
|
+ String expNewKey = paramsMap.getOrDefault("expNewKey", "ad_view_today");
|
|
|
+ double expNewThreshold = NumberUtils.toDouble(paramsMap.getOrDefault("expNewThreshold", "3000"));
|
|
|
+ double expLowerWeight = NumberUtils.toDouble(paramsMap.getOrDefault("expLowerWeight", "0.2"));
|
|
|
+ double expUpperWeight = NumberUtils.toDouble(paramsMap.getOrDefault("expUpperWeight", "1.0"));
|
|
|
+ double expScale = NumberUtils.toDouble(paramsMap.getOrDefault("expScale", "10.0"));
|
|
|
int openH5 = NumberUtils.toInt(paramsMap.getOrDefault("openH5", "0"));
|
|
|
|
|
|
- // 与 851 一致,通过尾号 rerank 配置填充 flowCtlC、flowCtlA 和 kFinal。
|
|
|
+ // 计算rerank权重
|
|
|
calRerankWeight(scoreParam, userLayer, result);
|
|
|
for (AdRankItem item : result) {
|
|
|
- // modelCtcvrScore 是负采样(含冷启动兜底)阶段固化的分数。
|
|
|
- double modelCtcvrScore = item.getScoreMap().getOrDefault("modelCtcvrScore", item.getLrScore());
|
|
|
double bid = item.getCpa();
|
|
|
+ if (scoreParam.getExpCodeSet().contains(correctCpaExp1) || scoreParam.getExpCodeSet().contains(correctCpaExp2)) {
|
|
|
+ Double correctionFactor = (Double) item.getExt().get("correctionFactor");
|
|
|
+ item.getScoreMap().put("correctionFactor", correctionFactor);
|
|
|
+ bid = bid * correctionFactor;
|
|
|
+ }
|
|
|
+ double ecpm = item.getLrScore() * bid * 1000;
|
|
|
+ if (isGuaranteedFlow && item.getExt().get("isGuaranteed") != null && (boolean) item.getExt().get("isGuaranteed")) {
|
|
|
+ isGuaranteeType = true;
|
|
|
+ }
|
|
|
+
|
|
|
double fRankCoefficient = item.getScoreMap().getOrDefault("fRankCoefficient", 1.0D);
|
|
|
double rcRankCoefficient = item.getScoreMap().getOrDefault("rcRankCoefficient", 1.0D);
|
|
|
double bidCoefficient = item.getScoreMap().getOrDefault("bidCoefficient", 1.0D);
|
|
|
- double ecpm = modelCtcvrScore * bid * fRankCoefficient * rcRankCoefficient * bidCoefficient * 1000;
|
|
|
|
|
|
- double h5Weight = 1.0D;
|
|
|
+
|
|
|
+ // h5 降权
|
|
|
+ double h5Weight = 1;
|
|
|
if (openH5 > 0) {
|
|
|
h5Weight = this.getH5SuppressWeight(item);
|
|
|
}
|
|
|
- double flowCtlC = item.getScoreMap().getOrDefault("flowCtlC", 1.0D);
|
|
|
- double flowCtlA = item.getScoreMap().getOrDefault("flowCtlA", 1.0D);
|
|
|
- double guaranteeScoreCoefficient = getGuaranteeScoreCoefficient(isGuaranteedFlow, item.getExt());
|
|
|
- double score = item.getLrScore() * bid * 1000;
|
|
|
|
|
|
- // 没有转化回传的广告主沿用配置 CPM,避免以不可靠的模型预估参与竞价。
|
|
|
- if (noApiAdVerIds.contains(item.getAdVerId())) {
|
|
|
- ecpm = item.getCpm();
|
|
|
- score = item.getCpm() * cpmCoefficient / 1000;
|
|
|
- }
|
|
|
-
|
|
|
- // scoreMap 是排序日志的数据源;写入 eCPM 分子及实际参与 score 计算的全部外围因子。
|
|
|
- item.getScoreMap().put("modelCtcvrScore", modelCtcvrScore);
|
|
|
+ // 控制曝光权重
|
|
|
+ Map<String, Map<String, String>> cidFeature = allCidFeature.getOrDefault(String.valueOf(item.getAdId()), EMPTY_NESTED_MAP);
|
|
|
+ Map<String, String> b3Feature = cidFeature.getOrDefault("alg_cid_feature_cid_action", EMPTY_STRING_MAP);
|
|
|
+ double expWeight = getExpWeight(b3Feature,
|
|
|
+ expOldKey, expOldThreshold,
|
|
|
+ expNewKey, expNewThreshold,
|
|
|
+ expLowerWeight, expUpperWeight, expScale);
|
|
|
+
|
|
|
+ // 控制流量权重
|
|
|
+ double flowCtlC = item.getScoreMap().getOrDefault("flowCtlC", 1.0);
|
|
|
+ double flowCtlA = item.getScoreMap().getOrDefault("flowCtlA", 1.0);
|
|
|
+ double kFinal = item.getScoreMap().getOrDefault("kFinal", 1.0);
|
|
|
+
|
|
|
+ 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 = flowCtlC * flowCtlA * h5Weight * expWeight * item.getLrScore() * bid * scoreCoefficient * guaranteeScoreCoefficient * layerAndCreativeWeight * kFinal;
|
|
|
+ item.getScoreMap().put("guaranteeScoreCoefficient", guaranteeScoreCoefficient);
|
|
|
+ item.getScoreMap().put("cpa", item.getCpa());
|
|
|
+ item.getScoreMap().put("cpm", item.getCpm());
|
|
|
item.getScoreMap().put("bid", bid);
|
|
|
+ item.getScoreMap().put("cpmCoefficient", cpmCoefficient);
|
|
|
+ item.getScoreMap().put("scoreCoefficient", scoreCoefficient);
|
|
|
+ item.getScoreMap().put("h5", h5Weight);
|
|
|
item.getScoreMap().put("fRankCoefficient", fRankCoefficient);
|
|
|
item.getScoreMap().put("rcRankCoefficient", rcRankCoefficient);
|
|
|
item.getScoreMap().put("bidCoefficient", bidCoefficient);
|
|
|
item.getScoreMap().put("ecpm", ecpm);
|
|
|
- item.getScoreMap().put("flowCtlC", flowCtlC);
|
|
|
- item.getScoreMap().put("flowCtlA", flowCtlA);
|
|
|
- item.getScoreMap().put("h5", h5Weight);
|
|
|
- item.getScoreMap().put("guaranteeScoreCoefficient", guaranteeScoreCoefficient);
|
|
|
- item.getScoreMap().put("score", score);
|
|
|
-
|
|
|
-
|
|
|
- if (isGuaranteedFlow && item.getExt().get("isGuaranteed") != null && (boolean) item.getExt().get("isGuaranteed")) {
|
|
|
- isGuaranteeType = true;
|
|
|
- }
|
|
|
- item.getScoreMap().put("cpa", item.getCpa());
|
|
|
- item.getScoreMap().put("cpm", item.getCpm());
|
|
|
item.getFeatureMap().putAll(userFeatureMap);
|
|
|
item.getFeatureMap().putAll(sceneFeatureMap);
|
|
|
+
|
|
|
+ // 没有转化回传的广告主,使用后台配置的CPM
|
|
|
+ if (noApiAdVerIds.contains(item.getAdVerId())) {
|
|
|
+ score = item.getCpm() * cpmCoefficient / 1000;
|
|
|
+ }
|
|
|
item.setScore(score);
|
|
|
}
|
|
|
|
|
|
@@ -418,10 +465,17 @@ public class RankStrategyBy886 extends RankStrategyBasic {
|
|
|
participateCompetitionType.add("guarantee");
|
|
|
}
|
|
|
top1Item.getExt().put("participateCompetitionType", StringUtils.join(participateCompetitionType, ","));
|
|
|
- top1Item.getExt().put("ecpm", top1Item.getScoreMap().get("ecpm"));
|
|
|
+ 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);
|
|
|
String filterEcpmValue = paramsMap.getOrDefault("filterEcpm", filterEcpm);
|
|
|
top1Item.getExt().put("filterEcpm", filterEcpmValue);
|
|
|
+ if (noApiAdVerIds.contains(top1Item.getAdVerId())) {
|
|
|
+ top1Item.getExt().put("ecpm", top1Item.getCpm());
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ top1Item.getExt().put("ecpm", modelCtcvrScore * top1Item.getCpa() * 1000);
|
|
|
}
|
|
|
putMetaFeature(top1Item, feature, reqFeature, sceneFeatureMap, request);
|
|
|
top1Item.getExt().put("model", logModelName);
|
|
|
@@ -437,10 +491,6 @@ public class RankStrategyBy886 extends RankStrategyBasic {
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
- private double normalizeCoefficient(Number coefficient) {
|
|
|
- return coefficient == null ? 1.0D : coefficient.doubleValue();
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* 获取人群分层和创意的权重
|
|
|
*
|
|
|
@@ -1075,4 +1125,31 @@ public class RankStrategyBy886 extends RankStrategyBasic {
|
|
|
return Math.min(Math.max(lowerWeight, weight), upperWeight);
|
|
|
}
|
|
|
|
|
|
+ private void calibrationCidCtcvr(List<AdRankItem> items, String modelName, Map<String, String> reqFeature,
|
|
|
+ double minValid, double maxValid,
|
|
|
+ double minVal, double maxVal) {
|
|
|
+ try {
|
|
|
+ String layer = reqFeature.get("layer_l4");
|
|
|
+ for (AdRankItem item : items) {
|
|
|
+ String cid = String.valueOf(item.getAdId());
|
|
|
+ Double diff = CreativeUserLayerDataHelper.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.setLrScore(ctcvr);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("calibCidScore error", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private double normalizeCoefficient(Number coefficient) {
|
|
|
+ return coefficient == null ? 1.0D : coefficient.doubleValue();
|
|
|
+ }
|
|
|
+
|
|
|
}
|