import argparse import json import os.path import torch from geopy.distance import geodesic from tqdm import tqdm from Preprocessing.multilinguality.MetricMetaLearner import create_learned_cache from Utility.storage_config import MODELS_DIR from Utility.utils import load_json_from_path class CacheCreator: def __init__(self, cache_root="."): self.iso_codes = list(load_json_from_path(os.path.join(cache_root, "iso_to_fullname.json")).keys()) self.iso_lookup = load_json_from_path(os.path.join(cache_root, "iso_lookup.json")) self.cache_root = cache_root self.pairs = list() # ignore order, collect all language pairs for index_1 in tqdm(range(len(self.iso_codes)), desc="Collecting language pairs"): for index_2 in range(index_1, len(self.iso_codes)): self.pairs.append((self.iso_codes[index_1], self.iso_codes[index_2])) def create_tree_cache(self, cache_root="."): iso_to_family_memberships = load_json_from_path(os.path.join(cache_root, "iso_to_memberships.json")) self.pair_to_tree_similarity = dict() self.pair_to_depth = dict() for pair in tqdm(self.pairs, desc="Generating tree pairs"): self.pair_to_tree_similarity[pair] = len(set(iso_to_family_memberships[pair[0]]).intersection(set(iso_to_family_memberships[pair[1]]))) lang_1_to_lang_2_to_tree_dist = dict() for pair in tqdm(self.pair_to_tree_similarity): lang_1 = pair[0] lang_2 = pair[1] if self.pair_to_tree_similarity[pair] == 2: dist = 1.0 else: dist = 1.0 - (self.pair_to_tree_similarity[pair] / max(len(iso_to_family_memberships[pair[0]]), len(iso_to_family_memberships[pair[1]]))) if lang_1 not in lang_1_to_lang_2_to_tree_dist.keys(): lang_1_to_lang_2_to_tree_dist[lang_1] = dict() lang_1_to_lang_2_to_tree_dist[lang_1][lang_2] = dist with open(os.path.join(cache_root, 'lang_1_to_lang_2_to_tree_dist.json'), 'w', encoding='utf-8') as f: json.dump(lang_1_to_lang_2_to_tree_dist, f, ensure_ascii=False, indent=4) def create_map_cache(self, cache_root="."): self.pair_to_map_dist = dict() iso_to_long_lat = load_json_from_path(os.path.join(cache_root, "iso_to_long_lat.json")) for pair in tqdm(self.pairs, desc="Generating map pairs"): try: long_1, lat_1 = iso_to_long_lat[pair[0]] long_2, lat_2 = iso_to_long_lat[pair[1]] geodesic((lat_1, long_1), (lat_2, long_2)) self.pair_to_map_dist[pair] = geodesic((lat_1, long_1), (lat_2, long_2)).miles except KeyError: pass lang_1_to_lang_2_to_map_dist = dict() for pair in self.pair_to_map_dist: lang_1 = pair[0] lang_2 = pair[1] dist = self.pair_to_map_dist[pair] if lang_1 not in lang_1_to_lang_2_to_map_dist.keys(): lang_1_to_lang_2_to_map_dist[lang_1] = dict() lang_1_to_lang_2_to_map_dist[lang_1][lang_2] = dist with open(os.path.join(cache_root, 'lang_1_to_lang_2_to_map_dist.json'), 'w', encoding='utf-8') as f: json.dump(lang_1_to_lang_2_to_map_dist, f, ensure_ascii=False, indent=4) def create_oracle_cache(self, model_path, cache_root="."): """Oracle language-embedding distance of supervised languages is only used for evaluation, not usable for zero-shot. Note: The generated oracle cache is only valid for the given `model_path`!""" loss_fn = torch.nn.MSELoss(reduction="mean") self.pair_to_oracle_dist = dict() lang_embs = torch.load(model_path)["model"]["encoder.language_embedding.weight"] lang_embs.requires_grad_(False) for pair in tqdm(self.pairs, desc="Generating oracle pairs"): try: dist = loss_fn(lang_embs[self.iso_lookup[-1][pair[0]]], lang_embs[self.iso_lookup[-1][pair[1]]]).item() self.pair_to_oracle_dist[pair] = dist except KeyError: pass lang_1_to_lang_2_oracle_dist = dict() for pair in self.pair_to_oracle_dist: lang_1 = pair[0] lang_2 = pair[1] dist = self.pair_to_oracle_dist[pair] if lang_1 not in lang_1_to_lang_2_oracle_dist.keys(): lang_1_to_lang_2_oracle_dist[lang_1] = dict() lang_1_to_lang_2_oracle_dist[lang_1][lang_2] = dist with open(os.path.join(cache_root, "lang_1_to_lang_2_to_oracle_dist.json"), "w", encoding="utf-8") as f: json.dump(lang_1_to_lang_2_oracle_dist, f, ensure_ascii=False, indent=4) def create_learned_cache(self, model_path, cache_root="."): """Note: The generated learned distance cache is only valid for the given `model_path`!""" create_learned_cache(model_path, cache_root=cache_root) def create_required_files(self, model_path, create_oracle=False): if not os.path.exists(os.path.join(self.cache_root, "lang_1_to_lang_2_to_tree_dist.json")): self.create_tree_cache(cache_root="Preprocessing/multilinguality") if not os.path.exists(os.path.join(self.cache_root, "lang_1_to_lang_2_to_map_dist.json")): self.create_map_cache(cache_root="Preprocessing/multilinguality") if not os.path.exists(os.path.join(self.cache_root, "asp_dict.pkl")): raise FileNotFoundError("asp_dict.pkl must be downloaded separately.") if not os.path.exists(os.path.join(self.cache_root, "lang_1_to_lang_2_to_learned_dist.json")): self.create_learned_cache(model_path=model_path, cache_root="Preprocessing/multilinguality") if create_oracle: if not os.path.exists(os.path.join(self.cache_root, "lang_1_to_lang_2_to_oracle_dist.json")): if not model_path: raise ValueError("model_path is required for creating oracle cache.") self.create_oracle_cache(model_path=args.model_path, cache_root="Preprocessing/multilinguality") print("All required cache files exist.") if __name__ == '__main__': default_model_path = os.path.join(MODELS_DIR, "ToucanTTS_Meta", "best.pt") # MODELS_DIR must be absolute path, the relative path will fail at this location parser = argparse.ArgumentParser() parser.add_argument("--model_path", "-m", type=str, default=default_model_path, help="model path that should be used for creating oracle lang emb distance cache") args = parser.parse_args() cc = CacheCreator() cc.create_required_files(args.model_path, create_oracle=True)