Rosyjska otwarta mowa na tekst

Kolekcja próbek mowy z różnych źródeł dźwięku. Ten zestaw danych zawiera krótkie klipy audio w języku rosyjskim.

Uwaga

Firma Microsoft udostępnia zestawy danych Azure Open Datasets w zasadzie "tak jak to jest". Firma Microsoft nie udziela żadnych gwarancji, wyraźnych lub domniemanych, gwarancji ani warunków w odniesieniu do korzystania z zestawów danych. W zakresie dozwolonym na mocy prawa lokalnego firma Microsoft nie ponosi odpowiedzialności za wszelkie szkody lub straty, w tym bezpośrednie, wynikowe, specjalne, pośrednie, przypadkowe lub karne wynikające z korzystania z zestawów danych.

Zestaw danych jest udostępniany zgodnie z pierwotnymi warunkami, na jakich firma Microsoft otrzymała dane źródłowe. Zestaw danych może zawierać dane pozyskane z firmy Microsoft.

Ten rosyjski zestaw danych zamiany mowy na tekst (STT) obejmuje:

  • Ok. 16 milionów wypowiedzi
  • ~20 000 godzin
  • 2,3 TB (nieskompresowane w formacie .wav w int16), 356G w systemie dei
  • Wszystkie pliki zostały przekształcone w zestawy danych weryfikacji, z wyjątkiem zestawów danych weryfikacji

Głównym przeznaczeniem zestawu danych jest trenowanie modeli przekształcających mowę na tekst.

Budowa zestawu danych

Rozmiar zestawu danych jest podawany dla plików wav.

DATASET WYPOWIEDZI GODZIN GB SECS/CHARS COMMENT ADNOTACJI JAKOŚĆ/HAŁAS
radio_v4 (*) 7 603 192 10 430 1,195 5 s / 68 Przycisk opcji Wyrównaj 95% / chrupiące
public_speech (*) 1 700 060 2 709 301 6 s / 79 Przemówienia publiczne Wyrównaj 95% / chrupiące
audiobook_2 1 149 404 1,511 162 5 s / 56 Książki Wyrównaj 95% / chrupiące
radio_2 651 645 1 439 154 8 s / 110 Przycisk opcji Wyrównaj 95% / chrupiące
public_youtube1120 1 410 979 1,104 237 3 s / 34 YouTube Napisy 95% / ~crisp
public_youtube700 759 483 701 75 3 s / 43 YouTube Napisy 95% / ~crisp
tts_russian_addresses 1 741 838 754 81 2 s / 20 Adresy Głosy TTS 4 100% / wyraźne
asr_public_phone_calls_2 603 797 601 66 4 s / 37 Rozmowy telefoniczne ASR 70% / hałaśliwy
public_youtube1120_hq 369 245 291 31 3 s / 37 YouTube HQ Napisy 95% / ~crisp
asr_public_phone_calls_1 233 868 / 2000 211 23 3 s / 29 Rozmowy telefoniczne ASR 70% / hałaśliwy
radio_v4_add (*) 92 679 157 18 6 s / 80 Przycisk opcji Wyrównaj 95% / chrupiące
asr_public_stories_2 78 186 78 9 4 s / 43 Książki ASR 80% / chrupiące
asr_public_stories_1 46 142 38 4 3 s / 30 Książki ASR 80% / chrupiące
public_series_1 20 243 17 2 3 s / 38 YouTube Napisy 95% / ~crisp
asr_calls_2_val 12 950 7,7 2 2 s / 34 Rozmowy telefoniczne Ręczne adnotacje 99% / chrupiące
public_lecture_1 6 803 6 1 3 s / 47 Wykłady Napisy 95% / chrupiące
buriy_audiobooks_2_val 7,850 4,9 1 2 s / 31 Książki Ręczne adnotacje 99% / chrupiące
public_youtube700_val 7,311 4,5 1 2 s / 35 YouTube Ręczne adnotacje 99% / chrupiące

(*) Za pomocą plików txt udostępniana jest tylko próbka danych.

Metodologia adnotacji

Zestaw danych został przygotowany za pomocą otwartych źródeł. Długie zdania są dzielone na fragmenty audio za pomocą funkcji wykrywania aktywności głosowej i wyrównywania. Niektóre typy audio są automatycznie dodawać adnotacje i weryfikować statystycznie przy użyciu heurystyki.

Woluminy danych i częstotliwość aktualizacji

Łączny rozmiar zestawu danych wynosi 350 GB. Całkowity rozmiar zestawu danych z publicznie udostępnionymi etykietami wynosi 130 GB.

Sam zestaw danych prawdopodobnie nie zostanie zaktualizowany pod kątem zgodności z poprzednimi wersjami. Postępuj zgodnie z oryginalnym repozytorium testów porównawczych i wyklucz pliki.

W przyszłości mogą zostać dodane nowe domeny i języki.

Normalizacja audio

Wszystkie pliki są znormalizowane w celu łatwiejszego i szybszego rozszerzania środowiska uruchomieniowego. Przetwarzanie jest następujące:

  • Przekonwertowane do formatu mono, jeśli to konieczne
  • W razie potrzeby przekonwertowana na częstotliwość próbkowania 16 kHz;
  • Zapisane jako 16-bitowe wartości całkowite
  • Przekonwertowane do formatu OPUS

Metodologia bazy danych na dysku

Każdy plik audio (wav, binarny) ma skrót. Skrót służy do tworzenia hierarchii folderów w celu uzyskania bardziej optymalnej operacji fs.

target_format = 'wav'
wavb = wav.tobytes()

f_hash = hashlib.sha1(wavb).hexdigest()

store_path = Path(root_folder,
                  f_hash[0],
                  f_hash[1:3],
                  f_hash[3:15] + '.' + target_format)

Pobieranie

Zestaw danych jest udostępniany w dwóch formularzach:

  • Archiwa dostępne za pośrednictwem usługi Azure Blob Storage i/lub linków bezpośrednich;
  • Oryginalne pliki dostępne za pośrednictwem usługi Azure Blob Storage; Wszystko jest przechowywane w 'https://azureopendatastorage.blob.core.windows.net/openstt/'

Struktura folderów:

└── ru_open_stt_opus                                            <= archived folders
│   │
│   ├── archives
│   │    ├── asr_calls_2_val.tar.gz                             <= tar.gz archives with opus and wav files
│   │    │   ...                                                <= see the below table for enumeration
│   │    └── tts_russian_addresses_rhvoice_4voices.tar.gz
│   │
│   └── manifests
│        ├── asr_calls_2_val.csv                                <= csv files with wav_path, text_path, duration (see notebooks)
│        │   ...
│        └── tts_russian_addresses_rhvoice_4voices.csv
│
└── ru_open_stt_opus_unpacked                                   <= a separate folder for each uploaded domain
    ├── public_youtube1120
    │    ├── 0                                                  <= see "On disk DB methodology" for details
    │    ├── 1
    │    │   ├── 00
    │    │   │  ...
    │    │   └── ff
    │    │        ├── *.opus                                   <= actual files
    │    │        └── *.txt
    │    │   ...
    │    └── f
    │
    ├── public_youtube1120_hq
    ├── public_youtube700_val
    ├── asr_calls_2_val
    ├── radio_2
    ├── private_buriy_audiobooks_2
    ├── asr_public_phone_calls_2
    ├── asr_public_stories_2
    ├── asr_public_stories_1
    ├── public_lecture_1
    ├── asr_public_phone_calls_1
    ├── public_series_1
    └── public_youtube700
DATASET GB, WAV GB, ARCHIWUM ARCHIWUM ŹRÓDŁO MANIFESTU
Szkolenie
Przykłady z radia i przemówień publicznych - 11,4 opus i txt - manifest
audiobook_2 162 25,8 opus i txt Internet i wyrównanie manifest
radio_2 154 24,6 opus i txt Przycisk opcji manifest
public_youtube1120 237 19,0 opus i txt Wideo z usługi YouTube manifest
asr_public_phone_calls_2 66 9,4 opus i txt Internet i ASR manifest
public_youtube1120_hq 31 4,9 opus i txt Wideo z usługi YouTube manifest
asr_public_stories_2 9 1.4 opus i txt Internet i wyrównanie manifest
tts_russian_addresses_rhvoice_4voices 80,9 12,9 opus i txt TTS manifest
public_youtube700 75,0 12,2 opus i txt Wideo z usługi YouTube manifest
asr_public_phone_calls_1 22,7 3.2 opus i txt Internet i ASR manifest
asr_public_stories_1 4.1 0,7 opus i txt Historie publiczne manifest
public_series_1 1,9 0.3 opus i txt Serie publiczne manifest
public_lecture_1 0,7 0,1 opus i txt Internet i ręcznie manifest
Val
asr_calls_2_val 2 0,8 wav i txt Internet manifest
buriy_audiobooks_2_val 1 0,5 wav i txt Książki i ręcznie manifest
public_youtube700_val 2 0.13 wav i txt Wideo z usługi YouTube i ręcznie manifest

Instrukcje pobierania

Pobieranie bezpośrednie

Aby uzyskać instrukcje dotyczące sposobu bezpośredniego pobierania zestawu danych, zobacz stronę instrukcji pobierania GitHub.

Dodatkowe informacje

Aby uzyskać pomoc lub pytania dotyczące danych, skontaktuj się z autorami danych pod adresem aveysov@gmail.com

Ta licencja pozwala użytkownikom rozpowszechniać, remix, dostosowywać i opierać się na materiale w dowolnym nośniku lub formacie tylko w celach niekomercyjnych i tylko tak długo, jak przypisanie jest podane twórcy. Obejmuje następujące elementy:

  • BY — środki muszą zostać przyznane twórcy
  • NC — dozwolone są tylko niekomercyjne zastosowania pracy

Licencja CC-BY-NC i użytek komercyjny jest możliwy po zawarciu umowy z autorami zestawu danych.

Dostęp do danych

Azure Notebooks

Funkcje /zależności pomocnika

Kompilowanie pliku libsndfile

Efektywnym sposobem odczytywania plików w języku Python, który nie wiąże się ze znaczącym obciążeniem, jest użycie pliku pysoundfile (otoki CFFI języka Python wokół pliku libsoundfile).

Obsługa firmy Opus została wdrożona w górę, ale nie została prawidłowo wydana. W związku z tym zdecydowaliśmy się na niestandardową kompilację + stosowanie małp.

Zazwyczaj należy to uruchomić w powłoce z dostępem sudo:

apt-get update
apt-get install cmake autoconf autogen automake build-essential libasound2-dev \
libflac-dev libogg-dev libtool libvorbis-dev libopus-dev pkg-config -y

cd /usr/local/lib
git clone https://github.com/erikd/libsndfile.git
cd libsndfile
git reset --hard 49b7d61
mkdir -p build && cd build

cmake .. -DBUILD_SHARED_LIBS=ON
make && make install
cmake --build .

Funkcje /zależności pomocnika

Zainstaluj następujące biblioteki:

pandas
numpy
scipy
tqdm
soundfile
librosa

Manifesty to pliki csv z następującymi kolumnami:

  • Ścieżka do dźwięku
  • Ścieżka do pliku tekstowego
  • Czas trwania

Okazały się najprostszym formatem uzyskiwania dostępu do danych.

Aby ułatwić korzystanie, wszystkie manifesty są już ponownie wyrootowane. Wszystkie ścieżki w nich są względne. Musisz podać folder główny.

# manifest utils
import os
import numpy as np
import pandas as pd
from tqdm import tqdm
from urllib.request import urlopen


def reroot_manifest(manifest_df,
                    source_path,
                    target_path):
    if source_path != '':
        manifest_df.wav_path = manifest_df.wav_path.apply(lambda x: x.replace(source_path,
                                                                              target_path))
        manifest_df.text_path = manifest_df.text_path.apply(lambda x: x.replace(source_path,
                                                                                target_path))
    else:
        manifest_df.wav_path = manifest_df.wav_path.apply(lambda x: os.path.join(target_path, x))
        manifest_df.text_path = manifest_df.text_path.apply(lambda x: os.path.join(target_path, x))    
    return manifest_df


def save_manifest(manifest_df,
                  path,
                  domain=False):
    if domain:
        assert list(manifest_df.columns) == ['wav_path', 'text_path', 'duration', 'domain']
    else:
        assert list(manifest_df.columns) == ['wav_path', 'text_path', 'duration']

    manifest_df.reset_index(drop=True).sort_values(by='duration',
                                                   ascending=True).to_csv(path,
                                                                          sep=',',
                                                                          header=False,
                                                                          index=False)
    return True


def read_manifest(manifest_path,
                  domain=False):
    if domain:
        return pd.read_csv(manifest_path,
                        names=['wav_path',
                               'text_path',
                               'duration',
                               'domain'])
    else:
        return pd.read_csv(manifest_path,
                        names=['wav_path',
                               'text_path',
                               'duration'])


def check_files(manifest_df,
                domain=False):
    orig_len = len(manifest_df)
    if domain:
        assert list(manifest_df.columns) == ['wav_path', 'text_path', 'duration']
    else:
        assert list(manifest_df.columns) == ['wav_path', 'text_path', 'duration', 'domain']
    wav_paths = list(manifest_df.wav_path.values)
    text_path = list(manifest_df.text_path.values)

    omitted_wavs = []
    omitted_txts = []

    for wav_path, text_path in zip(wav_paths, text_path):
        if not os.path.exists(wav_path):
            print('Dropping {}'.format(wav_path))
            omitted_wavs.append(wav_path)
        if not os.path.exists(text_path):
            print('Dropping {}'.format(text_path))
            omitted_txts.append(text_path)

    manifest_df = manifest_df[~manifest_df.wav_path.isin(omitted_wavs)]
    manifest_df = manifest_df[~manifest_df.text_path.isin(omitted_txts)]
    final_len = len(manifest_df)

    if final_len != orig_len:
        print('Removed {} lines'.format(orig_len-final_len))
    return manifest_df


def plain_merge_manifests(manifest_paths,
                          MIN_DURATION=0.1,
                          MAX_DURATION=100):

    manifest_df = pd.concat([read_manifest(_)
                             for _ in manifest_paths])
    manifest_df = check_files(manifest_df)

    manifest_df_fit = manifest_df[(manifest_df.duration>=MIN_DURATION) &
                                  (manifest_df.duration<=MAX_DURATION)]

    manifest_df_non_fit = manifest_df[(manifest_df.duration<MIN_DURATION) |
                                      (manifest_df.duration>MAX_DURATION)]

    print(f'Good hours: {manifest_df_fit.duration.sum() / 3600:.2f}')
    print(f'Bad hours: {manifest_df_non_fit.duration.sum() / 3600:.2f}')

    return manifest_df_fit


def save_txt_file(wav_path, text):
    txt_path = wav_path.replace('.wav','.txt')
    with open(txt_path, "w") as text_file:
        print(text, file=text_file)
    return txt_path


def read_txt_file(text_path):
    #with open(text_path, 'r') as file:
    response = urlopen(text_path)
    file = response.readlines()
    for i in range(len(file)):
        file[i] = file[i].decode('utf8')
    return file 

def create_manifest_from_df(df, domain=False):
    if domain:
        columns = ['wav_path', 'text_path', 'duration', 'domain']
    else:
        columns = ['wav_path', 'text_path', 'duration']
    manifest = df[columns]
    return manifest


def create_txt_files(manifest_df):
    assert 'text' in manifest_df.columns
    assert 'wav_path' in manifest_df.columns
    wav_paths, texts = list(manifest_df['wav_path'].values), list(manifest_df['text'].values)
    # not using multiprocessing for simplicity
    txt_paths = [save_txt_file(*_) for _ in tqdm(zip(wav_paths, texts), total=len(wav_paths))]
    manifest_df['text_path'] = txt_paths
    return manifest_df


def replace_encoded(text):
    text = text.lower()
    if '2' in text:
        text = list(text)
        _text = []
        for i,char in enumerate(text):
            if char=='2':
                try:
                    _text.extend([_text[-1]])
                except:
                    print(''.join(text))
            else:
                _text.extend([char])
        text = ''.join(_text)
    return text
# reading opus files
import os
import soundfile as sf



# Fx for soundfile read/write functions
def fx_seek(self, frames, whence=os.SEEK_SET):
    self._check_if_closed()
    position = sf._snd.sf_seek(self._file, frames, whence)
    return position


def fx_get_format_from_filename(file, mode):
    format = ''
    file = getattr(file, 'name', file)
    try:
        format = os.path.splitext(file)[-1][1:]
        format = format.decode('utf-8', 'replace')
    except Exception:
        pass
    if format == 'opus':
        return 'OGG'
    if format.upper() not in sf._formats and 'r' not in mode:
        raise TypeError("No format specified and unable to get format from "
                        "file extension: {0!r}".format(file))
    return format


#sf._snd = sf._ffi.dlopen('/usr/local/lib/libsndfile/build/libsndfile.so.1.0.29')
sf._subtypes['OPUS'] = 0x0064
sf.SoundFile.seek = fx_seek
sf._get_format_from_filename = fx_get_format_from_filename


def read(file, **kwargs):
    return sf.read(file, **kwargs)


def write(file, data, samplerate, **kwargs):
    return sf.write(file, data, samplerate, **kwargs)
# display utils
import gc
from IPython.display import HTML, Audio, display_html
pd.set_option('display.max_colwidth', 3000)
#Prepend_path is set to read directly from Azure. To read from local replace below string with path to the downloaded dataset files
prepend_path = 'https://azureopendatastorage.blob.core.windows.net/openstt/ru_open_stt_opus_unpacked/'


def audio_player(audio_path):
    return '<audio preload="none" controls="controls"><source src="{}" type="audio/wav"></audio>'.format(audio_path)

def display_manifest(manifest_df):
    display_df = manifest_df
    display_df['wav'] = [audio_player(prepend_path+path) for path in display_df.wav_path]
    display_df['txt'] = [read_txt_file(prepend_path+path) for path in tqdm(display_df.text_path)]
    audio_style = '<style>audio {height:44px;border:0;padding:0 20px 0px;margin:-10px -20px -20px;}</style>'
    display_df = display_df[['wav','txt', 'duration']]
    display(HTML(audio_style + display_df.to_html(escape=False)))
    del display_df
    gc.collect()

Odtwarzanie za pomocą zestawu danych

Odtwarzanie próbki plików

Większość przeglądarek platform obsługuje odtwarzanie dźwięku natywnego. Aby wyświetlić dane, możemy użyć odtwarzaczy audio HTML5.

manifest_df = read_manifest(prepend_path +'/manifests/public_series_1.csv')
#manifest_df = reroot_manifest(manifest_df,
                              #source_path='',
                              #target_path='../../../../../nvme/stt/data/ru_open_stt/')

sample = manifest_df.sample(n=20)
display_manifest(sample)

Odczytywanie pliku

!ls ru_open_stt_opus/manifests/*.csv

Niektóre przykłady pokazujące, jak najlepiej odczytywać pliki wav i opus.

Scipy jest najszybszy dla wav. Pysoundfile jest najlepszym ogólnie dla opus.

%matplotlib inline

import librosa
from scipy.io import wavfile
from librosa import display as ldisplay
from matplotlib import pyplot as plt

Odczytywanie wav

manifest_df = read_manifest(prepend_path +'manifests/asr_calls_2_val.csv')
#manifest_df = reroot_manifest(manifest_df,
                              #source_path='',
                              #target_path='../../../../../nvme/stt/data/ru_open_stt/')
sample = manifest_df.sample(n=5)
display_manifest(sample)
from io import BytesIO

wav_path = sample.iloc[0].wav_path
response = urlopen(prepend_path+wav_path)
data = response.read()
sr, wav = wavfile.read(BytesIO(data))
wav.astype('float32')
absmax = np.max(np.abs(wav))
wav =  wav / absmax
# shortest way to plot a spectrogram
D = librosa.amplitude_to_db(np.abs(librosa.stft(wav)), ref=np.max)
plt.figure(figsize=(12, 6))
ldisplay.specshow(D, y_axis='log')
plt.colorbar(format='%+2.0f dB')
plt.title('Log-frequency power spectrogram')
# shortest way to plot an envelope
plt.figure(figsize=(12, 6))
ldisplay.waveplot(wav, sr=sr, max_points=50000.0, x_axis='time', offset=0.0, max_sr=1000, ax=None)

Przeczytaj opus

manifest_df = read_manifest(prepend_path +'manifests/asr_public_phone_calls_2.csv')
#manifest_df = reroot_manifest(manifest_df,
                              #source_path='',
                              #target_path='../../../../../nvme/stt/data/ru_open_stt/')
sample = manifest_df.sample(n=5)
display_manifest(sample)
opus_path = sample.iloc[0].wav_path
response = urlopen(prepend_path+opus_path)
data = response.read()
wav, sr = sf.read(BytesIO(data))
wav.astype('float32')
absmax = np.max(np.abs(wav))
wav =  wav / absmax
# shortest way to plot a spectrogram
D = librosa.amplitude_to_db(np.abs(librosa.stft(wav)), ref=np.max)
plt.figure(figsize=(12, 6))
ldisplay.specshow(D, y_axis='log')
plt.colorbar(format='%+2.0f dB')
plt.title('Log-frequency power spectrogram')
# shortest way to plot an envelope
plt.figure(figsize=(12, 6))
ldisplay.waveplot(wav, sr=sr, max_points=50000.0, x_axis='time', offset=0.0, max_sr=1000, ax=None)

Następne kroki

Wyświetl resztę zestawów danych w katalogu Open Datasets (Otwarte zestawy danych).