Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
project_env/
.DS_Store
dataset/*

# from hydra
outputs/
Expand Down
3 changes: 2 additions & 1 deletion README.rst
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,7 +61,8 @@ choose the features that you like. This flexibility is one of the best
* Code formatting.
* Unit tests and continuous integration.
* Packaging and distribution.
* Remove development.
* Remote development.
* Creating and sharing datasets with Hugging Face.

The accompanying
`slides <https://docs.google.com/presentation/d/1D1_JywMl2rjaeuVzpykPBOJsDIuwQKGOJB4EFZjej2s/edit#slide=id.g2eaa4b61f15_0_1346>`__
Expand Down
29 changes: 29 additions & 0 deletions examples/configs/hf_dataset.yaml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
# python examples/create_huggingface_dataset.py hf_token=YOUR_TOKEN
hydra:
job:
chdir: True # change to output folder
job_logging:
formatters:
simple:
format: '[%(levelname)s] - %(message)s'

repo_id: bezzam/dummy-dataset
seed: 0
test_size: 0.15
hf_token:

data_dir:
audio:
dir: dataset/data_audio
type: wav
image:
dir: dataset/data_images
type: png
text:
dir: dataset/data_text
type: txt
label:
file: dataset/data_labels.csv
label: True

stratify_by_column: label
232 changes: 232 additions & 0 deletions examples/create_huggingface_dataset.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
"""
We will create a dataset with images, audios, and text data
so that you can see how various data types can be pushed to
Hugging Face!

The default configuration is in `examples/configs/hf_dataset.yaml`:

```bash
# install
pip install datasets huggingface_hub soundfile

# make a WRITE token on HuggingFace: https://huggingface.co/settings/tokens

# run
python examples/create_huggingface_dataset.py \
hf_token=... \
```
"""

import hydra
from hydra.utils import to_absolute_path
import os
import time
import glob
import numpy as np
import soundfile as sf
from PIL import Image as PILImage
from datasets import Dataset, Image, Audio, ClassLabel
from omegaconf import open_dict
from huggingface_hub import upload_file
import re
import pandas as pd


# -- helper functions
def convert(text):
return int(text) if text.isdigit() else text.lower()


def alphanum_key(key):
return [convert(c) for c in re.split("([0-9]+)", key)]


def natural_sort(arr):
return sorted(arr, key=alphanum_key)


@hydra.main(version_base=None, config_path="configs", config_name="hf_dataset")
def main(config):

start_time = time.time()

# extract and check parameters
repo_id = config.repo_id
hf_token = config.hf_token
test_size = config.test_size

assert repo_id is not None, "Please provide a Hugging Face repo_id."
assert hf_token is not None, "Please provide a Hugging Face token."

# to absolute path, as needed by Hugging Face upload
for data in config.data_dir:
if "dir" in config.data_dir[data]:
config.data_dir[data]["dir"] = to_absolute_path(config.data_dir[data]["dir"])
elif "file" in config.data_dir[data]:
config.data_dir[data]["file"] = to_absolute_path(config.data_dir[data]["file"])

# Step 1: Check data (create dummy data if not present)
n_files = 100 # number of dummy files to create
for data in config.data_dir:

# for directory of data
if "dir" in config.data_dir[data]:
input_dir = config.data_dir[data]["dir"]
data_type = config.data_dir[data]["type"]

if not os.path.exists(input_dir):
# create dummy data
print(f"-- Creating {n_files} dummy {data_type} files in {input_dir}")
os.makedirs(input_dir, exist_ok=True)
for i in range(n_files):
if data_type == "png":
dim = np.random.randint(100, 200)
img = np.random.randint(0, 255, (dim, dim, 3), dtype=np.uint8)
img_path = os.path.join(input_dir, f"{i}.png")
PILImage.fromarray(img).save(img_path)
elif data_type == "wav":
duration = np.random.randint(1, 4)
sample_rate = 16000
audio = np.random.randn(duration * sample_rate)
audio_path = os.path.join(input_dir, f"{i}.wav")
sf.write(audio_path, audio, samplerate=sample_rate)
elif data_type == "txt":
text = f"Hello, this is file {i}"
text_path = os.path.join(input_dir, f"{i}.txt")
with open(text_path, "w") as f:
f.write(text)

# check number of files
files = glob.glob(os.path.join(input_dir, "*." + data_type))
n_files = len(files)
print(f"Found {n_files} {data_type} files in {input_dir}")

# for CSV file where each line is a data point
elif "file" in config.data_dir[data]:
input_file = config.data_dir[data]["file"]

if not os.path.exists(input_file):
# create dummy labels
labels = ["good", "ok", "bad"]
file_labels = np.random.choice(labels, n_files)
with open(input_file, "w") as f:
for i in range(n_files):
f.write(f"{i},{file_labels[i]}\n")
print(f"-- Created dummy labels file at {input_file}")

# check number of unique labels (open with Pandas)
df = pd.read_csv(input_file, header=None)
n_files = len(df)
labels = df[1].unique()
n_labels = len(df[1].unique())
print(f"Found {n_files} lines with {n_labels} unique labels ({labels}) in {input_file}")

else:
raise ValueError("Please provide either `dir` or `file` in data_dir")

# -- only keep common files across all datasets
bn = [os.path.basename(f).split(".")[0] for f in files]
for data in config.data_dir:
if "dir" in config.data_dir[data]:
input_dir = config.data_dir[data]["dir"]
data_type = config.data_dir[data]["type"]
files = glob.glob(os.path.join(input_dir, "*." + data_type))
bn_data = [os.path.basename(f).split(".")[0] for f in files]
common_files = list(set(bn).intersection(bn_data))
common_files = natural_sort(common_files)
print(f"Number of common files: {len(common_files)}")

# -- add common files into dictionary
for data in config.data_dir:
if "dir" in config.data_dir[data]:
with open_dict(config):
config.data_dir[data]["data"] = common_files
if "file" in config.data_dir[data]:
# take row according to common_files
df = pd.read_csv(config.data_dir[data]["file"], header=None)
# -- make first column string
df[0] = df[0].astype(str)
df = df[df[0].isin(common_files)]
with open_dict(config):
config.data_dir[data]["data"] = df[1].tolist()

# Step 2: Create train and test data
dataset_dict = {}

# -- create dictionary of content
for data in config.data_dir:
if "dir" in config.data_dir[data]:
files = config.data_dir[data]["data"]
data_type = config.data_dir[data]["type"]
data_files = [
os.path.join(config.data_dir[data]["dir"], f"{f}.{data_type}") for f in files
]

if data_type in ["txt"]:
# open file content for text files
data_files = [open(f).read() for f in data_files]
dataset_dict[data] = data_files
elif "file" in config.data_dir[data]:
dataset_dict[data] = config.data_dir[data]["data"]

# -- create dataset
dataset = Dataset.from_dict(dataset_dict)
for data in config.data_dir:
if "dir" in config.data_dir[data]:
if config.data_dir[data]["type"] in ["png", "jpg", "jpeg", "tiff"]:
dataset = dataset.cast_column(data, Image())
elif config.data_dir[data]["type"] in ["wav", "mp3", "flac", "ogg"]:
dataset = dataset.cast_column(data, Audio())
elif "file" in config.data_dir[data]:
if config.data_dir[data]["label"]:
labels = list(set(config.data_dir[data]["data"]))
dataset = dataset.cast_column(data, ClassLabel(names=labels))

# -- split into train and test
dataset = dataset.train_test_split(
test_size=test_size,
seed=config.seed,
shuffle=True,
stratify_by_column=config.stratify_by_column, # shuffle must be True
)
print(dataset)

"""
DatasetDict({
train: Dataset({
features: ['audio', 'images', 'text', 'labels'],
num_rows: 85
})
test: Dataset({
features: ['audio', 'images', 'text', 'labels'],
num_rows: 15
})
})
"""

# Step 3: Push to Hugging Face
dataset.push_to_hub(repo_id, token=hf_token)

# -- push individual files
for data in config.data_dir:
if "dir" in config.data_dir[data]:
# push first file
local_fp = os.path.join(
config.data_dir[data]["dir"],
config.data_dir[data]["data"][0] + "." + config.data_dir[data]["type"],
)
remote_fn = "example." + config.data_dir[data]["type"]
upload_file(
path_or_fileobj=local_fp,
path_in_repo=remote_fn,
repo_id=repo_id,
repo_type="dataset",
token=hf_token,
)

# total time in minutes
print(f"Total time: {(time.time() - start_time) / 60} minutes")


if __name__ == "__main__":
main()
50 changes: 50 additions & 0 deletions examples/use_huggingface_dataset.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
"""
In this script, we use the Hugging Face dataset made
from the script examples/create_huggingface_dataset.py

The dataset is available at:
https://huggingface.co/datasets/bezzam/dummy-dataset

```bash
# install
pip install datasets librosa soundfile

# run
python examples/use_huggingface_dataset.py
```

During the first run, the dataset will be downloaded and cached.
Subsequent runs will use the cached dataset.

"""

from datasets import load_dataset
import numpy as np


# load train and test splits
ds_train = load_dataset("bezzam/dummy-dataset", split="train")
ds_test = load_dataset("bezzam/dummy-dataset", split="test")
print(f"Number of training samples: {len(ds_train)}")
print(f"Number of test samples: {len(ds_test)}")

# load first example
print("\n---- First example:")
example = ds_train[0]

# -- audio duration
duration = len(example["audio"]["array"]) / example["audio"]["sampling_rate"]
print(f"Duration of audio: {duration:.2f} seconds")

# -- image size
image = np.array(example["image"])
print(f"Size of image: {image.shape}")

# -- text
text = example["text"]
print(f"Text: {text}")

# -- label
label = example["label"]
label_str = ds_train.features["label"].int2str(label)
print(f"Label: {label_str}")
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
project_env/
.DS_Store
dataset/*

# from hydra
outputs/
Expand Down
3 changes: 2 additions & 1 deletion README.rst
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,7 +61,8 @@ choose the features that you like. This flexibility is one of the best
* Code formatting.
* Unit tests and continuous integration.
* Packaging and distribution.
* Remove development.
* Remote development.
* Creating and sharing datasets with Hugging Face.

The accompanying
`slides <https://docs.google.com/presentation/d/1D1_JywMl2rjaeuVzpykPBOJsDIuwQKGOJB4EFZjej2s/edit#slide=id.g2eaa4b61f15_0_1346>`__
Expand Down
29 changes: 29 additions & 0 deletions examples/configs/hf_dataset.yaml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
# python examples/create_huggingface_dataset.py hf_token=YOUR_TOKEN
hydra:
job:
chdir: True # change to output folder
job_logging:
formatters:
simple:
format: '[%(levelname)s] - %(message)s'

repo_id: bezzam/dummy-dataset
seed: 0
test_size: 0.15
hf_token:

data_dir:
audio:
dir: dataset/data_audio
type: wav
image:
dir: dataset/data_images
type: png
text:
dir: dataset/data_text
type: txt
label:
file: dataset/data_labels.csv
label: True

stratify_by_column: label
232 changes: 232 additions & 0 deletions examples/create_huggingface_dataset.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
"""
We will create a dataset with images, audios, and text data
so that you can see how various data types can be pushed to
Hugging Face!

The default configuration is in `examples/configs/hf_dataset.yaml`:

```bash
# install
pip install datasets huggingface_hub soundfile

# make a WRITE token on HuggingFace: https://huggingface.co/settings/tokens

# run
python examples/create_huggingface_dataset.py \
hf_token=... \
```
"""

import hydra
from hydra.utils import to_absolute_path
import os
import time
import glob
import numpy as np
import soundfile as sf
from PIL import Image as PILImage
from datasets import Dataset, Image, Audio, ClassLabel
from omegaconf import open_dict
from huggingface_hub import upload_file
import re
import pandas as pd


# -- helper functions
def convert(text):
return int(text) if text.isdigit() else text.lower()


def alphanum_key(key):
return [convert(c) for c in re.split("([0-9]+)", key)]


def natural_sort(arr):
return sorted(arr, key=alphanum_key)


@hydra.main(version_base=None, config_path="configs", config_name="hf_dataset")
def main(config):

start_time = time.time()

# extract and check parameters
repo_id = config.repo_id
hf_token = config.hf_token
test_size = config.test_size

assert repo_id is not None, "Please provide a Hugging Face repo_id."
assert hf_token is not None, "Please provide a Hugging Face token."

# to absolute path, as needed by Hugging Face upload
for data in config.data_dir:
if "dir" in config.data_dir[data]:
config.data_dir[data]["dir"] = to_absolute_path(config.data_dir[data]["dir"])
elif "file" in config.data_dir[data]:
config.data_dir[data]["file"] = to_absolute_path(config.data_dir[data]["file"])

# Step 1: Check data (create dummy data if not present)
n_files = 100 # number of dummy files to create
for data in config.data_dir:

# for directory of data
if "dir" in config.data_dir[data]:
input_dir = config.data_dir[data]["dir"]
data_type = config.data_dir[data]["type"]

if not os.path.exists(input_dir):
# create dummy data
print(f"-- Creating {n_files} dummy {data_type} files in {input_dir}")
os.makedirs(input_dir, exist_ok=True)
for i in range(n_files):
if data_type == "png":
dim = np.random.randint(100, 200)
img = np.random.randint(0, 255, (dim, dim, 3), dtype=np.uint8)
img_path = os.path.join(input_dir, f"{i}.png")
PILImage.fromarray(img).save(img_path)
elif data_type == "wav":
duration = np.random.randint(1, 4)
sample_rate = 16000
audio = np.random.randn(duration * sample_rate)
audio_path = os.path.join(input_dir, f"{i}.wav")
sf.write(audio_path, audio, samplerate=sample_rate)
elif data_type == "txt":
text = f"Hello, this is file {i}"
text_path = os.path.join(input_dir, f"{i}.txt")
with open(text_path, "w") as f:
f.write(text)

# check number of files
files = glob.glob(os.path.join(input_dir, "*." + data_type))
n_files = len(files)
print(f"Found {n_files} {data_type} files in {input_dir}")

# for CSV file where each line is a data point
elif "file" in config.data_dir[data]:
input_file = config.data_dir[data]["file"]

if not os.path.exists(input_file):
# create dummy labels
labels = ["good", "ok", "bad"]
file_labels = np.random.choice(labels, n_files)
with open(input_file, "w") as f:
for i in range(n_files):
f.write(f"{i},{file_labels[i]}\n")
print(f"-- Created dummy labels file at {input_file}")

# check number of unique labels (open with Pandas)
df = pd.read_csv(input_file, header=None)
n_files = len(df)
labels = df[1].unique()
n_labels = len(df[1].unique())
print(f"Found {n_files} lines with {n_labels} unique labels ({labels}) in {input_file}")

else:
raise ValueError("Please provide either `dir` or `file` in data_dir")

# -- only keep common files across all datasets
bn = [os.path.basename(f).split(".")[0] for f in files]
for data in config.data_dir:
if "dir" in config.data_dir[data]:
input_dir = config.data_dir[data]["dir"]
data_type = config.data_dir[data]["type"]
files = glob.glob(os.path.join(input_dir, "*." + data_type))
bn_data = [os.path.basename(f).split(".")[0] for f in files]
common_files = list(set(bn).intersection(bn_data))
common_files = natural_sort(common_files)
print(f"Number of common files: {len(common_files)}")

# -- add common files into dictionary
for data in config.data_dir:
if "dir" in config.data_dir[data]:
with open_dict(config):
config.data_dir[data]["data"] = common_files
if "file" in config.data_dir[data]:
# take row according to common_files
df = pd.read_csv(config.data_dir[data]["file"], header=None)
# -- make first column string
df[0] = df[0].astype(str)
df = df[df[0].isin(common_files)]
with open_dict(config):
config.data_dir[data]["data"] = df[1].tolist()

# Step 2: Create train and test data
dataset_dict = {}

# -- create dictionary of content
for data in config.data_dir:
if "dir" in config.data_dir[data]:
files = config.data_dir[data]["data"]
data_type = config.data_dir[data]["type"]
data_files = [
os.path.join(config.data_dir[data]["dir"], f"{f}.{data_type}") for f in files
]

if data_type in ["txt"]:
# open file content for text files
data_files = [open(f).read() for f in data_files]
dataset_dict[data] = data_files
elif "file" in config.data_dir[data]:
dataset_dict[data] = config.data_dir[data]["data"]

# -- create dataset
dataset = Dataset.from_dict(dataset_dict)
for data in config.data_dir:
if "dir" in config.data_dir[data]:
if config.data_dir[data]["type"] in ["png", "jpg", "jpeg", "tiff"]:
dataset = dataset.cast_column(data, Image())
elif config.data_dir[data]["type"] in ["wav", "mp3", "flac", "ogg"]:
dataset = dataset.cast_column(data, Audio())
elif "file" in config.data_dir[data]:
if config.data_dir[data]["label"]:
labels = list(set(config.data_dir[data]["data"]))
dataset = dataset.cast_column(data, ClassLabel(names=labels))

# -- split into train and test
dataset = dataset.train_test_split(
test_size=test_size,
seed=config.seed,
shuffle=True,
stratify_by_column=config.stratify_by_column, # shuffle must be True
)
print(dataset)

"""
DatasetDict({
train: Dataset({
features: ['audio', 'images', 'text', 'labels'],
num_rows: 85
})
test: Dataset({
features: ['audio', 'images', 'text', 'labels'],
num_rows: 15
})
})
"""

# Step 3: Push to Hugging Face
dataset.push_to_hub(repo_id, token=hf_token)

# -- push individual files
for data in config.data_dir:
if "dir" in config.data_dir[data]:
# push first file
local_fp = os.path.join(
config.data_dir[data]["dir"],
config.data_dir[data]["data"][0] + "." + config.data_dir[data]["type"],
)
remote_fn = "example." + config.data_dir[data]["type"]
upload_file(
path_or_fileobj=local_fp,
path_in_repo=remote_fn,
repo_id=repo_id,
repo_type="dataset",
token=hf_token,
)

# total time in minutes
print(f"Total time: {(time.time() - start_time) / 60} minutes")


if __name__ == "__main__":
main()
50 changes: 50 additions & 0 deletions examples/use_huggingface_dataset.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
"""
In this script, we use the Hugging Face dataset made
from the script examples/create_huggingface_dataset.py

The dataset is available at:
https://huggingface.co/datasets/bezzam/dummy-dataset

```bash
# install
pip install datasets librosa soundfile

# run
python examples/use_huggingface_dataset.py
```

During the first run, the dataset will be downloaded and cached.
Subsequent runs will use the cached dataset.

"""

from datasets import load_dataset
import numpy as np


# load train and test splits
ds_train = load_dataset("bezzam/dummy-dataset", split="train")
ds_test = load_dataset("bezzam/dummy-dataset", split="test")
print(f"Number of training samples: {len(ds_train)}")
print(f"Number of test samples: {len(ds_test)}")

# load first example
print("\n---- First example:")
example = ds_train[0]

# -- audio duration
duration = len(example["audio"]["array"]) / example["audio"]["sampling_rate"]
print(f"Duration of audio: {duration:.2f} seconds")

# -- image size
image = np.array(example["image"])
print(f"Size of image: {image.shape}")

# -- text
text = example["text"]
print(f"Text: {text}")

# -- label
label = example["label"]
label_str = ds_train.features["label"].int2str(label)
print(f"Label: {label_str}")
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
project_env/
.DS_Store
dataset/*

# from hydra
outputs/
Expand Down
3 changes: 2 additions & 1 deletion README.rst
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,7 +61,8 @@ choose the features that you like. This flexibility is one of the best
* Code formatting.
* Unit tests and continuous integration.
* Packaging and distribution.
* Remove development.
* Remote development.
* Creating and sharing datasets with Hugging Face.

The accompanying
`slides <https://docs.google.com/presentation/d/1D1_JywMl2rjaeuVzpykPBOJsDIuwQKGOJB4EFZjej2s/edit#slide=id.g2eaa4b61f15_0_1346>`__
Expand Down
29 changes: 29 additions & 0 deletions examples/configs/hf_dataset.yaml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
# python examples/create_huggingface_dataset.py hf_token=YOUR_TOKEN
hydra:
job:
chdir: True # change to output folder
job_logging:
formatters:
simple:
format: '[%(levelname)s] - %(message)s'

repo_id: bezzam/dummy-dataset
seed: 0
test_size: 0.15
hf_token:

data_dir:
audio:
dir: dataset/data_audio
type: wav
image:
dir: dataset/data_images
type: png
text:
dir: dataset/data_text
type: txt
label:
file: dataset/data_labels.csv
label: True

stratify_by_column: label
232 changes: 232 additions & 0 deletions examples/create_huggingface_dataset.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
"""
We will create a dataset with images, audios, and text data
so that you can see how various data types can be pushed to
Hugging Face!

The default configuration is in `examples/configs/hf_dataset.yaml`:

```bash
# install
pip install datasets huggingface_hub soundfile

# make a WRITE token on HuggingFace: https://huggingface.co/settings/tokens

# run
python examples/create_huggingface_dataset.py \
hf_token=... \
```
"""

import hydra
from hydra.utils import to_absolute_path
import os
import time
import glob
import numpy as np
import soundfile as sf
from PIL import Image as PILImage
from datasets import Dataset, Image, Audio, ClassLabel
from omegaconf import open_dict
from huggingface_hub import upload_file
import re
import pandas as pd


# -- helper functions
def convert(text):
return int(text) if text.isdigit() else text.lower()


def alphanum_key(key):
return [convert(c) for c in re.split("([0-9]+)", key)]


def natural_sort(arr):
return sorted(arr, key=alphanum_key)


@hydra.main(version_base=None, config_path="configs", config_name="hf_dataset")
def main(config):

start_time = time.time()

# extract and check parameters
repo_id = config.repo_id
hf_token = config.hf_token
test_size = config.test_size

assert repo_id is not None, "Please provide a Hugging Face repo_id."
assert hf_token is not None, "Please provide a Hugging Face token."

# to absolute path, as needed by Hugging Face upload
for data in config.data_dir:
if "dir" in config.data_dir[data]:
config.data_dir[data]["dir"] = to_absolute_path(config.data_dir[data]["dir"])
elif "file" in config.data_dir[data]:
config.data_dir[data]["file"] = to_absolute_path(config.data_dir[data]["file"])

# Step 1: Check data (create dummy data if not present)
n_files = 100 # number of dummy files to create
for data in config.data_dir:

# for directory of data
if "dir" in config.data_dir[data]:
input_dir = config.data_dir[data]["dir"]
data_type = config.data_dir[data]["type"]

if not os.path.exists(input_dir):
# create dummy data
print(f"-- Creating {n_files} dummy {data_type} files in {input_dir}")
os.makedirs(input_dir, exist_ok=True)
for i in range(n_files):
if data_type == "png":
dim = np.random.randint(100, 200)
img = np.random.randint(0, 255, (dim, dim, 3), dtype=np.uint8)
img_path = os.path.join(input_dir, f"{i}.png")
PILImage.fromarray(img).save(img_path)
elif data_type == "wav":
duration = np.random.randint(1, 4)
sample_rate = 16000
audio = np.random.randn(duration * sample_rate)
audio_path = os.path.join(input_dir, f"{i}.wav")
sf.write(audio_path, audio, samplerate=sample_rate)
elif data_type == "txt":
text = f"Hello, this is file {i}"
text_path = os.path.join(input_dir, f"{i}.txt")
with open(text_path, "w") as f:
f.write(text)

# check number of files
files = glob.glob(os.path.join(input_dir, "*." + data_type))
n_files = len(files)
print(f"Found {n_files} {data_type} files in {input_dir}")

# for CSV file where each line is a data point
elif "file" in config.data_dir[data]:
input_file = config.data_dir[data]["file"]

if not os.path.exists(input_file):
# create dummy labels
labels = ["good", "ok", "bad"]
file_labels = np.random.choice(labels, n_files)
with open(input_file, "w") as f:
for i in range(n_files):
f.write(f"{i},{file_labels[i]}\n")
print(f"-- Created dummy labels file at {input_file}")

# check number of unique labels (open with Pandas)
df = pd.read_csv(input_file, header=None)
n_files = len(df)
labels = df[1].unique()
n_labels = len(df[1].unique())
print(f"Found {n_files} lines with {n_labels} unique labels ({labels}) in {input_file}")

else:
raise ValueError("Please provide either `dir` or `file` in data_dir")

# -- only keep common files across all datasets
bn = [os.path.basename(f).split(".")[0] for f in files]
for data in config.data_dir:
if "dir" in config.data_dir[data]:
input_dir = config.data_dir[data]["dir"]
data_type = config.data_dir[data]["type"]
files = glob.glob(os.path.join(input_dir, "*." + data_type))
bn_data = [os.path.basename(f).split(".")[0] for f in files]
common_files = list(set(bn).intersection(bn_data))
common_files = natural_sort(common_files)
print(f"Number of common files: {len(common_files)}")

# -- add common files into dictionary
for data in config.data_dir:
if "dir" in config.data_dir[data]:
with open_dict(config):
config.data_dir[data]["data"] = common_files
if "file" in config.data_dir[data]:
# take row according to common_files
df = pd.read_csv(config.data_dir[data]["file"], header=None)
# -- make first column string
df[0] = df[0].astype(str)
df = df[df[0].isin(common_files)]
with open_dict(config):
config.data_dir[data]["data"] = df[1].tolist()

# Step 2: Create train and test data
dataset_dict = {}

# -- create dictionary of content
for data in config.data_dir:
if "dir" in config.data_dir[data]:
files = config.data_dir[data]["data"]
data_type = config.data_dir[data]["type"]
data_files = [
os.path.join(config.data_dir[data]["dir"], f"{f}.{data_type}") for f in files
]

if data_type in ["txt"]:
# open file content for text files
data_files = [open(f).read() for f in data_files]
dataset_dict[data] = data_files
elif "file" in config.data_dir[data]:
dataset_dict[data] = config.data_dir[data]["data"]

# -- create dataset
dataset = Dataset.from_dict(dataset_dict)
for data in config.data_dir:
if "dir" in config.data_dir[data]:
if config.data_dir[data]["type"] in ["png", "jpg", "jpeg", "tiff"]:
dataset = dataset.cast_column(data, Image())
elif config.data_dir[data]["type"] in ["wav", "mp3", "flac", "ogg"]:
dataset = dataset.cast_column(data, Audio())
elif "file" in config.data_dir[data]:
if config.data_dir[data]["label"]:
labels = list(set(config.data_dir[data]["data"]))
dataset = dataset.cast_column(data, ClassLabel(names=labels))

# -- split into train and test
dataset = dataset.train_test_split(
test_size=test_size,
seed=config.seed,
shuffle=True,
stratify_by_column=config.stratify_by_column, # shuffle must be True
)
print(dataset)

"""
DatasetDict({
train: Dataset({
features: ['audio', 'images', 'text', 'labels'],
num_rows: 85
})
test: Dataset({
features: ['audio', 'images', 'text', 'labels'],
num_rows: 15
})
})
"""

# Step 3: Push to Hugging Face
dataset.push_to_hub(repo_id, token=hf_token)

# -- push individual files
for data in config.data_dir:
if "dir" in config.data_dir[data]:
# push first file
local_fp = os.path.join(
config.data_dir[data]["dir"],
config.data_dir[data]["data"][0] + "." + config.data_dir[data]["type"],
)
remote_fn = "example." + config.data_dir[data]["type"]
upload_file(
path_or_fileobj=local_fp,
path_in_repo=remote_fn,
repo_id=repo_id,
repo_type="dataset",
token=hf_token,
)

# total time in minutes
print(f"Total time: {(time.time() - start_time) / 60} minutes")


if __name__ == "__main__":
main()
50 changes: 50 additions & 0 deletions examples/use_huggingface_dataset.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
"""
In this script, we use the Hugging Face dataset made
from the script examples/create_huggingface_dataset.py

The dataset is available at:
https://huggingface.co/datasets/bezzam/dummy-dataset

```bash
# install
pip install datasets librosa soundfile

# run
python examples/use_huggingface_dataset.py
```

During the first run, the dataset will be downloaded and cached.
Subsequent runs will use the cached dataset.

"""

from datasets import load_dataset
import numpy as np


# load train and test splits
ds_train = load_dataset("bezzam/dummy-dataset", split="train")
ds_test = load_dataset("bezzam/dummy-dataset", split="test")
print(f"Number of training samples: {len(ds_train)}")
print(f"Number of test samples: {len(ds_test)}")

# load first example
print("\n---- First example:")
example = ds_train[0]

# -- audio duration
duration = len(example["audio"]["array"]) / example["audio"]["sampling_rate"]
print(f"Duration of audio: {duration:.2f} seconds")

# -- image size
image = np.array(example["image"])
print(f"Size of image: {image.shape}")

# -- text
text = example["text"]
print(f"Text: {text}")

# -- label
label = example["label"]
label_str = ds_train.features["label"].int2str(label)
print(f"Label: {label_str}")
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
project_env/
.DS_Store
dataset/*

# from hydra
outputs/
Expand Down
3 changes: 2 additions & 1 deletion README.rst
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,7 +61,8 @@ choose the features that you like. This flexibility is one of the best
* Code formatting.
* Unit tests and continuous integration.
* Packaging and distribution.
* Remove development.
* Remote development.
* Creating and sharing datasets with Hugging Face.

The accompanying
`slides <https://docs.google.com/presentation/d/1D1_JywMl2rjaeuVzpykPBOJsDIuwQKGOJB4EFZjej2s/edit#slide=id.g2eaa4b61f15_0_1346>`__
Expand Down
29 changes: 29 additions & 0 deletions examples/configs/hf_dataset.yaml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
# python examples/create_huggingface_dataset.py hf_token=YOUR_TOKEN
hydra:
job:
chdir: True # change to output folder
job_logging:
formatters:
simple:
format: '[%(levelname)s] - %(message)s'

repo_id: bezzam/dummy-dataset
seed: 0
test_size: 0.15
hf_token:

data_dir:
audio:
dir: dataset/data_audio
type: wav
image:
dir: dataset/data_images
type: png
text:
dir: dataset/data_text
type: txt
label:
file: dataset/data_labels.csv
label: True

stratify_by_column: label
232 changes: 232 additions & 0 deletions examples/create_huggingface_dataset.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
"""
We will create a dataset with images, audios, and text data
so that you can see how various data types can be pushed to
Hugging Face!

The default configuration is in `examples/configs/hf_dataset.yaml`:

```bash
# install
pip install datasets huggingface_hub soundfile

# make a WRITE token on HuggingFace: https://huggingface.co/settings/tokens

# run
python examples/create_huggingface_dataset.py \
hf_token=... \
```
"""

import hydra
from hydra.utils import to_absolute_path
import os
import time
import glob
import numpy as np
import soundfile as sf
from PIL import Image as PILImage
from datasets import Dataset, Image, Audio, ClassLabel
from omegaconf import open_dict
from huggingface_hub import upload_file
import re
import pandas as pd


# -- helper functions
def convert(text):
return int(text) if text.isdigit() else text.lower()


def alphanum_key(key):
return [convert(c) for c in re.split("([0-9]+)", key)]


def natural_sort(arr):
return sorted(arr, key=alphanum_key)


@hydra.main(version_base=None, config_path="configs", config_name="hf_dataset")
def main(config):

start_time = time.time()

# extract and check parameters
repo_id = config.repo_id
hf_token = config.hf_token
test_size = config.test_size

assert repo_id is not None, "Please provide a Hugging Face repo_id."
assert hf_token is not None, "Please provide a Hugging Face token."

# to absolute path, as needed by Hugging Face upload
for data in config.data_dir:
if "dir" in config.data_dir[data]:
config.data_dir[data]["dir"] = to_absolute_path(config.data_dir[data]["dir"])
elif "file" in config.data_dir[data]:
config.data_dir[data]["file"] = to_absolute_path(config.data_dir[data]["file"])

# Step 1: Check data (create dummy data if not present)
n_files = 100 # number of dummy files to create
for data in config.data_dir:

# for directory of data
if "dir" in config.data_dir[data]:
input_dir = config.data_dir[data]["dir"]
data_type = config.data_dir[data]["type"]

if not os.path.exists(input_dir):
# create dummy data
print(f"-- Creating {n_files} dummy {data_type} files in {input_dir}")
os.makedirs(input_dir, exist_ok=True)
for i in range(n_files):
if data_type == "png":
dim = np.random.randint(100, 200)
img = np.random.randint(0, 255, (dim, dim, 3), dtype=np.uint8)
img_path = os.path.join(input_dir, f"{i}.png")
PILImage.fromarray(img).save(img_path)
elif data_type == "wav":
duration = np.random.randint(1, 4)
sample_rate = 16000
audio = np.random.randn(duration * sample_rate)
audio_path = os.path.join(input_dir, f"{i}.wav")
sf.write(audio_path, audio, samplerate=sample_rate)
elif data_type == "txt":
text = f"Hello, this is file {i}"
text_path = os.path.join(input_dir, f"{i}.txt")
with open(text_path, "w") as f:
f.write(text)

# check number of files
files = glob.glob(os.path.join(input_dir, "*." + data_type))
n_files = len(files)
print(f"Found {n_files} {data_type} files in {input_dir}")

# for CSV file where each line is a data point
elif "file" in config.data_dir[data]:
input_file = config.data_dir[data]["file"]

if not os.path.exists(input_file):
# create dummy labels
labels = ["good", "ok", "bad"]
file_labels = np.random.choice(labels, n_files)
with open(input_file, "w") as f:
for i in range(n_files):
f.write(f"{i},{file_labels[i]}\n")
print(f"-- Created dummy labels file at {input_file}")

# check number of unique labels (open with Pandas)
df = pd.read_csv(input_file, header=None)
n_files = len(df)
labels = df[1].unique()
n_labels = len(df[1].unique())
print(f"Found {n_files} lines with {n_labels} unique labels ({labels}) in {input_file}")

else:
raise ValueError("Please provide either `dir` or `file` in data_dir")

# -- only keep common files across all datasets
bn = [os.path.basename(f).split(".")[0] for f in files]
for data in config.data_dir:
if "dir" in config.data_dir[data]:
input_dir = config.data_dir[data]["dir"]
data_type = config.data_dir[data]["type"]
files = glob.glob(os.path.join(input_dir, "*." + data_type))
bn_data = [os.path.basename(f).split(".")[0] for f in files]
common_files = list(set(bn).intersection(bn_data))
common_files = natural_sort(common_files)
print(f"Number of common files: {len(common_files)}")

# -- add common files into dictionary
for data in config.data_dir:
if "dir" in config.data_dir[data]:
with open_dict(config):
config.data_dir[data]["data"] = common_files
if "file" in config.data_dir[data]:
# take row according to common_files
df = pd.read_csv(config.data_dir[data]["file"], header=None)
# -- make first column string
df[0] = df[0].astype(str)
df = df[df[0].isin(common_files)]
with open_dict(config):
config.data_dir[data]["data"] = df[1].tolist()

# Step 2: Create train and test data
dataset_dict = {}

# -- create dictionary of content
for data in config.data_dir:
if "dir" in config.data_dir[data]:
files = config.data_dir[data]["data"]
data_type = config.data_dir[data]["type"]
data_files = [
os.path.join(config.data_dir[data]["dir"], f"{f}.{data_type}") for f in files
]

if data_type in ["txt"]:
# open file content for text files
data_files = [open(f).read() for f in data_files]
dataset_dict[data] = data_files
elif "file" in config.data_dir[data]:
dataset_dict[data] = config.data_dir[data]["data"]

# -- create dataset
dataset = Dataset.from_dict(dataset_dict)
for data in config.data_dir:
if "dir" in config.data_dir[data]:
if config.data_dir[data]["type"] in ["png", "jpg", "jpeg", "tiff"]:
dataset = dataset.cast_column(data, Image())
elif config.data_dir[data]["type"] in ["wav", "mp3", "flac", "ogg"]:
dataset = dataset.cast_column(data, Audio())
elif "file" in config.data_dir[data]:
if config.data_dir[data]["label"]:
labels = list(set(config.data_dir[data]["data"]))
dataset = dataset.cast_column(data, ClassLabel(names=labels))

# -- split into train and test
dataset = dataset.train_test_split(
test_size=test_size,
seed=config.seed,
shuffle=True,
stratify_by_column=config.stratify_by_column, # shuffle must be True
)
print(dataset)

"""
DatasetDict({
train: Dataset({
features: ['audio', 'images', 'text', 'labels'],
num_rows: 85
})
test: Dataset({
features: ['audio', 'images', 'text', 'labels'],
num_rows: 15
})
})
"""

# Step 3: Push to Hugging Face
dataset.push_to_hub(repo_id, token=hf_token)

# -- push individual files
for data in config.data_dir:
if "dir" in config.data_dir[data]:
# push first file
local_fp = os.path.join(
config.data_dir[data]["dir"],
config.data_dir[data]["data"][0] + "." + config.data_dir[data]["type"],
)
remote_fn = "example." + config.data_dir[data]["type"]
upload_file(
path_or_fileobj=local_fp,
path_in_repo=remote_fn,
repo_id=repo_id,
repo_type="dataset",
token=hf_token,
)

# total time in minutes
print(f"Total time: {(time.time() - start_time) / 60} minutes")


if __name__ == "__main__":
main()
50 changes: 50 additions & 0 deletions examples/use_huggingface_dataset.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
"""
In this script, we use the Hugging Face dataset made
from the script examples/create_huggingface_dataset.py

The dataset is available at:
https://huggingface.co/datasets/bezzam/dummy-dataset

```bash
# install
pip install datasets librosa soundfile

# run
python examples/use_huggingface_dataset.py
```

During the first run, the dataset will be downloaded and cached.
Subsequent runs will use the cached dataset.

"""

from datasets import load_dataset
import numpy as np


# load train and test splits
ds_train = load_dataset("bezzam/dummy-dataset", split="train")
ds_test = load_dataset("bezzam/dummy-dataset", split="test")
print(f"Number of training samples: {len(ds_train)}")
print(f"Number of test samples: {len(ds_test)}")

# load first example
print("\n---- First example:")
example = ds_train[0]

# -- audio duration
duration = len(example["audio"]["array"]) / example["audio"]["sampling_rate"]
print(f"Duration of audio: {duration:.2f} seconds")

# -- image size
image = np.array(example["image"])
print(f"Size of image: {image.shape}")

# -- text
text = example["text"]
print(f"Text: {text}")

# -- label
label = example["label"]
label_str = ds_train.features["label"].int2str(label)
print(f"Label: {label_str}")
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
project_env/
.DS_Store
dataset/*

# from hydra
outputs/
Expand Down
3 changes: 2 additions & 1 deletion README.rst
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,7 +61,8 @@ choose the features that you like. This flexibility is one of the best
* Code formatting.
* Unit tests and continuous integration.
* Packaging and distribution.
* Remove development.
* Remote development.
* Creating and sharing datasets with Hugging Face.

The accompanying
`slides <https://docs.google.com/presentation/d/1D1_JywMl2rjaeuVzpykPBOJsDIuwQKGOJB4EFZjej2s/edit#slide=id.g2eaa4b61f15_0_1346>`__
Expand Down
29 changes: 29 additions & 0 deletions examples/configs/hf_dataset.yaml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
# python examples/create_huggingface_dataset.py hf_token=YOUR_TOKEN
hydra:
job:
chdir: True # change to output folder
job_logging:
formatters:
simple:
format: '[%(levelname)s] - %(message)s'

repo_id: bezzam/dummy-dataset
seed: 0
test_size: 0.15
hf_token:

data_dir:
audio:
dir: dataset/data_audio
type: wav
image:
dir: dataset/data_images
type: png
text:
dir: dataset/data_text
type: txt
label:
file: dataset/data_labels.csv
label: True

stratify_by_column: label
232 changes: 232 additions & 0 deletions examples/create_huggingface_dataset.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
"""
We will create a dataset with images, audios, and text data
so that you can see how various data types can be pushed to
Hugging Face!

The default configuration is in `examples/configs/hf_dataset.yaml`:

```bash
# install
pip install datasets huggingface_hub soundfile

# make a WRITE token on HuggingFace: https://huggingface.co/settings/tokens

# run
python examples/create_huggingface_dataset.py \
hf_token=... \
```
"""

import hydra
from hydra.utils import to_absolute_path
import os
import time
import glob
import numpy as np
import soundfile as sf
from PIL import Image as PILImage
from datasets import Dataset, Image, Audio, ClassLabel
from omegaconf import open_dict
from huggingface_hub import upload_file
import re
import pandas as pd


# -- helper functions
def convert(text):
return int(text) if text.isdigit() else text.lower()


def alphanum_key(key):
return [convert(c) for c in re.split("([0-9]+)", key)]


def natural_sort(arr):
return sorted(arr, key=alphanum_key)


@hydra.main(version_base=None, config_path="configs", config_name="hf_dataset")
def main(config):

start_time = time.time()

# extract and check parameters
repo_id = config.repo_id
hf_token = config.hf_token
test_size = config.test_size

assert repo_id is not None, "Please provide a Hugging Face repo_id."
assert hf_token is not None, "Please provide a Hugging Face token."

# to absolute path, as needed by Hugging Face upload
for data in config.data_dir:
if "dir" in config.data_dir[data]:
config.data_dir[data]["dir"] = to_absolute_path(config.data_dir[data]["dir"])
elif "file" in config.data_dir[data]:
config.data_dir[data]["file"] = to_absolute_path(config.data_dir[data]["file"])

# Step 1: Check data (create dummy data if not present)
n_files = 100 # number of dummy files to create
for data in config.data_dir:

# for directory of data
if "dir" in config.data_dir[data]:
input_dir = config.data_dir[data]["dir"]
data_type = config.data_dir[data]["type"]

if not os.path.exists(input_dir):
# create dummy data
print(f"-- Creating {n_files} dummy {data_type} files in {input_dir}")
os.makedirs(input_dir, exist_ok=True)
for i in range(n_files):
if data_type == "png":
dim = np.random.randint(100, 200)
img = np.random.randint(0, 255, (dim, dim, 3), dtype=np.uint8)
img_path = os.path.join(input_dir, f"{i}.png")
PILImage.fromarray(img).save(img_path)
elif data_type == "wav":
duration = np.random.randint(1, 4)
sample_rate = 16000
audio = np.random.randn(duration * sample_rate)
audio_path = os.path.join(input_dir, f"{i}.wav")
sf.write(audio_path, audio, samplerate=sample_rate)
elif data_type == "txt":
text = f"Hello, this is file {i}"
text_path = os.path.join(input_dir, f"{i}.txt")
with open(text_path, "w") as f:
f.write(text)

# check number of files
files = glob.glob(os.path.join(input_dir, "*." + data_type))
n_files = len(files)
print(f"Found {n_files} {data_type} files in {input_dir}")

# for CSV file where each line is a data point
elif "file" in config.data_dir[data]:
input_file = config.data_dir[data]["file"]

if not os.path.exists(input_file):
# create dummy labels
labels = ["good", "ok", "bad"]
file_labels = np.random.choice(labels, n_files)
with open(input_file, "w") as f:
for i in range(n_files):
f.write(f"{i},{file_labels[i]}\n")
print(f"-- Created dummy labels file at {input_file}")

# check number of unique labels (open with Pandas)
df = pd.read_csv(input_file, header=None)
n_files = len(df)
labels = df[1].unique()
n_labels = len(df[1].unique())
print(f"Found {n_files} lines with {n_labels} unique labels ({labels}) in {input_file}")

else:
raise ValueError("Please provide either `dir` or `file` in data_dir")

# -- only keep common files across all datasets
bn = [os.path.basename(f).split(".")[0] for f in files]
for data in config.data_dir:
if "dir" in config.data_dir[data]:
input_dir = config.data_dir[data]["dir"]
data_type = config.data_dir[data]["type"]
files = glob.glob(os.path.join(input_dir, "*." + data_type))
bn_data = [os.path.basename(f).split(".")[0] for f in files]
common_files = list(set(bn).intersection(bn_data))
common_files = natural_sort(common_files)
print(f"Number of common files: {len(common_files)}")

# -- add common files into dictionary
for data in config.data_dir:
if "dir" in config.data_dir[data]:
with open_dict(config):
config.data_dir[data]["data"] = common_files
if "file" in config.data_dir[data]:
# take row according to common_files
df = pd.read_csv(config.data_dir[data]["file"], header=None)
# -- make first column string
df[0] = df[0].astype(str)
df = df[df[0].isin(common_files)]
with open_dict(config):
config.data_dir[data]["data"] = df[1].tolist()

# Step 2: Create train and test data
dataset_dict = {}

# -- create dictionary of content
for data in config.data_dir:
if "dir" in config.data_dir[data]:
files = config.data_dir[data]["data"]
data_type = config.data_dir[data]["type"]
data_files = [
os.path.join(config.data_dir[data]["dir"], f"{f}.{data_type}") for f in files
]

if data_type in ["txt"]:
# open file content for text files
data_files = [open(f).read() for f in data_files]
dataset_dict[data] = data_files
elif "file" in config.data_dir[data]:
dataset_dict[data] = config.data_dir[data]["data"]

# -- create dataset
dataset = Dataset.from_dict(dataset_dict)
for data in config.data_dir:
if "dir" in config.data_dir[data]:
if config.data_dir[data]["type"] in ["png", "jpg", "jpeg", "tiff"]:
dataset = dataset.cast_column(data, Image())
elif config.data_dir[data]["type"] in ["wav", "mp3", "flac", "ogg"]:
dataset = dataset.cast_column(data, Audio())
elif "file" in config.data_dir[data]:
if config.data_dir[data]["label"]:
labels = list(set(config.data_dir[data]["data"]))
dataset = dataset.cast_column(data, ClassLabel(names=labels))

# -- split into train and test
dataset = dataset.train_test_split(
test_size=test_size,
seed=config.seed,
shuffle=True,
stratify_by_column=config.stratify_by_column, # shuffle must be True
)
print(dataset)

"""
DatasetDict({
train: Dataset({
features: ['audio', 'images', 'text', 'labels'],
num_rows: 85
})
test: Dataset({
features: ['audio', 'images', 'text', 'labels'],
num_rows: 15
})
})
"""

# Step 3: Push to Hugging Face
dataset.push_to_hub(repo_id, token=hf_token)

# -- push individual files
for data in config.data_dir:
if "dir" in config.data_dir[data]:
# push first file
local_fp = os.path.join(
config.data_dir[data]["dir"],
config.data_dir[data]["data"][0] + "." + config.data_dir[data]["type"],
)
remote_fn = "example." + config.data_dir[data]["type"]
upload_file(
path_or_fileobj=local_fp,
path_in_repo=remote_fn,
repo_id=repo_id,
repo_type="dataset",
token=hf_token,
)

# total time in minutes
print(f"Total time: {(time.time() - start_time) / 60} minutes")


if __name__ == "__main__":
main()
50 changes: 50 additions & 0 deletions examples/use_huggingface_dataset.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
"""
In this script, we use the Hugging Face dataset made
from the script examples/create_huggingface_dataset.py

The dataset is available at:
https://huggingface.co/datasets/bezzam/dummy-dataset

```bash
# install
pip install datasets librosa soundfile

# run
python examples/use_huggingface_dataset.py
```

During the first run, the dataset will be downloaded and cached.
Subsequent runs will use the cached dataset.

"""

from datasets import load_dataset
import numpy as np


# load train and test splits
ds_train = load_dataset("bezzam/dummy-dataset", split="train")
ds_test = load_dataset("bezzam/dummy-dataset", split="test")
print(f"Number of training samples: {len(ds_train)}")
print(f"Number of test samples: {len(ds_test)}")

# load first example
print("\n---- First example:")
example = ds_train[0]

# -- audio duration
duration = len(example["audio"]["array"]) / example["audio"]["sampling_rate"]
print(f"Duration of audio: {duration:.2f} seconds")

# -- image size
image = np.array(example["image"])
print(f"Size of image: {image.shape}")

# -- text
text = example["text"]
print(f"Text: {text}")

# -- label
label = example["label"]
label_str = ds_train.features["label"].int2str(label)
print(f"Label: {label_str}")
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
project_env/
.DS_Store
dataset/*

# from hydra
outputs/
Expand Down
3 changes: 2 additions & 1 deletion README.rst
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,7 +61,8 @@ choose the features that you like. This flexibility is one of the best
* Code formatting.
* Unit tests and continuous integration.
* Packaging and distribution.
* Remove development.
* Remote development.
* Creating and sharing datasets with Hugging Face.

The accompanying
`slides <https://docs.google.com/presentation/d/1D1_JywMl2rjaeuVzpykPBOJsDIuwQKGOJB4EFZjej2s/edit#slide=id.g2eaa4b61f15_0_1346>`__
Expand Down
29 changes: 29 additions & 0 deletions examples/configs/hf_dataset.yaml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
# python examples/create_huggingface_dataset.py hf_token=YOUR_TOKEN
hydra:
job:
chdir: True # change to output folder
job_logging:
formatters:
simple:
format: '[%(levelname)s] - %(message)s'

repo_id: bezzam/dummy-dataset
seed: 0
test_size: 0.15
hf_token:

data_dir:
audio:
dir: dataset/data_audio
type: wav
image:
dir: dataset/data_images
type: png
text:
dir: dataset/data_text
type: txt
label:
file: dataset/data_labels.csv
label: True

stratify_by_column: label
232 changes: 232 additions & 0 deletions examples/create_huggingface_dataset.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
"""
We will create a dataset with images, audios, and text data
so that you can see how various data types can be pushed to
Hugging Face!

The default configuration is in `examples/configs/hf_dataset.yaml`:

```bash
# install
pip install datasets huggingface_hub soundfile

# make a WRITE token on HuggingFace: https://huggingface.co/settings/tokens

# run
python examples/create_huggingface_dataset.py \
hf_token=... \
```
"""

import hydra
from hydra.utils import to_absolute_path
import os
import time
import glob
import numpy as np
import soundfile as sf
from PIL import Image as PILImage
from datasets import Dataset, Image, Audio, ClassLabel
from omegaconf import open_dict
from huggingface_hub import upload_file
import re
import pandas as pd


# -- helper functions
def convert(text):
return int(text) if text.isdigit() else text.lower()


def alphanum_key(key):
return [convert(c) for c in re.split("([0-9]+)", key)]


def natural_sort(arr):
return sorted(arr, key=alphanum_key)


@hydra.main(version_base=None, config_path="configs", config_name="hf_dataset")
def main(config):

start_time = time.time()

# extract and check parameters
repo_id = config.repo_id
hf_token = config.hf_token
test_size = config.test_size

assert repo_id is not None, "Please provide a Hugging Face repo_id."
assert hf_token is not None, "Please provide a Hugging Face token."

# to absolute path, as needed by Hugging Face upload
for data in config.data_dir:
if "dir" in config.data_dir[data]:
config.data_dir[data]["dir"] = to_absolute_path(config.data_dir[data]["dir"])
elif "file" in config.data_dir[data]:
config.data_dir[data]["file"] = to_absolute_path(config.data_dir[data]["file"])

# Step 1: Check data (create dummy data if not present)
n_files = 100 # number of dummy files to create
for data in config.data_dir:

# for directory of data
if "dir" in config.data_dir[data]:
input_dir = config.data_dir[data]["dir"]
data_type = config.data_dir[data]["type"]

if not os.path.exists(input_dir):
# create dummy data
print(f"-- Creating {n_files} dummy {data_type} files in {input_dir}")
os.makedirs(input_dir, exist_ok=True)
for i in range(n_files):
if data_type == "png":
dim = np.random.randint(100, 200)
img = np.random.randint(0, 255, (dim, dim, 3), dtype=np.uint8)
img_path = os.path.join(input_dir, f"{i}.png")
PILImage.fromarray(img).save(img_path)
elif data_type == "wav":
duration = np.random.randint(1, 4)
sample_rate = 16000
audio = np.random.randn(duration * sample_rate)
audio_path = os.path.join(input_dir, f"{i}.wav")
sf.write(audio_path, audio, samplerate=sample_rate)
elif data_type == "txt":
text = f"Hello, this is file {i}"
text_path = os.path.join(input_dir, f"{i}.txt")
with open(text_path, "w") as f:
f.write(text)

# check number of files
files = glob.glob(os.path.join(input_dir, "*." + data_type))
n_files = len(files)
print(f"Found {n_files} {data_type} files in {input_dir}")

# for CSV file where each line is a data point
elif "file" in config.data_dir[data]:
input_file = config.data_dir[data]["file"]

if not os.path.exists(input_file):
# create dummy labels
labels = ["good", "ok", "bad"]
file_labels = np.random.choice(labels, n_files)
with open(input_file, "w") as f:
for i in range(n_files):
f.write(f"{i},{file_labels[i]}\n")
print(f"-- Created dummy labels file at {input_file}")

# check number of unique labels (open with Pandas)
df = pd.read_csv(input_file, header=None)
n_files = len(df)
labels = df[1].unique()
n_labels = len(df[1].unique())
print(f"Found {n_files} lines with {n_labels} unique labels ({labels}) in {input_file}")

else:
raise ValueError("Please provide either `dir` or `file` in data_dir")

# -- only keep common files across all datasets
bn = [os.path.basename(f).split(".")[0] for f in files]
for data in config.data_dir:
if "dir" in config.data_dir[data]:
input_dir = config.data_dir[data]["dir"]
data_type = config.data_dir[data]["type"]
files = glob.glob(os.path.join(input_dir, "*." + data_type))
bn_data = [os.path.basename(f).split(".")[0] for f in files]
common_files = list(set(bn).intersection(bn_data))
common_files = natural_sort(common_files)
print(f"Number of common files: {len(common_files)}")

# -- add common files into dictionary
for data in config.data_dir:
if "dir" in config.data_dir[data]:
with open_dict(config):
config.data_dir[data]["data"] = common_files
if "file" in config.data_dir[data]:
# take row according to common_files
df = pd.read_csv(config.data_dir[data]["file"], header=None)
# -- make first column string
df[0] = df[0].astype(str)
df = df[df[0].isin(common_files)]
with open_dict(config):
config.data_dir[data]["data"] = df[1].tolist()

# Step 2: Create train and test data
dataset_dict = {}

# -- create dictionary of content
for data in config.data_dir:
if "dir" in config.data_dir[data]:
files = config.data_dir[data]["data"]
data_type = config.data_dir[data]["type"]
data_files = [
os.path.join(config.data_dir[data]["dir"], f"{f}.{data_type}") for f in files
]

if data_type in ["txt"]:
# open file content for text files
data_files = [open(f).read() for f in data_files]
dataset_dict[data] = data_files
elif "file" in config.data_dir[data]:
dataset_dict[data] = config.data_dir[data]["data"]

# -- create dataset
dataset = Dataset.from_dict(dataset_dict)
for data in config.data_dir:
if "dir" in config.data_dir[data]:
if config.data_dir[data]["type"] in ["png", "jpg", "jpeg", "tiff"]:
dataset = dataset.cast_column(data, Image())
elif config.data_dir[data]["type"] in ["wav", "mp3", "flac", "ogg"]:
dataset = dataset.cast_column(data, Audio())
elif "file" in config.data_dir[data]:
if config.data_dir[data]["label"]:
labels = list(set(config.data_dir[data]["data"]))
dataset = dataset.cast_column(data, ClassLabel(names=labels))

# -- split into train and test
dataset = dataset.train_test_split(
test_size=test_size,
seed=config.seed,
shuffle=True,
stratify_by_column=config.stratify_by_column, # shuffle must be True
)
print(dataset)

"""
DatasetDict({
train: Dataset({
features: ['audio', 'images', 'text', 'labels'],
num_rows: 85
})
test: Dataset({
features: ['audio', 'images', 'text', 'labels'],
num_rows: 15
})
})
"""

# Step 3: Push to Hugging Face
dataset.push_to_hub(repo_id, token=hf_token)

# -- push individual files
for data in config.data_dir:
if "dir" in config.data_dir[data]:
# push first file
local_fp = os.path.join(
config.data_dir[data]["dir"],
config.data_dir[data]["data"][0] + "." + config.data_dir[data]["type"],
)
remote_fn = "example." + config.data_dir[data]["type"]
upload_file(
path_or_fileobj=local_fp,
path_in_repo=remote_fn,
repo_id=repo_id,
repo_type="dataset",
token=hf_token,
)

# total time in minutes
print(f"Total time: {(time.time() - start_time) / 60} minutes")


if __name__ == "__main__":
main()
50 changes: 50 additions & 0 deletions examples/use_huggingface_dataset.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
"""
In this script, we use the Hugging Face dataset made
from the script examples/create_huggingface_dataset.py

The dataset is available at:
https://huggingface.co/datasets/bezzam/dummy-dataset

```bash
# install
pip install datasets librosa soundfile

# run
python examples/use_huggingface_dataset.py
```

During the first run, the dataset will be downloaded and cached.
Subsequent runs will use the cached dataset.

"""

from datasets import load_dataset
import numpy as np


# load train and test splits
ds_train = load_dataset("bezzam/dummy-dataset", split="train")
ds_test = load_dataset("bezzam/dummy-dataset", split="test")
print(f"Number of training samples: {len(ds_train)}")
print(f"Number of test samples: {len(ds_test)}")

# load first example
print("\n---- First example:")
example = ds_train[0]

# -- audio duration
duration = len(example["audio"]["array"]) / example["audio"]["sampling_rate"]
print(f"Duration of audio: {duration:.2f} seconds")

# -- image size
image = np.array(example["image"])
print(f"Size of image: {image.shape}")

# -- text
text = example["text"]
print(f"Text: {text}")

# -- label
label = example["label"]
label_str = ds_train.features["label"].int2str(label)
print(f"Label: {label_str}")
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
project_env/
.DS_Store
dataset/*

# from hydra
outputs/
Expand Down
3 changes: 2 additions & 1 deletion README.rst
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,7 +61,8 @@ choose the features that you like. This flexibility is one of the best
* Code formatting.
* Unit tests and continuous integration.
* Packaging and distribution.
* Remove development.
* Remote development.
* Creating and sharing datasets with Hugging Face.

The accompanying
`slides <https://docs.google.com/presentation/d/1D1_JywMl2rjaeuVzpykPBOJsDIuwQKGOJB4EFZjej2s/edit#slide=id.g2eaa4b61f15_0_1346>`__
Expand Down
29 changes: 29 additions & 0 deletions examples/configs/hf_dataset.yaml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
# python examples/create_huggingface_dataset.py hf_token=YOUR_TOKEN
hydra:
job:
chdir: True # change to output folder
job_logging:
formatters:
simple:
format: '[%(levelname)s] - %(message)s'

repo_id: bezzam/dummy-dataset
seed: 0
test_size: 0.15
hf_token:

data_dir:
audio:
dir: dataset/data_audio
type: wav
image:
dir: dataset/data_images
type: png
text:
dir: dataset/data_text
type: txt
label:
file: dataset/data_labels.csv
label: True

stratify_by_column: label
232 changes: 232 additions & 0 deletions examples/create_huggingface_dataset.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
"""
We will create a dataset with images, audios, and text data
so that you can see how various data types can be pushed to
Hugging Face!

The default configuration is in `examples/configs/hf_dataset.yaml`:

```bash
# install
pip install datasets huggingface_hub soundfile

# make a WRITE token on HuggingFace: https://huggingface.co/settings/tokens

# run
python examples/create_huggingface_dataset.py \
hf_token=... \
```
"""

import hydra
from hydra.utils import to_absolute_path
import os
import time
import glob
import numpy as np
import soundfile as sf
from PIL import Image as PILImage
from datasets import Dataset, Image, Audio, ClassLabel
from omegaconf import open_dict
from huggingface_hub import upload_file
import re
import pandas as pd


# -- helper functions
def convert(text):
return int(text) if text.isdigit() else text.lower()


def alphanum_key(key):
return [convert(c) for c in re.split("([0-9]+)", key)]


def natural_sort(arr):
return sorted(arr, key=alphanum_key)


@hydra.main(version_base=None, config_path="configs", config_name="hf_dataset")
def main(config):

start_time = time.time()

# extract and check parameters
repo_id = config.repo_id
hf_token = config.hf_token
test_size = config.test_size

assert repo_id is not None, "Please provide a Hugging Face repo_id."
assert hf_token is not None, "Please provide a Hugging Face token."

# to absolute path, as needed by Hugging Face upload
for data in config.data_dir:
if "dir" in config.data_dir[data]:
config.data_dir[data]["dir"] = to_absolute_path(config.data_dir[data]["dir"])
elif "file" in config.data_dir[data]:
config.data_dir[data]["file"] = to_absolute_path(config.data_dir[data]["file"])

# Step 1: Check data (create dummy data if not present)
n_files = 100 # number of dummy files to create
for data in config.data_dir:

# for directory of data
if "dir" in config.data_dir[data]:
input_dir = config.data_dir[data]["dir"]
data_type = config.data_dir[data]["type"]

if not os.path.exists(input_dir):
# create dummy data
print(f"-- Creating {n_files} dummy {data_type} files in {input_dir}")
os.makedirs(input_dir, exist_ok=True)
for i in range(n_files):
if data_type == "png":
dim = np.random.randint(100, 200)
img = np.random.randint(0, 255, (dim, dim, 3), dtype=np.uint8)
img_path = os.path.join(input_dir, f"{i}.png")
PILImage.fromarray(img).save(img_path)
elif data_type == "wav":
duration = np.random.randint(1, 4)
sample_rate = 16000
audio = np.random.randn(duration * sample_rate)
audio_path = os.path.join(input_dir, f"{i}.wav")
sf.write(audio_path, audio, samplerate=sample_rate)
elif data_type == "txt":
text = f"Hello, this is file {i}"
text_path = os.path.join(input_dir, f"{i}.txt")
with open(text_path, "w") as f:
f.write(text)

# check number of files
files = glob.glob(os.path.join(input_dir, "*." + data_type))
n_files = len(files)
print(f"Found {n_files} {data_type} files in {input_dir}")

# for CSV file where each line is a data point
elif "file" in config.data_dir[data]:
input_file = config.data_dir[data]["file"]

if not os.path.exists(input_file):
# create dummy labels
labels = ["good", "ok", "bad"]
file_labels = np.random.choice(labels, n_files)
with open(input_file, "w") as f:
for i in range(n_files):
f.write(f"{i},{file_labels[i]}\n")
print(f"-- Created dummy labels file at {input_file}")

# check number of unique labels (open with Pandas)
df = pd.read_csv(input_file, header=None)
n_files = len(df)
labels = df[1].unique()
n_labels = len(df[1].unique())
print(f"Found {n_files} lines with {n_labels} unique labels ({labels}) in {input_file}")

else:
raise ValueError("Please provide either `dir` or `file` in data_dir")

# -- only keep common files across all datasets
bn = [os.path.basename(f).split(".")[0] for f in files]
for data in config.data_dir:
if "dir" in config.data_dir[data]:
input_dir = config.data_dir[data]["dir"]
data_type = config.data_dir[data]["type"]
files = glob.glob(os.path.join(input_dir, "*." + data_type))
bn_data = [os.path.basename(f).split(".")[0] for f in files]
common_files = list(set(bn).intersection(bn_data))
common_files = natural_sort(common_files)
print(f"Number of common files: {len(common_files)}")

# -- add common files into dictionary
for data in config.data_dir:
if "dir" in config.data_dir[data]:
with open_dict(config):
config.data_dir[data]["data"] = common_files
if "file" in config.data_dir[data]:
# take row according to common_files
df = pd.read_csv(config.data_dir[data]["file"], header=None)
# -- make first column string
df[0] = df[0].astype(str)
df = df[df[0].isin(common_files)]
with open_dict(config):
config.data_dir[data]["data"] = df[1].tolist()

# Step 2: Create train and test data
dataset_dict = {}

# -- create dictionary of content
for data in config.data_dir:
if "dir" in config.data_dir[data]:
files = config.data_dir[data]["data"]
data_type = config.data_dir[data]["type"]
data_files = [
os.path.join(config.data_dir[data]["dir"], f"{f}.{data_type}") for f in files
]

if data_type in ["txt"]:
# open file content for text files
data_files = [open(f).read() for f in data_files]
dataset_dict[data] = data_files
elif "file" in config.data_dir[data]:
dataset_dict[data] = config.data_dir[data]["data"]

# -- create dataset
dataset = Dataset.from_dict(dataset_dict)
for data in config.data_dir:
if "dir" in config.data_dir[data]:
if config.data_dir[data]["type"] in ["png", "jpg", "jpeg", "tiff"]:
dataset = dataset.cast_column(data, Image())
elif config.data_dir[data]["type"] in ["wav", "mp3", "flac", "ogg"]:
dataset = dataset.cast_column(data, Audio())
elif "file" in config.data_dir[data]:
if config.data_dir[data]["label"]:
labels = list(set(config.data_dir[data]["data"]))
dataset = dataset.cast_column(data, ClassLabel(names=labels))

# -- split into train and test
dataset = dataset.train_test_split(
test_size=test_size,
seed=config.seed,
shuffle=True,
stratify_by_column=config.stratify_by_column, # shuffle must be True
)
print(dataset)

"""
DatasetDict({
train: Dataset({
features: ['audio', 'images', 'text', 'labels'],
num_rows: 85
})
test: Dataset({
features: ['audio', 'images', 'text', 'labels'],
num_rows: 15
})
})
"""

# Step 3: Push to Hugging Face
dataset.push_to_hub(repo_id, token=hf_token)

# -- push individual files
for data in config.data_dir:
if "dir" in config.data_dir[data]:
# push first file
local_fp = os.path.join(
config.data_dir[data]["dir"],
config.data_dir[data]["data"][0] + "." + config.data_dir[data]["type"],
)
remote_fn = "example." + config.data_dir[data]["type"]
upload_file(
path_or_fileobj=local_fp,
path_in_repo=remote_fn,
repo_id=repo_id,
repo_type="dataset",
token=hf_token,
)

# total time in minutes
print(f"Total time: {(time.time() - start_time) / 60} minutes")


if __name__ == "__main__":
main()
50 changes: 50 additions & 0 deletions examples/use_huggingface_dataset.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
"""
In this script, we use the Hugging Face dataset made
from the script examples/create_huggingface_dataset.py

The dataset is available at:
https://huggingface.co/datasets/bezzam/dummy-dataset

```bash
# install
pip install datasets librosa soundfile

# run
python examples/use_huggingface_dataset.py
```

During the first run, the dataset will be downloaded and cached.
Subsequent runs will use the cached dataset.

"""

from datasets import load_dataset
import numpy as np


# load train and test splits
ds_train = load_dataset("bezzam/dummy-dataset", split="train")
ds_test = load_dataset("bezzam/dummy-dataset", split="test")
print(f"Number of training samples: {len(ds_train)}")
print(f"Number of test samples: {len(ds_test)}")

# load first example
print("\n---- First example:")
example = ds_train[0]

# -- audio duration
duration = len(example["audio"]["array"]) / example["audio"]["sampling_rate"]
print(f"Duration of audio: {duration:.2f} seconds")

# -- image size
image = np.array(example["image"])
print(f"Size of image: {image.shape}")

# -- text
text = example["text"]
print(f"Text: {text}")

# -- label
label = example["label"]
label_str = ds_train.features["label"].int2str(label)
print(f"Label: {label_str}")
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
project_env/
.DS_Store
dataset/*

# from hydra
outputs/
Expand Down
3 changes: 2 additions & 1 deletion README.rst
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,7 +61,8 @@ choose the features that you like. This flexibility is one of the best
* Code formatting.
* Unit tests and continuous integration.
* Packaging and distribution.
* Remove development.
* Remote development.
* Creating and sharing datasets with Hugging Face.

The accompanying
`slides <https://docs.google.com/presentation/d/1D1_JywMl2rjaeuVzpykPBOJsDIuwQKGOJB4EFZjej2s/edit#slide=id.g2eaa4b61f15_0_1346>`__
Expand Down
29 changes: 29 additions & 0 deletions examples/configs/hf_dataset.yaml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
# python examples/create_huggingface_dataset.py hf_token=YOUR_TOKEN
hydra:
job:
chdir: True # change to output folder
job_logging:
formatters:
simple:
format: '[%(levelname)s] - %(message)s'

repo_id: bezzam/dummy-dataset
seed: 0
test_size: 0.15
hf_token:

data_dir:
audio:
dir: dataset/data_audio
type: wav
image:
dir: dataset/data_images
type: png
text:
dir: dataset/data_text
type: txt
label:
file: dataset/data_labels.csv
label: True

stratify_by_column: label
232 changes: 232 additions & 0 deletions examples/create_huggingface_dataset.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
"""
We will create a dataset with images, audios, and text data
so that you can see how various data types can be pushed to
Hugging Face!

The default configuration is in `examples/configs/hf_dataset.yaml`:

```bash
# install
pip install datasets huggingface_hub soundfile

# make a WRITE token on HuggingFace: https://huggingface.co/settings/tokens

# run
python examples/create_huggingface_dataset.py \
hf_token=... \
```
"""

import hydra
from hydra.utils import to_absolute_path
import os
import time
import glob
import numpy as np
import soundfile as sf
from PIL import Image as PILImage
from datasets import Dataset, Image, Audio, ClassLabel
from omegaconf import open_dict
from huggingface_hub import upload_file
import re
import pandas as pd


# -- helper functions
def convert(text):
return int(text) if text.isdigit() else text.lower()


def alphanum_key(key):
return [convert(c) for c in re.split("([0-9]+)", key)]


def natural_sort(arr):
return sorted(arr, key=alphanum_key)


@hydra.main(version_base=None, config_path="configs", config_name="hf_dataset")
def main(config):

start_time = time.time()

# extract and check parameters
repo_id = config.repo_id
hf_token = config.hf_token
test_size = config.test_size

assert repo_id is not None, "Please provide a Hugging Face repo_id."
assert hf_token is not None, "Please provide a Hugging Face token."

# to absolute path, as needed by Hugging Face upload
for data in config.data_dir:
if "dir" in config.data_dir[data]:
config.data_dir[data]["dir"] = to_absolute_path(config.data_dir[data]["dir"])
elif "file" in config.data_dir[data]:
config.data_dir[data]["file"] = to_absolute_path(config.data_dir[data]["file"])

# Step 1: Check data (create dummy data if not present)
n_files = 100 # number of dummy files to create
for data in config.data_dir:

# for directory of data
if "dir" in config.data_dir[data]:
input_dir = config.data_dir[data]["dir"]
data_type = config.data_dir[data]["type"]

if not os.path.exists(input_dir):
# create dummy data
print(f"-- Creating {n_files} dummy {data_type} files in {input_dir}")
os.makedirs(input_dir, exist_ok=True)
for i in range(n_files):
if data_type == "png":
dim = np.random.randint(100, 200)
img = np.random.randint(0, 255, (dim, dim, 3), dtype=np.uint8)
img_path = os.path.join(input_dir, f"{i}.png")
PILImage.fromarray(img).save(img_path)
elif data_type == "wav":
duration = np.random.randint(1, 4)
sample_rate = 16000
audio = np.random.randn(duration * sample_rate)
audio_path = os.path.join(input_dir, f"{i}.wav")
sf.write(audio_path, audio, samplerate=sample_rate)
elif data_type == "txt":
text = f"Hello, this is file {i}"
text_path = os.path.join(input_dir, f"{i}.txt")
with open(text_path, "w") as f:
f.write(text)

# check number of files
files = glob.glob(os.path.join(input_dir, "*." + data_type))
n_files = len(files)
print(f"Found {n_files} {data_type} files in {input_dir}")

# for CSV file where each line is a data point
elif "file" in config.data_dir[data]:
input_file = config.data_dir[data]["file"]

if not os.path.exists(input_file):
# create dummy labels
labels = ["good", "ok", "bad"]
file_labels = np.random.choice(labels, n_files)
with open(input_file, "w") as f:
for i in range(n_files):
f.write(f"{i},{file_labels[i]}\n")
print(f"-- Created dummy labels file at {input_file}")

# check number of unique labels (open with Pandas)
df = pd.read_csv(input_file, header=None)
n_files = len(df)
labels = df[1].unique()
n_labels = len(df[1].unique())
print(f"Found {n_files} lines with {n_labels} unique labels ({labels}) in {input_file}")

else:
raise ValueError("Please provide either `dir` or `file` in data_dir")

# -- only keep common files across all datasets
bn = [os.path.basename(f).split(".")[0] for f in files]
for data in config.data_dir:
if "dir" in config.data_dir[data]:
input_dir = config.data_dir[data]["dir"]
data_type = config.data_dir[data]["type"]
files = glob.glob(os.path.join(input_dir, "*." + data_type))
bn_data = [os.path.basename(f).split(".")[0] for f in files]
common_files = list(set(bn).intersection(bn_data))
common_files = natural_sort(common_files)
print(f"Number of common files: {len(common_files)}")

# -- add common files into dictionary
for data in config.data_dir:
if "dir" in config.data_dir[data]:
with open_dict(config):
config.data_dir[data]["data"] = common_files
if "file" in config.data_dir[data]:
# take row according to common_files
df = pd.read_csv(config.data_dir[data]["file"], header=None)
# -- make first column string
df[0] = df[0].astype(str)
df = df[df[0].isin(common_files)]
with open_dict(config):
config.data_dir[data]["data"] = df[1].tolist()

# Step 2: Create train and test data
dataset_dict = {}

# -- create dictionary of content
for data in config.data_dir:
if "dir" in config.data_dir[data]:
files = config.data_dir[data]["data"]
data_type = config.data_dir[data]["type"]
data_files = [
os.path.join(config.data_dir[data]["dir"], f"{f}.{data_type}") for f in files
]

if data_type in ["txt"]:
# open file content for text files
data_files = [open(f).read() for f in data_files]
dataset_dict[data] = data_files
elif "file" in config.data_dir[data]:
dataset_dict[data] = config.data_dir[data]["data"]

# -- create dataset
dataset = Dataset.from_dict(dataset_dict)
for data in config.data_dir:
if "dir" in config.data_dir[data]:
if config.data_dir[data]["type"] in ["png", "jpg", "jpeg", "tiff"]:
dataset = dataset.cast_column(data, Image())
elif config.data_dir[data]["type"] in ["wav", "mp3", "flac", "ogg"]:
dataset = dataset.cast_column(data, Audio())
elif "file" in config.data_dir[data]:
if config.data_dir[data]["label"]:
labels = list(set(config.data_dir[data]["data"]))
dataset = dataset.cast_column(data, ClassLabel(names=labels))

# -- split into train and test
dataset = dataset.train_test_split(
test_size=test_size,
seed=config.seed,
shuffle=True,
stratify_by_column=config.stratify_by_column, # shuffle must be True
)
print(dataset)

"""
DatasetDict({
train: Dataset({
features: ['audio', 'images', 'text', 'labels'],
num_rows: 85
})
test: Dataset({
features: ['audio', 'images', 'text', 'labels'],
num_rows: 15
})
})
"""

# Step 3: Push to Hugging Face
dataset.push_to_hub(repo_id, token=hf_token)

# -- push individual files
for data in config.data_dir:
if "dir" in config.data_dir[data]:
# push first file
local_fp = os.path.join(
config.data_dir[data]["dir"],
config.data_dir[data]["data"][0] + "." + config.data_dir[data]["type"],
)
remote_fn = "example." + config.data_dir[data]["type"]
upload_file(
path_or_fileobj=local_fp,
path_in_repo=remote_fn,
repo_id=repo_id,
repo_type="dataset",
token=hf_token,
)

# total time in minutes
print(f"Total time: {(time.time() - start_time) / 60} minutes")


if __name__ == "__main__":
main()
50 changes: 50 additions & 0 deletions examples/use_huggingface_dataset.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
"""
In this script, we use the Hugging Face dataset made
from the script examples/create_huggingface_dataset.py

The dataset is available at:
https://huggingface.co/datasets/bezzam/dummy-dataset

```bash
# install
pip install datasets librosa soundfile

# run
python examples/use_huggingface_dataset.py
```

During the first run, the dataset will be downloaded and cached.
Subsequent runs will use the cached dataset.

"""

from datasets import load_dataset
import numpy as np


# load train and test splits
ds_train = load_dataset("bezzam/dummy-dataset", split="train")
ds_test = load_dataset("bezzam/dummy-dataset", split="test")
print(f"Number of training samples: {len(ds_train)}")
print(f"Number of test samples: {len(ds_test)}")

# load first example
print("\n---- First example:")
example = ds_train[0]

# -- audio duration
duration = len(example["audio"]["array"]) / example["audio"]["sampling_rate"]
print(f"Duration of audio: {duration:.2f} seconds")

# -- image size
image = np.array(example["image"])
print(f"Size of image: {image.shape}")

# -- text
text = example["text"]
print(f"Text: {text}")

# -- label
label = example["label"]
label_str = ds_train.features["label"].int2str(label)
print(f"Label: {label_str}")