{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Loading The model","metadata":{}},{"cell_type":"code","source":"!pip install transformers --no-index --find-links=file:../input/wheel-of-packages/packages/transformers\n","metadata":{"execution":{"iopub.status.busy":"2022-08-26T11:04:11.840541Z","iopub.execute_input":"2022-08-26T11:04:11.841062Z","iopub.status.idle":"2022-08-26T11:04:25.336117Z","shell.execute_reply.started":"2022-08-26T11:04:11.841018Z","shell.execute_reply":"2022-08-26T11:04:25.334446Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install ../input/jiwerpackage/packages/jiwer/jiwer-2.3.0-py3-none-any.whl","metadata":{"execution":{"iopub.status.busy":"2022-08-26T11:04:25.340115Z","iopub.execute_input":"2022-08-26T11:04:25.340668Z","iopub.status.idle":"2022-08-26T11:04:57.669777Z","shell.execute_reply.started":"2022-08-26T11:04:25.34062Z","shell.execute_reply":"2022-08-26T11:04:57.668134Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from pathlib import Path\nTMP_DIR = Path('../temp')\nTMP_DIR.mkdir(exist_ok=True)\n!cp -r ../input/wav2vec2-library ../temp/wav2vec2-library","metadata":{"execution":{"iopub.status.busy":"2022-08-26T11:04:57.691494Z","iopub.execute_input":"2022-08-26T11:04:57.694143Z","iopub.status.idle":"2022-08-26T11:04:59.167819Z","shell.execute_reply.started":"2022-08-26T11:04:57.694049Z","shell.execute_reply":"2022-08-26T11:04:59.165763Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install ../temp/wav2vec2-library","metadata":{"execution":{"iopub.status.busy":"2022-08-26T11:04:59.170933Z","iopub.execute_input":"2022-08-26T11:04:59.173755Z","iopub.status.idle":"2022-08-26T11:05:34.575616Z","shell.execute_reply.started":"2022-08-26T11:04:59.1737Z","shell.execute_reply":"2022-08-26T11:05:34.573914Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nos.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' \nimport random\nimport tensorflow as tf\nimport transformers\nimport matplotlib.pyplot as plt\nimport numpy as np \nimport pandas as pd\nfrom tqdm.auto import tqdm\nfrom IPython.display import display,Audio\nfrom wav2vec2 import RobustWav2Vec2Config,Wav2Vec2,CTCLoss\ntqdm.pandas()\n","metadata":{"execution":{"iopub.status.busy":"2022-08-26T11:05:34.578886Z","iopub.execute_input":"2022-08-26T11:05:34.579386Z","iopub.status.idle":"2022-08-26T11:05:42.96188Z","shell.execute_reply.started":"2022-08-26T11:05:34.579323Z","shell.execute_reply":"2022-08-26T11:05:42.960363Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"VOCAB   =[' ', '<empty>', '<empty>', '<empty>', '<empty>', '<empty>', '<empty>', '<empty>', '<empty>', \n          '<empty>', '<empty>', '<empty>', '<empty>', '<empty>', '<empty>', '<empty>', '<empty>', \n          '<empty>', '<empty>', '<empty>', '<empty>', '<empty>', '<empty>', '<empty>', '<empty>', \n          '<empty>', '<empty>', '<empty>', '<empty>', '<empty>', '।', 'ঁ', 'ং', 'ঃ', 'অ', 'আ', 'ই', \n          'ঈ', 'উ', 'ঊ', 'ঋ', 'এ', 'ঐ', 'ও', 'ঔ', 'ক', 'খ', 'গ', 'ঘ', 'ঙ', 'চ', 'ছ', 'জ', 'ঝ', 'ঞ', \n          'ট', 'ঠ', 'ড', 'ঢ', 'ণ', 'ত', 'থ', 'দ', 'ধ', 'ন', 'প', 'ফ', 'ব', 'ভ', 'ম', 'য', 'র', 'ল',\n          'শ', 'ষ', 'স', 'হ', '<empty>', 'া', 'ি', 'ী', 'ু', 'ূ', 'ৃ', 'ে', 'ৈ', 'ো', 'ৌ', '্', 'ৎ', \n          '<empty>', 'ড়', 'ঢ়', 'য়', '০', '১', '২', '৩', '৪', '৫', '৬', '৭', '৮', '৯', '<empty>', '<empty>', \n          '\\u200d', '<empty>', '<empty>', '', '<s>', '</s>']\nconfig = RobustWav2Vec2Config()\nconfig.pad_id=VOCAB.index(\"\")\nprint(\"Vocab Len:\",len(VOCAB))","metadata":{"execution":{"iopub.status.busy":"2022-08-26T11:05:42.963733Z","iopub.execute_input":"2022-08-26T11:05:42.964659Z","iopub.status.idle":"2022-08-26T11:05:42.987786Z","shell.execute_reply.started":"2022-08-26T11:05:42.964627Z","shell.execute_reply":"2022-08-26T11:05:42.985946Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class cfg:\n    audio_shape      =  (246000,)                   # this is actually fixed for the pretrained weights we are using -- highets audio length=15 secs\n    label_shape      =  (250,)                      # this is actually fixed for the pretrained weights we are using \n    sample_rate      =  16000\n    shuffle_buffer   =  1024\n    batch_size       =  128\n    vocab_len        =  len(VOCAB)                \n    embed_dim        =  1024\n    ","metadata":{"execution":{"iopub.status.busy":"2022-08-26T11:05:42.990681Z","iopub.execute_input":"2022-08-26T11:05:42.991977Z","iopub.status.idle":"2022-08-26T11:05:43.009061Z","shell.execute_reply.started":"2022-08-26T11:05:42.991932Z","shell.execute_reply":"2022-08-26T11:05:43.007696Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"SUFFIX = \":0\"\nMAPPING = (\n    (\"layer_norm.weight\", \"layer_norm/gamma\"),\n    (\"layer_norm.bias\", \"layer_norm.beta\"),\n    (\"weight\", \"kernel\"),\n    (\".\", \"/\"),\n)\n\nSPECIAL_MAPPING_WITH_HEAD = {\n    \"wav2vec2.encoder.pos_conv_embed.conv.weight_g\": \"wav2vec2/encoder/pos_conv_embed/conv/weight_g:0\",\n    \"wav2vec2.encoder.pos_conv_embed.conv.weight_v\": \"wav2vec2/encoder/pos_conv_embed/conv/weight_v:0\",\n}\n\n\n\ndef replace(k):\n    \"\"\"\n    Converts PyTorch state_dict keys to TensorFlow varible name.\n    \"\"\"\n    for hf_v, tf_v in MAPPING:\n        k = k.replace(hf_v, tf_v)\n    return k + SUFFIX\n\n\ndef get_tf_pretrained_model(hf_model_id,tf_model):\n    \"\"\"\n    Converts HuggingFace PyTorch weights to TensorFlow compatible weights.\n    \"\"\"\n    hf_model = transformers.Wav2Vec2ForCTC.from_pretrained(hf_model_id)\n\n    hf_state_dict = hf_model.state_dict()\n\n    tf_variables = tf_model.variables\n    tf_variables_dict = {}\n    for v in tf_variables:\n        tf_variables_dict[v.name] = v\n\n    tf_weights = []\n    extra_keys = []\n    for k in tqdm(hf_state_dict):\n        if k in SPECIAL_MAPPING_WITH_HEAD:\n            new_k = (SPECIAL_MAPPING_WITH_HEAD[k])\n        else:\n            new_k = replace(k)\n        if new_k not in tf_variables_dict.keys():\n            extra_keys.append(k)\n            print(f\"SKIPPING {k}\")\n            continue\n\n        \n        array = hf_state_dict[k].numpy()\n\n        if k in SPECIAL_MAPPING_WITH_HEAD:\n            array = np.transpose(array, axes=(2, 1, 0))\n        elif \"kernel\" in new_k:\n            array = np.transpose(array)\n\n        tf_weights.append((tf_variables_dict[new_k], array))\n\n    print(\"EXTRA KEYS:\\n\", extra_keys)\n\n    tf.keras.backend.batch_set_value(tf_weights)\n    return tf_model","metadata":{"execution":{"iopub.status.busy":"2022-08-26T11:05:43.01233Z","iopub.execute_input":"2022-08-26T11:05:43.013523Z","iopub.status.idle":"2022-08-26T11:05:43.030124Z","shell.execute_reply.started":"2022-08-26T11:05:43.013425Z","shell.execute_reply":"2022-08-26T11:05:43.028234Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def create_model(cfg):\n    inputs = tf.keras.Input(shape=cfg.audio_shape)\n    # avoid using spec augmentation\n    config.apply_spec_augment=False\n    # freeze feature extractor\n    states = Wav2Vec2(config)(inputs)\n    logits= tf.keras.layers.Dense(cfg.vocab_len,name=\"lm_head\")(states)\n    model = tf.keras.Model(inputs=inputs, outputs=logits)\n    return model\n","metadata":{"execution":{"iopub.status.busy":"2022-08-26T11:05:43.038652Z","iopub.execute_input":"2022-08-26T11:05:43.039036Z","iopub.status.idle":"2022-08-26T11:05:43.050319Z","shell.execute_reply.started":"2022-08-26T11:05:43.039006Z","shell.execute_reply":"2022-08-26T11:05:43.048937Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model=get_tf_pretrained_model(\"../input/banglaasr-old-account/checkpoint-2670\",create_model(cfg))\n# freeze feature extractor\nmodel.layers[1].freeze_feature_extractor()\n#model.load_weights(\"../input/wave2vec2-tpu-transfer-learning-dl-sprint/model.h5\")\nmodel.load_weights(\"../input/tf-training-model-loc/model.h5\")\nmodel.summary()\n","metadata":{"execution":{"iopub.status.busy":"2022-08-26T11:05:43.052315Z","iopub.execute_input":"2022-08-26T11:05:43.052948Z","iopub.status.idle":"2022-08-26T11:06:35.416883Z","shell.execute_reply.started":"2022-08-26T11:05:43.052903Z","shell.execute_reply":"2022-08-26T11:06:35.415378Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Loading Language Model","metadata":{}},{"cell_type":"code","source":"!cp -r ../input/wheel-of-packages/pyctcdecode/pygtrie-2.5.0/pygtrie-2.5.0 ../temp/pygtrie-2.5.0\n!pip install ../temp/pygtrie-2.5.0\n!pip install pyctcdecode --no-index --find-links=file:../input/wheel-of-packages/pyctcdecode\n!cp -r ../input/wheel-of-packages/pypi_kenlm/pypi-kenlm-0.1.20220713/pypi-kenlm-0.1.20220713 ../temp/pypi_kenlm\n!python -m pip install ../temp/pypi_kenlm\n!cp -r ../input/wheel-of-packages/kenlm_master/master/kenlm-master ../temp/kenlm_master\n!pip install ../temp/kenlm_master","metadata":{"execution":{"iopub.status.busy":"2022-08-26T11:06:35.419355Z","iopub.execute_input":"2022-08-26T11:06:35.420269Z","iopub.status.idle":"2022-08-26T11:10:32.137485Z","shell.execute_reply.started":"2022-08-26T11:06:35.420201Z","shell.execute_reply":"2022-08-26T11:10:32.135782Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import soundfile as sf\nfrom transformers import Wav2Vec2Processor\n\nfrom transformers import Wav2Vec2ForCTC\nfrom pyctcdecode import build_ctcdecoder\nfrom transformers import Wav2Vec2ProcessorWithLM\n\nprocessor = Wav2Vec2Processor.from_pretrained(\"../input/download-huggingface-models/wav2vec2_bangla\")\n#model = Wav2Vec2ForCTC.from_pretrained(\"../input/banglaasr-old-account/MyBanglaAsr\")\n\nvocab_dict = processor.tokenizer.get_vocab()\nsorted_vocab_dict = {k.lower(): v for k, v in sorted(vocab_dict.items(), key=lambda item: item[1])}\n\n\ndecoderx = build_ctcdecoder(\n    labels=list(sorted_vocab_dict.keys()),\n    kenlm_model_path=\"../input/ngramlm/6gram_correct.arpa\",\n    alpha=0.68,  # tuned on a val set \n    beta=1.45,  # tuned on a val set \n)\n\n\n\nprocessor = Wav2Vec2ProcessorWithLM(\n    feature_extractor=processor.feature_extractor,\n    tokenizer=processor.tokenizer,\n    decoder=decoderx\n)","metadata":{"execution":{"iopub.status.busy":"2022-08-26T11:10:32.139958Z","iopub.execute_input":"2022-08-26T11:10:32.14053Z","iopub.status.idle":"2022-08-26T11:14:03.113991Z","shell.execute_reply.started":"2022-08-26T11:10:32.140473Z","shell.execute_reply":"2022-08-26T11:14:03.112123Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Loading Audio","metadata":{}},{"cell_type":"code","source":"import librosa","metadata":{"execution":{"iopub.status.busy":"2022-08-26T11:14:03.116176Z","iopub.execute_input":"2022-08-26T11:14:03.117466Z","iopub.status.idle":"2022-08-26T11:14:18.266711Z","shell.execute_reply.started":"2022-08-26T11:14:03.117396Z","shell.execute_reply":"2022-08-26T11:14:18.264463Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def load_audio(path):\n    wave,_= librosa.load(path, sr=16000, mono=True)\n    wave=processor(wave, sampling_rate=16000,padding='max_length',max_length=246000,truncation=True).input_values[0]\n    wave=np.expand_dims(wave,axis=0)\n    return wave","metadata":{"execution":{"iopub.status.busy":"2022-08-26T11:14:18.268534Z","iopub.execute_input":"2022-08-26T11:14:18.269307Z","iopub.status.idle":"2022-08-26T11:14:18.287713Z","shell.execute_reply.started":"2022-08-26T11:14:18.269262Z","shell.execute_reply":"2022-08-26T11:14:18.285491Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Update: The inference will be done on .wav files","metadata":{}},{"cell_type":"code","source":"import os\nfrom glob import glob\nfrom tqdm import tqdm\nimport pandas as pd\ntqdm.pandas()\n\n# CHANGE ACCORDINGLY\nBATCH_SIZE = 32\nTEST_DIRECTORY = '../input/test-wav-files-dl-sprint/test_files_wav'","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-08-26T11:14:18.289925Z","iopub.execute_input":"2022-08-26T11:14:18.291188Z","iopub.status.idle":"2022-08-26T11:14:18.302976Z","shell.execute_reply.started":"2022-08-26T11:14:18.291127Z","shell.execute_reply":"2022-08-26T11:14:18.301489Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from pandarallel import pandarallel\npandarallel.initialize(progress_bar=True,nb_workers=8)","metadata":{"execution":{"iopub.status.busy":"2022-08-26T11:14:18.304949Z","iopub.execute_input":"2022-08-26T11:14:18.306443Z","iopub.status.idle":"2022-08-26T11:14:18.375428Z","shell.execute_reply.started":"2022-08-26T11:14:18.306353Z","shell.execute_reply":"2022-08-26T11:14:18.373265Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Infer on a single data","metadata":{}},{"cell_type":"code","source":"def infer(audio_path):\n    audio=load_audio(audio_path)\n    tf_out = model.predict(audio)\n    transcription = processor.batch_decode(tf_out)\n    return transcription['text'][0]\n    '''\n    infers on a signle audio\n    args:\n      audio_path  : the path to audio file <string>\n    returns:\n      bangla predicted text <string>\n  '''\n    \n  # your code goes here","metadata":{"execution":{"iopub.status.busy":"2022-08-26T11:14:18.380932Z","iopub.execute_input":"2022-08-26T11:14:18.383802Z","iopub.status.idle":"2022-08-26T11:14:18.396601Z","shell.execute_reply.started":"2022-08-26T11:14:18.383751Z","shell.execute_reply":"2022-08-26T11:14:18.394722Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Infer on a batch of data - MOST IMPORTANT","metadata":{}},{"cell_type":"code","source":"def batch_infer(audio_paths, batch_size=BATCH_SIZE):\n    audio_list=None\n    for path in audio_paths:\n        audio=load_audio(path)\n        try:\n            audio_list=np.concatenate((audio_list,audio),axis =0)\n        except:\n            audio_list=audio\n        \n    tf_out = model.predict(audio_list)\n    transcription = processor.batch_decode(tf_out)\n    sentences=transcription['text']\n    return sentences\n\n    '''\n    infers on a batch of audio\n    args:\n      audio_paths  : list of path to audio files <list of string>\n    returns:\n      bangla predicted texts <list of string>\n    '''\n    # your code goes her\n    ","metadata":{"execution":{"iopub.status.busy":"2022-08-26T11:14:18.39919Z","iopub.execute_input":"2022-08-26T11:14:18.404812Z","iopub.status.idle":"2022-08-26T11:14:18.423464Z","shell.execute_reply.started":"2022-08-26T11:14:18.404753Z","shell.execute_reply":"2022-08-26T11:14:18.420892Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Infer on a directory","metadata":{}},{"cell_type":"code","source":"def directory_infer(audio_dir):\n    '''\n    infers on a directory that contains audio files\n    args:\n      audio_dir  : directory that contains some audio files <string>\n    returns:\n      a dataframe that contains 2 columns:\n        * path <string>\n        * sentence <string>\n    '''\n    # list all audio files\n\n    audio_paths=[audio_path for audio_path in tqdm(glob(os.path.join(audio_dir,\"*.*\")))]\n    sentences=[]\n    for idx in tqdm(range(0,len(audio_paths),BATCH_SIZE)):\n        batch_paths=audio_paths[idx:min(idx+BATCH_SIZE,len(audio_paths))]\n        sentences+=batch_infer(batch_paths)\n        print(idx)\n    df=pd.DataFrame({\"path\":audio_paths,\"sentence\":sentences})\n    return df ","metadata":{"execution":{"iopub.status.busy":"2022-08-26T11:14:18.431234Z","iopub.execute_input":"2022-08-26T11:14:18.433789Z","iopub.status.idle":"2022-08-26T11:14:18.452848Z","shell.execute_reply.started":"2022-08-26T11:14:18.433419Z","shell.execute_reply":"2022-08-26T11:14:18.4505Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import warnings\nwarnings.filterwarnings('ignore')","metadata":{"execution":{"iopub.status.busy":"2022-08-26T11:14:18.455586Z","iopub.execute_input":"2022-08-26T11:14:18.457746Z","iopub.status.idle":"2022-08-26T11:14:18.465034Z","shell.execute_reply.started":"2022-08-26T11:14:18.4577Z","shell.execute_reply":"2022-08-26T11:14:18.46275Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission = directory_infer(TEST_DIRECTORY)\n","metadata":{"execution":{"iopub.status.busy":"2022-08-26T11:14:18.467108Z","iopub.execute_input":"2022-08-26T11:14:18.468298Z","iopub.status.idle":"2022-08-26T11:15:49.96538Z","shell.execute_reply.started":"2022-08-26T11:14:18.468253Z","shell.execute_reply":"2022-08-26T11:15:49.962709Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Optional: path to file conversation","metadata":{}},{"cell_type":"code","source":"\"\"\"\nsubmission['path']=submission['path'].parallel_apply(lambda x:os.path.basename(x))\nsubmission.head(10)\n\"\"\"","metadata":{"execution":{"iopub.status.busy":"2022-08-26T11:16:47.739747Z","iopub.execute_input":"2022-08-26T11:16:47.740243Z","iopub.status.idle":"2022-08-26T11:16:50.797903Z","shell.execute_reply.started":"2022-08-26T11:16:47.740193Z","shell.execute_reply":"2022-08-26T11:16:50.795522Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Post Processing","metadata":{}},{"cell_type":"code","source":"!cp -r ../input/bnunicodenormalizer/packages/bnunicodenormalizer/bnunicodenormalizer-0.0.24/bnunicodenormalizer-0.0.24 ../temp/bnunicodenormalizer\n!pip install ../temp/bnunicodenormalizer","metadata":{"execution":{"iopub.status.busy":"2022-08-26T11:15:58.77916Z","iopub.status.idle":"2022-08-26T11:15:58.779687Z","shell.execute_reply.started":"2022-08-26T11:15:58.779457Z","shell.execute_reply":"2022-08-26T11:15:58.779483Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from bnunicodenormalizer import Normalizer \nbnorm=Normalizer()\ndef normalize(sen):\n    _words = [bnorm(word)['normalized']  for word in sen.split()]\n    return \" \".join([word for word in _words if word is not None]) ","metadata":{"execution":{"iopub.status.busy":"2022-08-26T11:15:58.782354Z","iopub.status.idle":"2022-08-26T11:15:58.783077Z","shell.execute_reply.started":"2022-08-26T11:15:58.782724Z","shell.execute_reply":"2022-08-26T11:15:58.782754Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for i in range(len(submission)):\n    try:\n        \n        if(submission.sentence[i][-1] == '।'):\n            submission.sentence[i] = submission.sentence[i]\n        else:\n            submission.sentence[i] = submission.sentence[i]+'।'\n    except:\n        submission.sentence[i] == '।'\nsubmission.sentence=submission.sentence.parallel_apply(lambda x:normalize(x))","metadata":{"execution":{"iopub.status.busy":"2022-08-26T11:15:58.785441Z","iopub.status.idle":"2022-08-26T11:15:58.787902Z","shell.execute_reply.started":"2022-08-26T11:15:58.787541Z","shell.execute_reply":"2022-08-26T11:15:58.787575Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission.to_csv(\"submission.csv\", index=False)\nsubmission.head(20)","metadata":{"execution":{"iopub.status.busy":"2022-08-26T11:15:58.790187Z","iopub.status.idle":"2022-08-26T11:15:58.791378Z","shell.execute_reply.started":"2022-08-26T11:15:58.791017Z","shell.execute_reply":"2022-08-26T11:15:58.791047Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Your code must output a submission.csv file in the end with predictions on the test_files","metadata":{}}]}