En samling av taleeksempler hentet fra forskjellige lydkilder. Datasettet inneholder korte lydklipp på russisk.
Kanskje det største offentlige russiske STT-datasettet til dags dato:
- ~16 mill. uttalelser;
- ~20 000 timer;
- 2,3 TB (ukomprimert i
.wav
-format iint16
), 356 G i.opus
; - Nå har alle filene blitt transformert til opus, bortsett fra datasett for validering;
Hovedformålet med datasettet er å lære opp tekst-til-tale-modeller.
DatasettsammensetningDatasettstørrelse blir gitt for .wav
filer.
Datasett | Uttalelser | Timer | GB | Sek/tegn | Kommentar | Merknad | Kvalitet/støy |
---|---|---|---|---|---|---|---|
radio_v4 (*) | 7 603 192 | 10 430 | 1 195 | 5 s / 68 | Radio | Juster | 95 % / klar |
public_speech (*) | 1 700 060 | 2 709 | 301 | 6 s / 79 | Offentlig tale | Juster | 95 % / klar |
audiobook_2 | 1 149 404 | 1 511 | 162 | 5 s / 56 | Bøker | Juster | 95 % / klar |
radio_2 | 651 645 | 1 439 | 154 | 8 s / 110 | Radio | Juster | 95 % / klar |
public_youtube1120 | 1 410 979 | 1 104 | 237 | 3 s / 34 | Youtube | Teksting | 95 % / ~klar |
public_youtube700 | 759 483 | 701 | 75 | 3 s / 43 | Youtube | Teksting | 95 % / ~klar |
tts_russian_addresses | 1 741 838 | 754 | 81 | 2 s / 20 | Adresser | TTS 4-stemmer | 100 % / klar |
asr_public_phone_calls_2 | 603 797 | 601 | 66 | 4 s / 37 | Telefonsamtaler | ASR | 70% / med støy |
public_youtube1120_hq | 369 245 | 291 | 31 | 3 s / 37 | YouTube HQ | Teksting | 95 % / ~klar |
asr_public_phone_calls_1 | 233 868 | 211 | 23 | 3 s / 29 | Telefonsamtaler | ASR | 70% / med støy |
radio_v4_add (*) | 92 679 | 157 | 18 | 6 s / 80 | Radio | Juster | 95 % / klar |
asr_public_stories_2 | 78 186 | 78 | 09: | 4 s / 43 | Bøker | ASR | 80 % / klar |
asr_public_stories_1 | 46 142 | 38 | 4 | 3 s / 30 | Bøker | ASR | 80 % / klar |
public_series_1 | 20 243 | 17 | 2 | 3 s / 38 | Youtube | Teksting | 95 % / ~klar |
asr_calls_2_val | 12 950 | 7,7 | 2 | 2 s / 34 | Telefonsamtaler | Manuell merknad | 99 % / klar |
public_lecture_1 | 6 803 | 6 | 1 | 3 s / 47 | Forelesninger | Teksting | 95 % / klar |
buriy_audiobooks_2_val | 7 850 | 4,9 | 1 | 2 s / 31 | Bøker | Manuell merknad | 99 % / klar |
public_youtube700_val | 7 311 | 4,5 | 1 | 2 s / 35 | Youtube | Manuell merknad | 99 % / klar |
(*) Bare et utvalg av data blir levert med txt-filer.
AnnoteringsmetodikkDatasettet blir satt sammen fra åpne kilder. Lange sekvenser blir delt opp i lydsnutter med oppdaging og justering til stemmeaktivitet. Noen lydtyper blir annotert automatisk og verifisert statistisk / med heuristikk.
Datavolumer og oppdateringsfrekvensTotal størrelse på hele datasettet er 350 GB. Total størrelse på datasettet med offentlig delte etiketter er 130 GB.
Selve datasettet blir sannsynligvis ikke oppdatert for bakoverkompatibilitet. Følg originalrepositoriet for ytelsestester og filer for utelatelse.
Nye domener og språk kan bli lagt til i fremtiden.
Normalisering av lyd
Alle filer blir normalisert for enklere og raskere tillegg og prosessering under kjøring som følger:
- Konvertert til mono, ved behov;
- Konvertert til 16 kHz samplingsfrekvens, ved behov;
- Lagret som 16-bits heltall;
- Konvertert til OPUS;
DB-metodikk på disk
Hver lydfil (wav, binær) blir hashet. Hashen brukes til å lage et mappehierarki for mer optimal filsystemdrift.
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)
Datasettet blir levert i to former:
- Arkiver tilgjengelige via Azure Blob Storage og/eller direktekoblinger;
- Originalfiler tilgjengelige via Azure Blob Storage;
Alt blir lagret i https://azureopendatastorage.blob.core.windows.net/openstt/
Mappestruktur:
└── 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
Datasett | GB, wav | GB, arkiv | Arkiv | Kilde | Manifest |
---|---|---|---|---|---|
Lær opp | |||||
Utvalg fra radio og offentlig tale | - | 11,4 | opus+txt | - | manifest |
audiobook_2 | 162 | 25,8 | opus+txt | Internett + justering | manifest |
radio_2 | 154 | 24,6 | opus+txt | Radio | manifest |
public_youtube1120 | 237 | 19,0 | opus+txt | YouTube-videoer | manifest |
asr_public_phone_calls_2 | 66 | 9.4 | opus+txt | Internett + ASR | manifest |
public_youtube1120_hq | 31 | 4,9 | opus+txt | YouTube-videoer | manifest |
asr_public_stories_2 | 09: | 1.4 | opus+txt | Internett + justering | manifest |
tts_russian_addresses_rhvoice_4voices | 80,9 | 12,9 | opus+txt | TTS | manifest |
public_youtube700 | 75,0 | 12,2 | opus+txt | YouTube-videoer | manifest |
asr_public_phone_calls_1 | 22,7 | 3.2 | opus+txt | Internett + ASR | manifest |
asr_public_stories_1 | 4.1 | 0.7 | opus+txt | Offentlige historier | manifest |
public_series_1 | 1.9 | 0.3 | opus+txt | Offentlige serier | manifest |
public_lecture_1 | 0.7 | 0.1 | opus+txt | Internett + manuelt | manifest |
Val | |||||
asr_calls_2_val | 2 | 0,8 | wav+txt | Internett | manifest |
buriy_audiobooks_2_val | 1 | 0,5 | wav+txt | Bøker + manuelt | manifest |
public_youtube700_val | 2 | 0.13 | wav+txt | YouTube-videoer + manuelt | manifest |
Direkte
Se her – https://github.com/snakers4/open_stt#download-instructions
Via montert Azure Blob Storage
Se notatboken som du finner i fanen «Datatilgang»
KontakterFor hjelp eller spørsmål om dataene, kontakt dataforfatteren(e) på aveysov@gmail.com
LisensDenne lisensen tillater gjenbrukere å distribuere, remikse, tilpasse og bygge videre på materialet i ethvert medium eller format for ikke-kommersielle formål, og bare så lenge tillegget blir gitt til den som opprettet det. Den inneholder følgende elementer:
* BY – Kreditering må gis til den som opprettet det
* NC – Bare ikke-kommersiell bruk av verket er tillatt
CC-BY-NC og kommersiell bruk tilgjengelig etter avtale med forfatterne av datasettet.
Referanser / videre lesningOpprinnelig datasett
- https://github.com/snakers4/open_stt
Engelske artikler
- https://thegradient.pub/towards-an-imagenet-moment-for-speech-to-text/
- https://thegradient.pub/a-speech-to-text-practitioners-criticisms-of-industry-and-academia/
Kinesiske artikler
- https://www.infoq.cn/article/4u58WcFCs0RdpoXev1E2
Russiske artikler
- https://habr.com/ru/post/494006/
- https://habr.com/ru/post/474462/
Access
Available in | When to use |
---|---|
Azure Notebooks | Quickly explore the dataset with Jupyter notebooks hosted on Azure or your local machine. |
Select your preferred service:
Azure Notebooks
!pip install numpy
!pip install tqdm
!pip install scipy
!pip install pandas
!pip install soundfile
!pip install librosa
!pip install azure-storage-blob
# 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()
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)
!ls ru_open_stt_opus/manifests/*.csv
%matplotlib inline
import librosa
from scipy.io import wavfile
from librosa import display as ldisplay
from matplotlib import pyplot as plt
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)
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)