├── packages.txt ├── wav2vec2 ├── .gitignore ├── README.md ├── requirements.txt └── wav2vec_train.ipynb ├── requirements.txt ├── .github ├── workflows │ └── hf-sync.yml └── FUNDING.yml ├── LICENSE ├── deepspeech ├── README.md ├── extract_text_corpus.py ├── wiki_import.py └── import_ukrainian.py ├── README.md ├── .gitignore └── app.py /packages.txt: -------------------------------------------------------------------------------- 1 | ffmpeg -------------------------------------------------------------------------------- /wav2vec2/.gitignore: -------------------------------------------------------------------------------- 1 | cached_dataset 2 | wav2vec2-xls-r* 3 | vocab.json -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | STT==1.4.0 2 | transformers==4.22.1 3 | pydub==0.25.1 4 | torch>=1.12 5 | gradio==3.41.2 6 | -------------------------------------------------------------------------------- /wav2vec2/README.md: -------------------------------------------------------------------------------- 1 | # Wav2Vec2 notebooks 2 | 3 | Run: 4 | 1. `data` notebook. 5 | 2. `train` notebook. 6 | 7 | Test with `inference` notebook. -------------------------------------------------------------------------------- /wav2vec2/requirements.txt: -------------------------------------------------------------------------------- 1 | datasets==2.4.0 2 | transformers==4.21.2 3 | #huggingface_hub==0.1 4 | #torchaudio 5 | librosa 6 | jiwer 7 | # install torch! -------------------------------------------------------------------------------- /.github/workflows/hf-sync.yml: -------------------------------------------------------------------------------- 1 | name: Sync to Hugging Face hub 2 | on: 3 | push: 4 | branches: [main] 5 | 6 | # to run this workflow manually from the Actions tab 7 | workflow_dispatch: 8 | 9 | jobs: 10 | sync-to-hub: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | with: 15 | fetch-depth: 0 16 | - name: Push to hub 17 | env: 18 | HF_TOKEN: ${{ secrets.HF_TOKEN }} 19 | run: git push https://robinhad:$HF_TOKEN@huggingface.co/spaces/robinhad/ukrainian-stt main 20 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: ["https://send.monobank.ua/jar/48iHq4xAXm"] 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Yurii Paniv 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /deepspeech/README.md: -------------------------------------------------------------------------------- 1 | # How to prepare dataset for training 2 | 3 | 1. Download Ukrainian dataset from [https://github.com/egorsmkv/speech-recognition-uk](https://github.com/egorsmkv/speech-recognition-uk). 4 | 2. Delete Common Voice folder in dataset 5 | 3. Download [import_ukrainian.py](scripts/import_ukrainian.py) and put into DeepSpeech/bin folder. 6 | 4. Run import script 7 | 5. Download Common Voice 6.1 Ukrainian dataset 8 | 6. Convert to DeepSpeech format 9 | 7. Merge train.csv from dataset and from DeepSpeech into one file 10 | 8. Put CV files into dataset files folder 11 | 9. Put dev.csv and test.csv into folder 12 | 13 | Note: you can also specify dataset with "," e.g. dataset1/train.csv,dataset2/train.csv. 14 | 15 | You have a reproducible dataset! 16 | 17 | 18 | # Scorer 19 | 20 | 1. Refer to DeepSpeech guide for further explanations. 21 | 22 | 2. Generate scorer package. 23 | ``` 24 | python3 generate_lm.py --input_txt ../../../voice-recognition-ua/data/all_text.txt --output_dir . \ 25 | --top_k 500000 --kenlm_bins ../../../voice-recognition-ua/kenlm/build/bin \ 26 | --arpa_order 5 --max_arpa_memory "85%" --arpa_prune "0|0|1" \ 27 | --binary_a_bits 255 --binary_q_bits 8 --binary_type trie 28 | ``` 29 | 3. Run lm_optimizer to find the best scorer value. 30 | 4. Rerun step 2 to generate new scorer. 31 | 32 | Caution: scorer is very model-dependant, so you'll likely need to adjust it to each model. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Ukrainian Speech-to-Text" 3 | emoji: 🐌 4 | colorFrom: blue 5 | colorTo: yellow 6 | sdk: gradio 7 | sdk_version: 3.41.2 8 | app_file: app.py 9 | pinned: false 10 | --- 11 | 12 | # 🇺🇦🎤 Voice recognition for Ukrainian language 13 | This is a repository with aim to apply various speech recognition models on Ukrainian language. 14 | 15 | You can see online demo here: https://huggingface.co/spaces/robinhad/ukrainian-stt. 16 | Github link: https://github.com/robinhad/voice-recognition-ua. 17 | Source code is in this repository together with auto-deploy pipeline scripts. 18 | 19 | 20 | # 🧮 Models 21 | Model name | CER | WER | License | Note 22 | :-------------------------|:-------------------------|:-------------------------|:-------------------------|:------------------------- 23 | [Wav2Vec2](https://github.com/robinhad/voice-recognition-ua/releases/tag/release%2Fwav2vec2-v0.1) | 6,01% | 27,99% | MIT | Common Voice 8 dataset, `test` set used as validation 24 | [DeepSpeech with Wiki LM](https://github.com/robinhad/voice-recognition-ua/releases/tag/v0.4) | 12% | 30,65% | CC-BY-NC 4.0 | Common Voice 6 dataset 25 | [DeepSpeech](https://github.com/robinhad/voice-recognition-ua/releases/tag/v0.4) | 16% | 57% | CC-BY-NC 4.0 | Common Voice 6 dataset 26 | 27 | 28 | Checkout latest releases here: https://github.com/robinhad/voice-recognition-ua/releases/. 29 | 30 | If you'd like to check out different models for Ukrainian language, please visit https://github.com/egorsmkv/speech-recognition-uk. 31 | 32 | # 🤖 Training scripts 33 | Guides for training are available in corresponding folders for each model. 34 | 35 | # Support 36 | If you like my work, please support here: https://send.monobank.ua/jar/48iHq4xAXm 37 | 38 | # 🤝 Attribution 39 | [@robinhad](https://github.com/robinhad) - model training. 40 | [@egorsmkv](https://github.com/egorsmkv) - organized [Ukrainian Speech recognition community](https://github.com/egorsmkv/speech-recognition-uk). 41 | [@tarasfrompir](https://github.com/tarasfrompir) - created synthetic 1200h Ukrainian Speech-to-Text dataset. 42 | [@AlexeyBoiler](https://github.com/AlexeyBoiler) - hosted Ukrainian Speech-to-Text dataset. 43 | -------------------------------------------------------------------------------- /deepspeech/extract_text_corpus.py: -------------------------------------------------------------------------------- 1 | # this script is used for importing random texts from folder and converting it for scorer 2 | import os 3 | import nltk 4 | import re 5 | nltk.download("punkt") 6 | 7 | FOLDER = "../data/текст/" 8 | OUT_FILE = "../data/texts.txt" 9 | text_file = open(OUT_FILE, mode="a") 10 | 11 | tokenizer = nltk.SpaceTokenizer() 12 | paranthesis_regex = re.compile(r'\(.*\)') 13 | allowed_chars = ["а", "б", "в", "г", "ґ", "д", "е", "є", "ж", "з", "и", "і", "ї", "й", "к", "л", 14 | "м", "н", "о", "п", "р", "с", "т", "у", "ф", "х", "ц", "ч", "ш", "щ", "ь", "ю", "я", "-", "’"] 15 | 16 | for subdir, dirs, files in os.walk(FOLDER): 17 | for file in files: 18 | file_path = os.path.join(subdir, file) 19 | print(file_path) 20 | input_file = open(file_path) 21 | try: 22 | cleaned_text = input_file.read() 23 | except: 24 | input_file.close() 25 | input_file = open(file_path, encoding="cp1251") 26 | cleaned_text = input_file.read() 27 | cleaned_text = cleaned_text.lower() 28 | cleaned_text = cleaned_text.replace("'", "’") 29 | cleaned_text = paranthesis_regex.sub('', cleaned_text) 30 | cleaned_text = cleaned_text.strip() 31 | cleaned_text = cleaned_text.split(".") 32 | out_text = [] 33 | for text in cleaned_text: 34 | text = text.strip() 35 | 36 | words = tokenizer.tokenize(text) 37 | words = [i for i in words if not i.isdigit()] 38 | new_words = [] 39 | for word in words: 40 | include = True 41 | for letter in word: 42 | if word.startswith("-"): 43 | word = word[1:] 44 | if letter not in allowed_chars: 45 | include = False 46 | if include: 47 | new_words.append(word) 48 | words = new_words 49 | if all([len(i) <= 1 for i in words]): 50 | continue 51 | if len(words) == 0: 52 | continue 53 | out_text.append( 54 | " ".join(words)) 55 | cleaned_text = "\n".join(out_text) 56 | if cleaned_text == "": 57 | continue 58 | text_file.write(cleaned_text + "\n") 59 | input_file.close() 60 | 61 | 62 | text_file.close() 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | *.tflite 132 | .DS_Store 133 | 134 | /data 135 | *.scorer -------------------------------------------------------------------------------- /deepspeech/wiki_import.py: -------------------------------------------------------------------------------- 1 | # this script is used for importing wiki text into scorer format 2 | from wiki_dump_reader import Cleaner, iterate 3 | from os import remove 4 | from os.path import exists 5 | import nltk 6 | import re 7 | nltk.download("punkt") 8 | 9 | OUT_PATH = "../data/wiki_text.txt" 10 | 11 | if exists(OUT_PATH): 12 | remove(OUT_PATH) 13 | text_file = open(OUT_PATH, mode="a") 14 | 15 | tokenizer = nltk.SpaceTokenizer() 16 | paranthesis_regex = re.compile(r'\(.*\)') 17 | allowed_chars = ["а", "б", "в", "г", "ґ", "д", "е", "є", "ж", "з", "и", "і", "ї", "й", "к", "л", 18 | "м", "н", "о", "п", "р", "с", "т", "у", "ф", "х", "ц", "ч", "ш", "щ", "ь", "ю", "я", "-", "’"] 19 | 20 | cleaner = Cleaner() 21 | # iter = 0 22 | for title, text in iterate('../data/ukwiki-20210320-pages-articles-multistream.xml'): 23 | text = cleaner.clean_text(text) 24 | cleaned_text, _ = cleaner.build_links(text) 25 | cleaned_text = cleaned_text.lower() 26 | cleaned_text = cleaned_text.replace(" ", " ") 27 | cleaned_text = cleaned_text.replace("н. е.", "нашої ери") 28 | cleaned_text = cleaned_text.replace("ім.", "імені") 29 | cleaned_text = cleaned_text.replace("див.", "дивись") 30 | cleaned_text = cleaned_text.replace("'", "’") 31 | cleaned_text = paranthesis_regex.sub('', cleaned_text) 32 | cleaned_text = cleaned_text.strip() 33 | cleaned_text = cleaned_text.split(".") 34 | out_text = [] 35 | for text in cleaned_text: 36 | text = text.strip() 37 | if text.endswith(", що вивчає"): 38 | continue 39 | if text.startswith("redirect") or text.startswith("перенаправлення"): 40 | continue 41 | 42 | words = tokenizer.tokenize(text) 43 | words = [i for i in words if not i.isdigit()] 44 | new_words = [] 45 | for word in words: 46 | include = True 47 | for letter in word: 48 | if word.startswith("-"): 49 | word = word[1:] 50 | if letter not in allowed_chars: 51 | include = False 52 | if include: 53 | new_words.append(word) 54 | words = new_words 55 | if all([len(i) <= 1 for i in words]): 56 | continue 57 | if len(words) == 0: 58 | continue 59 | out_text.append( 60 | " ".join(words)) 61 | cleaned_text = "\n".join(out_text) 62 | if cleaned_text == "": 63 | continue 64 | text_file.write(cleaned_text + "\n") 65 | # iter += 1 66 | # if iter > 5: 67 | # break 68 | 69 | text_file.close() 70 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from io import BytesIO 2 | from typing import Tuple 3 | import wave 4 | import gradio as gr 5 | import numpy as np 6 | from pydub.audio_segment import AudioSegment 7 | import requests 8 | from os.path import exists 9 | from stt import Model 10 | from datetime import datetime 11 | from transformers import Wav2Vec2ForCTC, Wav2Vec2Processor 12 | import torch 13 | 14 | # download model 15 | version = "v0.4" 16 | storage_url = f"https://github.com/robinhad/voice-recognition-ua/releases/download/{version}" 17 | model_name = "uk.tflite" 18 | scorer_name = "kenlm.scorer" 19 | model_link = f"{storage_url}/{model_name}" 20 | scorer_link = f"{storage_url}/{scorer_name}" 21 | 22 | model = Wav2Vec2ForCTC.from_pretrained("robinhad/wav2vec2-xls-r-300m-uk")#.to("cuda") 23 | processor = Wav2Vec2Processor.from_pretrained("robinhad/wav2vec2-xls-r-300m-uk") 24 | # TODO: download config.json, pytorch_model.bin, preprocessor_config.json, tokenizer_config.json, vocab.json, added_tokens.json, special_tokens.json 25 | 26 | def download(url, file_name): 27 | if not exists(file_name): 28 | print(f"Downloading {file_name}") 29 | r = requests.get(url, allow_redirects=True) 30 | with open(file_name, 'wb') as file: 31 | file.write(r.content) 32 | else: 33 | print(f"Found {file_name}. Skipping download...") 34 | 35 | 36 | def deepspeech(audio: np.array, use_scorer=False): 37 | ds = Model(model_name) 38 | if use_scorer: 39 | ds.enableExternalScorer("kenlm.scorer") 40 | 41 | result = ds.stt(audio) 42 | 43 | return result 44 | 45 | def wav2vec2(audio: np.array): 46 | input_dict = processor(audio, sampling_rate=16000, return_tensors="pt", padding=True) 47 | with torch.no_grad(): 48 | output = model(input_dict.input_values.float()) 49 | 50 | logits = output.logits 51 | 52 | pred_ids = torch.argmax(logits, dim=-1)[0] 53 | 54 | return processor.decode(pred_ids) 55 | 56 | def inference(audio: Tuple[int, np.array]): 57 | print("=============================") 58 | print(f"Time: {datetime.utcnow()}.`") 59 | 60 | output_audio = _convert_audio(audio[1], audio[0]) 61 | 62 | fin = wave.open(output_audio, 'rb') 63 | audio = np.frombuffer(fin.readframes(fin.getnframes()), np.int16) 64 | fin.close() 65 | 66 | transcripts = [] 67 | 68 | transcripts.append(wav2vec2(audio)) 69 | print(f"Wav2Vec2: `{transcripts[-1]}`") 70 | transcripts.append(deepspeech(audio, use_scorer=True)) 71 | print(f"Deepspeech with LM: `{transcripts[-1]}`") 72 | transcripts.append(deepspeech(audio)) 73 | print(f"Deepspeech: `{transcripts[-1]}`") 74 | return tuple(transcripts) 75 | 76 | 77 | def _convert_audio(audio_data: np.array, sample_rate: int): 78 | audio_limit = sample_rate * 60 * 2 # limit audio to 2 minutes max 79 | if audio_data.shape[0] > audio_limit: 80 | audio_data = audio_data[0:audio_limit] 81 | source_audio = BytesIO() 82 | source_audio.write(audio_data) 83 | source_audio.seek(0) 84 | output_audio = BytesIO() 85 | wav_file: AudioSegment = AudioSegment.from_raw( 86 | source_audio, 87 | channels=1, 88 | sample_width=audio_data.dtype.itemsize, 89 | frame_rate=sample_rate 90 | ) 91 | wav_file.export(output_audio, "wav", codec="pcm_s16le", parameters=["-ar", "16k"]) 92 | output_audio.seek(0) 93 | return output_audio 94 | 95 | with open("README.md") as file: 96 | article = file.read() 97 | article = article[article.find("---\n", 4) + 5::] 98 | 99 | iface = gr.Interface( 100 | fn=inference, 101 | inputs=[ 102 | gr.inputs.Audio(type="numpy", 103 | label="Аудіо", optional=False), 104 | ], 105 | outputs=[gr.outputs.Textbox(label="Wav2Vec2"), gr.outputs.Textbox(label="DeepSpeech with LM"), gr.outputs.Textbox(label="DeepSpeech")], 106 | title="🇺🇦 Ukrainian Speech-to-Text models", 107 | theme="huggingface", 108 | description="Україномовний🇺🇦 Speech-to-Text за допомогою Coqui STT", 109 | article=article, 110 | ) 111 | 112 | download(model_link, model_name) 113 | download(scorer_link, scorer_name) 114 | iface.launch() 115 | -------------------------------------------------------------------------------- /deepspeech/import_ukrainian.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | This script transforms custom dataset, gathered from Internet into 4 | DeepSpeech-ready .csv file 5 | Use "python3 import_ukrainian.py -h" for help 6 | """ 7 | import csv 8 | import os 9 | import subprocess 10 | import unicodedata 11 | from multiprocessing import Pool 12 | 13 | import progressbar 14 | import sox 15 | 16 | from deepspeech_training.util.downloader import SIMPLE_BAR 17 | from deepspeech_training.util.importers import ( 18 | get_counter, 19 | get_imported_samples, 20 | get_importers_parser, 21 | get_validate_label, 22 | print_import_report, 23 | ) 24 | from ds_ctcdecoder import Alphabet 25 | import re 26 | 27 | FIELDNAMES = ["wav_filename", "wav_filesize", "transcript"] 28 | SAMPLE_RATE = 16000 29 | CHANNELS = 1 30 | MAX_SECS = 10 31 | PARAMS = None 32 | FILTER_OBJ = None 33 | AUDIO_DIR = None 34 | 35 | 36 | class LabelFilter: 37 | def __init__(self, normalize, alphabet, validate_fun): 38 | self.normalize = normalize 39 | self.alphabet = alphabet 40 | self.validate_fun = validate_fun 41 | 42 | def filter(self, label): 43 | if self.normalize: 44 | label = unicodedata.normalize("NFKD", label.strip()).encode( 45 | "ascii", "ignore").decode("ascii", "ignore") 46 | label = self.validate_fun(label) 47 | if self.alphabet and label and not self.alphabet.CanEncode(label): 48 | label = None 49 | return label 50 | 51 | 52 | def init_worker(params): 53 | global FILTER_OBJ # pylint: disable=global-statement 54 | global AUDIO_DIR # pylint: disable=global-statement 55 | AUDIO_DIR = params.audio_dir if params.audio_dir else os.path.join( 56 | params.tsv_dir, "clips") 57 | validate_label = get_validate_label(params) 58 | alphabet = Alphabet( 59 | params.filter_alphabet) if params.filter_alphabet else None 60 | FILTER_OBJ = LabelFilter(params.normalize, alphabet, validate_label) 61 | 62 | 63 | def one_sample(sample): 64 | """ Take an audio file, and optionally convert it to 16kHz WAV """ 65 | global AUDIO_DIR 66 | source_filename = sample[0] 67 | if not os.path.splitext(source_filename.lower())[1] == ".wav": 68 | source_filename += ".wav" 69 | # Storing wav files next to the mp3 ones - just with a different suffix 70 | output_filename = f"{sample[2]}.wav" 71 | output_filepath = os.path.join(AUDIO_DIR, output_filename) 72 | _maybe_convert_wav(source_filename, output_filepath) 73 | file_size = -1 74 | frames = 0 75 | if os.path.exists(output_filepath): 76 | file_size = os.path.getsize(output_filepath) 77 | if file_size == 0: 78 | frames = 0 79 | else: 80 | frames = int( 81 | subprocess.check_output( 82 | ["soxi", "-s", output_filepath], stderr=subprocess.STDOUT 83 | ) 84 | ) 85 | label = FILTER_OBJ.filter(sample[1]) 86 | rows = [] 87 | counter = get_counter() 88 | if file_size == -1: 89 | # Excluding samples that failed upon conversion 90 | counter["failed"] += 1 91 | elif label is None: 92 | # Excluding samples that failed on label validation 93 | counter["invalid_label"] += 1 94 | # + 1 added for filtering surname dataset with too short audio files 95 | elif int(frames / SAMPLE_RATE * 1000 / 10 / 2) < len(str(label)) + 1: 96 | # Excluding samples that are too short to fit the transcript 97 | counter["too_short"] += 1 98 | elif frames / SAMPLE_RATE > MAX_SECS: 99 | # Excluding very long samples to keep a reasonable batch-size 100 | counter["too_long"] += 1 101 | else: 102 | # This one is good - keep it for the target CSV 103 | rows.append((os.path.split(output_filename) 104 | [-1], file_size, label, sample[2])) 105 | counter["imported_time"] += frames 106 | counter["all"] += 1 107 | counter["total_time"] += frames 108 | 109 | return (counter, rows) 110 | 111 | 112 | def convert_transcript(transcript): 113 | transcript = transcript.replace("'", "’") 114 | # transcript = transcript.replace("-", " ") 115 | return transcript.strip() 116 | 117 | 118 | def _maybe_convert_set(dataset_dir, audio_dir, filter_obj, space_after_every_character=None, rows=None): 119 | # iterate over all data lists and write converted version near them 120 | speaker_iterator = 1 121 | samples = [] 122 | total_file_dict = dict() 123 | for subdir, dirs, files in os.walk(dataset_dir): 124 | for file in files: 125 | # Get audiofile path and transcript for each sentence in tsv 126 | if file.endswith(".data"): 127 | file_path = os.path.join(subdir, file) 128 | file = open(file_path, mode="r") 129 | data = [] 130 | file_folder = os.path.join( 131 | os.path.dirname(subdir), "wav") 132 | file_dict = dict() 133 | for row in file.readlines(): 134 | if row.isspace(): 135 | continue 136 | splitted_row = row.replace("\n", "").replace( 137 | " wav ", ".wav ").split(" ", 1) 138 | if len(splitted_row) != 2: 139 | continue 140 | file_name, transcript = splitted_row 141 | if file_name.endswith(".wav"): 142 | pass 143 | elif file_name.endswith(".mp3"): 144 | pass 145 | elif file_name.find(".") == -1: 146 | file_name += ".wav" 147 | 148 | if file_name.startswith("/"): 149 | file_name = file_name[1::] 150 | file_name = os.path.join(dataset_dir, file_name) 151 | file_dict[file_name] = convert_transcript(transcript) 152 | 153 | file.close() 154 | 155 | for wav_subdir, wav_dirs, wav_files in os.walk(file_folder): 156 | for wav_file in wav_files: 157 | wav_file_path = os.path.join(wav_subdir, wav_file) 158 | if file_dict.get(wav_file_path) is not None: 159 | total_file_dict[wav_file_path] = file_dict[wav_file_path] 160 | 161 | for key in total_file_dict.keys(): 162 | samples.append((key, total_file_dict[key], speaker_iterator)) 163 | speaker_iterator += 1 164 | del(total_file_dict) 165 | 166 | if rows is None: 167 | rows = [] 168 | counter = get_counter() 169 | num_samples = len(samples) 170 | print("Importing dataset files...") 171 | pool = Pool(initializer=init_worker, initargs=(PARAMS,)) 172 | bar = progressbar.ProgressBar( 173 | max_value=num_samples, widgets=SIMPLE_BAR) 174 | for i, processed in enumerate(pool.imap_unordered(one_sample, samples), start=1): 175 | counter += processed[0] 176 | rows += processed[1] 177 | bar.update(i) 178 | bar.update(num_samples) 179 | pool.close() 180 | pool.join() 181 | 182 | imported_samples = get_imported_samples(counter) 183 | assert counter["all"] == num_samples 184 | assert len(rows) == imported_samples 185 | print_import_report(counter, SAMPLE_RATE, MAX_SECS) 186 | 187 | output_csv = os.path.join(os.path.abspath(audio_dir), "train.csv") 188 | print("Saving new DeepSpeech-formatted CSV file to: ", output_csv) 189 | with open(output_csv, "w", encoding="utf-8", newline="") as output_csv_file: 190 | print("Writing CSV file for DeepSpeech.py as: ", output_csv) 191 | writer = csv.DictWriter(output_csv_file, fieldnames=FIELDNAMES) 192 | writer.writeheader() 193 | bar = progressbar.ProgressBar( 194 | max_value=len(rows), widgets=SIMPLE_BAR) 195 | for filename, file_size, transcript, speaker in bar(rows): 196 | if space_after_every_character: 197 | writer.writerow( 198 | { 199 | "wav_filename": filename, 200 | "wav_filesize": file_size, 201 | "transcript": " ".join(transcript), 202 | } 203 | ) 204 | else: 205 | writer.writerow( 206 | { 207 | "wav_filename": filename, 208 | "wav_filesize": file_size, 209 | "transcript": transcript, 210 | } 211 | ) 212 | return rows 213 | 214 | 215 | def _preprocess_data(tsv_dir, audio_dir, space_after_every_character=False): 216 | set_samples = _maybe_convert_set( 217 | tsv_dir, audio_dir, space_after_every_character) 218 | 219 | 220 | def _maybe_convert_wav(mp3_filename, wav_filename): 221 | if not os.path.exists(wav_filename): 222 | transformer = sox.Transformer() 223 | transformer.convert(samplerate=SAMPLE_RATE, n_channels=CHANNELS) 224 | try: 225 | transformer.build(mp3_filename, wav_filename) 226 | except Exception as e: # TODO: improve exception handling 227 | pass 228 | 229 | 230 | def parse_args(): 231 | parser = get_importers_parser( 232 | description="Import CommonVoice v2.0 corpora") 233 | parser.add_argument("tsv_dir", help="Directory containing tsv files") 234 | parser.add_argument( 235 | "--audio_dir", 236 | help='Directory containing the audio clips - defaults to "/clips"', 237 | ) 238 | parser.add_argument( 239 | "--filter_alphabet", 240 | help="Exclude samples with characters not in provided alphabet", 241 | ) 242 | parser.add_argument( 243 | "--normalize", 244 | action="store_true", 245 | help="Converts diacritic characters to their base ones", 246 | ) 247 | parser.add_argument( 248 | "--space_after_every_character", 249 | action="store_true", 250 | help="To help transcript join by white space", 251 | ) 252 | return parser.parse_args() 253 | 254 | 255 | def main(): 256 | audio_dir = PARAMS.audio_dir if PARAMS.audio_dir else os.path.join( 257 | PARAMS.tsv_dir, "clips") 258 | _preprocess_data(PARAMS.tsv_dir, audio_dir, 259 | PARAMS.space_after_every_character) 260 | 261 | 262 | if __name__ == "__main__": 263 | PARAMS = parse_args() 264 | main() 265 | -------------------------------------------------------------------------------- /wav2vec2/wav2vec_train.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "colab": { 8 | "base_uri": "https://localhost:8080/" 9 | }, 10 | "executionInfo": { 11 | "elapsed": 829, 12 | "status": "ok", 13 | "timestamp": 1641588786523, 14 | "user": { 15 | "displayName": "Yurii Paniv", 16 | "photoUrl": "https://lh3.googleusercontent.com/a/default-user=s64", 17 | "userId": "13095662915325887123" 18 | }, 19 | "user_tz": -120 20 | }, 21 | "id": "YELVqGxMxnbG", 22 | "outputId": "876761c1-2e03-411b-e61b-07ac4ad61377" 23 | }, 24 | "outputs": [ 25 | { 26 | "name": "stdout", 27 | "output_type": "stream", 28 | "text": [ 29 | "Wed Dec 28 21:13:08 2022 \n", 30 | "+-----------------------------------------------------------------------------+\n", 31 | "| NVIDIA-SMI 515.86.01 Driver Version: 515.86.01 CUDA Version: 11.7 |\n", 32 | "|-------------------------------+----------------------+----------------------+\n", 33 | "| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |\n", 34 | "| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |\n", 35 | "| | | MIG M. |\n", 36 | "|===============================+======================+======================|\n", 37 | "| 0 NVIDIA GeForce ... Off | 00000000:0A:00.0 On | N/A |\n", 38 | "| 0% 41C P8 24W / 390W | 1364MiB / 24576MiB | 0% Default |\n", 39 | "| | | N/A |\n", 40 | "+-------------------------------+----------------------+----------------------+\n", 41 | " \n", 42 | "+-----------------------------------------------------------------------------+\n", 43 | "| Processes: |\n", 44 | "| GPU GI CI PID Type Process name GPU Memory |\n", 45 | "| ID ID Usage |\n", 46 | "|=============================================================================|\n", 47 | "| 0 N/A N/A 1345 G /usr/lib/xorg/Xorg 528MiB |\n", 48 | "| 0 N/A N/A 2100 G /usr/bin/kwalletd5 4MiB |\n", 49 | "| 0 N/A N/A 2266 G ...ec/xdg-desktop-portal-kde 4MiB |\n", 50 | "| 0 N/A N/A 2303 G /usr/bin/ksmserver 4MiB |\n", 51 | "| 0 N/A N/A 2305 G /usr/bin/kded5 4MiB |\n", 52 | "| 0 N/A N/A 2306 G /usr/bin/kwin_x11 102MiB |\n", 53 | "| 0 N/A N/A 2367 G /usr/bin/plasmashell 133MiB |\n", 54 | "| 0 N/A N/A 2396 G ...de-authentication-agent-1 4MiB |\n", 55 | "| 0 N/A N/A 2443 G ...x-gnu/libexec/kdeconnectd 4MiB |\n", 56 | "| 0 N/A N/A 2445 G .../usr/bin/telegram-desktop 7MiB |\n", 57 | "| 0 N/A N/A 2459 G /usr/bin/kaccess 4MiB |\n", 58 | "| 0 N/A N/A 2484 G ...1/usr/lib/firefox/firefox 214MiB |\n", 59 | "| 0 N/A N/A 2499 G .../libexec/DiscoverNotifier 4MiB |\n", 60 | "| 0 N/A N/A 2784 G /usr/bin/dolphin 4MiB |\n", 61 | "| 0 N/A N/A 2917 G /usr/bin/dolphin 4MiB |\n", 62 | "| 0 N/A N/A 2997 G /usr/bin/dolphin 4MiB |\n", 63 | "| 0 N/A N/A 3138 G ...gnu/libexec/kf5/kioslave5 4MiB |\n", 64 | "| 0 N/A N/A 3158 G ...gnu/libexec/kf5/kioslave5 4MiB |\n", 65 | "| 0 N/A N/A 3663 G /usr/bin/dolphin 4MiB |\n", 66 | "| 0 N/A N/A 3768 G /usr/bin/dolphin 4MiB |\n", 67 | "| 0 N/A N/A 3908 G ...gnu/libexec/kf5/kioslave5 4MiB |\n", 68 | "| 0 N/A N/A 3964 G ...gnu/libexec/kf5/kioslave5 4MiB |\n", 69 | "| 0 N/A N/A 4610 G ...RendererForSitePerProcess 293MiB |\n", 70 | "+-----------------------------------------------------------------------------+\n" 71 | ] 72 | } 73 | ], 74 | "source": [ 75 | "gpu_info = !nvidia-smi\n", 76 | "gpu_info = '\\n'.join(gpu_info)\n", 77 | "if gpu_info.find('failed') >= 0:\n", 78 | " print('Not connected to a GPU')\n", 79 | "else:\n", 80 | " print(gpu_info)" 81 | ] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": 2, 86 | "metadata": { 87 | "colab": { 88 | "base_uri": "https://localhost:8080/" 89 | }, 90 | "executionInfo": { 91 | "elapsed": 5334, 92 | "status": "ok", 93 | "timestamp": 1641588811766, 94 | "user": { 95 | "displayName": "Yurii Paniv", 96 | "photoUrl": "https://lh3.googleusercontent.com/a/default-user=s64", 97 | "userId": "13095662915325887123" 98 | }, 99 | "user_tz": -120 100 | }, 101 | "id": "2MMXcWFFgCXU", 102 | "outputId": "be9fd72e-4395-4cd0-ff87-631dad046e71" 103 | }, 104 | "outputs": [], 105 | "source": [ 106 | "from datasets import load_from_disk, load_metric, Audio\n", 107 | "\n", 108 | "common_voice_train = load_from_disk(\"cached_dataset/cv_train\")\n", 109 | "common_voice_test = load_from_disk(\"cached_dataset/cv_test\")" 110 | ] 111 | }, 112 | { 113 | "cell_type": "code", 114 | "execution_count": 3, 115 | "metadata": { 116 | "id": "kAR0-2KLkopp" 117 | }, 118 | "outputs": [], 119 | "source": [ 120 | "from transformers import Wav2Vec2FeatureExtractor\n", 121 | "\n", 122 | "feature_extractor = Wav2Vec2FeatureExtractor(feature_size=1, sampling_rate=16000, padding_value=0.0, do_normalize=True, return_attention_mask=True)" 123 | ] 124 | }, 125 | { 126 | "cell_type": "code", 127 | "execution_count": 4, 128 | "metadata": {}, 129 | "outputs": [ 130 | { 131 | "name": "stderr", 132 | "output_type": "stream", 133 | "text": [ 134 | "Special tokens have been added in the vocabulary, make sure the associated word embeddings are fine-tuned or trained.\n" 135 | ] 136 | } 137 | ], 138 | "source": [ 139 | "from transformers import Wav2Vec2CTCTokenizer\n", 140 | "\n", 141 | "tokenizer = Wav2Vec2CTCTokenizer.from_pretrained(\"./\", unk_token=\"[UNK]\", pad_token=\"[PAD]\", word_delimiter_token=\"|\")" 142 | ] 143 | }, 144 | { 145 | "cell_type": "code", 146 | "execution_count": 5, 147 | "metadata": { 148 | "id": "KYZtoW-tlZgl" 149 | }, 150 | "outputs": [], 151 | "source": [ 152 | "from transformers import Wav2Vec2Processor\n", 153 | "\n", 154 | "processor = Wav2Vec2Processor(feature_extractor=feature_extractor, tokenizer=tokenizer)" 155 | ] 156 | }, 157 | { 158 | "cell_type": "code", 159 | "execution_count": 6, 160 | "metadata": { 161 | "id": "tborvC9hx88e" 162 | }, 163 | "outputs": [], 164 | "source": [ 165 | "import torch\n", 166 | "\n", 167 | "from dataclasses import dataclass, field\n", 168 | "from typing import Any, Dict, List, Optional, Union\n", 169 | "\n", 170 | "@dataclass\n", 171 | "class DataCollatorCTCWithPadding:\n", 172 | " \"\"\"\n", 173 | " Data collator that will dynamically pad the inputs received.\n", 174 | " Args:\n", 175 | " processor (:class:`~transformers.Wav2Vec2Processor`)\n", 176 | " The processor used for proccessing the data.\n", 177 | " padding (:obj:`bool`, :obj:`str` or :class:`~transformers.tokenization_utils_base.PaddingStrategy`, `optional`, defaults to :obj:`True`):\n", 178 | " Select a strategy to pad the returned sequences (according to the model's padding side and padding index)\n", 179 | " among:\n", 180 | " * :obj:`True` or :obj:`'longest'`: Pad to the longest sequence in the batch (or no padding if only a single\n", 181 | " sequence if provided).\n", 182 | " * :obj:`'max_length'`: Pad to a maximum length specified with the argument :obj:`max_length` or to the\n", 183 | " maximum acceptable input length for the model if that argument is not provided.\n", 184 | " * :obj:`False` or :obj:`'do_not_pad'` (default): No padding (i.e., can output a batch with sequences of\n", 185 | " different lengths).\n", 186 | " \"\"\"\n", 187 | "\n", 188 | " processor: Wav2Vec2Processor\n", 189 | " padding: Union[bool, str] = True\n", 190 | "\n", 191 | " def __call__(self, features: List[Dict[str, Union[List[int], torch.Tensor]]]) -> Dict[str, torch.Tensor]:\n", 192 | " # split inputs and labels since they have to be of different lenghts and need\n", 193 | " # different padding methods\n", 194 | " input_features = [{\"input_values\": feature[\"input_values\"]} for feature in features]\n", 195 | " label_features = [{\"input_ids\": feature[\"labels\"]} for feature in features]\n", 196 | "\n", 197 | " batch = self.processor.pad(\n", 198 | " input_features,\n", 199 | " padding=self.padding,\n", 200 | " return_tensors=\"pt\",\n", 201 | " )\n", 202 | " with self.processor.as_target_processor():\n", 203 | " labels_batch = self.processor.pad(\n", 204 | " label_features,\n", 205 | " padding=self.padding,\n", 206 | " return_tensors=\"pt\",\n", 207 | " )\n", 208 | "\n", 209 | " # replace padding with -100 to ignore loss correctly\n", 210 | " labels = labels_batch[\"input_ids\"].masked_fill(labels_batch.attention_mask.ne(1), -100)\n", 211 | "\n", 212 | " batch[\"labels\"] = labels\n", 213 | "\n", 214 | " return batch" 215 | ] 216 | }, 217 | { 218 | "cell_type": "code", 219 | "execution_count": 7, 220 | "metadata": { 221 | "id": "lbQf5GuZyQ4_" 222 | }, 223 | "outputs": [], 224 | "source": [ 225 | "data_collator = DataCollatorCTCWithPadding(processor=processor, padding=True)" 226 | ] 227 | }, 228 | { 229 | "cell_type": "code", 230 | "execution_count": 8, 231 | "metadata": { 232 | "id": "9Xsux2gmyXso" 233 | }, 234 | "outputs": [], 235 | "source": [ 236 | "wer_metric = load_metric(\"wer\")\n", 237 | "cer_metric = load_metric(\"cer\")\n", 238 | "metrics = [wer_metric, cer_metric]" 239 | ] 240 | }, 241 | { 242 | "cell_type": "code", 243 | "execution_count": 9, 244 | "metadata": { 245 | "id": "1XZ-kjweyTy_" 246 | }, 247 | "outputs": [], 248 | "source": [ 249 | "import numpy as np\n", 250 | "\n", 251 | "def compute_metrics(pred):\n", 252 | " pred_logits = pred.predictions\n", 253 | " pred_ids = np.argmax(pred_logits, axis=-1)\n", 254 | "\n", 255 | " pred.label_ids[pred.label_ids == -100] = processor.tokenizer.pad_token_id\n", 256 | "\n", 257 | " pred_str = processor.batch_decode(pred_ids)\n", 258 | " # we do not want to group tokens when computing the metrics\n", 259 | " label_str = processor.batch_decode(pred.label_ids, group_tokens=False)\n", 260 | "\n", 261 | " wer = wer_metric.compute(predictions=pred_str, references=label_str)\n", 262 | " cer = cer_metric.compute(predictions=pred_str, references=label_str)\n", 263 | "\n", 264 | " return {\"wer\": wer, \"cer\": cer}" 265 | ] 266 | }, 267 | { 268 | "cell_type": "code", 269 | "execution_count": 10, 270 | "metadata": { 271 | "colab": { 272 | "base_uri": "https://localhost:8080/" 273 | }, 274 | "executionInfo": { 275 | "elapsed": 9496, 276 | "status": "ok", 277 | "timestamp": 1641588938616, 278 | "user": { 279 | "displayName": "Yurii Paniv", 280 | "photoUrl": "https://lh3.googleusercontent.com/a/default-user=s64", 281 | "userId": "13095662915325887123" 282 | }, 283 | "user_tz": -120 284 | }, 285 | "id": "e7cqAWIayn6w", 286 | "outputId": "b7b20ce9-e1b2-473f-8032-2a75f98dfa9e" 287 | }, 288 | "outputs": [ 289 | { 290 | "name": "stderr", 291 | "output_type": "stream", 292 | "text": [ 293 | "Some weights of the model checkpoint at facebook/wav2vec2-xls-r-300m were not used when initializing Wav2Vec2ForCTC: ['project_q.weight', 'quantizer.weight_proj.weight', 'project_q.bias', 'quantizer.weight_proj.bias', 'project_hid.bias', 'project_hid.weight', 'quantizer.codevectors']\n", 294 | "- This IS expected if you are initializing Wav2Vec2ForCTC from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).\n", 295 | "- This IS NOT expected if you are initializing Wav2Vec2ForCTC from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).\n", 296 | "Some weights of Wav2Vec2ForCTC were not initialized from the model checkpoint at facebook/wav2vec2-xls-r-300m and are newly initialized: ['lm_head.bias', 'lm_head.weight']\n", 297 | "You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n" 298 | ] 299 | } 300 | ], 301 | "source": [ 302 | "from transformers import Wav2Vec2ForCTC\n", 303 | "\n", 304 | "model = Wav2Vec2ForCTC.from_pretrained(\n", 305 | " \"facebook/wav2vec2-xls-r-300m\", \n", 306 | " attention_dropout=0.3,\n", 307 | " hidden_dropout=0.3,\n", 308 | " feat_proj_dropout=0.3,\n", 309 | " mask_time_prob=0.05,\n", 310 | " layerdrop=0.3,\n", 311 | " ctc_loss_reduction=\"mean\", \n", 312 | " pad_token_id=processor.tokenizer.pad_token_id,\n", 313 | " vocab_size=len(processor.tokenizer),\n", 314 | ")" 315 | ] 316 | }, 317 | { 318 | "cell_type": "code", 319 | "execution_count": 11, 320 | "metadata": { 321 | "id": "oGI8zObtZ3V0" 322 | }, 323 | "outputs": [ 324 | { 325 | "name": "stderr", 326 | "output_type": "stream", 327 | "text": [ 328 | "/home/robinhad/Projects/unchanged/voice-recognition-ua/env/lib/python3.10/site-packages/transformers/models/wav2vec2/modeling_wav2vec2.py:1618: FutureWarning: The method `freeze_feature_extractor` is deprecated and will be removed in Transformers v5.Please use the equivalent `freeze_feature_encoder` method instead.\n", 329 | " warnings.warn(\n" 330 | ] 331 | } 332 | ], 333 | "source": [ 334 | "model.freeze_feature_extractor()" 335 | ] 336 | }, 337 | { 338 | "cell_type": "code", 339 | "execution_count": 12, 340 | "metadata": { 341 | "id": "KbeKSV7uzGPP" 342 | }, 343 | "outputs": [], 344 | "source": [ 345 | "from transformers import TrainingArguments\n", 346 | "\n", 347 | "repo_name = \"wav2vec2-xls-r-base-uk\"\n", 348 | "\n", 349 | "training_args = TrainingArguments(\n", 350 | " output_dir=repo_name,\n", 351 | " group_by_length=True,\n", 352 | " per_device_train_batch_size=24,\n", 353 | " per_device_eval_batch_size=24, \n", 354 | " gradient_accumulation_steps=6,\n", 355 | " eval_accumulation_steps=6,\n", 356 | " evaluation_strategy=\"epoch\",\n", 357 | " save_strategy=\"epoch\",\n", 358 | " logging_strategy=\"epoch\",\n", 359 | " num_train_epochs=150,\n", 360 | " gradient_checkpointing=True,\n", 361 | " fp16=True,\n", 362 | " #save_steps=1,\n", 363 | " #eval_steps=1,\n", 364 | " #logging_steps=1,\n", 365 | " learning_rate=3e-4,\n", 366 | " warmup_steps=500,\n", 367 | " save_total_limit=2,\n", 368 | " report_to=\"tensorboard\",\n", 369 | " load_best_model_at_end=True,\n", 370 | " metric_for_best_model=\"cer\",\n", 371 | " greater_is_better=False\n", 372 | ")" 373 | ] 374 | }, 375 | { 376 | "cell_type": "code", 377 | "execution_count": 14, 378 | "metadata": { 379 | "colab": { 380 | "base_uri": "https://localhost:8080/" 381 | }, 382 | "executionInfo": { 383 | "elapsed": 11063, 384 | "status": "ok", 385 | "timestamp": 1641588949674, 386 | "user": { 387 | "displayName": "Yurii Paniv", 388 | "photoUrl": "https://lh3.googleusercontent.com/a/default-user=s64", 389 | "userId": "13095662915325887123" 390 | }, 391 | "user_tz": -120 392 | }, 393 | "id": "rY7vBmFCPFgC", 394 | "outputId": "2e89d5ea-5b25-44bf-8492-a6220b0b1c38" 395 | }, 396 | "outputs": [ 397 | { 398 | "name": "stderr", 399 | "output_type": "stream", 400 | "text": [ 401 | "Using cuda_amp half precision backend\n" 402 | ] 403 | } 404 | ], 405 | "source": [ 406 | "from transformers import Trainer\n", 407 | "\n", 408 | "trainer = Trainer(\n", 409 | " model=model,\n", 410 | " data_collator=data_collator,\n", 411 | " args=training_args,\n", 412 | " compute_metrics=compute_metrics,\n", 413 | " train_dataset=common_voice_train,\n", 414 | " eval_dataset=common_voice_test,\n", 415 | " tokenizer=processor.feature_extractor,\n", 416 | ")" 417 | ] 418 | }, 419 | { 420 | "cell_type": "code", 421 | "execution_count": null, 422 | "metadata": { 423 | "colab": { 424 | "base_uri": "https://localhost:8080/", 425 | "height": 409 426 | }, 427 | "id": "9fRr9TG5pGBl", 428 | "outputId": "c2a7c797-326c-4bd2-b167-9d2f41d77def" 429 | }, 430 | "outputs": [ 431 | { 432 | "name": "stderr", 433 | "output_type": "stream", 434 | "text": [ 435 | "Loading model from wav2vec2-xls-r-base-uk/checkpoint-7505.\n", 436 | "The following columns in the training set don't have a corresponding argument in `Wav2Vec2ForCTC.forward` and have been ignored: input_length. If input_length are not expected by `Wav2Vec2ForCTC.forward`, you can safely ignore this message.\n", 437 | "/home/robinhad/Projects/unchanged/voice-recognition-ua/env/lib/python3.10/site-packages/transformers/optimization.py:306: FutureWarning: This implementation of AdamW is deprecated and will be removed in a future version. Use the PyTorch implementation torch.optim.AdamW instead, or set `no_deprecation_warning=True` to disable this warning\n", 438 | " warnings.warn(\n", 439 | "***** Running training *****\n", 440 | " Num examples = 11463\n", 441 | " Num Epochs = 150\n", 442 | " Instantaneous batch size per device = 24\n", 443 | " Total train batch size (w. parallel, distributed & accumulation) = 144\n", 444 | " Gradient Accumulation steps = 6\n", 445 | " Total optimization steps = 11850\n", 446 | " Continuing training from checkpoint, will skip to saved global_step\n", 447 | " Continuing training from epoch 95\n", 448 | " Continuing training from global step 7505\n", 449 | " Will skip the first 95 epochs then the first 0 batches in the first epoch. If this takes a lot of time, you can add the `--ignore_data_skip` flag to your launch command, but you will resume the training on data already seen by your model.\n" 450 | ] 451 | }, 452 | { 453 | "data": { 454 | "application/vnd.jupyter.widget-view+json": { 455 | "model_id": "d39c143147e7431a91cf50b54464cbee", 456 | "version_major": 2, 457 | "version_minor": 0 458 | }, 459 | "text/plain": [ 460 | "0it [00:00, ?it/s]" 461 | ] 462 | }, 463 | "metadata": {}, 464 | "output_type": "display_data" 465 | }, 466 | { 467 | "data": { 468 | "text/html": [ 469 | "\n", 470 | "
\n", 471 | " \n", 472 | " \n", 473 | " [ 7910/11850 45:49 < 7:28:05, 0.15 it/s, Epoch 100.11/150]\n", 474 | "
\n", 475 | " \n", 476 | " \n", 477 | " \n", 478 | " \n", 479 | " \n", 480 | " \n", 481 | " \n", 482 | " \n", 483 | " \n", 484 | " \n", 485 | " \n", 486 | " \n", 487 | " \n", 488 | " \n", 489 | " \n", 490 | " \n", 491 | " \n", 492 | " \n", 493 | " \n", 494 | " \n", 495 | " \n", 496 | " \n", 497 | " \n", 498 | " \n", 499 | " \n", 500 | " \n", 501 | " \n", 502 | " \n", 503 | " \n", 504 | " \n", 505 | " \n", 506 | " \n", 507 | " \n", 508 | " \n", 509 | " \n", 510 | " \n", 511 | " \n", 512 | " \n", 513 | " \n", 514 | " \n", 515 | " \n", 516 | " \n", 517 | " \n", 518 | " \n", 519 | " \n", 520 | " \n", 521 | " \n", 522 | "
EpochTraining LossValidation LossWerCer
950.2712000.5969270.5195650.128453
960.2793000.5957890.5165180.128272
970.2768000.6234000.5125820.127275
980.2660000.6172450.5191810.130092
990.2816000.6067720.5124010.128527

" 523 | ], 524 | "text/plain": [ 525 | "" 526 | ] 527 | }, 528 | "metadata": {}, 529 | "output_type": "display_data" 530 | }, 531 | { 532 | "name": "stderr", 533 | "output_type": "stream", 534 | "text": [ 535 | "The following columns in the evaluation set don't have a corresponding argument in `Wav2Vec2ForCTC.forward` and have been ignored: input_length. If input_length are not expected by `Wav2Vec2ForCTC.forward`, you can safely ignore this message.\n", 536 | "***** Running Evaluation *****\n", 537 | " Num examples = 6783\n", 538 | " Batch size = 24\n", 539 | "Saving model checkpoint to wav2vec2-xls-r-base-uk/checkpoint-7584\n", 540 | "Configuration saved in wav2vec2-xls-r-base-uk/checkpoint-7584/config.json\n", 541 | "Model weights saved in wav2vec2-xls-r-base-uk/checkpoint-7584/pytorch_model.bin\n", 542 | "Feature extractor saved in wav2vec2-xls-r-base-uk/checkpoint-7584/preprocessor_config.json\n", 543 | "Deleting older checkpoint [wav2vec2-xls-r-base-uk/checkpoint-7505] due to args.save_total_limit\n", 544 | "The following columns in the evaluation set don't have a corresponding argument in `Wav2Vec2ForCTC.forward` and have been ignored: input_length. If input_length are not expected by `Wav2Vec2ForCTC.forward`, you can safely ignore this message.\n", 545 | "***** Running Evaluation *****\n", 546 | " Num examples = 6783\n", 547 | " Batch size = 24\n", 548 | "Saving model checkpoint to wav2vec2-xls-r-base-uk/checkpoint-7663\n", 549 | "Configuration saved in wav2vec2-xls-r-base-uk/checkpoint-7663/config.json\n", 550 | "Model weights saved in wav2vec2-xls-r-base-uk/checkpoint-7663/pytorch_model.bin\n", 551 | "Feature extractor saved in wav2vec2-xls-r-base-uk/checkpoint-7663/preprocessor_config.json\n", 552 | "Deleting older checkpoint [wav2vec2-xls-r-base-uk/checkpoint-7584] due to args.save_total_limit\n", 553 | "The following columns in the evaluation set don't have a corresponding argument in `Wav2Vec2ForCTC.forward` and have been ignored: input_length. If input_length are not expected by `Wav2Vec2ForCTC.forward`, you can safely ignore this message.\n", 554 | "***** Running Evaluation *****\n", 555 | " Num examples = 6783\n", 556 | " Batch size = 24\n", 557 | "Saving model checkpoint to wav2vec2-xls-r-base-uk/checkpoint-7742\n", 558 | "Configuration saved in wav2vec2-xls-r-base-uk/checkpoint-7742/config.json\n", 559 | "Model weights saved in wav2vec2-xls-r-base-uk/checkpoint-7742/pytorch_model.bin\n", 560 | "Feature extractor saved in wav2vec2-xls-r-base-uk/checkpoint-7742/preprocessor_config.json\n", 561 | "Deleting older checkpoint [wav2vec2-xls-r-base-uk/checkpoint-7663] due to args.save_total_limit\n", 562 | "The following columns in the evaluation set don't have a corresponding argument in `Wav2Vec2ForCTC.forward` and have been ignored: input_length. If input_length are not expected by `Wav2Vec2ForCTC.forward`, you can safely ignore this message.\n", 563 | "***** Running Evaluation *****\n", 564 | " Num examples = 6783\n", 565 | " Batch size = 24\n", 566 | "Saving model checkpoint to wav2vec2-xls-r-base-uk/checkpoint-7821\n", 567 | "Configuration saved in wav2vec2-xls-r-base-uk/checkpoint-7821/config.json\n", 568 | "Model weights saved in wav2vec2-xls-r-base-uk/checkpoint-7821/pytorch_model.bin\n", 569 | "Feature extractor saved in wav2vec2-xls-r-base-uk/checkpoint-7821/preprocessor_config.json\n", 570 | "Deleting older checkpoint [wav2vec2-xls-r-base-uk/checkpoint-7742] due to args.save_total_limit\n", 571 | "The following columns in the evaluation set don't have a corresponding argument in `Wav2Vec2ForCTC.forward` and have been ignored: input_length. If input_length are not expected by `Wav2Vec2ForCTC.forward`, you can safely ignore this message.\n", 572 | "***** Running Evaluation *****\n", 573 | " Num examples = 6783\n", 574 | " Batch size = 24\n", 575 | "Saving model checkpoint to wav2vec2-xls-r-base-uk/checkpoint-7900\n", 576 | "Configuration saved in wav2vec2-xls-r-base-uk/checkpoint-7900/config.json\n", 577 | "Model weights saved in wav2vec2-xls-r-base-uk/checkpoint-7900/pytorch_model.bin\n", 578 | "Feature extractor saved in wav2vec2-xls-r-base-uk/checkpoint-7900/preprocessor_config.json\n", 579 | "Deleting older checkpoint [wav2vec2-xls-r-base-uk/checkpoint-7821] due to args.save_total_limit\n" 580 | ] 581 | } 582 | ], 583 | "source": [ 584 | "trainer.train(resume_from_checkpoint=True)" 585 | ] 586 | }, 587 | { 588 | "cell_type": "code", 589 | "execution_count": null, 590 | "metadata": {}, 591 | "outputs": [], 592 | "source": [ 593 | "trainer.create_model_card()" 594 | ] 595 | } 596 | ], 597 | "metadata": { 598 | "accelerator": "GPU", 599 | "colab": { 600 | "collapsed_sections": [], 601 | "machine_shape": "hm", 602 | "name": "Копія записника \"Fine-Tune XLS-R on Common Voice.ipynb\"", 603 | "provenance": [ 604 | { 605 | "file_id": "https://github.com/patrickvonplaten/notebooks/blob/master/Fine_Tune_XLS_R_on_Common_Voice.ipynb", 606 | "timestamp": 1641583715050 607 | } 608 | ] 609 | }, 610 | "kernelspec": { 611 | "display_name": "Python 3 (ipykernel)", 612 | "language": "python", 613 | "name": "python3" 614 | }, 615 | "language_info": { 616 | "codemirror_mode": { 617 | "name": "ipython", 618 | "version": 3 619 | }, 620 | "file_extension": ".py", 621 | "mimetype": "text/x-python", 622 | "name": "python", 623 | "nbconvert_exporter": "python", 624 | "pygments_lexer": "ipython3", 625 | "version": "3.10.6" 626 | }, 627 | "vscode": { 628 | "interpreter": { 629 | "hash": "a5cdd9abf8df3af0fd61fdb3838d6c6f2f66a9ba4bf4484f45cd88abf9f04fe9" 630 | } 631 | }, 632 | "widgets": { 633 | "application/vnd.jupyter.widget-state+json": { 634 | "04ec68b059df4c628839c3ac29e2ebdd": { 635 | "model_module": "@jupyter-widgets/controls", 636 | "model_module_version": "1.5.0", 637 | "model_name": "DescriptionStyleModel", 638 | "state": { 639 | "_model_module": "@jupyter-widgets/controls", 640 | "_model_module_version": "1.5.0", 641 | "_model_name": "DescriptionStyleModel", 642 | "_view_count": null, 643 | "_view_module": "@jupyter-widgets/base", 644 | "_view_module_version": "1.2.0", 645 | "_view_name": "StyleView", 646 | "description_width": "" 647 | } 648 | }, 649 | "05d8496d54174ae298c319b0194fc710": { 650 | "model_module": "@jupyter-widgets/base", 651 | "model_module_version": "1.2.0", 652 | "model_name": "LayoutModel", 653 | "state": { 654 | "_model_module": "@jupyter-widgets/base", 655 | "_model_module_version": "1.2.0", 656 | "_model_name": "LayoutModel", 657 | "_view_count": null, 658 | "_view_module": "@jupyter-widgets/base", 659 | "_view_module_version": "1.2.0", 660 | "_view_name": "LayoutView", 661 | "align_content": null, 662 | "align_items": null, 663 | "align_self": null, 664 | "border": null, 665 | "bottom": null, 666 | "display": null, 667 | "flex": null, 668 | "flex_flow": null, 669 | "grid_area": null, 670 | "grid_auto_columns": null, 671 | "grid_auto_flow": null, 672 | "grid_auto_rows": null, 673 | "grid_column": null, 674 | "grid_gap": null, 675 | "grid_row": null, 676 | "grid_template_areas": null, 677 | "grid_template_columns": null, 678 | "grid_template_rows": null, 679 | "height": null, 680 | "justify_content": null, 681 | "justify_items": null, 682 | "left": null, 683 | "margin": null, 684 | "max_height": null, 685 | "max_width": null, 686 | "min_height": null, 687 | "min_width": null, 688 | "object_fit": null, 689 | "object_position": null, 690 | "order": null, 691 | "overflow": null, 692 | "overflow_x": null, 693 | "overflow_y": null, 694 | "padding": null, 695 | "right": null, 696 | "top": null, 697 | "visibility": null, 698 | "width": null 699 | } 700 | }, 701 | "116786d9364a4a57b521cddaabeda688": { 702 | "model_module": "@jupyter-widgets/controls", 703 | "model_module_version": "1.5.0", 704 | "model_name": "HBoxModel", 705 | "state": { 706 | "_dom_classes": [], 707 | "_model_module": "@jupyter-widgets/controls", 708 | "_model_module_version": "1.5.0", 709 | "_model_name": "HBoxModel", 710 | "_view_count": null, 711 | "_view_module": "@jupyter-widgets/controls", 712 | "_view_module_version": "1.5.0", 713 | "_view_name": "HBoxView", 714 | "box_style": "", 715 | "children": [ 716 | "IPY_MODEL_a1e2c04dc2cb45ea80bec125e3dbf56f", 717 | "IPY_MODEL_b6d46d40efa14b21814f41531f5a2f41", 718 | "IPY_MODEL_d8bf8dc5d6c84140a4e96c9c435b8f17" 719 | ], 720 | "layout": "IPY_MODEL_9baa2f69aa9c4387bf1086a04ed78420" 721 | } 722 | }, 723 | "18bc63944343440f837cdff76db004fc": { 724 | "model_module": "@jupyter-widgets/controls", 725 | "model_module_version": "1.5.0", 726 | "model_name": "HTMLModel", 727 | "state": { 728 | "_dom_classes": [], 729 | "_model_module": "@jupyter-widgets/controls", 730 | "_model_module_version": "1.5.0", 731 | "_model_name": "HTMLModel", 732 | "_view_count": null, 733 | "_view_module": "@jupyter-widgets/controls", 734 | "_view_module_version": "1.5.0", 735 | "_view_name": "HTMLView", 736 | "description": "", 737 | "description_tooltip": null, 738 | "layout": "IPY_MODEL_a4ae510b4f3845f891a796cf844fc2bb", 739 | "placeholder": "​", 740 | "style": "IPY_MODEL_e6e50da6516847878309fdc5c463edb3", 741 | "value": " 6962/6962 [01:46<00:00, 78.15ex/s]" 742 | } 743 | }, 744 | "1f3abdf2e0f6459da4179a94d691c4c4": { 745 | "model_module": "@jupyter-widgets/controls", 746 | "model_module_version": "1.5.0", 747 | "model_name": "FloatProgressModel", 748 | "state": { 749 | "_dom_classes": [], 750 | "_model_module": "@jupyter-widgets/controls", 751 | "_model_module_version": "1.5.0", 752 | "_model_name": "FloatProgressModel", 753 | "_view_count": null, 754 | "_view_module": "@jupyter-widgets/controls", 755 | "_view_module_version": "1.5.0", 756 | "_view_name": "ProgressView", 757 | "bar_style": "success", 758 | "description": "", 759 | "description_tooltip": null, 760 | "layout": "IPY_MODEL_c31a747e18df4b4aa4449a30e387448c", 761 | "max": 1, 762 | "min": 0, 763 | "orientation": "horizontal", 764 | "style": "IPY_MODEL_414efa8a08cd491cb78af8a95a151daa", 765 | "value": 1 766 | } 767 | }, 768 | "22ba979142074f1d976e1a905544fd2d": { 769 | "model_module": "@jupyter-widgets/base", 770 | "model_module_version": "1.2.0", 771 | "model_name": "LayoutModel", 772 | "state": { 773 | "_model_module": "@jupyter-widgets/base", 774 | "_model_module_version": "1.2.0", 775 | "_model_name": "LayoutModel", 776 | "_view_count": null, 777 | "_view_module": "@jupyter-widgets/base", 778 | "_view_module_version": "1.2.0", 779 | "_view_name": "LayoutView", 780 | "align_content": null, 781 | "align_items": null, 782 | "align_self": null, 783 | "border": null, 784 | "bottom": null, 785 | "display": null, 786 | "flex": null, 787 | "flex_flow": null, 788 | "grid_area": null, 789 | "grid_auto_columns": null, 790 | "grid_auto_flow": null, 791 | "grid_auto_rows": null, 792 | "grid_column": null, 793 | "grid_gap": null, 794 | "grid_row": null, 795 | "grid_template_areas": null, 796 | "grid_template_columns": null, 797 | "grid_template_rows": null, 798 | "height": null, 799 | "justify_content": null, 800 | "justify_items": null, 801 | "left": null, 802 | "margin": null, 803 | "max_height": null, 804 | "max_width": null, 805 | "min_height": null, 806 | "min_width": null, 807 | "object_fit": null, 808 | "object_position": null, 809 | "order": null, 810 | "overflow": null, 811 | "overflow_x": null, 812 | "overflow_y": null, 813 | "padding": null, 814 | "right": null, 815 | "top": null, 816 | "visibility": null, 817 | "width": null 818 | } 819 | }, 820 | "3dedffa30b774426bd474072a3a0d591": { 821 | "model_module": "@jupyter-widgets/controls", 822 | "model_module_version": "1.5.0", 823 | "model_name": "DescriptionStyleModel", 824 | "state": { 825 | "_model_module": "@jupyter-widgets/controls", 826 | "_model_module_version": "1.5.0", 827 | "_model_name": "DescriptionStyleModel", 828 | "_view_count": null, 829 | "_view_module": "@jupyter-widgets/base", 830 | "_view_module_version": "1.2.0", 831 | "_view_name": "StyleView", 832 | "description_width": "" 833 | } 834 | }, 835 | "414efa8a08cd491cb78af8a95a151daa": { 836 | "model_module": "@jupyter-widgets/controls", 837 | "model_module_version": "1.5.0", 838 | "model_name": "ProgressStyleModel", 839 | "state": { 840 | "_model_module": "@jupyter-widgets/controls", 841 | "_model_module_version": "1.5.0", 842 | "_model_name": "ProgressStyleModel", 843 | "_view_count": null, 844 | "_view_module": "@jupyter-widgets/base", 845 | "_view_module_version": "1.2.0", 846 | "_view_name": "StyleView", 847 | "bar_color": null, 848 | "description_width": "" 849 | } 850 | }, 851 | "427056895c674c428400bee0f5b43995": { 852 | "model_module": "@jupyter-widgets/base", 853 | "model_module_version": "1.2.0", 854 | "model_name": "LayoutModel", 855 | "state": { 856 | "_model_module": "@jupyter-widgets/base", 857 | "_model_module_version": "1.2.0", 858 | "_model_name": "LayoutModel", 859 | "_view_count": null, 860 | "_view_module": "@jupyter-widgets/base", 861 | "_view_module_version": "1.2.0", 862 | "_view_name": "LayoutView", 863 | "align_content": null, 864 | "align_items": null, 865 | "align_self": null, 866 | "border": null, 867 | "bottom": null, 868 | "display": null, 869 | "flex": null, 870 | "flex_flow": null, 871 | "grid_area": null, 872 | "grid_auto_columns": null, 873 | "grid_auto_flow": null, 874 | "grid_auto_rows": null, 875 | "grid_column": null, 876 | "grid_gap": null, 877 | "grid_row": null, 878 | "grid_template_areas": null, 879 | "grid_template_columns": null, 880 | "grid_template_rows": null, 881 | "height": null, 882 | "justify_content": null, 883 | "justify_items": null, 884 | "left": null, 885 | "margin": null, 886 | "max_height": null, 887 | "max_width": null, 888 | "min_height": null, 889 | "min_width": null, 890 | "object_fit": null, 891 | "object_position": null, 892 | "order": null, 893 | "overflow": null, 894 | "overflow_x": null, 895 | "overflow_y": null, 896 | "padding": null, 897 | "right": null, 898 | "top": null, 899 | "visibility": null, 900 | "width": null 901 | } 902 | }, 903 | "445c84e1e2e541f2a54fb989def386ae": { 904 | "model_module": "@jupyter-widgets/base", 905 | "model_module_version": "1.2.0", 906 | "model_name": "LayoutModel", 907 | "state": { 908 | "_model_module": "@jupyter-widgets/base", 909 | "_model_module_version": "1.2.0", 910 | "_model_name": "LayoutModel", 911 | "_view_count": null, 912 | "_view_module": "@jupyter-widgets/base", 913 | "_view_module_version": "1.2.0", 914 | "_view_name": "LayoutView", 915 | "align_content": null, 916 | "align_items": null, 917 | "align_self": null, 918 | "border": null, 919 | "bottom": null, 920 | "display": null, 921 | "flex": null, 922 | "flex_flow": null, 923 | "grid_area": null, 924 | "grid_auto_columns": null, 925 | "grid_auto_flow": null, 926 | "grid_auto_rows": null, 927 | "grid_column": null, 928 | "grid_gap": null, 929 | "grid_row": null, 930 | "grid_template_areas": null, 931 | "grid_template_columns": null, 932 | "grid_template_rows": null, 933 | "height": null, 934 | "justify_content": null, 935 | "justify_items": null, 936 | "left": null, 937 | "margin": null, 938 | "max_height": null, 939 | "max_width": null, 940 | "min_height": null, 941 | "min_width": null, 942 | "object_fit": null, 943 | "object_position": null, 944 | "order": null, 945 | "overflow": null, 946 | "overflow_x": null, 947 | "overflow_y": null, 948 | "padding": null, 949 | "right": null, 950 | "top": null, 951 | "visibility": null, 952 | "width": null 953 | } 954 | }, 955 | "48c60be3ca9349a295b83f65769c7f27": { 956 | "model_module": "@jupyter-widgets/controls", 957 | "model_module_version": "1.5.0", 958 | "model_name": "HTMLModel", 959 | "state": { 960 | "_dom_classes": [], 961 | "_model_module": "@jupyter-widgets/controls", 962 | "_model_module_version": "1.5.0", 963 | "_model_name": "HTMLModel", 964 | "_view_count": null, 965 | "_view_module": "@jupyter-widgets/controls", 966 | "_view_module_version": "1.5.0", 967 | "_view_name": "HTMLView", 968 | "description": "", 969 | "description_tooltip": null, 970 | "layout": "IPY_MODEL_05d8496d54174ae298c319b0194fc710", 971 | "placeholder": "​", 972 | "style": "IPY_MODEL_3dedffa30b774426bd474072a3a0d591", 973 | "value": " 1/1 [00:00<00:00, 11.09ba/s]" 974 | } 975 | }, 976 | "5815ae1348994bfebba4a8e968489a96": { 977 | "model_module": "@jupyter-widgets/controls", 978 | "model_module_version": "1.5.0", 979 | "model_name": "DescriptionStyleModel", 980 | "state": { 981 | "_model_module": "@jupyter-widgets/controls", 982 | "_model_module_version": "1.5.0", 983 | "_model_name": "DescriptionStyleModel", 984 | "_view_count": null, 985 | "_view_module": "@jupyter-widgets/base", 986 | "_view_module_version": "1.2.0", 987 | "_view_name": "StyleView", 988 | "description_width": "" 989 | } 990 | }, 991 | "5c2a7fea8c434d51ada69a0854b88baf": { 992 | "model_module": "@jupyter-widgets/base", 993 | "model_module_version": "1.2.0", 994 | "model_name": "LayoutModel", 995 | "state": { 996 | "_model_module": "@jupyter-widgets/base", 997 | "_model_module_version": "1.2.0", 998 | "_model_name": "LayoutModel", 999 | "_view_count": null, 1000 | "_view_module": "@jupyter-widgets/base", 1001 | "_view_module_version": "1.2.0", 1002 | "_view_name": "LayoutView", 1003 | "align_content": null, 1004 | "align_items": null, 1005 | "align_self": null, 1006 | "border": null, 1007 | "bottom": null, 1008 | "display": null, 1009 | "flex": null, 1010 | "flex_flow": null, 1011 | "grid_area": null, 1012 | "grid_auto_columns": null, 1013 | "grid_auto_flow": null, 1014 | "grid_auto_rows": null, 1015 | "grid_column": null, 1016 | "grid_gap": null, 1017 | "grid_row": null, 1018 | "grid_template_areas": null, 1019 | "grid_template_columns": null, 1020 | "grid_template_rows": null, 1021 | "height": null, 1022 | "justify_content": null, 1023 | "justify_items": null, 1024 | "left": null, 1025 | "margin": null, 1026 | "max_height": null, 1027 | "max_width": null, 1028 | "min_height": null, 1029 | "min_width": null, 1030 | "object_fit": null, 1031 | "object_position": null, 1032 | "order": null, 1033 | "overflow": null, 1034 | "overflow_x": null, 1035 | "overflow_y": null, 1036 | "padding": null, 1037 | "right": null, 1038 | "top": null, 1039 | "visibility": null, 1040 | "width": null 1041 | } 1042 | }, 1043 | "68502fb433564eee8dfdf272ed7e4f56": { 1044 | "model_module": "@jupyter-widgets/controls", 1045 | "model_module_version": "1.5.0", 1046 | "model_name": "HTMLModel", 1047 | "state": { 1048 | "_dom_classes": [], 1049 | "_model_module": "@jupyter-widgets/controls", 1050 | "_model_module_version": "1.5.0", 1051 | "_model_name": "HTMLModel", 1052 | "_view_count": null, 1053 | "_view_module": "@jupyter-widgets/controls", 1054 | "_view_module_version": "1.5.0", 1055 | "_view_name": "HTMLView", 1056 | "description": "", 1057 | "description_tooltip": null, 1058 | "layout": "IPY_MODEL_5c2a7fea8c434d51ada69a0854b88baf", 1059 | "placeholder": "​", 1060 | "style": "IPY_MODEL_6c80bd8a8fe14a5989fe27445c14650f", 1061 | "value": "100%" 1062 | } 1063 | }, 1064 | "6c80bd8a8fe14a5989fe27445c14650f": { 1065 | "model_module": "@jupyter-widgets/controls", 1066 | "model_module_version": "1.5.0", 1067 | "model_name": "DescriptionStyleModel", 1068 | "state": { 1069 | "_model_module": "@jupyter-widgets/controls", 1070 | "_model_module_version": "1.5.0", 1071 | "_model_name": "DescriptionStyleModel", 1072 | "_view_count": null, 1073 | "_view_module": "@jupyter-widgets/base", 1074 | "_view_module_version": "1.2.0", 1075 | "_view_name": "StyleView", 1076 | "description_width": "" 1077 | } 1078 | }, 1079 | "77f1a51099b24831ad8b2be3d2dc833a": { 1080 | "model_module": "@jupyter-widgets/base", 1081 | "model_module_version": "1.2.0", 1082 | "model_name": "LayoutModel", 1083 | "state": { 1084 | "_model_module": "@jupyter-widgets/base", 1085 | "_model_module_version": "1.2.0", 1086 | "_model_name": "LayoutModel", 1087 | "_view_count": null, 1088 | "_view_module": "@jupyter-widgets/base", 1089 | "_view_module_version": "1.2.0", 1090 | "_view_name": "LayoutView", 1091 | "align_content": null, 1092 | "align_items": null, 1093 | "align_self": null, 1094 | "border": null, 1095 | "bottom": null, 1096 | "display": null, 1097 | "flex": null, 1098 | "flex_flow": null, 1099 | "grid_area": null, 1100 | "grid_auto_columns": null, 1101 | "grid_auto_flow": null, 1102 | "grid_auto_rows": null, 1103 | "grid_column": null, 1104 | "grid_gap": null, 1105 | "grid_row": null, 1106 | "grid_template_areas": null, 1107 | "grid_template_columns": null, 1108 | "grid_template_rows": null, 1109 | "height": null, 1110 | "justify_content": null, 1111 | "justify_items": null, 1112 | "left": null, 1113 | "margin": null, 1114 | "max_height": null, 1115 | "max_width": null, 1116 | "min_height": null, 1117 | "min_width": null, 1118 | "object_fit": null, 1119 | "object_position": null, 1120 | "order": null, 1121 | "overflow": null, 1122 | "overflow_x": null, 1123 | "overflow_y": null, 1124 | "padding": null, 1125 | "right": null, 1126 | "top": null, 1127 | "visibility": null, 1128 | "width": null 1129 | } 1130 | }, 1131 | "8b6b7f28751c45c8869aa86eb2a0ab26": { 1132 | "model_module": "@jupyter-widgets/controls", 1133 | "model_module_version": "1.5.0", 1134 | "model_name": "HBoxModel", 1135 | "state": { 1136 | "_dom_classes": [], 1137 | "_model_module": "@jupyter-widgets/controls", 1138 | "_model_module_version": "1.5.0", 1139 | "_model_name": "HBoxModel", 1140 | "_view_count": null, 1141 | "_view_module": "@jupyter-widgets/controls", 1142 | "_view_module_version": "1.5.0", 1143 | "_view_name": "HBoxView", 1144 | "box_style": "", 1145 | "children": [ 1146 | "IPY_MODEL_68502fb433564eee8dfdf272ed7e4f56", 1147 | "IPY_MODEL_1f3abdf2e0f6459da4179a94d691c4c4", 1148 | "IPY_MODEL_48c60be3ca9349a295b83f65769c7f27" 1149 | ], 1150 | "layout": "IPY_MODEL_445c84e1e2e541f2a54fb989def386ae" 1151 | } 1152 | }, 1153 | "9baa2f69aa9c4387bf1086a04ed78420": { 1154 | "model_module": "@jupyter-widgets/base", 1155 | "model_module_version": "1.2.0", 1156 | "model_name": "LayoutModel", 1157 | "state": { 1158 | "_model_module": "@jupyter-widgets/base", 1159 | "_model_module_version": "1.2.0", 1160 | "_model_name": "LayoutModel", 1161 | "_view_count": null, 1162 | "_view_module": "@jupyter-widgets/base", 1163 | "_view_module_version": "1.2.0", 1164 | "_view_name": "LayoutView", 1165 | "align_content": null, 1166 | "align_items": null, 1167 | "align_self": null, 1168 | "border": null, 1169 | "bottom": null, 1170 | "display": null, 1171 | "flex": null, 1172 | "flex_flow": null, 1173 | "grid_area": null, 1174 | "grid_auto_columns": null, 1175 | "grid_auto_flow": null, 1176 | "grid_auto_rows": null, 1177 | "grid_column": null, 1178 | "grid_gap": null, 1179 | "grid_row": null, 1180 | "grid_template_areas": null, 1181 | "grid_template_columns": null, 1182 | "grid_template_rows": null, 1183 | "height": null, 1184 | "justify_content": null, 1185 | "justify_items": null, 1186 | "left": null, 1187 | "margin": null, 1188 | "max_height": null, 1189 | "max_width": null, 1190 | "min_height": null, 1191 | "min_width": null, 1192 | "object_fit": null, 1193 | "object_position": null, 1194 | "order": null, 1195 | "overflow": null, 1196 | "overflow_x": null, 1197 | "overflow_y": null, 1198 | "padding": null, 1199 | "right": null, 1200 | "top": null, 1201 | "visibility": null, 1202 | "width": null 1203 | } 1204 | }, 1205 | "9c875952cdd649a5bab87de9bb3f5200": { 1206 | "model_module": "@jupyter-widgets/controls", 1207 | "model_module_version": "1.5.0", 1208 | "model_name": "DescriptionStyleModel", 1209 | "state": { 1210 | "_model_module": "@jupyter-widgets/controls", 1211 | "_model_module_version": "1.5.0", 1212 | "_model_name": "DescriptionStyleModel", 1213 | "_view_count": null, 1214 | "_view_module": "@jupyter-widgets/base", 1215 | "_view_module_version": "1.2.0", 1216 | "_view_name": "StyleView", 1217 | "description_width": "" 1218 | } 1219 | }, 1220 | "a1e2c04dc2cb45ea80bec125e3dbf56f": { 1221 | "model_module": "@jupyter-widgets/controls", 1222 | "model_module_version": "1.5.0", 1223 | "model_name": "HTMLModel", 1224 | "state": { 1225 | "_dom_classes": [], 1226 | "_model_module": "@jupyter-widgets/controls", 1227 | "_model_module_version": "1.5.0", 1228 | "_model_name": "HTMLModel", 1229 | "_view_count": null, 1230 | "_view_module": "@jupyter-widgets/controls", 1231 | "_view_module_version": "1.5.0", 1232 | "_view_name": "HTMLView", 1233 | "description": "", 1234 | "description_tooltip": null, 1235 | "layout": "IPY_MODEL_427056895c674c428400bee0f5b43995", 1236 | "placeholder": "​", 1237 | "style": "IPY_MODEL_04ec68b059df4c628839c3ac29e2ebdd", 1238 | "value": "100%" 1239 | } 1240 | }, 1241 | "a29f88f174f8499082fbb36a36c47fa4": { 1242 | "model_module": "@jupyter-widgets/controls", 1243 | "model_module_version": "1.5.0", 1244 | "model_name": "HBoxModel", 1245 | "state": { 1246 | "_dom_classes": [], 1247 | "_model_module": "@jupyter-widgets/controls", 1248 | "_model_module_version": "1.5.0", 1249 | "_model_name": "HBoxModel", 1250 | "_view_count": null, 1251 | "_view_module": "@jupyter-widgets/controls", 1252 | "_view_module_version": "1.5.0", 1253 | "_view_name": "HBoxView", 1254 | "box_style": "", 1255 | "children": [ 1256 | "IPY_MODEL_d45747150d0b434593a3a7c98399599a", 1257 | "IPY_MODEL_ea73f7deb1c643f7b81de7fb7acaaf1b", 1258 | "IPY_MODEL_18bc63944343440f837cdff76db004fc" 1259 | ], 1260 | "layout": "IPY_MODEL_efc3bc0c48124ebeb79d245216eaf0fe" 1261 | } 1262 | }, 1263 | "a4ae510b4f3845f891a796cf844fc2bb": { 1264 | "model_module": "@jupyter-widgets/base", 1265 | "model_module_version": "1.2.0", 1266 | "model_name": "LayoutModel", 1267 | "state": { 1268 | "_model_module": "@jupyter-widgets/base", 1269 | "_model_module_version": "1.2.0", 1270 | "_model_name": "LayoutModel", 1271 | "_view_count": null, 1272 | "_view_module": "@jupyter-widgets/base", 1273 | "_view_module_version": "1.2.0", 1274 | "_view_name": "LayoutView", 1275 | "align_content": null, 1276 | "align_items": null, 1277 | "align_self": null, 1278 | "border": null, 1279 | "bottom": null, 1280 | "display": null, 1281 | "flex": null, 1282 | "flex_flow": null, 1283 | "grid_area": null, 1284 | "grid_auto_columns": null, 1285 | "grid_auto_flow": null, 1286 | "grid_auto_rows": null, 1287 | "grid_column": null, 1288 | "grid_gap": null, 1289 | "grid_row": null, 1290 | "grid_template_areas": null, 1291 | "grid_template_columns": null, 1292 | "grid_template_rows": null, 1293 | "height": null, 1294 | "justify_content": null, 1295 | "justify_items": null, 1296 | "left": null, 1297 | "margin": null, 1298 | "max_height": null, 1299 | "max_width": null, 1300 | "min_height": null, 1301 | "min_width": null, 1302 | "object_fit": null, 1303 | "object_position": null, 1304 | "order": null, 1305 | "overflow": null, 1306 | "overflow_x": null, 1307 | "overflow_y": null, 1308 | "padding": null, 1309 | "right": null, 1310 | "top": null, 1311 | "visibility": null, 1312 | "width": null 1313 | } 1314 | }, 1315 | "aa329cb93df44a6da6012c7cc49d7489": { 1316 | "model_module": "@jupyter-widgets/base", 1317 | "model_module_version": "1.2.0", 1318 | "model_name": "LayoutModel", 1319 | "state": { 1320 | "_model_module": "@jupyter-widgets/base", 1321 | "_model_module_version": "1.2.0", 1322 | "_model_name": "LayoutModel", 1323 | "_view_count": null, 1324 | "_view_module": "@jupyter-widgets/base", 1325 | "_view_module_version": "1.2.0", 1326 | "_view_name": "LayoutView", 1327 | "align_content": null, 1328 | "align_items": null, 1329 | "align_self": null, 1330 | "border": null, 1331 | "bottom": null, 1332 | "display": null, 1333 | "flex": null, 1334 | "flex_flow": null, 1335 | "grid_area": null, 1336 | "grid_auto_columns": null, 1337 | "grid_auto_flow": null, 1338 | "grid_auto_rows": null, 1339 | "grid_column": null, 1340 | "grid_gap": null, 1341 | "grid_row": null, 1342 | "grid_template_areas": null, 1343 | "grid_template_columns": null, 1344 | "grid_template_rows": null, 1345 | "height": null, 1346 | "justify_content": null, 1347 | "justify_items": null, 1348 | "left": null, 1349 | "margin": null, 1350 | "max_height": null, 1351 | "max_width": null, 1352 | "min_height": null, 1353 | "min_width": null, 1354 | "object_fit": null, 1355 | "object_position": null, 1356 | "order": null, 1357 | "overflow": null, 1358 | "overflow_x": null, 1359 | "overflow_y": null, 1360 | "padding": null, 1361 | "right": null, 1362 | "top": null, 1363 | "visibility": null, 1364 | "width": null 1365 | } 1366 | }, 1367 | "b39b6e9131ca4ce3b31e84ceb04e1b83": { 1368 | "model_module": "@jupyter-widgets/controls", 1369 | "model_module_version": "1.5.0", 1370 | "model_name": "ProgressStyleModel", 1371 | "state": { 1372 | "_model_module": "@jupyter-widgets/controls", 1373 | "_model_module_version": "1.5.0", 1374 | "_model_name": "ProgressStyleModel", 1375 | "_view_count": null, 1376 | "_view_module": "@jupyter-widgets/base", 1377 | "_view_module_version": "1.2.0", 1378 | "_view_name": "StyleView", 1379 | "bar_color": null, 1380 | "description_width": "" 1381 | } 1382 | }, 1383 | "b6d46d40efa14b21814f41531f5a2f41": { 1384 | "model_module": "@jupyter-widgets/controls", 1385 | "model_module_version": "1.5.0", 1386 | "model_name": "FloatProgressModel", 1387 | "state": { 1388 | "_dom_classes": [], 1389 | "_model_module": "@jupyter-widgets/controls", 1390 | "_model_module_version": "1.5.0", 1391 | "_model_name": "FloatProgressModel", 1392 | "_view_count": null, 1393 | "_view_module": "@jupyter-widgets/controls", 1394 | "_view_module_version": "1.5.0", 1395 | "_view_name": "ProgressView", 1396 | "bar_style": "success", 1397 | "description": "", 1398 | "description_tooltip": null, 1399 | "layout": "IPY_MODEL_77f1a51099b24831ad8b2be3d2dc833a", 1400 | "max": 1, 1401 | "min": 0, 1402 | "orientation": "horizontal", 1403 | "style": "IPY_MODEL_d518f2c2ab6945b78a6d336dad6262bd", 1404 | "value": 1 1405 | } 1406 | }, 1407 | "c31a747e18df4b4aa4449a30e387448c": { 1408 | "model_module": "@jupyter-widgets/base", 1409 | "model_module_version": "1.2.0", 1410 | "model_name": "LayoutModel", 1411 | "state": { 1412 | "_model_module": "@jupyter-widgets/base", 1413 | "_model_module_version": "1.2.0", 1414 | "_model_name": "LayoutModel", 1415 | "_view_count": null, 1416 | "_view_module": "@jupyter-widgets/base", 1417 | "_view_module_version": "1.2.0", 1418 | "_view_name": "LayoutView", 1419 | "align_content": null, 1420 | "align_items": null, 1421 | "align_self": null, 1422 | "border": null, 1423 | "bottom": null, 1424 | "display": null, 1425 | "flex": null, 1426 | "flex_flow": null, 1427 | "grid_area": null, 1428 | "grid_auto_columns": null, 1429 | "grid_auto_flow": null, 1430 | "grid_auto_rows": null, 1431 | "grid_column": null, 1432 | "grid_gap": null, 1433 | "grid_row": null, 1434 | "grid_template_areas": null, 1435 | "grid_template_columns": null, 1436 | "grid_template_rows": null, 1437 | "height": null, 1438 | "justify_content": null, 1439 | "justify_items": null, 1440 | "left": null, 1441 | "margin": null, 1442 | "max_height": null, 1443 | "max_width": null, 1444 | "min_height": null, 1445 | "min_width": null, 1446 | "object_fit": null, 1447 | "object_position": null, 1448 | "order": null, 1449 | "overflow": null, 1450 | "overflow_x": null, 1451 | "overflow_y": null, 1452 | "padding": null, 1453 | "right": null, 1454 | "top": null, 1455 | "visibility": null, 1456 | "width": null 1457 | } 1458 | }, 1459 | "c5eed102ef134a4e8ca41713b82ff6a4": { 1460 | "model_module": "@jupyter-widgets/base", 1461 | "model_module_version": "1.2.0", 1462 | "model_name": "LayoutModel", 1463 | "state": { 1464 | "_model_module": "@jupyter-widgets/base", 1465 | "_model_module_version": "1.2.0", 1466 | "_model_name": "LayoutModel", 1467 | "_view_count": null, 1468 | "_view_module": "@jupyter-widgets/base", 1469 | "_view_module_version": "1.2.0", 1470 | "_view_name": "LayoutView", 1471 | "align_content": null, 1472 | "align_items": null, 1473 | "align_self": null, 1474 | "border": null, 1475 | "bottom": null, 1476 | "display": null, 1477 | "flex": null, 1478 | "flex_flow": null, 1479 | "grid_area": null, 1480 | "grid_auto_columns": null, 1481 | "grid_auto_flow": null, 1482 | "grid_auto_rows": null, 1483 | "grid_column": null, 1484 | "grid_gap": null, 1485 | "grid_row": null, 1486 | "grid_template_areas": null, 1487 | "grid_template_columns": null, 1488 | "grid_template_rows": null, 1489 | "height": null, 1490 | "justify_content": null, 1491 | "justify_items": null, 1492 | "left": null, 1493 | "margin": null, 1494 | "max_height": null, 1495 | "max_width": null, 1496 | "min_height": null, 1497 | "min_width": null, 1498 | "object_fit": null, 1499 | "object_position": null, 1500 | "order": null, 1501 | "overflow": null, 1502 | "overflow_x": null, 1503 | "overflow_y": null, 1504 | "padding": null, 1505 | "right": null, 1506 | "top": null, 1507 | "visibility": null, 1508 | "width": null 1509 | } 1510 | }, 1511 | "d45747150d0b434593a3a7c98399599a": { 1512 | "model_module": "@jupyter-widgets/controls", 1513 | "model_module_version": "1.5.0", 1514 | "model_name": "HTMLModel", 1515 | "state": { 1516 | "_dom_classes": [], 1517 | "_model_module": "@jupyter-widgets/controls", 1518 | "_model_module_version": "1.5.0", 1519 | "_model_name": "HTMLModel", 1520 | "_view_count": null, 1521 | "_view_module": "@jupyter-widgets/controls", 1522 | "_view_module_version": "1.5.0", 1523 | "_view_name": "HTMLView", 1524 | "description": "", 1525 | "description_tooltip": null, 1526 | "layout": "IPY_MODEL_aa329cb93df44a6da6012c7cc49d7489", 1527 | "placeholder": "​", 1528 | "style": "IPY_MODEL_9c875952cdd649a5bab87de9bb3f5200", 1529 | "value": "100%" 1530 | } 1531 | }, 1532 | "d518f2c2ab6945b78a6d336dad6262bd": { 1533 | "model_module": "@jupyter-widgets/controls", 1534 | "model_module_version": "1.5.0", 1535 | "model_name": "ProgressStyleModel", 1536 | "state": { 1537 | "_model_module": "@jupyter-widgets/controls", 1538 | "_model_module_version": "1.5.0", 1539 | "_model_name": "ProgressStyleModel", 1540 | "_view_count": null, 1541 | "_view_module": "@jupyter-widgets/base", 1542 | "_view_module_version": "1.2.0", 1543 | "_view_name": "StyleView", 1544 | "bar_color": null, 1545 | "description_width": "" 1546 | } 1547 | }, 1548 | "d8bf8dc5d6c84140a4e96c9c435b8f17": { 1549 | "model_module": "@jupyter-widgets/controls", 1550 | "model_module_version": "1.5.0", 1551 | "model_name": "HTMLModel", 1552 | "state": { 1553 | "_dom_classes": [], 1554 | "_model_module": "@jupyter-widgets/controls", 1555 | "_model_module_version": "1.5.0", 1556 | "_model_name": "HTMLModel", 1557 | "_view_count": null, 1558 | "_view_module": "@jupyter-widgets/controls", 1559 | "_view_module_version": "1.5.0", 1560 | "_view_name": "HTMLView", 1561 | "description": "", 1562 | "description_tooltip": null, 1563 | "layout": "IPY_MODEL_22ba979142074f1d976e1a905544fd2d", 1564 | "placeholder": "​", 1565 | "style": "IPY_MODEL_5815ae1348994bfebba4a8e968489a96", 1566 | "value": " 1/1 [00:00<00:00, 7.95ba/s]" 1567 | } 1568 | }, 1569 | "e6e50da6516847878309fdc5c463edb3": { 1570 | "model_module": "@jupyter-widgets/controls", 1571 | "model_module_version": "1.5.0", 1572 | "model_name": "DescriptionStyleModel", 1573 | "state": { 1574 | "_model_module": "@jupyter-widgets/controls", 1575 | "_model_module_version": "1.5.0", 1576 | "_model_name": "DescriptionStyleModel", 1577 | "_view_count": null, 1578 | "_view_module": "@jupyter-widgets/base", 1579 | "_view_module_version": "1.2.0", 1580 | "_view_name": "StyleView", 1581 | "description_width": "" 1582 | } 1583 | }, 1584 | "ea73f7deb1c643f7b81de7fb7acaaf1b": { 1585 | "model_module": "@jupyter-widgets/controls", 1586 | "model_module_version": "1.5.0", 1587 | "model_name": "FloatProgressModel", 1588 | "state": { 1589 | "_dom_classes": [], 1590 | "_model_module": "@jupyter-widgets/controls", 1591 | "_model_module_version": "1.5.0", 1592 | "_model_name": "FloatProgressModel", 1593 | "_view_count": null, 1594 | "_view_module": "@jupyter-widgets/controls", 1595 | "_view_module_version": "1.5.0", 1596 | "_view_name": "ProgressView", 1597 | "bar_style": "success", 1598 | "description": "", 1599 | "description_tooltip": null, 1600 | "layout": "IPY_MODEL_c5eed102ef134a4e8ca41713b82ff6a4", 1601 | "max": 6962, 1602 | "min": 0, 1603 | "orientation": "horizontal", 1604 | "style": "IPY_MODEL_b39b6e9131ca4ce3b31e84ceb04e1b83", 1605 | "value": 6962 1606 | } 1607 | }, 1608 | "efc3bc0c48124ebeb79d245216eaf0fe": { 1609 | "model_module": "@jupyter-widgets/base", 1610 | "model_module_version": "1.2.0", 1611 | "model_name": "LayoutModel", 1612 | "state": { 1613 | "_model_module": "@jupyter-widgets/base", 1614 | "_model_module_version": "1.2.0", 1615 | "_model_name": "LayoutModel", 1616 | "_view_count": null, 1617 | "_view_module": "@jupyter-widgets/base", 1618 | "_view_module_version": "1.2.0", 1619 | "_view_name": "LayoutView", 1620 | "align_content": null, 1621 | "align_items": null, 1622 | "align_self": null, 1623 | "border": null, 1624 | "bottom": null, 1625 | "display": null, 1626 | "flex": null, 1627 | "flex_flow": null, 1628 | "grid_area": null, 1629 | "grid_auto_columns": null, 1630 | "grid_auto_flow": null, 1631 | "grid_auto_rows": null, 1632 | "grid_column": null, 1633 | "grid_gap": null, 1634 | "grid_row": null, 1635 | "grid_template_areas": null, 1636 | "grid_template_columns": null, 1637 | "grid_template_rows": null, 1638 | "height": null, 1639 | "justify_content": null, 1640 | "justify_items": null, 1641 | "left": null, 1642 | "margin": null, 1643 | "max_height": null, 1644 | "max_width": null, 1645 | "min_height": null, 1646 | "min_width": null, 1647 | "object_fit": null, 1648 | "object_position": null, 1649 | "order": null, 1650 | "overflow": null, 1651 | "overflow_x": null, 1652 | "overflow_y": null, 1653 | "padding": null, 1654 | "right": null, 1655 | "top": null, 1656 | "visibility": null, 1657 | "width": null 1658 | } 1659 | } 1660 | } 1661 | } 1662 | }, 1663 | "nbformat": 4, 1664 | "nbformat_minor": 4 1665 | } 1666 | --------------------------------------------------------------------------------