# Препроцессинг набора аудиоданных

Загрузка набора данных с помощью 🤗 Datasets - это только половина удовольствия. Если вы планируете использовать ее либо для обучения модели, либо для выполнения инференса, необходимо предварительно обработать данные. В общем случае это включает в себя следующие шаги:

- Передискретизация аудиоданных
- Фильтрация набора данных
- Преобразование аудиоданных в ожидаемый моделью формат входных данных

## Передискретизация аудиоданных

Функция `load_dataset` загружает аудиопримеры с той частотой дискретизации, с которой они были опубликованы. Это не всегда та частота дискретизации, которая ожидается моделью, которую вы планируете обучать или использовать для инференса. Если есть расхождение между частотой дискретизации, можно передискретизировать звук до ожидаемой моделью частоты дискретизации.

Большинство имеющихся предварительно обученных моделей были обучены на аудиоданных с частотой дискретизации 16 кГц. Когда мы исследовали набор данных MINDS-14, вы могли заметить, что он сэмплирован с частотой 8 кГц, что означает, что нам, скорее всего, потребуется увеличить частоту дискретизации.

Чтобы сделать это, используйте метод 🤗 Datasets `cast_column`. Эта операция не изменяет звук непосредственно в наборе данных (in-place), а дает сигнал datasets для передискретизации аудиопримеров “на лету” при их загрузке. Следующий код установит частоту дискретизации равной 16 кГц:

    from datasets import Audio

    minds = minds.cast_column("audio", Audio(sampling_rate=16_000))

Перезагрузим первый аудиопример из набора данных MINDS-14 и проверим, что он был передискретизирован до нужной `sampling rate`:

    minds[0]

Output:

    {
        "path": "/root/.cache/huggingface/datasets/downloads/extracted/f14948e0e84be638dd7943ac36518a4cf3324e8b7aa331c5ab11541518e9368c/en-AU~PAY_BILL/response_4.wav",
        "audio": {
            "path": "/root/.cache/huggingface/datasets/downloads/extracted/f14948e0e84be638dd7943ac36518a4cf3324e8b7aa331c5ab11541518e9368c/en-AU~PAY_BILL/response_4.wav",
            "array": array(
                [
                    2.0634243e-05,
                    1.9437837e-04,
                    2.2419340e-04,
                    ...,
                    9.3852862e-04,
                    1.1302452e-03,
                    7.1531429e-04,
                ],
                dtype=float32,
            ),
            "sampling_rate": 16000,
        },
        "transcription": "I would like to pay my electricity bill using my card can you please assist",
        "intent_class": 13,
    }

Вы можете заметить, что значения массива теперь также отличаются. Это связано с тем, что теперь для каждого значения амплитуды мы имеем в два раза больше значений чем раньше.

💡 Некоторые сведения о передискретизации: Если аудиосигнал дискретизирован с частотой 8 кГц, т.е. имеет 8000 выборок в секунду, то мы знаем, что он не содержит частот выше 4 кГц. Это гарантируется теоремой Найквиста о дискретизации. Благодаря этому мы можем быть уверены, что между точками дискретизации исходный непрерывный сигнал всегда имеет плавную кривую. Повышение частоты дискретизации до более высокой сводится к вычислению дополнительных значений выборки, которые находятся между существующими, путем аппроксимации этой кривой. Однако понижающая дискретизация требует, чтобы мы сначала отфильтровали все частоты, которые будут выше нового предела Найквиста, прежде чем оценивать новые точки дискретизации. Другими словами, нельзя понизить дискретизацию в 2 раза, просто отбрасывая каждый второй сэмпл - это приведет к появлению искажений в сигнале, называемых наложениями. Корректная передискретизация - дело непростое, и его лучше доверить проверенным библиотекам, таким как librosa или 🤗 Datasets.

## Фильтрация набора данных

Возможно, потребуется отфильтровать данные по каким-либо критериям. Одним из распространенных случаев является ограничение аудиопримеров определенной продолжительности. Например, для предотвращения ошибок, связанных с выходом за пределы доступного обьёма памяти, необходимо отфильтровать все примеры длительностью более 20 секунд при обучении модели.

Мы можем сделать это, используя метод 🤗 Datasets `filter` и передать ему функцию с логикой фильтрации. Начнем с того, что напишем функцию которая определяет, какие примеры следует оставить, а какие отбросить. Эта функция, `is_audio_length_in_range`, возвращает `True`, если длина образца меньше 20 с, и `False`, если больше 20 с.

    MAX_DURATION_IN_SECONDS = 20.0

    def is_audio_length_in_range(input_length):
        return input_length < MAX_DURATION_IN_SECONDS

Функция фильтрации может быть применена к столбцу набора данных, но в данном наборе [NL1] столбец с длительностью звуковой дорожки отсутствует. Однако мы можем его создать, отфильтровать по значениям в этом столбце, а затем удалить.

    # используем librosa для получения длительности фрагмента из аудиофайла
    new_column = [librosa.get_duration(path=x) for x in minds["path"]]
    minds = minds.add_column("duration", new_column)

    # используем метод 🤗 Datasets `filter` для применения функции фильтрации
    minds = minds.filter(is_audio_length_in_range, input_columns=["duration"])

    # удалим временный вспомогательный столбец
    minds = minds.remove_columns(["duration"])
    minds

Output:

    Dataset({features: ["path", "audio", "transcription", "intent_class"], num_rows: 624})

Мы можем убедиться, что набор данных был отфильтрован с 654 примеров до 624.

## Препроцессинг аудиоданных

Одним из наиболее сложных аспектов работы с наборами аудиоданных является подготовка данных в нужном для обучения модели формате. Как вы видели, исходные аудиоданные поступают в виде массива значений образцов. Однако предварительно обученные модели, независимо от того, используете ли вы их для инференса или для дообучения под вашу задачу, ожидают, что сырые данные будут преобразованы во входные признаки. Требования к входным признакам могут быть различными для разных моделей - они зависят от архитектуры модели и данных, на которых она была предварительно обучена. Хорошей новостью является то, что для каждой поддерживаемой аудиомодели 🤗 Transformers предлагает класс feature extractor который может преобразовать сырые аудиоданные во входные признаки, ожидаемые моделью.

Что же делает экстрактор признаков с исходными аудиоданными? Давайте посмотрим на экстрактор признаков в Whisper, чтобы понять некоторые общие преобразования извлечения признаков. Whisper - это предварительно обученная модель для автоматического распознавания речи (ASR), опубликованная в сентябре 2022 года Алеком Рэдфордом и другими из OpenAI.

Сначала экстрактор признаков Whisper дополняет/обрезает батч аудиопримеров таким образом, что все образцы имеют длительность входного сигнала 30 секунд. Примеры короче этого значения дополняются до 30 секунд путем добавления нулей в конец последовательности (нули в аудиосигнале соответствуют отсутствию сигнала или тишине). Примеры длиной более 30 секунд усекаются до 30 секунд. Поскольку все элементы в батче дополняются/обрезаются до максимальной длины во входном пространстве, необходимость в использованрии маски внимания отпадает. Whisper уникален в этом отношении, большинству других аудиомоделей требуется маска внимания, которая подробно описывает, где последовательности были дополненны, и, следовательно, где они должны быть проигнорированы в механизме самовнимания. Whisper обучен работать без маски внимания и непосредственно по речевым сигналам определять, где следует игнорировать входные сигналы.

Второй операцией, которую выполняет экстрактор признаков Whisper, является преобразование дополненных звуковых массивов в лог-мел спектрограммы. Как вы помните, эти спектрограммы описывают, как изменяются частоты сигнала с течением времени, выраженные в шкале мел и измеряются в децибелах (логарифмическая часть), чтобы сделать частоты и амплитуды более репрезентативными для человеческого слуха.

Все эти преобразования могут быть применены к необработанным аудиоданным с помощью пары строк кода. Давайте загрузим экстрактор признаков из предварительно обученной контрольной точки Whisper, чтобы получить готовые аудиоданные:

    from transformers import WhisperFeatureExtractor

    feature_extractor = WhisperFeatureExtractor.from_pretrained("openai/whisper-small")

Далее можно написать функцию для предварительной обработки одного аудиопримера, передавая его в `feature_extractor`.

    def prepare_dataset(example):
        audio = example["audio"]
        features = feature_extractor(
            audio["array"], sampling_rate=audio["sampling_rate"], padding=True
        )
        return features

Мы можем применить функцию подготовки данных ко всем нашим обучающим примерам, используя метод 🤗 Datasets’ map:

    minds = minds.map(prepare_dataset)
    minds

Output:

    Dataset(
        {
            features: ["path", "audio", "transcription", "intent_class", "input_features"],
            num_rows: 624,
        }
    )

Вот так просто мы получили лог-мел спектрограммы в качестве `input_features` в наборе данных.

Визуализируем ее для одного из примеров в наборе данных `minds`:

    import numpy as np

    example = minds[0]
    input_features = example["input_features"]

    plt.figure().set_figwidth(12)
    librosa.display.specshow(
        np.asarray(input_features[0]),
        x_axis="time",
        y_axis="mel",
        sr=feature_extractor.sampling_rate,
        hop_length=feature_extractor.hop_length,
    )
    plt.colorbar()

Теперь вы можете увидеть, как выглядит входной аудиосигнал для модели Whisper после препроцессинга.

Класс модели feature extractor занимается преобразованием сырых аудиоданных в формат, ожидаемый моделью. Однако, многие задачи с использованием звука являются мультимодальными, например, распознавание речи. В таких случаях 🤗 трансформеры также предлагают специфичные для конкретной модели токенизаторы для обработки текстовых данных. Для более глубокого изучения токенизаторов обратитесь к нашему NLP курсу.

Вы можете загрузить экстрактор признаков и токенизатор для Whisper и других мультимодальных моделей отдельно, либо загрузить их через так называемый процессор. Чтобы еще больше упростить задачу, используйте `AutoProcessor` для загрузки экстрактора признаков и процессора модели из контрольной точки, например, так:

    from transformers import AutoProcessor

    processor = AutoProcessor.from_pretrained("openai/whisper-small")

Здесь мы проиллюстрировали основные этапы подготовки данных. Конечно, пользовательские данные могут потребовать более сложного препроцессинга. В этом случае можно расширить функцию `prepare_dataset` для выполнения любых преобразований пользовательских данных. С 🤗 Datasets, если вы можете записать процес подготовки данных как функцию Python, вы можете применить его к вашему набору данных!