File size: 6,067 Bytes
1bc7a0c 330f0b8 1bc7a0c 0fab24c 1bc7a0c 330f0b8 1bc7a0c 330f0b8 1bc7a0c 330f0b8 1bc7a0c 330f0b8 1bc7a0c 330f0b8 1bc7a0c 330f0b8 1bc7a0c 330f0b8 1bc7a0c 330f0b8 1bc7a0c 330f0b8 1bc7a0c 330f0b8 1bc7a0c 330f0b8 1bc7a0c 330f0b8 1bc7a0c 330f0b8 1bc7a0c 330f0b8 1bc7a0c 330f0b8 1bc7a0c 330f0b8 1bc7a0c 330f0b8 1bc7a0c 330f0b8 1bc7a0c 330f0b8 1bc7a0c 330f0b8 1bc7a0c 330f0b8 1bc7a0c 330f0b8 1bc7a0c 330f0b8 1bc7a0c 330f0b8 1bc7a0c 330f0b8 1bc7a0c |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
import trueskill as ts
import pandas as pd
import random
import datetime
from typing import Dict, List, Tuple, Union
import db
import pandas as pd
from typing import TypedDict
MU_init = ts.Rating().mu
SIGMA_init = ts.Rating().sigma
class Prompt(TypedDict):
id: int
name: str
text: str
class Arena:
"""
Une arène pour comparer et classer des prompts en utilisant l'algorithme TrueSkill.
"""
def init_estimates(self, reboot=True) -> None:
"""
Initialise les estimations des prompts avec des ratings TrueSkill par défaut.
reboot : si le fichier estimates.csv existe déjà, on le laisse tel quel.
"""
estimates = db.load("estimates")
if not estimates.empty and reboot:
return None
if estimates.empty:
for i in db.load("prompts")["id"].to_list():
db.insert(
"estimates",
{
"prompt_id": i,
"mu": MU_init,
"sigma": SIGMA_init,
},
)
def load(self, table_name: str) -> pd.DataFrame:
"""
fonction back pour l'UI.
Charge les données d'une table depuis le fichier CSV.
"""
return db.load(table_name)
def replace(self, table_name: str, df: pd.DataFrame) -> pd.DataFrame:
"""
fonction back pour l'UI.
Remplace le contenu d'une table par les données du fichier CSV.
Pour l'admin uniquement
"""
return db.replace(table_name, df)
def select_match(self) -> Tuple[Prompt, Prompt]:
"""
Sélectionne deux prompts pour un match en privilégiant ceux avec une grande incertitude.
Returns:
Un tuple contenant les IDs des deux prompts à comparer (prompt_a, prompt_b)
"""
# le prompt le plus incertain (sigma le plus élevé)
estimates = db.load("estimates")
estimate_a = estimates.sort_values(by="sigma", ascending=False).iloc[0]
# le prompt le plus proche en niveau (mu) du prompt_a
estimate_b = (
estimates.loc[estimates["prompt_id"] != estimate_a["prompt_id"]]
.assign(delta_mu=lambda df_: abs(df_["mu"] - estimate_a["mu"]))
.sort_values(by="delta_mu", ascending=True)
.iloc[0]
)
prompts = db.load("prompts")
prompt_a = prompts.query(f"id == {estimate_a['prompt_id']}").iloc[0].to_dict()
prompt_b = prompts.query(f"id == {estimate_b['prompt_id']}").iloc[0].to_dict()
# We need to update the selection strategy to prefer prompts with high uncertainty
# but also consider prompts that are close in ranking (within 5 positions)
# Create pairs of prompts that are at most 5 positions apart in the ranking
# close_pairs = []
# for i in range(len(prompt_ids)):
# for j in range(i + 1, min(i + 6, len(prompt_ids))):
# close_pairs.append((prompt_ids[i], prompt_ids[j]))
return prompt_a, prompt_b
def record_result(self, winner_id: str, loser_id: str) -> None:
# Obtenir les ratings actuels
estimates = db.load("estimates")
winner_estimate = (
estimates[estimates["prompt_id"] == winner_id].iloc[0].to_dict()
)
loser_estimate = estimates[estimates["prompt_id"] == loser_id].iloc[0].to_dict()
winner_rating = ts.Rating(winner_estimate["mu"], winner_estimate["sigma"])
loser_rating = ts.Rating(loser_estimate["mu"], loser_estimate["sigma"])
winner_new_rating, loser_new_rating = ts.rate_1vs1(winner_rating, loser_rating)
db.update(
"estimates",
winner_estimate["id"],
{"mu": winner_new_rating.mu, "sigma": winner_new_rating.sigma},
)
db.update(
"estimates",
loser_estimate["id"],
{"mu": loser_new_rating.mu, "sigma": loser_new_rating.sigma},
)
db.insert(
"votes",
{
"winner_id": winner_id,
"loser_id": loser_id,
# "timestamp": datetime.datetime.now().isoformat(),
},
)
return None
def get_rankings(self) -> pd.DataFrame:
"""
Obtient le classement actuel des prompts.
Returns:
Liste de dictionnaires contenant le classement de chaque prompt avec
ses informations (rang, id, texte, mu, sigma, score)
"""
prompts = db.load("prompts")
estimates = db.load("estimates").drop(columns=["id"])
rankings = prompts.merge(estimates, left_on="id", right_on="prompt_id").drop(
columns=["id", "prompt_id"]
)
return rankings.sort_values(by="mu", ascending=False)
# eventuellement afficher plutôt mu - 3 sigma pour être conservateur
def get_progress(self) -> str:
"""
Renvoie des statistiques sur la progression du tournoi.
Returns:
Dictionnaire contenant des informations sur la progression:
- total_prompts: nombre total de prompts
- total_matches: nombre total de matchs joués
- avg_sigma: incertitude moyenne des ratings
- progress: pourcentage estimé de progression du tournoi
- estimated_remaining_matches: estimation du nombre de matchs restants
"""
prompts = db.load("prompts")
estimates = db.load("estimates")
votes = db.load("votes")
avg_sigma = estimates["sigma"].mean()
# Estimer quel pourcentage du tournoi est complété
# En se basant sur la réduction moyenne de sigma par rapport à la valeur initiale
initial_sigma = ts.Rating().sigma
progress = min(100, max(0, (1 - avg_sigma / initial_sigma) * 100))
msg = f"""{len(prompts)} propositions à départager
{len(votes)} matchs joués
{avg_sigma:.2f} d'incertitude moyenne"""
return msg
|