Spaces:
Runtime error
Runtime error
import json | |
import re | |
from pathlib import Path | |
import requests | |
import streamlit as st | |
import yaml | |
from huggingface_hub import hf_hub_download | |
from streamlit_ace import st_ace | |
from streamlit_tags import st_tags | |
# exact same regex as in the Hub server. Please keep in sync. | |
REGEX_YAML_BLOCK = re.compile(r"---[\n\r]+([\S\s]*?)[\n\r]+---[\n\r]") | |
with open("languages.json") as f: | |
lang2name = json.load(f) | |
def try_parse_yaml(yaml_block): | |
try: | |
metadata = yaml.load(yaml_block, yaml.SafeLoader) | |
except yaml.YAMLError as e: | |
print("Error while parsing the metadata YAML:") | |
if hasattr(e, "problem_mark"): | |
if e.context is not None: | |
st.error( | |
str(e.problem_mark) | |
+ "\n " | |
+ str(e.problem) | |
+ " " | |
+ str(e.context) | |
+ "\nPlease correct the README.md and retry." | |
) | |
else: | |
st.error( | |
str(e.problem_mark) | |
+ "\n " | |
+ str(e.problem) | |
+ "\nPlease correct the README.md and retry." | |
) | |
else: | |
st.error( | |
"Something went wrong while parsing the metadata. " | |
"Make sure it's written according to the YAML spec!" | |
) | |
return None | |
return metadata | |
def main(): | |
st.markdown("# The π€ Speech Bench Metrics Editor") | |
st.markdown("This tool will help you report the evaluation metrics for all of your speech recognition models. " | |
"Follow the steps and watch your models appear on the [Speech Bench Leaderboard](https://huggingface.co/spaces/huggingface/hf-speech-bench)!") | |
st.markdown("## 1. Load your model's metadata") | |
st.markdown("Enter your model's path below.") | |
model_id = st.text_input("", placeholder="<username>/<model>") | |
if not model_id.strip(): | |
st.stop() | |
try: | |
readme_path = hf_hub_download(model_id, filename="README.md") | |
except requests.exceptions.HTTPError: | |
st.error( | |
f"ERROR: https://huggingface.co/{model_id}/blob/main/README.md " | |
f"not found, make sure you've entered a correct model path and created a model card for it!" | |
) | |
st.stop() | |
content = Path(readme_path).read_text() | |
match = REGEX_YAML_BLOCK.search(content) | |
if match: | |
meta_yaml = match.group(1) | |
else: | |
st.error( | |
"ERROR: Couldn't find the metadata section inside your model's `README.md`. Do you have some basic metadata " | |
"enclosed in `---` as described in [the Hub documentation](https://huggingface.co/docs/hub/model-repos#model-card-metadata)?" | |
) | |
st.stop() | |
metadata = try_parse_yaml(meta_yaml) | |
if metadata is None: | |
st.stop() | |
return | |
else: | |
st.success("Successfully loaded the metadata!") | |
with st.expander("Inspect the parsed metadata for debugging"): | |
st.json(metadata) | |
st.markdown("## 2. Edit the data") | |
if "tags" not in metadata: | |
metadata["tags"] = [] | |
metadata["tags"].append("hf-asr-leaderboard") | |
############################ | |
# LANGUAGES | |
############################ | |
st.markdown("### Language(s)") | |
st.markdown( | |
"For each spoken language that your model handles, enter an " | |
"[ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) language code, or " | |
"find an appropriate alternative from " | |
"[our list here](https://huggingface.co/spaces/huggingface/hf-speech-bench/blob/main/languages.json). " | |
"When in doubt, use the most generic language code, e.g. `en` instead of `en-GB` and `en-US`." | |
) | |
st.markdown("*Example*: `en, gsw, pt-BR`") | |
metadata["language"] = metadata["language"] if "language" in metadata else [] | |
metadata["language"] = ( | |
metadata["language"] | |
if isinstance(metadata["language"], list) | |
else [metadata["language"]] | |
) | |
languages = st_tags( | |
label="", text="add more if needed, and press enter", value=metadata["language"], | |
key=model_id+"_langs" | |
) | |
if not languages: | |
st.stop() | |
return | |
lang_names = [lang2name[lang] if lang in lang2name else lang for lang in languages] | |
st.markdown("These languages will be parsed by the leaderboard as: ") | |
st.code(", ".join(lang_names)) | |
metadata["language"] = languages | |
############################ | |
# TRAIN DATASETS | |
############################ | |
st.markdown("### Training dataset(s)") | |
st.markdown( | |
"List the datasets that your model was **trained** on. " | |
"If the datasets aren't published on the Hub yet, just add their names anyway." | |
) | |
st.markdown( | |
"*Example*: `librispeech_asr, mozilla-foundation/common_voice_8_0, my_custom_youtube_dataset`" | |
) | |
if "datasets" not in metadata: | |
metadata["datasets"] = [] | |
train_datasets = st_tags( | |
label="", text="add more if needed, and press enter", value=metadata["datasets"], | |
key=model_id+"_train_dataset" | |
) | |
if not train_datasets: | |
st.stop() | |
return | |
if "common_voice" in train_datasets: | |
st.warning( | |
"WARNING: `common_voice` is deprecated, please replace it with its equivalent: " | |
"`mozilla-foundation/common_voice_6_1`" | |
) | |
metadata["datasets"] = train_datasets | |
############################ | |
# MODEL NAME | |
############################ | |
st.markdown("### Model name") | |
st.markdown("Enter a pretty name for your model.") | |
st.markdown("*Example*: `XLS-R Wav2Vec2 LM Spanish by Jane Doe`") | |
if "model-index" not in metadata: | |
metadata["model-index"] = [{}] | |
if "name" not in ["model-index"][0]: | |
metadata["model-index"][0]["name"] = model_id.split("/")[-1] | |
model_name = st.text_input("", value=metadata["model-index"][0]["name"]) | |
if not model_name: | |
st.stop() | |
return | |
metadata["model-index"][0]["name"] = model_name | |
############################ | |
# EVAL RESULTS | |
############################ | |
st.markdown("### Evaluation results") | |
st.markdown( | |
"To edit the metrics, you can either use the YAML editor below, or add new metrics using the handy " | |
"form under it." | |
) | |
if "results" not in metadata["model-index"][0]: | |
metadata["model-index"][0]["results"] = [] | |
results_editor = st.empty() | |
with results_editor: | |
results_yaml = yaml.dump( | |
metadata["model-index"][0]["results"], sort_keys=False, line_break="\n" | |
) | |
results_yaml = st_ace(value=results_yaml, language="yaml") | |
metadata["model-index"][0]["results"] = try_parse_yaml(results_yaml) | |
dataset_path_kwargs = {} | |
dataset_name_kwargs = {} | |
if ( | |
len(metadata["model-index"][0]["results"]) > 0 | |
and "dataset" in metadata["model-index"][0]["results"][0] | |
): | |
if "type" in metadata["model-index"][0]["results"][0]["dataset"]: | |
dataset_path_kwargs["value"] = metadata["model-index"][0]["results"][0][ | |
"dataset" | |
]["type"] | |
if "name" in metadata["model-index"][0]["results"][0]["dataset"]: | |
dataset_name_kwargs["value"] = metadata["model-index"][0]["results"][0][ | |
"dataset" | |
]["name"] | |
with st.form(key="eval_form"): | |
dataset_path = st.text_input( | |
label="Dataset path / id", | |
placeholder="mozilla-foundation/common_voice_8_0", | |
**dataset_path_kwargs, | |
) | |
dataset_name = st.text_input( | |
label="A pretty name for the dataset. Examples: 'Common Voice 9.0 (French)', 'LibriSpeech (clean)'", | |
placeholder="Common Voice 9.0 (French)", | |
**dataset_name_kwargs, | |
) | |
dataset_config = st.text_input( | |
label="Dataset configuration. Examples: clean, other, en, pt-BR", | |
placeholder="en", | |
) | |
dataset_language_kwargs = {"value": languages[0]} if len(languages) > 0 else {} | |
dataset_language = st.text_input( | |
label="Dataset language. Examples: en, pt-BR", | |
placeholder="en", | |
**dataset_language_kwargs | |
) | |
dataset_split = st.text_input( | |
label="Dataset split. Examples: test, validation", | |
value="test", | |
placeholder="test", | |
) | |
metric2name = {"wer": "Word Error Rate", "cer": "Character Error Rate"} | |
metric_type = st.selectbox( | |
label="Metric", | |
options=["wer", "cer"], | |
format_func=lambda key: metric2name[key], | |
) | |
metric_name = st.text_input( | |
label="A pretty name for the metric. Example: Test WER (+LM)", | |
placeholder="Test WER", | |
value="Test WER", | |
) | |
metric_value = st.text_input( | |
label="Metric value. Use values in range 0.0 to 100.0.", | |
placeholder="12.34", | |
) | |
# try: | |
# metric_value = float(metric_value) | |
# except ValueError: | |
# st.error( | |
# f"Couldn't parse `{metric_value}`. Make sure it's a number from 0.0 to 100.0" | |
# ) | |
submitted = st.form_submit_button("Add metric") | |
if ( | |
submitted | |
and dataset_name | |
and dataset_path | |
and dataset_config | |
and dataset_split | |
and dataset_language | |
and metric_name | |
and metric_type | |
and metric_value | |
): | |
metric = { | |
"name": metric_name, | |
"type": metric_type, | |
"value": metric_value, | |
} | |
# first, try to find an existing dataset+config record to add a new metric to it | |
updated_existing = False | |
for existing_result in metadata["model-index"][0]["results"]: | |
existing_dataset = existing_result["dataset"] | |
if ( | |
existing_dataset["type"] == dataset_path | |
and "config" in existing_dataset | |
and existing_dataset["config"] == dataset_config | |
and "split" in existing_dataset | |
and existing_dataset["split"] == dataset_split | |
): | |
if "metrics" not in existing_result: | |
existing_result["metrics"] = [] | |
existing_result["metrics"].append(metric) | |
updated_existing = True | |
break | |
# if no dataset+config results found, create a new one | |
if not updated_existing: | |
result = { | |
"task": { | |
"name": "Automatic Speech Recognition", | |
"type": "automatic-speech-recognition", | |
}, | |
"dataset": { | |
"name": dataset_name, | |
"type": dataset_path, | |
"config": dataset_config, | |
"split": dataset_split, | |
"args": {"language": dataset_language}, | |
}, | |
"metrics": [metric], | |
} | |
metadata["model-index"][0]["results"].append(result) | |
# update the code editor | |
with results_editor: | |
results_yaml = yaml.dump( | |
metadata["model-index"][0]["results"], | |
sort_keys=False, | |
line_break="\n", | |
) | |
results_yaml = st_ace(value=results_yaml, language="yaml") | |
metadata["model-index"][0]["results"] = try_parse_yaml(results_yaml) | |
st.success( | |
f"Added the metric for {dataset_path} - {dataset_config}! " | |
f"Check the result in the YAML editor above." | |
) | |
elif submitted: | |
st.error( | |
f"Make sure that you've filled the whole form before clicking 'Add metric'!" | |
) | |
############################ | |
# FINAL YAML | |
############################ | |
st.markdown("## 3. Copy the generated metadata") | |
st.markdown( | |
"Copy the YAML from below and replace the metadata at the top of your model's README.md here: " | |
f"https://huggingface.co/{model_id}/edit/main/README.md" | |
) | |
st.markdown("For more info on the metadata schema please refer to " | |
"https://raw.githubusercontent.com/huggingface/hub-docs/main/modelcard.md") | |
new_yaml = yaml.dump(metadata, sort_keys=False, line_break="\n") | |
st.markdown(f"```yaml\n---\n{new_yaml}---\n```") | |
if __name__ == "__main__": | |
main() | |