Skip to content

Commit

Permalink
Implement ratio_max scaling for downsamplers (#541)
Browse files Browse the repository at this point in the history
Before, we only were able to enter ratios from 0-100 as percentages for
downsamplers. With this PR, we allow scaling by a ratio_max factor.
  • Loading branch information
MaxiBoether authored Jun 22, 2024
1 parent 70e2a57 commit 4d78544
Show file tree
Hide file tree
Showing 27 changed files with 109 additions and 40 deletions.
29 changes: 26 additions & 3 deletions modyn/config/schema/pipeline/config.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
from __future__ import annotations

from typing import Optional
from typing import Optional, Self

from modyn.config.schema.base_model import ModynBaseModel
from pydantic import Field
from pydantic import Field, model_validator

from .data import DataConfig
from .evaluation.config import EvaluationConfig
from .model import ModelConfig
from .model_storage import PipelineModelStorageConfig
from .sampling.config import SelectionStrategy
from .sampling.config import CoresetStrategyConfig, SelectionStrategy
from .sampling.downsampling_config import MultiDownsamplingConfig
from .training import TrainingConfig
from .trigger import TriggerConfig

Expand All @@ -32,3 +33,25 @@ class ModynPipelineConfig(ModynBaseModel):
trigger: TriggerConfig
selection_strategy: SelectionStrategy
evaluation: EvaluationConfig | None = Field(None)

@model_validator(mode="after")
def validate_bts_training_selection_works(self) -> Self:
# Validates that when using Downsampling with BtS, we choose a functional ratio
if isinstance(self.selection_strategy, CoresetStrategyConfig) and not isinstance(
self.selection_strategy.downsampling_config, MultiDownsamplingConfig
):
if not self.selection_strategy.downsampling_config.sample_then_batch: # bts
ratio = self.selection_strategy.downsampling_config.ratio
ratio_max = self.selection_strategy.downsampling_config.ratio_max
batch_size = self.training.batch_size

post_downsampling_size = max((ratio * batch_size) // ratio_max, 1)
if batch_size % post_downsampling_size != 0:
raise ValueError(
f"The target batch size of {batch_size} is not a multiple of the batch size "
+ f"after downsampling with ratio {ratio} a batch in BtS mode ({post_downsampling_size}). "
+ "We cannot accumulate batches. "
+ "Please choose the downsampling ratio and batch size such that this is possible."
)

return self
21 changes: 19 additions & 2 deletions modyn/config/schema/pipeline/sampling/downsampling_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,20 @@ class BaseDownsamplingConfig(ModynBaseModel):
),
)
ratio: int = Field(
description="Ratio post_sampling_size/pre_sampling_size. E.g. with 160 records and a ratio of 50 we keep 80.",
description=(
"Ratio post_sampling_size/pre_sampling_size * ratio_max. "
"For the default of ratio_max of 100, this implies percent, "
"e.g., with 160 records and a ratio of 50 we keep 80."
),
min=0,
max=100,
)
ratio_max: int = Field(
description=(
"Reference maximum ratio value. Defaults to 100, which implies percent."
" If you set this to 1000, ratio describes promille instead."
),
default=100,
min=1,
)
period: int = Field(
1,
Expand All @@ -34,6 +45,12 @@ class BaseDownsamplingConfig(ModynBaseModel):
min=0,
)

@model_validator(mode="after")
def validate_ratio(self) -> Self:
if self.ratio > self.ratio_max:
raise ValueError("ratio cannot be greater than ratio_max.")
return self


class UncertaintyDownsamplingConfig(BaseDownsamplingConfig):
"""Config for the Craig downsampling strategy."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def __init__(

self.downsampling_period = downsampling_config.period
self.downsampling_ratio = downsampling_config.ratio
self.ratio_max = downsampling_config.ratio_max

self.requires_remote_computation = True
self.maximum_keys_in_memory = maximum_keys_in_memory
Expand All @@ -60,6 +61,7 @@ def _compute_status_bar_scale(self) -> int:
def downsampling_params(self) -> dict:
config = {
"downsampling_ratio": self.downsampling_ratio,
"ratio_max": self.ratio_max,
"maximum_keys_in_memory": self.maximum_keys_in_memory,
"sample_then_batch": self.downsampling_mode == DownsamplingMode.SAMPLE_THEN_BATCH,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ def test_downsampling_params(il_training_config: ILTrainingConfig, data_config:

expected = {
"downsampling_ratio": 60,
"ratio_max": 100,
"maximum_keys_in_memory": maximum_keys_in_memory,
"sample_then_batch": False,
"il_model_id": 3,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ def test_switch_functions():
"downsampling_ratio": 50,
"maximum_keys_in_memory": 1000,
"sample_then_batch": True,
"ratio_max": 100,
}
assert downs.downsampling_strategy == "RemoteLossDownsampling"
assert downs.training_status_bar_scale == 50
Expand All @@ -98,6 +99,7 @@ def test_switch_functions():
"downsampling_ratio": 25,
"maximum_keys_in_memory": 1000,
"sample_then_batch": False,
"ratio_max": 100,
}
assert downs.downsampling_strategy == "RemoteGradNormDownsampling"
assert downs.training_status_bar_scale == 100
Expand Down Expand Up @@ -140,6 +142,7 @@ def test_double_threshold():
"downsampling_ratio": 50,
"maximum_keys_in_memory": 1000,
"sample_then_batch": True,
"ratio_max": 100,
}
assert downs.downsampling_strategy == "RemoteLossDownsampling"
assert downs.training_status_bar_scale == 50
Expand All @@ -152,6 +155,7 @@ def test_double_threshold():
"downsampling_ratio": 25,
"maximum_keys_in_memory": 1000,
"sample_then_batch": False,
"ratio_max": 100,
}
assert downs.downsampling_strategy == "RemoteGradNormDownsampling"
assert downs.training_status_bar_scale == 100
Expand Down Expand Up @@ -179,6 +183,7 @@ def test_wrong_trigger():
"downsampling_ratio": 50,
"maximum_keys_in_memory": 1000,
"sample_then_batch": True,
"ratio_max": 100,
}
assert downs.downsampling_strategy == "RemoteLossDownsampling"
assert downs.training_status_bar_scale == 50
Expand All @@ -195,6 +200,7 @@ def test_wrong_trigger():
"downsampling_ratio": 25,
"maximum_keys_in_memory": 1000,
"sample_then_batch": False,
"ratio_max": 100,
}
assert downs.downsampling_strategy == "RemoteGradNormDownsampling"
assert downs.training_status_bar_scale == 100
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def get_sampler_config(dummy_system_config: ModynConfig, balance=False):
"sample_then_batch": False,
"args": {},
"balance": balance,
"ratio_max": 100,
}
return (
0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
def test_batch_then_sample_general(dummy_system_config: ModynConfig):
downsampling_ratio = 50

params_from_selector = {"downsampling_ratio": downsampling_ratio}
params_from_selector = {"downsampling_ratio": downsampling_ratio, "ratio_max": 100}
sampler = AbstractRemoteDownsamplingStrategy(
154, 128, 64, params_from_selector, dummy_system_config.model_dump(by_alias=True), "cpu"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def get_sampler_config(modyn_config, balance=False):
"balance": balance,
"selection_batch": 64,
"greedy": "NaiveGreedy",
"ratio_max": 100,
}
return 0, 0, 0, params_from_selector, modyn_config.model_dump(by_alias=True), per_sample_loss_fct, "cpu"

Expand Down Expand Up @@ -347,7 +348,7 @@ def test_matching_results_with_deepcore(dummy_system_config: ModynConfig):
0,
0,
5,
{"downsampling_ratio": 20, "balance": False, "selection_batch": 64, "greedy": "NaiveGreedy"},
{"downsampling_ratio": 20, "balance": False, "selection_batch": 64, "greedy": "NaiveGreedy", "ratio_max": 100},
dummy_system_config.model_dump(by_alias=True),
BCEWithLogitsLoss(reduction="none"),
"cpu",
Expand Down Expand Up @@ -402,7 +403,7 @@ def test_matching_results_with_deepcore_permutation(dummy_system_config: ModynCo
0,
0,
5,
{"downsampling_ratio": 30, "balance": False, "selection_batch": 64, "greedy": "NaiveGreedy"},
{"downsampling_ratio": 30, "balance": False, "selection_batch": 64, "greedy": "NaiveGreedy", "ratio_max": 100},
dummy_system_config.model_dump(by_alias=True),
BCEWithLogitsLoss(reduction="none"),
"cpu",
Expand Down Expand Up @@ -461,7 +462,7 @@ def test_matching_results_with_deepcore_permutation_fancy_ids(dummy_system_confi
0,
0,
5,
{"downsampling_ratio": 50, "balance": False, "selection_batch": 64, "greedy": "NaiveGreedy"},
{"downsampling_ratio": 50, "balance": False, "selection_batch": 64, "greedy": "NaiveGreedy", "ratio_max": 100},
dummy_system_config.model_dump(by_alias=True),
BCEWithLogitsLoss(reduction="none"),
"cpu",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def get_sampler_config(modyn_config: ModynConfig, balance=False):
"sample_then_batch": False,
"args": {},
"balance": balance,
"ratio_max": 100,
}
return 0, 0, 0, params_from_selector, modyn_config.model_dump(by_alias=True), per_sample_loss_fct, "cpu"

Expand Down Expand Up @@ -185,7 +186,7 @@ def test_matching_results_with_deepcore(dummy_system_config: ModynConfig):
0,
0,
5,
{"downsampling_ratio": 10 * num_of_target_samples, "balance": False},
{"downsampling_ratio": 10 * num_of_target_samples, "balance": False, "ratio_max": 100},
dummy_system_config.model_dump(by_alias=True),
BCEWithLogitsLoss(reduction="none"),
"cpu",
Expand Down Expand Up @@ -237,7 +238,7 @@ def test_matching_results_with_deepcore_permutation_fancy_ids(dummy_system_confi
0,
0,
5,
{"downsampling_ratio": 50, "balance": False},
{"downsampling_ratio": 50, "balance": False, "ratio_max": 100},
dummy_system_config.model_dump(by_alias=True),
BCEWithLogitsLoss(reduction="none"),
"cpu",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def test_sample_shape_ce(dummy_system_config: ModynConfig):
downsampling_ratio = 50
per_sample_loss_fct = torch.nn.CrossEntropyLoss(reduction="none")

params_from_selector = {"downsampling_ratio": downsampling_ratio, "sample_then_batch": False}
params_from_selector = {"downsampling_ratio": downsampling_ratio, "sample_then_batch": False, "ratio_max": 100}
sampler = RemoteGradNormDownsampling(
0, 0, 0, params_from_selector, dummy_system_config.model_dump(by_alias=True), per_sample_loss_fct, "cpu"
)
Expand Down Expand Up @@ -45,7 +45,7 @@ def test_sample_shape_other_losses(dummy_system_config: ModynConfig):
downsampling_ratio = 50
per_sample_loss_fct = torch.nn.BCEWithLogitsLoss(reduction="none")

params_from_selector = {"downsampling_ratio": downsampling_ratio, "sample_then_batch": False}
params_from_selector = {"downsampling_ratio": downsampling_ratio, "sample_then_batch": False, "ratio_max": 100}
sampler = RemoteGradNormDownsampling(
0, 0, 0, params_from_selector, dummy_system_config.model_dump(by_alias=True), per_sample_loss_fct, "cpu"
)
Expand Down Expand Up @@ -84,6 +84,7 @@ def test_sampling_crossentropy(dummy_system_config: ModynConfig):
"downsampling_ratio": downsampling_ratio,
"replacement": False,
"sample_then_batch": False,
"ratio_max": 100,
}

# Here we use autograd since the number of classes is not provided
Expand Down Expand Up @@ -135,7 +136,7 @@ def test_sample_dict_input(dummy_system_config: ModynConfig):
model = DictLikeModel()
per_sample_loss_fct = torch.nn.CrossEntropyLoss(reduction="none")

params_from_selector = {"downsampling_ratio": 50, "sample_then_batch": False}
params_from_selector = {"downsampling_ratio": 50, "sample_then_batch": False, "ratio_max": 100}
sampler = RemoteGradNormDownsampling(
0, 0, 0, params_from_selector, dummy_system_config.model_dump(by_alias=True), per_sample_loss_fct, "cpu"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def get_sampler_config(modyn_config: ModynConfig, balance=False):
"sample_then_batch": False,
"args": {},
"balance": balance,
"ratio_max": 100,
}
return 0, 0, 0, params_from_selector, modyn_config.model_dump(by_alias=True), per_sample_loss_fct, "cpu"

Expand Down Expand Up @@ -137,7 +138,7 @@ def test_matching_results_with_deepcore(dummy_system_config: ModynConfig):
0,
0,
5,
{"downsampling_ratio": 10 * num_of_target_samples, "balance": False},
{"downsampling_ratio": 10 * num_of_target_samples, "balance": False, "ratio_max": 100},
dummy_system_config.model_dump(by_alias=True),
BCEWithLogitsLoss(reduction="none"),
"cpu",
Expand Down Expand Up @@ -166,7 +167,7 @@ def test_matching_results_with_deepcore_permutation_fancy_ids(dummy_system_confi
0,
0,
5,
{"downsampling_ratio": 50, "balance": False},
{"downsampling_ratio": 50, "balance": False, "ratio_max": 100},
dummy_system_config.model_dump(by_alias=True),
BCEWithLogitsLoss(reduction="none"),
"cpu",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def test_sample_shape(dummy_system_config: ModynConfig):
downsampling_ratio = 50
per_sample_loss_fct = torch.nn.CrossEntropyLoss(reduction="none")

params_from_selector = {"downsampling_ratio": downsampling_ratio, "sample_then_batch": False}
params_from_selector = {"downsampling_ratio": downsampling_ratio, "sample_then_batch": False, "ratio_max": 100}
sampler = RemoteLossDownsampling(
0, 0, 0, params_from_selector, dummy_system_config.model_dump(by_alias=True), per_sample_loss_fct, "cpu"
)
Expand All @@ -39,7 +39,7 @@ def test_sample_weights(dummy_system_config: ModynConfig):
downsampling_ratio = 50
per_sample_loss_fct = torch.nn.CrossEntropyLoss(reduction="none")

params_from_selector = {"downsampling_ratio": downsampling_ratio, "sample_then_batch": False}
params_from_selector = {"downsampling_ratio": downsampling_ratio, "sample_then_batch": False, "ratio_max": 100}
sampler = RemoteLossDownsampling(
0, 0, 0, params_from_selector, dummy_system_config.model_dump(by_alias=True), per_sample_loss_fct, "cpu"
)
Expand Down Expand Up @@ -67,7 +67,7 @@ def test_sample_loss_dependent_sampling(dummy_system_config: ModynConfig):
downsampling_ratio = 50
per_sample_loss_fct = torch.nn.MSELoss(reduction="none")

params_from_selector = {"downsampling_ratio": downsampling_ratio, "sample_then_batch": False}
params_from_selector = {"downsampling_ratio": downsampling_ratio, "sample_then_batch": False, "ratio_max": 100}
sampler = RemoteLossDownsampling(
0, 0, 0, params_from_selector, dummy_system_config.model_dump(by_alias=True), per_sample_loss_fct, "cpu"
)
Expand Down Expand Up @@ -116,7 +116,7 @@ def test_sample_dict_input(dummy_system_config: ModynConfig):
mymodel = DictLikeModel()
per_sample_loss_fct = torch.nn.CrossEntropyLoss(reduction="none")

params_from_selector = {"downsampling_ratio": 50, "sample_then_batch": False}
params_from_selector = {"downsampling_ratio": 50, "sample_then_batch": False, "ratio_max": 100}
sampler = RemoteLossDownsampling(
0, 0, 0, params_from_selector, dummy_system_config.model_dump(by_alias=True), per_sample_loss_fct, "cpu"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def dummy_init_params(dummy_system_config: ModynConfig):
"il_model_id": 2,
"downsampling_ratio": 50,
"sample_then_batch": False,
"ratio_max": 100,
}
modyn_config = dummy_system_config.model_dump(by_alias=True)
per_sample_loss_fct = torch.nn.CrossEntropyLoss(reduction="none")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def test_init(dummy_system_config: ModynConfig):
pipeline_id = 0
trigger_id = 0
batch_size = 32
params_from_selector = {"replacement": True, "downsampling_ratio": 50}
params_from_selector = {"replacement": True, "downsampling_ratio": 50, "ratio_max": 100}
per_sample_loss = None
device = "cpu"

Expand Down Expand Up @@ -41,7 +41,7 @@ def test_inform_samples(dummy_system_config: ModynConfig):
pipeline_id = 0
trigger_id = 0
batch_size = 32
params_from_selector = {"replacement": True, "downsampling_ratio": 50}
params_from_selector = {"replacement": True, "downsampling_ratio": 50, "ratio_max": 100}
per_sample_loss = None
device = "cpu"

Expand Down Expand Up @@ -76,7 +76,7 @@ def test_multiple_epochs_with_replacement(dummy_system_config: ModynConfig):
pipeline_id = 0
trigger_id = 0
batch_size = 32
params_from_selector = {"replacement": True, "downsampling_ratio": 50}
params_from_selector = {"replacement": True, "downsampling_ratio": 50, "ratio_max": 100}
per_sample_loss = None
device = "cpu"

Expand Down Expand Up @@ -110,7 +110,7 @@ def test_multiple_epochs_without_replacement(dummy_system_config: ModynConfig):
pipeline_id = 0
trigger_id = 0
batch_size = 32
params_from_selector = {"replacement": False, "downsampling_ratio": 50}
params_from_selector = {"replacement": False, "downsampling_ratio": 50, "ratio_max": 100}
per_sample_loss = None
device = "cpu"

Expand Down Expand Up @@ -171,7 +171,7 @@ def test_multiple_epochs_without_replacement_leftover_data(dummy_system_config:
pipeline_id = 0
trigger_id = 0
batch_size = 32
params_from_selector = {"replacement": False, "downsampling_ratio": 40}
params_from_selector = {"replacement": False, "downsampling_ratio": 40, "ratio_max": 100}
per_sample_loss = None
device = "cpu"

Expand Down Expand Up @@ -207,7 +207,7 @@ def test_multiple_epochs_empty_without_replacement_leftover_data(dummy_system_co
pipeline_id = 0
trigger_id = 0
batch_size = 32
params_from_selector = {"replacement": False, "downsampling_ratio": 40}
params_from_selector = {"replacement": False, "downsampling_ratio": 40, "ratio_max": 100}
per_sample_loss = None
device = "cpu"

Expand Down
Loading

0 comments on commit 4d78544

Please sign in to comment.