Condividi tramite


Riconoscimento vocale aperto in russo

Raccolta di campioni di parlato derivati da diverse origini audio. Il set di dati contiene brevi clip audio in russo.

Nota

Microsoft fornisce i set di dati aperti di Azure così come sono e non fornisce né garanzie, esplicite o implicite, né specifica alcuna condizione in relazione all'uso dei set di dati. Nella misura consentita dalla legge locale, Microsoft declina tutte le responsabilità per eventuali danni o perdite, incluse dirette, consequenziali, speciali, indirette, accidentali o irreversibili, risultanti dall'uso dei set di dati.

Questo set di dati viene fornito in conformità con le condizioni originali in base alle quali Microsoft ha ricevuto i dati di origine. Il set di dati potrebbe includere dati provenienti da Microsoft.

Questo set di dati del riconoscimento vocale aperto (STT) in russo include:

  • ~16 milioni di espressioni
  • ~20.000 ore
  • 2,3 TB (non compresso in formato .wav in int16), 356G in opus
  • Tutti i file sono stati trasformati in opus, ad eccezione dei set di dati di convalida

Lo scopo principale del set di dati consiste nell'eseguire il training dei modelli di riconoscimento vocale.

Composizione del set di dati

Le dimensioni del set di dati sono indicate per i file .wav.

DATASET ESPRESSIONI ORE GB SECS/CHARS COMMENT ANNOTAZIONE QUALITÀ/RUMORE
radio_v4 (*) 7.603.192 10.430 1.195 5s/68 Pulsante di opzione Align 95%/nitido
public_speech (*) 1.700.060 2.709 301 6s/79 Discorso pubblico Align 95%/nitido
audiobook_2 1.149.404 1.511 162 5s/56 Libri Align 95%/nitido
radio_2 651.645 1.439 154 8s/110 Pulsante di opzione Align 95%/nitido
public_youtube1120 1.410.979 1.104 237 3s/34 YouTube Sottotitoli 95%/abbastanza nitido
public_youtube700 759.483 701 75 3s/43 YouTube Sottotitoli 95%/abbastanza nitido
tts_russian_addresses 1.741.838 754 81 2s/20 Indirizzi Sintesi vocale a 4 voci 100%/nitido
asr_public_phone_calls_2 603.797 601 66 4s/37 Telefonate ASR 70%/disturbato
public_youtube1120_hq 369.245 291 31 3s/37 YouTube HQ Sottotitoli 95%/abbastanza nitido
asr_public_phone_calls_1 233.868 211 23 3s/29 Telefonate ASR 70%/disturbato
radio_v4_add (*) 92.679 157 18 6s/80 Pulsante di opzione Align 95%/nitido
asr_public_stories_2 78.186 78 9 4s/43 Libri ASR 80%/nitido
asr_public_stories_1 46.142 38 4 3s/30 Libri ASR 80%/nitido
public_series_1 20.243 17 2 3s/38 YouTube Sottotitoli 95%/abbastanza nitido
asr_calls_2_val 12.950 7,7 2 2s/34 Telefonate Annotazione manuale 99%/nitido
public_lecture_1 6.803 6 1 3s/47 Lezioni Sottotitoli 95%/nitido
buriy_audiobooks_2_val 7.850 4,9 1 2s/31 Libri Annotazione manuale 99%/nitido
public_youtube700_val 7.311 4,5 1 2s/35 YouTube Annotazione manuale 99%/nitido

(*) Con i file TXT viene fornito solo un campione dei dati.

Metodologia di annotazione

Il set di dati viene compilato con dati open source. Le sequenze lunghe vengono suddivise in blocchi di audio usando il rilevamento attività vocale e l'allineamento. Alcuni tipi di audio sono annotati automaticamente e verificati statisticamente utilizzando l'euristica.

Volumi di dati e frequenza di aggiornamento

Le dimensioni totali del set di dati sono 350 GB. La dimensione totale del set di dati con etichette condivise pubblicamente è di 130 GB.

È improbabile che il set di dati venga aggiornato per garantire la compatibilità con le versioni precedenti. Seguire il repository originale per i benchmark ed escludere i file.

È possibile che nuove lingue e nuovi domini vengano aggiunti in futuro.

Normalizzazione dell'audio

Tutti i file vengono normalizzati per ottenere incrementi di runtime più semplici e veloci. L'elaborazione è la seguente:

  • Conversione in mono, se necessario.
  • Convertito in frequenza di campionamento a 16 kHz, se necessario;
  • Archiviazione come integer a 16 bit.
  • Conversione in OPUS.

Metodologia di database su disco

Ogni file audio (WAV, binario) include hash. L'hash viene usato per creare una gerarchia di cartelle per un'operazione fs più ottimale.

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)

Download

Il set di dati viene fornito in due forme:

  • Archivi disponibili tramite l'archiviazione BLOB di Azure e/o collegamenti diretti;
  • File originali disponibili tramite l'archiviazione BLOB di Azure; Tutto viene archiviato in 'https://azureopendatastorage.blob.core.windows.net/openstt/'

Struttura di cartelle:

└── 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, ARCHIVIO ARCHIVE ORIGINE MANIFESTO
Esecuzione del training
Campione di audio radio e di discorso pubblico - 11,4 opus+txt - manifest
audiobook_2 162 25,8 opus+txt Internet e allineamento manifest
radio_2 154 24,6 opus+txt Pulsante di opzione manifest
public_youtube1120 237 19,0 opus+txt Video YouTube manifest
asr_public_phone_calls_2 66 9.4 opus+txt Internet e ASR manifest
public_youtube1120_hq 31 4.9 opus+txt Video YouTube manifest
asr_public_stories_2 9 1.4 opus+txt Internet e allineamento manifest
tts_russian_addresses_rhvoice_4voices 80,9 12,9 opus+txt Sintesi vocale manifest
public_youtube700 75.0 12,2 opus+txt Video YouTube manifest
asr_public_phone_calls_1 22,7 3.2 opus+txt Internet e ASR manifest
asr_public_stories_1 4.1 0,7 opus+txt Storie pubbliche manifest
public_series_1 1,9 0,3 opus+txt Serie pubbliche manifest
public_lecture_1 0,7 0,1 opus+txt Internet e manuale manifest
Val
asr_calls_2_val 2 0,8 wav+txt Internet manifest
buriy_audiobooks_2_val 1 0.5 wav+txt Libri e manuale manifest
public_youtube700_val 2 0,13 wav+txt Video di YouTube e manuale manifest

Istruzioni per il download

Download diretto

Per istruzioni su come scaricare direttamente il set di dati, vedere la pagina delle istruzioni di download di GitHub.

Informazioni aggiuntive

Per informazioni o domande sui dati contattare gli autori di dati al aveysov@gmail.com

Questa licenza consente agli utenti di distribuire, remixare, adattare e costruire sul materiale in qualsiasi mezzo o formato solo per scopi non commerciali e solo a condizione che sia data attribuzione al creatore. Include gli elementi seguenti:

  • BY - Il credito deve essere dato al creatore
  • NC - Sono consentiti solo usi non commerciali dell'opera

CC-BY-NC e utilizzo commerciale disponibili solo a seguito di accordo con gli autori del set di dati.

Accesso ai dati

Azure Notebooks

Funzioni helper/dipendenze

Compilazione di libsndfile

Un modo efficiente per leggere i file opus in Python che non comporta un sovraccarico significativo consiste nell'usare pysoundfile (un wrapper CFFI Python intorno a libsoundfile).

Il supporto per Opus è stato implementato a monte, ma non è stato rilasciato correttamente. Pertanto, abbiamo optato per una costruzione personalizzata + patch monkey.

In genere, è necessario eseguirlo nella shell con accesso 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 .

Funzioni helper/dipendenze

Installare le librerie seguenti:

pandas
numpy
scipy
tqdm
soundfile
librosa

I manifesti sono file CSV con le colonne seguenti:

  • Percorso audio
  • Percorso del file di testo
  • Durata

Si sono rivelati il formato più semplice per accedere ai dati.

Per facilitare l'uso, tutti i manifesti sono già radicati. Tutti i percorsi in essi contenuti sono relativi, è necessario fornire una cartella radice.

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

Riprodurre con un set di dati

Riprodurre un esempio di file

La maggior parte delle piattaforme browser supporta la riproduzione audio nativa. Quindi è possibile utilizzare i lettori audio HTML5 per visualizzare i dati.

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)

Leggere un file

!ls ru_open_stt_opus/manifests/*.csv

Alcuni esempi che illustrano come leggere meglio i file wav e opus.

Scipy è il più veloce per wav. Pysoundfile è il migliore per opus.

%matplotlib inline

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

Leggere un 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)

Leggere 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)

Passaggi successivi

Visualizzare il resto dei set di dati nel catalogo dei set di dati aperti.