bead 0.1.0__py3-none-any.whl
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.
- bead/__init__.py +11 -0
- bead/__main__.py +11 -0
- bead/active_learning/__init__.py +15 -0
- bead/active_learning/config.py +231 -0
- bead/active_learning/loop.py +566 -0
- bead/active_learning/models/__init__.py +24 -0
- bead/active_learning/models/base.py +852 -0
- bead/active_learning/models/binary.py +910 -0
- bead/active_learning/models/categorical.py +943 -0
- bead/active_learning/models/cloze.py +862 -0
- bead/active_learning/models/forced_choice.py +956 -0
- bead/active_learning/models/free_text.py +773 -0
- bead/active_learning/models/lora.py +365 -0
- bead/active_learning/models/magnitude.py +835 -0
- bead/active_learning/models/multi_select.py +795 -0
- bead/active_learning/models/ordinal_scale.py +811 -0
- bead/active_learning/models/peft_adapter.py +155 -0
- bead/active_learning/models/random_effects.py +639 -0
- bead/active_learning/selection.py +354 -0
- bead/active_learning/strategies.py +391 -0
- bead/active_learning/trainers/__init__.py +26 -0
- bead/active_learning/trainers/base.py +210 -0
- bead/active_learning/trainers/data_collator.py +172 -0
- bead/active_learning/trainers/dataset_utils.py +261 -0
- bead/active_learning/trainers/huggingface.py +304 -0
- bead/active_learning/trainers/lightning.py +324 -0
- bead/active_learning/trainers/metrics.py +424 -0
- bead/active_learning/trainers/mixed_effects.py +551 -0
- bead/active_learning/trainers/model_wrapper.py +509 -0
- bead/active_learning/trainers/registry.py +104 -0
- bead/adapters/__init__.py +11 -0
- bead/adapters/huggingface.py +61 -0
- bead/behavioral/__init__.py +116 -0
- bead/behavioral/analytics.py +646 -0
- bead/behavioral/extraction.py +343 -0
- bead/behavioral/merging.py +343 -0
- bead/cli/__init__.py +11 -0
- bead/cli/active_learning.py +513 -0
- bead/cli/active_learning_commands.py +779 -0
- bead/cli/completion.py +359 -0
- bead/cli/config.py +624 -0
- bead/cli/constraint_builders.py +286 -0
- bead/cli/deployment.py +859 -0
- bead/cli/deployment_trials.py +493 -0
- bead/cli/deployment_ui.py +332 -0
- bead/cli/display.py +378 -0
- bead/cli/items.py +960 -0
- bead/cli/items_factories.py +776 -0
- bead/cli/list_constraints.py +714 -0
- bead/cli/lists.py +490 -0
- bead/cli/main.py +430 -0
- bead/cli/models.py +877 -0
- bead/cli/resource_loaders.py +621 -0
- bead/cli/resources.py +1036 -0
- bead/cli/shell.py +356 -0
- bead/cli/simulate.py +840 -0
- bead/cli/templates.py +1158 -0
- bead/cli/training.py +1080 -0
- bead/cli/utils.py +614 -0
- bead/cli/workflow.py +1273 -0
- bead/config/__init__.py +68 -0
- bead/config/active_learning.py +1009 -0
- bead/config/config.py +192 -0
- bead/config/defaults.py +118 -0
- bead/config/deployment.py +217 -0
- bead/config/env.py +147 -0
- bead/config/item.py +45 -0
- bead/config/list.py +193 -0
- bead/config/loader.py +149 -0
- bead/config/logging.py +42 -0
- bead/config/model.py +49 -0
- bead/config/paths.py +46 -0
- bead/config/profiles.py +320 -0
- bead/config/resources.py +47 -0
- bead/config/serialization.py +210 -0
- bead/config/simulation.py +206 -0
- bead/config/template.py +238 -0
- bead/config/validation.py +267 -0
- bead/data/__init__.py +65 -0
- bead/data/base.py +87 -0
- bead/data/identifiers.py +97 -0
- bead/data/language_codes.py +61 -0
- bead/data/metadata.py +270 -0
- bead/data/range.py +123 -0
- bead/data/repository.py +358 -0
- bead/data/serialization.py +249 -0
- bead/data/timestamps.py +89 -0
- bead/data/validation.py +349 -0
- bead/data_collection/__init__.py +11 -0
- bead/data_collection/jatos.py +223 -0
- bead/data_collection/merger.py +154 -0
- bead/data_collection/prolific.py +198 -0
- bead/deployment/__init__.py +5 -0
- bead/deployment/distribution.py +402 -0
- bead/deployment/jatos/__init__.py +1 -0
- bead/deployment/jatos/api.py +200 -0
- bead/deployment/jatos/exporter.py +210 -0
- bead/deployment/jspsych/__init__.py +9 -0
- bead/deployment/jspsych/biome.json +44 -0
- bead/deployment/jspsych/config.py +411 -0
- bead/deployment/jspsych/generator.py +598 -0
- bead/deployment/jspsych/package.json +51 -0
- bead/deployment/jspsych/pnpm-lock.yaml +2141 -0
- bead/deployment/jspsych/randomizer.py +299 -0
- bead/deployment/jspsych/src/lib/list-distributor.test.ts +327 -0
- bead/deployment/jspsych/src/lib/list-distributor.ts +1282 -0
- bead/deployment/jspsych/src/lib/randomizer.test.ts +232 -0
- bead/deployment/jspsych/src/lib/randomizer.ts +367 -0
- bead/deployment/jspsych/src/plugins/cloze-dropdown.ts +252 -0
- bead/deployment/jspsych/src/plugins/forced-choice.ts +265 -0
- bead/deployment/jspsych/src/plugins/plugins.test.ts +141 -0
- bead/deployment/jspsych/src/plugins/rating.ts +248 -0
- bead/deployment/jspsych/src/slopit/index.ts +9 -0
- bead/deployment/jspsych/src/types/jatos.d.ts +256 -0
- bead/deployment/jspsych/src/types/jspsych.d.ts +228 -0
- bead/deployment/jspsych/templates/experiment.css +1 -0
- bead/deployment/jspsych/templates/experiment.js.template +289 -0
- bead/deployment/jspsych/templates/index.html +51 -0
- bead/deployment/jspsych/templates/randomizer.js +241 -0
- bead/deployment/jspsych/templates/randomizer.js.template +313 -0
- bead/deployment/jspsych/trials.py +723 -0
- bead/deployment/jspsych/tsconfig.json +23 -0
- bead/deployment/jspsych/tsup.config.ts +30 -0
- bead/deployment/jspsych/ui/__init__.py +1 -0
- bead/deployment/jspsych/ui/components.py +383 -0
- bead/deployment/jspsych/ui/styles.py +411 -0
- bead/dsl/__init__.py +80 -0
- bead/dsl/ast.py +168 -0
- bead/dsl/context.py +178 -0
- bead/dsl/errors.py +71 -0
- bead/dsl/evaluator.py +570 -0
- bead/dsl/grammar.lark +81 -0
- bead/dsl/parser.py +231 -0
- bead/dsl/stdlib.py +929 -0
- bead/evaluation/__init__.py +13 -0
- bead/evaluation/convergence.py +485 -0
- bead/evaluation/interannotator.py +398 -0
- bead/items/__init__.py +40 -0
- bead/items/adapters/__init__.py +70 -0
- bead/items/adapters/anthropic.py +224 -0
- bead/items/adapters/api_utils.py +167 -0
- bead/items/adapters/base.py +216 -0
- bead/items/adapters/google.py +259 -0
- bead/items/adapters/huggingface.py +1074 -0
- bead/items/adapters/openai.py +323 -0
- bead/items/adapters/registry.py +202 -0
- bead/items/adapters/sentence_transformers.py +224 -0
- bead/items/adapters/togetherai.py +309 -0
- bead/items/binary.py +515 -0
- bead/items/cache.py +558 -0
- bead/items/categorical.py +593 -0
- bead/items/cloze.py +757 -0
- bead/items/constructor.py +784 -0
- bead/items/forced_choice.py +413 -0
- bead/items/free_text.py +681 -0
- bead/items/generation.py +432 -0
- bead/items/item.py +396 -0
- bead/items/item_template.py +787 -0
- bead/items/magnitude.py +573 -0
- bead/items/multi_select.py +621 -0
- bead/items/ordinal_scale.py +569 -0
- bead/items/scoring.py +448 -0
- bead/items/validation.py +723 -0
- bead/lists/__init__.py +30 -0
- bead/lists/balancer.py +263 -0
- bead/lists/constraints.py +1067 -0
- bead/lists/experiment_list.py +286 -0
- bead/lists/list_collection.py +378 -0
- bead/lists/partitioner.py +1141 -0
- bead/lists/stratification.py +254 -0
- bead/participants/__init__.py +73 -0
- bead/participants/collection.py +699 -0
- bead/participants/merging.py +312 -0
- bead/participants/metadata_spec.py +491 -0
- bead/participants/models.py +276 -0
- bead/resources/__init__.py +29 -0
- bead/resources/adapters/__init__.py +19 -0
- bead/resources/adapters/base.py +104 -0
- bead/resources/adapters/cache.py +128 -0
- bead/resources/adapters/glazing.py +508 -0
- bead/resources/adapters/registry.py +117 -0
- bead/resources/adapters/unimorph.py +796 -0
- bead/resources/classification.py +856 -0
- bead/resources/constraint_builders.py +329 -0
- bead/resources/constraints.py +165 -0
- bead/resources/lexical_item.py +223 -0
- bead/resources/lexicon.py +744 -0
- bead/resources/loaders.py +209 -0
- bead/resources/template.py +441 -0
- bead/resources/template_collection.py +707 -0
- bead/resources/template_generation.py +349 -0
- bead/simulation/__init__.py +29 -0
- bead/simulation/annotators/__init__.py +15 -0
- bead/simulation/annotators/base.py +175 -0
- bead/simulation/annotators/distance_based.py +135 -0
- bead/simulation/annotators/lm_based.py +114 -0
- bead/simulation/annotators/oracle.py +182 -0
- bead/simulation/annotators/random.py +181 -0
- bead/simulation/dsl_extension/__init__.py +3 -0
- bead/simulation/noise_models/__init__.py +13 -0
- bead/simulation/noise_models/base.py +42 -0
- bead/simulation/noise_models/random_noise.py +82 -0
- bead/simulation/noise_models/systematic.py +132 -0
- bead/simulation/noise_models/temperature.py +86 -0
- bead/simulation/runner.py +144 -0
- bead/simulation/strategies/__init__.py +23 -0
- bead/simulation/strategies/base.py +123 -0
- bead/simulation/strategies/binary.py +103 -0
- bead/simulation/strategies/categorical.py +123 -0
- bead/simulation/strategies/cloze.py +224 -0
- bead/simulation/strategies/forced_choice.py +127 -0
- bead/simulation/strategies/free_text.py +105 -0
- bead/simulation/strategies/magnitude.py +116 -0
- bead/simulation/strategies/multi_select.py +129 -0
- bead/simulation/strategies/ordinal_scale.py +131 -0
- bead/templates/__init__.py +27 -0
- bead/templates/adapters/__init__.py +17 -0
- bead/templates/adapters/base.py +128 -0
- bead/templates/adapters/cache.py +178 -0
- bead/templates/adapters/huggingface.py +312 -0
- bead/templates/combinatorics.py +103 -0
- bead/templates/filler.py +605 -0
- bead/templates/renderers.py +177 -0
- bead/templates/resolver.py +178 -0
- bead/templates/strategies.py +1806 -0
- bead/templates/streaming.py +195 -0
- bead-0.1.0.dist-info/METADATA +212 -0
- bead-0.1.0.dist-info/RECORD +231 -0
- bead-0.1.0.dist-info/WHEEL +4 -0
- bead-0.1.0.dist-info/entry_points.txt +2 -0
- bead-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
"""Item selectors for active learning.
|
|
2
|
+
|
|
3
|
+
This module implements sample selection algorithms that use uncertainty
|
|
4
|
+
strategies to intelligently select the most informative items for labeling
|
|
5
|
+
in the active learning loop.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import TYPE_CHECKING, Any
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
|
|
14
|
+
from bead.active_learning.strategies import create_strategy
|
|
15
|
+
from bead.items.item import Item
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from collections.abc import Callable
|
|
19
|
+
|
|
20
|
+
from bead.active_learning.models.base import ActiveLearningModel
|
|
21
|
+
from bead.config.active_learning import UncertaintySamplerConfig
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ItemSelector:
|
|
25
|
+
"""Base class for item selection algorithms.
|
|
26
|
+
|
|
27
|
+
Item selectors determine which unlabeled items should be selected
|
|
28
|
+
for annotation in each active learning iteration.
|
|
29
|
+
|
|
30
|
+
Examples
|
|
31
|
+
--------
|
|
32
|
+
>>> selector = ItemSelector()
|
|
33
|
+
>>> # Subclasses implement select() method
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def select(
|
|
37
|
+
self,
|
|
38
|
+
items: list[Item],
|
|
39
|
+
model: ActiveLearningModel,
|
|
40
|
+
predict_fn: Callable[[ActiveLearningModel, Item], np.ndarray],
|
|
41
|
+
budget: int,
|
|
42
|
+
) -> list[Item]:
|
|
43
|
+
"""Select items for annotation.
|
|
44
|
+
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
items : list[Item]
|
|
48
|
+
Unlabeled items to select from.
|
|
49
|
+
model : ActiveLearningModel
|
|
50
|
+
Trained model for making predictions.
|
|
51
|
+
predict_fn : Callable[[ActiveLearningModel, Item], np.ndarray]
|
|
52
|
+
Function to get prediction probabilities from model.
|
|
53
|
+
Should return array of shape (n_classes,) with probabilities.
|
|
54
|
+
budget : int
|
|
55
|
+
Number of items to select.
|
|
56
|
+
|
|
57
|
+
Returns
|
|
58
|
+
-------
|
|
59
|
+
list[Item]
|
|
60
|
+
Selected items for annotation.
|
|
61
|
+
|
|
62
|
+
Examples
|
|
63
|
+
--------
|
|
64
|
+
>>> selector = UncertaintySampler() # doctest: +SKIP
|
|
65
|
+
>>> selected = selector.select( # doctest: +SKIP
|
|
66
|
+
... items, model, predict_fn, budget=10
|
|
67
|
+
... )
|
|
68
|
+
>>> len(selected) <= 10 # doctest: +SKIP
|
|
69
|
+
True
|
|
70
|
+
"""
|
|
71
|
+
raise NotImplementedError("Subclasses must implement select()")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class UncertaintySampler(ItemSelector):
|
|
75
|
+
"""Uncertainty-based item selector.
|
|
76
|
+
|
|
77
|
+
Selects items using uncertainty sampling strategies (entropy, margin,
|
|
78
|
+
or least confidence). This is the main item selection algorithm for
|
|
79
|
+
active learning in bead.
|
|
80
|
+
|
|
81
|
+
Parameters
|
|
82
|
+
----------
|
|
83
|
+
config : UncertaintySamplerConfig | None
|
|
84
|
+
Configuration for the uncertainty sampler.
|
|
85
|
+
|
|
86
|
+
Attributes
|
|
87
|
+
----------
|
|
88
|
+
config : UncertaintySamplerConfig
|
|
89
|
+
Configuration for the sampler.
|
|
90
|
+
strategy : SamplingStrategy
|
|
91
|
+
The underlying sampling strategy.
|
|
92
|
+
|
|
93
|
+
Examples
|
|
94
|
+
--------
|
|
95
|
+
>>> import numpy as np
|
|
96
|
+
>>> from uuid import uuid4
|
|
97
|
+
>>> from bead.items.item import Item
|
|
98
|
+
>>> from bead.config.active_learning import UncertaintySamplerConfig
|
|
99
|
+
>>> # Create sampler
|
|
100
|
+
>>> config = UncertaintySamplerConfig(method="entropy")
|
|
101
|
+
>>> sampler = UncertaintySampler(config=config)
|
|
102
|
+
>>> # Mock items
|
|
103
|
+
>>> items = [Item(item_template_id=uuid4(), rendered_elements={}) for _ in range(5)]
|
|
104
|
+
>>> # Mock model and predict function
|
|
105
|
+
>>> def predict_fn(model, item):
|
|
106
|
+
... return np.array([0.5, 0.5]) # Mock probabilities
|
|
107
|
+
>>> # Select items
|
|
108
|
+
>>> selected = sampler.select(items, None, predict_fn, budget=2)
|
|
109
|
+
>>> len(selected)
|
|
110
|
+
2
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
def __init__(
|
|
114
|
+
self,
|
|
115
|
+
config: UncertaintySamplerConfig | None = None,
|
|
116
|
+
) -> None:
|
|
117
|
+
"""Initialize uncertainty sampler.
|
|
118
|
+
|
|
119
|
+
Parameters
|
|
120
|
+
----------
|
|
121
|
+
config : UncertaintySamplerConfig | None
|
|
122
|
+
Configuration for the sampler. If None, uses defaults.
|
|
123
|
+
"""
|
|
124
|
+
self.config = config or UncertaintySamplerConfig()
|
|
125
|
+
self.strategy = create_strategy(self.config.method)
|
|
126
|
+
|
|
127
|
+
def select(
|
|
128
|
+
self,
|
|
129
|
+
items: list[Item],
|
|
130
|
+
model: Any,
|
|
131
|
+
predict_fn: Callable[[Any, Item], np.ndarray],
|
|
132
|
+
budget: int,
|
|
133
|
+
) -> list[Item]:
|
|
134
|
+
"""Select items using uncertainty sampling.
|
|
135
|
+
|
|
136
|
+
Parameters
|
|
137
|
+
----------
|
|
138
|
+
items : list[Item]
|
|
139
|
+
Unlabeled items to select from.
|
|
140
|
+
model : Any
|
|
141
|
+
Trained model for making predictions.
|
|
142
|
+
predict_fn : Callable[[Any, Item], np.ndarray]
|
|
143
|
+
Function to get prediction probabilities from model.
|
|
144
|
+
Should return array of shape (n_classes,) for each item.
|
|
145
|
+
budget : int
|
|
146
|
+
Number of items to select.
|
|
147
|
+
|
|
148
|
+
Returns
|
|
149
|
+
-------
|
|
150
|
+
list[Item]
|
|
151
|
+
Selected items for annotation, ordered by uncertainty (most to least).
|
|
152
|
+
|
|
153
|
+
Raises
|
|
154
|
+
------
|
|
155
|
+
ValueError
|
|
156
|
+
If items list is empty or budget is invalid.
|
|
157
|
+
|
|
158
|
+
Examples
|
|
159
|
+
--------
|
|
160
|
+
>>> import numpy as np
|
|
161
|
+
>>> from uuid import uuid4
|
|
162
|
+
>>> from bead.items.item import Item
|
|
163
|
+
>>> from bead.config.active_learning import UncertaintySamplerConfig
|
|
164
|
+
>>> config = UncertaintySamplerConfig(method="entropy")
|
|
165
|
+
>>> sampler = UncertaintySampler(config=config)
|
|
166
|
+
>>> items = [
|
|
167
|
+
... Item(item_template_id=uuid4(), rendered_elements={"text": "item1"}),
|
|
168
|
+
... Item(item_template_id=uuid4(), rendered_elements={"text": "item2"}),
|
|
169
|
+
... ]
|
|
170
|
+
>>> def predict_fn(model, item):
|
|
171
|
+
... # First item is uncertain, second is confident
|
|
172
|
+
... if "item1" in item.rendered_elements.get("text", ""):
|
|
173
|
+
... return np.array([0.5, 0.5])
|
|
174
|
+
... return np.array([0.9, 0.1])
|
|
175
|
+
>>> selected = sampler.select(items, None, predict_fn, budget=1)
|
|
176
|
+
>>> "item1" in selected[0].rendered_elements["text"]
|
|
177
|
+
True
|
|
178
|
+
"""
|
|
179
|
+
# Validate inputs
|
|
180
|
+
if not items:
|
|
181
|
+
raise ValueError("Items list cannot be empty")
|
|
182
|
+
|
|
183
|
+
if budget <= 0:
|
|
184
|
+
raise ValueError(f"Budget must be positive, got {budget}")
|
|
185
|
+
|
|
186
|
+
# Handle case where budget >= number of items
|
|
187
|
+
if budget >= len(items):
|
|
188
|
+
return items.copy()
|
|
189
|
+
|
|
190
|
+
# Compute predictions for all items
|
|
191
|
+
probabilities = self._batch_predict(items, model, predict_fn)
|
|
192
|
+
|
|
193
|
+
# Compute uncertainty scores
|
|
194
|
+
scores = self.strategy.compute_scores(probabilities)
|
|
195
|
+
|
|
196
|
+
# Select top k items
|
|
197
|
+
selected_indices = self.strategy.select_top_k(scores, k=budget)
|
|
198
|
+
|
|
199
|
+
# Return selected items (convert numpy array to list of Python ints)
|
|
200
|
+
return [items[i] for i in selected_indices.tolist()]
|
|
201
|
+
|
|
202
|
+
def _batch_predict(
|
|
203
|
+
self,
|
|
204
|
+
items: list[Item],
|
|
205
|
+
model: Any,
|
|
206
|
+
predict_fn: Callable[[Any, Item], np.ndarray],
|
|
207
|
+
) -> np.ndarray:
|
|
208
|
+
"""Compute predictions in batches.
|
|
209
|
+
|
|
210
|
+
Parameters
|
|
211
|
+
----------
|
|
212
|
+
items : list[Item]
|
|
213
|
+
Items to predict.
|
|
214
|
+
model : Any
|
|
215
|
+
Trained model.
|
|
216
|
+
predict_fn : Callable[[Any, Item], np.ndarray]
|
|
217
|
+
Prediction function.
|
|
218
|
+
|
|
219
|
+
Returns
|
|
220
|
+
-------
|
|
221
|
+
np.ndarray
|
|
222
|
+
Prediction probabilities with shape (n_items, n_classes).
|
|
223
|
+
|
|
224
|
+
Examples
|
|
225
|
+
--------
|
|
226
|
+
>>> import numpy as np
|
|
227
|
+
>>> from uuid import uuid4
|
|
228
|
+
>>> from bead.items.item import Item
|
|
229
|
+
>>> sampler = UncertaintySampler()
|
|
230
|
+
>>> items = [
|
|
231
|
+
... Item(item_template_id=uuid4(), rendered_elements={})
|
|
232
|
+
... for _ in range(3)
|
|
233
|
+
... ]
|
|
234
|
+
>>> def predict_fn(model, item):
|
|
235
|
+
... return np.array([0.6, 0.4])
|
|
236
|
+
>>> probs = sampler._batch_predict(items, None, predict_fn)
|
|
237
|
+
>>> probs.shape
|
|
238
|
+
(3, 2)
|
|
239
|
+
"""
|
|
240
|
+
all_probs = []
|
|
241
|
+
|
|
242
|
+
# Process in batches
|
|
243
|
+
batch_size = self.config.batch_size or 32
|
|
244
|
+
for i in range(0, len(items), batch_size):
|
|
245
|
+
batch_items = items[i : i + batch_size]
|
|
246
|
+
|
|
247
|
+
# Get predictions for batch
|
|
248
|
+
batch_probs = [predict_fn(model, item) for item in batch_items]
|
|
249
|
+
|
|
250
|
+
all_probs.extend(batch_probs)
|
|
251
|
+
|
|
252
|
+
# Stack into array
|
|
253
|
+
return np.array(all_probs)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
class RandomSelector(ItemSelector):
|
|
257
|
+
"""Random item selector (baseline).
|
|
258
|
+
|
|
259
|
+
Selects items randomly without considering model predictions.
|
|
260
|
+
Useful as a baseline for comparison with uncertainty-based methods.
|
|
261
|
+
|
|
262
|
+
Parameters
|
|
263
|
+
----------
|
|
264
|
+
seed : int | None
|
|
265
|
+
Random seed for reproducibility.
|
|
266
|
+
|
|
267
|
+
Attributes
|
|
268
|
+
----------
|
|
269
|
+
rng : np.random.Generator
|
|
270
|
+
Random number generator.
|
|
271
|
+
|
|
272
|
+
Examples
|
|
273
|
+
--------
|
|
274
|
+
>>> from uuid import uuid4
|
|
275
|
+
>>> from bead.items.item import Item
|
|
276
|
+
>>> selector = RandomSelector(seed=42)
|
|
277
|
+
>>> items = [
|
|
278
|
+
... Item(item_template_id=uuid4(), rendered_elements={})
|
|
279
|
+
... for _ in range(10)
|
|
280
|
+
... ]
|
|
281
|
+
>>> selected = selector.select(items, None, None, budget=3)
|
|
282
|
+
>>> len(selected)
|
|
283
|
+
3
|
|
284
|
+
"""
|
|
285
|
+
|
|
286
|
+
def __init__(self, seed: int | None = None) -> None:
|
|
287
|
+
"""Initialize random selector.
|
|
288
|
+
|
|
289
|
+
Parameters
|
|
290
|
+
----------
|
|
291
|
+
seed : int | None
|
|
292
|
+
Random seed for reproducibility.
|
|
293
|
+
"""
|
|
294
|
+
self.rng = np.random.default_rng(seed)
|
|
295
|
+
|
|
296
|
+
def select(
|
|
297
|
+
self,
|
|
298
|
+
items: list[Item],
|
|
299
|
+
model: Any,
|
|
300
|
+
predict_fn: Callable[[Any, Item], np.ndarray],
|
|
301
|
+
budget: int,
|
|
302
|
+
) -> list[Item]:
|
|
303
|
+
"""Select items randomly.
|
|
304
|
+
|
|
305
|
+
Parameters
|
|
306
|
+
----------
|
|
307
|
+
items : list[Item]
|
|
308
|
+
Items to select from.
|
|
309
|
+
model : Any
|
|
310
|
+
Model (unused, kept for interface compatibility).
|
|
311
|
+
predict_fn : Callable[[Any, Item], np.ndarray]
|
|
312
|
+
Prediction function (unused, kept for interface compatibility).
|
|
313
|
+
budget : int
|
|
314
|
+
Number of items to select.
|
|
315
|
+
|
|
316
|
+
Returns
|
|
317
|
+
-------
|
|
318
|
+
list[Item]
|
|
319
|
+
Randomly selected items.
|
|
320
|
+
|
|
321
|
+
Raises
|
|
322
|
+
------
|
|
323
|
+
ValueError
|
|
324
|
+
If items list is empty or budget is invalid.
|
|
325
|
+
|
|
326
|
+
Examples
|
|
327
|
+
--------
|
|
328
|
+
>>> from uuid import uuid4
|
|
329
|
+
>>> from bead.items.item import Item
|
|
330
|
+
>>> selector = RandomSelector(seed=123)
|
|
331
|
+
>>> items = [
|
|
332
|
+
... Item(item_template_id=uuid4(), rendered_elements={})
|
|
333
|
+
... for _ in range(5)
|
|
334
|
+
... ]
|
|
335
|
+
>>> selected = selector.select(items, None, None, budget=2)
|
|
336
|
+
>>> len(selected)
|
|
337
|
+
2
|
|
338
|
+
"""
|
|
339
|
+
# Validate inputs
|
|
340
|
+
if not items:
|
|
341
|
+
raise ValueError("Items list cannot be empty")
|
|
342
|
+
|
|
343
|
+
if budget <= 0:
|
|
344
|
+
raise ValueError(f"Budget must be positive, got {budget}")
|
|
345
|
+
|
|
346
|
+
# Handle case where budget >= number of items
|
|
347
|
+
if budget >= len(items):
|
|
348
|
+
return items.copy()
|
|
349
|
+
|
|
350
|
+
# Select random indices without replacement
|
|
351
|
+
selected_indices = self.rng.choice(len(items), size=budget, replace=False)
|
|
352
|
+
|
|
353
|
+
# Return selected items (convert numpy array to list of Python ints)
|
|
354
|
+
return [items[i] for i in selected_indices.tolist()]
|