Files changed (7) hide show
  1. .gitattributes +35 -0
  2. .gitignore +0 -3
  3. api.py +0 -135
  4. app.py +0 -140
  5. competitions.py +0 -20
  6. requirements.txt +0 -11
  7. utils.py +0 -564
.gitattributes ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
.gitignore DELETED
@@ -1,3 +0,0 @@
1
- .venv
2
- __pycache__/
3
- .env
 
 
 
 
api.py DELETED
@@ -1,135 +0,0 @@
1
-
2
- import atexit
3
- import datetime
4
-
5
- from flask import Flask, request, jsonify
6
- from apscheduler.schedulers.background import BackgroundScheduler
7
-
8
- import utils
9
-
10
- app = Flask(__name__)
11
-
12
- # Global variables (saves time on loading data)
13
- state_vars = None
14
- reload_timestamp = datetime.datetime.now().strftime('%D %T')
15
-
16
-
17
- def load_data(test=False):
18
- """
19
- Reload the state variables
20
- """
21
- global state_vars, reload_timestamp
22
- if test:
23
- state_vars = utils.test_load_state_vars()
24
- else:
25
- state_vars = utils.load_state_vars()
26
-
27
- reload_timestamp = datetime.datetime.now().strftime('%D %T')
28
-
29
- print(f'Reloaded data at {reload_timestamp}')
30
-
31
-
32
- def start_scheduler():
33
- scheduler = BackgroundScheduler()
34
- scheduler.add_job(func=load_data, trigger="interval", seconds=60*30)
35
- scheduler.start()
36
-
37
- # Shut down the scheduler when exiting the app
38
- atexit.register(lambda: scheduler.shutdown())
39
-
40
-
41
- @app.route('/', methods=['GET'])
42
- def home():
43
- return "Welcome to the Bittensor Pretraining Leaderboard API!"
44
-
45
-
46
- @app.route('/updated', methods=['GET'])
47
- def updated():
48
- return reload_timestamp
49
-
50
-
51
- @app.route('/benchmark', methods=['GET'])
52
- def benchmark():
53
- """
54
- Get the benchmarks and the timestamp
55
-
56
- Returns:
57
- - benchmarks: List of dicts (from pandas DataFrame)
58
- - benchmark_timestamp: String
59
- """
60
-
61
- benchmarks = state_vars.get("benchmarks", None)
62
- benchmark_timestamp = state_vars.get("benchmark_timestamp", None)
63
-
64
- return jsonify(
65
- {
66
- "benchmarks": benchmarks.to_dict(orient='records'),
67
- "benchmark_timestamp": benchmark_timestamp.strftime('%Y-%m-%d %H:%M:%S')
68
- }
69
- )
70
-
71
- @app.route('/metagraph', methods=['GET'])
72
- def metagraph():
73
- """
74
- Get the metagraph data
75
- Returns:
76
- - metagraph_data: List of dicts (from pandas DataFrame)
77
- """
78
-
79
- metagraph = state_vars["metagraph"]
80
-
81
- return jsonify(
82
- utils.make_metagraph_dataframe(metagraph).to_dict(orient='records')
83
- )
84
-
85
- @app.route('/leaderboard', methods=['GET'])
86
- def leaderboard():
87
- """
88
- Get the leaderboard data
89
- Returns:
90
- - leaderboard_data: List of dicts (from pandas DataFrame)
91
- """
92
-
93
- model_data = state_vars["model_data"]
94
- scores = state_vars["scores"]
95
- show_stale = request.args.get('show_stale')
96
- return jsonify(
97
- utils.leaderboard_data(model_data, scores, show_stale=show_stale)
98
- )
99
-
100
-
101
- @app.route('/loss', methods=['GET'])
102
- def loss():
103
- """
104
- Get the losses over time
105
- Returns:
106
- - losses_over_time: List of dicts (from pandas DataFrame)
107
- """
108
- vali_runs = state_vars["vali_runs"]
109
-
110
- return jsonify(
111
- utils.get_losses_over_time(vali_runs).to_dict(orient='records')
112
- )
113
-
114
-
115
- @app.route('/validator', methods=['GET'])
116
- def validator():
117
- """
118
- Get the validator data
119
- Returns:
120
- - validator_data: List of dicts (from pandas DataFrame)
121
- """
122
- model_data = state_vars["model_data"]
123
- validator_df = state_vars["validator_df"]
124
-
125
- return jsonify(
126
- utils.make_validator_dataframe(validator_df, model_data).to_dict(orient='records')
127
- )
128
-
129
-
130
- if __name__ == '__main__':
131
-
132
- load_data()
133
- start_scheduler()
134
-
135
- app.run(host='0.0.0.0', port=5000, debug=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app.py CHANGED
@@ -1,140 +0,0 @@
1
- # Code adapted from: https://huggingface.co/spaces/RaoFoundation/pretraining-leaderboard/blob/main/app.py
2
-
3
- import os
4
- import datetime
5
- from typing import Dict
6
- import gradio as gr
7
-
8
- from dotenv import load_dotenv
9
- from huggingface_hub import HfApi
10
- from apscheduler.schedulers.background import BackgroundScheduler
11
-
12
- import competitions
13
- import utils
14
-
15
- FONT = (
16
- """<link href="https://fonts.cdnfonts.com/css/jmh-typewriter" rel="stylesheet">"""
17
- )
18
- TITLE = """<h1 align="center" id="space-title" class="typewriter">Finetuning Subnet Leaderboard</h1>"""
19
- HEADER = """<h2 align="center" class="typewriter"><a href="https://github.com/macrocosm-os/finetuning" target="_blank">Finetuning</a> is a <a href="https://bittensor.com/" target="_blank">Bittensor</a> subnet that rewards miners for producing finetuned models in defined competitions. The model with the best head-to-head score in each competition receive a steady emission of TAO.</h3>"""
20
- EVALUATION_DETAILS = """<ul><li><b>Name:</b> the 🤗 Hugging Face model name (click to go to the model card)</li><li><b>Rewards / Day:</b> the expected rewards per day based on current ranking.</li><li><b>Last Average Loss:</b> the last loss value on the evaluation data for the model as calculated by a validator (lower is better)</li><li><b>UID:</b> the Bittensor UID of the miner</li><li><b>Block:</b> the Bittensor block that the model was submitted in</li></ul><br/>More stats on <a href="https://taostats.io/subnets/netuid-37/" target="_blank">taostats</a>."""
21
- EVALUATION_HEADER = """<h3 align="center">Shows the latest internal evaluation statistics as calculated by the Opentensor validator</h3>"""
22
-
23
- HF_REPO_ID = "macrocosm-os/finetuning-leaderboard"
24
- SECONDS_PER_BLOCK = 12
25
-
26
- load_dotenv()
27
-
28
- HF_TOKEN = os.environ.get("HF_TOKEN", None)
29
- API = HfApi(token=HF_TOKEN)
30
-
31
-
32
- def get_next_update_div(current_block: int, next_update_block: int) -> str:
33
- now = datetime.datetime.now()
34
- blocks_to_go = next_update_block - current_block
35
- next_update_time = now + datetime.timedelta(
36
- seconds=blocks_to_go * SECONDS_PER_BLOCK
37
- )
38
- delta = next_update_time - now
39
- 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>"""
40
-
41
-
42
- def get_last_updated_div() -> str:
43
- return f"""<div>Last Updated: {datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")} (UTC)</div>"""
44
-
45
-
46
- def restart_space():
47
- API.restart_space(repo_id=HF_REPO_ID, token=HF_TOKEN)
48
-
49
-
50
- def main():
51
- # To avoid leaderboard failures, infinitely try until we get all data
52
- # needed to populate the dashboard
53
-
54
- state_vars = utils.load_state_vars()
55
- model_data = state_vars["model_data"]
56
- vali_runs = state_vars["vali_runs"]
57
- scores = state_vars["scores"]
58
- validator_df = state_vars["validator_df"]
59
- benchmarks = state_vars.get("benchmarks", None)
60
- benchmark_timestamp = state_vars.get("benchmark_timestamp", None)
61
-
62
- demo = gr.Blocks(css=".typewriter {font-family: 'JMH Typewriter', sans-serif;}")
63
- with demo:
64
- gr.HTML(FONT)
65
- gr.HTML(TITLE)
66
- gr.HTML(HEADER)
67
-
68
- # TODO: Re-enable once ""SubtensorModule.BlocksSinceEpoch" not found" issue is resolved.
69
- # gr.HTML(value=get_next_update_div(current_block, next_epoch_block))
70
-
71
- # TODO: Figure out the best approach to showing the per competition rewards.
72
- gr.Label(
73
- value={
74
- f"{c.namespace}/{c.name} ({c.commit[0:8]}) · (τ{round(c.emission, 2):,})": c.incentive
75
- for c in model_data
76
- if c.incentive
77
- },
78
- num_top_classes=10,
79
- )
80
- if benchmarks is not None:
81
- with gr.Accordion("Top Model Benchmarks"):
82
- gr.components.Dataframe(benchmarks)
83
- gr.HTML("""<div>PPL computed using a stride of 512. See <a href='https://github.com/macrocosm-os/finetuning/blob/dev/scripts/run_benchmarks.py'>here</a> for the full code.</div>""")
84
- gr.HTML(f"""<div>Last Updated: {benchmark_timestamp.strftime("%Y-%m-%d %H:%M:%S")} (UTC)</div>""")
85
-
86
- with gr.Accordion("Evaluation Stats"):
87
- gr.HTML(EVALUATION_HEADER)
88
- show_stale = gr.Checkbox(label="Show Stale", interactive=True)
89
- competition_leaderboards = []
90
- # TODO: Dynamically generate per-competition leaderboards based on model_data.
91
- competition_details = competitions.COMPETITION_DETAILS[1]
92
- with gr.Accordion(f"{competition_details.name} competition"):
93
- gr.HTML(competition_details.html_description)
94
- competition_leaderboards.append(gr.components.Dataframe(
95
- value=utils.leaderboard_data(model_data, scores, show_stale.value),
96
- headers=["Name", "Win Rate", "Average Loss", "Weight", "UID", "Block"],
97
- datatype=["markdown", "number", "number", "number", "number", "number"],
98
- elem_id="leaderboard-table",
99
- interactive=False,
100
- visible=True,
101
- ))
102
- gr.HTML(EVALUATION_DETAILS)
103
- show_stale.change(
104
- lambda stale: utils.leaderboard_data(model_data, scores, stale),
105
- inputs=[show_stale],
106
- outputs=competition_leaderboards,
107
- )
108
-
109
- # TODO: Make this a multi-competition line plot
110
- gr.LinePlot(
111
- utils.get_losses_over_time(vali_runs),
112
- x="timestamp",
113
- x_title="Date",
114
- y="SN9_MODEL",
115
- y_title="Average Loss",
116
- tooltip="SN9_MODEL",
117
- interactive=True,
118
- visible=True,
119
- width=1024,
120
- title="Best Average Loss Over Time",
121
- )
122
-
123
- with gr.Accordion("Validator Stats"):
124
- gr.components.Dataframe(
125
- utils.make_validator_dataframe(validator_df, model_data),
126
- interactive=False,
127
- visible=True,
128
- )
129
- gr.HTML(value=get_last_updated_div())
130
-
131
- scheduler = BackgroundScheduler()
132
- scheduler.add_job(
133
- restart_space, "interval", seconds=60 * 30
134
- ) # restart every 15 minutes
135
- scheduler.start()
136
-
137
- demo.launch()
138
-
139
-
140
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
competitions.py DELETED
@@ -1,20 +0,0 @@
1
- from dataclasses import dataclass
2
- from typing import Dict
3
-
4
-
5
- @dataclass(frozen=True)
6
- class CompetitionDetails:
7
- # The display name of the competition.
8
- name: str
9
-
10
- # The HTML description of the competition.
11
- html_description: str
12
-
13
-
14
- # A map of competition IDs to HTML descriptions.
15
- COMPETITION_DETAILS: Dict[int, CompetitionDetails] = {
16
- 1: CompetitionDetails(
17
- name="SN9_MODEL",
18
- html_description="""<b>Competition ID 1</b><br/>Produce the best fine-tuned model from a Subnet 9 pretrained model. Models are evaluated using synthetic prompt/response data from Subnet 18.""",
19
- )
20
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
requirements.txt DELETED
@@ -1,11 +0,0 @@
1
- bittensor
2
- requests
3
- wandb==0.17.1
4
- numpy==1.26.4
5
- python-dotenv
6
- APScheduler
7
- huggingface-hub
8
- gradio
9
- pandas
10
- flask
11
-
 
 
 
 
 
 
 
 
 
 
 
 
utils.py DELETED
@@ -1,564 +0,0 @@
1
- import argparse
2
- import datetime
3
- import functools
4
- import json
5
- import math
6
- import os
7
- import time
8
- import traceback
9
- from collections import defaultdict
10
- from dataclasses import dataclass
11
- from typing import Any, Dict, List, Optional, Tuple
12
-
13
- import bittensor as bt
14
- import numpy as np
15
- import pandas as pd
16
- import wandb
17
- from bittensor.extrinsics.serving import get_metadata
18
- from dotenv import load_dotenv
19
- from wandb.apis.public.history import HistoryScan
20
-
21
- import competitions
22
-
23
- NETUID = 37
24
- DELAY_SECS = 3
25
- RETRIES = 3
26
-
27
- load_dotenv()
28
-
29
- WANDB_TOKEN = os.environ.get("WANDB_API_KEY", None)
30
- SUBTENSOR_ENDPOINT = os.environ.get("SUBTENSOR_ENDPOINT", None)
31
- VALIDATOR_WANDB_PROJECT = "rusticluftig/finetuning"
32
- BENCHMARK_WANDB_PROJECT = ""
33
- BENCHMARK_FLAG = os.environ.get("BENCHMARK_FLAG", None)
34
-
35
-
36
- @dataclass(frozen=True)
37
- class ModelData:
38
- uid: int
39
- hotkey: str
40
- competition_id: int
41
- namespace: str
42
- name: str
43
- commit: str
44
-
45
- # Hash of (hash(model) + hotkey)
46
- secure_hash: str
47
- block: int
48
- incentive: float
49
- emission: float
50
-
51
- @classmethod
52
- def from_compressed_str(
53
- cls,
54
- uid: int,
55
- hotkey: str,
56
- cs: str,
57
- block: int,
58
- incentive: float,
59
- emission: float,
60
- ):
61
- """Returns an instance of this class from a compressed string representation"""
62
- tokens = cs.split(":")
63
- return ModelData(
64
- uid=uid,
65
- hotkey=hotkey,
66
- namespace=tokens[0],
67
- name=tokens[1],
68
- commit=tokens[2],
69
- secure_hash=tokens[3],
70
- competition_id=int(tokens[4]),
71
- block=block,
72
- incentive=incentive,
73
- emission=emission,
74
- )
75
-
76
-
77
- def run_with_retries(func, *args, **kwargs):
78
- """Runs a provided function with retries in the event of a failure."""
79
- for i in range(0, RETRIES):
80
- try:
81
- return func(*args, **kwargs)
82
- except (Exception, RuntimeError):
83
- bt.logging.error(f"Failed to run function: {traceback.format_exc()}")
84
- if i == RETRIES - 1:
85
- raise
86
- time.sleep(DELAY_SECS)
87
- raise RuntimeError("Should never happen")
88
-
89
-
90
- def get_subtensor_and_metagraph() -> Tuple[bt.subtensor, bt.metagraph]:
91
- """Returns a subtensor and metagraph for the finetuning subnet."""
92
-
93
- def _internal() -> Tuple[bt.subtensor, bt.metagraph]:
94
- if SUBTENSOR_ENDPOINT:
95
- parser = argparse.ArgumentParser()
96
- bt.subtensor.add_args(parser)
97
- subtensor = bt.subtensor(
98
- config=bt.config(
99
- parser=parser,
100
- args=["--subtensor.chain_endpoint", SUBTENSOR_ENDPOINT],
101
- )
102
- )
103
- else:
104
- subtensor = bt.subtensor("finney")
105
-
106
- metagraph = subtensor.metagraph(NETUID, lite=False)
107
-
108
- return subtensor, metagraph
109
-
110
- return run_with_retries(_internal)
111
-
112
-
113
- def get_subnet_data(
114
- subtensor: bt.subtensor, metagraph: bt.metagraph
115
- ) -> List[ModelData]:
116
- result = []
117
- for uid in metagraph.uids.tolist():
118
- hotkey = metagraph.hotkeys[uid]
119
- metadata = None
120
- try:
121
- metadata = run_with_retries(
122
- functools.partial(get_metadata, subtensor, metagraph.netuid, hotkey)
123
- )
124
- except:
125
- print(f"Failed to get metadata for UID {uid}: {traceback.format_exc()}")
126
-
127
- if not metadata:
128
- continue
129
-
130
- commitment = metadata["info"]["fields"][0]
131
- hex_data = commitment[list(commitment.keys())[0]][2:]
132
- chain_str = bytes.fromhex(hex_data).decode()
133
- block = metadata["block"]
134
-
135
- incentive = np.nan_to_num(metagraph.incentive[uid]).item()
136
- emission = (
137
- np.nan_to_num(metagraph.emission[uid]).item() * 20
138
- ) # convert to daily TAO
139
-
140
- model_data = None
141
- try:
142
- model_data = ModelData.from_compressed_str(
143
- uid, hotkey, chain_str, block, incentive, emission
144
- )
145
- except:
146
- continue
147
-
148
- result.append(model_data)
149
- return result
150
-
151
-
152
- def get_wandb_runs(project: str, filters: Dict[str, Any]) -> List:
153
- """Get the latest runs from Wandb, retrying infinitely until we get them.
154
-
155
- Returns:
156
- List: List of runs matching the provided filters, newest run (by creation time) first.
157
- """
158
- while True:
159
- api = wandb.Api(api_key=WANDB_TOKEN)
160
- runs = list(
161
- api.runs(
162
- project,
163
- filters=filters,
164
- order="-created_at",
165
- )
166
- )
167
- if len(runs) > 0:
168
- return runs
169
- # WandDB API is quite unreliable. Wait another minute and try again.
170
- bt.logging.error("Failed to get runs from Wandb. Trying again in 60 seconds.")
171
- time.sleep(60)
172
-
173
-
174
- def get_scores(
175
- uids: List[int],
176
- wandb_runs: List,
177
- ) -> Dict[int, Dict[str, Optional[float]]]:
178
- """Returns the most recent scores for the provided UIDs.
179
-
180
- Args:
181
- uids (List[int]): List of UIDs to get scores for.
182
- wandb_runs (List): List of validator runs from Wandb. Requires the runs are provided in descending order.
183
- """
184
- result = {}
185
- previous_timestamp = None
186
- # Iterate through the runs until we've processed all the uids.
187
- for i, run in enumerate(wandb_runs):
188
- if not "original_format_json" in run.summary:
189
- continue
190
- data = json.loads(run.summary["original_format_json"])
191
- all_uid_data = data["uid_data"]
192
- timestamp = data["timestamp"]
193
- # Make sure runs are indeed in descending time order.
194
- assert (
195
- previous_timestamp is None or timestamp < previous_timestamp
196
- ), f"Timestamps are not in descending order: {timestamp} >= {previous_timestamp}"
197
- previous_timestamp = timestamp
198
-
199
- for uid in uids:
200
- if uid in result:
201
- continue
202
- if str(uid) in all_uid_data:
203
- uid_data = all_uid_data[str(uid)]
204
- # Only the most recent run is fresh.
205
- is_fresh = i == 0
206
- result[uid] = {
207
- "avg_loss": uid_data.get("average_loss", None),
208
- "win_rate": uid_data.get("win_rate", None),
209
- "win_total": uid_data.get("win_total", None),
210
- "weight": uid_data.get("weight", None),
211
- "competition_id": uid_data.get("competition_id", None),
212
- "fresh": is_fresh,
213
- }
214
- if len(result) == len(uids):
215
- break
216
- return result
217
-
218
-
219
- def get_validator_weights(
220
- metagraph: bt.metagraph,
221
- ) -> Dict[int, Tuple[float, int, Dict[int, float]]]:
222
- """Returns a dictionary of validator UIDs to (vtrust, stake, {uid: weight})."""
223
- ret = {}
224
- for uid in metagraph.uids.tolist():
225
- vtrust = metagraph.validator_trust[uid].item()
226
- stake = metagraph.stake[uid].item()
227
- if vtrust > 0 and stake > 10_000:
228
- ret[uid] = (vtrust, stake, {})
229
- for ouid in metagraph.uids.tolist():
230
- if ouid == uid:
231
- continue
232
- weight = round(metagraph.weights[uid][ouid].item(), 4)
233
- if weight > 0:
234
- ret[uid][-1][ouid] = weight
235
- return ret
236
-
237
-
238
- def get_losses_over_time(wandb_runs: List) -> pd.DataFrame:
239
- """Returns a dataframe of the best average model loss over time."""
240
- timestamps = []
241
- datapoints_per_comp_id = {id: [] for id in competitions.COMPETITION_DETAILS}
242
-
243
- for run in wandb_runs:
244
- # For each run, check the 10 most recent steps.
245
- best_loss_per_competition_id = defaultdict(lambda: math.inf)
246
- should_add_datapoint = False
247
- min_step = max(0, run.lastHistoryStep - 10)
248
- history_scan = HistoryScan(
249
- run.client, run, min_step, run.lastHistoryStep, page_size=10
250
- )
251
- max_timestamp = None
252
- for step in history_scan:
253
- if "original_format_json" not in step:
254
- continue
255
- data = json.loads(step["original_format_json"])
256
- all_uid_data = data["uid_data"]
257
- timestamp = datetime.datetime.fromtimestamp(data["timestamp"])
258
- if max_timestamp is None:
259
- max_timestamp = timestamp
260
- max_timestamp = max(max_timestamp, timestamp)
261
-
262
- for _, uid_data in all_uid_data.items():
263
- loss = uid_data.get("average_loss", math.inf)
264
- competition_id = uid_data.get("competition_id", None)
265
- if not competition_id:
266
- continue
267
-
268
- if loss < best_loss_per_competition_id[competition_id]:
269
- best_loss_per_competition_id[competition_id] = uid_data["average_loss"]
270
- should_add_datapoint = True
271
- # Now that we've processed the run's most recent steps, check if we should add a datapoint.
272
- if should_add_datapoint:
273
- timestamps.append(max_timestamp)
274
- # Iterate through all possible competitions and add the best loss for each.
275
- # Set None for any that aren't active during this run.
276
- for id, losses in datapoints_per_comp_id.items():
277
- losses.append(best_loss_per_competition_id.get(id, None))
278
-
279
- # Create a dictionary of competitions to lists of losses.
280
- output_columns = {competitions.COMPETITION_DETAILS[id].name: losses for id, losses in datapoints_per_comp_id.items()}
281
-
282
- return pd.DataFrame({"timestamp": timestamps, **output_columns})
283
-
284
-
285
- def next_epoch(subtensor: bt.subtensor, block: int) -> int:
286
- return (
287
- block
288
- + subtensor.get_subnet_hyperparameters(NETUID).tempo
289
- - subtensor.blocks_since_epoch(NETUID, block)
290
- )
291
-
292
-
293
- def is_floatable(x) -> bool:
294
- return (
295
- isinstance(x, float) and not math.isnan(x) and not math.isinf(x)
296
- ) or isinstance(x, int)
297
-
298
-
299
- def format_score(uid: int, scores, key) -> Optional[float]:
300
- if uid in scores:
301
- if key in scores[uid]:
302
- point = scores[uid][key]
303
- if is_floatable(point):
304
- return round(scores[uid][key], 4)
305
- return None
306
-
307
-
308
- def leaderboard_data(
309
- leaderboard: List[ModelData],
310
- scores: Dict[int, Dict[str, Optional[float]]],
311
- show_stale: bool,
312
- ) -> List[List[Any]]:
313
- """Returns the leaderboard data, based on models data and UID scores."""
314
- return [
315
- [
316
- f"[{c.namespace}/{c.name} ({c.commit[0:8]})](https://huggingface.co/{c.namespace}/{c.name}/commit/{c.commit})",
317
- format_score(c.uid, scores, "win_rate"),
318
- format_score(c.uid, scores, "avg_loss"),
319
- format_score(c.uid, scores, "weight"),
320
- c.uid,
321
- c.block,
322
- ]
323
- for c in leaderboard
324
- if (c.uid in scores and scores[c.uid]["fresh"]) or show_stale
325
- ]
326
-
327
-
328
- def get_benchmarks() -> Tuple[pd.DataFrame, datetime.datetime]:
329
- """Returns the latest benchmarks and the time they were run."""
330
- if not BENCHMARK_WANDB_PROJECT:
331
- bt.logging.error("No benchmark project set.")
332
- return None, None
333
- runs = get_wandb_runs(project=BENCHMARK_WANDB_PROJECT, filters=None)
334
- for run in runs:
335
- artifacts = list(run.logged_artifacts())
336
- if artifacts:
337
- table = artifacts[-1].get("benchmarks")
338
- if table:
339
- return table.get_dataframe(), datetime.datetime.strptime(
340
- run.metadata["startedAt"], "%Y-%m-%dT%H:%M:%S.%f"
341
- )
342
- bt.logging.error("Failed to get benchmarks from Wandb.")
343
- return None, None
344
-
345
-
346
- def make_validator_dataframe(
347
- validator_df: pd.DataFrame, model_data: ModelData
348
- ) -> pd.DataFrame:
349
-
350
- values = [
351
- [uid, int(validator_df[uid][1]), round(validator_df[uid][0], 4)]
352
- + [validator_df[uid][-1].get(c.uid) for c in model_data if c.incentive]
353
- for uid, _ in sorted(
354
- zip(
355
- validator_df.keys(),
356
- [validator_df[x][1] for x in validator_df.keys()],
357
- ),
358
- key=lambda x: x[1],
359
- reverse=True,
360
- )
361
- ]
362
- dtypes = {"UID": int, "Stake (τ)": float, "V-Trust": float}
363
- dtypes.update(
364
- {
365
- f"{c.namespace}/{c.name} ({c.commit[0:8]})": float
366
- for c in model_data
367
- if c.incentive
368
- }
369
- )
370
- return pd.DataFrame(values, columns=dtypes.keys()).astype(dtypes)
371
-
372
-
373
- def make_metagraph_dataframe(metagraph: bt.metagraph, weights=False) -> pd.DataFrame:
374
-
375
- cols = [
376
- "stake",
377
- "emission",
378
- "trust",
379
- "validator_trust",
380
- "dividends",
381
- "incentive",
382
- "R",
383
- "consensus",
384
- "validator_permit",
385
- ]
386
-
387
- frame = pd.DataFrame({k: getattr(metagraph, k) for k in cols})
388
- frame["block"] = metagraph.block.item()
389
- frame["netuid"] = NETUID
390
- frame["uid"] = range(len(frame))
391
- frame["hotkey"] = [axon.hotkey for axon in metagraph.axons]
392
- frame["coldkey"] = [axon.coldkey for axon in metagraph.axons]
393
- if weights and metagraph.W is not None:
394
- # convert NxN tensor to a list of lists so it fits into the dataframe
395
- frame["weights"] = [w.tolist() for w in metagraph.W]
396
-
397
- return frame
398
-
399
-
400
- def load_state_vars() -> dict[Any]:
401
- while True:
402
- try:
403
- subtensor, metagraph = get_subtensor_and_metagraph()
404
-
405
- bt.logging.success("Loaded subtensor and metagraph")
406
-
407
- model_data: List[ModelData] = get_subnet_data(subtensor, metagraph)
408
- model_data.sort(key=lambda x: x.incentive, reverse=True)
409
-
410
- bt.logging.success(f"Loaded {len(model_data)} models")
411
- vali_runs = get_wandb_runs(
412
- project=VALIDATOR_WANDB_PROJECT,
413
- # TODO: Update to point to the OTF vali on finetuning
414
- filters={"config.type": "validator", "config.uid": 28},
415
- )
416
-
417
- scores = get_scores([x.uid for x in model_data], vali_runs)
418
-
419
- # TODO: Re-enable once ""SubtensorModule.BlocksSinceEpoch" not found" issue is resolved.
420
- # current_block = metagraph.block.item()
421
- # next_epoch_block = next_epoch(subtensor, current_block)
422
-
423
- validator_df = get_validator_weights(metagraph)
424
- weight_keys = set()
425
- for uid, stats in validator_df.items():
426
- weight_keys.update(stats[-1].keys())
427
-
428
- # Enable benchmark if the flag is set
429
- if BENCHMARK_FLAG:
430
- benchmarks, benchmark_timestamp = get_benchmarks()
431
- else:
432
- benchmarks, benchmark_timestamp = None, None
433
- break
434
-
435
- except KeyboardInterrupt:
436
- bt.logging.error("Exiting...")
437
- break
438
-
439
- except Exception as e:
440
- print(f"Failed to get data: {traceback.format_exc()}")
441
- time.sleep(30)
442
-
443
- return {
444
- "metagraph": metagraph,
445
- "model_data": model_data,
446
- "vali_runs": vali_runs,
447
- "scores": scores,
448
- "validator_df": validator_df,
449
- "benchmarks": benchmarks,
450
- "benchmark_timestamp": benchmark_timestamp,
451
- }
452
-
453
-
454
- def test_load_state_vars():
455
- # TODO: Change to finetuning data.
456
- subtensor = bt.subtensor("finney")
457
- metagraph = subtensor.metagraph(NETUID, lite=True)
458
- model_data = [
459
- ModelData(
460
- uid=253,
461
- hotkey="5DjoPAgZ54Zf6NsuiVYh8RjonnWWWREE2iXBNzM2VDBMQDPm",
462
- namespace="jw-hf-test",
463
- name="jw2",
464
- commit="aad131f6b02219964e6dcf749c2a23e75a7ceca8",
465
- secure_hash="L1ImYzWJwV+9KSnZ2TYW0Iy2KMcVjJVTd30YJoRkpbw=",
466
- block=3131103,
467
- incentive=1.0,
468
- emission=209.06051635742188,
469
- ),
470
- ModelData(
471
- uid=1,
472
- hotkey="5CccVtjk4yamCao6QYgEg7jc8vktdj16RbLKNUftHfEsjuJS",
473
- namespace="borggAI",
474
- name="bittensor-subnet9-models",
475
- commit="d373864bc6c972872edb8db95eed570958054bac",
476
- secure_hash="+drdTIKYEGYClW2FFVVID6A2Dh//4rLmExRFCJsH6Y4=",
477
- block=2081837,
478
- incentive=0.0,
479
- emission=0.0,
480
- ),
481
- ModelData(
482
- uid=2,
483
- hotkey="5HYwoXaczs3jAptbb5mk4aUCkgZqeNcNzJKxSec97GwasfLy",
484
- namespace="jungiebeen",
485
- name="pretrain1",
486
- commit="4c0c6bfd0f92e243d6c8a82209142e7204c852c3",
487
- secure_hash="ld/agc0XIWICom/Cpj0fkQLcMogMNj/F65MJogK5RLY=",
488
- block=2467482,
489
- incentive=0.0,
490
- emission=0.0,
491
- ),
492
- ModelData(
493
- uid=3,
494
- hotkey="5Dnb6edh9yTeEp5aasRPZVPRAkxvQ6qnERVcXw22awMZ5rxm",
495
- namespace="jungiebeen",
496
- name="pretrain2",
497
- commit="e827b7281c92224adb11124489cc45356553a87a",
498
- secure_hash="ld/agc0XIWICom/Cpj0fkQLcMogMNj/F65MJogK5RLY=",
499
- block=2467497,
500
- incentive=0.0,
501
- emission=0.0,
502
- ),
503
- ModelData(
504
- uid=4,
505
- hotkey="5FRfca8NbnH424WaX43PMhKBnbLA1bZpRRoXXiVs6HgsxN4K",
506
- namespace="ZainAli60",
507
- name="mine_modeles",
508
- commit="8a4ed4ad1f1fb58d424fd22e8e9874b87d32917c",
509
- secure_hash="tVcbZAFoNIOF+Ntxq31OQ2NrLXf5iFCmmPUJlpkMYYo=",
510
- block=2508509,
511
- incentive=0.0,
512
- emission=0.0,
513
- ),
514
- ]
515
- vali_runs = get_wandb_runs(
516
- project=VALIDATOR_WANDB_PROJECT,
517
- filters={"config.type": "validator", "config.uid": 238},
518
- )
519
-
520
- scores = get_scores([x.uid for x in model_data], vali_runs)
521
-
522
- validator_df = {
523
- 28: (1.0, 33273.4453125, {253: 1.0}),
524
- 49: (
525
- 0.9127794504165649,
526
- 10401.677734375,
527
- {
528
- 7: 0.0867,
529
- 217: 0.0001,
530
- 219: 0.0001,
531
- 241: 0.0001,
532
- 248: 0.0001,
533
- 253: 0.9128,
534
- },
535
- ),
536
- 78: (1.0, 26730.37109375, {253: 1.0}),
537
- 116: (1.0, 629248.4375, {253: 1.0}),
538
- 150: (1.0, 272634.53125, {253: 1.0}),
539
- 161: (1.0, 280212.53125, {253: 1.0}),
540
- 180: (1.0, 16838.0, {253: 1.0}),
541
- 184: (1.0, 47969.3984375, {253: 1.0}),
542
- 210: (1.0, 262846.28125, {253: 1.0}),
543
- 213: (1.0, 119462.734375, {253: 1.0}),
544
- 215: (1.0, 274747.46875, {253: 1.0}),
545
- 234: (1.0, 38831.6953125, {253: 1.0}),
546
- 236: (1.0, 183966.9375, {253: 1.0}),
547
- 238: (1.0, 1293707.25, {253: 1.0}),
548
- 240: (1.0, 106461.6015625, {253: 1.0}),
549
- 243: (1.0, 320271.5, {253: 1.0}),
550
- 244: (1.0, 116138.9609375, {253: 1.0}),
551
- 247: (0.9527428150177002, 119812.390625, {7: 0.0472, 253: 0.9528}),
552
- 249: (1.0, 478127.3125, {253: 1.0}),
553
- 252: (1.0, 442395.03125, {253: 1.0}),
554
- 254: (1.0, 46845.2109375, {253: 1.0}),
555
- 255: (1.0, 28977.56640625, {253: 1.0}),
556
- }
557
-
558
- return {
559
- "metagraph": metagraph,
560
- "model_data": model_data,
561
- "vali_runs": vali_runs,
562
- "scores": scores,
563
- "validator_df": validator_df,
564
- }