Ruská otevřená řeč na text

Kolekce ukázek řeči získaných z různých zdrojů zvuku. Tato datová sada obsahuje krátké zvukové klipy v ruštině.

Poznámka:

Microsoft poskytuje datové sady Azure Open Datasets na bázi "tak, jak je". Společnost Microsoft neposkytuje žádné záruky, výslovné ani předpokládané záruky ani podmínky týkající se vašeho používání datových sad. V rozsahu povoleném vaším místním zákonem společnost Microsoft odmítá veškerou odpovědnost za případné škody nebo ztráty, včetně přímých, následných, zvláštních, nepřímých, náhodných nebo represivních, vyplývajících z vašeho používání datových sad.

Na tuto datovou sadu se vztahují původní podmínky, které Microsoft přijal se zdrojovými daty. Datová sada může obsahovat data pocházející z Microsoftu.

Tato datová sada s textem (STT) v ruštině zahrnuje:

  • Přibližně 16 milionů promluv
  • ~20 000 hodin
  • 2,3 TB (nekomprimováno ve formátu .wav v int16), 356G v opusu
  • Všechny soubory byly transformovány na opus s výjimkou ověřovacích datových sad.

Hlavním cílem datové sady je trénovat modely převodu řeči na text.

Kompozice datové sady

Velikost datové sady je uvedena pro soubory .wav.

DATASET PROMLUVY HODIN GB SECS/CHARS COMMENT POZNÁMKY KVALITA/ŠUM
radio_v4 (*) 7 603 192 10 430 1 195 5 s / 68 Rádio Align 95% / čisté
public_speech (*) 1 700 060 2 709 301 6 s / 79 Veřejné projevy Align 95% / čisté
audiobook_2 1 149 404 1 511 162 5 s / 56 Knihy Align 95% / čisté
radio_2 651 645 1 439 154 8 s / 110 Rádio Align 95% / čisté
public_youtube1120 1 410 979 1 104 237 3 s / 34 YouTube Titulky 95% / ~praskání
public_youtube700 759 483 701 75 3 s / 43 YouTube Titulky 95% / ~praskání
tts_russian_addresses 1 741 838 754 81 2 s / 20 Adresy Hlasy TTS 4 100% / praskání
asr_public_phone_calls_2 603 797 601 66 4 s / 37 Telefonní hovory ASR 70% / zašumělé
public_youtube1120_hq 369 245 291 31 3 s / 37 YouTube HQ Titulky 95% / ~praskání
asr_public_phone_calls_1 233 868 211 23 3 s / 29 Telefonní hovory ASR 70% / zašumělé
radio_v4_add (*) 92 679 157 18 6 s / 80 Rádio Align 95% / čisté
asr_public_stories_2 78 186 78 9 4 s / 43 Knihy ASR 80% / čisté
asr_public_stories_1 46 142 38 4 3 s / 30 Knihy ASR 80% / čisté
public_series_1 20 243 17 2 3 s / 38 YouTube Titulky 95% / ~praskání
asr_calls_2_val 12 950 7,7 2 2 s / 34 Telefonní hovory Ruční anotace 99% / praskání
public_lecture_1 6 803 6 0 3 s / 47 Přednášky Titulky 95% / čisté
buriy_audiobooks_2_val 7 850 4,9 0 2 s / 31 Knihy Ruční anotace 99% / praskání
public_youtube700_val 7 311 4,5 0 2 s / 35 YouTube Ruční anotace 99% / praskání

(*) Se soubory txt je k dispozici jen vzorek dat.

Metodologie anotací

Ke kompilaci datové sady se používá open source. Dlouhé sekvence jsou rozdělené do audiobloků na základě detekce hlasové aktivity a zarovnání. Některé typy zvuku se automaticky a ověřují statisticky pomocí heuristiky.

Objemy dat a frekvence aktualizací

Celková velikost datové sady je 350 GB. Celková velikost datové sady s veřejně sdílenými popisky je 130 GB.

Samotná datová sada se pravděpodobně neaktualizuje kvůli zpětné kompatibilitě. Postupujte podle původního úložiště pro srovnávací testy a vylučte soubory.

V budoucnu můžou být přidány nové domény a jazyky.

Normalizace zvuku

Všechny soubory jsou normalizovány pro snadnější a rychlejší rozšíření modulu runtime. Zpracování je následující:

  • V případě potřeby převedené na monofonní
  • V případě potřeby se převede na vzorkovací frekvenci 16 kHz;
  • Uložené jako 16bitová celá čísla
  • Převedené na formát OPUS

Metodologie databáze na disku

Každý zvukový soubor (wav, binární) se hashuje. Hodnota hash se používá k vytvoření hierarchie složek pro optimální operaci 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)

Soubory ke stažení

Datová sada je poskytována ve dvou formách:

  • Archivy dostupné prostřednictvím služby Azure Blob Storage nebo přímých odkazů;
  • Původní soubory dostupné prostřednictvím úložiště objektů blob v Azure; Všechno je uloženo v 'https://azureopendatastorage.blob.core.windows.net/openstt/'

Struktura složek:

└── 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, ARCHIV ARCHIV ZDROJ MANIFEST
Trénink
Ukázka rozhlasových a veřejných projevů - 11,4 opus+txt - manifest
audiobook_2 162 25,8 opus+txt Internet + zarovnání manifest
radio_2 154 24,6 opus+txt Rádio manifest
public_youtube1120 237 19,0 opus+txt Videa YouTube manifest
asr_public_phone_calls_2 66 9.4 opus+txt Internet + ASR manifest
public_youtube1120_hq 31 4,9 opus+txt Videa YouTube manifest
asr_public_stories_2 9 1.4 opus+txt Internet + zarovnání manifest
tts_russian_addresses_rhvoice_4voices 80,9 12,9 opus+txt TTS manifest
public_youtube700 75.0 12,2 opus+txt Videa YouTube manifest
asr_public_phone_calls_1 22,7 3.2 opus+txt Internet + ASR manifest
asr_public_stories_1 4.1 0,7 opus+txt Veřejné příběhy manifest
public_series_1 1,9 0.3 opus+txt Veřejné série manifest
public_lecture_1 0,7 0,1 opus+txt Internet + ruční manifest
Val
asr_calls_2_val 2 0,8 wav+txt Internet manifest
buriy_audiobooks_2_val 0 0.5 wav+txt Knihy + ruční manifest
public_youtube700_val 2 0,13 wav+txt Videa YouTube + ruční manifest

Pokyny ke stažení

Přímé stahování

Pokyny k přímému stažení datové sady najdete na stránce s pokyny ke stažení GitHubu.

Další informace

Pokud získáte nápovědu nebo dotazy týkající se dat, obraťte se na autora dat na adrese aveysov@gmail.com

Tato licence umožňuje uživatelům distribuovat, remixovat, přizpůsobovat a stavět na materiálu v libovolném médiu nebo formátu pouze pro neschválené účely, a to pouze za předpokladu, že je autorovi uděleno přisuzování. Zahrnuje následující elementy:

  • BY – Kredit musí být udělen tvůrci
  • NC – Povoleny jsou pouze nekomerční použití práce.

CC-BY-NC a komerční použití po dohodě s autory datové sady.

Přístup k datům

Azure Notebooks

Pomocné funkce / závislosti

Sestavení souboru libsndfile

Efektivní způsob, jak číst soubory opus v Pythonu, které neúčtují značné režijní náklady, je použít pysoundfile (obálku CFFI Pythonu kolem libsoundfile).

Podpora Opus byla implementována v upstreamu, ale nebyla správně vydána. Proto jsme se rozhodli pro vlastní sestavení + opičí opravy.

Obvykle ho musíte spustit ve svém prostředí s přístupem 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 .

Pomocné funkce / závislosti

Nainstalujte následující knihovny:

pandas
numpy
scipy
tqdm
soundfile
librosa

Manifesty jsou soubory CSV s následujícími sloupci:

  • Cesta ke zvuku
  • Cesta k textovému souboru
  • Doba trvání

Ukázaly se jako nejjednodušší formát přístupu k datům.

Pro snadné použití jsou všechny manifesty již vyrootovány. Všechny cesty v nich jsou relativní, musíte zadat kořenovou složku.

# 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()

Přehrávání s datovou sadou

Přehrání ukázky souborů

Většina prohlížečů platforem podporuje nativní přehrávání zvuku. K zobrazení dat můžeme použít zvukové přehrávače 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)

Čtení souboru

!ls ru_open_stt_opus/manifests/*.csv

Některé příklady ukazující, jak nejlépe číst soubory wav a opus.

Scipy je nejrychlejší pro vlnovku. Pysoundfile je nejlepší celkově pro opus.

%matplotlib inline

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

Čtení vlnovek

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)

Přečíst 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)

Další kroky

Prohlédněte si zbývající datové sady v katalogu Open Datasets.