Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions DashAI/back/evaluation/cv.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from kink import di

from DashAI.back.core.enums.metrics import LevelEnum, SplitEnum
from DashAI.back.core.utils import MultilingualString
from DashAI.back.dependencies.database.models import Metric, Run
from DashAI.back.evaluation.base_evaluation_strategy import BaseEvaluationStrategy
from DashAI.back.splitters.base_splitter import BaseSplitter
Expand Down Expand Up @@ -425,6 +426,41 @@ class CrossValidationEvaluationStrategy(FoldEvaluationStrategy):
``ForecastingCrossValidationEvaluationStrategy`` handles that.
"""

DESCRIPTION = MultilingualString(
en=(
"Cross validation cuts the dataset into folds. Each fold takes a "
"turn as the validation set while the model trains on the rest, and "
"the scores are averaged, so the result leans less on any single "
"cut."
),
es=(
"La validacion cruzada corta el conjunto en pliegues. Cada pliegue "
"actua por turno como conjunto de validacion mientras el modelo "
"entrena con el resto, y los puntajes se promedian, asi el resultado "
"depende menos de un solo corte."
),
pt=(
"A validacao cruzada corta o conjunto em dobras. Cada dobra serve "
"por vez como conjunto de validacao enquanto o modelo treina no "
"resto, e as pontuacoes sao promediadas, entao o resultado depende "
"menos de um unico corte."
),
de=(
"Die Kreuzvalidierung teilt den Datensatz in Folds. Jeder Fold dient "
"reihum als Validierungsmenge, waehrend das Modell auf dem Rest "
"trainiert, und die Ergebnisse werden gemittelt, sodass das Resultat "
"weniger von einer einzelnen Teilung abhaengt."
),
zh=(
"交叉验证把数据集切成"
"若干折。每一折轮流作为"
"验证集,模型在其余部分"
"上训练,最后取平均分,"
"因此结果不那么依赖某"
"一次切分。"
),
)

COMPATIBLE_COMPONENTS = [
"TabularClassificationTask",
"TextClassificationTask",
Expand Down
38 changes: 38 additions & 0 deletions DashAI/back/evaluation/forecasting_cv.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Cross-validation for models that forecast a series from its own history."""

from DashAI.back.core.enums.metrics import SplitEnum
from DashAI.back.core.utils import MultilingualString
from DashAI.back.evaluation.cv import FoldEvaluationStrategy


Expand All @@ -21,5 +22,42 @@ class ForecastingCrossValidationEvaluationStrategy(FoldEvaluationStrategy):
origin forward through time.
"""

DESCRIPTION = MultilingualString(
en=(
"Walks the origin forward through the series. Each fold trains on "
"everything up to a point and is scored on the rows just after it, "
"then the origin moves on and the model is refitted with more "
"history. Training partitions are not scored."
),
es=(
"Avanza el origen a lo largo de la serie. Cada pliegue entrena con "
"todo lo anterior a un punto y se evalua con las filas justo "
"posteriores; luego el origen avanza y el modelo se reajusta con mas "
"historia. Las particiones de entrenamiento no se evaluan."
),
pt=(
"Avanca a origem ao longo da serie. Cada dobra treina com tudo o que "
"vem antes de um ponto e e avaliada nas linhas logo depois; entao a "
"origem avanca e o modelo e reajustado com mais historico. As "
"particoes de treino nao sao avaliadas."
),
de=(
"Schiebt den Ursprung durch die Zeitreihe. Jeder Fold trainiert auf "
"allem bis zu einem Punkt und wird auf den unmittelbar folgenden "
"Zeilen bewertet, dann rueckt der Ursprung weiter und das Modell "
"wird mit mehr Historie neu angepasst. Trainingspartitionen werden "
"nicht bewertet."
),
zh=(
"让起点沿序列向前推进。"
"每一折用某个时点之前的"
"全部数据训练,并在紧随"
"其后的行上评分;随后起"
"点前移,模型用更多历史"
"重新拟合。训练部分不参"
"与评分。"
),
)

COMPATIBLE_COMPONENTS = ["ForecastingTask"]
SCORED_SPLITS: tuple = (SplitEnum.VALIDATION, SplitEnum.TEST)
40 changes: 40 additions & 0 deletions DashAI/back/evaluation/forecasting_holdout.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Holdout evaluation for models that forecast a series from its own history."""

from DashAI.back.core.enums.metrics import SplitEnum
from DashAI.back.core.utils import MultilingualString
from DashAI.back.evaluation.holdout import SinglePartitionEvaluationStrategy


Expand Down Expand Up @@ -44,5 +45,44 @@ class ForecastingHoldoutEvaluationStrategy(SinglePartitionEvaluationStrategy):
they must not be fitted on it.
"""

DESCRIPTION = MultilingualString(
en=(
"Cuts the series once, in time order: the model trains on the "
"earliest rows and is scored on the ones that come after. The "
"training partition is not scored, since a forecaster asked about "
"dates it was fitted on reports a fit, not a forecast."
),
es=(
"Corta la serie una sola vez, en orden temporal: el modelo entrena "
"con las filas mas antiguas y se evalua con las que vienen despues. "
"La particion de entrenamiento no se evalua, porque preguntarle a un "
"pronosticador por fechas con las que fue ajustado da un ajuste, no "
"un pronostico."
),
pt=(
"Corta a serie uma unica vez, em ordem temporal: o modelo treina nas "
"linhas mais antigas e e avaliado nas que vem depois. A particao de "
"treino nao e avaliada, porque perguntar a um previsor sobre datas "
"em que ele foi ajustado da um ajuste, nao uma previsao."
),
de=(
"Teilt die Zeitreihe ein einziges Mal in zeitlicher Reihenfolge: Das "
"Modell trainiert auf den fruehesten Zeilen und wird auf den "
"folgenden bewertet. Die Trainingspartition wird nicht bewertet, "
"denn ein Prognosemodell, das nach Daten seiner eigenen Anpassung "
"gefragt wird, liefert eine Anpassung und keine Prognose."
),
zh=(
"按时间顺序只切分序列"
"一次:模型在最早的行"
"上训练,并在其后的行"
"上评分。训练部分不参"
"与评分,因为让预测模"
"型回答它自己拟合过的"
"日期,得到的是拟合而"
"不是预测。"
),
)

COMPATIBLE_COMPONENTS = ["ForecastingTask"]
SCORED_SPLITS: tuple = (SplitEnum.VALIDATION, SplitEnum.TEST)
32 changes: 32 additions & 0 deletions DashAI/back/evaluation/holdout.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from DashAI.back.core.enums.metrics import LevelEnum, SplitEnum
from DashAI.back.core.utils import MultilingualString
from DashAI.back.dependencies.database.models import Metric, Run
from DashAI.back.evaluation.base_evaluation_strategy import BaseEvaluationStrategy

Expand Down Expand Up @@ -180,6 +181,37 @@ class HoldoutEvaluationStrategy(SinglePartitionEvaluationStrategy):
partition alone, so it is the model the recorded metrics describe.
"""

DESCRIPTION = MultilingualString(
en=(
"Holdout cuts the dataset once. The model trains on one part and is "
"scored on rows it never saw. Fast, but the score depends on which "
"rows happened to land where."
),
es=(
"Holdout corta el conjunto una sola vez. El modelo entrena con una "
"parte y se evalua con filas que nunca vio. Es rapido, pero el "
"resultado depende de que filas cayeron en cada parte."
),
pt=(
"O holdout corta o conjunto uma unica vez. O modelo treina em uma "
"parte e e avaliado em linhas que nunca viu. E rapido, mas o "
"resultado depende de quais linhas cairam em cada parte."
),
de=(
"Holdout teilt den Datensatz ein einziges Mal. Das Modell trainiert "
"auf einem Teil und wird auf Zeilen bewertet, die es nie gesehen "
"hat. Schnell, aber das Ergebnis haengt davon ab, welche Zeilen wo "
"gelandet sind."
),
zh=(
"留出法只切分数据集一次。"
"模型在一部分上训练,"
"并在从未见过的行上评分。"
"速度快,但结果取决于"
"哪些行落在了哪一部分。"
),
)

COMPATIBLE_COMPONENTS = [
"TabularClassificationTask",
"TextClassificationTask",
Expand Down
21 changes: 13 additions & 8 deletions DashAI/back/splitters/base_splitter.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,31 +22,36 @@ class BaseSplitter(ConfigObject, metaclass=ABCMeta):

TYPE: Final[str] = "Splitter"

# How this splitter carves the dataset, which decides the evaluation
# strategy it belongs to and therefore where the frontend may offer it.
# "holdout" splitters produce one set of partitions; "folds" splitters
# produce several train and validation pairs. Without this the frontend
# cannot tell the two apart and has to hardcode a splitter name, which is
# how a shuffling splitter ended up being offered for time series.
PARTITIONING: str = "holdout"

# Name of the partition the model was fitted on. Every other partition a
# splitter declares holds rows the model never saw, so any of them can
# carry an explanation when the test partition came out empty.
TRAINING_PARTITION: str = "train"

GEOMETRY: str = "unknown"

@classmethod
def get_metadata(cls) -> Dict[str, Any]:
"""Return metadata describing how this splitter carves the dataset.

``geometry`` names the shape of the carve rather than the splitter, so
the frontend can draw a preview of the splits without knowing which
splitter produced them. It is declared rather than inferred from the
schema parameters: two splitters can take the same ``n_splits`` and
still lay their folds out differently, and a preview that guesses would
draw a confident picture of the wrong thing. A splitter whose shape has
no renderer yet leaves the default, and the frontend draws nothing.

Returns
-------
Dict[str, Any]
Mapping with ``partitioning``, which the frontend uses to decide
whether the splitter belongs to the holdout or the
cross-validation strategy.
cross-validation strategy, and ``geometry``, which it uses to
preview the splits.
"""
return {"partitioning": cls.PARTITIONING}
return {"partitioning": cls.PARTITIONING, "geometry": cls.GEOMETRY}

@classmethod
def explainable_partitions(
Expand Down
2 changes: 2 additions & 0 deletions DashAI/back/splitters/fold_splitter.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ class FoldSplitter(BaseSplitter):
# cross-validation strategy instead of the holdout one.
PARTITIONING: str = "folds"

GEOMETRY: str = "blocked_folds"

# How the rows of the test set are chosen. ``"random"`` samples them
# uniformly, ``"stratified"`` preserves the target distribution, and
# ``"group"`` moves whole groups so a group never spans the carve.
Expand Down
41 changes: 41 additions & 0 deletions DashAI/back/splitters/group_k_fold.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,47 @@ class GroupKFoldSplitter(FoldSplitter):
)
COMPATIBLE_INNER_SPLITTERS = ["GroupKFoldSplitter", "StratifiedGroupKFoldSplitter"]
SCHEMA = GroupKFoldSplitterSchema
DESCRIPTION = MultilingualString(
en=(
"K folds that never split a group across the train and "
"validation sides. Use it when several rows describe the same "
"subject, so a model is never scored on a subject it was "
"trained on. Grouping changes which rows land in each fold, "
"not how many, and the rows held out of the folds are whole "
"groups."
),
es=(
"K pliegues que nunca reparten un grupo entre entrenamiento y "
"validacion. Usalo cuando varias filas describen al mismo "
"sujeto, para que un modelo nunca se evalue con un sujeto con "
"el que fue entrenado. Agrupar cambia que filas caen en cada "
"pliegue, no cuantas, y las filas reservadas fuera de los "
"pliegues son grupos enteros."
),
pt=(
"K dobras que nunca separam um grupo entre treino e "
"validacao. Use quando varias linhas descrevem o mesmo "
"sujeito, para que o modelo nunca seja avaliado num sujeito "
"com que foi treinado. Agrupar muda quais linhas caem em cada "
"dobra, nao quantas, e as linhas reservadas fora das dobras "
"sao grupos inteiros."
),
de=(
"K Folds, die eine Gruppe nie zwischen Training und "
"Validierung aufteilen. Sinnvoll, wenn mehrere Zeilen "
"dasselbe Subjekt beschreiben, damit ein Modell nie auf einem "
"Subjekt bewertet wird, mit dem es trainiert wurde. "
"Gruppieren aendert, welche Zeilen in welchen Fold fallen, "
"nicht wie viele, und die zurueckgelegten Zeilen sind ganze "
"Gruppen."
),
zh=(
"k 个折不会把同一组拆到训练和验证两边。当多行描述同一个"
"对象时使用,以免模型在已经训练过的对象上被评分。分组改变"
"的是哪些行落入每一折,而不是有多少行,预留出来的行都是完"
"整的组。"
),
)

def __init__(self, splits_data):
"""Initialize the group-based K-fold splitter.
Expand Down
38 changes: 38 additions & 0 deletions DashAI/back/splitters/holdout.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ class PartitionSplitter(BaseSplitter):
- https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.html
"""

GEOMETRY: str = "partitions"

@classmethod
def explainable_partitions(cls, split_indexes):
"""Return the train, test and validation partitions of a holdout run.
Expand Down Expand Up @@ -374,6 +376,42 @@ class HoldoutSplitter(PartitionSplitter):
"""

SCHEMA = HoldoutSplitterSchema
DESCRIPTION = MultilingualString(
en=(
"Cuts the dataset into train, validation and test in the "
"proportions you set. Rows are sampled at random unless you "
"turn shuffling off, and can be drawn keeping the class "
"balance of the full dataset. The usual choice when a single "
"split is enough."
),
es=(
"Corta el conjunto en entrenamiento, validacion y prueba con "
"las proporciones que elijas. Las filas se toman al azar "
"salvo que desactives la mezcla, y pueden tomarse conservando "
"el balance de clases del conjunto completo. Es la opcion "
"habitual cuando basta con un solo corte."
),
pt=(
"Corta o conjunto em treino, validacao e teste nas proporcoes "
"que voce escolher. As linhas sao sorteadas ao acaso a menos "
"que voce desative o embaralhamento, e podem ser sorteadas "
"mantendo o balanco de classes do conjunto completo. E a "
"escolha usual quando um unico corte basta."
),
de=(
"Teilt den Datensatz in den gewaehlten Anteilen in Training, "
"Validierung und Test. Die Zeilen werden zufaellig gezogen, "
"sofern das Mischen nicht abgeschaltet ist, und koennen unter "
"Beibehaltung der Klassenverteilung des gesamten Datensatzes "
"gezogen werden. Die uebliche Wahl, wenn eine einzelne "
"Teilung genuegt."
),
zh=(
"按你设定的比例把数据集切成训练、验证和测试三部分。除非关"
"闭打乱,行都是随机抽取的,并且可以在抽取时保持完整数据集"
"的类别比例。只需一次切分时的常用选择。"
),
)
# Listed per task rather than left universal, so the frontend can resolve
# a holdout splitter from the task instead of hardcoding this class.
COMPATIBLE_COMPONENTS = [
Expand Down
34 changes: 34 additions & 0 deletions DashAI/back/splitters/k_fold.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,40 @@ class KFoldSplitter(FoldSplitter):
)
COMPATIBLE_INNER_SPLITTERS = ["KFoldSplitter", "StratifiedKFoldSplitter"]
SCHEMA = KFoldSplitterSchema
DESCRIPTION = MultilingualString(
en=(
"Divides the rows into k equal folds. Each fold is scored "
"once while the other k-1 train the model. The plain cross "
"validation split, with optional shuffling. Rows held out of "
"the folds are sampled from the whole dataset."
),
es=(
"Divide las filas en k pliegues iguales. Cada pliegue se "
"evalua una vez mientras los otros k-1 entrenan el modelo. Es "
"el corte de validacion cruzada simple, con mezcla opcional. "
"Las filas reservadas fuera de los pliegues se toman al azar "
"de todo el conjunto."
),
pt=(
"Divide as linhas em k dobras iguais. Cada dobra e avaliada "
"uma vez enquanto as outras k-1 treinam o modelo. E o corte "
"de validacao cruzada simples, com embaralhamento opcional. "
"As linhas reservadas fora das dobras sao sorteadas de todo o "
"conjunto."
),
de=(
"Teilt die Zeilen in k gleich grosse Folds. Jeder Fold wird "
"einmal bewertet, waehrend die anderen k-1 das Modell "
"trainieren. Die einfache Kreuzvalidierung, auf Wunsch mit "
"Mischen. Die aus den Folds zurueckgelegten Zeilen werden aus "
"dem gesamten Datensatz gezogen."
),
zh=(
"把行分成 k 个大小相等的折。每一折被评分一次,其余 k"
"-1 折用于训练模型。最基本的交叉验证切分,可选是否打乱"
"顺序。从各折中预留出来的行是从整个数据集中随机抽取的。"
),
)

def split_indexes(
self, x: DashAIDataset, y: DashAIDataset
Expand Down
Loading
Loading