rusticluftig commited on
Commit
e3a1176
Β·
1 Parent(s): 2d84b7d

Initial Commit

Browse files
Files changed (3) hide show
  1. README.md +3 -4
  2. app.py +273 -0
  3. requirements.txt +6 -0
README.md CHANGED
@@ -1,13 +1,12 @@
1
  ---
2
  title: 9 Leaderboard
3
  emoji: 🏒
4
- colorFrom: purple
5
- colorTo: pink
6
  sdk: gradio
7
- sdk_version: 4.16.0
8
  app_file: app.py
9
  pinned: false
10
- license: mit
11
  ---
12
 
13
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
  title: 9 Leaderboard
3
  emoji: 🏒
4
+ colorFrom: indigo
5
+ colorTo: blue
6
  sdk: gradio
7
+ sdk_version: 3.41.0
8
  app_file: app.py
9
  pinned: false
 
10
  ---
11
 
12
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,273 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import bittensor as bt
3
+ import typing
4
+ from bittensor.extrinsics.serving import get_metadata
5
+ from dataclasses import dataclass
6
+ import requests
7
+ import wandb
8
+ import math
9
+ import os
10
+ import datetime
11
+ import time
12
+ from dotenv import load_dotenv
13
+ from huggingface_hub import HfApi
14
+ from apscheduler.schedulers.background import BackgroundScheduler
15
+
16
+ load_dotenv()
17
+
18
+ FONT = """<link href="https://fonts.cdnfonts.com/css/jmh-typewriter" rel="stylesheet">"""
19
+ TITLE = """<h1 align="center" id="space-title" class="typewriter">Subnet 6 Leaderboard</h1>"""
20
+ #IMAGE = """<a href="https://discord.gg/jqVphNsB4H" target="_blank"><img src="https://i.ibb.co/88wyVQ7/nousgirl.png" alt="nousgirl" style="margin: auto; width: 20%; border: 0;" /></a>"""
21
+ HEADER = """<h2 align="center" class="typewriter"><a href="https://github.com/RaoFoundation/pretraining" target="_blank">Subnet 9</a> is a <a href="https://bittensor.com/" target="_blank">Bittensor</a> subnet that rewards miners for producing pretrained Foundation-Models on the <a href="https://huggingface.co/datasets/tiiuae/falcon-refinedweb" target="_blank">Falcon Refined Web dataset</a>. It acts like a continuous benchmark whereby miners are rewarded for attaining the best losses on randomly sampled pages of Falcon. The models with the best head-to-head loss on the evaluation data receive a steady emission of TAO.</h3>"""
22
+ EVALUATION_DETAILS = """<b>Name</b> is the πŸ€— Hugging Face model name (click to go to the model card). <b>Rewards / Day</b> are the expected rewards per day for each model. <b>Last Average Loss</b> is the last loss value on the evaluation data for the model as calculated by a validator (lower is better). <b>UID</b> is the Bittensor user id of the submitter. <b>Block</b> is the Bittensor block that the model was submitted in. More stats on <a href="https://taostats.io/subnets/netuid-6/" target="_blank">taostats</a>."""
23
+ EVALUATION_HEADER = """<h3 align="center">Shows the latest internal evaluation statistics as calculated by the Opentensor validator</h3>"""
24
+ VALIDATOR_WANDB_PROJECT = "opentensor-dev/pretraining-subnet"
25
+ H4_TOKEN = os.environ.get("H4_TOKEN", None)
26
+ API = HfApi(token=H4_TOKEN)
27
+ # TODO: Update.
28
+ REPO_ID = "RusticLuftig/9-leaderboard"
29
+ MAX_AVG_LOSS_POINTS = 1
30
+ RETRIES = 5
31
+ DELAY_SECS = 3
32
+ NETUID = 9
33
+ # TODO: Update this for SN 9.
34
+ SUBNET_START_BLOCK = 2225782
35
+ SECONDS_PER_BLOCK = 12
36
+
37
+ @dataclass
38
+ class ModelData:
39
+ uid: int
40
+ hotkey: str
41
+ namespace: str
42
+ name: str
43
+ commit: str
44
+ hash: str
45
+ block: int
46
+ incentive: float
47
+ emission: float
48
+
49
+ @classmethod
50
+ def from_compressed_str(cls, uid: int, hotkey: str, cs: str, block: int, incentive: float, emission: float):
51
+ """Returns an instance of this class from a compressed string representation"""
52
+ tokens = cs.split(":")
53
+ return ModelData(
54
+ uid=uid,
55
+ hotkey=hotkey,
56
+ namespace=tokens[0],
57
+ name=tokens[1],
58
+ commit=tokens[2] if tokens[2] != "None" else None,
59
+ hash=tokens[3] if tokens[3] != "None" else None,
60
+ block=block,
61
+ incentive=incentive,
62
+ emission=emission
63
+ )
64
+
65
+ def run_with_retries(func, *args, **kwargs):
66
+ for i in range(0, RETRIES):
67
+ try:
68
+ return func(*args, **kwargs)
69
+ except:
70
+ if i == RETRIES - 1:
71
+ raise
72
+ time.sleep(DELAY_SECS)
73
+ raise RuntimeError("Should never happen")
74
+
75
+ def get_subtensor_and_metagraph() -> typing.Tuple[bt.subtensor, bt.metagraph]:
76
+ def _internal() -> typing.Tuple[bt.subtensor, bt.metagraph]:
77
+ subtensor: bt.subtensor = bt.subtensor("finney")
78
+ metagraph: bt.metagraph = bt.metagraph(9, lite=False)
79
+ return subtensor, metagraph
80
+ return run_with_retries(_internal)
81
+
82
+ def get_tao_price() -> float:
83
+ return run_with_retries(lambda: float(requests.get("https://api.kucoin.com/api/v1/market/stats?symbol=TAO-USDT").json()["data"]["last"]))
84
+
85
+ def get_validator_weights(metagraph: bt.metagraph) -> typing.Dict[int, typing.Tuple[float, int, typing.Dict[int, float]]]:
86
+ ret = {}
87
+ for uid in metagraph.uids.tolist():
88
+ vtrust = metagraph.validator_trust[uid].item()
89
+ if vtrust > 0:
90
+ ret[uid] = (vtrust, metagraph.S[uid].item(), {})
91
+ for ouid in metagraph.uids.tolist():
92
+ if ouid == uid:
93
+ continue
94
+ weight = round(metagraph.weights[uid][ouid].item(), 4)
95
+ if weight > 0:
96
+ ret[uid][-1][ouid] = weight
97
+ return ret
98
+
99
+ def get_subnet_data(subtensor: bt.subtensor, metagraph: bt.metagraph) -> typing.List[ModelData]:
100
+ result = []
101
+ for uid in metagraph.uids.tolist():
102
+ hotkey = metagraph.hotkeys[uid]
103
+ metadata = get_metadata(subtensor, metagraph.netuid, hotkey)
104
+ if not metadata:
105
+ continue
106
+
107
+ commitment = metadata["info"]["fields"][0]
108
+ hex_data = commitment[list(commitment.keys())[0]][2:]
109
+ chain_str = bytes.fromhex(hex_data).decode()
110
+ block = metadata["block"]
111
+ incentive = metagraph.incentive[uid].nan_to_num().item()
112
+ emission = metagraph.emission[uid].nan_to_num().item() * 20 # convert to daily TAO
113
+
114
+ model_data = None
115
+ try:
116
+ model_data = ModelData.from_compressed_str(uid, hotkey, chain_str, block, incentive, emission)
117
+ except:
118
+ continue
119
+
120
+ result.append(model_data)
121
+ return result
122
+
123
+ def is_floatable(x) -> bool:
124
+ return (isinstance(x, float) and not math.isnan(x) and not math.isinf(x)) or isinstance(x, int)
125
+
126
+ def get_float_score(key: str, history) -> typing.Tuple[typing.Optional[float], bool]:
127
+ if key in history:
128
+ data = list(history[key])
129
+ if len(data) > 0:
130
+ if is_floatable(data[-1]):
131
+ return float(data[-1]), True
132
+ else:
133
+ data = [float(x) for x in data if is_floatable(x)]
134
+ if len(data) > 0:
135
+ return float(data[-1]), False
136
+ return None, False
137
+
138
+ def get_scores(uids: typing.List[int]) -> typing.Dict[int, typing.Dict[str, typing.Optional[float]]]:
139
+ api = wandb.Api()
140
+ runs = list(api.runs(VALIDATOR_WANDB_PROJECT),
141
+ filters={
142
+ "type": "validator",
143
+ "uid": 238
144
+ })
145
+
146
+ result = {}
147
+ for run in runs:
148
+ history = run.history()
149
+ for uid in uids:
150
+ if uid in result:
151
+ continue
152
+ avg_loss, avg_loss_fresh = get_float_score(f"uid_data.{uid}", history)
153
+ win_rate, win_rate_fresh = get_float_score(f"win_rate_data.{uid}", history)
154
+ win_total, win_total_fresh = get_float_score(f"win_total_data.{uid}", history)
155
+ weight, weight_fresh = get_float_score(f"weight_data.{uid}", history)
156
+ result[uid] = {
157
+ "avg_loss": avg_loss,
158
+ "win_rate": win_rate,
159
+ "win_total": win_total,
160
+ "weight": weight,
161
+ "fresh": avg_loss_fresh and win_rate_fresh and win_total_fresh
162
+ }
163
+ if len(result.keys()) == len(uids):
164
+ break
165
+ return result
166
+
167
+ def format_score(uid: int, scores, key) -> typing.Optional[float]:
168
+ if uid in scores:
169
+ if key in scores[uid]:
170
+ point = scores[uid][key]
171
+ if is_floatable(point):
172
+ return round(scores[uid][key], 4)
173
+ return None
174
+
175
+ def next_tempo(start_block: int, tempo: int, block: int) -> int:
176
+ start_num = start_block + tempo
177
+ intervals = (block - start_num) // tempo
178
+ nearest_num = start_num + ((intervals + 1) * tempo)
179
+ return nearest_num
180
+
181
+ def get_next_update_div(current_block: int, next_update_block: int) -> str:
182
+ now = datetime.datetime.now()
183
+ blocks_to_go = next_update_block - current_block
184
+ next_update_time = now + datetime.timedelta(seconds=blocks_to_go * SECONDS_PER_BLOCK)
185
+ delta = next_update_time - now
186
+ return f"""<div align="center" style="font-size: larger;">Next reward update: <b>{blocks_to_go}</b> blocks (~{int(delta.total_seconds() // 60)} minutes)</div>"""
187
+
188
+ subtensor, metagraph = get_subtensor_and_metagraph()
189
+
190
+ tao_price = get_tao_price()
191
+
192
+ leaderboard_df = get_subnet_data(subtensor, metagraph)
193
+ leaderboard_df.sort(key=lambda x: x.incentive, reverse=True)
194
+
195
+ scores = get_scores([x.uid for x in leaderboard_df])
196
+
197
+ current_block = metagraph.block.item()
198
+ next_update = next_tempo(
199
+ SUBNET_START_BLOCK,
200
+ subtensor.get_subnet_hyperparameters(NETUID).tempo,
201
+ current_block
202
+ )
203
+
204
+ validator_df = get_validator_weights(metagraph)
205
+ weight_keys = set()
206
+ for uid, stats in validator_df.items():
207
+ weight_keys.update(stats[-1].keys())
208
+
209
+ def leaderboard_data(show_stale: bool):
210
+ value = [
211
+ [
212
+ f'[{c.namespace}/{c.name} ({c.commit[0:8]})](https://huggingface.co/{c.namespace}/{c.name}/commit/{c.commit})',
213
+ format_score(c.uid, scores, "win_rate"),
214
+ format_score(c.uid, scores, "avg_loss"),
215
+ format_score(c.uid, scores, "weight"),
216
+ c.uid,
217
+ c.block
218
+ ] for c in leaderboard_df if scores[c.uid]["fresh"] or show_stale
219
+ ]
220
+ return value
221
+
222
+ demo = gr.Blocks(css=".typewriter {font-family: 'JMH Typewriter', sans-serif;}")
223
+ with demo:
224
+ gr.HTML(FONT)
225
+ gr.HTML(TITLE)
226
+ #gr.HTML(IMAGE)
227
+ gr.HTML(HEADER)
228
+
229
+ gr.HTML(value=get_next_update_div(current_block, next_update))
230
+
231
+ gr.Label(
232
+ value={ f"{c.namespace}/{c.name} ({c.commit[0:8]}) Β· ${round(c.emission * tao_price, 2):,} (Ο„{round(c.emission, 2):,})": c.incentive for c in leaderboard_df if c.incentive},
233
+ num_top_classes=10,
234
+ )
235
+
236
+ with gr.Accordion("Evaluation Stats"):
237
+ gr.HTML(EVALUATION_HEADER)
238
+ show_stale = gr.Checkbox(label="Show Stale", interactive=True)
239
+ leaderboard_table = gr.components.Dataframe(
240
+ value=leaderboard_data(show_stale.value),
241
+ headers=["Name", "Win Rate", "Average Loss", "Weight", "UID", "Block"],
242
+ datatype=["markdown", "number", "number", "number", "number", "number"],
243
+ elem_id="leaderboard-table",
244
+ interactive=False,
245
+ visible=True,
246
+ )
247
+ gr.HTML(EVALUATION_DETAILS)
248
+ show_stale.change(leaderboard_data, [show_stale], leaderboard_table)
249
+
250
+ with gr.Accordion("Validator Stats"):
251
+ validator_table = gr.components.Dataframe(
252
+ value=[
253
+ [uid, int(validator_df[uid][1]), round(validator_df[uid][0], 4)] + [validator_df[uid][-1].get(c.uid) for c in leaderboard_df if c.incentive]
254
+ for uid, _ in sorted(
255
+ zip(validator_df.keys(), [validator_df[x][1] for x in validator_df.keys()]),
256
+ key=lambda x: x[1],
257
+ reverse=True
258
+ )
259
+ ],
260
+ headers=["UID", "Stake (Ο„)", "V-Trust"] + [f"{c.namespace}/{c.name} ({c.commit[0:8]})" for c in leaderboard_df if c.incentive],
261
+ datatype=["number", "number", "number"] + ["number" for c in leaderboard_df if c.incentive],
262
+ interactive=False,
263
+ visible=True,
264
+ )
265
+
266
+ def restart_space():
267
+ API.restart_space(repo_id=REPO_ID, token=H4_TOKEN)
268
+
269
+ scheduler = BackgroundScheduler()
270
+ scheduler.add_job(restart_space, "interval", seconds=60 * 15) # restart every 15 minutes
271
+ scheduler.start()
272
+
273
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ bittensor==6.7.0
2
+ requests==2.31.0
3
+ wandb==0.16.2
4
+ python-dotenv==1.0.1
5
+ APScheduler==3.10.1
6
+ huggingface-hub>=0.18.0