# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     name: python3
# ---

# + [markdown] id="R1JCDzPJSfi2"
# # Практика №1

# + [markdown] id="LA6s0CLLSi5c"
# Первое практическое занятие мы начнем с реализации dynamic time warping (DTW) алгоритм на основе token passing algorithm (TPA).
#
# У нас имеется несколько эталонов (различные вариации произнесения слов YES и NO) и пара записей (также YES и NO). С помощью алгоритма DTW нам будет необходимо определить, на какой из эталонов больше всего похожа каждая запись. Данные wav файлы взяты из открытой базы данных Google Speech Commands Dataset (https://ai.googleblog.com/2017/08/launching-speech-commands-dataset.html).

# + [markdown] id="aV9Va3ZdS-6Y"
# ### Bootstrap

# + colab={"base_uri": "https://localhost:8080/"} id="QYIcD-9_3RiA" outputId="8368cce9-1ac1-4313-d497-26d650a73010"
# !gdown --id "1VNi62ZxZA7rgMKsvECDGZdl3WQJcBKRb"

# + id="16D45dWZTRwg" colab={"base_uri": "https://localhost:8080/"} outputId="2df1e893-bfb1-44b2-856d-b8b72953de38"
# !unzip -q lab1.zip
# !rm -rf lab1.zip sample_data
# %cd lab1

# + id="o1IPr9a8VxXn"
import os
import time

import librosa, librosa.display
import matplotlib.pyplot as plt
# %matplotlib inline
import numpy as np
from tqdm.notebook import tqdm
import IPython.display as ipd

# + id="wZmH0Go6-8-4"
plt.rcParams["figure.figsize"] = (15.0, 5.0)
plt.rcParams["image.interpolation"] = "nearest"
plt.rcParams["image.cmap"] = "gray"

# + [markdown] id="zcMtqRtgV3aa"
# Рассмотрим на примере один из эталонов:

# + id="I0Hf07iEV5XN" colab={"base_uri": "https://localhost:8080/", "height": 126} outputId="b6d8834c-1745-4a6b-9258-ac6c8618ab42"
wav_example = "data/yes_no/etalons/yes_004ae714_nohash_0.wav"

# Read wav-file.
# Here sr=None to preserve the native sampling rate.
x, sr = librosa.load(wav_example, sr=None)

print(f"Number of samples: {len(x)}.")
print(f"Sampling rate: {sr} Hz.")
# ~ librosa.get_duration(x, sr):
print(f"Duration: {len(x) / sr:.2f} s.")

# Playback:
ipd.Audio(x, rate=sr)

# + id="DQcrs79wV8jn" colab={"base_uri": "https://localhost:8080/", "height": 315} outputId="304f9029-4135-419f-f505-9f04bdfe1cf3"
# Time representation of the signal:
librosa.display.waveplot(x, sr=sr);

# + colab={"base_uri": "https://localhost:8080/", "height": 301} id="JoyQMrfWVhUz" outputId="b53ea90b-e714-491e-a3d9-8a4fb6747185"
plt.plot(x);

# + colab={"base_uri": "https://localhost:8080/", "height": 310} id="gHq8YVbnu7eY" outputId="33484c8f-fc5d-44b7-80bb-cb908f93f9f9"
librosa.display.waveplot(x[:200], sr=sr);

# + colab={"base_uri": "https://localhost:8080/", "height": 297} id="4b7MyNgfVaTQ" outputId="6d55e42c-a635-49fc-feaf-c118b23f36ee"
plt.plot(x[:200]);

# + [markdown] id="Wqum98XPBGMp"
# The view is different from the one that can be plotted with `matplotlib` in two aspects:
# * `librosa` transforms signal samples into seconds with respect to sampling rate ($t = k / sr$, where $k$ is the sample number), whereas `matplotlib` takes plain sample numbers as units;
# * for a monophonic signal (like the one that we have) `librosa` plots a curve filled between `[-abs(y), abs(y)]` (symmetric with respect to Y-axis), whereas `matplotlib`'s plot is asymmetric.

# + id="G639c-CYWJT9" colab={"base_uri": "https://localhost:8080/", "height": 350} outputId="de43645f-4068-4a5c-afb2-82b77277ca54"
# Spectrogram:
D = librosa.amplitude_to_db(np.abs(librosa.stft(x)), ref=np.max)
librosa.display.specshow(D, sr=sr, y_axis="hz", x_axis="time")
plt.colorbar(format="%+2.0f dB")
plt.title("Log-frequency power spectrogram");

# + [markdown] id="lrXMLQSDWajP"
# Теперь посчитаем речевые признаки нашего эталона.
# Для начала будем работать с 13-размерными MFCC признаками.

# + id="pcuVfy_3WLS8" colab={"base_uri": "https://localhost:8080/"} outputId="4a7cd9ad-0a09-43f8-d3bc-b5f06b693c81"
# n_fft = 25 ms        -- длина кадра
# hop_length = 10 ms   -- длина шага
# Тогда в отсчётах получаем следующее:
n_fft = int(sr * 0.025)
hop_length = int(sr * 0.01)

mfcc = librosa.feature.mfcc(x, sr=sr, n_mfcc=13, n_fft=n_fft,
                            hop_length=hop_length)

print(mfcc.shape)
print(mfcc[:, 0])  # вектор признаков нулевого кадра


# + [markdown] id="myZAPeZSWsKH"
# Теперь приступим к описанию самого DTW алгоритма.
# Для начала нам потребуется создать граф, ветвями которого будут наши эталоны, с которыми мы и будем сравнивать записи. Все ветви графа будут состоять из узлов типа `State`. Каждый такой узел представляет собой описание отдельного кадра эталона. Таким образом число узлов в ветви равно числу кадров в эталоне.
#
# На данном этапе узлы в графе будут иметь переходы только в себя и следующий узел. Нулевой узел является корневым и имеет переходы в начальные узлы каждой ветви. Финальный узел каждой ветви имеет переход только в себя. Схема такого графа представлена ниже:

# + [markdown] id="OKAQH5_FW4Jo"
# ![](https://drive.google.com/uc?export=view&id=1PQwZSf9EFEZUWQt9dmOifpyqCM6uuCRM)

# + [markdown] id="04Xj2ymdzQhn"
# Здесь мы имеем два эталона слов YES и NO длиной 4 и 3 кадра соответственно.

# + id="DxieTkKhWV0M"
class State:
    def __init__(self, feats, index):
        # feats: node feature vector
        # is_final: whether the node is final in the word
        # word: etalon word (only present for the final node)
        # best_token: token with the least distance in the node
        # current_word: current etalon word
        # next_states: the list of next nodes
        # index: node index
        self.feats = feats
        self.is_final = False
        self.word = None
        self.best_token = None
        self.current_word = None
        self.next_states = []
        self.index = index


def load_graph(etalons_dict, enable_skips=False):
    start_state = State(None, 0)
    graph = [start_state,]
    state_index = 1
    for word in etalons_dict.keys():
        previous_state = start_state
        for feats_frame in etalons_dict[word]:
            state = State(feats_frame, state_index)
            # Etalon word will now be stored in each node:
            state.current_word = word
            # Add loop:
            state.next_states.append(state)
            previous_state.next_states.append(state)
            previous_state = state
            #-----------------------------TODO №2------------------------------
            # Добавьте переходы через один и через два узла
            # (hint): добавлять переходы проще задним числом
            if enable_skips:
                if state in graph[state.index - 1].next_states \
                    and graph[state.index - 1] in graph[state.index - 2].next_states \
                    and state.index - 2 > 0:
                    graph[state.index - 2].next_states.append(state)
                    if graph[state.index - 2] in graph[state.index - 3].next_states \
                        and state.index - 3 > 0:
                        graph[state.index - 3].next_states.append(state)
            #------------------------------------------------------------------
            graph.append(state)
            state_index += 1
        if state:
            state.word = word
            state.is_final = True
    return graph


def print_graph(graph):
    if not os.path.exists("exp"):
        os.mkdir("exp")
    with open("exp/graph.txt", 'w') as fn:
        np.set_printoptions(formatter={"float": "{: 0.1f}".format})
        for state in graph:
            next_state_indexes = [s.index for s in state.next_states]
            fn.write(
                "State: index={} word={} is_final={} " \
                "next_state_indexes={} ftr={} \n".format(
                    state.index, state.word, state.is_final,
                    next_state_indexes, state.feats
                )
            )
    print("*** See exp/graph.txt ***")


# + [markdown] id="4lEU31czXNy5"
# Подготовка наших эталонов и записей для распознавания:

# + id="Douk24ayXO4c"
def load_data_dict(dir_path):
    data_dict = {}
    for wav_name in os.listdir(dir_path):
        x, sr = librosa.load(os.path.join(dir_path, wav_name), sr=None) 
        mfcc = librosa.feature.mfcc(x, sr=sr, n_mfcc=13, 
                                    n_fft=int(sr * 0.025),
                                    hop_length=int(sr * 0.01))
        data_dict[wav_name] = mfcc.T

    return data_dict


# + id="7rCUtky5XTF-"
etalons_dir = "data/yes_no/etalons"
records_dir = "data/yes_no/records"

etalons_data_dict = load_data_dict(etalons_dir)
records_data_dict = load_data_dict(records_dir)

# + id="2xTbfklgXa4A" colab={"base_uri": "https://localhost:8080/"} outputId="8802ca85-4870-4e24-cebe-3e7f4ed53a44"
graph = load_graph(etalons_data_dict)
print_graph(graph)


# + [markdown] id="f6IgmAjjXkAS"
# Далее идет описание класса Token и самого алгоритма TPA:
#
# - Алгоритм TPA двигается последовательно по кадрам записи, на каждом кадре берёт множество токенов от предыдущего кадра и порождает множество токенов для текущего кадра.
# - Один токен – это вещь, олицетворяющая собой один из возможных вариантов разметки (соотнесения кадров записи кадрам эталона), заканчивающаяся на данном кадре. По токену можно понять, какую суммарную дистанцию набрал данный токен (то есть то, насколько он хорош), в каком узле графа эталонов он находится (чтобы от него можно было породить токен для следующего кадра) и на ветке какого слова он находится на данный момент.
# - На некотором кадре всё множество токенов описывает все возможные разметки, которые можно получить к данному кадру. После обработки последнего кадра мы просто переберём все финальные токены (разметки) и выберем лучший (тот, который имеет минимальное суммарное расстояние от записи до эталона).
# - Финальные токены – это токены, которым соответствует законченная разметка (то есть та, в которой после финального кадра мы оказались в финальном узле графа). Остальные токены могут рассматриваться как бракованные.
#
# Получается, что TPA (в том виде, в котором он тут описан) – это удобная форма записи полного перебора всех возможных разметок.

# + [markdown] id="oOyXHyDTXukI"
# ### Проблемы:
# Такой TPA будет перебирать все возможные варианты разметки, что приведет к значительному увеличению времени работы нашего DTW. Для решения этой проблемы мы будем отбрасывать "плохие" токены ещё на этапе прохождения по графу. Этим занимаются так называемые **beam** и **state prunings**.
#
# ### **state pruning**:
# В классе `State` нужно добавить атрибут `best_token` – ссылку на лучший токен, находящийся в данном состоянии на текущем кадре записи. После порождения всех токенов за один кадр записи пройдёмся по каждому из полученных `next_tokens`, затем впишем текущий токен в `State.best_token` (здесь `State` – это узел, на котором находится токен), убив предыдущий лучший токен, либо убьём сам токен, если он хуже лучшего на этом узле. За жизнеспособность токена отвечает его атрибут `is_alive` (`True` или `False`, соответственно). Логика в том, что если несколько токенов находятся в узле *n*, то кратчайшее расстояние, которое им надо преодолеть до финиша, одинаковое. То есть мы можем оставить только лучший из них, а остальные отсеять. Главное не забыть очистить поле `best_token` у всех узлов графа перед началом обработки следующего кадра записи.
#
# ### **beam pruning**:
# Идея состоит в том, чтобы на каждом кадре записи находить плохие токены и откидывать их (`token.is_alive = False`).
# Плохие – это, очевидно, накопившие слишком большое отклонение от состояний, по которым они идут. Слишком большое отклонение – это непонятно какое (может токен плохой, может слово слишком длинное, может звук очень плохой – не разобрать). Поэтому "качество" токена считают относительно лучшего токена. Заведем переменную `threshold` (обычно её называют `beam` – ширина луча поиска), и если `token.dist` > `best_token.dist + threshold`, то `token` плохой и мы его отбросим.
# Выкидывая какой-то токен из-за его отклонения, мы рискуем тем, что через сколько-то кадров все потомки выживших токенов могут оказаться очень плохими, а только потомки отброшенного токена оказались бы чудо как хороши. То есть, вводя `threshold`, мы вводим ошибку. Поэтому `threshold` нужно подобрать так, чтобы скорость работы алгоритма сильно выросла, а ошибка выросла незначительно.
#
# Введение этих методов может привести к тому, что у нас просто не окажется в конце выживших токенов в финальных узлах графа. Для того, чтобы иметь возможность выдавать результат в этом случае, мы введем дополнительный атрибут `current_word` у класса `State`. Теперь в любом узле каждой ветви будет храниться слово соответствующего эталона для этой ветви. 
#
# Тогда в конце работы DTW, если у нас не будет живых финальных токенов, то мы просто выберем лучший из оставшихся и по полю `state.current_word` определим слово эталона.

# + id="Zvu-la1tXb0A"
class Token:
    def __init__(self, state, dist=0.0, word=""):
        # state: graph state that the given token has at the moment
        # dist: total accumulated distance traveled by the token
        # word: the word that was recognized by the token
        # alive: whether the token is alive
        self.state = state
        self.dist = dist
        self.word = word
        self.alive = True


def beam_pruning(next_tokens, beam_threshold):
    #--------------------------------TODO №1-----------------------------------
    # 1. Ищем токен с лучшей дистанцией из next_tokens.
    # 2. Присваиваем token.alive значение False, если дистанция этого токена
    #    больше, чем дистанция лучшего токена + beam_threshold.
    # To drop dead tokens completely:
    # next_tokens = list(filter(lambda x: x.alive, next_tokens))
    best_token = min(next_tokens, key=lambda x: x.dist if x.alive else np.inf)
    for token in next_tokens:
        if token.alive and token.dist > best_token.dist + beam_threshold:
            token.alive = False
    #--------------------------------------------------------------------------
    return next_tokens


def state_pruning(next_tokens):
    for token in next_tokens:
        if not token.state.best_token:
            token.state.best_token = token
        else:
            if token.dist <= token.state.best_token.dist:
                token.state.best_token.alive = False
                token.state.best_token = token
            else:
                token.alive = False

    # сбрасываем best_token на None для всеx узлов графа:
    for token in next_tokens:
        if token.state.best_token:
            token.state.best_token = None
    return next_tokens


def distance(X, Y):
    result = float(np.sqrt(sum(pow(X - Y, 2))))
    return result


def recognize(filename, features, graph, recognition_results,
              beam_threshold=None):
    start_state = graph[0]
    active_tokens = [Token(start_state),]
    next_tokens = []

    for ftr_frame in tqdm(features, desc="Recognition..."):
        for token in active_tokens:
            if token.alive:
                for transition_state in token.state.next_states:
                    new_token = Token(transition_state, token.dist, token.word)
                    new_token.dist += distance(ftr_frame,
                                               transition_state.feats)
                    next_tokens.append(new_token)
        # state pruning:
        next_tokens = state_pruning(next_tokens)
        #--------------------------------TODO №1-------------------------------
        # beam pruning:
        if beam_threshold is not None:
            next_tokens = beam_pruning(next_tokens, beam_threshold)
        #----------------------------------------------------------------------

        active_tokens = next_tokens
        next_tokens = []

    # поиск финальных токенов:
    final_tokens = []
    for token in active_tokens:
        if token.state.is_final and token.alive:
            final_tokens.append(token)

    # если нет финальных, то берем лучший из выживших:
    if len(final_tokens) != 0:
        win_token = final_tokens[
            np.argmin([token.dist for token in final_tokens])
        ]
    else:
        alive_tokens = [token for token in active_tokens if token.alive]
        win_token = alive_tokens[
            np.argmin([token.dist for token in alive_tokens])
        ]
        win_token.state.word = win_token.state.current_word

    # вывод результата DTW
    print("Result: {} ==> {}".format(filename, win_token.state.word))

    # совпадает ли запись с полученным эталоном:
    record_word = filename.split('_')[0]
    etalon_word = win_token.state.word.split('_')[0]
    recognition_results.append(etalon_word == record_word)

    return recognition_results


# + [markdown] id="7d5lcrUnX-Hs"
# ### Запустим наше распознавание

# + id="gyOGKDeoX-xe"
def run_recognizer(records_data_dict, graph, beam_threshold=10):
    start_time = time.time()
    recognition_results = []

    for filename in records_data_dict.keys():    
        recognition_results = recognize(
            filename, records_data_dict[filename], graph,
            recognition_results, beam_threshold
        )

    print('-' * 60)
    wer = (1 - sum(recognition_results) / len(recognition_results)) * 100
    print(f"WER: {wer:.2f}%.")
    total_time = time.time() - start_time
    print(f"Total time: {total_time:.2f} sec.")
    print('-' * 60)
    return wer, total_time


# + id="5OrSn2WoYB29" colab={"base_uri": "https://localhost:8080/", "height": 745, "referenced_widgets": ["af703f4c6d3447caa8e3a305568ff809", "3b0f377f84f24adaa5969932a30bec28", "b462ae9222e04f45aa4972a4830b56c7", "05132beca29244e7bf97e5981b4a733d", "<KEY>", "<KEY>", "<KEY>", "<KEY>", "299d562d18854973a1387cbca08ac1ab", "<KEY>", "<KEY>", "<KEY>", "<KEY>", "30a21cee1586432b9965b685602fe90f", "c8a24f0e9cb9464d881734a09581b935", "<KEY>", "<KEY>", "<KEY>", "<KEY>", "<KEY>", "<KEY>", "<KEY>", "e327219acfc5484984768111668ae5ce", "<KEY>", "<KEY>", "<KEY>", "<KEY>", "<KEY>", "<KEY>", "<KEY>", "<KEY>", "<KEY>", "<KEY>", "0edeedcc30c246599eabcb6e5a84d434", "132d351584404e559ca79bc718616299", "6ad7077eabb8458e81fa75d2f15a81df", "<KEY>", "<KEY>", "<KEY>", "<KEY>", "<KEY>", "<KEY>", "5c124f30482f4c7eaf89e575b4ab674e", "<KEY>", "e72a0402c67045ceb674d59ed0e998a9", "<KEY>", "e14033a47f46437cb0215bef28fb0ac9", "46b5522b9f6645e7b6487ab2af034e95", "<KEY>", "592dbab0720e4994a3b3dfb8849ee53a", "8f2a5795d4e143299fd93b75a2cb2641", "<KEY>", "f738afcd125b4ed8af340758934174f3", "a15313b1e32b4e5485f863e485b17bd5", "<KEY>", "3a3d68a5da6b4507b8281880a809b400", "d649001c982e4830b12078ea3872f2ea", "d8b2938529ce4e64bd02cca701074ed2", "<KEY>", "048e4e8d6c20456a963be8309bf70344", "771779900b5b4e48af4369fd2f390275", "dcd80568e3314978a08121c24a88b815", "7b1f4e2bef474523b9900bee2d2c8da1", "69a4124bb4b846da9be041d5a75db873", "<KEY>", "dc5a63d0505a4b57b169effba1fd8d9d", "<KEY>", "<KEY>", "<KEY>", "80f405e5eefc4e9880cbc90efcfa49c0", "2dca2d9d50aa40dd8188590a02d930fb", "<KEY>", "<KEY>", "<KEY>", "<KEY>", "<KEY>", "<KEY>", "da32160d6ddd4b4a92b23785cda24be1", "<KEY>", "10dc1bc380ea4bb08445be6879e293bb"]} outputId="7f927dc7-2877-42e2-a9b7-87a9d6f69228"
wer_without_skips, time_without_bp = run_recognizer(records_data_dict, graph,
                                                    beam_threshold=None)

# + [markdown] id="qoYSL3bkY6U_"
# ### **Задание №1**  (6 баллов):
# Реализуйте функцию отсечения токенов **`beam_pruning`**, описанную ранее. Подберите значение `beam_threshold` так, чтобы качество распознаваниия не ухудшалось. Во сколько раз ускорился процесс распознавания?

# + colab={"base_uri": "https://localhost:8080/", "height": 745, "referenced_widgets": ["33a58db229b9497f853f7a240362fefb", "2c42e78ee76c42cfad9ea8ab72a8f7ff", "ad78f5658f684756aecdf7d7130203c8", "523f339b333648f7ba6b6843c742511a", "9959236cf2f9415d8a1a96496bdc8ab2", "bc5a467ce0f04a999df1ffd3be170299", "d912de4cf8174725be3967ae256d224b", "c2ce2be5963e43f59d3c9d3de1bc9811", "b5b4cf9c195f4d179e4f03e826c4a656", "249a71f485f9429a963ddd4352577378", "8f25bfcee9604d0297b63a5532163e2b", "2b22ceb5e2674e8398e7c5c564bfd69c", "c6892fb33154461daaa728b898466f38", "3b9fef37a2fc41c6a36487037e14a628", "<KEY>", "0412a156ec114c75ba873be9ccf4eb45", "bd474a4727ff4c308f2a687d46329d00", "1b416f53ad9e4677a517cf50a8c38ec8", "6441e49722f04d9cbed549761fd4b65e", "<KEY>", "<KEY>", "3c4ce9fbb6ee43748eb3b55673547dab", "83349773eadb4f08a1808498edac3132", "f0e2398f398b4116aa9b822fe2909a97", "<KEY>", "<KEY>", "<KEY>", "<KEY>", "09982fea153049d3a1ba8d9348371b72", "<KEY>", "d123ff2f11ff4a2ca2b0dabaaccd8891", "862f82a33c384214a8ffdad6fb6c6c54", "<KEY>", "<KEY>", "<KEY>", "5d50e4d6b2484a3ca6e28bfe66e839c4", "049e3ed9a20749ab964ca97fad7af978", "a5f9995e7f5e4a22959ed019f8325b08", "<KEY>", "cb1599302e9e4eadb501aeec1e4c5cb8", "4d804bb7e5e943f08aba21cf4333ae5e", "<KEY>", "203b8c192a464d3d98a6949a99222948", "<KEY>", "4624333dfba44b5085cadef67ed84b6e", "73d602d208a8478e8768a5a9825fe606", "<KEY>", "<KEY>", "<KEY>", "0610a14fe8994a1fa663b9e2b0aa0dbf", "d8035d6939824504ba52511b329c4acb", "35231c2acdbe4ea894681e5a06b53e7b", "8b2e12695df243bfa4ba6f96a8e65a31", "<KEY>", "265163c01e6f48f9a3431c1bd398391c", "39aee86df4c045c186ca9f87af14eb97", "0904e6a8f589493891f3790b0285b216", "<KEY>", "180370192c71477995e831863337ccad", "<KEY>", "<KEY>", "22e8fb3145e94f5fb57d83df4326fcba", "be15bad70877494cb9aba988248cd179", "<KEY>", "47103e7474124d5492f279c76a68e637", "<KEY>", "<KEY>", "e7a0c776c57f48048b566be872501c60", "bdff15dc8a7b4a61804d1355030a8534", "6d79c397514c4959998332e75d3a5285", "<KEY>", "<KEY>", "274dc28c1e474ec8900d6dc6beac0ca1", "<KEY>", "<KEY>", "257c290e1eb94792958d6a8f81d7d7fb", "72ceb78d90f14153a77929616c72e307", "b372a17d1c41420da05d255632a4843e", "da84146226c1468ebbeb8a3a30ecb73e", "36c493097b1e40edafeeb717363187bf"]} id="Jk0DVJ1vyEt3" outputId="f404c5da-1df7-49a9-b6a0-735f01291ebf"
# WER stays the same for beam_threshold >= 2600 (heuristic value):
_, time_with_bp = run_recognizer(records_data_dict, graph,
                                 beam_threshold=2600)

# + colab={"base_uri": "https://localhost:8080/"} id="tgaqnqzjzJjo" outputId="8fff22d6-e418-4d09-ce83-0dca3ba64bb2"
time_gain = time_without_bp / time_with_bp

print(f"The recognition process has accelerated by {time_gain:.2f} times.")

# + [markdown] id="JM5YlLVXY7GA"
# ### **Задание №2** (4 балла):
# На данный момент граф реализован так, что эталоны могут только растягиваться относительно записей. Но порой нужно уметь и сжиматься. Для этого нужно добавить для узлов графа дополнительные переходы через один и два состояния (нулевой узел должен остаться прежним).
#
# Улучшила ли эта модификация качество распознавания?

# + id="4-UjeaPYMzwF"
# Let's enable skips and repeat the steps fom above:
graph = load_graph(etalons_data_dict, enable_skips=True)

# + colab={"base_uri": "https://localhost:8080/", "height": 745, "referenced_widgets": ["0dabad56a237434e927128a164eb66c5", "2f4cf1f7b0c94b428e9f9b5de0d72040", "56376b2ee4884af09e99c609b0385cab", "074b652821394285ac2b872c0076b250", "2d1baa8b4455486b8db38116b0b7b3cc", "c33013e80c2c480983876af4872ba60f", "<KEY>", "d425bbeb46d84573a2bf4e97e3efa397", "<KEY>", "<KEY>", "<KEY>", "fe9ec3e0d36b4e2a95126b98cb390943", "<KEY>", "605ad7b7f4ef44e9b464b6d2d64d1a63", "<KEY>", "b13ea83080cc4ceeaa913343ff7b16c9", "<KEY>", "<KEY>", "3bbe65ba7b7b4acda86f7270acf6f273", "83fe097182d64623a04513f31b845bdc", "<KEY>", "d27711965c3640a787a7fa768fe3a20d", "fbcb4e6ce56745eba34d2e3094f19c0c", "9b55ac0a922f4d6b9987e0989fe78875", "<KEY>", "a98185b32d8a401d84a90b4145bc63e7", "8ede97d481504a19b3a02746a8250a16", "<KEY>", "9f9aa6ef71994f88af4ca45ed4f8515f", "<KEY>", "<KEY>", "0b94adfebfc748ed920cc4b62a5d40d8", "<KEY>", "701430c03a7742f6b34629d80ab355e1", "<KEY>", "<KEY>", "<KEY>", "<KEY>", "c0d41e68c3a64025810062918b4856d3", "f1a38d86b1454e399a81babbe8137b70", "d14ec36676e94aa39a8a3c9de4863667", "381cad1a631c4214a2ad90254b843a0c", "e7b43c28eda340c39e5c24c1723f1e69", "a73c52025ff54dad97210788938728e4", "<KEY>", "<KEY>", "<KEY>", "26deac6122c544f08d59e69d383562fd", "d52c9dad1a7e4f018b5ef5a0190e1887", "7fe73a704cda43caadd4edf5c76898a9", "<KEY>", "2970606ed36346b3826c469e38352782", "<KEY>", "317e0e80e9b24112abe701e2a7e3d847", "c977e19fd03b4fb591c38adb0968a847", "9c2abb6b4f774a2491d1528ce07fd35b", "62bc85ae3f7b4291a2b19eae047f0b2a", "b37878d7336c4aeb9534b33e6cabe87a", "<KEY>", "<KEY>", "902786cab1984b39917ff131689e2e48", "8809d11e6fda43c696fdb13eff1f8ec1", "f25a9702f38b4503807ef0a1d0d72d07", "83f5230a78af468eafefc337d86d4d07", "<KEY>", "<KEY>", "<KEY>", "ca057a4eedf043888d8dad22a7c2818e", "f62ec86244bb4117ae36367f06e118a2", "96985998988c46a3a8ce582b9d00aedf", "af9f1b2a55c54467892a8d98a3e009a5", "<KEY>", "<KEY>", "ac324d76df7a4900be64de475a2b5f7e", "fce430bbd896403a856dee63a1e3f3c1", "<KEY>", "ece64a4aaf284e3ca645471d180c1ea9", "17918f6d974a4a979ef6bdefb69b70fa", "<KEY>", "47507b2494bb4a249cc127ced6c92b44"]} id="03MNMx-xM2CJ" outputId="b679f0ce-f966-4a8f-fb7e-ec9f7acb0c50"
wer_with_skips, time_without_bp = run_recognizer(records_data_dict, graph,
                                                 beam_threshold=None)

# + colab={"base_uri": "https://localhost:8080/", "height": 745, "referenced_widgets": ["31c8cb76d3fa4e859fbe807da2dae3f0", "94a6d7f7da3c4860b031b1267e1ec160", "<KEY>", "74e52039dfe8467b8e02affb98492359", "<KEY>", "561e0992b51f449dad5104c9b6e32fdd", "dcc623a9d2fe47cf95b9ad6c5b4e27ef", "a0ad052ffe3842558cacb1e05b244392", "18d481e9cd3d4621a266e9380bcea490", "<KEY>", "<KEY>", "007beae93e144d069f1732d102dff173", "a8f274d6c8174f9a8f70a9c17c2e63aa", "<KEY>", "<KEY>", "7f12036412e74462a2a011b985234637", "8a37c6265fa746e7be534d18d23c8050", "<KEY>", "<KEY>", "d6d55f3c265e408e9ebd6dd7570d7ac4", "0d8a450ee61648a684196f102f5fa05e", "8dc308ab98604214b01e6f56a6234176", "<KEY>", "<KEY>", "9191f7871c534ab0a1153a84d427fac1", "54f21a8615b5442e935149a302079082", "3b3087e423544ed69ddd050cc603a6ca", "98296695291c4ebe80ce7b65b579e57d", "3be2cd63bb3b408f88a4d215e283c0a6", "0a7d703276b043a281162c16f15ed226", "46f1755d2db4496c8be250f5ea5e776a", "a24bf893b7c3409ca05d1b1140854e32", "afbd099df2fe4ba0922814981b624835", "261645d2448346f58245f0e0ca1e75f5", "96228efe333d44d4ae8535fa344997fa", "fd24c947d38b4ef4b90666828ce6494c", "9266e4c4e0814b50a61c7a88ca3d299c", "<KEY>", "212336ddcfc8402eb50ef0a0f5638b48", "<KEY>", "65c4f5c701dc4af683135a2614bab32a", "04916a9108964a1cbfce6caf04ea3b0e", "abd3b32a3e77430fb2eb546795ef0b48", "<KEY>", "9aabbc25c52a4d1aba1f23335c1a6024", "38e28c15394443bab1a2c80a6b4b9936", "<KEY>", "e4fd1a648c6f4f28aeca1deaff568af6", "3ccd47c34ea045deb80a6bd38bd5a027", "c25ae3452ed64c728eb186fe4038a0ff", "2c1149aaba46497186a73ce031a1471f", "<KEY>", "761c81c8d12d424da36b9831c53864e6", "<KEY>", "<KEY>", "d3c9a1a8a0854e0f90168cc5ad3319d6", "a88a6f4407be423a887566ed37569c1d", "92382fa4c53b4374b431eb8415d182ea", "<KEY>", "<KEY>", "e5f25f57475640e4af8e5e405f788701", "<KEY>", "<KEY>", "<KEY>", "9afe72cd3e0f467c87b458ef3adc31da", "643071562213451ea9cc8e0b0817b9c1", "1dbdaeb2ece046fe85d1ec8999498d8c", "<KEY>", "68a5ce15db0d4c79a31c2106664b9ad0", "2ea4c5ae579441649dcb0f93abb4e23a", "3a0638e9f6af4061a48df3912cef7763", "b3d614eb82974d469e9003e085df2080", "<KEY>", "a85e7bc6cf1441ce8dc6a0775b9e26f1", "<KEY>", "f07ca66827b140718dff540fdb4af6d8", "2dd76813cc8a49d4baa1517a84270458", "92f6afa4920e4c8493cd60894043a4c6", "7c94f1298ada4e94b62011c8d8901d6a", "3b97b48cf0fd4ce79ff9193ce74c86a5"]} id="5bcZTqTqM4uo" outputId="2609263c-f153-4986-8489-3baf6cbf6c81"
_, time_with_bp = run_recognizer(records_data_dict, graph,
                                 beam_threshold=2600)

# + colab={"base_uri": "https://localhost:8080/"} id="ZLZD-yNegZ_9" outputId="addd9e53-6f9e-429b-fe87-e1ec7ece2d5f"
wer_diff = wer_without_skips - wer_with_skips

if wer_diff > 0:
    print(f"The modification improved the recognition quality " \
          f"by {wer_diff:.2f}%.")
else:
    print(f"Whoops, for some reason the recognition quality decreased " \
          f"from {wer_without_skips}% to {wer_with_skips}%.")

# + colab={"base_uri": "https://localhost:8080/"} id="-m01llqKM7BX" outputId="7f7e83fe-4922-4c34-b7be-3b9daca6c0a4"
time_gain = time_without_bp / time_with_bp

print(f"The recognition process has accelerated by {time_gain:.2f} times.")

StarcoderdataJupyterScriptsDedupFiltered