Skip to content

Commit 997c6b6

Browse files
authored
Merge pull request #1675 from Atenrev/concon_support
Added concon dataset and benchmarks.
2 parents d0cad91 + 05c66e4 commit 997c6b6

5 files changed

Lines changed: 393 additions & 0 deletions

File tree

avalanche/benchmarks/classic/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@
1313
from .clear import *
1414
from .stream51 import *
1515
from .ex_model import *
16+
from .concon import *
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
import random
2+
3+
from pathlib import Path
4+
from typing import Optional, Union, Any, List, TypeVar
5+
6+
from torchvision import transforms
7+
8+
from avalanche.benchmarks.utils.data import AvalancheDataset
9+
from avalanche.benchmarks.utils.classification_dataset import _as_taskaware_supervised_classification_dataset
10+
from avalanche.benchmarks import benchmark_from_datasets, CLScenario
11+
12+
from avalanche.benchmarks.datasets.concon import ConConDataset
13+
14+
15+
TCLDataset = TypeVar("TCLDataset", bound="AvalancheDataset")
16+
17+
18+
_default_train_transform = transforms.Compose(
19+
[
20+
transforms.ToTensor(),
21+
transforms.Normalize(
22+
mean=[0.5, 0.5, 0.5],
23+
std=[0.5, 0.5, 0.5]
24+
)
25+
]
26+
)
27+
28+
_default_eval_transform = transforms.Compose(
29+
[
30+
transforms.ToTensor(),
31+
transforms.Normalize(
32+
mean=[0.5, 0.5, 0.5],
33+
std=[0.5, 0.5, 0.5]
34+
)
35+
]
36+
)
37+
38+
39+
def build_concon_scenario(
40+
list_train_dataset: List[TCLDataset],
41+
list_test_dataset: List[TCLDataset],
42+
seed: Optional[int] = None,
43+
n_experiences: int = 3,
44+
shuffle_order: bool = False,
45+
):
46+
if shuffle_order and not n_experiences == 1:
47+
random.seed(seed)
48+
random.shuffle(list_train_dataset)
49+
random.seed(seed)
50+
random.shuffle(list_test_dataset)
51+
52+
if n_experiences == 1:
53+
new_list_train_dataset = []
54+
new_list_train_dataset.append(list_train_dataset[0])
55+
56+
for i in range(1, len(list_train_dataset)):
57+
new_list_train_dataset[0] = new_list_train_dataset[0].concat(
58+
list_train_dataset[i])
59+
60+
list_train_dataset = new_list_train_dataset
61+
62+
new_list_test_dataset = []
63+
new_list_test_dataset.append(list_test_dataset[0])
64+
65+
for i in range(1, len(list_test_dataset)):
66+
new_list_test_dataset[0] = new_list_test_dataset[0].concat(
67+
list_test_dataset[i])
68+
69+
list_test_dataset = new_list_test_dataset
70+
71+
return benchmark_from_datasets(
72+
train=list_train_dataset,
73+
test=list_test_dataset
74+
)
75+
76+
77+
def ConConDisjoint(
78+
n_experiences: int,
79+
*,
80+
seed: Optional[int] = None,
81+
shuffle_order: bool = False,
82+
train_transform: Optional[Any] = _default_train_transform,
83+
eval_transform: Optional[Any] = _default_eval_transform,
84+
dataset_root: Optional[Union[str, Path]] = None,
85+
) -> CLScenario:
86+
"""
87+
Creates a ConCon Disjoint benchmark.
88+
89+
If the dataset is not present in the computer, this method will
90+
automatically download and store it.
91+
92+
The returned benchmark will be a domain-incremental one, where each task
93+
is a different domain with different confounders. In this setting,
94+
task-specific confounders never appear in other tasks.
95+
96+
The benchmark instance returned by this method will have two fields,
97+
`train_stream` and `test_stream`, which can be iterated to obtain
98+
training and test :class:`Experience`. Each Experience contains the
99+
`dataset` and the associated task label.
100+
101+
:param dataset_root: The root directory of the dataset.
102+
:param n_experiences: The number of experiences to use.
103+
:param seed: The seed to use.
104+
:param shuffle_order: Whether to shuffle the order of the experiences.
105+
:param train_transform: The training transform to use.
106+
:param eval_transform: The evaluation transform to use.
107+
108+
:returns: The ConCon Disjoint benchmark.
109+
"""
110+
assert n_experiences == 3 or n_experiences == 1, "n_experiences must be 1 or 3 for ConCon Disjoint"
111+
list_train_dataset = []
112+
list_test_dataset = []
113+
114+
for i in range(3):
115+
train_dataset = ConConDataset("disjoint", i, root=dataset_root, train=True)
116+
test_dataset = ConConDataset("disjoint", i, root=dataset_root, train=False)
117+
train_dataset = _as_taskaware_supervised_classification_dataset(
118+
train_dataset,
119+
transform=train_transform
120+
)
121+
test_dataset = _as_taskaware_supervised_classification_dataset(
122+
test_dataset,
123+
transform=eval_transform
124+
)
125+
list_train_dataset.append(train_dataset)
126+
list_test_dataset.append(test_dataset)
127+
128+
return build_concon_scenario(
129+
list_train_dataset,
130+
list_test_dataset,
131+
seed=seed,
132+
n_experiences=n_experiences,
133+
shuffle_order=shuffle_order
134+
)
135+
136+
137+
def ConConStrict(
138+
n_experiences: int,
139+
*,
140+
seed: Optional[int] = None,
141+
shuffle_order: bool = False,
142+
train_transform: Optional[Any] = _default_train_transform,
143+
eval_transform: Optional[Any] = _default_eval_transform,
144+
dataset_root: Optional[Union[str, Path]] = None,
145+
) -> CLScenario:
146+
"""
147+
Creates a ConCon Strict benchmark.
148+
149+
If the dataset is not present in the computer, this method will
150+
automatically download and store it.
151+
152+
The returned benchmark will be a domain-incremental one, where each task
153+
is a different domain with different confounders. In this setting,
154+
task-specific confounders may appear in other tasks as random features
155+
in both positive and negative samples.
156+
157+
The benchmark instance returned by this method will have two fields,
158+
`train_stream` and `test_stream`, which can be iterated to obtain
159+
training and test :class:`Experience`. Each Experience contains the
160+
`dataset` and the associated task label.
161+
162+
:param dataset_root: The root directory of the dataset.
163+
:param n_experiences: The number of experiences to use.
164+
:param seed: The seed to use.
165+
:param shuffle_order: Whether to shuffle the order of the experiences.
166+
:param train_transform: The training transform to use.
167+
:param eval_transform: The evaluation transform to use.
168+
169+
:returns: The ConCon Strict benchmark.
170+
"""
171+
assert n_experiences == 3 or n_experiences == 1, "n_experiences must be 1 or 3 for ConCon Disjoint"
172+
list_train_dataset = []
173+
list_test_dataset = []
174+
175+
for i in range(3):
176+
train_dataset = ConConDataset("strict", i, root=dataset_root, train=True)
177+
test_dataset = ConConDataset("strict", i, root=dataset_root, train=False)
178+
train_dataset = _as_taskaware_supervised_classification_dataset(
179+
train_dataset,
180+
transform=train_transform
181+
)
182+
test_dataset = _as_taskaware_supervised_classification_dataset(
183+
test_dataset,
184+
transform=eval_transform
185+
)
186+
list_train_dataset.append(train_dataset)
187+
list_test_dataset.append(test_dataset)
188+
189+
return build_concon_scenario(
190+
list_train_dataset,
191+
list_test_dataset,
192+
seed=seed,
193+
n_experiences=n_experiences,
194+
shuffle_order=shuffle_order
195+
)
196+
197+
198+
def ConConUnconfounded(
199+
*,
200+
train_transform: Optional[Any] = _default_train_transform,
201+
eval_transform: Optional[Any] = _default_eval_transform,
202+
dataset_root: Optional[Union[str, Path]] = None,
203+
) -> CLScenario:
204+
"""
205+
Creates a ConCon Unconfounded benchmark.
206+
207+
If the dataset is not present in the computer, this method will
208+
automatically download and store it.
209+
210+
The returned benchmark will only contain one task, where no task-specific
211+
confounders are present.
212+
213+
The benchmark instance returned by this method will have two fields,
214+
`train_stream` and `test_stream`, which can be iterated to obtain
215+
training and test :class:`Experience`. Each Experience contains the
216+
`dataset` and the associated task label.
217+
218+
:param dataset_root: The root directory of the dataset.
219+
:param train_transform: The training transform to use.
220+
:param eval_transform: The evaluation transform to use.
221+
222+
:returns: The ConCon Unconfounded benchmark.
223+
"""
224+
train_dataset = []
225+
test_dataset = []
226+
227+
train_dataset.append(ConConDataset(
228+
"unconfounded", 0, root=dataset_root, train=True))
229+
test_dataset.append(ConConDataset(
230+
"unconfounded", 0, root=dataset_root, train=False))
231+
232+
train_dataset[0] = _as_taskaware_supervised_classification_dataset(
233+
train_dataset[0],
234+
transform=train_transform
235+
)
236+
237+
test_dataset[0] = _as_taskaware_supervised_classification_dataset(
238+
test_dataset[0],
239+
transform=eval_transform
240+
)
241+
242+
return benchmark_from_datasets(
243+
train=train_dataset,
244+
test=test_dataset
245+
)
246+
247+
248+
__all__ = [
249+
"ConConDisjoint",
250+
"ConConStrict",
251+
"ConConUnconfounded",
252+
]

avalanche/benchmarks/datasets/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@
1212
from .inaturalist import *
1313
from .penn_fudan import *
1414
from .clear import *
15+
from .concon import *
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .concon import *

0 commit comments

Comments
 (0)