Conversión de voz en texto abierta en ruso
Recopilación de muestras de voz procedentes de varios orígenes de audio. El conjunto de datos contiene breves clips de audio en ruso.
Nota
Microsoft proporciona Azure Open Datasets "tal cual". Microsoft no ofrece ninguna garantía, expresa o implícita, ni condición con respecto al uso que usted haga de los conjuntos de datos. En la medida en la que lo permita su legislación local, Microsoft declina toda responsabilidad por posibles daños o pérdidas, incluidos los daños directos, consecuenciales, especiales, indirectos, incidentales o punitivos, que resulten de su uso de los conjuntos de datos.
Este conjunto de datos se proporciona bajo los términos originales con los que Microsoft recibió los datos de origen. El conjunto de datos puede incluir datos procedentes de Microsoft.
Este conjunto de datos de conversión de voz en texto (STT) en ruso incluye:
- ~16 millones de expresiones
- ~20 000 horas
- 2,3 TB (descomprimidos en formato .wav en int16), 356G en Opus
- Todos los archivos se han migrado a Opus, excepto los conjuntos de datos de validación
El objetivo principal del conjunto de datos es entrenar modelos de conversión de voz en texto.
Composición del conjunto de datos
El tamaño del conjunto de datos se proporciona para los archivos en formato .wav.
CONJUNTO DE DATOS | EXPRESIONES | HORAS | GB | SECS/CHARS | COMMENT | ANOTACIONES | CALIDAD/RUIDO |
---|---|---|---|---|---|---|---|
radio_v4 (*) | 7 603 192 | 10 430 | 1195 | 5 s/68 | Radio | Alinear | 95 %/nítida |
public_speech (*) | 1 700 060 | 2709 | 301 | 6 s/79 | Voz pública | Alinear | 95 %/nítida |
audiobook_2 | 1 149 404 | 1511 | 162 | 5 s/56 | Libros | Alinear | 95 %/nítida |
radio_2 | 651 645 | 1439 | 154 | 8 s/110 | Radio | Alinear | 95 %/nítida |
public_youtube1120 | 1 410 979 | 1104 | 237 | 3 s/34 | YouTube | Subtítulos | 95 %/~nítida |
public_youtube700 | 759 483 | 701 | 75 | 3 s/43 | YouTube | Subtítulos | 95 %/~nítida |
tts_russian_addresses | 1 741 838 | 754 | 81 | 2 s/20 | Direcciones | 4 voces de TTS | 100 %/nítida |
asr_public_phone_calls_2 | 603 797 | 601 | 66 | 4 s/37 | Llamadas de teléfono | ASR | 70 %/con ruido |
public_youtube1120_hq | 369 245 | 291 | 31 | 3 s/37 | YouTube HQ | Subtítulos | 95 %/~nítida |
asr_public_phone_calls_1 | 233 868 | 211 | 23 | 3 s/29 | Llamadas de teléfono | ASR | 70 %/con ruido |
radio_v4_add (*) | 92 679 | 157 | 18 | 6 s/80 | Radio | Alinear | 95 %/nítida |
asr_public_stories_2 | 78 186 | 78 | 9 | 4 s/43 | Libros | ASR | 80 %/nítida |
asr_public_stories_1 | 46 142 | 38 | 4 | 3 s/30 | Libros | ASR | 80 %/nítida |
public_series_1 | 20 243 | 17 | 2 | 3 s/38 | YouTube | Subtítulos | 95 %/~nítida |
asr_calls_2_val | 12 950 | 7,7 | 2 | 2 s/34 | Llamadas de teléfono | Anotación manual | 99 %/nítida |
public_lecture_1 | 6803 | 6 | 1 | 3 s/47 | Lecturas | Subtítulos | 95 %/nítida |
buriy_audiobooks_2_val | 7850 | 4,9 | 1 | 2 s/31 | Libros | Anotación manual | 99 %/nítida |
public_youtube700_val | 7311 | 4,5 | 1 | 2 s/35 | YouTube | Anotación manual | 99 %/nítida |
(*) Solo se proporciona una muestra de datos con los archivos .txt.
Metodología para las anotaciones
El conjunto de datos se ha compilado a partir de orígenes públicos. Las secuencias largas están divididas en fragmentos de audio usando detección y alineación de actividad de voz. Algunos tipos de audio se han anotado automáticamente y se han comprobado estadísticamente o mediante heurística.
Volúmenes de datos y frecuencia de actualización
El tamaño total del conjunto de datos es de 350 GB. El tamaño total del conjunto de datos con etiquetas compartidas públicamente es de 130 GB.
Probablemente, el conjunto de datos en sí no se actualizará para que sea compatible con versiones anteriores. Siga el repositorio original para obtener bancos de pruebas y excluir archivos.
Pueden agregarse nuevos dominios e idiomas en el futuro.
Normalización de audio
Todos los archivos se han normalizado para facilitar y agilizar el aumento en tiempo de ejecución. El procesamiento es el siguiente:
- Convertido a mono, si es necesario;
- Convertido a una frecuencia de muestreo de 16 kHz, si es necesario;
- Almacenado como enteros de 16 bits;
- Convertido a OPUS;
Metodología de base datos en disco
Cada archivo de audio (.wav, binario) tiene un algoritmo hash. El algoritmo hash se usa para crear una jerarquía de carpetas con el fin de optimizar el funcionamiento de 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)
Descargas
El conjunto de datos se proporciona con dos formatos:
- Archivos disponibles a través de Azure Blob Storage o vínculos directos;
- Archivos originales disponibles a través de Azure Blob Storage; todo se almacena en https://azureopendatastorage.blob.core.windows.net/openstt/
Estructura de carpetas:
└── 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
CONJUNTO DE DATOS | GB, WAV | GB, ARCHIVO | ARCHIVO | ORIGEN | MANIFIESTO |
---|---|---|---|---|---|
Train | |||||
Muestra de radio y voz pública | - | 11.4 | .opus y .txt | - | manifest |
audiobook_2 | 162 | 25,8 | .opus y .txt | Internet y alineación | manifest |
radio_2 | 154 | 24,6 | .opus y .txt | Radio | manifest |
public_youtube1120 | 237 | 19,0 | .opus y .txt | Vídeos de YouTube | manifest |
asr_public_phone_calls_2 | 66 | 9,4 | .opus y .txt | Internet y ASR | manifest |
public_youtube1120_hq | 31 | 4,9 | .opus y .txt | Vídeos de YouTube | manifest |
asr_public_stories_2 | 9 | 1.4 | .opus y .txt | Internet y alineación | manifest |
tts_russian_addresses_rhvoice_4voices | 80,9 | 12.9 | .opus y .txt | TTS | manifest |
public_youtube700 | 75,0 | 12,2 | .opus y .txt | Vídeos de YouTube | manifest |
asr_public_phone_calls_1 | 22,7 | 3.2 | .opus y .txt | Internet y ASR | manifest |
asr_public_stories_1 | 4,1 | 0,7 | .opus y .txt | Historias públicas | manifest |
public_series_1 | 1,9 | 0,3 | .opus y .txt | Series públicas | manifest |
public_lecture_1 | 0,7 | 0,1 | .opus y .txt | Internet y manual | manifest |
Val | |||||
asr_calls_2_val | 2 | 0,8 | .wav y .txt | Internet | manifest |
buriy_audiobooks_2_val | 1 | 0,5 | .wav y .txt | Libros y manual | manifest |
public_youtube700_val | 2 | 0,13 | .wav y .txt | Vídeos de YouTube y manual | manifest |
Instrucciones de descarga
Descarga directa
Para instrucciones sobre cómo descargar el conjunto de datos directamente, consulte la página de instrucciones de descarga de GitHub.
Información adicional
Para ayuda o preguntas sobre los datos, póngase en contacto con los autores de datos en aveysov@gmail.com
Esta licencia permite a los usuarios distribuir, remezclar, adaptar y desarrollar el material en cualquier soporte o formato para fines no comerciales exclusivamente, siempre y cuando la atribución se asigne al creador. Incluye los siguientes elementos:
- BY: el mérito se debe atribuir al creador.
- NC: solo se permite utilizar el trabajo para fines no comerciales.
CC-BY-NC y uso comercial disponibles después de firmar un acuerdo con los creadores del conjunto de datos.
Acceso a datos
Azure Notebooks
Funciones o dependencias del asistente
Compilación de libsndfile
Una manera eficaz de leer archivos de Opus en Python que no suponga una sobrecarga considerable es usar pysoundfile (un contenedor CFFI de Python para libsoundfile).
Se ha implementado la compatibilidad con Opus ascendente, pero no se ha publicado correctamente. Por lo tanto, optamos por la creación personalizada y la aplicación de revisiones en tiempo de ejecución.
Normalmente, debe ejecutar esto en el shell con acceso 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 .
Funciones o dependencias del asistente
Instale las siguientes bibliotecas:
pandas
numpy
scipy
tqdm
soundfile
librosa
Los manifiestos son archivos csv con las columnas siguientes:
- Ruta de acceso al audio
- Ruta de acceso al archivo de texto
- Duration
Demostraron ser el formato más sencillo de acceso a los datos.
Para facilitar el uso, todos los manifiestos ya se tienen otra ruta raíz. Todas las rutas de acceso son relativas; debe proporcionar una carpeta raíz.
# 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()
Reproducción con un conjunto de datos
Reproducción de una muestra de archivos
La mayoría de los exploradores de plataforma admiten la reproducción de audio nativo. Por lo tanto, podemos usar reproductores de audio HTML5 para ver los datos.
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)
Lectura de un archivo
!ls ru_open_stt_opus/manifests/*.csv
Muestras de cómo leer mejor los archivos wav y opus.
Scipy es lo más rápido para wav. Pysoundfile es mejor en general para opus.
%matplotlib inline
import librosa
from scipy.io import wavfile
from librosa import display as ldisplay
from matplotlib import pyplot as plt
Lectura de 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)
Lectura de un 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)
Pasos siguientes
Consulte el resto de los conjuntos de datos en el catálogo de Open Datasets.