Russo Discurso Aberto para Texto

Uma coleção de amostras de voz derivadas de várias origens de áudio. Este conjunto de dados contém clipes de áudio em russo.

Nota

A Microsoft fornece os Conjuntos de Dados Abertos do Azure "no estado em que se encontram". A Microsoft não oferece garantias, expressas ou implícitas, garantias ou condições em relação ao seu uso dos conjuntos de dados. Na medida permitida pela legislação local, a Microsoft se isenta de qualquer responsabilidade por quaisquer danos ou perdas, incluindo diretos, consequenciais, especiais, indiretos, incidentais ou punitivos, resultantes do uso dos conjuntos de dados por parte do cliente.

Este conjunto de dados é disponibilizado de acordo com os termos originais em que a Microsoft recebeu os dados de origem. O conjunto de dados pode incluir dados obtidos junto da Microsoft.

Este conjunto de dados russo de fala para texto (STT) inclui:

  • ~16 milhões de declarações
  • ~20.000 horas
  • 2,3 TB (não comprimido no formato .wav em int16), 356G em opus
  • Todos os arquivos foram transformados em opus, exceto conjuntos de dados de validação

O principal objetivo do conjunto de dados é preparar modelos de voz em texto.

Composição do conjunto de dados

O tamanho do conjunto de dados é fornecido para .wav arquivos.

DATASET ENUNCIADOS HORÁRIO GB SECS/CHARS COMENTAR ANOTAÇÃO QUALIDADE/RUÍDO
radio_v4 (*) 7 603 192 10 430 1195 5s / 68 Botão de opção Align 95% / nítido
public_speech (*) 1 700 060 2709 301 6s / 79 Voz pública Align 95% / nítido
audiobook_2 1 149 404 1511 162 5s / 56 Livros Align 95% / nítido
radio_2 651 645 1439 154 8s / 110 Botão de opção Align 95% / nítido
public_youtube1120 1 410 979 1104 237 3s / 34 YouTube Legendas 95% / nítido
public_youtube700 759 483 701 75 3s / 43 YouTube Legendas 95% / nítido
tts_russian_addresses 1 741 838 754 81 2s / 20 Endereços 4 vozes TTS 100% / nítido
asr_public_phone_calls_2 603,797 601 66 4s / 37 Chamadas telefónicas ASR 70% / ruído
public_youtube1120_hq 369 245 291 31 3s / 37 YouTube HQ Legendas 95% / nítido
asr_public_phone_calls_1 233 868 211 23 3s / 29 Chamadas telefónicas ASR 70% / ruído
radio_v4_add (*) 92 679 157 18 6s / 80 Botão de opção Align 95% / nítido
asr_public_stories_2 78 186 78 9 4s / 43 Livros ASR 80% / nítido
asr_public_stories_1 46 142 38 4 3s / 30 Livros ASR 80% / nítido
public_series_1 20 243 17 2 3s / 38 YouTube Legendas 95% / nítido
asr_calls_2_val 12 950 7,7 2 2s / 34 Chamadas telefónicas Anotação manual 99% / nítido
public_lecture_1 6803 6 1 3s / 47 Palestras Legendas 95% / nítido
buriy_audiobooks_2_val 7850 4,9 1 2s / 31 Livros Anotação manual 99% / nítido
public_youtube700_val 7311 4,5 1 2s / 35 YouTube Anotação manual 99% / nítido

(*) Com ficheiros txt, só é fornecida uma amostra dos dados.

Metodologia da anotação

O conjunto de dados é compilado com open source. As sequências compridas são divididas em fragmentos de áudio com deteção de atividade e alinhamento de voz. Alguns tipos de áudio são anotados automaticamente e verificados estatisticamente usando heurística.

Volumes de dados e frequência de atualização

O tamanho total do conjunto de dados é de 350 GB. O tamanho total do conjunto de dados com rótulos compartilhados publicamente é de 130 GB.

Não é provável que o conjunto de dados em si seja atualizado para compatibilidade com versões anteriores. Siga o repositório original para benchmarks e exclua arquivos.

Poderão ser adicionados domínios e idiomas novos no futuro.

Normalização de áudio

Todos os arquivos são normalizados para aumentos de tempo de execução mais fáceis e rápidos. O processamento é o seguinte:

  • Conversão em mono, se necessário;
  • Conversão para uma taxa de amostragem de 16 kHz, se necessário;
  • Armazenamento como números inteiros de 16 bits;
  • Conversão em OPUS;

Metodologia de DB em disco

Cada ficheiro de áudio (wav, binário) é transformado em hash. O hash é utilizado para criar uma hierarquia de pastas para um melhor funcionamento do 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)

Transferências

O conjunto de dados é fornecido de duas formas:

  • Arquivos disponíveis por meio do armazenamento de blob do Azure e/ou links diretos;
  • Arquivos originais disponíveis por meio do armazenamento de blob do Azure; Tudo é armazenado em ''https://azureopendatastorage.blob.core.windows.net/openstt/

Estrutura da pasta:

└── 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, ARQUIVO ARQUIVO ORIGEM MANIFESTO
Comboio
Amostra de voz de rádio e pública - 11,4 opus+txt - manifesto
audiobook_2 162 25,8 opus+txt Internet + alinhamento manifesto
radio_2 154 24,6 opus+txt Botão de opção manifesto
public_youtube1120 237 19,0 opus+txt Vídeos do YouTube manifesto
asr_public_phone_calls_2 66 9.4 opus+txt Internet + ASR manifesto
public_youtube1120_hq 31 4,9 opus+txt Vídeos do YouTube manifesto
asr_public_stories_2 9 1.4 opus+txt Internet + alinhamento manifesto
tts_russian_addresses_rhvoice_4voices 80,9 12,9 opus+txt TTS manifesto
public_youtube700 75.0 12,2 opus+txt Vídeos do YouTube manifesto
asr_public_phone_calls_1 22.7 3.2 opus+txt Internet + ASR manifesto
asr_public_stories_1 4.1 0.7 opus+txt Histórias públicas manifesto
public_series_1 1.9 0.3 opus+txt Série pública manifesto
public_lecture_1 0.7 0.1 opus+txt Internet + manual manifesto
Val
asr_calls_2_val 2 0.8 wav+txt Internet manifesto
buriy_audiobooks_2_val 1 0.5 wav+txt Livros + manual manifesto
public_youtube700_val 2 0,13 wav+txt Vídeos do YouTube + manual manifesto

Instruções de transferência

Download direto

Para obter instruções sobre como baixar o conjunto de dados diretamente, consulte a página de instruções de download do GitHub.

Informações adicionais

Para obter ajuda ou perguntas sobre os dados, entre em contato com o(s) autor(es) dos dados em aveysov@gmail.com

Esta licença permite aos usuários distribuir, remixar, adaptar e construir sobre o material em qualquer meio ou formato apenas para fins não comerciais, e apenas enquanto a atribuição for dada ao criador. Inclui os seguintes elementos:

  • BY – O crédito deve ser dado ao criador
  • NC – Apenas são permitidas utilizações não comerciais da obra

Utilização comercial e ao abrigo da licença CC-BY-NC disponível após acordo estabelecido com os autores do conjunto de dados.

Acesso a dados

Azure Notebooks

Funções auxiliares / dependências

Construindo libsndfile

Uma maneira eficiente de ler arquivos opus em Python que não incorre em sobrecarga significativa é usar pysoundfile (um wrapper Python CFFI em torno de libsoundfile).

O suporte ao Opus foi implementado a montante, mas não foi lançado corretamente. Portanto, optamos por build personalizado + monkey patching.

Normalmente, você precisa executar isso em seu shell com acesso 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 .

Funções auxiliares / dependências

Instale as bibliotecas a seguir:

pandas
numpy
scipy
tqdm
soundfile
librosa

Manifestos são arquivos csv com as seguintes colunas:

  • Caminho para o áudio
  • Caminho para o arquivo de texto
  • Duração

Eles provaram ser o formato mais simples de acesso aos dados.

Para facilitar o uso, todos os manifestos já estão reenraizados. Todos os caminhos neles são relativos, você precisa fornecer uma pasta raiz.

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

Jogar com um conjunto de dados

Reproduzir uma amostra de ficheiros

A maioria dos navegadores de plataformas suporta reprodução de áudio nativa. Assim, podemos usar players de áudio HTML5 para visualizar nossos dados.

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)

Ler um ficheiro

!ls ru_open_stt_opus/manifests/*.csv

Alguns exemplos mostrando como ler melhor arquivos wav e opus.

Scipy é o mais rápido para wav. Pysoundfile é o melhor geral para opus.

%matplotlib inline

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

Ler um 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)

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

Próximos passos

Exiba o restante dos conjuntos de dados no catálogo Open Datasets.