senselab 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,68 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
10
+
11
+ "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
12
+
13
+ "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
14
+
15
+ "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
16
+
17
+ "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
18
+
19
+ "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
20
+
21
+ "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
22
+
23
+ "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
24
+
25
+ "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
26
+
27
+ "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
28
+
29
+ 2. Grant of Copyright License.
30
+
31
+ Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
32
+
33
+ 3. Grant of Patent License.
34
+
35
+ Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
36
+
37
+ 4. Redistribution.
38
+
39
+ You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
40
+
41
+ You must give any other recipients of the Work or Derivative Works a copy of this License; and
42
+ You must cause any modified files to carry prominent notices stating that You changed the files; and
43
+ You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
44
+ If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
45
+
46
+ You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
47
+
48
+ 5. Submission of Contributions.
49
+
50
+ Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
51
+
52
+ 6. Trademarks.
53
+
54
+ This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
55
+
56
+ 7. Disclaimer of Warranty.
57
+
58
+ Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
59
+
60
+ 8. Limitation of Liability.
61
+
62
+ In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
63
+
64
+ 9. Accepting Warranty or Additional Liability.
65
+
66
+ While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
67
+
68
+ END OF TERMS AND CONDITIONS
@@ -0,0 +1,113 @@
1
+ Metadata-Version: 2.1
2
+ Name: senselab
3
+ Version: 0.1.0
4
+ Summary: Senselab is a Python package that simplifies building pipelines for speech and voice analysis.
5
+ Home-page: https://github.com/sensein/senselab
6
+ License: Apache-2.0
7
+ Keywords: voice,speech,audio
8
+ Author: Fabio Catania
9
+ Author-email: fabiocat@mit.edu
10
+ Maintainer: Fabio Catania
11
+ Maintainer-email: fabiocat@mit.edu
12
+ Requires-Python: >=3.10,<4.0
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: License :: OSI Approved :: Apache Software License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Requires-Dist: accelerate (>=0.29.3,<0.30.0)
21
+ Requires-Dist: click (>=8.1.7,<9.0.0)
22
+ Requires-Dist: datasets (>=2.18.0,<3.0.0)
23
+ Requires-Dist: ffmpeg-python (>=0.2.0,<0.3.0)
24
+ Requires-Dist: huggingface-hub (>=0.23.0,<0.24.0)
25
+ Requires-Dist: ipykernel (>=6.29.4,<7.0.0)
26
+ Requires-Dist: jsonschema (>=4.21.1,<5.0.0)
27
+ Requires-Dist: librosa (>=0.10.1,<0.11.0)
28
+ Requires-Dist: pydantic (>=2.7.1,<3.0.0)
29
+ Requires-Dist: pydra (>=0.23,<0.24)
30
+ Requires-Dist: soundfile (>=0.12.1,<0.13.0)
31
+ Requires-Dist: torch (>=2.2.2,<3.0.0)
32
+ Requires-Dist: torchaudio (>=2.2.2,<3.0.0)
33
+ Requires-Dist: torchvision (>=0.17.2,<0.18.0)
34
+ Requires-Dist: transformers (>=4.39.3,<5.0.0)
35
+ Project-URL: Documentation, https://sensein.github.io/senselab
36
+ Project-URL: Repository, https://github.com/sensein/senselab
37
+ Description-Content-Type: text/markdown
38
+
39
+ # The ```senselab``` repo
40
+
41
+ [![Build](https://github.com/sensein/senselab/actions/workflows/test.yaml/badge.svg?branch=main)](https://github.com/sensein/senselab/actions/workflows/test.yaml?query=branch%3Amain)
42
+ [![codecov](https://codecov.io/gh/sensein/senselab/branch/main/graph/badge.svg?token=MFU1LM80ET)](https://codecov.io/gh/sensein/senselab)
43
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
44
+
45
+ [![PyPI](https://img.shields.io/pypi/v/senselab.svg)](https://pypi.org/project/senselab/)
46
+ [![Python Version](https://img.shields.io/pypi/pyversions/senselab)](https://pypi.org/project/senselab)
47
+ [![License](https://img.shields.io/pypi/l/senselab)](https://opensource.org/licenses/Apache-2.0)
48
+
49
+ [![pages](https://img.shields.io/badge/api-docs-blue)](https://sensein.github.io/senselab)
50
+
51
+ Welcome to the ```senselab``` repo! This is a Python package for streamlining the processing and analysis of behavioral data, such as voice and speech patterns, with robust and reproducible methodologies.
52
+
53
+ **Caution:**: this package is still under development and may change rapidly over the next few weeks.
54
+
55
+ ## Features
56
+ - **Modular design**: Utilize a variety of task-specific transformations that can be easily integrated or used standalone, allowing for flexible data manipulation and analysis strategies.
57
+
58
+ - **Pre-built pipelines**: Access pre-configured pipelines combining multiple transformations tailored for common analysis tasks, which help in reducing setup time and effort.
59
+
60
+ - **Reproducibility**: Ensures consistent outputs through the use of fixed seeds and version-controlled processing steps, making your results verifiable and easily comparable.
61
+
62
+ - **Easy integration**: Designed to fit into existing workflows with minimal configuration, `senselab` can be used alongside other data analysis tools and frameworks seamlessly.
63
+
64
+ - **Extensible**: Open to modifications and contributions, the package can be expanded with custom transformations and pipelines to meet specific research needs. <u>Do you want to contribute? Please, reach out!</u>
65
+
66
+ - **Comprehensive documentation**: Comes with detailed documentation for all features and modules, including examples and guides on how to extend the package for other types of behavioral data analysis.
67
+
68
+ - **Performance Optimized**: Efficiently processes large datasets with optimized code and algorithms, ensuring quick turnaround times even for complex analyses.
69
+
70
+ - **Interactive Examples**: Includes Jupyter notebooks that provide practical examples of how `senselab` can be implemented to derive insights from real-world data sets.
71
+
72
+ Whether you're researching speech disorders, analyzing customer service calls, or studying communication patterns, `senselab` provides the tools and flexibility needed to extract meaningful conclusions from your data.
73
+
74
+
75
+ ## Installation
76
+ Install this package via:
77
+
78
+ ```sh
79
+ pip install senselab
80
+ ```
81
+
82
+ Or get the newest development version via:
83
+
84
+ ```sh
85
+ pip install git+https://github.com/sensein/senselab.git
86
+ ```
87
+
88
+ ## Quick start
89
+ ```Python
90
+ from senselab.app import hello_world
91
+
92
+ hello_world()
93
+ ```
94
+
95
+ ## To do:
96
+ - [ ] Integrating more audio tasks and moving functions from b2aiprep package:
97
+ - [ ] data_augmentation
98
+ - [ ] data_representation
99
+ - [x] example_task
100
+ - [x] input_output
101
+ - [ ] raw_signal_processing
102
+ - [ ] speaker_diarization
103
+ - [ ] speech emotion recognition
104
+ - [ ] speech enhancement
105
+ - [ ] speech_to_text
106
+ - [ ] text_to_speech
107
+ - [ ] voice conversion
108
+ - [ ] Integrating more video tasks:
109
+ - [x] input_output
110
+
111
+ - [ ] Preparing some pipelines with pydra
112
+ - [ ] Populating the CLI
113
+
@@ -0,0 +1,74 @@
1
+ # The ```senselab``` repo
2
+
3
+ [![Build](https://github.com/sensein/senselab/actions/workflows/test.yaml/badge.svg?branch=main)](https://github.com/sensein/senselab/actions/workflows/test.yaml?query=branch%3Amain)
4
+ [![codecov](https://codecov.io/gh/sensein/senselab/branch/main/graph/badge.svg?token=MFU1LM80ET)](https://codecov.io/gh/sensein/senselab)
5
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
6
+
7
+ [![PyPI](https://img.shields.io/pypi/v/senselab.svg)](https://pypi.org/project/senselab/)
8
+ [![Python Version](https://img.shields.io/pypi/pyversions/senselab)](https://pypi.org/project/senselab)
9
+ [![License](https://img.shields.io/pypi/l/senselab)](https://opensource.org/licenses/Apache-2.0)
10
+
11
+ [![pages](https://img.shields.io/badge/api-docs-blue)](https://sensein.github.io/senselab)
12
+
13
+ Welcome to the ```senselab``` repo! This is a Python package for streamlining the processing and analysis of behavioral data, such as voice and speech patterns, with robust and reproducible methodologies.
14
+
15
+ **Caution:**: this package is still under development and may change rapidly over the next few weeks.
16
+
17
+ ## Features
18
+ - **Modular design**: Utilize a variety of task-specific transformations that can be easily integrated or used standalone, allowing for flexible data manipulation and analysis strategies.
19
+
20
+ - **Pre-built pipelines**: Access pre-configured pipelines combining multiple transformations tailored for common analysis tasks, which help in reducing setup time and effort.
21
+
22
+ - **Reproducibility**: Ensures consistent outputs through the use of fixed seeds and version-controlled processing steps, making your results verifiable and easily comparable.
23
+
24
+ - **Easy integration**: Designed to fit into existing workflows with minimal configuration, `senselab` can be used alongside other data analysis tools and frameworks seamlessly.
25
+
26
+ - **Extensible**: Open to modifications and contributions, the package can be expanded with custom transformations and pipelines to meet specific research needs. <u>Do you want to contribute? Please, reach out!</u>
27
+
28
+ - **Comprehensive documentation**: Comes with detailed documentation for all features and modules, including examples and guides on how to extend the package for other types of behavioral data analysis.
29
+
30
+ - **Performance Optimized**: Efficiently processes large datasets with optimized code and algorithms, ensuring quick turnaround times even for complex analyses.
31
+
32
+ - **Interactive Examples**: Includes Jupyter notebooks that provide practical examples of how `senselab` can be implemented to derive insights from real-world data sets.
33
+
34
+ Whether you're researching speech disorders, analyzing customer service calls, or studying communication patterns, `senselab` provides the tools and flexibility needed to extract meaningful conclusions from your data.
35
+
36
+
37
+ ## Installation
38
+ Install this package via:
39
+
40
+ ```sh
41
+ pip install senselab
42
+ ```
43
+
44
+ Or get the newest development version via:
45
+
46
+ ```sh
47
+ pip install git+https://github.com/sensein/senselab.git
48
+ ```
49
+
50
+ ## Quick start
51
+ ```Python
52
+ from senselab.app import hello_world
53
+
54
+ hello_world()
55
+ ```
56
+
57
+ ## To do:
58
+ - [ ] Integrating more audio tasks and moving functions from b2aiprep package:
59
+ - [ ] data_augmentation
60
+ - [ ] data_representation
61
+ - [x] example_task
62
+ - [x] input_output
63
+ - [ ] raw_signal_processing
64
+ - [ ] speaker_diarization
65
+ - [ ] speech emotion recognition
66
+ - [ ] speech enhancement
67
+ - [ ] speech_to_text
68
+ - [ ] text_to_speech
69
+ - [ ] voice conversion
70
+ - [ ] Integrating more video tasks:
71
+ - [x] input_output
72
+
73
+ - [ ] Preparing some pipelines with pydra
74
+ - [ ] Populating the CLI
@@ -0,0 +1,133 @@
1
+ [tool.poetry]
2
+ name = "senselab"
3
+ version = "0.1.0"
4
+ description = "Senselab is a Python package that simplifies building pipelines for speech and voice analysis."
5
+ authors = [
6
+ "Fabio Catania <fabiocat@mit.edu>",
7
+ "Satra Ghosh <satra@mit.edu>",
8
+ ]
9
+ maintainers = [
10
+ "Fabio Catania <fabiocat@mit.edu>",
11
+ "Satra Ghosh <satra@mit.edu>",
12
+ ]
13
+ license = "Apache-2.0"
14
+ readme = "README.md"
15
+ homepage = "https://github.com/sensein/senselab"
16
+ repository = "https://github.com/sensein/senselab"
17
+ documentation = "https://sensein.github.io/senselab"
18
+ keywords = ["voice", "speech", "audio"]
19
+ classifiers = [
20
+ "Development Status :: 3 - Alpha",
21
+ "Programming Language :: Python :: 3.10",
22
+ "License :: OSI Approved :: Apache Software License",
23
+ "Operating System :: OS Independent"
24
+ ]
25
+ packages = [{include = "senselab", from = "src"}]
26
+
27
+ [tool.poetry.dependencies]
28
+ python = "^3.10"
29
+ click = "^8.1.7"
30
+ jsonschema = "^4.21.1"
31
+ datasets = "^2.18.0"
32
+ torch = "^2.2.2"
33
+ torchvision = "^0.17.2"
34
+ torchaudio = "^2.2.2"
35
+ transformers = "^4.39.3"
36
+ librosa = "^0.10.1"
37
+ soundfile = "^0.12.1"
38
+ ffmpeg-python = "^0.2.0"
39
+ ipykernel = "^6.29.4"
40
+ pydra = "^0.23"
41
+ pydantic = "^2.7.1"
42
+ accelerate = "^0.29.3"
43
+ huggingface-hub = "^0.23.0"
44
+
45
+ [tool.poetry.group.dev]
46
+ optional = true
47
+ [tool.poetry.group.dev.dependencies]
48
+ pytest = "^8.1.1"
49
+ mypy = "^1.9.0"
50
+ pre-commit = "^3.7.0"
51
+ pytest-cov = "^5.0.0"
52
+ ruff = "^0.3.4"
53
+ pytest-mock = "^3.14.0"
54
+
55
+ [tool.poetry.group.docs]
56
+ optional = true
57
+ [tool.poetry.group.docs.dependencies]
58
+ pdoc = "^14.4.0"
59
+
60
+ [tool.pytest.ini_options]
61
+ pythonpath = [
62
+ "src"
63
+ ]
64
+ testpaths = [
65
+ "src/tests"
66
+ ]
67
+
68
+ [tool.mypy]
69
+ ignore_missing_imports = true
70
+
71
+ [tool.ruff]
72
+ exclude = [
73
+ ".bzr",
74
+ ".direnv",
75
+ ".eggs",
76
+ ".git",
77
+ ".git-rewrite",
78
+ ".hg",
79
+ ".mypy_cache",
80
+ ".nox",
81
+ ".pants.d",
82
+ ".pytype",
83
+ ".ruff_cache",
84
+ ".svn",
85
+ ".tox",
86
+ ".venv",
87
+ "__pypackages__",
88
+ "_build",
89
+ "buck-out",
90
+ "build",
91
+ "dist",
92
+ "node_modules",
93
+ "venv"
94
+ ]
95
+ line-length = 200
96
+ indent-width = 4
97
+ src = ["src"]
98
+ target-version = "py310"
99
+
100
+ [tool.ruff.lint]
101
+ select = ["ANN", "D", "E", "F", "I"]
102
+ ignore = [
103
+ "ANN101", # self should not be annotated.
104
+ "ANN102" # cls should not be annotated.
105
+ ]
106
+ fixable = ["ALL"]
107
+ unfixable = []
108
+ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
109
+
110
+ [tool.ruff.lint.pydocstyle]
111
+ convention = "google"
112
+
113
+ [tool.ruff.lint.per-file-ignores]
114
+ "src/tests/**/*.py" = []
115
+
116
+ [tool.poetry.scripts]
117
+ senselab-cli = "senselab.cli:main"
118
+
119
+ [tool.ruff.format]
120
+ quote-style = "double"
121
+ indent-style = "space"
122
+ skip-magic-trailing-comma = false
123
+ line-ending = "auto"
124
+
125
+ [tool.poetry-dynamic-versioning]
126
+ enable = false
127
+ vcs = "git"
128
+ style = "pep440"
129
+ bump = true
130
+ pattern = "default-unprefixed"
131
+ [build-system]
132
+ requires = ["poetry-core>=1.0.0", "poetry-dynamic-versioning>=1.0.0,<2.0.0"]
133
+ build-backend = "poetry_dynamic_versioning.backend"
@@ -0,0 +1 @@
1
+ """.. include:: ../../README.md""" # noqa: D415
@@ -0,0 +1,18 @@
1
+ """This script defines a simple function that prints 'Hello World!' to the console.
2
+
3
+ It then calls this function if the script is run as the main program.
4
+ """
5
+
6
+ from typing import Any, Dict
7
+
8
+ from senselab.utils.decorators import get_response_time
9
+
10
+
11
+ @get_response_time
12
+ def hello_world() -> Dict[str, Any]:
13
+ """Prints hello world."""
14
+ return {"output": "Hello World!"}
15
+
16
+ if __name__ == "__main__":
17
+ a = hello_world()
18
+ print(a)
@@ -0,0 +1,65 @@
1
+ """This module implements some utilities for the speech-to-text task."""
2
+ from typing import Any, Dict
3
+
4
+ from datasets import Dataset
5
+ from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor, pipeline
6
+
7
+ from senselab.utils.functions import _select_device_and_dtype
8
+ from senselab.utils.hf import HFModel
9
+ from senselab.utils.tasks.input_output import _from_dict_to_hf_dataset, _from_hf_dataset_to_dict
10
+
11
+
12
+ def transcribe_dataset(dataset: Dict, model_id: str, audio_column: str = 'audio') -> Dict[str, Any]:
13
+ """Transcribes all audio samples in the dataset."""
14
+ _ = HFModel(model_id=model_id) # check HF model is valid
15
+ hf_dataset = _from_dict_to_hf_dataset(dataset)
16
+ transcript_dataset = _transcribe_dataset_with_hf(dataset=hf_dataset, model_id=model_id, audio_column=audio_column)
17
+ return _from_hf_dataset_to_dict(transcript_dataset)
18
+
19
+ def _transcribe_dataset_with_hf(
20
+ dataset: Dataset,
21
+ model_id: str,
22
+ low_cpu_mem_usage: bool = True,
23
+ use_safetensors: bool = True,
24
+ max_new_tokens: int = 128,
25
+ chunk_length_s: int = 30,
26
+ batch_size: int = 16,
27
+ return_timestamps: bool = True,
28
+ audio_column: str = 'audio'
29
+ ) -> Dict[str, Any]:
30
+ """Transcribes all audio samples in the dataset and adds the transcriptions as a new column.
31
+
32
+ # TODO: optmizing the function replacing the loop with pydra wf
33
+ """
34
+ _ = HFModel(model_id=model_id) # check HF model is valid
35
+
36
+ # Using batched map function to transcribe audio in batches for efficiency
37
+ device, torch_dtype = _select_device_and_dtype(device_options=['cuda', 'cpu'])
38
+ model = AutoModelForSpeechSeq2Seq.from_pretrained(model_id, torch_dtype=torch_dtype, low_cpu_mem_usage=low_cpu_mem_usage, use_safetensors=use_safetensors).to(device)
39
+ processor = AutoProcessor.from_pretrained(model_id)
40
+
41
+ pipe = pipeline(
42
+ "automatic-speech-recognition",
43
+ model=model,
44
+ tokenizer=processor.tokenizer,
45
+ feature_extractor=processor.feature_extractor,
46
+ max_new_tokens=max_new_tokens,
47
+ chunk_length_s=chunk_length_s,
48
+ batch_size=batch_size,
49
+ return_timestamps=return_timestamps,
50
+ torch_dtype=torch_dtype,
51
+ device=device,
52
+ )
53
+
54
+ def _transcribe_row_with_hf(dataset_row: Dataset, pipe: pipeline, audio_column_name: str ="audio") -> Dict[str, Any]:
55
+ """Transcribes a batch of audio samples and retains the original batch data."""
56
+ # Perform transcription
57
+ transcriptions = pipe(dataset_row[audio_column_name])
58
+ return transcriptions
59
+
60
+ transcripts = []
61
+ for row in dataset:
62
+ result = _transcribe_row_with_hf(row, pipe, audio_column_name=audio_column)
63
+ transcripts.append(result)
64
+ transcript_dataset = Dataset.from_dict({"asr": transcripts})
65
+ return _from_hf_dataset_to_dict(transcript_dataset)
@@ -0,0 +1,6 @@
1
+ """This module defines a pydra API for the speech to text task."""
2
+ import pydra
3
+
4
+ from senselab.audio.tasks.speech_to_text import transcribe_dataset
5
+
6
+ transcribe_dataset_pt = pydra.mark.task(transcribe_dataset)
File without changes
@@ -0,0 +1,17 @@
1
+ """This module defines CLI commands for the Senselab application."""
2
+
3
+ import click
4
+
5
+ from .app import hello_world as hw_function # Renamed to avoid conflict
6
+
7
+
8
+ @click.group()
9
+ def main() -> None:
10
+ """Define the main CLI group."""
11
+ pass
12
+
13
+
14
+ @main.command()
15
+ def hello_world() -> None:
16
+ """Execute the hello_world command from the app module."""
17
+ hw_function()
@@ -0,0 +1,10 @@
1
+ {
2
+ "files": {
3
+ "audio": {
4
+ "extensions": [".wav", ".mp3"]
5
+ },
6
+ "video": {
7
+ "extensions": [".mp4", ".avi"]
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,16 @@
1
+ """This module provides the implementation of config utilities."""
2
+ import json
3
+ import os
4
+ from typing import Any, Dict
5
+
6
+
7
+ def get_config() -> Dict[str, Any]:
8
+ """Reads and returns the contents of the config file."""
9
+ # Get the directory of the current script
10
+ script_dir = os.path.dirname(__file__)
11
+ # Build the path to the config file
12
+ config_file_path = os.path.join(script_dir, 'config.json')
13
+ # Load the config file
14
+ with open(config_file_path, 'r', encoding='utf-8') as file:
15
+ config = json.load(file)
16
+ return config
@@ -0,0 +1,26 @@
1
+ """This module is for decorator functions."""
2
+
3
+ import time
4
+ from datetime import datetime
5
+ from functools import wraps
6
+ from typing import Any, Callable
7
+
8
+
9
+ def get_response_time(func: Callable) -> Callable:
10
+ """Decorator to measure and print response time information."""
11
+ @wraps(func)
12
+ def wrapper(*args: Any, **kwargs: Any) -> Any: # noqa: ANN401
13
+ print(f"Hello from {func.__name__}!")
14
+ start_time = time.time()
15
+ result = func(*args, **kwargs)
16
+ end_time = time.time()
17
+ duration = end_time - start_time
18
+
19
+ start_str = datetime.fromtimestamp(start_time).strftime("%Y-%m-%d %H:%M:%S.%f")
20
+ end_str = datetime.fromtimestamp(end_time).strftime("%Y-%m-%d %H:%M:%S.%f")
21
+
22
+ print("start_str: ", start_str, "end_str: ", end_str, "duration: ", duration)
23
+
24
+ return result
25
+
26
+ return wrapper
@@ -0,0 +1,40 @@
1
+ """This module provides the implementation of file utilities."""
2
+ import os
3
+ from pathlib import Path
4
+ from typing import List
5
+
6
+ from pydantic import BaseModel, FilePath, field_validator
7
+
8
+ from senselab.utils.config import get_config
9
+
10
+
11
+ class File(BaseModel):
12
+ """This class represents a file."""
13
+ filepath: FilePath
14
+
15
+ @property
16
+ def type(self) -> str:
17
+ """Determine the type of the file based on its extension."""
18
+ extension = os.path.splitext(self.filepath)[1].lower()
19
+ for type_name, type_info in get_config()['files'].items():
20
+ if extension in type_info['extensions']:
21
+ return type_name
22
+ raise ValueError("File type could not be determined from extension.")
23
+
24
+ @field_validator('filepath')
25
+ def validate_filepath(cls, v: FilePath) -> FilePath:
26
+ """Validate the filepath."""
27
+ if not os.path.exists(v):
28
+ raise ValueError(f'File {v} does not exist')
29
+
30
+ file_extension = os.path.splitext(v)[1].lower()
31
+ valid_extensions = [ext for types in get_config()['files'].values() for ext in types['extensions']]
32
+
33
+ if file_extension not in valid_extensions:
34
+ raise ValueError(f'Unsupported file extension: {file_extension}. Supported extensions: {valid_extensions}')
35
+
36
+ return v
37
+
38
+ def from_strings_to_files(list_of_files: List[str]) -> List[File]:
39
+ """Create a list of `File` objects from a list of strings."""
40
+ return [File(filepath=Path(file)) for file in list_of_files]
@@ -0,0 +1,17 @@
1
+ """Utility functions for senselab."""
2
+ import torch
3
+
4
+
5
+ def _select_device_and_dtype(device_options: list[str] = ['cuda', 'mps', 'cpu']) -> tuple[str, torch.dtype]:
6
+ """Determines the device and data type for PyTorch operations."""
7
+ # TODO: maybe move this function as a generic utility function?
8
+ if torch.cuda.is_available() and "cuda" in device_options:
9
+ device = "cuda"
10
+ torch_dtype = torch.float16 # Using half precision for CUDA
11
+ elif torch.backends.mps.is_available() and "mps" in device_options:
12
+ device = "mps"
13
+ torch_dtype = torch.float16 # Using half precision for MPS if suitable
14
+ else:
15
+ device = "cpu"
16
+ torch_dtype = torch.float32 # Default to float32 on CPU for better precision
17
+ return device, torch_dtype
@@ -0,0 +1,33 @@
1
+ """This module provides the implementation of Hugging Face utilities."""
2
+
3
+ import os
4
+ from typing import Optional
5
+
6
+ from huggingface_hub import HfApi
7
+ from pydantic import BaseModel, field_validator
8
+
9
+
10
+ class HFModel(BaseModel):
11
+ """Hugging Face model."""
12
+ model_id: str
13
+ revision: str = "main"
14
+
15
+ @field_validator('model_id')
16
+ def validate_model_id(cls, value: str) -> str:
17
+ """Validate the model_id."""
18
+ if not value:
19
+ raise ValueError("model_id cannot be empty")
20
+ if not os.path.isfile(value) and not _check_hf_repo_exists(value, "model", None):
21
+ raise ValueError("model_id is not a valid Hugging Face model")
22
+ return value
23
+
24
+ def _check_hf_repo_exists(repo_id: str, repo_type: str, token: Optional[str] = None) -> bool:
25
+ """Private function to check if a Hugging Face repository exists."""
26
+ api = HfApi()
27
+ try:
28
+ repo_refs = api.list_repo_refs(repo_id=repo_id, repo_type=repo_type, token=token)
29
+ if repo_refs.branches:
30
+ return True
31
+ except Exception as e:
32
+ raise RuntimeError(f"An error occurred: {e}")
33
+ return False
@@ -0,0 +1,144 @@
1
+ """This module implements the IOTask class."""
2
+
3
+ import os
4
+ from typing import Any, Dict, List, Optional
5
+
6
+ from datasets import Audio, Dataset, Image, load_dataset
7
+
8
+ from senselab.utils.file import File, from_strings_to_files
9
+ from senselab.utils.hf import _check_hf_repo_exists
10
+
11
+
12
+ def read_files_from_disk(files: List[str]) -> Dict[str, Any]:
13
+ """Read files from disk and create a Hugging Face `Dataset` object."""
14
+ formatted_files = from_strings_to_files(files)
15
+
16
+ def _from_files_to_dataset(files: List[File]) -> Dataset:
17
+ """Reading files from disk and create a Hugging Face `Dataset` object."""
18
+ # Checking if all files are of the same type
19
+ if not all(file.type == files[0].type for file in files):
20
+ raise ValueError("All files must be of the same type.")
21
+
22
+ # Loading file paths
23
+ file_data = [str(file.filepath) for file in files]
24
+
25
+ # Creating the Dataset object
26
+ dict_obj = {files[0].type: file_data} # Using the type of the first file as the key
27
+
28
+ return Dataset.from_dict(dict_obj)
29
+
30
+ dataset = _from_files_to_dataset(formatted_files)
31
+ return _from_hf_dataset_to_dict(dataset)
32
+
33
+ def read_dataset_from_disk(input_path: str, split: str, streaming: bool = False) -> Dict[str, Any]:
34
+ """Loads a Hugging Face `Dataset` object from disk, determining the format based on the file extension or directory."""
35
+ # Determine the input format based on the file extension or if it is a directory
36
+ if os.path.isdir(input_path):
37
+ input_format = 'arrow'
38
+ else:
39
+ input_format = os.path.splitext(input_path)[1].strip('.')
40
+
41
+ # Load the dataset
42
+ try:
43
+ dataset = load_dataset(input_format, split=split, data_files=input_path, streaming=streaming)
44
+ return _from_hf_dataset_to_dict(dataset)
45
+ except Exception as e:
46
+ # Generic error handling, e.g., network issues, data loading issues
47
+ raise RuntimeError(f"An error occurred while loading the dataset: {str(e)}")
48
+
49
+ def read_dataset_from_hub(remote_repository: str, revision: str = "main", split: str = "all", hf_token: Optional[str] = None) -> Dict[str, Any]:
50
+ """Loads a Hugging Face `Dataset` object from the Hugging Face Hub, including support for private repositories."""
51
+ if not _check_hf_repo_exists(remote_repository, "dataset", hf_token):
52
+ raise RuntimeError(f"The repository {remote_repository} - {revision} - {split} does not exist or could not be accessed.")
53
+
54
+ # Load the dataset
55
+ try:
56
+ dataset = load_dataset(path=remote_repository, revision=revision, split=split, token=hf_token)
57
+ except Exception as e:
58
+ # Generic error handling, e.g., network issues, data loading issues
59
+ raise RuntimeError(f"An error occurred while loading the dataset: {str(e)}")
60
+
61
+ return _from_hf_dataset_to_dict(dataset)
62
+
63
+ def push_dataset_to_hub(dataset: Dict[str, Any], remote_repository: str, revision: str = "main", split: str = "all", hf_token: Optional[str] = None) -> None:
64
+ """Uploads a Hugging Face `Dataset` object to the Hugging Face Hub."""
65
+ hf_dataset = _from_dict_to_hf_dataset(dataset)
66
+
67
+ try:
68
+ # Upload the dataset to the Hugging Face Hub
69
+ if hf_token:
70
+ hf_dataset.push_to_hub(repo_id=remote_repository, revision=revision, split=split, private=True, token=hf_token)
71
+ else:
72
+ hf_dataset.push_to_hub(repo_id=remote_repository, revision=revision, split=split)
73
+ except Exception as e:
74
+ raise RuntimeError(f"Failed to push dataset to the hub: {str(e)}")
75
+ return
76
+
77
+ def save_dataset_to_disk(dataset: Dict[str, Any], output_directory: str, output_name: str, output_format: str = "parquet") -> None:
78
+ """Saves a Hugging Face `Dataset` object to disk."""
79
+ # TODO: optimize saving process (e.g., playing with batch size and num of processors)
80
+ hf_dataset = _from_dict_to_hf_dataset(dataset)
81
+
82
+ # Prepare the output path differently if the format is Arrow
83
+ if output_format == "arrow":
84
+ output_path = os.path.join(output_directory, output_name) # No extension for Arrow, it's a directory
85
+ else:
86
+ output_path = os.path.join(output_directory, f"{output_name}.{output_format}")
87
+
88
+ # Use os.makedirs to create the output directory, ignore error if it already exists
89
+ os.makedirs(output_directory, exist_ok=True)
90
+
91
+ if output_format == "parquet":
92
+ def _save_hf_dataset_as_parquet(dataset: Dataset, output_path: str) -> None:
93
+ """Saves a Hugging Face `Dataset` object to parquet format."""
94
+ dataset.to_parquet(output_path)
95
+
96
+ _save_hf_dataset_as_parquet(hf_dataset, output_path)
97
+ elif output_format == "json":
98
+ def _save_hf_dataset_as_json(dataset: Dataset, output_path: str) -> None:
99
+ """Saves a Hugging Face `Dataset` object to json format."""
100
+ dataset.to_json(output_path)
101
+
102
+ _save_hf_dataset_as_json(hf_dataset, output_path)
103
+ elif output_format == "csv":
104
+ def _save_hf_dataset_as_csv(dataset: Dataset, output_path: str) -> None:
105
+ """Saves a Hugging Face `Dataset` object to csv format."""
106
+ dataset.to_csv(output_path)
107
+
108
+ _save_hf_dataset_as_csv(hf_dataset, output_path)
109
+ elif output_format == "sql":
110
+ def _save_hf_dataset_as_sql(dataset: Dataset, output_path: str) -> None:
111
+ """Saves a Hugging Face `Dataset` object to sql format."""
112
+ dataset.to_sql(output_path)
113
+
114
+ _save_hf_dataset_as_sql(hf_dataset, output_path)
115
+ elif output_format == "arrow":
116
+ def _save_hf_dataset_as_arrow(dataset: Dataset, output_path: str) -> None:
117
+ """Saves a Hugging Face `Dataset` object in Apache Arrow format."""
118
+ dataset.save_to_disk(output_path)
119
+
120
+ _save_hf_dataset_as_arrow(hf_dataset, output_path)
121
+ else:
122
+ raise ValueError(f"Unsupported output format: {output_format}")
123
+ return
124
+
125
+ def _from_hf_dataset_to_dict(dataset: Dataset) -> Dict[str, Any]:
126
+ """Converts a Hugging Face `Dataset` object to a dictionary."""
127
+ return dataset.to_dict()
128
+
129
+ def _from_dict_to_hf_dataset(data: Dict[str, Any], image_columns: list[str] = ['image'], audio_columns: list[str] = ['audio']) -> Dataset:
130
+ """Converts a dictionary to a Hugging Face `Dataset` object with casting for image and audio columns."""
131
+ dataset = Dataset.from_dict(data)
132
+
133
+ # Cast image columns
134
+ for column in image_columns:
135
+ if column in dataset.column_names:
136
+ dataset = dataset.cast_column(column, Image())
137
+
138
+ # Cast audio columns
139
+ for column in audio_columns:
140
+ if column in dataset.column_names:
141
+ dataset = dataset.cast_column(column, Audio(mono=False))
142
+
143
+ return dataset
144
+
@@ -0,0 +1,10 @@
1
+ """This module defines a pydra API for the input output tasks."""
2
+ import pydra
3
+
4
+ from senselab.utils.tasks.input_output import push_dataset_to_hub, read_dataset_from_disk, read_dataset_from_hub, read_files_from_disk, save_dataset_to_disk
5
+
6
+ read_files_from_disk_pt = pydra.mark.task(read_files_from_disk)
7
+ read_dataset_from_disk_pt = pydra.mark.task(read_dataset_from_disk)
8
+ read_dataset_from_hub_pt = pydra.mark.task(read_dataset_from_hub)
9
+ push_dataset_to_hub_pt = pydra.mark.task(push_dataset_to_hub)
10
+ save_dataset_to_disk_pt = pydra.mark.task(save_dataset_to_disk)
File without changes