views_pipeline_core 0.1.0__tar.gz
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.
- views_pipeline_core-0.1.0/LICENSE.md +21 -0
- views_pipeline_core-0.1.0/PKG-INFO +96 -0
- views_pipeline_core-0.1.0/README.md +76 -0
- views_pipeline_core-0.1.0/pyproject.toml +25 -0
- views_pipeline_core-0.1.0/views_pipeline_core/__init__.py +0 -0
- views_pipeline_core-0.1.0/views_pipeline_core/cache/__init__.py +0 -0
- views_pipeline_core-0.1.0/views_pipeline_core/cache/global_cache.py +237 -0
- views_pipeline_core-0.1.0/views_pipeline_core/cli/__init__.py +0 -0
- views_pipeline_core-0.1.0/views_pipeline_core/cli/utils.py +143 -0
- views_pipeline_core-0.1.0/views_pipeline_core/configs/drift_detection.py +35 -0
- views_pipeline_core-0.1.0/views_pipeline_core/configs/logging.yaml +62 -0
- views_pipeline_core-0.1.0/views_pipeline_core/data/__init__.py +0 -0
- views_pipeline_core-0.1.0/views_pipeline_core/data/dataloaders.py +304 -0
- views_pipeline_core-0.1.0/views_pipeline_core/data/utils.py +19 -0
- views_pipeline_core-0.1.0/views_pipeline_core/evaluation/__init__.py +0 -0
- views_pipeline_core-0.1.0/views_pipeline_core/evaluation/metrics.py +150 -0
- views_pipeline_core-0.1.0/views_pipeline_core/files/__init__.py +0 -0
- views_pipeline_core-0.1.0/views_pipeline_core/files/utils.py +118 -0
- views_pipeline_core-0.1.0/views_pipeline_core/logging/__init__.py +0 -0
- views_pipeline_core-0.1.0/views_pipeline_core/logging/utils.py +80 -0
- views_pipeline_core-0.1.0/views_pipeline_core/managers/__init__.py +0 -0
- views_pipeline_core-0.1.0/views_pipeline_core/managers/model_manager.py +438 -0
- views_pipeline_core-0.1.0/views_pipeline_core/managers/path_manager.py +858 -0
- views_pipeline_core-0.1.0/views_pipeline_core/models/__init__.py +0 -0
- views_pipeline_core-0.1.0/views_pipeline_core/models/outputs.py +141 -0
- views_pipeline_core-0.1.0/views_pipeline_core/wandb/__init__.py +0 -0
- views_pipeline_core-0.1.0/views_pipeline_core/wandb/utils.py +88 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 views platform
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: views_pipeline_core
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary:
|
|
5
|
+
Author: Borbála Farkas
|
|
6
|
+
Author-email: borbala.farkas@pcr.uu.se
|
|
7
|
+
Requires-Python: >=3.11,<3.15
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Requires-Dist: ingester3 (>=2.1.0,<3.0.0)
|
|
13
|
+
Requires-Dist: properscoring (>=0.1,<0.2)
|
|
14
|
+
Requires-Dist: pytest (>=8.3.3,<9.0.0)
|
|
15
|
+
Requires-Dist: views_forecasts (>=0.5.5,<0.6.0)
|
|
16
|
+
Requires-Dist: viewser (>=6.6.4,<7.0.0)
|
|
17
|
+
Requires-Dist: wandb (>=0.18.7,<0.19.0)
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# views_pipeline_core
|
|
21
|
+
|
|
22
|
+
<div style="width: 100%; max-width: 1500px; height: 400px; overflow: hidden; position: relative;">
|
|
23
|
+
<img src="https://pbs.twimg.com/profile_banners/1237000633896652800/1717069203/1500x500" alt="VIEWS Twitter Header" style="position: absolute; top: -50px; width: 100%; height: auto;">
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
The [**Violence & Impacts Early Warning System (VIEWS)**](https://viewsforecasting.org/) produces monthly predictions of future violent conflict at both a country and sub-country level of analysis. This repository contains code, configuration files, and documentation that encapsulates the entire process of developing, experimenting, training, evaluating, and deploying the VIEWS machine learning model pipeline.
|
|
27
|
+
|
|
28
|
+
Use our [interactive data dashboard](https://data.viewsforecasting.org/) to explore our latest predictions of future armed conflict.
|
|
29
|
+
|
|
30
|
+
> [!CAUTION]
|
|
31
|
+
> Please note that this pipeline is **actively under construction**. We're in the **early stages of development**, meaning it's **not yet ready for operational use**. We're working hard to bring you a robust and fully-functional tool, so stay tuned for updates!
|
|
32
|
+
|
|
33
|
+
## Table of contents
|
|
34
|
+
|
|
35
|
+
<!-- toc -->
|
|
36
|
+
- [Repository Contents, Structure, and Explanations](#repository-contents-structure-and-explanations)
|
|
37
|
+
- [Pipeline Execution](#pipeline-execution)
|
|
38
|
+
- [Pipeline Documentation](#pipeline-documentation)
|
|
39
|
+
- [About the VIEWS Project](#about-the-views-project)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
<!-- tocstop -->
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
## Repository Contents, Structure, and Explanations
|
|
46
|
+

|
|
47
|
+
|
|
48
|
+
### Repository Contents
|
|
49
|
+
|
|
50
|
+
This repository includes:
|
|
51
|
+
|
|
52
|
+
- **Code:** Source code for the VIEWS project's machine learning models and the full pipeline.
|
|
53
|
+
- **Configuration Files:** Settings and configurations for running the models, ensembles, and orchestration scripts.
|
|
54
|
+
- **Documentation:** Detailed instructions and information about the project and how to interact with the pipeline and the individual components.
|
|
55
|
+
|
|
56
|
+
### Pipeline Overview
|
|
57
|
+
|
|
58
|
+
The VIEWS machine learning pipeline involves several key processes:
|
|
59
|
+
|
|
60
|
+
- **Developing:** Creating and refining machine learning models.
|
|
61
|
+
- **Experimentation:** Testing and validating various model configurations and approaches.
|
|
62
|
+
- **Training:** Training models with relevant data.
|
|
63
|
+
- **Evaluating:** Assessing model performance and accuracy.
|
|
64
|
+
- **Deploying:** Implementing models in a production environment to generate monthly true-future forecasts
|
|
65
|
+
|
|
66
|
+
## Pipeline Documentation
|
|
67
|
+
High-level documentation on the pipeline and its components can be found in the folder [`documentation`](https://github.com/views-platform/views_pipeline_core/tree/main/documentation). For a comprehensive understanding of the terms and concepts used, please consult the [`Glossary`](https://github.com/views-platform/views_pipeline_core/blob/main/documentation/glossary.md). To explore the rationale behind our architectural choices, visit the [`Architectural Decision Records (ADRs)`](https://github.com/views-platform/views_pipeline_core/blob/main/documentation/ADRs/README.md).
|
|
68
|
+
|
|
69
|
+
Additionally, refer to READMEs and docstrings of various functions and classes in the source code.
|
|
70
|
+
|
|
71
|
+
The operational fatalities model generates forecasts for state-based armed conflict during each month in a rolling 3-year window.
|
|
72
|
+
The latest iteration, currently in production, is called [Fatalities002](https://viewsforecasting.org/early-warning-system/models/fatalities002/).
|
|
73
|
+
|
|
74
|
+
The following links cover **modelling documentation** for Fatalities002:
|
|
75
|
+
- [Prediction models and input variables in main ensemble](https://viewsforecasting.org/views_documentation_models_fatalities002/)
|
|
76
|
+
- [Levels of analysis and dependent variables](https://viewsforecasting.org/wp-content/uploads/VIEWS_documentation_LevelsandOutcomes.pdf)
|
|
77
|
+
- [Partitioning and time shifting data for training, calibration, testing/forecasting, model weighting, and out-of-sample evaluation](https://viewsforecasting.org/wp-content/uploads/VIEWS_Documentation_Partitioningandtimeshifting_Fatalities002.pdf)
|
|
78
|
+
- [Ensembling and calibration](https://viewsforecasting.org/wp-content/uploads/VIEWS_documentation_Ensembling_Fatalities002.pdf)
|
|
79
|
+
|
|
80
|
+
For VIEWS-specific **infrastructure documentation**, please refer to following GitHub repositories:
|
|
81
|
+
- [`ingester3`: Loading input data into the views database](https://github.com/UppsalaConflictDataProgram/ingester3)
|
|
82
|
+
- [`viewser`: Accessing input data from views database](https://github.com/prio-data/viewser)
|
|
83
|
+
- [`views_api`: Our API for accessing predictions](https://github.com/prio-data/views_api)
|
|
84
|
+
|
|
85
|
+
## About the VIEWS Project
|
|
86
|
+
|
|
87
|
+
The VIEWS project is a collaborative effort supported by leading research institutions focused on peace and conflict studies. For more information about the project, visit the [VIEWS Forecasting webpage](https://viewsforecasting.org/).
|
|
88
|
+
|
|
89
|
+
### Affiliations
|
|
90
|
+
|
|
91
|
+
- **Peace Research Institute Oslo (PRIO):**
|
|
92
|
+
The [Peace Research Institute Oslo (PRIO)](https://www.prio.org/) conducts research on the conditions for peaceful relations between states, groups, and people. PRIO is dedicated to understanding the processes that lead to violence and those that create sustainable peace. About half of the VIEWS core team is currently located at PRIO.
|
|
93
|
+
|
|
94
|
+
- **Department of Peace and Conflict Research at the University of Uppsala:**
|
|
95
|
+
The [Department of Peace and Conflict Research at the University of Uppsala](https://www.uu.se/en/department/peace-and-conflict-research) is a leading academic institution in the study of conflict resolution, peacebuilding, and security. The department is renowned for its research and education programs aimed at fostering a deeper understanding of conflict dynamics and peace processes. This department also hosts the [Uppsala Conflict Data Program (UCDP)](https://ucdp.uu.se/), a central data source for the VIEWS project. About half of the VIEWS core team is currently located at the University of Uppsala.
|
|
96
|
+
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# views_pipeline_core
|
|
2
|
+
|
|
3
|
+
<div style="width: 100%; max-width: 1500px; height: 400px; overflow: hidden; position: relative;">
|
|
4
|
+
<img src="https://pbs.twimg.com/profile_banners/1237000633896652800/1717069203/1500x500" alt="VIEWS Twitter Header" style="position: absolute; top: -50px; width: 100%; height: auto;">
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
The [**Violence & Impacts Early Warning System (VIEWS)**](https://viewsforecasting.org/) produces monthly predictions of future violent conflict at both a country and sub-country level of analysis. This repository contains code, configuration files, and documentation that encapsulates the entire process of developing, experimenting, training, evaluating, and deploying the VIEWS machine learning model pipeline.
|
|
8
|
+
|
|
9
|
+
Use our [interactive data dashboard](https://data.viewsforecasting.org/) to explore our latest predictions of future armed conflict.
|
|
10
|
+
|
|
11
|
+
> [!CAUTION]
|
|
12
|
+
> Please note that this pipeline is **actively under construction**. We're in the **early stages of development**, meaning it's **not yet ready for operational use**. We're working hard to bring you a robust and fully-functional tool, so stay tuned for updates!
|
|
13
|
+
|
|
14
|
+
## Table of contents
|
|
15
|
+
|
|
16
|
+
<!-- toc -->
|
|
17
|
+
- [Repository Contents, Structure, and Explanations](#repository-contents-structure-and-explanations)
|
|
18
|
+
- [Pipeline Execution](#pipeline-execution)
|
|
19
|
+
- [Pipeline Documentation](#pipeline-documentation)
|
|
20
|
+
- [About the VIEWS Project](#about-the-views-project)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
<!-- tocstop -->
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
## Repository Contents, Structure, and Explanations
|
|
27
|
+

|
|
28
|
+
|
|
29
|
+
### Repository Contents
|
|
30
|
+
|
|
31
|
+
This repository includes:
|
|
32
|
+
|
|
33
|
+
- **Code:** Source code for the VIEWS project's machine learning models and the full pipeline.
|
|
34
|
+
- **Configuration Files:** Settings and configurations for running the models, ensembles, and orchestration scripts.
|
|
35
|
+
- **Documentation:** Detailed instructions and information about the project and how to interact with the pipeline and the individual components.
|
|
36
|
+
|
|
37
|
+
### Pipeline Overview
|
|
38
|
+
|
|
39
|
+
The VIEWS machine learning pipeline involves several key processes:
|
|
40
|
+
|
|
41
|
+
- **Developing:** Creating and refining machine learning models.
|
|
42
|
+
- **Experimentation:** Testing and validating various model configurations and approaches.
|
|
43
|
+
- **Training:** Training models with relevant data.
|
|
44
|
+
- **Evaluating:** Assessing model performance and accuracy.
|
|
45
|
+
- **Deploying:** Implementing models in a production environment to generate monthly true-future forecasts
|
|
46
|
+
|
|
47
|
+
## Pipeline Documentation
|
|
48
|
+
High-level documentation on the pipeline and its components can be found in the folder [`documentation`](https://github.com/views-platform/views_pipeline_core/tree/main/documentation). For a comprehensive understanding of the terms and concepts used, please consult the [`Glossary`](https://github.com/views-platform/views_pipeline_core/blob/main/documentation/glossary.md). To explore the rationale behind our architectural choices, visit the [`Architectural Decision Records (ADRs)`](https://github.com/views-platform/views_pipeline_core/blob/main/documentation/ADRs/README.md).
|
|
49
|
+
|
|
50
|
+
Additionally, refer to READMEs and docstrings of various functions and classes in the source code.
|
|
51
|
+
|
|
52
|
+
The operational fatalities model generates forecasts for state-based armed conflict during each month in a rolling 3-year window.
|
|
53
|
+
The latest iteration, currently in production, is called [Fatalities002](https://viewsforecasting.org/early-warning-system/models/fatalities002/).
|
|
54
|
+
|
|
55
|
+
The following links cover **modelling documentation** for Fatalities002:
|
|
56
|
+
- [Prediction models and input variables in main ensemble](https://viewsforecasting.org/views_documentation_models_fatalities002/)
|
|
57
|
+
- [Levels of analysis and dependent variables](https://viewsforecasting.org/wp-content/uploads/VIEWS_documentation_LevelsandOutcomes.pdf)
|
|
58
|
+
- [Partitioning and time shifting data for training, calibration, testing/forecasting, model weighting, and out-of-sample evaluation](https://viewsforecasting.org/wp-content/uploads/VIEWS_Documentation_Partitioningandtimeshifting_Fatalities002.pdf)
|
|
59
|
+
- [Ensembling and calibration](https://viewsforecasting.org/wp-content/uploads/VIEWS_documentation_Ensembling_Fatalities002.pdf)
|
|
60
|
+
|
|
61
|
+
For VIEWS-specific **infrastructure documentation**, please refer to following GitHub repositories:
|
|
62
|
+
- [`ingester3`: Loading input data into the views database](https://github.com/UppsalaConflictDataProgram/ingester3)
|
|
63
|
+
- [`viewser`: Accessing input data from views database](https://github.com/prio-data/viewser)
|
|
64
|
+
- [`views_api`: Our API for accessing predictions](https://github.com/prio-data/views_api)
|
|
65
|
+
|
|
66
|
+
## About the VIEWS Project
|
|
67
|
+
|
|
68
|
+
The VIEWS project is a collaborative effort supported by leading research institutions focused on peace and conflict studies. For more information about the project, visit the [VIEWS Forecasting webpage](https://viewsforecasting.org/).
|
|
69
|
+
|
|
70
|
+
### Affiliations
|
|
71
|
+
|
|
72
|
+
- **Peace Research Institute Oslo (PRIO):**
|
|
73
|
+
The [Peace Research Institute Oslo (PRIO)](https://www.prio.org/) conducts research on the conditions for peaceful relations between states, groups, and people. PRIO is dedicated to understanding the processes that lead to violence and those that create sustainable peace. About half of the VIEWS core team is currently located at PRIO.
|
|
74
|
+
|
|
75
|
+
- **Department of Peace and Conflict Research at the University of Uppsala:**
|
|
76
|
+
The [Department of Peace and Conflict Research at the University of Uppsala](https://www.uu.se/en/department/peace-and-conflict-research) is a leading academic institution in the study of conflict resolution, peacebuilding, and security. The department is renowned for its research and education programs aimed at fostering a deeper understanding of conflict dynamics and peace processes. This department also hosts the [Uppsala Conflict Data Program (UCDP)](https://ucdp.uu.se/), a central data source for the VIEWS project. About half of the VIEWS core team is currently located at the University of Uppsala.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "views_pipeline_core"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = ""
|
|
5
|
+
authors = [
|
|
6
|
+
"Borbála Farkas <borbala.farkas@pcr.uu.se>",
|
|
7
|
+
"Dylan Pinheiro <dylpin@prio.org>",
|
|
8
|
+
"Xiaolong Sun <xiaolong.sun@pcr.uu.se>"
|
|
9
|
+
]
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
|
|
12
|
+
[tool.poetry.dependencies]
|
|
13
|
+
python = ">=3.11,<3.15"
|
|
14
|
+
viewser = "^6.6.4"
|
|
15
|
+
pytest = "^8.3.3"
|
|
16
|
+
ingester3 = "^2.1.0"
|
|
17
|
+
views_forecasts = "^0.5.5"
|
|
18
|
+
properscoring = "^0.1"
|
|
19
|
+
wandb = "^0.18.7"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
[build-system]
|
|
24
|
+
requires = ["poetry-core"]
|
|
25
|
+
build-backend = "poetry.core.masonry.api"
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import pickle
|
|
2
|
+
import os
|
|
3
|
+
import logging
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import threading
|
|
7
|
+
import atexit
|
|
8
|
+
import signal
|
|
9
|
+
from views_pipeline_core.managers.path_manager import ModelPath
|
|
10
|
+
# Configure logging
|
|
11
|
+
logging.basicConfig(
|
|
12
|
+
level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s"
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class GlobalCacheMeta(type):
|
|
17
|
+
def __getitem__(cls, key):
|
|
18
|
+
instance = cls()
|
|
19
|
+
return instance[key]
|
|
20
|
+
|
|
21
|
+
def __setitem__(cls, key, value):
|
|
22
|
+
instance = cls()
|
|
23
|
+
instance[key] = value
|
|
24
|
+
|
|
25
|
+
def delete(cls, key):
|
|
26
|
+
instance = cls()
|
|
27
|
+
instance._delete(key)
|
|
28
|
+
|
|
29
|
+
class GlobalCache(metaclass=GlobalCacheMeta):
|
|
30
|
+
"""
|
|
31
|
+
A thread-safe singleton cache class that uses a global cache file to store key-value pairs.
|
|
32
|
+
|
|
33
|
+
Attributes:
|
|
34
|
+
_instance (GlobalCache): The singleton instance of the GlobalCache class.
|
|
35
|
+
_lock (threading.Lock): A lock to ensure thread safety.
|
|
36
|
+
filepath (Path): The path to the cache file.
|
|
37
|
+
cache (dict): The in-memory cache dictionary.
|
|
38
|
+
initialized (bool): A flag to check if the instance is initialized.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
_instance = None
|
|
42
|
+
_lock = threading.Lock()
|
|
43
|
+
|
|
44
|
+
def __new__(cls, *args, **kwargs):
|
|
45
|
+
"""
|
|
46
|
+
Ensures that only one instance of the GlobalCache class is created (singleton pattern).
|
|
47
|
+
|
|
48
|
+
Uses double-checked locking to minimize the overhead of acquiring the lock.
|
|
49
|
+
"""
|
|
50
|
+
if cls._instance is None:
|
|
51
|
+
with cls._lock:
|
|
52
|
+
if cls._instance is None:
|
|
53
|
+
cls._instance = super(GlobalCache, cls).__new__(cls)
|
|
54
|
+
return cls._instance
|
|
55
|
+
|
|
56
|
+
def __init__(self, filepath=None):
|
|
57
|
+
"""
|
|
58
|
+
Initializes the GlobalCache instance.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
filepath (Path, optional): The path to the cache file. Defaults to '.global_cache.pkl' in the project root.
|
|
62
|
+
"""
|
|
63
|
+
if not hasattr(self, "initialized"):
|
|
64
|
+
self.local_imports()
|
|
65
|
+
if filepath is None:
|
|
66
|
+
filepath = (
|
|
67
|
+
ModelPath.find_project_root() / ".global_cache.pkl"
|
|
68
|
+
)
|
|
69
|
+
self.filepath = filepath
|
|
70
|
+
self.cache = {}
|
|
71
|
+
self.ensure_cache_file_exists()
|
|
72
|
+
self.load_cache()
|
|
73
|
+
self.initialized = True
|
|
74
|
+
|
|
75
|
+
def __getitem__(self, key):
|
|
76
|
+
"""
|
|
77
|
+
Retrieves a value from the cache by key.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
key (str): The key to retrieve the value for.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
The value associated with the key, or None if the key is not found.
|
|
84
|
+
"""
|
|
85
|
+
with self._lock:
|
|
86
|
+
return self.get(key)
|
|
87
|
+
|
|
88
|
+
def __setitem__(self, key, value):
|
|
89
|
+
"""
|
|
90
|
+
Sets a value in the cache for a given key.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
key (str): The key to set the value for.
|
|
94
|
+
value: The value to set.
|
|
95
|
+
"""
|
|
96
|
+
with self._lock:
|
|
97
|
+
self.set(key, value)
|
|
98
|
+
|
|
99
|
+
def local_imports(self):
|
|
100
|
+
pass
|
|
101
|
+
|
|
102
|
+
def ensure_cache_file_exists(self):
|
|
103
|
+
"""
|
|
104
|
+
Ensures that the cache file exists. If it does not exist, creates a new cache file.
|
|
105
|
+
"""
|
|
106
|
+
if not self.filepath.exists():
|
|
107
|
+
logging.warning(
|
|
108
|
+
f"Cache file: {self.filepath} does not exist. Creating new cache file..."
|
|
109
|
+
)
|
|
110
|
+
with open(self.filepath, "wb") as f:
|
|
111
|
+
pickle.dump({}, f)
|
|
112
|
+
logging.debug(f"Created new cache file: {self.filepath}")
|
|
113
|
+
|
|
114
|
+
def set(self, key, value):
|
|
115
|
+
"""
|
|
116
|
+
Sets a value in the in-memory cache and saves the cache to the file.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
key (str): The key to set the value for.
|
|
120
|
+
value: The value to set.
|
|
121
|
+
"""
|
|
122
|
+
self.cache[key] = value
|
|
123
|
+
self.save_cache()
|
|
124
|
+
logging.debug(f"Set key: {key} with value: {value}")
|
|
125
|
+
|
|
126
|
+
def get(self, key):
|
|
127
|
+
"""
|
|
128
|
+
Retrieves a value from the in-memory cache by key.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
key (str): The key to retrieve the value for.
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
The value associated with the key, or None if the key is not found.
|
|
135
|
+
"""
|
|
136
|
+
self.load_cache()
|
|
137
|
+
value = self.cache.get(key, None)
|
|
138
|
+
logging.debug(f"Get key: {key} returned value: {value}")
|
|
139
|
+
return value
|
|
140
|
+
|
|
141
|
+
def _delete(self, key):
|
|
142
|
+
"""
|
|
143
|
+
Deletes a key-value pair from the in-memory cache and saves the cache to the file.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
key (str): The key to delete.
|
|
147
|
+
"""
|
|
148
|
+
if key in self.cache:
|
|
149
|
+
del self.cache[key]
|
|
150
|
+
self.save_cache()
|
|
151
|
+
logging.debug(f'Deleted key: {key}')
|
|
152
|
+
else:
|
|
153
|
+
logging.warning(f'Key: {key} not found in cache')
|
|
154
|
+
|
|
155
|
+
def save_cache(self):
|
|
156
|
+
"""
|
|
157
|
+
Saves the in-memory cache to the cache file.
|
|
158
|
+
"""
|
|
159
|
+
with open(self.filepath, "wb") as f:
|
|
160
|
+
pickle.dump(self.cache, f)
|
|
161
|
+
logging.debug(f"Cache saved to file: {self.filepath}")
|
|
162
|
+
|
|
163
|
+
def load_cache(self):
|
|
164
|
+
"""
|
|
165
|
+
Loads the cache from the cache file into the in-memory cache.
|
|
166
|
+
"""
|
|
167
|
+
if self.filepath.exists():
|
|
168
|
+
try:
|
|
169
|
+
with open(str(self.filepath), "rb") as f:
|
|
170
|
+
loaded_cache = pickle.loads(f.read())
|
|
171
|
+
if isinstance(loaded_cache, dict):
|
|
172
|
+
self.cache = loaded_cache
|
|
173
|
+
logging.debug(f"Cache loaded from file: {self.filepath}")
|
|
174
|
+
else:
|
|
175
|
+
logging.error(
|
|
176
|
+
f"Loaded cache is not a dictionary. Initializing empty cache."
|
|
177
|
+
)
|
|
178
|
+
self.cache = {}
|
|
179
|
+
except (EOFError, pickle.UnpicklingError) as e:
|
|
180
|
+
logging.error(
|
|
181
|
+
f"Failed to load cache from file: {self.filepath}. Error: {e}"
|
|
182
|
+
)
|
|
183
|
+
self.cache = {}
|
|
184
|
+
else:
|
|
185
|
+
self.cache = {}
|
|
186
|
+
logging.debug(f"Cache file does not exist. Initialized empty cache.")
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def cleanup_cache_file():
|
|
190
|
+
"""
|
|
191
|
+
Deletes the global cache file upon program termination.
|
|
192
|
+
|
|
193
|
+
This function is registered with the atexit module to ensure that the
|
|
194
|
+
global cache file is deleted when the program exits normally. It acquires
|
|
195
|
+
the lock to ensure thread safety and then checks if the cache file exists.
|
|
196
|
+
If the file exists, it deletes the file and logs the action.
|
|
197
|
+
"""
|
|
198
|
+
try:
|
|
199
|
+
# Acquire the lock to ensure thread safety
|
|
200
|
+
with GlobalCache._lock:
|
|
201
|
+
if os.path.exists(GlobalCache().filepath):
|
|
202
|
+
os.remove(GlobalCache().filepath)
|
|
203
|
+
logging.info(f"Cache file: {GlobalCache().filepath} deleted at exit")
|
|
204
|
+
except Exception as e:
|
|
205
|
+
logging.error(f"Error during cleanup: {e}")
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def signal_handler(sig, frame):
|
|
209
|
+
"""
|
|
210
|
+
Signal handler for SIGINT to delete the cache file upon user interruption.
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
sig (int): The signal number.
|
|
214
|
+
frame (FrameType): The current stack frame.
|
|
215
|
+
"""
|
|
216
|
+
logging.info("SIGINT received. Deleting cache file...")
|
|
217
|
+
cleanup_cache_file()
|
|
218
|
+
os._exit(0) # Use os._exit to ensure the process exits immediately
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
# Register the cleanup_cache_file function to be called upon normal program termination
|
|
222
|
+
atexit.register(cleanup_cache_file)
|
|
223
|
+
|
|
224
|
+
# Register the signal handler for SIGINT
|
|
225
|
+
signal.signal(signal.SIGINT, signal_handler)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
# if __name__ == '__main__':
|
|
229
|
+
# # Example usage of the GlobalCache class
|
|
230
|
+
# print(GlobalCache["test"]) # Returns None
|
|
231
|
+
|
|
232
|
+
# GlobalCache["test"] = "Hello, World!"
|
|
233
|
+
# print(GlobalCache["test"])
|
|
234
|
+
# GlobalCache.delete("test")
|
|
235
|
+
|
|
236
|
+
# print(GlobalCache["test"]) # Returns None
|
|
237
|
+
|
|
File without changes
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import argparse
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def parse_args():
|
|
6
|
+
"""
|
|
7
|
+
CLI parser for model specific main.py scripts.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
parser = argparse.ArgumentParser(
|
|
11
|
+
description="Run model pipeline with specified run type."
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
parser.add_argument(
|
|
15
|
+
"-r",
|
|
16
|
+
"--run_type",
|
|
17
|
+
choices=["calibration", "testing", "forecasting"],
|
|
18
|
+
type=str,
|
|
19
|
+
default="calibration",
|
|
20
|
+
help="Choose the run type for the model: calibration, testing, or forecasting. Default is calibration. "
|
|
21
|
+
"Note: If --sweep is flagged, --run_type must be calibration.",
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
parser.add_argument(
|
|
25
|
+
"-s",
|
|
26
|
+
"--sweep",
|
|
27
|
+
action="store_true",
|
|
28
|
+
help="Set flag to run the model pipeline as part of a sweep. No explicit flag means no sweep."
|
|
29
|
+
"Note: If --sweep is flagged, --run_type must be calibration, and both training and evaluation is automatically implied.",
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
parser.add_argument(
|
|
33
|
+
"-t",
|
|
34
|
+
"--train",
|
|
35
|
+
action="store_true",
|
|
36
|
+
help="Flag to indicate if a new model should be trained. "
|
|
37
|
+
"Note: If --sweep is flagged, --train will also automatically be flagged.",
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
parser.add_argument(
|
|
41
|
+
"-e",
|
|
42
|
+
"--evaluate",
|
|
43
|
+
action="store_true",
|
|
44
|
+
help="Flag to indicate if the model should be evaluated. "
|
|
45
|
+
"Note: If --sweep is specified, --evaluate will also automatically be flagged. "
|
|
46
|
+
"Cannot be used with --run_type forecasting.",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
parser.add_argument(
|
|
50
|
+
"-f",
|
|
51
|
+
"--forecast",
|
|
52
|
+
action="store_true",
|
|
53
|
+
help="Flag to indicate if the model should produce predictions. "
|
|
54
|
+
"Note: If --sweep is specified, --forecast will also automatically be flagged. "
|
|
55
|
+
"Can only be used with --run_type forecasting.",
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
parser.add_argument(
|
|
59
|
+
"-a",
|
|
60
|
+
"--artifact_name",
|
|
61
|
+
type=str,
|
|
62
|
+
help="Specify the name of the model artifact to be used for evaluation. "
|
|
63
|
+
"The file extension will be added in the main and fit with the specific model algorithm."
|
|
64
|
+
"The artifact name should be in the format: <run_type>_model_<timestamp>.pt."
|
|
65
|
+
"where <run_type> is calibration, testing, or forecasting, and <timestamp> is in the format YMD_HMS."
|
|
66
|
+
"If not provided, the latest artifact will be used by default.",
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
parser.add_argument(
|
|
70
|
+
"-en",
|
|
71
|
+
"--ensemble",
|
|
72
|
+
action="store_true",
|
|
73
|
+
help="Flag to indicate if the model is an ensemble.",
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
parser.add_argument(
|
|
77
|
+
"-sa", "--saved", action="store_true", help="Used locally stored data"
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
parser.add_argument(
|
|
81
|
+
"-o", "--override_month", help="Over-ride use of current month", type=int
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
parser.add_argument(
|
|
85
|
+
"-dd", "--drift_self_test", action="store_true", default=False,
|
|
86
|
+
help="Enable drift-detection self_test at data-fetch"
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
return parser.parse_args()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def validate_arguments(args):
|
|
93
|
+
if args.sweep and args.run_type != "calibration":
|
|
94
|
+
print("Error: Sweep runs must have --run_type set to 'calibration'. Exiting.")
|
|
95
|
+
print("To fix: Use --run_type calibration when --sweep is flagged.")
|
|
96
|
+
sys.exit(1)
|
|
97
|
+
|
|
98
|
+
if args.evaluate and args.run_type == "forecasting":
|
|
99
|
+
print("Error: Forecasting runs cannot evaluate. Exiting.")
|
|
100
|
+
print("To fix: Remove --evaluate flag when --run_type is 'forecasting'.")
|
|
101
|
+
sys.exit(1)
|
|
102
|
+
|
|
103
|
+
if (
|
|
104
|
+
args.run_type in ["calibration", "testing", "forecasting"]
|
|
105
|
+
and not args.train
|
|
106
|
+
and not args.evaluate
|
|
107
|
+
and not args.forecast
|
|
108
|
+
and not args.sweep
|
|
109
|
+
):
|
|
110
|
+
print(
|
|
111
|
+
f"Error: Run type is {args.run_type} but neither --train, --evaluate, nor --sweep flag is set. Nothing to do... Exiting."
|
|
112
|
+
)
|
|
113
|
+
print(
|
|
114
|
+
"To fix: Add --train and/or --evaluate flag. Or use --sweep to run both training and evaluation in a WadnB sweep loop."
|
|
115
|
+
)
|
|
116
|
+
sys.exit(1)
|
|
117
|
+
|
|
118
|
+
if args.train and args.artifact_name:
|
|
119
|
+
print("Error: Both --train and --artifact_name flags are set. Exiting.")
|
|
120
|
+
print("To fix: Remove --artifact_name if --train is set, or vice versa.")
|
|
121
|
+
sys.exit(1)
|
|
122
|
+
|
|
123
|
+
if args.forecast and args.run_type != "forecasting":
|
|
124
|
+
print(
|
|
125
|
+
"Error: --forecast flag can only be used with --run_type forecasting. Exiting."
|
|
126
|
+
)
|
|
127
|
+
print("To fix: Set --run_type to forecasting if --forecast is flagged.")
|
|
128
|
+
sys.exit(1)
|
|
129
|
+
|
|
130
|
+
if args.ensemble and args.sweep:
|
|
131
|
+
# This is a temporary solution. In the future we might need to train and sweep the ensemble models.
|
|
132
|
+
print(
|
|
133
|
+
"Error: --aggregation flag cannot be used with --sweep. Exiting."
|
|
134
|
+
)
|
|
135
|
+
sys.exit(1)
|
|
136
|
+
|
|
137
|
+
if not args.train and not args.saved:
|
|
138
|
+
# if not training, then we need to use saved data
|
|
139
|
+
print(
|
|
140
|
+
"Error: if --train is not set, you should only use --saved flag. Exiting."
|
|
141
|
+
)
|
|
142
|
+
print("To fix: Add --train or --saved flag.")
|
|
143
|
+
sys.exit(1)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
drift_detection_partition_dict={
|
|
2
|
+
'calibration':{
|
|
3
|
+
'global_missingness': {'threshold': 0.01},
|
|
4
|
+
'time_missingness': {'threshold': 0.05},
|
|
5
|
+
'space_missingness': {'threshold': 0.05},
|
|
6
|
+
'feature_missingness': {'threshold': 0.05},
|
|
7
|
+
'global_zeros': {'threshold': 0.99},
|
|
8
|
+
'time_zeros': {'threshold': 0.95},
|
|
9
|
+
'feature_zeros': {'threshold': 0.95},
|
|
10
|
+
'standard_partition_length': 30,
|
|
11
|
+
'test_partition_length': 1
|
|
12
|
+
|
|
13
|
+
},
|
|
14
|
+
'testing':{
|
|
15
|
+
'global_missingness': {'threshold': 0.01},
|
|
16
|
+
'time_missingness': {'threshold': 0.05},
|
|
17
|
+
'space_missingness': {'threshold': 0.05},
|
|
18
|
+
'feature_missingness': {'threshold': 0.05},
|
|
19
|
+
'global_zeros': {'threshold': 0.99},
|
|
20
|
+
'time_zeros': {'threshold': 0.95},
|
|
21
|
+
'feature_zeros': {'threshold': 0.95},
|
|
22
|
+
'standard_partition_length': 30,
|
|
23
|
+
'test_partition_length': 1
|
|
24
|
+
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
'forecasting':{
|
|
28
|
+
'delta_completeness': {'threshold': 0.01},
|
|
29
|
+
'delta_zeroes': {'threshold': 0.01},
|
|
30
|
+
# 'ks_drift': {'threshold': 100},
|
|
31
|
+
'extreme_values': {'threshold': 5.0},
|
|
32
|
+
'standard_partition_length': 30,
|
|
33
|
+
'test_partition_length': 1
|
|
34
|
+
}
|
|
35
|
+
}
|