Reconnaissance vocale ouverte pour le russe
Extraits de discours provenant de diverses sources audio. Le jeu de données contient de brefs clips audio en russe.
Notes
Microsoft fournit Azure Open Datasets « en l’état ». Microsoft n’offre aucune garantie, expresse ou implicite, ni de conditions relatives à votre utilisation des jeux de données. Dans la mesure autorisée par votre droit local, Microsoft décline toute responsabilité pour les dommages ou pertes, y compris directs, consécutifs, spéciaux, indirects ou punitifs, résultant de votre utilisation des jeux de données.
Ce jeu de données est fourni selon les conditions initiales par lesquelles Microsoft a reçu les données sources. Le jeu de données peut inclure des données provenant de Microsoft.
Ce jeu de données Reconnaissance vocale pour le russe comprend les éléments suivants :
- Environ 16 millions d’énoncés
- Environ 20 000 heures
- 2,3 To (non compressés au format .wav en int16), 356G en opus
- Tous les fichiers ont été convertis en opus, à l’exception des jeux de données de validation
L’objectif principal du jeu de données est d’entraîner des modèles de reconnaissance vocale.
Composition du jeu de données
La taille du jeu de données est indiquée pour les fichiers .wav.
JEU DE DONNÉES | ÉNONCÉS | HEURES | Go | SECONDES/CARACTÈRES | COMMENT | ANNOTATION | QUALITÉ/BRUIT |
---|---|---|---|---|---|---|---|
radio_v4 (*) | 7 603 192 | 10 430 | 1 195 | 5s/68 | Case d’option | Aligner | 95 % / net |
public_speech (*) | 1 700 060 | 2 709 | 301 | 6s/79 | Discours public | Aligner | 95 % / net |
audiobook_2 | 1 149 404 | 1 511 | 162 | 5s/56 | Livres | Aligner | 95 % / net |
radio_2 | 651 645 | 1 439 | 154 | 8s/110 | Case d’option | Aligner | 95 % / net |
public_youtube1120 | 1 410 979 | 1 104 | 237 | 3s/34 | YouTube | Sous-titres | 95 % / presque net |
public_youtube700 | 759 483 | 701 | 75 | 3s/43 | YouTube | Sous-titres | 95 % / presque net |
tts_russian_addresses | 1 741 838 | 754 | 81 | 2s/20 | Adresses | Synthèse vocale 4 voix | 100 %/net |
asr_public_phone_calls_2 | 603 797 | 601 | 66 | 4s/37 | Appels téléphoniques | ASR | 70 % / bruyant |
public_youtube1120_hq | 369 245 | 291 | 31 | 3s/37 | HQ YouTube | Sous-titres | 95 % / presque net |
asr_public_phone_calls_1 | 233 868 | 211 | 23 | 3s/29 | Appels téléphoniques | ASR | 70 % / bruyant |
radio_v4_add (*) | 92 679 | 157 | 18 | 6s/80 | Case d’option | Aligner | 95 % / net |
asr_public_stories_2 | 78 186 | 78 | 9 | 4s/43 | Livres | ASR | 80 % / net |
asr_public_stories_1 | 46 142 | 38 | 4 | 3s/30 | Livres | ASR | 80 % / net |
public_series_1 | 20 243 | 17 | 2 | 3s/38 | YouTube | Sous-titres | 95 % / presque net |
asr_calls_2_val | 12 950 | 7 7 | 2 | 2s/34 | Appels téléphoniques | Annotation manuelle | 99 % / net |
public_lecture_1 | 6 803 | 6 | 1 | 3s/47 | Conférences | Sous-titres | 95 % / net |
buriy_audiobooks_2_val | 7 850 | 4,9 | 1 | 2s/31 | Livres | Annotation manuelle | 99 % / net |
public_youtube700_val | 7 311 | 4,5 | 1 | 2s/35 | YouTube | Annotation manuelle | 99 % / net |
(*) Seul un exemple de données est fourni avec les fichiers .txt.
Méthodologie d’annotation
Le jeu de données est compilé à l’aide de sources ouvertes. Les longues séquences sont divisées en morceaux audio à l’aide de l’alignement et de la détection d’activité vocale. Certains types d’audio sont annotés automatiquement et vérifiés statistiquement à l’aide d’euristiques.
Volumes de données et fréquence de mise à jour
La taille totale du jeu de données est de 350 Go. La taille totale du jeu de données avec des étiquettes partagées publiquement est de 130 Go.
Il est peu probable que le jeu de données lui-même soit mis à jour pour assurer une compatibilité descendante. Suivez le référentiel d’origine pour les points de référence et les fichiers exclude.
De nouveaux domaines et de nouvelles langues pourront être ajoutés à l’avenir.
Normalisation audio
Tous les fichiers sont normalisés pour faciliter et accélérer les augmentations du runtime. Le traitement est le suivant :
- Convertis en mono, si nécessaire
- Convertis en fréquence d’échantillonnage 16 kHz, si nécessaire
- Stockés sous forme d’entiers 16 bits
- Convertis en OPUS
Méthodologie de base de données sur disque
Chaque fichier audio (wav, binaire) est haché. Le code de hachage est utilisé pour créer une arborescence des dossiers pour un fonctionnement fs plus optimal.
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)
Téléchargements
Ce jeu de données est fourni sous deux formes :
- Des archives disponibles via Stockage Blob Azure et/ou des liens directs
- Les fichiers originaux disponibles via Stockage Blob Azure. Le tout est stocké dans « https://azureopendatastorage.blob.core.windows.net/openstt/ »
Structure des dossiers :
└── 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
JEU DE DONNÉES | GO, WAV | GO, ARCHIVE | ARCHIVER | SOURCE | MANIFESTE |
---|---|---|---|---|---|
Former | |||||
Échantillon radio et discours public | - | 11,4 | opus+txt | - | manifest |
audiobook_2 | 162 | 25,8 | opus+txt | Internet + alignment | manifest |
radio_2 | 154 | 24,6 | opus+txt | Case d’option | manifest |
public_youtube1120 | 237 | 19,0 | opus+txt | Vidéos YouTube | manifest |
asr_public_phone_calls_2 | 66 | 9,4 | opus+txt | Internet + ASR | manifest |
public_youtube1120_hq | 31 | 4,9 | opus+txt | Vidéos YouTube | manifest |
asr_public_stories_2 | 9 | 1.4 | opus+txt | Internet + alignment | manifest |
tts_russian_addresses_rhvoice_4voices | 80,9 | 12.9 | opus+txt | TTS | manifest |
public_youtube700 | 75,0 | 12,2 | opus+txt | Vidéos 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 | Histoires publiques | manifest |
public_series_1 | 1,9 | 0.3 | opus+txt | Séries publiques | manifest |
public_lecture_1 | 0,7 | 0,1 | opus+txt | Internet + manuel | manifest |
Val | |||||
asr_calls_2_val | 2 | 0,8 | wav+txt | Internet | manifest |
buriy_audiobooks_2_val | 1 | 0,5 | wav+txt | Livres + manuel | manifest |
public_youtube700_val | 2 | 0,13 | wav+txt | Vidéos YouTube + manuel | manifest |
Instructions de téléchargement
Téléchargement direct
Pour savoir comment télécharger directement le jeu de données, consultez la page d’instructions de téléchargement de GitHub.
Informations supplémentaires
Pour obtenir de l’aide ou poser des questions sur les données, contactez les auteurs des données à l’adresse aveysov@gmail.com.
Cette licence permet aux utilisateurs de distribuer, de remixer, d’adapter et d’exploiter le matériel sur n’importe quel support ou dans n’importe quel format, à des fins non commerciales uniquement, et à condition que l’auteur soit mentionné. Elle inclut les éléments suivants :
- BY : Le créateur doit être cité
- NC : Seules les utilisations non commerciales de l’œuvre sont autorisées
CC-BY-NC - Utilisation commerciale disponible après accord avec les auteurs du jeu de données.
Accès aux données
Azure Notebooks
Fonctions/dépendances de l’assistance
Génération de libsndfile
Un moyen efficace de lire les fichiers opus en Python qui n’entraîne pas de surcharge importante consiste à utiliser pysoundfile (un wrapper CFFI Python autour de libsoundfile).
La prise en charge d’opus a été implémenté en amont, mais elle n’a pas été correctement publiée. C’est pourquoi nous avons opté pour une build personnalisée et des MonkeyPatch.
En règle générale, vous devez l’exécuter dans votre interpréteur de commandes avec un accès 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 .
Fonctions/dépendances de l’assistance
Installez les bibliothèques suivantes :
pandas
numpy
scipy
tqdm
soundfile
librosa
Les manifestes sont des fichiers CSV avec les colonnes suivantes :
- Chemin d’accès au fichier audio
- Chemin d’accès au fichier texte
- Duration
Ils se sont avérés être le format le plus simple pour accéder aux données.
Pour faciliter l’utilisation, tous les manifestes sont déjà réenracinés. Tous les chemins d’accès étant relatifs, vous devez fournir un dossier racine.
# 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()
Jouer avec un jeu de données
Lire un échantillon de fichiers
La plupart des navigateurs de plateformes prennent en charge la lecture audio native. Nous pouvons donc utiliser des lecteurs audio HTML5 pour consulter nos données.
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)
Lire un fichier
!ls ru_open_stt_opus/manifests/*.csv
Quelques exemples montrant comment lire au mieux les fichiers WAV et opus.
Scipy est le plus rapide pour le format WAV. Pysoundfile est dans l’ensemble le meilleur pour opus.
%matplotlib inline
import librosa
from scipy.io import wavfile
from librosa import display as ldisplay
from matplotlib import pyplot as plt
Lire 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)
Lire 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)
Étapes suivantes
Consultez les autres jeux de données du catalogue Open Datasets.