tau-eval 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.
Files changed (33) hide show
  1. tau_eval-0.1.0/PKG-INFO +170 -0
  2. tau_eval-0.1.0/README.md +127 -0
  3. tau_eval-0.1.0/pyproject.toml +109 -0
  4. tau_eval-0.1.0/setup.cfg +4 -0
  5. tau_eval-0.1.0/tau_eval/__init__.py +18 -0
  6. tau_eval-0.1.0/tau_eval/experiment.py +231 -0
  7. tau_eval-0.1.0/tau_eval/logger.py +28 -0
  8. tau_eval-0.1.0/tau_eval/metrics/bertscore.py +35 -0
  9. tau_eval-0.1.0/tau_eval/metrics/cola.py +89 -0
  10. tau_eval-0.1.0/tau_eval/metrics/luar.py +54 -0
  11. tau_eval-0.1.0/tau_eval/metrics/meteor.py +46 -0
  12. tau_eval-0.1.0/tau_eval/metrics/nli.py +78 -0
  13. tau_eval-0.1.0/tau_eval/metrics/perplexity.py +24 -0
  14. tau_eval-0.1.0/tau_eval/metrics/rouge.py +28 -0
  15. tau_eval-0.1.0/tau_eval/metrics/sbert.py +52 -0
  16. tau_eval-0.1.0/tau_eval/models/__init__.py +4 -0
  17. tau_eval-0.1.0/tau_eval/models/anonymizer.py +10 -0
  18. tau_eval-0.1.0/tau_eval/models/authorship.py +129 -0
  19. tau_eval-0.1.0/tau_eval/models/dummy.py +12 -0
  20. tau_eval-0.1.0/tau_eval/models/llm_api.py +107 -0
  21. tau_eval-0.1.0/tau_eval/models/presidio.py +340 -0
  22. tau_eval-0.1.0/tau_eval/models/styleremix.py +163 -0
  23. tau_eval-0.1.0/tau_eval/tasks/__init__.py +5 -0
  24. tau_eval-0.1.0/tau_eval/tasks/customtask.py +14 -0
  25. tau_eval-0.1.0/tau_eval/tasks/deidentification.py +113 -0
  26. tau_eval-0.1.0/tau_eval/utils.py +273 -0
  27. tau_eval-0.1.0/tau_eval/visualization.py +633 -0
  28. tau_eval-0.1.0/tau_eval.egg-info/PKG-INFO +170 -0
  29. tau_eval-0.1.0/tau_eval.egg-info/SOURCES.txt +31 -0
  30. tau_eval-0.1.0/tau_eval.egg-info/dependency_links.txt +1 -0
  31. tau_eval-0.1.0/tau_eval.egg-info/requires.txt +28 -0
  32. tau_eval-0.1.0/tau_eval.egg-info/top_level.txt +1 -0
  33. tau_eval-0.1.0/tests/test_metrics.py +0 -0
@@ -0,0 +1,170 @@
1
+ Metadata-Version: 2.4
2
+ Name: tau-eval
3
+ Version: 0.1.0
4
+ Summary: Text Anonymization Evaluation Library
5
+ Author-email: Gabriel Loiseau <gabriel.loiseau@hornetsecurity.com>
6
+ Maintainer-email: Gabriel Loiseau <gabriel.loiseau@hornetsecurity.com>
7
+ License-Expression: GPL-3.0
8
+ Keywords: Text anonymization,evaluation,NLP
9
+ Classifier: Intended Audience :: Science/Research
10
+ Classifier: Programming Language :: Python :: 3.9
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
15
+ Requires-Python: >=3.9
16
+ Description-Content-Type: text/markdown
17
+ Requires-Dist: transformers<5.0.0,>=4.48.0
18
+ Requires-Dist: sentence-transformers>=3.3.1
19
+ Requires-Dist: torch>=2.5.0
20
+ Requires-Dist: evaluate>=0.4.1
21
+ Requires-Dist: datasets>=2.14.4
22
+ Requires-Dist: huggingface-hub>=0.20.0
23
+ Requires-Dist: tasknet>=1.57.0
24
+ Requires-Dist: tasksource>=0.0.47
25
+ Requires-Dist: ipywidgets>=8.1.5
26
+ Requires-Dist: ipykernel>=6.29.5
27
+ Requires-Dist: rich>=14.0.0
28
+ Requires-Dist: accelerate>=1.6.0
29
+ Requires-Dist: bert-score>=0.3.13
30
+ Requires-Dist: nltk>=3.9.1
31
+ Requires-Dist: rouge-score>=0.1.2
32
+ Requires-Dist: faker>=37.1.0
33
+ Requires-Dist: presidio-analyzer>=2.2.358
34
+ Requires-Dist: presidio-anonymizer>=2.2.358
35
+ Requires-Dist: pip>=25.0.1
36
+ Requires-Dist: pytest>=8.3.5
37
+ Provides-Extra: tests
38
+ Requires-Dist: pytest; extra == "tests"
39
+ Requires-Dist: accelerate>=0.20.3; extra == "tests"
40
+ Provides-Extra: quality
41
+ Requires-Dist: ruff; extra == "quality"
42
+ Requires-Dist: pyyaml>=5.3.1; extra == "quality"
43
+
44
+ # 𝜏 Tau-Eval: A Unified Evaluation Framework for Useful and Private Text Anonymization
45
+
46
+ **Tau-Eval** is a user-friendly, modular, and customizable Python library designed to benchmark and evaluate text anonymization algorithms. It enables granular analysis of anonymization impacts from both privacy and utility perspectives. Tau-Eval seamlessly integrates with [LiteLLM](https://www.litellm.ai/) and [🤗 Hugging Face](https://huggingface.co/) to support a wide range of datasets, models, and evaluation metrics.
47
+
48
+ <div align="center">
49
+
50
+ [![GNU-GPLv3](https://img.shields.io/badge/license-%20%20GNU%20GPLv3%20-green?style=plastic)](LICENSE)
51
+ [![v0.0.3](https://img.shields.io/badge/pypi-v0.0.3-orange)](https://pypi.org/project/xxx/xxx/)
52
+ [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue)](https://www.python.org/downloads/)
53
+ [![Tutorials](https://img.shields.io/badge/tutorials-colab-orange)](https://github.com/.../tree/main/examples)
54
+
55
+ </div>
56
+
57
+
58
+ ## Installation
59
+
60
+ ### From PyPI
61
+
62
+ Install Tau-Eval via pip:
63
+
64
+ ```
65
+ pip install tau-eval
66
+ ```
67
+
68
+ ### From source
69
+
70
+ To install from source:
71
+
72
+ 1) Clone this repository on your own path:
73
+ ```
74
+ git clone https://github.com/gabrielloiseau/tau-eval.git
75
+ cd tau-eval
76
+ ```
77
+
78
+ 2) Create an environment with your own preferred package manager. We used [python 3.9](https://www.python.org/downloads/release/python-390/) and dependencies listed in [`pyproject.toml`](pyproject.toml). If you use [conda](https://docs.conda.io/en/latest/), you can just run the following commands from the root of the project:
79
+
80
+ ```
81
+ conda create --name taueval python=3.9 # create the environment
82
+ conda activate taueval # activate the environment
83
+ pip install --user -r pyproject.toml # install the required packages
84
+ ```
85
+
86
+
87
+ ## Quickstart
88
+
89
+ Tau-Eval is designed for flexibility. With just a few lines of code, you can set up and run evaluations.
90
+
91
+ #### 1. Define Your Anonymization Model
92
+
93
+ Create a custom anonymization model by extending the Anonymizer interface:
94
+ ```python
95
+ from tau_eval.models import Anonymizer
96
+
97
+ class TestModel(Anonymizer):
98
+ def __init__(self):
99
+ self.name = "Test Model"
100
+
101
+ def anonymize(self, text: str) -> str:
102
+ # Implement anonymization logic
103
+ return text
104
+
105
+ def anonymize_batch(self, texts: list[str]) -> list[str]:
106
+ # Batch processing
107
+ return texts
108
+
109
+ ```
110
+ Or use prebuilt models from `tau_eval.models`.
111
+
112
+ #### 2. Configure Evaluation Metrics
113
+ Use built-in metrics from `tau_eval.metrics` or define your own following this signature:
114
+ ```python
115
+ Callable[[str | list[str], str | list[str]], dict]
116
+ ```
117
+ This allows complete control over what and how you evaluate.
118
+
119
+ #### 3. Instantiate Tasks
120
+ Tasks can be created using prebuilt options in `tau_eval.tasks`, or customized using `CustomTask`. Tau-Eval also supports [tasksource](https://github.com/sileod/tasksource) for dataset integration.
121
+
122
+ #### 4. Configure and Run Your Experiment
123
+ Define an experiment configuration:
124
+ ```python
125
+ from tau_eval.config import ExperimentConfig
126
+
127
+ config = ExperimentConfig(
128
+ exp_name="test-experiment",
129
+ classifier_name="medicalai/ClinicalBERT",
130
+ train_task_models=True,
131
+ train_with_generations=False,
132
+ )
133
+ ```
134
+ Run the experiment:
135
+ ```python
136
+ from tau_eval.experiment import Experiment
137
+
138
+ Experiment(
139
+ models=[m1, m2, m3, m4, m5],
140
+ metrics=["bertscore"],
141
+ tasks=[mednli, pii],
142
+ config=config
143
+ ).run()
144
+ ```
145
+ #### 5. Visualize Results
146
+
147
+ Tau-Eval includes built-in visualization tools to compare model anonymization strategies and evaluation results. You can find them with `tau_eval.visualization`.
148
+
149
+
150
+ ## Tutorials
151
+
152
+ You can find our tutorials to learn how to better leverage **Tau-Eval** in the [`examples/`]() folder.
153
+
154
+ There you can also find a set of interesting case studies using real-world datasets! :chart_with_upwards_trend:
155
+
156
+
157
+
158
+ ## Contributors
159
+
160
+ - **[Gabriel Loiseau](https://gabrielloiseau.github.io)**, *Hornetsecurty, Inria Lille*
161
+
162
+
163
+ ## Citation
164
+
165
+ If you use 𝜏 **Tau-Eval** in your work, please cite our paper as follows:
166
+
167
+ ```
168
+
169
+
170
+ ```
@@ -0,0 +1,127 @@
1
+ # 𝜏 Tau-Eval: A Unified Evaluation Framework for Useful and Private Text Anonymization
2
+
3
+ **Tau-Eval** is a user-friendly, modular, and customizable Python library designed to benchmark and evaluate text anonymization algorithms. It enables granular analysis of anonymization impacts from both privacy and utility perspectives. Tau-Eval seamlessly integrates with [LiteLLM](https://www.litellm.ai/) and [🤗 Hugging Face](https://huggingface.co/) to support a wide range of datasets, models, and evaluation metrics.
4
+
5
+ <div align="center">
6
+
7
+ [![GNU-GPLv3](https://img.shields.io/badge/license-%20%20GNU%20GPLv3%20-green?style=plastic)](LICENSE)
8
+ [![v0.0.3](https://img.shields.io/badge/pypi-v0.0.3-orange)](https://pypi.org/project/xxx/xxx/)
9
+ [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue)](https://www.python.org/downloads/)
10
+ [![Tutorials](https://img.shields.io/badge/tutorials-colab-orange)](https://github.com/.../tree/main/examples)
11
+
12
+ </div>
13
+
14
+
15
+ ## Installation
16
+
17
+ ### From PyPI
18
+
19
+ Install Tau-Eval via pip:
20
+
21
+ ```
22
+ pip install tau-eval
23
+ ```
24
+
25
+ ### From source
26
+
27
+ To install from source:
28
+
29
+ 1) Clone this repository on your own path:
30
+ ```
31
+ git clone https://github.com/gabrielloiseau/tau-eval.git
32
+ cd tau-eval
33
+ ```
34
+
35
+ 2) Create an environment with your own preferred package manager. We used [python 3.9](https://www.python.org/downloads/release/python-390/) and dependencies listed in [`pyproject.toml`](pyproject.toml). If you use [conda](https://docs.conda.io/en/latest/), you can just run the following commands from the root of the project:
36
+
37
+ ```
38
+ conda create --name taueval python=3.9 # create the environment
39
+ conda activate taueval # activate the environment
40
+ pip install --user -r pyproject.toml # install the required packages
41
+ ```
42
+
43
+
44
+ ## Quickstart
45
+
46
+ Tau-Eval is designed for flexibility. With just a few lines of code, you can set up and run evaluations.
47
+
48
+ #### 1. Define Your Anonymization Model
49
+
50
+ Create a custom anonymization model by extending the Anonymizer interface:
51
+ ```python
52
+ from tau_eval.models import Anonymizer
53
+
54
+ class TestModel(Anonymizer):
55
+ def __init__(self):
56
+ self.name = "Test Model"
57
+
58
+ def anonymize(self, text: str) -> str:
59
+ # Implement anonymization logic
60
+ return text
61
+
62
+ def anonymize_batch(self, texts: list[str]) -> list[str]:
63
+ # Batch processing
64
+ return texts
65
+
66
+ ```
67
+ Or use prebuilt models from `tau_eval.models`.
68
+
69
+ #### 2. Configure Evaluation Metrics
70
+ Use built-in metrics from `tau_eval.metrics` or define your own following this signature:
71
+ ```python
72
+ Callable[[str | list[str], str | list[str]], dict]
73
+ ```
74
+ This allows complete control over what and how you evaluate.
75
+
76
+ #### 3. Instantiate Tasks
77
+ Tasks can be created using prebuilt options in `tau_eval.tasks`, or customized using `CustomTask`. Tau-Eval also supports [tasksource](https://github.com/sileod/tasksource) for dataset integration.
78
+
79
+ #### 4. Configure and Run Your Experiment
80
+ Define an experiment configuration:
81
+ ```python
82
+ from tau_eval.config import ExperimentConfig
83
+
84
+ config = ExperimentConfig(
85
+ exp_name="test-experiment",
86
+ classifier_name="medicalai/ClinicalBERT",
87
+ train_task_models=True,
88
+ train_with_generations=False,
89
+ )
90
+ ```
91
+ Run the experiment:
92
+ ```python
93
+ from tau_eval.experiment import Experiment
94
+
95
+ Experiment(
96
+ models=[m1, m2, m3, m4, m5],
97
+ metrics=["bertscore"],
98
+ tasks=[mednli, pii],
99
+ config=config
100
+ ).run()
101
+ ```
102
+ #### 5. Visualize Results
103
+
104
+ Tau-Eval includes built-in visualization tools to compare model anonymization strategies and evaluation results. You can find them with `tau_eval.visualization`.
105
+
106
+
107
+ ## Tutorials
108
+
109
+ You can find our tutorials to learn how to better leverage **Tau-Eval** in the [`examples/`]() folder.
110
+
111
+ There you can also find a set of interesting case studies using real-world datasets! :chart_with_upwards_trend:
112
+
113
+
114
+
115
+ ## Contributors
116
+
117
+ - **[Gabriel Loiseau](https://gabrielloiseau.github.io)**, *Hornetsecurty, Inria Lille*
118
+
119
+
120
+ ## Citation
121
+
122
+ If you use 𝜏 **Tau-Eval** in your work, please cite our paper as follows:
123
+
124
+ ```
125
+
126
+
127
+ ```
@@ -0,0 +1,109 @@
1
+ [project]
2
+ name = "tau-eval"
3
+ version = "0.1.0"
4
+ description = "Text Anonymization Evaluation Library"
5
+ license = "GPL-3.0"
6
+ readme = "README.md"
7
+ authors = [
8
+ { name = "Gabriel Loiseau", email = "gabriel.loiseau@hornetsecurity.com" },
9
+ ]
10
+ maintainers = [
11
+ { name = "Gabriel Loiseau", email = "gabriel.loiseau@hornetsecurity.com" },
12
+ ]
13
+ requires-python = ">=3.9"
14
+ keywords = [
15
+ "Text anonymization",
16
+ "evaluation",
17
+ "NLP",
18
+ ]
19
+ classifiers = [
20
+ "Intended Audience :: Science/Research",
21
+ "Programming Language :: Python :: 3.9",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
26
+ ]
27
+ dependencies = [
28
+ "transformers>=4.48.0,<5.0.0",
29
+ "sentence-transformers>=3.3.1",
30
+ "torch>=2.5.0",
31
+ "evaluate>=0.4.1",
32
+ "datasets>=2.14.4",
33
+ "huggingface-hub>=0.20.0",
34
+ "tasknet>=1.57.0",
35
+ "tasksource>=0.0.47",
36
+ "ipywidgets>=8.1.5",
37
+ "ipykernel>=6.29.5",
38
+ "rich>=14.0.0",
39
+ "accelerate>=1.6.0",
40
+ "bert-score>=0.3.13",
41
+ "nltk>=3.9.1",
42
+ "rouge-score>=0.1.2",
43
+ "faker>=37.1.0",
44
+ "presidio-analyzer>=2.2.358",
45
+ "presidio-anonymizer>=2.2.358",
46
+ "pip>=25.0.1",
47
+ "pytest>=8.3.5",
48
+ ]
49
+
50
+
51
+ [project.optional-dependencies]
52
+ tests = [
53
+ "pytest",
54
+ "accelerate>=0.20.3",
55
+ ]
56
+ quality = [
57
+ "ruff",
58
+ "pyyaml>=5.3.1",
59
+ ]
60
+
61
+
62
+ [tool.ruff]
63
+ line-length = 119
64
+ fix = true
65
+
66
+ [tool.ruff.lint]
67
+ # Never enforce `E501` (line length violations).
68
+ ignore = ["C901", "E501", "E741", "F402", "F823", "C416" ]
69
+ select = ["C", "E", "F", "I", "W"]
70
+
71
+ [tool.ruff.lint.per-file-ignores]
72
+ "examples/**" = [
73
+ # Ignore `E402` (import violations) in all examples
74
+ "E402",
75
+ # Ignore missing required imports
76
+ "I002"
77
+ ]
78
+ "docs/**" = [
79
+ # Ignore missing required imports
80
+ "I002"
81
+ ]
82
+
83
+ [tool.ruff.lint.isort]
84
+ lines-after-imports = 2
85
+ known-first-party = ["transformers"]
86
+
87
+ [tool.ruff.format]
88
+ # Like Black, use double quotes for strings.
89
+ quote-style = "double"
90
+
91
+ # Like Black, indent with spaces, rather than tabs.
92
+ indent-style = "space"
93
+
94
+ # Like Black, respect magic trailing commas.
95
+ skip-magic-trailing-comma = false
96
+
97
+ # Like Black, automatically detect the appropriate line ending.
98
+ line-ending = "auto"
99
+
100
+
101
+ [tool.pytest.ini_options]
102
+ testpaths = [
103
+ "tests"
104
+ ]
105
+ addopts = "--strict-markers -m 'not slow'"
106
+ markers = [
107
+ "slow: marks tests as slow"
108
+ ]
109
+
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,18 @@
1
+ """Top-level package for tau-eval."""
2
+
3
+ from . import (
4
+ logger,
5
+ models,
6
+ utils,
7
+ )
8
+ from .experiment import Experiment, ExperimentConfig
9
+
10
+
11
+ __all__ = [
12
+ "Experiment",
13
+ "ExperimentConfig",
14
+ "models",
15
+ "utils",
16
+ "logger",
17
+ ]
18
+ __version__ = "0.1.0"
@@ -0,0 +1,231 @@
1
+ import json
2
+ import os
3
+ from dataclasses import dataclass, field
4
+
5
+ import pandas as pd
6
+ from rich.table import Table
7
+ from tasknet import Task
8
+
9
+ from tau_eval.tasks.customtask import CustomTask
10
+
11
+ from .logger import logger
12
+ from .models import Anonymizer
13
+ from .utils import run_models_on_custom_task, run_models_on_task
14
+
15
+
16
+ def rich_display_dataframe(df, title="Dataframe") -> None:
17
+ """Display dataframe as table using rich library.
18
+ Args:
19
+ df (pd.DataFrame): dataframe to display
20
+ title (str, optional): title of the table. Defaults to "Dataframe".
21
+ Raises:
22
+ NotRenderableError: if dataframe cannot be rendered
23
+ Returns:
24
+ rich.table.Table: rich table
25
+ """
26
+ from rich import print
27
+
28
+ # ensure dataframe contains only string values
29
+ df = df.astype(str)
30
+
31
+ table = Table(title=title)
32
+ for col in df.columns:
33
+ table.add_column(col)
34
+ for row in df.values:
35
+ table.add_row(*row)
36
+ print(table)
37
+
38
+ @dataclass
39
+ class ExperimentConfig:
40
+ """Evaluation experiment config"""
41
+
42
+ exp_name: str = os.path.basename(__file__)[: -len(".py")]
43
+ classifier_name: str = "answerdotai/ModernBERT-base"
44
+ train_task_models: bool = False
45
+ train_with_generations: bool = False
46
+ device: str | None = "cuda"
47
+ classifier_args: dict = field(default_factory=dict)
48
+
49
+
50
+ class Experiment:
51
+ def __init__(
52
+ self,
53
+ models: list[Anonymizer],
54
+ metrics: list[str],
55
+ tasks: list[Task],
56
+ config: ExperimentConfig = ExperimentConfig(),
57
+ ):
58
+ self.models = models
59
+ self.metrics = metrics
60
+ self.tasks = tasks
61
+ self.args = config
62
+
63
+ def run(self, output_dir="", device="cuda"):
64
+ self.output_dir = output_dir
65
+ logger.info("Running experiment...")
66
+ out = {}
67
+
68
+ for i, task in enumerate(self.tasks):
69
+ logger.info(f"Running task: {i}")
70
+ if isinstance(task, CustomTask):
71
+ results = run_models_on_custom_task(self.models, task, self.metrics)
72
+ else:
73
+ results = run_models_on_task(
74
+ self.models,
75
+ task,
76
+ self.metrics,
77
+ self.args.classifier_name,
78
+ self.args.train_task_models,
79
+ self.args.train_with_generations,
80
+ device,
81
+ )
82
+ if hasattr(task, "name"):
83
+ out[f"{task.name}_{i}"] = results
84
+ else:
85
+ out[f"{task.__class__.__name__}_{i}"] = results
86
+
87
+ self.results = out
88
+ with open(self.output_dir, "w") as f:
89
+ json.dump(out, f)
90
+ logger.info("Results saved")
91
+
92
+ def summary(self, output_dir=None, to_rich=False):
93
+ if output_dir is not None:
94
+ results = json.load(open(output_dir))
95
+ else:
96
+ results = json.load(open(self.output_dir))
97
+
98
+ task_dataframes = {}
99
+
100
+ for task_name, task in results.items():
101
+ # Prepare columns for DataFrame
102
+ if isinstance(task, CustomTask):
103
+ columns = ["Model Name"] + task["metrics"]
104
+ rows = []
105
+
106
+ # Add original metrics
107
+ original_metrics = task.get("original_metrics", {})
108
+ if original_metrics:
109
+ original_row = ["Original"]
110
+ for m in task["metrics"]:
111
+ if m == "cola":
112
+ original_row.append(round(original_metrics.get("cola", 0), 4))
113
+ else:
114
+ original_row.append("-")
115
+ rows.append(original_row)
116
+ # Add rewritten metrics
117
+ for model_name, model in task.items():
118
+ if model_name in ["original_metrics", "metrics"]:
119
+ continue
120
+
121
+ row = [model_name]
122
+
123
+ for m in task["metrics"]:
124
+ row.append(round(model.get(m, 0), 4))
125
+
126
+ rows.append(row)
127
+
128
+ else:
129
+ columns = ["Model Name", "Accuracy", "F1"] + task["metrics"]
130
+ rows = []
131
+
132
+ # Add original metrics
133
+ original_metrics = task.get("original_metrics", {})
134
+ if original_metrics:
135
+ original_row = ["Original"]
136
+ original_row.append(round(original_metrics.get("test_accuracy", 0), 4))
137
+ original_row.append(round(original_metrics.get("test_f1", 0), 4))
138
+ for m in task["metrics"]:
139
+ if m == "cola":
140
+ original_row.append(round(original_metrics.get("cola", 0), 4))
141
+ else:
142
+ original_row.append("-")
143
+ rows.append(original_row)
144
+
145
+ # Add rewritten metrics
146
+ for model_name, model in task.items():
147
+ if model_name in ["original_metrics", "metrics"]:
148
+ continue
149
+
150
+ row = [model_name]
151
+ row.append(model.get("test_accuracy", 0))
152
+ row.append(model.get("test_f1", 0))
153
+
154
+ for m in task["metrics"]:
155
+ row.append(round(model.get(m, 0), 4))
156
+
157
+ rows.append(row)
158
+
159
+ # Create DataFrame for the task
160
+ task_dataframe = pd.DataFrame(rows, columns=columns)
161
+ task_dataframes[task_name] = task_dataframe
162
+ if to_rich:
163
+ for t, value in task_dataframes.items():
164
+ rich_display_dataframe(value)
165
+
166
+ return task_dataframes
167
+
168
+ def plot_tradeoff(
169
+ self,
170
+ task_name1,
171
+ target1,
172
+ task_name2,
173
+ target2,
174
+ x_label="Metric 1",
175
+ y_label="Metric 2",
176
+ title="Tradeoff Between Metrics",
177
+ figsize=(4, 4),
178
+ ):
179
+ from itertools import cycle
180
+
181
+ import matplotlib.pyplot as plt
182
+
183
+ task1 = self.results[task_name1]
184
+ task2 = self.results[task_name2]
185
+ model_names = []
186
+ x_vals = []
187
+ y_vals = []
188
+
189
+ for model_name, model in task1.items():
190
+ model_names.append(model_name)
191
+ if model_name in ["original_metrics", "metrics"]:
192
+ continue
193
+ else:
194
+ x_vals.append(model.get(target1))
195
+
196
+ for model_name, model in task2.items():
197
+ model_names.append(model_name)
198
+ if model_name in ["original_metrics", "metrics"]:
199
+ continue
200
+ else:
201
+ y_vals.append(model.get(target2))
202
+
203
+ markers = cycle(
204
+ ["o", "s", "D", "^", "v", "<", ">", "p", "*", "h", "H"]
205
+ ) # Cycle through different marker styles
206
+ plt.figure(figsize=figsize)
207
+
208
+ for i, (x_val, y_val) in enumerate(zip(x_vals, y_vals)):
209
+ marker = next(markers)
210
+ plt.scatter(
211
+ x_val,
212
+ y_val,
213
+ label=model_names[i] if model_names else f"Model {i + 1}",
214
+ marker=marker,
215
+ s=100,
216
+ alpha=0.7,
217
+ edgecolor="k",
218
+ )
219
+ # Annotate points with model names if provided
220
+ if model_names:
221
+ plt.text(x_val, y_val, model_names[i], fontsize=9, ha="right", va="bottom")
222
+
223
+ plt.axhline(0, color="gray", linewidth=0.5, linestyle="--")
224
+ plt.axvline(0, color="gray", linewidth=0.5, linestyle="--")
225
+ plt.grid(alpha=0.3)
226
+
227
+ plt.xlabel(x_label)
228
+ plt.ylabel(y_label)
229
+ plt.title(title)
230
+ plt.legend(loc="best", fontsize=9)
231
+ plt.tight_layout()
@@ -0,0 +1,28 @@
1
+ import logging
2
+
3
+ from rich.logging import RichHandler
4
+
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+ # the handler determines where the logs go: stdout/file
9
+ shell_handler = RichHandler()
10
+ file_handler = logging.FileHandler("debug.log")
11
+
12
+ logger.setLevel(logging.DEBUG)
13
+ shell_handler.setLevel(logging.DEBUG)
14
+ file_handler.setLevel(logging.DEBUG)
15
+
16
+ # the formatter determines what our logs will look like
17
+ fmt_shell = "%(message)s"
18
+ fmt_file = "%(levelname)s %(asctime)s [%(filename)s:%(funcName)s:%(lineno)d] %(message)s"
19
+
20
+ shell_formatter = logging.Formatter(fmt_shell)
21
+ file_formatter = logging.Formatter(fmt_file)
22
+
23
+ # here we hook everything together
24
+ shell_handler.setFormatter(shell_formatter)
25
+ file_handler.setFormatter(file_formatter)
26
+
27
+ logger.addHandler(shell_handler)
28
+ logger.addHandler(file_handler)