Spaces:
Sleeping
Sleeping
Update app/bandit.py
Browse files- app/bandit.py +10 -19
app/bandit.py
CHANGED
|
@@ -5,26 +5,20 @@ from . import storage
|
|
| 5 |
|
| 6 |
class ThompsonBandit:
|
| 7 |
"""
|
| 8 |
-
CTR
|
| 9 |
-
|
| 10 |
-
季節性は CTR 側の仮想カウントで補正。
|
| 11 |
"""
|
| 12 |
def __init__(self, campaign_id: str, seasonality_boost: float = 5.0):
|
| 13 |
self.campaign_id = campaign_id
|
| 14 |
self.seasonality_boost = seasonality_boost
|
| 15 |
|
| 16 |
-
def sample_arm(
|
| 17 |
-
|
| 18 |
-
context: Dict[str, Any],
|
| 19 |
-
seasonal_fn: Optional[Callable[[Dict[str, Any]], float]] = None, # ← キーワード引数OK
|
| 20 |
-
) -> Tuple[Optional[str], float]:
|
| 21 |
metrics = storage.get_metrics(self.campaign_id)
|
| 22 |
if not metrics:
|
| 23 |
return None, -1.0
|
| 24 |
-
|
| 25 |
vpc = storage.get_campaign_value_per_conversion(self.campaign_id)
|
| 26 |
|
| 27 |
-
# 季節性スコア s ∈ (0, 1)
|
| 28 |
s = 0.5
|
| 29 |
if seasonal_fn is not None:
|
| 30 |
try:
|
|
@@ -33,21 +27,18 @@ class ThompsonBandit:
|
|
| 33 |
except Exception:
|
| 34 |
s = 0.5
|
| 35 |
|
| 36 |
-
best_score,
|
| 37 |
-
for
|
| 38 |
-
ac, bc = float(
|
| 39 |
-
av, bv = float(
|
| 40 |
-
|
| 41 |
-
# 季節性でクリック側の事前分布を微調整
|
| 42 |
ac_eff = ac + self.seasonality_boost * s
|
| 43 |
bc_eff = bc + self.seasonality_boost * (1.0 - s)
|
| 44 |
-
|
| 45 |
pc = random.betavariate(max(1e-6, ac_eff), max(1e-6, bc_eff))
|
| 46 |
pv = random.betavariate(max(1e-6, av), max(1e-6, bv))
|
| 47 |
score = pc * pv * vpc
|
| 48 |
if score > best_score:
|
| 49 |
-
best_score,
|
| 50 |
-
return
|
| 51 |
|
| 52 |
@staticmethod
|
| 53 |
def update_with_event(campaign_id: str, variant_id: str, event_type: str):
|
|
|
|
| 5 |
|
| 6 |
class ThompsonBandit:
|
| 7 |
"""
|
| 8 |
+
CTRとCVRの二段ベータ。EV=pc*pv*V。
|
| 9 |
+
季節性はクリック側の事前にブースト。
|
|
|
|
| 10 |
"""
|
| 11 |
def __init__(self, campaign_id: str, seasonality_boost: float = 5.0):
|
| 12 |
self.campaign_id = campaign_id
|
| 13 |
self.seasonality_boost = seasonality_boost
|
| 14 |
|
| 15 |
+
def sample_arm(self, context: Dict[str, Any], seasonal_fn: Optional[Callable[[Dict[str, Any]], float]] = None
|
| 16 |
+
) -> Tuple[Optional[str], float]:
|
|
|
|
|
|
|
|
|
|
| 17 |
metrics = storage.get_metrics(self.campaign_id)
|
| 18 |
if not metrics:
|
| 19 |
return None, -1.0
|
|
|
|
| 20 |
vpc = storage.get_campaign_value_per_conversion(self.campaign_id)
|
| 21 |
|
|
|
|
| 22 |
s = 0.5
|
| 23 |
if seasonal_fn is not None:
|
| 24 |
try:
|
|
|
|
| 27 |
except Exception:
|
| 28 |
s = 0.5
|
| 29 |
|
| 30 |
+
best_score, best_vid = -1.0, None
|
| 31 |
+
for r in metrics:
|
| 32 |
+
ac, bc = float(r["alpha_click"]), float(r["beta_click"])
|
| 33 |
+
av, bv = float(r["alpha_conv"]), float(r["beta_conv"])
|
|
|
|
|
|
|
| 34 |
ac_eff = ac + self.seasonality_boost * s
|
| 35 |
bc_eff = bc + self.seasonality_boost * (1.0 - s)
|
|
|
|
| 36 |
pc = random.betavariate(max(1e-6, ac_eff), max(1e-6, bc_eff))
|
| 37 |
pv = random.betavariate(max(1e-6, av), max(1e-6, bv))
|
| 38 |
score = pc * pv * vpc
|
| 39 |
if score > best_score:
|
| 40 |
+
best_score, best_vid = score, r["variant_id"]
|
| 41 |
+
return best_vid, best_score
|
| 42 |
|
| 43 |
@staticmethod
|
| 44 |
def update_with_event(campaign_id: str, variant_id: str, event_type: str):
|