hamverbot commited on
Commit
945356f
·
verified ·
1 Parent(s): cc95cbf

Upload train.py

Browse files
Files changed (1) hide show
  1. train.py +161 -0
train.py ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ RTB Bidding Algorithm Comparison on Real Criteo Data
3
+ """
4
+ import numpy as np
5
+ import pandas as pd
6
+ import json
7
+ from datasets import load_dataset
8
+ from sklearn.linear_model import LogisticRegression
9
+ from sklearn.ensemble import GradientBoostingRegressor
10
+ from sklearn.model_selection import train_test_split
11
+
12
+ print("="*60)
13
+ print("RTB BIDDING ON REAL CRITEO DATA")
14
+ print("="*60)
15
+
16
+ # Load Criteo_x4 dataset
17
+ ds = load_dataset("reczoo/Criteo_x4", split="train", streaming=True)
18
+ rows = []
19
+ for i, row in enumerate(ds):
20
+ if i >= 100000:
21
+ break
22
+ rows.append(row)
23
+ df = pd.DataFrame(rows)
24
+ print(f"Loaded {len(df)} rows, CTR: {df['Label'].mean():.4f}")
25
+
26
+ # Feature prep
27
+ sparse_cols = [f"C{i}" for i in range(1, 27)]
28
+ dense_cols = [f"I{i}" for i in range(1, 14)]
29
+
30
+ for col in dense_cols:
31
+ df[col] = df[col].fillna(df[col].median())
32
+ for col in sparse_cols:
33
+ df[col] = df[col].fillna("MISSING")
34
+ vocab = {v: i+1 for i, v in enumerate(df[col].unique())}
35
+ df[col] = df[col].map(vocab)
36
+
37
+ for col in dense_cols:
38
+ df[col] = (df[col] - df[col].mean()) / (df[col].std() + 1e-8)
39
+
40
+ feature_cols = dense_cols + sparse_cols
41
+ X = df[feature_cols].values
42
+ y = df['Label'].values
43
+
44
+ # Simulate market prices
45
+ np.random.seed(42)
46
+ mu_price = 1.0 + 0.02 * X[:, 0] + 0.01 * X[:, 1]
47
+ market_price = np.random.lognormal(mu_price, 0.6)
48
+ print(f"Market price mean: {market_price.mean():.2f}")
49
+
50
+ # Train/test split
51
+ train_idx, test_idx = train_test_split(range(len(df)), test_size=0.2, random_state=42)
52
+ X_train, X_test = X[train_idx], X[test_idx]
53
+ y_train, y_test = y[train_idx], y[test_idx]
54
+ price_train, price_test = market_price[train_idx], market_price[test_idx]
55
+
56
+ # CTR model
57
+ print("\nTraining CTR model...")
58
+ lr = LogisticRegression(max_iter=200, C=0.1)
59
+ lr.fit(X_train, y_train)
60
+ pctr = lr.predict_proba(X_test)[:, 1]
61
+ print(f"pCTR range: [{pctr.min():.4f}, {pctr.max():.4f}], mean: {pctr.mean():.4f}")
62
+
63
+ # Price model
64
+ print("Training price model...")
65
+ price_model = GradientBoostingRegressor(n_estimators=50, max_depth=4)
66
+ price_model.fit(X_train, price_train)
67
+ price_pred = price_model.predict(X_test)
68
+ print(f"Price prediction MAE: {np.mean(np.abs(price_pred - price_test)):.2f}")
69
+
70
+ # Bidding algorithms
71
+ VALUE_PER_CLICK = 50.0
72
+
73
+ class LinearBid:
74
+ def __init__(self, base, avg_pctr):
75
+ self.base = base; self.avg = avg_pctr
76
+ def bid(self, pctr, **kw):
77
+ return self.base * (pctr / self.avg)
78
+
79
+ class ORTB:
80
+ def __init__(self, lam, c):
81
+ self.lam = lam; self.c = c
82
+ def bid(self, pctr, **kw):
83
+ return np.sqrt((self.c / self.lam) * pctr + self.c**2) - self.c
84
+
85
+ class DualOGD:
86
+ def __init__(self, budget, T, vpc=50, eps=None):
87
+ self.B = budget; self.T = T; self.rho = budget / T
88
+ self.vpc = vpc; self.eps = eps or 1.0 / np.sqrt(T)
89
+ self.lam = 0.0; self.spent = 0.0; self.t = 0
90
+ def bid(self, pctr, win_prob, **kw):
91
+ self.t += 1
92
+ rem = self.B - self.spent
93
+ if rem <= 0: return 0.0
94
+ v = pctr * self.vpc
95
+ max_b = min(v * 2, rem)
96
+ if max_b <= 0: return 0.0
97
+ bids = np.linspace(0.5, max_b, 40)
98
+ rewards = [(v - b) * win_prob(b) - self.lam * b * win_prob(b) for b in bids]
99
+ return float(bids[np.argmax(rewards)])
100
+ def update(self, cost):
101
+ self.spent += cost
102
+ self.lam = max(0.0, self.lam - self.eps * (self.rho - cost))
103
+
104
+ class Threshold:
105
+ def __init__(self, th, val):
106
+ self.th = th; self.val = val
107
+ def bid(self, pctr, **kw):
108
+ return self.val if pctr > self.th else 0.0
109
+
110
+ def simulate(algo, pctr, prices, clicks, budget, T):
111
+ spent = 0.0
112
+ clicks_got = 0
113
+ imp = 0
114
+ for i in range(min(T, len(pctr))):
115
+ if spent >= budget: break
116
+ def wp(b):
117
+ if b <= 0: return 0.0
118
+ return 1.0 / (1.0 + np.exp(-(b - prices[i]) / (prices[i] * 0.5)))
119
+ if isinstance(algo, DualOGD):
120
+ b = algo.bid(pctr[i], wp)
121
+ else:
122
+ b = algo.bid(pctr[i])
123
+ if b >= prices[i] and spent + b <= budget:
124
+ spent += b; imp += 1; clicks_got += int(clicks[i])
125
+ if isinstance(algo, DualOGD):
126
+ algo.update(float(b) if b >= prices[i] else 0.0)
127
+ return {
128
+ 'clicks': int(clicks_got),
129
+ 'impressions': int(imp),
130
+ 'spent': float(spent),
131
+ 'budget': float(budget),
132
+ 'ctr': float(clicks_got / max(imp, 1)),
133
+ 'budget_used': float(spent / budget),
134
+ 'cpc': float(spent / max(clicks_got, 1))
135
+ }
136
+
137
+ # Run comparison
138
+ budget = 5000; T = 10000
139
+ avg_pctr = float(pctr.mean())
140
+
141
+ algos = {
142
+ 'Linear': LinearBid(20, avg_pctr),
143
+ 'ORTB': ORTB(0.002, 8),
144
+ 'DualOGD': DualOGD(budget, T, VALUE_PER_CLICK),
145
+ 'Threshold': Threshold(0.3, 30)
146
+ }
147
+
148
+ print("\n" + "="*60)
149
+ print("BIDDING ALGORITHM COMPARISON ON REAL CRITEO DATA")
150
+ print("="*60)
151
+
152
+ results = {}
153
+ for name, algo in algos.items():
154
+ results[name] = simulate(algo, pctr, price_test, y_test, budget, T)
155
+ r = results[name]
156
+ print(f"{name:12} Clicks:{r['clicks']:4} CTR:{r['ctr']:.4f} Budget:{r['budget_used']:.2%} CPC:{r['cpc']:.2f}")
157
+
158
+ with open('results_real.json', 'w') as f:
159
+ json.dump(results, f, indent=2)
160
+
161
+ print("\nSaved to results_real.json")