Reading data in Python
h5pack includes a small Python API to read files created with h5pack pack, so you don't need to deal with the details of how each field is stored (e.g. FLAC audio, categorical values or multichannel audio of different lengths).
Basic usage
import h5pack
with h5pack.open("dataset.h5") as data:
print(len(data)) # Number of rows
print(data.fields) # e.g. ['audio', 'speaker', 'split']
row = data[0] # Dict with the value of each field
audio = row["audio"] # np.ndarray
fs = data.sample_rate("audio") # e.g. 16000
h5pack.open() works with partitions and virtual datasets. Each row is returned as a dict where:
- Audio is returned as an
np.ndarraywith shape(num_samples,)for mono audio or(num_channels, num_samples)for multichannel audio. Audio keeps the data type of its parser (e.g.int16foras_audioint16), while audio packed withas_audioflacis decoded asfloat32(seeaudio_dtypebelow). - Lists are returned as
np.ndarray. - Text and categorical values are returned as
str(or the original integer for categorical integer columns). - Single values are returned as
numpyscalars.
Reference
| Method / property | Description |
|---|---|
h5pack.open(file, fields=None, audio_dtype="float32") |
Opens a file. fields selects the fields returned for each row. audio_dtype is the data type used to decode FLAC audio. |
len(data) |
Number of rows. |
data[idx] |
Row idx as a dict. Negative indices are supported. |
for row in data |
Iterates over all rows. |
data.fields |
Fields returned for each row. |
data.read(field, idx, decode=True) |
Value of a single field. With decode=False, the raw stored value is returned (e.g. FLAC bytes or categorical codes). |
data.sample_rate(field) |
Sample rate of an audio field. |
data.categories(field) |
Categories of a field packed with as_categorical. |
data.filepath(field, idx) |
Path of the original audio file of a row. |
data.field_attrs(field) |
Attributes of a field. |
data.attrs |
Attributes of the file (e.g. producer or your own attrs). |
data.data |
Underlying h5py.Group for direct access. |
data.close() |
Closes the file (also done when leaving a with block). |
Using it with PyTorch
The file is opened lazily the first time a row is read, and reopened in each process. This means a single h5pack.open() object can be shared with all workers of a torch.utils.data.DataLoader, which is the safe way to read HDF5 files from multiple processes.
The example below trains on random 1-second crops of the audio of each row, and uses a categorical speaker field as the target class:
import h5pack
import torch
from torch.utils.data import Dataset, DataLoader
class AudioDataset(Dataset):
def __init__(self, file: str, crop_len: int = 16000):
self.data = h5pack.open(file, fields=["audio", "speaker"])
self.speakers = self.data.categories("speaker")
self.crop_len = crop_len
def __len__(self) -> int:
return len(self.data)
def __getitem__(self, idx: int) -> tuple[torch.Tensor, int]:
row = self.data[idx]
audio = torch.from_numpy(row["audio"]).float()
# int16 audio is scaled to [-1, 1)
if row["audio"].dtype.kind == "i":
audio = audio / 32768.0
# Random crop (or zero padding) to a fixed length
if audio.shape[-1] >= self.crop_len:
start = torch.randint(audio.shape[-1] - self.crop_len + 1, ())
audio = audio[..., start:start + self.crop_len]
else:
audio = torch.nn.functional.pad(
audio,
(0, self.crop_len - audio.shape[-1])
)
label = self.speakers.index(row["speaker"])
return audio, label
dataset = AudioDataset("dataset.h5")
loader = DataLoader(dataset, batch_size=32, shuffle=True, num_workers=4)
for audio, label in loader:
... # audio: (32, 16000), label: (32,)
Tip
- Create the
h5pack.open()object in__init__as shown above. It does not keep the file open until the first row is read, so it is safely copied to each worker. - If your rows have different lengths and you need the full audio, use a custom
collate_fnthat pads each batch to its longest row instead of cropping. - On macOS and Windows,
DataLoaderworkers are started usingspawn, so the code that creates theDataLoadermust be inside anif __name__ == "__main__":block when it is run as a script. - Reading from a virtual dataset is as fast as reading from its partitions, so you can train on a dataset split across many files.
Using h5py directly
Files created with h5pack are regular HDF5 files, so they can be read with any HDF5 library. All fields are stored in the data group, and their attributes describe how they were stored (see Parsers):