dslighting 1.7.1__py3-none-any.whl → 1.7.8__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.
- dslighting/__init__.py +1 -1
- dslighting/core/agent.py +78 -62
- {dslighting-1.7.1.dist-info → dslighting-1.7.8.dist-info}/METADATA +3 -1
- {dslighting-1.7.1.dist-info → dslighting-1.7.8.dist-info}/RECORD +352 -7
- {dslighting-1.7.1.dist-info → dslighting-1.7.8.dist-info}/top_level.txt +1 -0
- mlebench/README.md +39 -0
- mlebench/__init__.py +0 -0
- mlebench/cli.py +221 -0
- mlebench/competitions/3d-object-detection-for-autonomous-vehicles/grade.py +161 -0
- mlebench/competitions/3d-object-detection-for-autonomous-vehicles/mAP_evaluation.py +425 -0
- mlebench/competitions/3d-object-detection-for-autonomous-vehicles/prepare.py +483 -0
- mlebench/competitions/3d-object-detection-for-autonomous-vehicles/prepare_val.py +719 -0
- mlebench/competitions/AI4Code/grade.py +70 -0
- mlebench/competitions/AI4Code/prepare.py +84 -0
- mlebench/competitions/AI4Code/prepare_val.py +159 -0
- mlebench/competitions/__init__.py +0 -0
- mlebench/competitions/aerial-cactus-identification/grade.py +11 -0
- mlebench/competitions/aerial-cactus-identification/prepare.py +71 -0
- mlebench/competitions/aerial-cactus-identification/prepare_val.py +133 -0
- mlebench/competitions/alaska2-image-steganalysis/grade.py +136 -0
- mlebench/competitions/alaska2-image-steganalysis/prepare.py +88 -0
- mlebench/competitions/alaska2-image-steganalysis/prepare_val.py +148 -0
- mlebench/competitions/aptos2019-blindness-detection/grade.py +35 -0
- mlebench/competitions/aptos2019-blindness-detection/prepare.py +75 -0
- mlebench/competitions/aptos2019-blindness-detection/prepare_val.py +123 -0
- mlebench/competitions/bike-sharing-demand/__init__.py +0 -0
- mlebench/competitions/bike-sharing-demand/grade.py +55 -0
- mlebench/competitions/bike-sharing-demand/prepare.py +37 -0
- mlebench/competitions/billion-word-imputation/grade.py +37 -0
- mlebench/competitions/billion-word-imputation/prepare.py +107 -0
- mlebench/competitions/billion-word-imputation/prepare_val.py +179 -0
- mlebench/competitions/bms-molecular-translation/grade.py +40 -0
- mlebench/competitions/bms-molecular-translation/prepare.py +68 -0
- mlebench/competitions/bms-molecular-translation/prepare_val.py +131 -0
- mlebench/competitions/cassava-leaf-disease-classification/grade.py +12 -0
- mlebench/competitions/cassava-leaf-disease-classification/prepare.py +113 -0
- mlebench/competitions/cassava-leaf-disease-classification/prepare_val.py +186 -0
- mlebench/competitions/cdiscount-image-classification-challenge/grade.py +11 -0
- mlebench/competitions/cdiscount-image-classification-challenge/prepare.py +144 -0
- mlebench/competitions/cdiscount-image-classification-challenge/prepare_val.py +205 -0
- mlebench/competitions/chaii-hindi-and-tamil-question-answering/grade.py +67 -0
- mlebench/competitions/chaii-hindi-and-tamil-question-answering/prepare.py +31 -0
- mlebench/competitions/chaii-hindi-and-tamil-question-answering/prepare_val.py +94 -0
- mlebench/competitions/champs-scalar-coupling/grade.py +60 -0
- mlebench/competitions/champs-scalar-coupling/prepare.py +116 -0
- mlebench/competitions/champs-scalar-coupling/prepare_val.py +155 -0
- mlebench/competitions/conways-reverse-game-of-life-2020/__init__.py +0 -0
- mlebench/competitions/conways-reverse-game-of-life-2020/grade.py +40 -0
- mlebench/competitions/conways-reverse-game-of-life-2020/prepare.py +41 -0
- mlebench/competitions/demand-forecasting-kernels-only/__init__.py +0 -0
- mlebench/competitions/demand-forecasting-kernels-only/grade.py +66 -0
- mlebench/competitions/demand-forecasting-kernels-only/prepare.py +27 -0
- mlebench/competitions/demand_forecasting_kernels_only/__init__.py +0 -0
- mlebench/competitions/demand_forecasting_kernels_only/grade.py +66 -0
- mlebench/competitions/demand_forecasting_kernels_only/prepare.py +27 -0
- mlebench/competitions/denoising-dirty-documents/grade.py +44 -0
- mlebench/competitions/denoising-dirty-documents/prepare.py +134 -0
- mlebench/competitions/denoising-dirty-documents/prepare_val.py +178 -0
- mlebench/competitions/detecting-insults-in-social-commentary/grade.py +11 -0
- mlebench/competitions/detecting-insults-in-social-commentary/prepare.py +72 -0
- mlebench/competitions/detecting-insults-in-social-commentary/prepare_val.py +128 -0
- mlebench/competitions/dog-breed-identification/dogs.py +124 -0
- mlebench/competitions/dog-breed-identification/grade.py +42 -0
- mlebench/competitions/dog-breed-identification/prepare.py +55 -0
- mlebench/competitions/dog-breed-identification/prepare_val.py +104 -0
- mlebench/competitions/dogs-vs-cats-redux-kernels-edition/grade.py +43 -0
- mlebench/competitions/dogs-vs-cats-redux-kernels-edition/prepare.py +70 -0
- mlebench/competitions/dogs-vs-cats-redux-kernels-edition/prepare_val.py +143 -0
- mlebench/competitions/ethanol-concentration/grade.py +23 -0
- mlebench/competitions/ethanol-concentration/prepare.py +90 -0
- mlebench/competitions/facebook-recruiting-iii-keyword-extraction/grade.py +60 -0
- mlebench/competitions/facebook-recruiting-iii-keyword-extraction/prepare.py +41 -0
- mlebench/competitions/facebook-recruiting-iii-keyword-extraction/prepare_val.py +92 -0
- mlebench/competitions/feedback-prize-english-language-learning/__init__.py +0 -0
- mlebench/competitions/feedback-prize-english-language-learning/grade.py +60 -0
- mlebench/competitions/feedback-prize-english-language-learning/prepare.py +39 -0
- mlebench/competitions/freesound-audio-tagging-2019/grade.py +64 -0
- mlebench/competitions/freesound-audio-tagging-2019/prepare.py +94 -0
- mlebench/competitions/freesound-audio-tagging-2019/prepare_val.py +175 -0
- mlebench/competitions/freesound-audio-tagging-2019/vocabulary.py +83 -0
- mlebench/competitions/google-quest-challenge/classes.py +32 -0
- mlebench/competitions/google-quest-challenge/grade.py +45 -0
- mlebench/competitions/google-quest-challenge/prepare.py +58 -0
- mlebench/competitions/google-quest-challenge/prepare_val.py +120 -0
- mlebench/competitions/google-research-identify-contrails-reduce-global-warming/grade.py +77 -0
- mlebench/competitions/google-research-identify-contrails-reduce-global-warming/prepare.py +155 -0
- mlebench/competitions/google-research-identify-contrails-reduce-global-warming/prepare_val.py +211 -0
- mlebench/competitions/h-and-m-personalized-fashion-recommendations/grade.py +42 -0
- mlebench/competitions/h-and-m-personalized-fashion-recommendations/prepare.py +102 -0
- mlebench/competitions/h-and-m-personalized-fashion-recommendations/prepare_val.py +132 -0
- mlebench/competitions/handwriting/grade.py +23 -0
- mlebench/competitions/handwriting/prepare.py +179 -0
- mlebench/competitions/herbarium-2020-fgvc7/grade.py +34 -0
- mlebench/competitions/herbarium-2020-fgvc7/prepare.py +251 -0
- mlebench/competitions/herbarium-2020-fgvc7/prepare_val.py +242 -0
- mlebench/competitions/herbarium-2021-fgvc8/grade.py +34 -0
- mlebench/competitions/herbarium-2021-fgvc8/prepare.py +251 -0
- mlebench/competitions/herbarium-2021-fgvc8/prepare_val.py +222 -0
- mlebench/competitions/herbarium-2022-fgvc9/grade.py +31 -0
- mlebench/competitions/herbarium-2022-fgvc9/prepare.py +233 -0
- mlebench/competitions/herbarium-2022-fgvc9/prepare_val.py +213 -0
- mlebench/competitions/histopathologic-cancer-detection/grade.py +12 -0
- mlebench/competitions/histopathologic-cancer-detection/prepare.py +59 -0
- mlebench/competitions/histopathologic-cancer-detection/prepare_val.py +131 -0
- mlebench/competitions/hms-harmful-brain-activity-classification/constants.py +9 -0
- mlebench/competitions/hms-harmful-brain-activity-classification/grade.py +43 -0
- mlebench/competitions/hms-harmful-brain-activity-classification/kaggle_metric_utilities.py +96 -0
- mlebench/competitions/hms-harmful-brain-activity-classification/kullback_leibler_divergence.py +118 -0
- mlebench/competitions/hms-harmful-brain-activity-classification/prepare.py +121 -0
- mlebench/competitions/hms-harmful-brain-activity-classification/prepare_val.py +190 -0
- mlebench/competitions/hotel-id-2021-fgvc8/grade.py +41 -0
- mlebench/competitions/hotel-id-2021-fgvc8/prepare.py +63 -0
- mlebench/competitions/hotel-id-2021-fgvc8/prepare_val.py +132 -0
- mlebench/competitions/hubmap-kidney-segmentation/grade.py +62 -0
- mlebench/competitions/hubmap-kidney-segmentation/prepare.py +108 -0
- mlebench/competitions/hubmap-kidney-segmentation/prepare_val.py +153 -0
- mlebench/competitions/icecube-neutrinos-in-deep-ice/grade.py +111 -0
- mlebench/competitions/icecube-neutrinos-in-deep-ice/prepare.py +127 -0
- mlebench/competitions/icecube-neutrinos-in-deep-ice/prepare_val.py +183 -0
- mlebench/competitions/ili/grade.py +60 -0
- mlebench/competitions/ili/prepare.py +99 -0
- mlebench/competitions/imet-2020-fgvc7/grade.py +54 -0
- mlebench/competitions/imet-2020-fgvc7/prepare.py +77 -0
- mlebench/competitions/imet-2020-fgvc7/prepare_val.py +157 -0
- mlebench/competitions/inaturalist-2019-fgvc6/grade.py +35 -0
- mlebench/competitions/inaturalist-2019-fgvc6/prepare.py +259 -0
- mlebench/competitions/inaturalist-2019-fgvc6/prepare_val.py +304 -0
- mlebench/competitions/instant-gratification/__init__.py +0 -0
- mlebench/competitions/instant-gratification/grade.py +55 -0
- mlebench/competitions/instant-gratification/prepare.py +25 -0
- mlebench/competitions/instant_gratification/__init__.py +0 -0
- mlebench/competitions/instant_gratification/grade.py +55 -0
- mlebench/competitions/instant_gratification/prepare.py +25 -0
- mlebench/competitions/invasive-species-monitoring/grade.py +11 -0
- mlebench/competitions/invasive-species-monitoring/prepare.py +97 -0
- mlebench/competitions/invasive-species-monitoring/prepare_val.py +164 -0
- mlebench/competitions/iwildcam-2019-fgvc6/grade.py +44 -0
- mlebench/competitions/iwildcam-2019-fgvc6/prepare.py +118 -0
- mlebench/competitions/iwildcam-2019-fgvc6/prepare_val.py +194 -0
- mlebench/competitions/iwildcam-2020-fgvc7/grade.py +11 -0
- mlebench/competitions/iwildcam-2020-fgvc7/prepare.py +164 -0
- mlebench/competitions/iwildcam-2020-fgvc7/prepare_val.py +245 -0
- mlebench/competitions/jigsaw-toxic-comment-classification-challenge/classes.py +1 -0
- mlebench/competitions/jigsaw-toxic-comment-classification-challenge/grade.py +54 -0
- mlebench/competitions/jigsaw-toxic-comment-classification-challenge/prepare.py +42 -0
- mlebench/competitions/jigsaw-toxic-comment-classification-challenge/prepare_val.py +88 -0
- mlebench/competitions/jigsaw-unintended-bias-in-toxicity-classification/grade.py +153 -0
- mlebench/competitions/jigsaw-unintended-bias-in-toxicity-classification/prepare.py +36 -0
- mlebench/competitions/jigsaw-unintended-bias-in-toxicity-classification/prepare_val.py +117 -0
- mlebench/competitions/kuzushiji-recognition/grade.py +58 -0
- mlebench/competitions/kuzushiji-recognition/kuzushiji_metric.py +118 -0
- mlebench/competitions/kuzushiji-recognition/prepare.py +92 -0
- mlebench/competitions/kuzushiji-recognition/prepare_val.py +149 -0
- mlebench/competitions/leaf-classification/classes.py +101 -0
- mlebench/competitions/leaf-classification/grade.py +44 -0
- mlebench/competitions/leaf-classification/prepare.py +60 -0
- mlebench/competitions/leaf-classification/prepare_val.py +116 -0
- mlebench/competitions/learning-agency-lab-automated-essay-scoring-2/grade.py +44 -0
- mlebench/competitions/learning-agency-lab-automated-essay-scoring-2/prepare.py +51 -0
- mlebench/competitions/learning-agency-lab-automated-essay-scoring-2/prepare_val.py +96 -0
- mlebench/competitions/liverpool-ion-switching/__init__.py +0 -0
- mlebench/competitions/liverpool-ion-switching/grade.py +52 -0
- mlebench/competitions/liverpool-ion-switching/prepare.py +27 -0
- mlebench/competitions/liverpool_ion_switching/__init__.py +0 -0
- mlebench/competitions/liverpool_ion_switching/grade.py +52 -0
- mlebench/competitions/liverpool_ion_switching/prepare.py +27 -0
- mlebench/competitions/lmsys-chatbot-arena/grade.py +63 -0
- mlebench/competitions/lmsys-chatbot-arena/prepare.py +52 -0
- mlebench/competitions/lmsys-chatbot-arena/prepare_val.py +115 -0
- mlebench/competitions/mcm_2024_c_test/grade.py +107 -0
- mlebench/competitions/mcm_2024_c_test/prepare.py +2 -0
- mlebench/competitions/ml2021spring-hw2/grade.py +11 -0
- mlebench/competitions/ml2021spring-hw2/prepare.py +58 -0
- mlebench/competitions/ml2021spring-hw2/prepare_val.py +135 -0
- mlebench/competitions/mlsp-2013-birds/grade.py +11 -0
- mlebench/competitions/mlsp-2013-birds/prepare.py +182 -0
- mlebench/competitions/mlsp-2013-birds/prepare_val.py +241 -0
- mlebench/competitions/movie-review-sentiment-analysis-kernels-only/grade.py +11 -0
- mlebench/competitions/movie-review-sentiment-analysis-kernels-only/prepare.py +58 -0
- mlebench/competitions/movie-review-sentiment-analysis-kernels-only/prepare_val.py +120 -0
- mlebench/competitions/multi-modal-gesture-recognition/grade.py +58 -0
- mlebench/competitions/multi-modal-gesture-recognition/prepare.py +85 -0
- mlebench/competitions/multi-modal-gesture-recognition/prepare_val.py +139 -0
- mlebench/competitions/my-custom-task-01/prepare.py +2 -0
- mlebench/competitions/new-my-task-01/prepare.py +2 -0
- mlebench/competitions/new-my-task-03/grade.py +107 -0
- mlebench/competitions/new-my-task-03/prepare.py +2 -0
- mlebench/competitions/new-york-city-taxi-fare-prediction/grade.py +28 -0
- mlebench/competitions/new-york-city-taxi-fare-prediction/prepare.py +44 -0
- mlebench/competitions/new-york-city-taxi-fare-prediction/prepare_val.py +89 -0
- mlebench/competitions/nfl-player-contact-detection/grade.py +36 -0
- mlebench/competitions/nfl-player-contact-detection/prepare.py +101 -0
- mlebench/competitions/nfl-player-contact-detection/prepare_val.py +186 -0
- mlebench/competitions/nomad2018-predict-transparent-conductors/grade.py +47 -0
- mlebench/competitions/nomad2018-predict-transparent-conductors/prepare.py +77 -0
- mlebench/competitions/nomad2018-predict-transparent-conductors/prepare_val.py +144 -0
- mlebench/competitions/osic-pulmonary-fibrosis-progression/grade.py +74 -0
- mlebench/competitions/osic-pulmonary-fibrosis-progression/prepare.py +95 -0
- mlebench/competitions/osic-pulmonary-fibrosis-progression/prepare_val.py +167 -0
- mlebench/competitions/paddy-disease-classification/grade.py +35 -0
- mlebench/competitions/paddy-disease-classification/prepare.py +69 -0
- mlebench/competitions/paddy-disease-classification/prepare_val.py +122 -0
- mlebench/competitions/petfinder-pawpularity-score/grade.py +41 -0
- mlebench/competitions/petfinder-pawpularity-score/prepare.py +76 -0
- mlebench/competitions/petfinder-pawpularity-score/prepare_val.py +154 -0
- mlebench/competitions/plant-pathology-2020-fgvc7/grade.py +41 -0
- mlebench/competitions/plant-pathology-2020-fgvc7/prepare.py +74 -0
- mlebench/competitions/plant-pathology-2020-fgvc7/prepare_val.py +160 -0
- mlebench/competitions/plant-pathology-2021-fgvc8/grade.py +54 -0
- mlebench/competitions/plant-pathology-2021-fgvc8/prepare.py +65 -0
- mlebench/competitions/plant-pathology-2021-fgvc8/prepare_val.py +130 -0
- mlebench/competitions/plant-seedlings-classification/grade.py +39 -0
- mlebench/competitions/plant-seedlings-classification/prepare.py +91 -0
- mlebench/competitions/plant-seedlings-classification/prepare_val.py +158 -0
- mlebench/competitions/playground-series-s3e1/__init__.py +0 -0
- mlebench/competitions/playground-series-s3e1/grade.py +52 -0
- mlebench/competitions/playground-series-s3e1/prepare.py +25 -0
- mlebench/competitions/playground-series-s3e11/__init__.py +0 -0
- mlebench/competitions/playground-series-s3e11/grade.py +55 -0
- mlebench/competitions/playground-series-s3e11/prepare.py +25 -0
- mlebench/competitions/playground-series-s3e18/grade.py +39 -0
- mlebench/competitions/playground-series-s3e18/prepare.py +36 -0
- mlebench/competitions/playground-series-s3e18/prepare_val.py +89 -0
- mlebench/competitions/playground_series_s3e1/__init__.py +0 -0
- mlebench/competitions/playground_series_s3e1/grade.py +52 -0
- mlebench/competitions/playground_series_s3e1/prepare.py +25 -0
- mlebench/competitions/playground_series_s3e11/__init__.py +0 -0
- mlebench/competitions/playground_series_s3e11/grade.py +55 -0
- mlebench/competitions/playground_series_s3e11/prepare.py +25 -0
- mlebench/competitions/predict-volcanic-eruptions-ingv-oe/grade.py +44 -0
- mlebench/competitions/predict-volcanic-eruptions-ingv-oe/prepare.py +68 -0
- mlebench/competitions/predict-volcanic-eruptions-ingv-oe/prepare_val.py +146 -0
- mlebench/competitions/random-acts-of-pizza/grade.py +14 -0
- mlebench/competitions/random-acts-of-pizza/prepare.py +80 -0
- mlebench/competitions/random-acts-of-pizza/prepare_val.py +144 -0
- mlebench/competitions/ranzcr-clip-catheter-line-classification/classes.py +11 -0
- mlebench/competitions/ranzcr-clip-catheter-line-classification/grade.py +31 -0
- mlebench/competitions/ranzcr-clip-catheter-line-classification/prepare.py +53 -0
- mlebench/competitions/ranzcr-clip-catheter-line-classification/prepare_val.py +113 -0
- mlebench/competitions/rsna-2022-cervical-spine-fracture-detection/grade.py +124 -0
- mlebench/competitions/rsna-2022-cervical-spine-fracture-detection/prepare.py +219 -0
- mlebench/competitions/rsna-2022-cervical-spine-fracture-detection/prepare_val.py +257 -0
- mlebench/competitions/rsna-breast-cancer-detection/grade.py +65 -0
- mlebench/competitions/rsna-breast-cancer-detection/prepare.py +141 -0
- mlebench/competitions/rsna-breast-cancer-detection/prepare_val.py +201 -0
- mlebench/competitions/rsna-miccai-brain-tumor-radiogenomic-classification/grade.py +13 -0
- mlebench/competitions/rsna-miccai-brain-tumor-radiogenomic-classification/prepare.py +47 -0
- mlebench/competitions/rsna-miccai-brain-tumor-radiogenomic-classification/prepare_val.py +97 -0
- mlebench/competitions/santander-customer-satisfaction/grade.py +10 -0
- mlebench/competitions/santander-customer-satisfaction/prepare.py +41 -0
- mlebench/competitions/sciencebench-001-clintox-nn/__init__.py +0 -0
- mlebench/competitions/sciencebench-001-clintox-nn/grade.py +56 -0
- mlebench/competitions/sciencebench-001-clintox-nn/prepare.py +75 -0
- mlebench/competitions/sciencebench-015-aai/grade.py +37 -0
- mlebench/competitions/sciencebench-015-aai/prepare.py +102 -0
- mlebench/competitions/sciencebench-051-brain-blood-qsar/grade.py +58 -0
- mlebench/competitions/sciencebench-051-brain-blood-qsar/prepare.py +69 -0
- mlebench/competitions/sciencebench-101-experimental-band-gap-prediction/grade.py +55 -0
- mlebench/competitions/sciencebench-101-experimental-band-gap-prediction/prepare.py +88 -0
- mlebench/competitions/see-click-predict-fix/__init__.py +0 -0
- mlebench/competitions/see-click-predict-fix/grade.py +66 -0
- mlebench/competitions/see-click-predict-fix/prepare.py +25 -0
- mlebench/competitions/see_click_predict_fix/__init__.py +0 -0
- mlebench/competitions/see_click_predict_fix/grade.py +66 -0
- mlebench/competitions/see_click_predict_fix/prepare.py +25 -0
- mlebench/competitions/seti-breakthrough-listen/grade.py +11 -0
- mlebench/competitions/seti-breakthrough-listen/prepare.py +71 -0
- mlebench/competitions/seti-breakthrough-listen/prepare_val.py +159 -0
- mlebench/competitions/siim-covid19-detection/grade.py +194 -0
- mlebench/competitions/siim-covid19-detection/prepare.py +123 -0
- mlebench/competitions/siim-covid19-detection/prepare_val.py +164 -0
- mlebench/competitions/siim-isic-melanoma-classification/grade.py +11 -0
- mlebench/competitions/siim-isic-melanoma-classification/prepare.py +127 -0
- mlebench/competitions/siim-isic-melanoma-classification/prepare_val.py +158 -0
- mlebench/competitions/smartphone-decimeter-2022/grade.py +55 -0
- mlebench/competitions/smartphone-decimeter-2022/notebook.py +86 -0
- mlebench/competitions/smartphone-decimeter-2022/prepare.py +143 -0
- mlebench/competitions/smartphone-decimeter-2022/prepare_val.py +199 -0
- mlebench/competitions/spaceship-titanic/grade.py +11 -0
- mlebench/competitions/spaceship-titanic/prepare.py +23 -0
- mlebench/competitions/spaceship-titanic/prepare_val.py +61 -0
- mlebench/competitions/spooky-author-identification/classes.py +1 -0
- mlebench/competitions/spooky-author-identification/grade.py +38 -0
- mlebench/competitions/spooky-author-identification/prepare.py +40 -0
- mlebench/competitions/spooky-author-identification/prepare_val.py +78 -0
- mlebench/competitions/stanford-covid-vaccine/grade.py +65 -0
- mlebench/competitions/stanford-covid-vaccine/prepare.py +129 -0
- mlebench/competitions/stanford-covid-vaccine/prepare_val.py +199 -0
- mlebench/competitions/statoil-iceberg-classifier-challenge/grade.py +41 -0
- mlebench/competitions/statoil-iceberg-classifier-challenge/prepare.py +105 -0
- mlebench/competitions/statoil-iceberg-classifier-challenge/prepare_val.py +157 -0
- mlebench/competitions/tabular-playground-series-dec-2021/grade.py +11 -0
- mlebench/competitions/tabular-playground-series-dec-2021/prepare.py +39 -0
- mlebench/competitions/tabular-playground-series-dec-2021/prepare_val.py +99 -0
- mlebench/competitions/tabular-playground-series-may-2022/grade.py +9 -0
- mlebench/competitions/tabular-playground-series-may-2022/prepare.py +56 -0
- mlebench/competitions/tabular-playground-series-may-2022/prepare_val.py +116 -0
- mlebench/competitions/tensorflow-speech-recognition-challenge/grade.py +11 -0
- mlebench/competitions/tensorflow-speech-recognition-challenge/prepare.py +90 -0
- mlebench/competitions/tensorflow-speech-recognition-challenge/prepare_val.py +148 -0
- mlebench/competitions/tensorflow2-question-answering/grade.py +122 -0
- mlebench/competitions/tensorflow2-question-answering/prepare.py +122 -0
- mlebench/competitions/tensorflow2-question-answering/prepare_val.py +187 -0
- mlebench/competitions/text-normalization-challenge-english-language/grade.py +49 -0
- mlebench/competitions/text-normalization-challenge-english-language/prepare.py +115 -0
- mlebench/competitions/text-normalization-challenge-english-language/prepare_val.py +213 -0
- mlebench/competitions/text-normalization-challenge-russian-language/grade.py +49 -0
- mlebench/competitions/text-normalization-challenge-russian-language/prepare.py +113 -0
- mlebench/competitions/text-normalization-challenge-russian-language/prepare_val.py +165 -0
- mlebench/competitions/tgs-salt-identification-challenge/grade.py +144 -0
- mlebench/competitions/tgs-salt-identification-challenge/prepare.py +158 -0
- mlebench/competitions/tgs-salt-identification-challenge/prepare_val.py +166 -0
- mlebench/competitions/the-icml-2013-whale-challenge-right-whale-redux/grade.py +11 -0
- mlebench/competitions/the-icml-2013-whale-challenge-right-whale-redux/prepare.py +95 -0
- mlebench/competitions/the-icml-2013-whale-challenge-right-whale-redux/prepare_val.py +141 -0
- mlebench/competitions/tmdb-box-office-prediction/__init__.py +0 -0
- mlebench/competitions/tmdb-box-office-prediction/grade.py +55 -0
- mlebench/competitions/tmdb-box-office-prediction/prepare.py +35 -0
- mlebench/competitions/tweet-sentiment-extraction/grade.py +67 -0
- mlebench/competitions/tweet-sentiment-extraction/prepare.py +36 -0
- mlebench/competitions/tweet-sentiment-extraction/prepare_val.py +106 -0
- mlebench/competitions/us-patent-phrase-to-phrase-matching/grade.py +31 -0
- mlebench/competitions/us-patent-phrase-to-phrase-matching/prepare.py +33 -0
- mlebench/competitions/us-patent-phrase-to-phrase-matching/prepare_val.py +71 -0
- mlebench/competitions/utils.py +266 -0
- mlebench/competitions/uw-madison-gi-tract-image-segmentation/grade.py +158 -0
- mlebench/competitions/uw-madison-gi-tract-image-segmentation/prepare.py +139 -0
- mlebench/competitions/uw-madison-gi-tract-image-segmentation/prepare_val.py +193 -0
- mlebench/competitions/ventilator-pressure-prediction/__init__.py +0 -0
- mlebench/competitions/ventilator-pressure-prediction/grade.py +52 -0
- mlebench/competitions/ventilator-pressure-prediction/prepare.py +27 -0
- mlebench/competitions/ventilator-pressure-prediction/prepare_val.py +142 -0
- mlebench/competitions/ventilator_pressure_prediction/__init__.py +0 -0
- mlebench/competitions/ventilator_pressure_prediction/grade.py +52 -0
- mlebench/competitions/ventilator_pressure_prediction/prepare.py +27 -0
- mlebench/competitions/vesuvius-challenge-ink-detection/grade.py +97 -0
- mlebench/competitions/vesuvius-challenge-ink-detection/prepare.py +122 -0
- mlebench/competitions/vesuvius-challenge-ink-detection/prepare_val.py +170 -0
- mlebench/competitions/vinbigdata-chest-xray-abnormalities-detection/grade.py +220 -0
- mlebench/competitions/vinbigdata-chest-xray-abnormalities-detection/prepare.py +129 -0
- mlebench/competitions/vinbigdata-chest-xray-abnormalities-detection/prepare_val.py +204 -0
- mlebench/competitions/whale-categorization-playground/grade.py +41 -0
- mlebench/competitions/whale-categorization-playground/prepare.py +103 -0
- mlebench/competitions/whale-categorization-playground/prepare_val.py +196 -0
- mlebench/data.py +420 -0
- mlebench/grade.py +209 -0
- mlebench/grade_helpers.py +235 -0
- mlebench/metrics.py +75 -0
- mlebench/registry.py +332 -0
- mlebench/utils.py +346 -0
- {dslighting-1.7.1.dist-info → dslighting-1.7.8.dist-info}/WHEEL +0 -0
- {dslighting-1.7.1.dist-info → dslighting-1.7.8.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
import pandas as pd
|
|
3
|
+
|
|
4
|
+
from sklearn.model_selection import train_test_split
|
|
5
|
+
|
|
6
|
+
from mlebench.utils import read_csv
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _split_and_save(
|
|
10
|
+
source_df: pd.DataFrame,
|
|
11
|
+
test_size: float,
|
|
12
|
+
random_state: int,
|
|
13
|
+
public_dir: Path,
|
|
14
|
+
private_dir: Path,
|
|
15
|
+
label_col: str = "selected_text",
|
|
16
|
+
id_col: str = "textID",
|
|
17
|
+
) -> tuple[pd.DataFrame, pd.DataFrame]:
|
|
18
|
+
"""
|
|
19
|
+
Helper function to perform a train/test split and save the results.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
source_df: The DataFrame to split.
|
|
23
|
+
test_size: The proportion of the dataset to allocate to the test split.
|
|
24
|
+
random_state: The seed used by the random number generator.
|
|
25
|
+
public_dir: The directory to save public artifacts (train set, unlabeled test set).
|
|
26
|
+
private_dir: The directory to save private artifacts (labeled test set).
|
|
27
|
+
label_col: The name of the column containing the label.
|
|
28
|
+
id_col: The name of the column containing the unique ID.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
A tuple containing the created train and test DataFrames.
|
|
32
|
+
"""
|
|
33
|
+
# Ensure output directories exist
|
|
34
|
+
public_dir.mkdir(parents=True, exist_ok=True)
|
|
35
|
+
private_dir.mkdir(parents=True, exist_ok=True)
|
|
36
|
+
|
|
37
|
+
# Perform the split
|
|
38
|
+
train_df, test_df = train_test_split(
|
|
39
|
+
source_df, test_size=test_size, random_state=random_state
|
|
40
|
+
)
|
|
41
|
+
test_df_without_labels = test_df.drop(columns=[label_col])
|
|
42
|
+
|
|
43
|
+
# Save the datasets
|
|
44
|
+
train_df.to_csv(public_dir / "train.csv", index=False)
|
|
45
|
+
test_df.to_csv(private_dir / "test.csv", index=False)
|
|
46
|
+
test_df_without_labels.to_csv(public_dir / "test.csv", index=False)
|
|
47
|
+
|
|
48
|
+
assert len(test_df_without_labels) == len(
|
|
49
|
+
test_df
|
|
50
|
+
), f"Expected test_df_without_labels ({len(test_df_without_labels)}) == test_df ({len(test_df)})"
|
|
51
|
+
assert len(train_df) + len(test_df) == len(
|
|
52
|
+
source_df
|
|
53
|
+
), f"Expected train_df ({len(train_df)}) + test_df ({len(test_df)}) == source_df ({len(source_df)})"
|
|
54
|
+
|
|
55
|
+
# Create and save a sample submission file
|
|
56
|
+
submission_df = test_df.copy()[[id_col, label_col]]
|
|
57
|
+
submission_df[label_col] = ""
|
|
58
|
+
|
|
59
|
+
submission_df.to_csv(public_dir / "sample_submission.csv", index=False)
|
|
60
|
+
assert len(submission_df) == len(
|
|
61
|
+
test_df
|
|
62
|
+
), f"Expected submission_df ({len(submission_df)}) == test_df ({len(test_df)})"
|
|
63
|
+
|
|
64
|
+
return train_df, test_df
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def prepare(raw: Path, public: Path, private: Path):
|
|
68
|
+
"""
|
|
69
|
+
Splits the data in raw into public and private datasets with appropriate test/train splits.
|
|
70
|
+
Also creates a second, parallel split for validation purposes.
|
|
71
|
+
"""
|
|
72
|
+
old_train = read_csv(raw / "train.csv")
|
|
73
|
+
|
|
74
|
+
# ---- Original Data Split (for final test set) ----
|
|
75
|
+
# This split creates the main train and test sets for the competition.
|
|
76
|
+
# The outputs in `public` and `private` are left completely unchanged.
|
|
77
|
+
train_for_val_split, _ = _split_and_save(
|
|
78
|
+
source_df=old_train,
|
|
79
|
+
test_size=0.1,
|
|
80
|
+
random_state=0,
|
|
81
|
+
public_dir=public,
|
|
82
|
+
private_dir=private,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# ---- New Validation Data Split ----
|
|
86
|
+
# This second split takes the training set created above and splits it *again*
|
|
87
|
+
# to create a new, smaller training set and a validation set. The results are
|
|
88
|
+
# saved to parallel `public_val` and `private_val` directories.
|
|
89
|
+
|
|
90
|
+
# Define paths for the new validation split
|
|
91
|
+
public_val = public.parent / "public_val"
|
|
92
|
+
private_val = private.parent / "private_val"
|
|
93
|
+
|
|
94
|
+
# The original test set size was 0.1 of the total data. The new train set
|
|
95
|
+
# is 0.9 of the total. To make the new validation set (test_val) have
|
|
96
|
+
# roughly the same number of samples as the original test set, we use a
|
|
97
|
+
# test_size of (0.1 / 0.9) = 1/9.
|
|
98
|
+
validation_test_size = 1 / 9
|
|
99
|
+
|
|
100
|
+
_split_and_save(
|
|
101
|
+
source_df=train_for_val_split,
|
|
102
|
+
test_size=validation_test_size,
|
|
103
|
+
random_state=0, # Use same random_state for consistency
|
|
104
|
+
public_dir=public_val,
|
|
105
|
+
private_dir=private_val,
|
|
106
|
+
)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
from scipy.stats import pearsonr
|
|
3
|
+
|
|
4
|
+
from mlebench.grade_helpers import InvalidSubmissionError
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def prepare_for_metric(submission: pd.DataFrame, answers: pd.DataFrame) -> tuple:
|
|
8
|
+
"""
|
|
9
|
+
`submission` and `answers` are pd.DataFrames with columns `id` and `score`
|
|
10
|
+
Submissions are evaluated on the Pearson correlation coefficient between the predicted and actual similarity scores.
|
|
11
|
+
"""
|
|
12
|
+
if len(submission) != len(answers):
|
|
13
|
+
raise InvalidSubmissionError("Submission and answers must have the same number of rows")
|
|
14
|
+
if not set(["id", "score"]).issubset(submission.columns):
|
|
15
|
+
raise InvalidSubmissionError(
|
|
16
|
+
f"Submission must have columns ['id', 'score'], got {submission.columns}"
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
submission = submission.sort_values("id")
|
|
20
|
+
answers = answers.sort_values("id")
|
|
21
|
+
if (submission["id"].values != answers["id"].values).any():
|
|
22
|
+
raise InvalidSubmissionError("Ids mismatch between submission and answers")
|
|
23
|
+
y_true = answers["score"].to_numpy()
|
|
24
|
+
y_pred = submission["score"].to_numpy()
|
|
25
|
+
|
|
26
|
+
return y_true, y_pred
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def grade(submission: pd.DataFrame, answers: pd.DataFrame) -> float:
|
|
30
|
+
y_true, y_pred = prepare_for_metric(submission, answers)
|
|
31
|
+
return pearsonr(y_true, y_pred)[0]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from sklearn.model_selection import train_test_split
|
|
4
|
+
|
|
5
|
+
from mlebench.utils import read_csv
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def prepare(raw: Path, public: Path, private: Path):
|
|
9
|
+
"""
|
|
10
|
+
Splits the data in raw into public and private datasets with appropriate test/train splits.
|
|
11
|
+
"""
|
|
12
|
+
public.mkdir(exist_ok=True)
|
|
13
|
+
private.mkdir(exist_ok=True)
|
|
14
|
+
|
|
15
|
+
# Create train, test from train split
|
|
16
|
+
old_train = read_csv(raw / "train.csv")
|
|
17
|
+
new_train, new_test = train_test_split(old_train, test_size=0.1, random_state=0)
|
|
18
|
+
new_test_without_labels = new_test.drop(columns=["score"])
|
|
19
|
+
|
|
20
|
+
# Save new train and test
|
|
21
|
+
new_train.to_csv(public / "train.csv", index=False)
|
|
22
|
+
new_test.to_csv(private / "test.csv", index=False)
|
|
23
|
+
new_test_without_labels.to_csv(public / "test.csv", index=False)
|
|
24
|
+
|
|
25
|
+
assert len(new_train) + len(new_test) == len(old_train)
|
|
26
|
+
assert len(new_test) == len(new_test_without_labels)
|
|
27
|
+
|
|
28
|
+
# Create a sample submission file
|
|
29
|
+
submission_df = new_test.copy()[["id", "score"]]
|
|
30
|
+
submission_df["score"] = 0
|
|
31
|
+
submission_df.to_csv(public / "sample_submission.csv", index=False)
|
|
32
|
+
|
|
33
|
+
assert len(submission_df) == len(new_test)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from sklearn.model_selection import train_test_split
|
|
4
|
+
|
|
5
|
+
from mlebench.utils import read_csv
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def prepare(raw: Path, public: Path, private: Path):
|
|
9
|
+
"""
|
|
10
|
+
Splits the data in raw into public and private datasets with appropriate test/train splits.
|
|
11
|
+
Additionally, it creates a secondary validation split for model development.
|
|
12
|
+
"""
|
|
13
|
+
# Create directories for the primary competition split
|
|
14
|
+
public.mkdir(exist_ok=True)
|
|
15
|
+
private.mkdir(exist_ok=True)
|
|
16
|
+
|
|
17
|
+
# Create directories for the new validation split
|
|
18
|
+
public_val = public.parent / "public_val"
|
|
19
|
+
private_val = private.parent / "private_val"
|
|
20
|
+
public_val.mkdir(exist_ok=True)
|
|
21
|
+
private_val.mkdir(exist_ok=True)
|
|
22
|
+
|
|
23
|
+
# Load raw data
|
|
24
|
+
old_train = read_csv(raw / "train.csv")
|
|
25
|
+
|
|
26
|
+
# --- Block 1: Original Split (for final competition data) ---
|
|
27
|
+
# This block creates the main train/test split. Its output in `public`
|
|
28
|
+
# and `private` must remain unchanged from the original script.
|
|
29
|
+
|
|
30
|
+
# Create train, test from the full raw data
|
|
31
|
+
train, test = train_test_split(old_train, test_size=0.1, random_state=0)
|
|
32
|
+
test_without_labels = test.drop(columns=["score"])
|
|
33
|
+
|
|
34
|
+
# Save new train and test to the original directories
|
|
35
|
+
train.to_csv(public / "train.csv", index=False)
|
|
36
|
+
test.to_csv(private / "test.csv", index=False)
|
|
37
|
+
test_without_labels.to_csv(public / "test.csv", index=False)
|
|
38
|
+
|
|
39
|
+
assert len(train) + len(test) == len(old_train)
|
|
40
|
+
assert len(test) == len(test_without_labels)
|
|
41
|
+
|
|
42
|
+
# Create a sample submission file for the original test set
|
|
43
|
+
submission_df = test.copy()[["id", "score"]]
|
|
44
|
+
submission_df["score"] = 0
|
|
45
|
+
submission_df.to_csv(public / "sample_submission.csv", index=False)
|
|
46
|
+
|
|
47
|
+
assert len(submission_df) == len(test)
|
|
48
|
+
|
|
49
|
+
# --- Block 2: New Validation Split (for model development) ---
|
|
50
|
+
# This block takes the `train` data from the first split and splits it
|
|
51
|
+
# again to create a new, smaller training set and a validation set.
|
|
52
|
+
# The logic and filenames mirror the original split for consistency.
|
|
53
|
+
|
|
54
|
+
# Create a new train/validation split from the main training set
|
|
55
|
+
train_val, test_val = train_test_split(train, test_size=0.1, random_state=0)
|
|
56
|
+
test_val_without_labels = test_val.drop(columns=["score"])
|
|
57
|
+
|
|
58
|
+
# Save the new validation split data into the `_val` directories
|
|
59
|
+
train_val.to_csv(public_val / "train.csv", index=False)
|
|
60
|
+
test_val.to_csv(private_val / "test.csv", index=False)
|
|
61
|
+
test_val_without_labels.to_csv(public_val / "test.csv", index=False)
|
|
62
|
+
|
|
63
|
+
assert len(train_val) + len(test_val) == len(train)
|
|
64
|
+
assert len(test_val) == len(test_val_without_labels)
|
|
65
|
+
|
|
66
|
+
# Create a sample submission file for the new validation set
|
|
67
|
+
submission_val_df = test_val.copy()[["id", "score"]]
|
|
68
|
+
submission_val_df["score"] = 0
|
|
69
|
+
submission_val_df.to_csv(public_val / "sample_submission.csv", index=False)
|
|
70
|
+
|
|
71
|
+
assert len(submission_val_df) == len(test_val)
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pandas as pd
|
|
5
|
+
from numpy import ndarray
|
|
6
|
+
from scipy.special import softmax
|
|
7
|
+
|
|
8
|
+
from mlebench.grade_helpers import InvalidSubmissionError
|
|
9
|
+
from mlebench.utils import get_logger
|
|
10
|
+
|
|
11
|
+
logger = get_logger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def df_to_one_hot(
|
|
15
|
+
df: pd.DataFrame, id_column: str, target_column: str, classes: list[str]
|
|
16
|
+
) -> pd.DataFrame:
|
|
17
|
+
"""
|
|
18
|
+
Convert class labels to one-hot encoded vectors.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
y_onehot = pd.DataFrame(0, index=df.index, columns=[id_column] + classes)
|
|
22
|
+
y_onehot[id_column] = df[id_column]
|
|
23
|
+
|
|
24
|
+
for i, row in df.iterrows():
|
|
25
|
+
y_onehot.loc[i, row[target_column]] = 1
|
|
26
|
+
|
|
27
|
+
return y_onehot
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def one_hot_dfs_to_log_loss_inputs(
|
|
31
|
+
submission_one_hot: pd.DataFrame,
|
|
32
|
+
answers_one_hot: pd.DataFrame,
|
|
33
|
+
id_column: str = "id",
|
|
34
|
+
apply_softmax: bool = True,
|
|
35
|
+
) -> dict:
|
|
36
|
+
"""
|
|
37
|
+
Frequently used logic to prepare one-hotted dfs for log loss calculation.
|
|
38
|
+
"""
|
|
39
|
+
required_cols = set(answers_one_hot.columns)
|
|
40
|
+
submission_cols = set(submission_one_hot.columns)
|
|
41
|
+
|
|
42
|
+
if not submission_cols.issuperset(required_cols):
|
|
43
|
+
raise InvalidSubmissionError(
|
|
44
|
+
f"The submission DataFrame is missing some columns required by the `answers` DataFrame. "
|
|
45
|
+
f"Missing columns: {required_cols - submission_cols}."
|
|
46
|
+
)
|
|
47
|
+
if id_column not in submission_one_hot.columns:
|
|
48
|
+
raise InvalidSubmissionError(f"Submission is missing id column '{id_column}'.")
|
|
49
|
+
|
|
50
|
+
assert id_column in answers_one_hot.columns, f"Answers is missing id column '{id_column}'."
|
|
51
|
+
|
|
52
|
+
# Filter submission to only include columns that are in the answers
|
|
53
|
+
submission_filtered = submission_one_hot[
|
|
54
|
+
[col for col in answers_one_hot.columns if col in submission_cols]
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
# Sort submission and answers by id to align them
|
|
58
|
+
submission_sorted = submission_filtered.sort_values(by=id_column).reset_index(drop=True)
|
|
59
|
+
answers_sorted = answers_one_hot.sort_values(by=id_column).reset_index(drop=True)
|
|
60
|
+
|
|
61
|
+
assert submission_sorted[id_column].tolist() == answers_sorted[id_column].tolist(), (
|
|
62
|
+
f"Mismatch in {id_column.capitalize()}s between `submission` and `answers` after sorting. "
|
|
63
|
+
f"Number of mismatched {id_column.capitalize()}s: {len(set(submission_sorted[id_column]) ^ set(answers_sorted[id_column]))}. "
|
|
64
|
+
f"Ensure both DataFrames have the same {id_column.capitalize()}s."
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
assert list(submission_sorted.columns) == list(answers_sorted.columns), (
|
|
68
|
+
"Column order mismatch after filtering and sorting. "
|
|
69
|
+
"Ensure both DataFrames have columns in the same order."
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
y_true = answers_sorted.drop(columns=[id_column]).to_numpy()
|
|
73
|
+
y_pred = submission_sorted.drop(columns=[id_column]).to_numpy()
|
|
74
|
+
|
|
75
|
+
if apply_softmax and is_one_hot_encoded(y_pred):
|
|
76
|
+
logger.warning(
|
|
77
|
+
"The flag `apply_softmax` has been set to `True` but the submission is already "
|
|
78
|
+
"one-hot encoded. Skipping softmax."
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
if apply_softmax and not is_one_hot_encoded(y_pred):
|
|
82
|
+
y_pred = softmax(y_pred, axis=-1)
|
|
83
|
+
|
|
84
|
+
log_loss_inputs = {
|
|
85
|
+
"y_true": y_true,
|
|
86
|
+
"y_pred": y_pred,
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return log_loss_inputs
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def is_one_hot_encoded(xs: ndarray) -> bool:
|
|
93
|
+
"""Check if a 2D NumPy array is one-hot encoded."""
|
|
94
|
+
|
|
95
|
+
assert isinstance(xs, ndarray), f"Expected a NumPy array, got {type(xs)}."
|
|
96
|
+
assert xs.ndim == 2, f"Expected a 2D array, got {xs.ndim}D."
|
|
97
|
+
|
|
98
|
+
is_binary_matrix = np.bitwise_or(xs == 0, xs == 1).all()
|
|
99
|
+
is_normalized = np.allclose(xs.sum(axis=-1), 1)
|
|
100
|
+
is_one_hot = bool(is_binary_matrix and is_normalized)
|
|
101
|
+
|
|
102
|
+
assert isinstance(is_one_hot, bool), f"Expected a boolean, got {type(is_one_hot)}."
|
|
103
|
+
|
|
104
|
+
return is_one_hot
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def rle_decode(rle_string: str, height: int, width: int) -> ndarray:
|
|
108
|
+
"""
|
|
109
|
+
Decode an RLE string into a binary mask. The RLE encoding is top-down, left-right. So 1 is
|
|
110
|
+
(1,1), 2 is (2, 1), etc. The RLE is 1-indexed. Checks that the pairs are sorted, positive, and
|
|
111
|
+
the decoded pixel values do not overlap.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
rle_string (str): The RLE string.
|
|
115
|
+
height (int): The height of the image.
|
|
116
|
+
width (int): The width of the image.
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
np.array: The decoded binary mask.
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
assert isinstance(
|
|
123
|
+
rle_string, str
|
|
124
|
+
), f"Expected a string, but got {type(rle_string)}: {rle_string}"
|
|
125
|
+
assert isinstance(height, int), f"Expected an integer, but got {type(height)}: {height}"
|
|
126
|
+
assert isinstance(width, int), f"Expected an integer, but got {type(width)}: {width}"
|
|
127
|
+
|
|
128
|
+
if not rle_string.strip(): # Check if the string is empty or contains only whitespace
|
|
129
|
+
return np.zeros((height, width), dtype=bool)
|
|
130
|
+
|
|
131
|
+
s = list(map(int, rle_string.split()))
|
|
132
|
+
starts, lengths = s[0::2], s[1::2]
|
|
133
|
+
|
|
134
|
+
assert starts == sorted(starts), "The pairs in the RLE string must be sorted."
|
|
135
|
+
assert all(x > 0 for x in starts), "All pairs in the RLE string must be positive integers."
|
|
136
|
+
assert all(x > 0 for x in lengths), "All pairs in the RLE string must be positive integers."
|
|
137
|
+
|
|
138
|
+
# Convert to 0-based indices
|
|
139
|
+
starts = np.array(starts) - 1
|
|
140
|
+
ends = starts + lengths
|
|
141
|
+
img = np.zeros(height * width, dtype=bool)
|
|
142
|
+
|
|
143
|
+
for lo, hi in zip(starts, ends):
|
|
144
|
+
assert not img[lo:hi].any(), "Overlapping RLE pairs are not allowed."
|
|
145
|
+
img[lo:hi] = True
|
|
146
|
+
|
|
147
|
+
# reshape appropriately given how the RLE was encoded
|
|
148
|
+
return img.reshape((width, height)).T
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
# https://www.kaggle.com/code/inversion/contrails-rle-submission
|
|
152
|
+
def rle_encode(x: ndarray, fg_val=1):
|
|
153
|
+
"""
|
|
154
|
+
Args:
|
|
155
|
+
x: numpy array of shape (height, width), 1 - mask, 0 - background
|
|
156
|
+
Returns: run length encoding as list
|
|
157
|
+
"""
|
|
158
|
+
dots = np.where(x.T.flatten() == fg_val)[0] # .T sets Fortran order down-then-right
|
|
159
|
+
run_lengths = []
|
|
160
|
+
prev = -2
|
|
161
|
+
for b in dots:
|
|
162
|
+
if b > prev + 1:
|
|
163
|
+
run_lengths.extend((b + 1, 0))
|
|
164
|
+
run_lengths[-1] += 1
|
|
165
|
+
prev = b
|
|
166
|
+
return run_lengths
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def rles_to_masks(
|
|
170
|
+
rl_encodings: list[str], image_heights: list[int], image_widths: list[int]
|
|
171
|
+
) -> list[np.ndarray]:
|
|
172
|
+
"""
|
|
173
|
+
Performs run-length decoding on a list of run-length encodings to get the binary masks
|
|
174
|
+
"""
|
|
175
|
+
masks = [
|
|
176
|
+
rle_decode(encoding, height=image_height, width=image_width)
|
|
177
|
+
for encoding, image_height, image_width in zip(rl_encodings, image_heights, image_widths)
|
|
178
|
+
]
|
|
179
|
+
return masks
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def get_ids_from_tf_records(tf_record_path: Path, id_feature: str = "image_name") -> list[str]:
|
|
183
|
+
import tensorflow as tf # Import only if needed, otherwise it slows down the module import
|
|
184
|
+
|
|
185
|
+
tf_record_dataset = tf.data.TFRecordDataset(tf_record_path.as_posix())
|
|
186
|
+
|
|
187
|
+
ids = []
|
|
188
|
+
for record in tf_record_dataset:
|
|
189
|
+
features = tf.train.Example.FromString(record.numpy())
|
|
190
|
+
id = features.features.feature[id_feature].bytes_list.value[0].decode("utf-8")
|
|
191
|
+
ids.append(id)
|
|
192
|
+
|
|
193
|
+
return ids
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def prepare_for_accuracy_metric(
|
|
197
|
+
submission: pd.DataFrame,
|
|
198
|
+
answers: pd.DataFrame,
|
|
199
|
+
target_column: str,
|
|
200
|
+
id_column: str,
|
|
201
|
+
) -> dict:
|
|
202
|
+
|
|
203
|
+
# Answers checks
|
|
204
|
+
assert target_column in answers.columns, f"Answers must have a `{target_column}` column"
|
|
205
|
+
assert id_column in answers.columns, f"Answers must have a `{id_column}` column"
|
|
206
|
+
|
|
207
|
+
# Submission checks
|
|
208
|
+
if len(submission) != len(answers):
|
|
209
|
+
raise InvalidSubmissionError("Submission must have the same length as the answers.")
|
|
210
|
+
if target_column not in submission.columns:
|
|
211
|
+
raise InvalidSubmissionError(f"Submission must have a `{target_column}` column")
|
|
212
|
+
if id_column not in submission.columns:
|
|
213
|
+
raise InvalidSubmissionError(f"Submission must have a `{id_column}` column")
|
|
214
|
+
|
|
215
|
+
# sort on id to ensure correct order
|
|
216
|
+
submission = submission.sort_values(id_column)
|
|
217
|
+
answers = answers.sort_values(id_column)
|
|
218
|
+
|
|
219
|
+
if (submission[id_column].values != answers[id_column].values).any():
|
|
220
|
+
raise InvalidSubmissionError(f"Submission and Answers `{id_column}`'s do not match")
|
|
221
|
+
|
|
222
|
+
y_pred = submission[target_column].to_numpy()
|
|
223
|
+
y_true = answers[target_column].to_numpy()
|
|
224
|
+
|
|
225
|
+
return {"y_true": y_true, "y_pred": y_pred}
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def prepare_for_auroc_metric(
|
|
229
|
+
submission: pd.DataFrame, answers: pd.DataFrame, id_col: str, target_col: str
|
|
230
|
+
) -> dict:
|
|
231
|
+
|
|
232
|
+
# Answers checks
|
|
233
|
+
assert id_col in answers.columns, f"answers dataframe should have an {id_col} column"
|
|
234
|
+
assert target_col in answers.columns, f"answers dataframe should have a {target_col} column"
|
|
235
|
+
|
|
236
|
+
# Submission checks
|
|
237
|
+
if id_col not in submission.columns:
|
|
238
|
+
raise InvalidSubmissionError(f"Submission should have an {id_col} column")
|
|
239
|
+
if target_col not in submission.columns:
|
|
240
|
+
raise InvalidSubmissionError(f"Submission should have a {target_col} column")
|
|
241
|
+
if len(submission) != len(answers):
|
|
242
|
+
raise InvalidSubmissionError(f"Submission and answers should have the same number of rows")
|
|
243
|
+
try:
|
|
244
|
+
pd.to_numeric(submission[target_col])
|
|
245
|
+
except ValueError:
|
|
246
|
+
raise InvalidSubmissionError(
|
|
247
|
+
f"Expected {target_col} column to be numeric, got {submission[target_col].dtype} instead"
|
|
248
|
+
)
|
|
249
|
+
if submission[target_col].min() < 0 or submission[target_col].max() > 1:
|
|
250
|
+
raise InvalidSubmissionError(
|
|
251
|
+
f"Submission {target_col} column should contain probabilities,"
|
|
252
|
+
" and therefore contain values between 0 and 1 inclusive"
|
|
253
|
+
)
|
|
254
|
+
# Sort
|
|
255
|
+
submission = submission.sort_values(id_col)
|
|
256
|
+
answers = answers.sort_values(id_col)
|
|
257
|
+
|
|
258
|
+
if (submission[id_col].values != answers[id_col].values).any():
|
|
259
|
+
raise InvalidSubmissionError(f"Submission and answers should have the same {id_col} values")
|
|
260
|
+
|
|
261
|
+
roc_auc_inputs = {
|
|
262
|
+
"y_true": answers[target_col].to_numpy(),
|
|
263
|
+
"y_score": submission[target_col].to_numpy(),
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return roc_auc_inputs
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from pandas import DataFrame
|
|
3
|
+
from scipy.spatial.distance import directed_hausdorff
|
|
4
|
+
from tqdm.auto import tqdm
|
|
5
|
+
|
|
6
|
+
from mlebench.competitions.utils import rles_to_masks
|
|
7
|
+
from mlebench.grade_helpers import InvalidSubmissionError
|
|
8
|
+
from mlebench.metrics import dice_coefficient
|
|
9
|
+
|
|
10
|
+
UNIT_CUBE_DIAGONAL = np.sqrt(3)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def group_masks_by_day(masks: list[np.ndarray], cases_days_slices: list[str]) -> list[np.ndarray]:
|
|
14
|
+
"""
|
|
15
|
+
Groups the 2D masks into 3D masks based on the case days
|
|
16
|
+
- We first join masks of the same slice into a single mask with an OR operation
|
|
17
|
+
- We then stack them with all the other slices from the same day in the same case into a 3D image
|
|
18
|
+
"""
|
|
19
|
+
case_day_slice_to_masks = {}
|
|
20
|
+
unique_slices = [] # list to keep ordering consistent
|
|
21
|
+
for mask, case_day_slice in sorted(zip(masks, cases_days_slices), key=lambda x: x[1]):
|
|
22
|
+
if case_day_slice not in case_day_slice_to_masks:
|
|
23
|
+
case_day_slice_to_masks[case_day_slice] = []
|
|
24
|
+
unique_slices.append(case_day_slice)
|
|
25
|
+
case_day_slice_to_masks[case_day_slice].append(mask)
|
|
26
|
+
|
|
27
|
+
case_day_to_masks = {}
|
|
28
|
+
case_days = [] # list to keep ordering consistent
|
|
29
|
+
for case_day_slice in unique_slices:
|
|
30
|
+
# go from e.g. "case123_day2_slice_0001" to "case123_day2"
|
|
31
|
+
case_day = case_day_slice.split("_slice_")[0]
|
|
32
|
+
if case_day not in case_day_to_masks:
|
|
33
|
+
case_day_to_masks[case_day] = []
|
|
34
|
+
case_days.append(case_day)
|
|
35
|
+
# OR on the masks from the same case_day_slice
|
|
36
|
+
joined_mask = np.logical_or.reduce(case_day_slice_to_masks[case_day_slice]).astype(np.uint8)
|
|
37
|
+
# add the joined slice to its relevant case_day
|
|
38
|
+
case_day_to_masks[case_day].append(joined_mask)
|
|
39
|
+
|
|
40
|
+
# now we stack the joined masks for each case_day to make a 3d image
|
|
41
|
+
for case_day in case_day_to_masks:
|
|
42
|
+
# make into 3d image
|
|
43
|
+
case_day_to_masks[case_day] = np.stack(case_day_to_masks[case_day], axis=0)
|
|
44
|
+
|
|
45
|
+
# keep the order of the cases consistent
|
|
46
|
+
case_day_masks = [case_day_to_masks[case_day] for case_day in case_days]
|
|
47
|
+
return case_day_masks
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def prepare_for_metric(submission: DataFrame, answers: DataFrame):
|
|
51
|
+
"""
|
|
52
|
+
Answers dataframe contains image_height and image_width columns necessary for RLE decoding
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
assert "image_height" in answers.columns, "image_height column not found in answers"
|
|
56
|
+
assert "image_width" in answers.columns, "image_width column not found in answers"
|
|
57
|
+
if not {"id", "class", "predicted"}.issubset(set(submission.columns)):
|
|
58
|
+
raise InvalidSubmissionError("Submission must have columns: id, class, predicted")
|
|
59
|
+
|
|
60
|
+
# prepare appropriate types
|
|
61
|
+
submission["predicted"] = submission["predicted"].fillna("")
|
|
62
|
+
answers["predicted"] = answers["predicted"].fillna("")
|
|
63
|
+
answers["image_height"] = answers["image_height"].astype(int)
|
|
64
|
+
answers["image_width"] = answers["image_width"].astype(int)
|
|
65
|
+
|
|
66
|
+
image_heights = answers["image_height"].tolist()
|
|
67
|
+
image_widths = answers["image_width"].tolist()
|
|
68
|
+
|
|
69
|
+
# extract masked images from RLE in the dataframes
|
|
70
|
+
submission_masks = rles_to_masks(submission["predicted"].to_list(), image_heights, image_widths)
|
|
71
|
+
answer_masks = rles_to_masks(answers["predicted"].to_list(), image_heights, image_widths)
|
|
72
|
+
|
|
73
|
+
# group 2d slice masks into 3d case_day masks
|
|
74
|
+
submission_masks_by_day = group_masks_by_day(submission_masks, submission["id"])
|
|
75
|
+
answer_masks_by_day = group_masks_by_day(answer_masks, answers["id"])
|
|
76
|
+
|
|
77
|
+
return submission_masks, answer_masks, submission_masks_by_day, answer_masks_by_day
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def hausdorff_distance(predicted_mask: np.ndarray, true_mask: np.ndarray) -> float:
|
|
81
|
+
"""
|
|
82
|
+
Computes the hausdorff distance between two 3-dimensional binary masks (H, W, C)
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
predicted_mask: A 3D binary numpy array indicating where the segmentation is predicted
|
|
86
|
+
true_mask: A 3D binary numpy array indicating where the segmentation is
|
|
87
|
+
"""
|
|
88
|
+
# if both empty, return nan, if identical return 0, if only one is empty, return 1
|
|
89
|
+
if np.sum(predicted_mask) == 0 and np.sum(true_mask) == 0:
|
|
90
|
+
return np.nan
|
|
91
|
+
elif np.array_equal(predicted_mask, true_mask):
|
|
92
|
+
return 0
|
|
93
|
+
elif (np.sum(predicted_mask) == 0 and np.sum(true_mask) > 0) or (
|
|
94
|
+
np.sum(predicted_mask) > 0 and np.sum(true_mask) == 0
|
|
95
|
+
):
|
|
96
|
+
return 1
|
|
97
|
+
|
|
98
|
+
# if the pred mask is > 1 OOM larger than the true mask, return 1 to avoid unnecess heavy computation
|
|
99
|
+
if predicted_mask.sum() > 10 * true_mask.sum():
|
|
100
|
+
return 1
|
|
101
|
+
|
|
102
|
+
true_coordinates = np.argwhere(true_mask)
|
|
103
|
+
predicted_coordinates = np.argwhere(predicted_mask)
|
|
104
|
+
|
|
105
|
+
# normalize so that the coordinates are in the range [0, 1], to get a unit cube
|
|
106
|
+
normalization_constants = true_mask.shape
|
|
107
|
+
true_coordinates = true_coordinates / normalization_constants
|
|
108
|
+
predicted_coordinates = predicted_coordinates / normalization_constants
|
|
109
|
+
|
|
110
|
+
# Symmetrize Hausdorff distance
|
|
111
|
+
hausdorff_distance1, _, _ = directed_hausdorff(true_coordinates, predicted_coordinates)
|
|
112
|
+
hausdorff_distance2, _, _ = directed_hausdorff(predicted_coordinates, true_coordinates)
|
|
113
|
+
hausdorff_distance = max(hausdorff_distance1, hausdorff_distance2)
|
|
114
|
+
|
|
115
|
+
# normalize by the maximum distance possible in a unit cube, which is the diagonal
|
|
116
|
+
hausdorff_distance = hausdorff_distance / UNIT_CUBE_DIAGONAL
|
|
117
|
+
|
|
118
|
+
return hausdorff_distance
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def grade(submission: DataFrame, answers: DataFrame) -> float:
|
|
122
|
+
"""
|
|
123
|
+
Computes dice_weight * dice + hausdorff_weight * (1 - hausdorff)
|
|
124
|
+
"""
|
|
125
|
+
dice_weight = 0.4
|
|
126
|
+
hausdorff_weight = 0.6
|
|
127
|
+
|
|
128
|
+
(
|
|
129
|
+
submission_masks,
|
|
130
|
+
answer_masks,
|
|
131
|
+
submission_masks_by_day,
|
|
132
|
+
answer_masks_by_day,
|
|
133
|
+
) = prepare_for_metric(submission, answers)
|
|
134
|
+
|
|
135
|
+
# we skip cases where both masks are empty, so we use nanmean
|
|
136
|
+
# https://www.kaggle.com/competitions/uw-madison-gi-tract-image-segmentation/discussion/324934#1795540
|
|
137
|
+
dice_coeff = np.nanmean(
|
|
138
|
+
[
|
|
139
|
+
dice_coefficient(submission_mask, answer_mask, np.nan)
|
|
140
|
+
for submission_mask, answer_mask in tqdm(
|
|
141
|
+
zip(submission_masks, answer_masks),
|
|
142
|
+
total=len(submission_masks),
|
|
143
|
+
)
|
|
144
|
+
]
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
# skip cases where both masks are empty, to match behaviour of dice
|
|
148
|
+
hausdorff_dis = np.nanmean(
|
|
149
|
+
[
|
|
150
|
+
hausdorff_distance(submission_mask_by_day, answer_mask_by_day)
|
|
151
|
+
for submission_mask_by_day, answer_mask_by_day in tqdm(
|
|
152
|
+
zip(submission_masks_by_day, answer_masks_by_day),
|
|
153
|
+
total=len(submission_masks_by_day),
|
|
154
|
+
)
|
|
155
|
+
]
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
return dice_weight * dice_coeff + hausdorff_weight * (1 - hausdorff_dis)
|