├── .all-contributorsrc ├── .circleci └── config.yml ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.yml │ ├── config.yml │ ├── documentation-issue.yml │ └── feature-request.yml ├── dependabot.yml ├── release-drafter.yml └── workflows │ ├── build-docs.yaml │ ├── ci.yaml │ ├── lint.yaml │ ├── no-response.yaml │ ├── python-publish.yml │ ├── release-drafter.yml │ └── test-python-publish.yml ├── .gitignore ├── .gitmodules ├── .pre-commit-config.yaml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── MANIFEST.in ├── README.md ├── action_files ├── imports_with_code.py └── test_models │ ├── requirements.txt │ └── src │ ├── data.py │ ├── evaluation.py │ ├── evaluation2.py │ ├── models.py │ ├── models2.py │ ├── multivariate_evaluation.py │ └── multivariate_models.py ├── environment-cpu.yml ├── environment-cuda.yml ├── experiments ├── kan_benchmark │ ├── README.md │ ├── environment.yml │ └── run_experiment.py ├── long_horizon │ ├── README.md │ ├── environment.yml │ └── run_nhits.py └── nbeats_basis │ └── nbeats_basis_experiment.ipynb ├── nbs ├── .gitignore ├── _quarto.yml ├── common.base_auto.ipynb ├── common.base_model.ipynb ├── common.model_checks.ipynb ├── common.modules.ipynb ├── common.scalers.ipynb ├── compat.ipynb ├── core.ipynb ├── custom.yml ├── docs │ ├── api-reference │ │ ├── .notest │ │ └── 01_neuralforecast_map.ipynb │ ├── capabilities │ │ ├── .notest │ │ ├── 01_overview.ipynb │ │ ├── 02_objectives.ipynb │ │ ├── 03_exogenous_variables.ipynb │ │ ├── 04_hyperparameter_tuning.ipynb │ │ ├── 05_predictInsample.ipynb │ │ ├── 06_save_load_models.ipynb │ │ ├── 07_time_series_scaling.ipynb │ │ └── 08_cross_validation.ipynb │ ├── getting-started │ │ ├── .notest │ │ ├── 01_introduction.ipynb │ │ ├── 02_quickstart.ipynb │ │ ├── 04_installation.ipynb │ │ └── 05_datarequirements.ipynb │ ├── tutorials │ │ ├── .notest │ │ ├── 01_getting_started_complete.ipynb │ │ ├── 02_cross_validation.ipynb │ │ ├── 03_uncertainty_quantification.ipynb │ │ ├── 04_longhorizon_nhits.ipynb │ │ ├── 05_longhorizon_transformers.ipynb │ │ ├── 06_longhorizon_probabilistic.ipynb │ │ ├── 07_forecasting_tft.ipynb │ │ ├── 08_multivariate_tsmixer.ipynb │ │ ├── 09_hierarchical_forecasting.ipynb │ │ ├── 10_distributed_neuralforecast.ipynb │ │ ├── 11_intermittent_data.ipynb │ │ ├── 12_using_mlflow.ipynb │ │ ├── 13_robust_forecasting.ipynb │ │ ├── 14_interpretable_decompositions.ipynb │ │ ├── 15_comparing_methods.ipynb │ │ ├── 16_temporal_classification.ipynb │ │ ├── 17_transfer_learning.ipynb │ │ ├── 18_adding_models.ipynb │ │ ├── 19_large_datasets.ipynb │ │ ├── 20_conformal_prediction.ipynb │ │ └── 21_configure_optimizers.ipynb │ └── use-cases │ │ ├── .notest │ │ ├── electricity_peak_forecasting.ipynb │ │ └── predictive_maintenance.ipynb ├── favicon.png ├── favicon_png.png ├── imgs_indx │ ├── logo_mid.png │ ├── logo_new.png │ ├── nbeats_example.png │ ├── nf_map.png │ └── predict_insample.png ├── imgs_losses │ ├── gmm.png │ ├── hmq_loss.png │ ├── huber_loss.png │ ├── huber_qloss.png │ ├── mae_loss.png │ ├── mape_loss.png │ ├── mase_loss.png │ ├── mq_loss.png │ ├── mse_loss.png │ ├── pmm.png │ ├── q_loss.png │ ├── rmae_loss.png │ ├── rmse_loss.png │ ├── tukey_loss.png │ └── turbofan_engine.png ├── imgs_models │ ├── StemGNN.png │ ├── autoformer.png │ ├── bitcn.png │ ├── data_splits.png │ ├── deepar.jpeg │ ├── dilated_rnn.png │ ├── dlinear.png │ ├── fedformer.png │ ├── gru.png │ ├── hint.png │ ├── hint_notation.png │ ├── iTransformer.png │ ├── informer_architecture.png │ ├── kan.png │ ├── lstm.png │ ├── mlp.png │ ├── nbeats.png │ ├── nbeatsx.png │ ├── nhits.png │ ├── patchtst.png │ ├── rmok.png │ ├── rnn.png │ ├── softs_architecture.png │ ├── tcn.png │ ├── temporal_norm.png │ ├── tft_architecture.png │ ├── tft_grn.png │ ├── tft_vsn.png │ ├── tide.png │ ├── timellm.png │ ├── timemixer.png │ ├── timesnet.png │ ├── timexer.png │ ├── tsmixer.png │ ├── tsmixerx.png │ └── vanilla_transformer.png ├── losses.numpy.ipynb ├── losses.pytorch.ipynb ├── mint.json ├── models.autoformer.ipynb ├── models.bitcn.ipynb ├── models.deepar.ipynb ├── models.deepnpts.ipynb ├── models.dilated_rnn.ipynb ├── models.dlinear.ipynb ├── models.fedformer.ipynb ├── models.gru.ipynb ├── models.hint.ipynb ├── models.informer.ipynb ├── models.ipynb ├── models.itransformer.ipynb ├── models.kan.ipynb ├── models.lstm.ipynb ├── models.mlp.ipynb ├── models.mlpmultivariate.ipynb ├── models.nbeats.ipynb ├── models.nbeatsx.ipynb ├── models.nhits.ipynb ├── models.nlinear.ipynb ├── models.patchtst.ipynb ├── models.rmok.ipynb ├── models.rnn.ipynb ├── models.softs.ipynb ├── models.stemgnn.ipynb ├── models.tcn.ipynb ├── models.tft.ipynb ├── models.tide.ipynb ├── models.timellm.ipynb ├── models.timemixer.ipynb ├── models.timesnet.ipynb ├── models.timexer.ipynb ├── models.tsmixer.ipynb ├── models.tsmixerx.ipynb ├── models.vanillatransformer.ipynb ├── nbdev.yml ├── sidebar.yml ├── styles.css ├── tsdataset.ipynb └── utils.ipynb ├── neuralforecast ├── __init__.py ├── _modidx.py ├── auto.py ├── common │ ├── __init__.py │ ├── _base_auto.py │ ├── _base_model.py │ ├── _model_checks.py │ ├── _modules.py │ └── _scalers.py ├── compat.py ├── core.py ├── losses │ ├── __init__.py │ ├── numpy.py │ └── pytorch.py ├── models │ ├── __init__.py │ ├── autoformer.py │ ├── bitcn.py │ ├── deepar.py │ ├── deepnpts.py │ ├── dilated_rnn.py │ ├── dlinear.py │ ├── fedformer.py │ ├── gru.py │ ├── hint.py │ ├── informer.py │ ├── itransformer.py │ ├── kan.py │ ├── lstm.py │ ├── mlp.py │ ├── mlpmultivariate.py │ ├── nbeats.py │ ├── nbeatsx.py │ ├── nhits.py │ ├── nlinear.py │ ├── patchtst.py │ ├── rmok.py │ ├── rnn.py │ ├── softs.py │ ├── stemgnn.py │ ├── tcn.py │ ├── tft.py │ ├── tide.py │ ├── timellm.py │ ├── timemixer.py │ ├── timesnet.py │ ├── timexer.py │ ├── tsmixer.py │ ├── tsmixerx.py │ └── vanillatransformer.py ├── tsdataset.py └── utils.py ├── pyproject.toml ├── settings.ini ├── setup.py └── test ├── test_iqloss.py └── test_isqfdistribution.py /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "contributors": [ 8 | { 9 | "login": "AzulGarza", 10 | "name": "azul", 11 | "avatar_url": "https://avatars.githubusercontent.com/u/10517170?v=4", 12 | "profile": "https://github.com/AzulGarza", 13 | "contributions": [ 14 | "code", 15 | "maintenance" 16 | ] 17 | }, 18 | { 19 | "login": "cchallu", 20 | "name": "Cristian Challu", 21 | "avatar_url": "https://avatars.githubusercontent.com/u/31133398?v=4", 22 | "profile": "https://github.com/cchallu", 23 | "contributions": [ 24 | "code", 25 | "maintenance" 26 | ] 27 | }, 28 | { 29 | "login": "jmoralez", 30 | "name": "José Morales", 31 | "avatar_url": "https://avatars.githubusercontent.com/u/8473587?v=4", 32 | "profile": "https://github.com/jmoralez", 33 | "contributions": [ 34 | "code", 35 | "maintenance" 36 | ] 37 | }, 38 | { 39 | "login": "mergenthaler", 40 | "name": "mergenthaler", 41 | "avatar_url": "https://avatars.githubusercontent.com/u/4086186?v=4", 42 | "profile": "https://github.com/mergenthaler", 43 | "contributions": [ 44 | "doc", 45 | "code" 46 | ] 47 | }, 48 | { 49 | "login": "kdgutier", 50 | "name": "Kin", 51 | "avatar_url": "https://avatars.githubusercontent.com/u/19935241?v=4", 52 | "profile": "https://github.com/kdgutier", 53 | "contributions": [ 54 | "code", 55 | "bug", 56 | "data" 57 | ] 58 | }, 59 | { 60 | "login": "gdevos010", 61 | "name": "Greg DeVos", 62 | "avatar_url": "https://avatars.githubusercontent.com/u/15316026?v=4", 63 | "profile": "https://github.com/gdevos010", 64 | "contributions": [ 65 | "ideas" 66 | ] 67 | }, 68 | { 69 | "login": "alejandroxag", 70 | "name": "Alejandro", 71 | "avatar_url": "https://avatars.githubusercontent.com/u/64334543?v=4", 72 | "profile": "https://github.com/alejandroxag", 73 | "contributions": [ 74 | "code" 75 | ] 76 | }, 77 | { 78 | "login": "stefanialvs", 79 | "name": "stefanialvs", 80 | "avatar_url": "https://avatars.githubusercontent.com/u/48966177?v=4", 81 | "profile": "http://lavattiata.com", 82 | "contributions": [ 83 | "design" 84 | ] 85 | }, 86 | { 87 | "login": "eltociear", 88 | "name": "Ikko Ashimine", 89 | "avatar_url": "https://avatars.githubusercontent.com/u/22633385?v=4", 90 | "profile": "https://bandism.net/", 91 | "contributions": [ 92 | "bug" 93 | ] 94 | }, 95 | { 96 | "login": "vglaucus", 97 | "name": "vglaucus", 98 | "avatar_url": "https://avatars.githubusercontent.com/u/75549033?v=4", 99 | "profile": "https://github.com/vglaucus", 100 | "contributions": [ 101 | "bug" 102 | ] 103 | }, 104 | { 105 | "login": "pitmonticone", 106 | "name": "Pietro Monticone", 107 | "avatar_url": "https://avatars.githubusercontent.com/u/38562595?v=4", 108 | "profile": "https://github.com/pitmonticone", 109 | "contributions": [ 110 | "bug" 111 | ] 112 | } 113 | ], 114 | "contributorsPerLine": 7, 115 | "projectName": "neuralforecast", 116 | "projectOwner": "Nixtla", 117 | "repoType": "github", 118 | "repoHost": "https://github.com", 119 | "skipCi": true 120 | } 121 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | jobs: 3 | nbdev-tests: 4 | resource_class: xlarge 5 | docker: 6 | - image: mambaorg/micromamba:1.5-focal 7 | steps: 8 | - checkout 9 | - run: 10 | name: Install dependencies 11 | command: micromamba install -n base -c conda-forge -y python=3.10 git -f environment-cpu.yml 12 | - run: 13 | name: Run nbdev tests 14 | no_output_timeout: 20m 15 | command: | 16 | eval "$(micromamba shell hook --shell bash)" 17 | micromamba activate base 18 | pip install ".[dev]" 19 | nbdev_test --do_print --timing --n_workers 1 20 | test-model-performance: 21 | resource_class: xlarge 22 | docker: 23 | - image: mambaorg/micromamba:1.5-focal 24 | steps: 25 | - checkout 26 | - run: 27 | name: Install dependencies 28 | command: micromamba install -n base -c conda-forge -y python=3.10 -f environment-cpu.yml 29 | - run: 30 | name: Run model performance tests 31 | command: | 32 | eval "$(micromamba shell hook --shell bash)" 33 | micromamba activate base 34 | pip install -e ".[dev]" 35 | export LD_LIBRARY_PATH=/opt/conda/lib:$LD_LIBRARY_PATH 36 | cd ./action_files/test_models/ 37 | pip install -r requirements.txt 38 | python -m src.models 39 | python -m src.evaluation 40 | cd ../../ 41 | - store_artifacts: 42 | path: ./action_files/test_models/data/evaluation.csv 43 | destination: evaluation.csv 44 | test-model-performance2: 45 | resource_class: xlarge 46 | docker: 47 | - image: mambaorg/micromamba:1.5-focal 48 | steps: 49 | - checkout 50 | - run: 51 | name: Install dependencies 52 | command: micromamba install -n base -c conda-forge -y python=3.10 -f environment-cpu.yml 53 | - run: 54 | name: Run model performance tests 55 | command: | 56 | eval "$(micromamba shell hook --shell bash)" 57 | micromamba activate base 58 | pip install -e ".[dev]" 59 | export LD_LIBRARY_PATH=/opt/conda/lib:$LD_LIBRARY_PATH 60 | cd ./action_files/test_models/ 61 | pip install -r requirements.txt 62 | python -m src.models2 63 | python -m src.evaluation2 64 | cd ../../ 65 | - store_artifacts: 66 | path: ./action_files/test_models/data/evaluation.csv 67 | destination: evaluation.csv 68 | test-multivariate-model-performance: 69 | resource_class: xlarge 70 | docker: 71 | - image: mambaorg/micromamba:1.5-focal 72 | steps: 73 | - checkout 74 | - run: 75 | name: Install dependencies 76 | command: micromamba install -n base -c conda-forge -y python=3.10 -f environment-cpu.yml 77 | - run: 78 | name: Run model performance tests 79 | command: | 80 | eval "$(micromamba shell hook --shell bash)" 81 | micromamba activate base 82 | pip install -e ".[dev]" 83 | export LD_LIBRARY_PATH=/opt/conda/lib:$LD_LIBRARY_PATH 84 | cd ./action_files/test_models/ 85 | pip install -r requirements.txt 86 | python -m src.multivariate_models 87 | python -m src.multivariate_evaluation 88 | cd ../../ 89 | - store_artifacts: 90 | path: ./action_files/test_models/data/multi_evaluation.csv 91 | destination: multi_evaluation.csv 92 | 93 | workflows: 94 | sample: 95 | jobs: 96 | - nbdev-tests 97 | - test-model-performance 98 | - test-model-performance2 99 | - test-multivariate-model-performance 100 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.ipynb merge=nbdev-merge 2 | nbs/** linguist-documentation 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | name: Bug report 2 | title: "[] " 3 | description: Problems and issues with code of the library 4 | labels: [bug] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thank you for reporting the problem! 10 | Please make sure what you are reporting is a bug with reproducible steps. To ask questions 11 | or share ideas, please post on our [Slack community](https://join.slack.com/t/nixtlacommunity/shared_invite/zt-1h77esh5y-iL1m8N0F7qV1HmH~0KYeAQ) instead. 12 | 13 | - type: textarea 14 | attributes: 15 | label: What happened + What you expected to happen 16 | description: Describe 1. the bug 2. expected behavior 3. useful information (e.g., logs) 17 | placeholder: > 18 | Please provide the context in which the problem occurred and explain what happened. Further, 19 | please also explain why you think the behaviour is erroneous. It is extremely helpful if you can 20 | copy and paste the fragment of logs showing the exact error messages or wrong behaviour here. 21 | 22 | **NOTE**: please copy and paste texts instead of taking screenshots of them for easy future search. 23 | validations: 24 | required: true 25 | 26 | - type: textarea 27 | attributes: 28 | label: Versions / Dependencies 29 | description: Please specify the versions of the library, Python, OS, and other libraries that are used. 30 | placeholder: > 31 | Please specify the versions of dependencies. 32 | validations: 33 | required: true 34 | 35 | - type: textarea 36 | attributes: 37 | label: Reproduction script 38 | description: > 39 | Please provide a reproducible script. Providing a narrow reproduction (minimal / no external dependencies) will 40 | help us triage and address issues in the timely manner! 41 | placeholder: > 42 | Please provide a short code snippet (less than 50 lines if possible) that can be copy-pasted to 43 | reproduce the issue. The snippet should have **no external library dependencies** 44 | (i.e., use fake or mock data / environments). 45 | 46 | **NOTE**: If the code snippet cannot be run by itself, the issue will be marked as "needs-repro-script" 47 | until the repro instruction is updated. 48 | validations: 49 | required: true 50 | 51 | - type: dropdown 52 | attributes: 53 | label: Issue Severity 54 | description: | 55 | How does this issue affect your experience as user? 56 | multiple: false 57 | options: 58 | - "Low: It annoys or frustrates me." 59 | - "Medium: It is a significant difficulty but I can work around it." 60 | - "High: It blocks me from completing my task." 61 | validations: 62 | required: false 63 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: Ask a question or get support 4 | url: https://join.slack.com/t/nixtlacommunity/shared_invite/zt-1h77esh5y-iL1m8N0F7qV1HmH~0KYeAQ 5 | about: Ask a question or request support for using a library of the nixtlaverse 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation-issue.yml: -------------------------------------------------------------------------------- 1 | name: Documentation 2 | title: "[] " 3 | description: Report an issue with the library documentation 4 | labels: [documentation] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: Thank you for helping us improve the library documentation! 9 | 10 | - type: textarea 11 | attributes: 12 | label: Description 13 | description: | 14 | Tell us about the change you'd like to see. For example, "I'd like to 15 | see more examples of how to use `cross_validation`." 16 | validations: 17 | required: true 18 | 19 | - type: textarea 20 | attributes: 21 | label: Link 22 | description: | 23 | If the problem is related to an existing section, please add a link to 24 | the section. 25 | validations: 26 | required: false 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | name: Library feature request 2 | description: Suggest an idea for a project 3 | title: "[] " 4 | labels: [enhancement, feature] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thank you for finding the time to propose a new feature! 10 | We really appreciate the community efforts to improve the nixtlaverse. 11 | 12 | - type: textarea 13 | attributes: 14 | label: Description 15 | description: A short description of your feature 16 | 17 | - type: textarea 18 | attributes: 19 | label: Use case 20 | description: > 21 | Describe the use case of your feature request. It will help us understand and 22 | prioritize the feature request. 23 | placeholder: > 24 | Rather than telling us how you might implement this feature, try to take a 25 | step back and describe what you are trying to achieve. 26 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: weekly 7 | groups: 8 | ci-dependencies: 9 | patterns: ["*"] 10 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name-template: 'v$NEXT_PATCH_VERSION' 2 | tag-template: 'v$NEXT_PATCH_VERSION' 3 | categories: 4 | - title: 'New Features' 5 | label: 'feature' 6 | - title: 'Breaking Change' 7 | label: 'breaking change' 8 | - title: 'Bug Fixes' 9 | label: 'fix' 10 | - title: 'Documentation' 11 | label: 'documentation' 12 | - title: 'Dependencies' 13 | label: 'dependencies' 14 | - title: 'Enhancement' 15 | label: 'enhancement' 16 | change-template: '- $TITLE @$AUTHOR (#$NUMBER)' 17 | template: | 18 | ## Changes 19 | $CHANGES 20 | -------------------------------------------------------------------------------- /.github/workflows/build-docs.yaml: -------------------------------------------------------------------------------- 1 | name: "build-docs" 2 | on: 3 | release: 4 | types: [released] 5 | pull_request: 6 | branches: ["main"] 7 | workflow_dispatch: 8 | 9 | defaults: 10 | run: 11 | shell: bash 12 | 13 | jobs: 14 | build-docs: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Clone repo 18 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 19 | 20 | - name: Clone docs repo 21 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 22 | with: 23 | repository: Nixtla/docs 24 | ref: scripts 25 | path: docs-scripts 26 | 27 | - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 28 | with: 29 | python-version: '3.10' 30 | cache-dependency-path: settings.ini 31 | 32 | - name: Install dependencies 33 | run: pip install uv && uv pip install ".[dev]" --system 34 | 35 | - name: Build docs 36 | run: | 37 | mkdir nbs/_extensions 38 | cp -r docs-scripts/mintlify/ nbs/_extensions/ 39 | python docs-scripts/update-quarto.py 40 | nbdev_docs 41 | 42 | - name: Apply final formats 43 | run: bash ./docs-scripts/docs-final-formatting.bash 44 | 45 | - name: Copy over necessary assets 46 | run: cp nbs/mint.json _docs/mint.json && cp docs-scripts/imgs/* _docs/ 47 | 48 | - name: Deploy to Mintlify Docs 49 | if: | 50 | github.event_name == 'release' || 51 | github.event_name == 'workflow_dispatch' 52 | uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0 53 | with: 54 | github_token: ${{ secrets.GITHUB_TOKEN }} 55 | publish_branch: docs 56 | publish_dir: ./_docs 57 | user_name: github-actions[bot] 58 | user_email: 41898282+github-actions[bot]@users.noreply.github.com 59 | 60 | - name: Trigger mintlify workflow 61 | if: | 62 | github.event_name == 'release' || 63 | github.event_name == 'workflow_dispatch' 64 | uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 65 | with: 66 | github-token: ${{ secrets.DOCS_WORKFLOW_TOKEN }} 67 | script: | 68 | await github.rest.actions.createWorkflowDispatch({ 69 | owner: 'nixtla', 70 | repo: 'docs', 71 | workflow_id: 'mintlify-action.yml', 72 | ref: 'main', 73 | }); 74 | 75 | - name: Configure redirects for gh-pages 76 | run: python docs-scripts/configure-redirects.py neuralforecast 77 | 78 | - name: Deploy to Github Pages 79 | if: | 80 | github.event_name == 'release' || 81 | github.event_name == 'workflow_dispatch' 82 | uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0 83 | with: 84 | github_token: ${{ secrets.GITHUB_TOKEN }} 85 | publish_branch: gh-pages 86 | publish_dir: ./gh-pages 87 | user_name: github-actions[bot] 88 | user_email: 41898282+github-actions[bot]@users.noreply.github.com 89 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | run-tests: 15 | runs-on: ${{ matrix.os }} 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | os: [ubuntu-latest, macos-13, windows-latest] 20 | python-version: ["3.9", "3.10", "3.11", "3.12"] 21 | exclude: 22 | - os: windows-latest 23 | python-version: "3.11" 24 | env: 25 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID_NIXTLA_TMP }} 26 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY_NIXTLA_TMP }} 27 | steps: 28 | - name: Clone repo 29 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 30 | 31 | - name: Set up environment 32 | uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # 5.3.0 33 | with: 34 | python-version: ${{ matrix.python-version }} 35 | 36 | - name: Install pip requirements 37 | run: > 38 | pip install uv && 39 | uv pip install --system -i https://download.pytorch.org/whl/cpu "torch>=2.0.0,<=2.6.0" && 40 | uv pip install --system "numpy<2" ".[dev]" 41 | 42 | - name: Tests 43 | run: nbdev_test --do_print --timing --n_workers 0 --flags polars -------------------------------------------------------------------------------- /.github/workflows/lint.yaml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | lint: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Clone repo 14 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 15 | 16 | - name: Set up python 17 | uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # 5.3.0 18 | with: 19 | python-version: "3.10" 20 | 21 | - name: Install dependencies 22 | run: pip install black "fastcore<1.8.0" nbdev==2.3.25 pre-commit 23 | 24 | - name: Run pre-commit 25 | run: pre-commit run --files neuralforecast/* 26 | -------------------------------------------------------------------------------- /.github/workflows/no-response.yaml: -------------------------------------------------------------------------------- 1 | name: No Response Bot 2 | 3 | on: 4 | issue_comment: 5 | types: [created] 6 | schedule: 7 | - cron: '0 4 * * *' 8 | 9 | jobs: 10 | noResponse: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: lee-dohm/no-response@9bb0a4b5e6a45046f00353d5de7d90fb8bd773bb # v0.5.0 14 | with: 15 | closeComment: > 16 | This issue has been automatically closed because it has been awaiting a response for too long. 17 | When you have time to to work with the maintainers to resolve this issue, please post a new comment and it will be re-opened. 18 | If the issue has been locked for editing by the time you return to it, please open a new issue and reference this one. 19 | daysUntilClose: 30 20 | responseRequiredLabel: awaiting response 21 | token: ${{ github.token }} 22 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | name: Upload Python Package 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | id-token: write 13 | steps: 14 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 15 | 16 | - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # 5.3.0 17 | with: 18 | python-version: '3.x' 19 | 20 | - name: Install dependencies 21 | run: python -m pip install --upgrade pip && pip install build 22 | 23 | - name: Build package 24 | run: python -m build 25 | 26 | - name: Publish package 27 | uses: pypa/gh-action-pypi-publish@67339c736fd9354cd4f8cb0b744f2b82a74b5c70 # v1.12.3 28 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | permissions: 9 | contents: read 10 | 11 | jobs: 12 | update_release_draft: 13 | permissions: 14 | contents: write 15 | pull-requests: read 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: release-drafter/release-drafter@b1476f6e6eb133afa41ed8589daba6dc69b4d3f5 # v6.1.0 19 | env: 20 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 21 | -------------------------------------------------------------------------------- /.github/workflows/test-python-publish.yml: -------------------------------------------------------------------------------- 1 | name: Upload Python Package to TestPyPI 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | deploy: 8 | runs-on: ubuntu-latest 9 | permissions: 10 | id-token: write 11 | steps: 12 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 13 | 14 | - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # 5.3.0 15 | with: 16 | python-version: '3.x' 17 | 18 | - name: Install dependencies 19 | run: python -m pip install --upgrade pip && pip install build 20 | 21 | - name: Build package 22 | run: python -m build 23 | 24 | - name: Publish package 25 | uses: pypa/gh-action-pypi-publish@67339c736fd9354cd4f8cb0b744f2b82a74b5c70 # v1.12.3 26 | with: 27 | repository-url: https://test.pypi.org/legacy/ 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .ipynb_checkpoints 2 | __pycache__ 3 | *.egg-info 4 | Gemfile* 5 | Gemfile.lock 6 | docs/_site 7 | build 8 | dist 9 | .vscode 10 | *.gif 11 | *.csv 12 | */data/* 13 | *.parquet 14 | tmp 15 | _docs/ 16 | sidebar.yml 17 | .DS_Store 18 | .gitattributes 19 | .gitconfig 20 | nbs/.last_checked 21 | _proc 22 | lightning_logs/ 23 | /**/data/ 24 | debug_run 25 | 26 | longhorizon/ 27 | _old/ 28 | data 29 | mlruns 30 | checkpoints 31 | nbs/_extensions/mintlify 32 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/.gitmodules -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | fail_fast: true 2 | 3 | repos: 4 | - repo: local 5 | hooks: 6 | - id: imports_with_code 7 | name: Cells with imports and code 8 | entry: python action_files/imports_with_code.py 9 | language: system 10 | - repo: https://github.com/fastai/nbdev 11 | rev: 2.2.10 12 | hooks: 13 | - id: nbdev_clean 14 | - id: nbdev_export 15 | - repo: https://github.com/astral-sh/ruff-pre-commit 16 | rev: v0.2.1 17 | hooks: 18 | - id: ruff 19 | - repo: https://github.com/pre-commit/mirrors-mypy 20 | rev: v1.8.0 21 | hooks: 22 | - id: mypy 23 | args: [--ignore-missing-imports] 24 | exclude: 'setup.py' 25 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | ops@nixtla.io. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | ## Did you find a bug? 4 | 5 | * Ensure the bug was not already reported by searching on GitHub under Issues. 6 | * If you're unable to find an open issue addressing the problem, open a new one. Be sure to include a title and clear description, as much relevant information as possible, and a code sample or an executable test case demonstrating the expected behavior that is not occurring. 7 | * Be sure to add the complete error messages. 8 | 9 | ## Do you have a feature request? 10 | 11 | * Ensure that it hasn't been yet implemented in the `main` branch of the repository and that there's not an Issue requesting it yet. 12 | * Open a new issue and make sure to describe it clearly, mention how it improves the project and why its useful. 13 | 14 | ## Do you want to fix a bug or implement a feature? 15 | 16 | Bug fixes and features are added through pull requests (PRs). 17 | 18 | ## PR submission guidelines 19 | 20 | * Keep each PR focused. While it's more convenient, do not combine several unrelated fixes together. Create as many branches as needing to keep each PR focused. 21 | * Ensure that your PR includes a test that fails without your patch, and passes with it. 22 | * Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable. 23 | * Do not mix style changes/fixes with "functional" changes. It's very difficult to review such PRs and it most likely get rejected. 24 | * Do not add/remove vertical whitespace. Preserve the original style of the file you edit as much as you can. 25 | * Do not turn an already submitted PR into your development playground. If after you submitted PR, you discovered that more work is needed - close the PR, do the required work and then submit a new PR. Otherwise each of your commits requires attention from maintainers of the project. 26 | * If, however, you submitted a PR and received a request for changes, you should proceed with commits inside that PR, so that the maintainer can see the incremental fixes and won't need to review the whole PR again. In the exception case where you realize it'll take many many commits to complete the requests, then it's probably best to close the PR, do the work and then submit it again. Use common sense where you'd choose one way over another. 27 | 28 | ### Local setup for working on a PR 29 | 30 | #### Clone the repository 31 | * HTTPS: `git clone https://github.com/Nixtla/neuralforecast.git` 32 | * SSH: `git clone git@github.com:Nixtla/neuralforecast.git` 33 | * GitHub CLI: `gh repo clone Nixtla/neuralforecast` 34 | 35 | #### Set up a conda environment 36 | The repo comes with an `environment.yml` file which contains the libraries needed to run all the tests. In order to set up the environment you must have `conda` installed, we recommend [miniconda](https://docs.conda.io/en/latest/miniconda.html). 37 | 38 | Once you have `conda` go to the top level directory of the repository and run the following lines: 39 | ``` 40 | conda create -n neuralforecast python=3.10 41 | conda activate neuralforecast 42 | ``` 43 | Then, run one of the following commands: 44 | ``` 45 | conda env update -f environment-cpu.yml # choose this if you want to install the CPU-only version of neuralforecast 46 | conda env update -f environment-cuda.yml # choose this if you want to install the CUDA-enabled version of neuralforecast 47 | ``` 48 | 49 | #### Install the library 50 | Once you have your environment setup, activate it using `conda activate neuralforecast` and then install the library in editable mode using `pip install -e ".[dev]"` 51 | 52 | #### Install git hooks 53 | Before doing any changes to the code, please install the git hooks that run automatic scripts during each commit and merge to strip the notebooks of superfluous metadata (and avoid merge conflicts). 54 | ``` 55 | nbdev_install_hooks 56 | pre-commit install 57 | ``` 58 | 59 | ### Preview Changes 60 | You can preview changes in your local browser before pushing by using the `nbdev_preview`. 61 | 62 | ### Building the library 63 | The library is built using the notebooks contained in the `nbs` folder. If you want to make any changes to the library you have to find the relevant notebook, make your changes and then call: 64 | ``` 65 | nbdev_export 66 | ``` 67 | 68 | ### Running tests 69 | If you're working on the local interface you can just use `nbdev_test --n_workers 1 --do_print --timing`. 70 | 71 | ### Cleaning notebooks 72 | Since the notebooks output cells can vary from run to run (even if they produce the same outputs) the notebooks are cleaned before committing them. Please make sure to run `nbdev_clean --clear_all` before committing your changes. If you clean the library's notebooks with this command please backtrack the changes you make to the example notebooks `git checkout nbs/docs`, unless you intend to change the examples. 73 | 74 | ## Do you want to contribute to the documentation? 75 | 76 | * Docs are automatically created from the notebooks in the `nbs` folder. 77 | * In order to modify the documentation: 78 | 1. Find the relevant notebook. 79 | 2. Make your changes. 80 | 3. Run all cells. 81 | 4. If you are modifying library notebooks (not in `nbs/docs`), clear all outputs using `Edit > Clear All Outputs`. 82 | 5. Run `nbdev_preview`. 83 | 6. Clean the notebook metadata using `nbdev_clean`. 84 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | ## Include List 2 | 3 | include README.md 4 | include LICENSE 5 | include settings.ini 6 | 7 | recursive-include neuralforecast * 8 | 9 | 10 | ## Exclude List 11 | 12 | exclude CONTRIBUTING.md 13 | exclude Makefile 14 | exclude environment.yml 15 | 16 | exclude .gitconfig 17 | exclude .gitignore 18 | exclude .gitmodules 19 | 20 | recursive-exclude .github * 21 | recursive-exclude docs * 22 | recursive-exclude examples * 23 | recursive-exclude experiments * 24 | recursive-exclude nbs * 25 | -------------------------------------------------------------------------------- /action_files/imports_with_code.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import warnings 3 | from pathlib import Path 4 | 5 | from nbdev.processors import NBProcessor, _do_eval 6 | 7 | 8 | def check_nb(nb_path: str) -> None: 9 | with warnings.catch_warnings(record=True) as issued_warnings: 10 | NBProcessor(nb_path, _do_eval, process=True) 11 | if any( 12 | "Found cells containing imports and other code" in str(w) 13 | for w in issued_warnings 14 | ): 15 | print(f"{nb_path} has cells containing imports and code.") 16 | sys.exit(1) 17 | 18 | 19 | if __name__ == "__main__": 20 | repo_root = Path(__file__).parents[1] 21 | for nb_path in (repo_root / "nbs").glob("*.ipynb"): 22 | check_nb(str(nb_path)) 23 | -------------------------------------------------------------------------------- /action_files/test_models/requirements.txt: -------------------------------------------------------------------------------- 1 | fire 2 | datasetsforecast -------------------------------------------------------------------------------- /action_files/test_models/src/data.py: -------------------------------------------------------------------------------- 1 | import fire 2 | from datasetsforecast.m3 import M3, M3Info 3 | from datasetsforecast.long_horizon import LongHorizon, LongHorizonInfo 4 | 5 | dict_datasets = { 6 | 'M3': (M3, M3Info), 7 | 'multivariate': (LongHorizon, LongHorizonInfo) 8 | } 9 | 10 | def get_data(directory: str, dataset: str, group: str, train: bool = True): 11 | if dataset not in dict_datasets.keys(): 12 | raise Exception(f'dataset {dataset} not found') 13 | 14 | dataclass, datainfo = dict_datasets[dataset] 15 | if group not in datainfo.groups: 16 | raise Exception(f'group {group} not found for {dataset}') 17 | 18 | Y_df, *_ = dataclass.load(directory, group) 19 | 20 | if dataset == 'multivariate': 21 | horizon = datainfo[group].horizons[0] 22 | else: 23 | horizon = datainfo[group].horizon 24 | seasonality = datainfo[group].seasonality 25 | freq = datainfo[group].freq 26 | Y_df_test = Y_df.groupby('unique_id').tail(horizon) 27 | Y_df = Y_df.drop(Y_df_test.index) 28 | 29 | if train: 30 | if dataset == 'multivariate': 31 | return Y_df, horizon, freq 32 | else: 33 | return Y_df, horizon, freq, seasonality 34 | if dataset == 'multivariate': 35 | return Y_df_test, horizon, freq 36 | else: 37 | return Y_df_test, horizon, freq, seasonality 38 | 39 | def save_data(dataset: str, group: str, train: bool = True): 40 | df, *_ = get_data('data', dataset, group, train) 41 | if train: 42 | df.to_csv(f'data/{dataset}-{group}.csv', index=False) 43 | else: 44 | df.to_csv(f'data/{dataset}-{group}-test.csv', index=False) 45 | 46 | 47 | if __name__=="__main__": 48 | fire.Fire(save_data) -------------------------------------------------------------------------------- /action_files/test_models/src/evaluation.py: -------------------------------------------------------------------------------- 1 | from itertools import product 2 | 3 | import numpy as np 4 | import pandas as pd 5 | 6 | from src.data import get_data 7 | 8 | def mae(y, y_hat, axis): 9 | delta_y = np.abs(y - y_hat) 10 | mae = np.average(delta_y, axis=axis) 11 | return mae 12 | 13 | def smape(y, y_hat, axis): 14 | delta_y = np.abs(y - y_hat) 15 | scale = np.abs(y) + np.abs(y_hat) 16 | smape = delta_y / scale 17 | smape = 200 * np.average(smape, axis=axis) 18 | return smape 19 | 20 | def evaluate(model: str, dataset: str, group: str): 21 | try: 22 | forecast = pd.read_csv(f'data/{model}-forecasts-{dataset}-{group}.csv') 23 | except: 24 | return None 25 | y_test, horizon, freq, seasonality = get_data('data/', dataset, group, False) 26 | y_hat = forecast[model].values.reshape(-1, horizon) 27 | y_test = y_test['y'].values.reshape(-1, horizon) 28 | 29 | evals = {} 30 | for metric in (mae, smape): 31 | metric_name = metric.__name__ 32 | loss = metric(y_test, y_hat, axis=1).mean() 33 | evals[metric_name] = loss 34 | 35 | evals = pd.DataFrame(evals, index=[f'{dataset}_{group}']).rename_axis('dataset').reset_index() 36 | times = pd.read_csv(f'data/{model}-time-{dataset}-{group}.csv') 37 | evals = pd.concat([evals, times], axis=1) 38 | 39 | return evals 40 | 41 | 42 | if __name__ == '__main__': 43 | groups = ['Monthly'] 44 | models = ['AutoDilatedRNN', 'RNN', 45 | 'TCN', 46 | 'DeepAR', 47 | 'NHITS', 'TFT', 'AutoMLP', 'DLinear', 'VanillaTransformer', 48 | 'BiTCN', 'TiDE', 'DeepNPTS', 'NBEATS', 'KAN' 49 | ] 50 | datasets = ['M3'] 51 | evaluation = [evaluate(model, dataset, group) for model, group in product(models, groups) for dataset in datasets] 52 | evaluation = [eval_ for eval_ in evaluation if eval_ is not None] 53 | df_evaluation = pd.concat(evaluation) 54 | df_evaluation = df_evaluation.loc[:, ['dataset', 'model', 'time', 'mae', 'smape']] 55 | df_evaluation['time'] /= 60 #minutes 56 | df_evaluation = df_evaluation.set_index(['dataset', 'model']).stack().reset_index() 57 | df_evaluation.columns = ['dataset', 'model', 'metric', 'val'] 58 | df_evaluation = df_evaluation.set_index(['dataset', 'metric', 'model']).unstack().round(3) 59 | df_evaluation = df_evaluation.droplevel(0, 1).reset_index() 60 | df_evaluation['AutoARIMA'] = [666.82, 15.35, 3.000] 61 | df_evaluation.to_csv('data/evaluation.csv') 62 | print(df_evaluation.T) 63 | -------------------------------------------------------------------------------- /action_files/test_models/src/evaluation2.py: -------------------------------------------------------------------------------- 1 | from itertools import product 2 | 3 | import numpy as np 4 | import pandas as pd 5 | 6 | from src.data import get_data 7 | 8 | def mae(y, y_hat, axis): 9 | delta_y = np.abs(y - y_hat) 10 | mae = np.average(delta_y, axis=axis) 11 | return mae 12 | 13 | def smape(y, y_hat, axis): 14 | delta_y = np.abs(y - y_hat) 15 | scale = np.abs(y) + np.abs(y_hat) 16 | smape = delta_y / scale 17 | smape = 200 * np.average(smape, axis=axis) 18 | return smape 19 | 20 | def evaluate(model: str, dataset: str, group: str): 21 | try: 22 | forecast = pd.read_csv(f'data/{model}-forecasts-{dataset}-{group}.csv') 23 | except: 24 | return None 25 | y_test, horizon, freq, seasonality = get_data('data/', dataset, group, False) 26 | y_hat = forecast[model].values.reshape(-1, horizon) 27 | y_test = y_test['y'].values.reshape(-1, horizon) 28 | 29 | evals = {} 30 | for metric in (mae, smape): 31 | metric_name = metric.__name__ 32 | loss = metric(y_test, y_hat, axis=1).mean() 33 | evals[metric_name] = loss 34 | 35 | evals = pd.DataFrame(evals, index=[f'{dataset}_{group}']).rename_axis('dataset').reset_index() 36 | times = pd.read_csv(f'data/{model}-time-{dataset}-{group}.csv') 37 | evals = pd.concat([evals, times], axis=1) 38 | 39 | return evals 40 | 41 | 42 | if __name__ == '__main__': 43 | groups = ['Monthly'] 44 | models = ['LSTM', 'DilatedRNN', 'GRU', 'NBEATSx', 45 | 'PatchTST', 'AutoNHITS', 'AutoNBEATS'] 46 | datasets = ['M3'] 47 | evaluation = [evaluate(model, dataset, group) for model, group in product(models, groups) for dataset in datasets] 48 | evaluation = [eval_ for eval_ in evaluation if eval_ is not None] 49 | evaluation = pd.concat(evaluation) 50 | evaluation = evaluation[['dataset', 'model', 'time', 'mae', 'smape']] 51 | evaluation['time'] /= 60 #minutes 52 | evaluation = evaluation.set_index(['dataset', 'model']).stack().reset_index() 53 | evaluation.columns = ['dataset', 'model', 'metric', 'val'] 54 | evaluation = evaluation.set_index(['dataset', 'metric', 'model']).unstack().round(3) 55 | evaluation = evaluation.droplevel(0, 1).reset_index() 56 | evaluation['AutoARIMA'] = [666.82, 15.35, 3.000] 57 | evaluation.to_csv('data/evaluation.csv') 58 | print(evaluation.T) 59 | -------------------------------------------------------------------------------- /action_files/test_models/src/models.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | import fire 4 | import pandas as pd 5 | 6 | from neuralforecast.core import NeuralForecast 7 | 8 | from neuralforecast.models.rnn import RNN 9 | from neuralforecast.models.tcn import TCN 10 | from neuralforecast.models.deepar import DeepAR 11 | from neuralforecast.models.nhits import NHITS 12 | from neuralforecast.models.nbeats import NBEATS 13 | from neuralforecast.models.tft import TFT 14 | from neuralforecast.models.vanillatransformer import VanillaTransformer 15 | from neuralforecast.models.dlinear import DLinear 16 | from neuralforecast.models.bitcn import BiTCN 17 | from neuralforecast.models.tide import TiDE 18 | from neuralforecast.models.deepnpts import DeepNPTS 19 | from neuralforecast.models.kan import KAN 20 | 21 | from neuralforecast.auto import ( 22 | AutoMLP, 23 | AutoDilatedRNN, 24 | ) 25 | 26 | from neuralforecast.losses.pytorch import SMAPE, MAE, IQLoss 27 | from ray import tune 28 | 29 | from src.data import get_data 30 | 31 | def main(dataset: str = 'M3', group: str = 'Monthly') -> None: 32 | train, horizon, freq, seasonality = get_data('data/', dataset, group) 33 | train['ds'] = pd.to_datetime(train['ds']) 34 | 35 | config = { 36 | "hidden_size": tune.choice([256, 512]), 37 | "num_layers": tune.choice([2, 4]), 38 | "input_size": tune.choice([2 * horizon]), 39 | "max_steps": 1000, 40 | "val_check_steps": 300, 41 | "scaler_type": "minmax1", 42 | "random_seed": tune.choice([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), 43 | } 44 | config_drnn = {'input_size': tune.choice([2 * horizon]), 45 | 'encoder_hidden_size': tune.choice([16]), 46 | "max_steps": 300, 47 | "val_check_steps": 100, 48 | "random_seed": tune.choice([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), 49 | "scaler_type": "minmax1"} 50 | 51 | models = [ 52 | AutoDilatedRNN(h=horizon, loss=MAE(), config=config_drnn, num_samples=2, cpus=1), 53 | RNN(h=horizon, input_size=2 * horizon, encoder_hidden_size=64, max_steps=300), 54 | TCN(h=horizon, input_size=2 * horizon, encoder_hidden_size=64, max_steps=300), 55 | NHITS(h=horizon, input_size=2 * horizon, dropout_prob_theta=0.5, loss=MAE(), max_steps=1000, val_check_steps=500), 56 | AutoMLP(h=horizon, loss=MAE(), config=config, num_samples=2, cpus=1), 57 | DLinear(h=horizon, input_size=2 * horizon, loss=MAE(), max_steps=2000, val_check_steps=500), 58 | TFT(h=horizon, input_size=2 * horizon, loss=SMAPE(), hidden_size=64, scaler_type='robust', windows_batch_size=512, max_steps=1500, val_check_steps=500), 59 | VanillaTransformer(h=horizon, input_size=2 * horizon, loss=MAE(), hidden_size=64, scaler_type='minmax1', windows_batch_size=512, max_steps=1500, val_check_steps=500), 60 | DeepAR(h=horizon, input_size=2 * horizon, scaler_type='minmax1', max_steps=500), 61 | BiTCN(h=horizon, input_size=2 * horizon, loss=MAE(), dropout=0.0, max_steps=1000, val_check_steps=500), 62 | TiDE(h=horizon, input_size=2 * horizon, loss=MAE(), max_steps=1000, val_check_steps=500), 63 | DeepNPTS(h=horizon, input_size=2 * horizon, loss=MAE(), max_steps=1000, val_check_steps=500), 64 | NBEATS(h=horizon, input_size=2 * horizon, loss=IQLoss(), max_steps=2000, val_check_steps=500), 65 | KAN(h=horizon, input_size= 2 * horizon, loss=MAE(), max_steps=1000, val_check_steps=500) 66 | ] 67 | 68 | # Models 69 | for model in models: 70 | model_name = type(model).__name__ 71 | print(50*'-', model_name, 50*'-') 72 | start = time.time() 73 | fcst = NeuralForecast(models=[model], freq=freq) 74 | fcst.fit(train) 75 | forecasts = fcst.predict() 76 | end = time.time() 77 | print(end - start) 78 | 79 | if model_name == 'DeepAR': 80 | forecasts = forecasts[['unique_id', 'ds', 'DeepAR-median']] 81 | 82 | forecasts.columns = ['unique_id', 'ds', model_name] 83 | forecasts.to_csv(f'data/{model_name}-forecasts-{dataset}-{group}.csv', index=False) 84 | time_df = pd.DataFrame({'time': [end - start], 'model': [model_name]}) 85 | time_df.to_csv(f'data/{model_name}-time-{dataset}-{group}.csv', index=False) 86 | 87 | if __name__ == '__main__': 88 | fire.Fire(main) 89 | -------------------------------------------------------------------------------- /action_files/test_models/src/models2.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | import fire 4 | import pandas as pd 5 | 6 | from neuralforecast.core import NeuralForecast 7 | 8 | from neuralforecast.models.gru import GRU 9 | from neuralforecast.models.lstm import LSTM 10 | from neuralforecast.models.dilated_rnn import DilatedRNN 11 | from neuralforecast.models.nbeatsx import NBEATSx 12 | 13 | from neuralforecast.auto import ( 14 | AutoNHITS, 15 | AutoNBEATS, 16 | ) 17 | 18 | from neuralforecast.losses.pytorch import MAE 19 | from ray import tune 20 | 21 | from src.data import get_data 22 | 23 | def main(dataset: str = 'M3', group: str = 'Monthly') -> None: 24 | train, horizon, freq, seasonality = get_data('data/', dataset, group) 25 | train['ds'] = pd.to_datetime(train['ds']) 26 | 27 | config_nbeats = { 28 | "input_size": tune.choice([2 * horizon]), 29 | "max_steps": 1000, 30 | "val_check_steps": 300, 31 | "scaler_type": "minmax1", 32 | "random_seed": tune.choice([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), 33 | } 34 | models = [ 35 | LSTM(h=horizon, input_size=2 * horizon, encoder_hidden_size=64, max_steps=300), 36 | DilatedRNN(h=horizon, input_size=2 * horizon, encoder_hidden_size=64, max_steps=300), 37 | GRU(h=horizon, input_size=2 * horizon, encoder_hidden_size=64, max_steps=300), 38 | AutoNBEATS(h=horizon, loss=MAE(), config=config_nbeats, num_samples=2, cpus=1), 39 | AutoNHITS(h=horizon, loss=MAE(), config=config_nbeats, num_samples=2, cpus=1), 40 | NBEATSx(h=horizon, input_size=2 * horizon, loss=MAE(), max_steps=1000), 41 | ] 42 | 43 | # Models 44 | for model in models: 45 | model_name = type(model).__name__ 46 | print(50*'-', model_name, 50*'-') 47 | start = time.time() 48 | fcst = NeuralForecast(models=[model], freq=freq) 49 | fcst.fit(train) 50 | forecasts = fcst.predict() 51 | end = time.time() 52 | print(end - start) 53 | 54 | forecasts.columns = ['unique_id', 'ds', model_name] 55 | forecasts.to_csv(f'data/{model_name}-forecasts-{dataset}-{group}.csv', index=False) 56 | time_df = pd.DataFrame({'time': [end - start], 'model': [model_name]}) 57 | time_df.to_csv(f'data/{model_name}-time-{dataset}-{group}.csv', index=False) 58 | 59 | 60 | if __name__ == '__main__': 61 | fire.Fire(main) 62 | -------------------------------------------------------------------------------- /action_files/test_models/src/multivariate_evaluation.py: -------------------------------------------------------------------------------- 1 | from itertools import product 2 | 3 | import numpy as np 4 | import pandas as pd 5 | 6 | from src.data import get_data 7 | 8 | def mae(y, y_hat, axis): 9 | delta_y = np.abs(y - y_hat) 10 | mae = np.average(delta_y, axis=axis) 11 | return mae 12 | 13 | def smape(y, y_hat, axis): 14 | delta_y = np.abs(y - y_hat) 15 | scale = np.abs(y) + np.abs(y_hat) 16 | smape = delta_y / scale 17 | smape = 200 * np.average(smape, axis=axis) 18 | return smape 19 | 20 | def evaluate(model: str, dataset: str, group: str): 21 | try: 22 | forecast = pd.read_csv(f'data/{model}-forecasts-{dataset}-{group}.csv') 23 | except: 24 | return None 25 | y_test, horizon, freq = get_data('data/', dataset, group, False) 26 | y_hat = forecast[model].values.reshape(-1, horizon) 27 | y_test = y_test['y'].values.reshape(-1, horizon) 28 | 29 | evals = {} 30 | for metric in (mae, smape): 31 | metric_name = metric.__name__ 32 | loss = metric(y_test, y_hat, axis=1).mean() 33 | evals[metric_name] = loss 34 | 35 | evals = pd.DataFrame(evals, index=[f'{dataset}_{group}']).rename_axis('dataset').reset_index() 36 | times = pd.read_csv(f'data/{model}-time-{dataset}-{group}.csv') 37 | evals = pd.concat([evals, times], axis=1) 38 | 39 | return evals 40 | 41 | 42 | if __name__ == '__main__': 43 | 44 | groups = ['ETTm2'] 45 | 46 | models = ['SOFTS', 47 | 'TSMixer', 48 | 'TSMixerx', 49 | 'iTransformer', 50 | 'StemGNN', 51 | 'MLPMultivariate', 52 | 'TimeMixer', 53 | 'TimeXer'] 54 | 55 | datasets = ['multivariate'] 56 | evaluation = [evaluate(model, dataset, group) for model, group in product(models, groups) for dataset in datasets] 57 | evaluation = [eval_ for eval_ in evaluation if eval_ is not None] 58 | df_evaluation = pd.concat(evaluation) 59 | df_evaluation = df_evaluation.loc[:, ['dataset', 'model', 'time', 'mae', 'smape']] 60 | df_evaluation['time'] /= 60 #minutes 61 | df_evaluation = df_evaluation.set_index(['dataset', 'model']).stack().reset_index() 62 | df_evaluation.columns = ['dataset', 'model', 'metric', 'val'] 63 | df_evaluation = df_evaluation.set_index(['dataset', 'metric', 'model']).unstack().round(3) 64 | df_evaluation = df_evaluation.droplevel(0, 1).reset_index() 65 | df_evaluation.to_csv('data/evaluation.csv') 66 | print(df_evaluation.T) 67 | -------------------------------------------------------------------------------- /action_files/test_models/src/multivariate_models.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | 4 | import fire 5 | import pandas as pd 6 | 7 | from neuralforecast.core import NeuralForecast 8 | 9 | from neuralforecast.models.softs import SOFTS 10 | from neuralforecast.models.tsmixer import TSMixer 11 | from neuralforecast.models.tsmixerx import TSMixerx 12 | from neuralforecast.models.itransformer import iTransformer 13 | from neuralforecast.models.mlpmultivariate import MLPMultivariate 14 | from neuralforecast.models.timemixer import TimeMixer 15 | from neuralforecast.models.timexer import TimeXer 16 | 17 | from neuralforecast.losses.pytorch import MAE 18 | 19 | from src.data import get_data 20 | 21 | os.environ['NIXTLA_ID_AS_COL'] = '1' 22 | 23 | 24 | def main(dataset: str = 'multivariate', group: str = 'ETTm2') -> None: 25 | train, horizon, freq = get_data('data/', dataset, group) 26 | train['ds'] = pd.to_datetime(train['ds']) 27 | 28 | models = [ 29 | SOFTS(h=horizon, n_series=7, input_size=2 * horizon, loss=MAE(), dropout=0.0, max_steps=500, val_check_steps=100, windows_batch_size=64, inference_windows_batch_size=64), 30 | TSMixer(h=horizon, n_series=7, input_size=2 * horizon, loss=MAE(), dropout=0.0, max_steps=1000, val_check_steps=100, windows_batch_size=64, inference_windows_batch_size=64), 31 | TSMixerx(h=horizon, n_series=7, input_size=2*horizon, loss=MAE(), dropout=0.0, max_steps=1000, val_check_steps=100, windows_batch_size=64, inference_windows_batch_size=64), 32 | iTransformer(h=horizon, n_series=7, input_size=2 * horizon, loss=MAE(), dropout=0.0, max_steps=500, val_check_steps=100, windows_batch_size=64, inference_windows_batch_size=64), 33 | MLPMultivariate(h=horizon, n_series=7, input_size=2*horizon, loss=MAE(), max_steps=1000, val_check_steps=100, windows_batch_size=64, inference_windows_batch_size=64), 34 | TimeMixer(h=horizon, n_series=7, input_size=2*horizon, loss=MAE(), dropout=0.0, max_steps=500, val_check_steps=100, windows_batch_size=64, inference_windows_batch_size=64), 35 | TimeXer(h=horizon, n_series=7, input_size=2*horizon, loss=MAE(), dropout=0.0, max_steps=500, val_check_steps=100, windows_batch_size=64, inference_windows_batch_size=64, hidden_size=128, d_ff=512) 36 | ] 37 | 38 | # Models 39 | for model in models: 40 | model_name = type(model).__name__ 41 | print(50*'-', model_name, 50*'-') 42 | start = time.time() 43 | fcst = NeuralForecast(models=[model], freq=freq) 44 | fcst.fit(train) 45 | forecasts = fcst.predict() 46 | end = time.time() 47 | print(end - start) 48 | 49 | forecasts.columns = ['unique_id', 'ds', model_name] 50 | forecasts.to_csv(f'data/{model_name}-forecasts-{dataset}-{group}.csv', index=False) 51 | time_df = pd.DataFrame({'time': [end - start], 'model': [model_name]}) 52 | time_df.to_csv(f'data/{model_name}-time-{dataset}-{group}.csv', index=False) 53 | 54 | if __name__ == '__main__': 55 | fire.Fire(main) 56 | -------------------------------------------------------------------------------- /environment-cpu.yml: -------------------------------------------------------------------------------- 1 | name: neuralforecast 2 | channels: 3 | - pytorch 4 | - conda-forge 5 | dependencies: 6 | - coreforecast>=0.0.6 7 | - cpuonly 8 | - fsspec 9 | - gitpython 10 | - hyperopt 11 | - jupyterlab 12 | - matplotlib 13 | - numba 14 | - numpy>=1.21.6 15 | - optuna 16 | - pandas>=1.3.5 17 | - pyarrow 18 | - pytorch>=2.0.0,<=2.6.0 19 | - pytorch-lightning>=2.0.0 20 | - pip 21 | - s3fs 22 | - snappy<1.2.0 23 | - pip: 24 | - nbdev 25 | - ipython<=8.32.0 26 | - black 27 | - polars 28 | - ray[tune]>=2.2.0 29 | - utilsforecast>=0.0.25 30 | -------------------------------------------------------------------------------- /environment-cuda.yml: -------------------------------------------------------------------------------- 1 | name: neuralforecast 2 | channels: 3 | - pytorch 4 | - nvidia 5 | - conda-forge 6 | dependencies: 7 | - coreforecast>=0.0.6 8 | - fsspec 9 | - gitpython 10 | - hyperopt 11 | - jupyterlab 12 | - matplotlib 13 | - numba 14 | - numpy>=1.21.6 15 | - optuna 16 | - pandas>=1.3.5 17 | - pyarrow 18 | - pytorch>=2.0.0,<=2.6.0 19 | - pytorch-cuda>=11.8 20 | - pytorch-lightning>=2.0.0 21 | - pip 22 | - s3fs 23 | - pip: 24 | - nbdev 25 | - ipython<=8.32.0 26 | - black 27 | - polars 28 | - "ray[tune]>=2.2.0" 29 | - utilsforecast>=0.0.24 30 | -------------------------------------------------------------------------------- /experiments/kan_benchmark/README.md: -------------------------------------------------------------------------------- 1 | # KAN for Forecasting - Benchmark on M3 and M4 datasets 2 | 3 | [Kolmogorov-Arnold Networks](https://arxiv.org/abs/2404.19756) (KANs) is an alternative to the multilayer perceptron (MLP). In this experiment, we assess the performance of KANs in forecasting time series. 4 | 5 | We use the M3 and M4 datasets, which represents more than 102 000 unique time series covering yearly, quarterly, monthly, weekly, daily and hourly frequencies. 6 | 7 | While KANs reduce the number of parameters by 38% to 92% compared to the MLP, it also rarely performs better than the MLP in time series forecasting tasks. In this benchmark, N-BEATS still performs best across the vast majority of datasets. 8 | 9 | The detailed results are shown in the table below. 10 | 11 | | Dataset | Model | MAE | sMAPE (%) | time (s) | 12 | |---------------|--------|-------------|----------------|-----------| 13 | | M3 - Yearly | KAN | 1206 | 9.74 | 23 | 14 | | | MLP | 1111 | 8.68 | 9 | 15 | | | NBEATS | **1027** | **8.35** | 11 | 16 | | | NHITS | 1087 | 8.36 | 14 | 17 | | M3 - Quarterly| KAN | 565 | 5.19 | 45 | 18 | | | MLP | **540** | 4.99 | 10 | 19 | | | NBEATS | 542 | **4.97** | 26 | 20 | | | NHITS | 573 | 5.29 | 26 | 21 | | M3 - Monthly | KAN | 676 | 7.55 | 38 | 22 | | | MLP | 653 | 7.19 | 20 | 23 | | | NBEATS | **637** | 7.11 | 24 | 24 | | | NHITS | **637** | **7.08** | 35 | 25 | | M4 - Yearly | KAN | 875 | 7.20 | 132 | 26 | | | MLP | 921 | 7.37 | 51 | 27 | | | NBEATS | 855 | **6.87** | 62 | 28 | | | NHITS | **852** | 6.88 | 73 | 29 | | M4 - Quarterly| KAN | 603 | 5.36 | 121 | 30 | | | MLP | 602 | 5.35 | 40 | 31 | | | NBEATS | **588** | **5.15** | 49 | 32 | | | NHITS | 591 | 5.19 | 61 | 33 | | M4 - Monthly | KAN | 607 | 7.00 | 215 | 34 | | | MLP | 594 | 6.80 | 150 | 35 | | | NBEATS | **584** | **6.70%** | 131 | 36 | | | NHITS | **584** | **6.70** | 173 | 37 | | M4 - Weekly | KAN | 341 | 4.70 | 34 | 38 | | | MLP | 375 | 5.00% | 22 | 39 | | | NBEATS | **313** | **4.00** | 18 | 40 | | | NHITS | 329 | 4.40 | 21 | 41 | | M4 - Daily | KAN | 194 | 1.60 | 53 | 42 | | | MLP | 189 | 1.60 | 24 | 43 | | | NBEATS | **176** | **1.50** | 43 | 44 | | | NHITS | **176** | **1.50** | 51 | 45 | | M4 - Hourly | KAN | **267** | **7.10** | 33 | 46 | | | MLP | 315 | 7.80 | 10 | 47 | | | NBEATS | 280 | 7.40 | 18 | 48 | | | NHITS | 302 | 6.95 | 23 | 49 |
50 | 51 | ## Reproducibility 52 | 53 | 1. Create a conda environment `kan_benchmark` using the `environment.yml` file. 54 | ```shell 55 | conda env create -f environment.yml 56 | ``` 57 | 58 | 3. Activate the conda environment using 59 | ```shell 60 | conda activate kan_benchmark 61 | ``` 62 | 63 | 4. Run the experiments using:
64 | - `--dataset` parameter in `['M3-yearly', 'M3-quarterly', 'M3-monthly', 'M4-yearly', 'M4-quarterly', 'M4-monthly', 'M4-weekly', 'M4-daily', 'M4-hourly']`
65 | 66 | ```shell 67 | python run_experiment.py --dataset 68 | ``` 69 |
70 | 71 | ## References 72 | -[Ziming Liu, Yixuan Wang, Sachin Vaidya, Fabian Ruehle, James Halverson, Marin Soljačić, Thomas Y. Hou, Max Tegmark - "KAN: Kolmogorov-Arnold Networks"](https://arxiv.org/abs/2404.19756) -------------------------------------------------------------------------------- /experiments/kan_benchmark/environment.yml: -------------------------------------------------------------------------------- 1 | name: kan_benchmark 2 | channels: 3 | - conda-forge 4 | dependencies: 5 | - numpy 6 | - utilsforecast 7 | - pip 8 | - pip: 9 | - "git+https://github.com/Nixtla/datasetsforecast.git" 10 | - "git+https://github.com/Nixtla/neuralforecast.git" -------------------------------------------------------------------------------- /experiments/kan_benchmark/run_experiment.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | import argparse 4 | 5 | import pandas as pd 6 | 7 | from datasetsforecast.m3 import M3 8 | from datasetsforecast.m4 import M4 9 | 10 | from utilsforecast.losses import mae, smape 11 | from utilsforecast.evaluation import evaluate 12 | 13 | from neuralforecast import NeuralForecast 14 | from neuralforecast.models import KAN, MLP, NBEATS, NHITS 15 | results = [] 16 | 17 | def get_dataset(name): 18 | if name == 'M3-yearly': 19 | Y_df, *_ = M3.load("./data", "Yearly") 20 | horizon = 6 21 | freq = 'Y' 22 | elif name == 'M3-quarterly': 23 | Y_df, *_ = M3.load("./data", "Quarterly") 24 | horizon = 8 25 | freq = 'Q' 26 | elif name == 'M3-monthly': 27 | Y_df, *_ = M3.load("./data", "Monthly") 28 | horizon = 18 29 | freq = 'M' 30 | elif name == 'M4-yearly': 31 | Y_df, *_ = M4.load("./data", "Yearly") 32 | Y_df['ds'] = Y_df['ds'].astype(int) 33 | horizon = 6 34 | freq = 1 35 | elif name == 'M4-quarterly': 36 | Y_df, *_ = M4.load("./data", "Quarterly") 37 | Y_df['ds'] = Y_df['ds'].astype(int) 38 | horizon = 8 39 | freq = 1 40 | elif name == 'M4-monthly': 41 | Y_df, *_ = M4.load("./data", "Monthly") 42 | Y_df['ds'] = Y_df['ds'].astype(int) 43 | horizon = 18 44 | freq = 1 45 | elif name == 'M4-weekly': 46 | Y_df, *_ = M4.load("./data", "Weekly") 47 | Y_df['ds'] = Y_df['ds'].astype(int) 48 | horizon = 13 49 | freq = 1 50 | elif name == 'M4-daily': 51 | Y_df, *_ = M4.load("./data", "Daily") 52 | Y_df['ds'] = Y_df['ds'].astype(int) 53 | horizon = 14 54 | freq = 1 55 | elif name == 'M4-hourly': 56 | Y_df, *_ = M4.load("./data", "Hourly") 57 | Y_df['ds'] = Y_df['ds'].astype(int) 58 | horizon = 48 59 | freq = 1 60 | 61 | return Y_df, horizon, freq 62 | 63 | if __name__ == "__main__": 64 | parser = argparse.ArgumentParser() 65 | parser.add_argument("-dataset", "--dataset", type=str) 66 | 67 | args = parser.parse_args() 68 | dataset = args.dataset 69 | 70 | Y_df, horizon, freq = get_dataset(dataset) 71 | 72 | test_df = Y_df.groupby('unique_id').tail(horizon) 73 | train_df = Y_df.drop(test_df.index).reset_index(drop=True) 74 | 75 | kan_model = KAN(input_size=2*horizon, h=horizon, scaler_type='robust', early_stop_patience_steps=3) 76 | mlp_model = MLP(input_size=2*horizon, h=horizon, scaler_type='robust', max_steps=1000, early_stop_patience_steps=3) 77 | nbeats_model = NBEATS(input_size=2*horizon, h=horizon, scaler_type='robust', max_steps=1000, early_stop_patience_steps=3) 78 | nhits_model = NHITS(input_size=2*horizon, h=horizon, scaler_type='robust', max_steps=1000, early_stop_patience_steps=3) 79 | 80 | MODELS = [kan_model, mlp_model, nbeats_model, nhits_model] 81 | MODEL_NAMES = ['KAN', 'MLP', 'NBEATS', 'NHITS'] 82 | 83 | for i, model in enumerate(MODELS): 84 | nf = NeuralForecast(models=[model], freq=freq) 85 | 86 | start = time.time() 87 | 88 | nf.fit(train_df, val_size=horizon) 89 | preds = nf.predict() 90 | 91 | end = time.time() 92 | elapsed_time = round(end - start,0) 93 | 94 | preds = preds.reset_index() 95 | test_df = pd.merge(test_df, preds, 'left', ['ds', 'unique_id']) 96 | 97 | evaluation = evaluate( 98 | test_df, 99 | metrics=[mae, smape], 100 | models=[f"{MODEL_NAMES[i]}"], 101 | target_col="y", 102 | ) 103 | 104 | evaluation = evaluation.drop(['unique_id'], axis=1).groupby('metric').mean().reset_index() 105 | 106 | model_mae = evaluation[f"{MODEL_NAMES[i]}"][0] 107 | model_smape = evaluation[f"{MODEL_NAMES[i]}"][1] 108 | 109 | results.append([dataset, MODEL_NAMES[i], round(model_mae, 0), round(model_smape*100,2), elapsed_time]) 110 | 111 | results_df = pd.DataFrame(data=results, columns=['dataset', 'model', 'mae', 'smape', 'time']) 112 | os.makedirs('./results', exist_ok=True) 113 | results_df.to_csv(f'./results/{dataset}_results_KANtuned.csv', header=True, index=False) 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /experiments/long_horizon/README.md: -------------------------------------------------------------------------------- 1 | # Long Horizon Forecasting Experiments with NHITS 2 | 3 | In these experiments we use `NHITS` on the [ETTh1, ETTh2, ETTm1, ETTm2](https://github.com/zhouhaoyi/ETDataset) benchmark datasets. 4 | 5 | | Dataset | Horizon | NHITS-MSE | NHITS-MAE | TIDE-MSE | TIDE-MAE | 6 | |----------|----------|------------|------------|------------|------------| 7 | | ETTh1 | 96 | 0.378 | 0.393 | 0.375 | 0.398 | 8 | | ETTh1 | 192 | 0.427 | 0.436 | 0.412 | 0.422 | 9 | | ETTh1 | 336 | 0.458 | 0.484 | 0.435 | 0.433 | 10 | | ETTh1 | 720 | 0.561 | 0.501 | 0.454 | 0.465 | 11 | |----------|----------|------------|------------|------------|------------| 12 | | ETTh2 | 96 | 0.274 | 0.345 | 0.270 | 0.336 | 13 | | ETTh2 | 192 | 0.353 | 0.401 | 0.332 | 0.380 | 14 | | ETTh2 | 336 | 0.382 | 0.425 | 0.360 | 0.407 | 15 | | ETTh2 | 720 | 0.625 | 0.557 | 0.419 | 0.451 | 16 | |----------|----------|------------|------------|------------|------------| 17 | | ETTm1 | 96 | 0.302 | 0.35 | 0.306 | 0.349 | 18 | | ETTm1 | 192 | 0.347 | 0.383 | 0.335 | 0.366 | 19 | | ETTm1 | 336 | 0.369 | 0.402 | 0.364 | 0.384 | 20 | | ETTm1 | 720 | 0.431 | 0.441 | 0.413 | 0.413 | 21 | |----------|----------|------------|------------|------------|------------| 22 | | ETTm2 | 96 | 0.176 | 0.255 | 0.161 | 0.251 | 23 | | ETTm2 | 192 | 0.245 | 0.305 | 0.215 | 0.289 | 24 | | ETTm2 | 336 | 0.295 | 0.346 | 0.267 | 0.326 | 25 | | ETTm2 | 720 | 0.401 | 0.413 | 0.352 | 0.383 | 26 | |----------|----------|------------|------------|------------|------------| 27 |
28 | 29 | ## Reproducibility 30 | 31 | 1. Create a conda environment `long_horizon` using the `environment.yml` file. 32 | ```shell 33 | conda env create -f environment.yml 34 | ``` 35 | 36 | 3. Activate the conda environment using 37 | ```shell 38 | conda activate long_horizon 39 | ``` 40 | 41 | Alternatively simply installing neuralforecast and datasetsforecast with pip may suffice: 42 | ``` 43 | pip install git+https://github.com/Nixtla/datasetsforecast.git 44 | pip install git+https://github.com/Nixtla/neuralforecast.git 45 | ``` 46 | 47 | 4. Run the experiments for each dataset and each model using with 48 | - `--horizon` parameter in `[96, 192, 336, 720]` 49 | - `--dataset` parameter in `['ETTh1', 'ETTh2', 'ETTm1', 'ETTm2']` 50 |
51 | 52 | ```shell 53 | python run_nhits.py --dataset 'ETTh1' --horizon 96 --num_samples 20 54 | ``` 55 | 56 | You can access the final forecasts from the `./data/{dataset}/{horizon}_forecasts.csv` file. Example: `./data/ETTh1/96_forecasts.csv`. 57 |

58 | 59 | ## References 60 | -[Cristian Challu, Kin G. Olivares, Boris N. Oreshkin, Federico Garza, Max Mergenthaler-Canseco, Artur Dubrawski (2023). "NHITS: Neural Hierarchical Interpolation for Time Series Forecasting". Accepted at the Thirty-Seventh AAAI Conference on Artificial Intelligence.](https://arxiv.org/abs/2201.12886) -------------------------------------------------------------------------------- /experiments/long_horizon/environment.yml: -------------------------------------------------------------------------------- 1 | name: long_horizon 2 | channels: 3 | - conda-forge 4 | dependencies: 5 | - numpy<1.24 6 | - pip 7 | - pip: 8 | - "git+https://github.com/Nixtla/datasetsforecast.git" 9 | - "git+https://github.com/Nixtla/neuralforecast.git" -------------------------------------------------------------------------------- /experiments/long_horizon/run_nhits.py: -------------------------------------------------------------------------------- 1 | import os 2 | os.environ["PYTORCH_ENABLE_MPS_FALLBACK"] = "1" 3 | 4 | import argparse 5 | import pandas as pd 6 | 7 | from ray import tune 8 | 9 | from neuralforecast.auto import AutoNHITS 10 | from neuralforecast.core import NeuralForecast 11 | 12 | from neuralforecast.losses.pytorch import MAE, HuberLoss 13 | from neuralforecast.losses.numpy import mae, mse 14 | #from datasetsforecast.long_horizon import LongHorizon, LongHorizonInfo 15 | from datasetsforecast.long_horizon2 import LongHorizon2, LongHorizon2Info 16 | 17 | import logging 18 | logging.getLogger("pytorch_lightning").setLevel(logging.WARNING) 19 | 20 | 21 | if __name__ == '__main__': 22 | 23 | # Parse execution parameters 24 | verbose = True 25 | parser = argparse.ArgumentParser() 26 | parser.add_argument("-horizon", "--horizon", type=int) 27 | parser.add_argument("-dataset", "--dataset", type=str) 28 | parser.add_argument("-num_samples", "--num_samples", default=5, type=int) 29 | 30 | args = parser.parse_args() 31 | horizon = args.horizon 32 | dataset = args.dataset 33 | num_samples = args.num_samples 34 | 35 | assert horizon in [96, 192, 336, 720] 36 | 37 | # Load dataset 38 | #Y_df, _, _ = LongHorizon.load(directory='./data/', group=dataset) 39 | #Y_df['ds'] = pd.to_datetime(Y_df['ds']) 40 | 41 | Y_df = LongHorizon2.load(directory='./data/', group=dataset) 42 | freq = LongHorizon2Info[dataset].freq 43 | n_time = len(Y_df.ds.unique()) 44 | #val_size = int(.2 * n_time) 45 | #test_size = int(.2 * n_time) 46 | val_size = LongHorizon2Info[dataset].val_size 47 | test_size = LongHorizon2Info[dataset].test_size 48 | 49 | # Adapt input_size to available data 50 | input_size = tune.choice([7 * horizon]) 51 | if dataset=='ETTm1' and horizon==720: 52 | input_size = tune.choice([2 * horizon]) 53 | 54 | nhits_config = { 55 | #"learning_rate": tune.choice([1e-3]), # Initial Learning rate 56 | "learning_rate": tune.loguniform(1e-5, 5e-3), 57 | "max_steps": tune.choice([200, 1000]), # Number of SGD steps 58 | "input_size": input_size, # input_size = multiplier * horizon 59 | "batch_size": tune.choice([7]), # Number of series in windows 60 | "windows_batch_size": tune.choice([256]), # Number of windows in batch 61 | "n_pool_kernel_size": tune.choice([[2, 2, 2], [16, 8, 1]]), # MaxPool's Kernelsize 62 | "n_freq_downsample": tune.choice([[(96*7)//2, 96//2, 1], 63 | [(24*7)//2, 24//2, 1], 64 | [1, 1, 1]]), # Interpolation expressivity ratios 65 | "dropout_prob_theta": tune.choice([0.5]), # Dropout regularization 66 | "activation": tune.choice(['ReLU']), # Type of non-linear activation 67 | "n_blocks": tune.choice([[1, 1, 1]]), # Blocks per each 3 stacks 68 | "mlp_units": tune.choice([[[512, 512], [512, 512], [512, 512]]]), # 2 512-Layers per block for each stack 69 | "interpolation_mode": tune.choice(['linear']), # Type of multi-step interpolation 70 | "val_check_steps": tune.choice([100]), # Compute validation every 100 epochs 71 | "random_seed": tune.randint(1, 10), 72 | } 73 | 74 | models = [AutoNHITS(h=horizon, 75 | loss=HuberLoss(delta=0.5), 76 | valid_loss=MAE(), 77 | config=nhits_config, 78 | num_samples=num_samples, 79 | refit_with_val=True)] 80 | 81 | nf = NeuralForecast(models=models, freq=freq) 82 | 83 | Y_hat_df = nf.cross_validation(df=Y_df, val_size=val_size, 84 | test_size=test_size, n_windows=None) 85 | 86 | 87 | y_true = Y_hat_df.y.values 88 | y_hat = Y_hat_df['AutoNHITS'].values 89 | 90 | n_series = len(Y_df.unique_id.unique()) 91 | 92 | y_true = y_true.reshape(n_series, -1, horizon) 93 | y_hat = y_hat.reshape(n_series, -1, horizon) 94 | 95 | print('\n'*4) 96 | print('Parsed results') 97 | print(f'NHITS {dataset} h={horizon}') 98 | print('test_size', test_size) 99 | print('y_true.shape (n_series, n_windows, n_time_out):\t', y_true.shape) 100 | print('y_hat.shape (n_series, n_windows, n_time_out):\t', y_hat.shape) 101 | 102 | print('MSE: ', mse(y_hat, y_true)) 103 | print('MAE: ', mae(y_hat, y_true)) 104 | 105 | # Save Outputs 106 | if not os.path.exists(f'./data/{dataset}'): 107 | os.makedirs(f'./data/{dataset}') 108 | yhat_file = f'./data/{dataset}/{horizon}_forecasts.csv' 109 | Y_hat_df.to_csv(yhat_file, index=False) 110 | -------------------------------------------------------------------------------- /nbs/.gitignore: -------------------------------------------------------------------------------- 1 | /.quarto/ 2 | /lightning_logs/ 3 | -------------------------------------------------------------------------------- /nbs/_quarto.yml: -------------------------------------------------------------------------------- 1 | project: 2 | type: website 3 | 4 | format: 5 | html: 6 | theme: cosmo 7 | fontsize: 1em 8 | linestretch: 1.7 9 | css: styles.css 10 | toc: true 11 | 12 | website: 13 | twitter-card: 14 | image: "https://farm6.staticflickr.com/5510/14338202952_93595258ff_z.jpg" 15 | site: "@Nixtlainc" 16 | open-graph: 17 | image: "https://github.com/Nixtla/styles/blob/2abf51612584169874c90cd7c4d347e3917eaf73/images/Banner%20Github.png" 18 | google-analytics: "G-NXJNCVR18L" 19 | repo-actions: [issue] 20 | favicon: favicon_png.png 21 | navbar: 22 | background: primary 23 | search: true 24 | collapse-below: lg 25 | left: 26 | - text: "Get Started" 27 | href: docs/getting-started/02_quickstart.ipynb 28 | - text: "NixtlaVerse" 29 | menu: 30 | - text: "MLForecast 🤖" 31 | href: https://github.com/nixtla/mlforecast 32 | - text: "StatsForecast ⚡️" 33 | href: https://github.com/nixtla/statsforecast 34 | - text: "HierarchicalForecast 👑" 35 | href: "https://github.com/nixtla/hierarchicalforecast" 36 | 37 | - text: "Help" 38 | menu: 39 | - text: "Report an Issue" 40 | icon: bug 41 | href: https://github.com/nixtla/neuralforecast/issues/new/choose 42 | - text: "Join our Slack" 43 | icon: chat-right-text 44 | href: https://join.slack.com/t/nixtlaworkspace/shared_invite/zt-135dssye9-fWTzMpv2WBthq8NK0Yvu6A 45 | right: 46 | - icon: github 47 | href: "https://github.com/nixtla/neuralforecast" 48 | - icon: twitter 49 | href: https://twitter.com/nixtlainc 50 | aria-label: Nixtla Twitter 51 | 52 | sidebar: 53 | style: floating 54 | body-footer: | 55 | Give us a ⭐ on [Github](https://github.com/nixtla/neuralforecast) 56 | 57 | metadata-files: [nbdev.yml, sidebar.yml] 58 | -------------------------------------------------------------------------------- /nbs/common.model_checks.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "#| default_exp common._model_checks" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": null, 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "#| hide\n", 19 | "%load_ext autoreload\n", 20 | "%autoreload 2" 21 | ] 22 | }, 23 | { 24 | "cell_type": "markdown", 25 | "metadata": {}, 26 | "source": [ 27 | "# 1. Checks for models" 28 | ] 29 | }, 30 | { 31 | "cell_type": "markdown", 32 | "metadata": {}, 33 | "source": [ 34 | "This file provides a set of unit tests for all models" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": null, 40 | "metadata": {}, 41 | "outputs": [], 42 | "source": [ 43 | "#| export\n", 44 | "import pandas as pd\n", 45 | "import neuralforecast.losses.pytorch as losses\n", 46 | "\n", 47 | "from neuralforecast import NeuralForecast\n", 48 | "from neuralforecast.utils import AirPassengersPanel, AirPassengersStatic, generate_series" 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": null, 54 | "metadata": {}, 55 | "outputs": [], 56 | "source": [ 57 | "#| export\n", 58 | "seed = 0\n", 59 | "test_size = 14\n", 60 | "FREQ = \"D\"\n", 61 | "\n", 62 | "# 1 series, no exogenous\n", 63 | "N_SERIES_1 = 1\n", 64 | "df = generate_series(n_series=N_SERIES_1, seed=seed, freq=FREQ, equal_ends=True)\n", 65 | "max_ds = df.ds.max() - pd.Timedelta(test_size, FREQ)\n", 66 | "Y_TRAIN_DF_1 = df[df.ds < max_ds]\n", 67 | "Y_TEST_DF_1 = df[df.ds >= max_ds]\n", 68 | "\n", 69 | "# 5 series, no exogenous\n", 70 | "N_SERIES_2 = 5\n", 71 | "df = generate_series(n_series=N_SERIES_2, seed=seed, freq=FREQ, equal_ends=True)\n", 72 | "max_ds = df.ds.max() - pd.Timedelta(test_size, FREQ)\n", 73 | "Y_TRAIN_DF_2 = df[df.ds < max_ds]\n", 74 | "Y_TEST_DF_2 = df[df.ds >= max_ds]\n", 75 | "\n", 76 | "# 1 series, with static and temporal exogenous\n", 77 | "N_SERIES_3 = 1\n", 78 | "df, STATIC_3 = generate_series(n_series=N_SERIES_3, n_static_features=2, \n", 79 | " n_temporal_features=2, seed=seed, freq=FREQ, equal_ends=True)\n", 80 | "max_ds = df.ds.max() - pd.Timedelta(test_size, FREQ)\n", 81 | "Y_TRAIN_DF_3 = df[df.ds < max_ds]\n", 82 | "Y_TEST_DF_3 = df[df.ds >= max_ds]\n", 83 | "\n", 84 | "# 5 series, with static and temporal exogenous\n", 85 | "N_SERIES_4 = 5\n", 86 | "df, STATIC_4 = generate_series(n_series=N_SERIES_4, n_static_features=2, \n", 87 | " n_temporal_features=2, seed=seed, freq=FREQ, equal_ends=True)\n", 88 | "max_ds = df.ds.max() - pd.Timedelta(test_size, FREQ)\n", 89 | "Y_TRAIN_DF_4 = df[df.ds < max_ds]\n", 90 | "Y_TEST_DF_4 = df[df.ds >= max_ds]\n", 91 | "\n", 92 | "# Generic test for a given config for a model\n", 93 | "def _run_model_tests(model_class, config):\n", 94 | " if model_class.RECURRENT:\n", 95 | " config[\"inference_input_size\"] = config[\"input_size\"]\n", 96 | "\n", 97 | " # DF_1\n", 98 | " if model_class.MULTIVARIATE:\n", 99 | " config[\"n_series\"] = N_SERIES_1\n", 100 | " if isinstance(config[\"loss\"], losses.relMSE):\n", 101 | " config[\"loss\"].y_train = Y_TRAIN_DF_1[\"y\"].values \n", 102 | " if isinstance(config[\"valid_loss\"], losses.relMSE):\n", 103 | " config[\"valid_loss\"].y_train = Y_TRAIN_DF_1[\"y\"].values \n", 104 | "\n", 105 | " model = model_class(**config)\n", 106 | " fcst = NeuralForecast(models=[model], freq=FREQ)\n", 107 | " fcst.fit(df=Y_TRAIN_DF_1, val_size=24)\n", 108 | " _ = fcst.predict(futr_df=Y_TEST_DF_1)\n", 109 | " # DF_2\n", 110 | " if model_class.MULTIVARIATE:\n", 111 | " config[\"n_series\"] = N_SERIES_2\n", 112 | " if isinstance(config[\"loss\"], losses.relMSE):\n", 113 | " config[\"loss\"].y_train = Y_TRAIN_DF_2[\"y\"].values \n", 114 | " if isinstance(config[\"valid_loss\"], losses.relMSE):\n", 115 | " config[\"valid_loss\"].y_train = Y_TRAIN_DF_2[\"y\"].values\n", 116 | " model = model_class(**config)\n", 117 | " fcst = NeuralForecast(models=[model], freq=FREQ)\n", 118 | " fcst.fit(df=Y_TRAIN_DF_2, val_size=24)\n", 119 | " _ = fcst.predict(futr_df=Y_TEST_DF_2)\n", 120 | "\n", 121 | " if model.EXOGENOUS_STAT and model.EXOGENOUS_FUTR:\n", 122 | " # DF_3\n", 123 | " if model_class.MULTIVARIATE:\n", 124 | " config[\"n_series\"] = N_SERIES_3\n", 125 | " if isinstance(config[\"loss\"], losses.relMSE):\n", 126 | " config[\"loss\"].y_train = Y_TRAIN_DF_3[\"y\"].values \n", 127 | " if isinstance(config[\"valid_loss\"], losses.relMSE):\n", 128 | " config[\"valid_loss\"].y_train = Y_TRAIN_DF_3[\"y\"].values\n", 129 | " model = model_class(**config)\n", 130 | " fcst = NeuralForecast(models=[model], freq=FREQ)\n", 131 | " fcst.fit(df=Y_TRAIN_DF_3, static_df=STATIC_3, val_size=24)\n", 132 | " _ = fcst.predict(futr_df=Y_TEST_DF_3)\n", 133 | "\n", 134 | " # DF_4\n", 135 | " if model_class.MULTIVARIATE:\n", 136 | " config[\"n_series\"] = N_SERIES_4\n", 137 | " if isinstance(config[\"loss\"], losses.relMSE):\n", 138 | " config[\"loss\"].y_train = Y_TRAIN_DF_4[\"y\"].values \n", 139 | " if isinstance(config[\"valid_loss\"], losses.relMSE):\n", 140 | " config[\"valid_loss\"].y_train = Y_TRAIN_DF_4[\"y\"].values \n", 141 | " model = model_class(**config)\n", 142 | " fcst = NeuralForecast(models=[model], freq=FREQ)\n", 143 | " fcst.fit(df=Y_TRAIN_DF_4, static_df=STATIC_4, val_size=24)\n", 144 | " _ = fcst.predict(futr_df=Y_TEST_DF_4) \n", 145 | "\n", 146 | "# Tests a model against every loss function\n", 147 | "def check_loss_functions(model_class):\n", 148 | " loss_list = [losses.MAE(), losses.MSE(), losses.RMSE(), losses.MAPE(), losses.SMAPE(), losses.MASE(seasonality=7), \n", 149 | " losses.QuantileLoss(q=0.5), losses.MQLoss(), losses.IQLoss(), losses.HuberIQLoss(), losses.DistributionLoss(\"Normal\"), \n", 150 | " losses.DistributionLoss(\"StudentT\"), losses.DistributionLoss(\"Poisson\"), losses.DistributionLoss(\"NegativeBinomial\"), \n", 151 | " losses.DistributionLoss(\"Tweedie\", rho=1.5), losses.DistributionLoss(\"ISQF\"), losses.PMM(), losses.PMM(weighted=True), \n", 152 | " losses.GMM(), losses.GMM(weighted=True), losses.NBMM(), losses.NBMM(weighted=True), losses.HuberLoss(), \n", 153 | " losses.TukeyLoss(), losses.HuberQLoss(q=0.5), losses.HuberMQLoss()]\n", 154 | " for loss in loss_list:\n", 155 | " test_name = f\"{model_class.__name__}: checking {loss._get_name()}\"\n", 156 | " print(f\"{test_name}\")\n", 157 | " config = {'max_steps': 2,\n", 158 | " 'h': 7,\n", 159 | " 'input_size': 28,\n", 160 | " 'loss': loss,\n", 161 | " 'valid_loss': None,\n", 162 | " 'enable_progress_bar': False,\n", 163 | " 'enable_model_summary': False,\n", 164 | " 'val_check_steps': 2} \n", 165 | " try:\n", 166 | " _run_model_tests(model_class, config) \n", 167 | " except RuntimeError:\n", 168 | " raise Exception(f\"{test_name} failed.\")\n", 169 | " except Exception:\n", 170 | " print(f\"{test_name} skipped on raised Exception.\")\n", 171 | " pass\n", 172 | "\n", 173 | "# Tests a model against the AirPassengers dataset\n", 174 | "def check_airpassengers(model_class):\n", 175 | " print(f\"{model_class.__name__}: checking forecast AirPassengers dataset\")\n", 176 | " Y_train_df = AirPassengersPanel[AirPassengersPanel.ds=AirPassengersPanel['ds'].values[-12]].reset_index(drop=True) # 12 test\n", 178 | "\n", 179 | " config = {'max_steps': 2,\n", 180 | " 'h': 12,\n", 181 | " 'input_size': 24,\n", 182 | " 'enable_progress_bar': False,\n", 183 | " 'enable_model_summary': False,\n", 184 | " 'val_check_steps': 2,\n", 185 | " }\n", 186 | "\n", 187 | " if model_class.MULTIVARIATE:\n", 188 | " config[\"n_series\"] = Y_train_df[\"unique_id\"].nunique()\n", 189 | " # Normal forecast\n", 190 | " fcst = NeuralForecast(models=[model_class(**config)], freq='M')\n", 191 | " fcst.fit(df=Y_train_df, static_df=AirPassengersStatic)\n", 192 | " _ = fcst.predict(futr_df=Y_test_df) \n", 193 | "\n", 194 | " # Cross-validation\n", 195 | " fcst = NeuralForecast(models=[model_class(**config)], freq='M')\n", 196 | " _ = fcst.cross_validation(df=AirPassengersPanel, static_df=AirPassengersStatic, n_windows=2, step_size=12)\n", 197 | "\n", 198 | "# Add unit test functions to this function\n", 199 | "def check_model(model_class, checks=[\"losses\", \"airpassengers\"]):\n", 200 | " \"\"\"\n", 201 | " Check model with various tests. Options for checks are:
\n", 202 | " \"losses\": test the model against all loss functions
\n", 203 | " \"airpassengers\": test the model against the airpassengers dataset for forecasting and cross-validation
\n", 204 | " \n", 205 | " \"\"\"\n", 206 | " if \"losses\" in checks:\n", 207 | " check_loss_functions(model_class) \n", 208 | " if \"airpassengers\" in checks:\n", 209 | " try:\n", 210 | " check_airpassengers(model_class) \n", 211 | " except RuntimeError:\n", 212 | " raise Exception(f\"{model_class.__name__}: AirPassengers forecast test failed.\")\n" 213 | ] 214 | }, 215 | { 216 | "cell_type": "code", 217 | "execution_count": null, 218 | "metadata": {}, 219 | "outputs": [], 220 | "source": [ 221 | "#| eval: false\n", 222 | "#| hide\n", 223 | "# Run tests in this file. This is a slow test\n", 224 | "import warnings\n", 225 | "import logging\n", 226 | "from neuralforecast.models import RNN, GRU, TCN, LSTM, DeepAR, DilatedRNN, BiTCN, MLP, NBEATS, NBEATSx, NHITS, DLinear, NLinear, TiDE, DeepNPTS, TFT, VanillaTransformer, Informer, Autoformer, FEDformer, TimesNet, iTransformer, KAN, RMoK, StemGNN, TSMixer, TSMixerx, MLPMultivariate, SOFTS, TimeMixer\n", 227 | "\n", 228 | "models = [RNN, GRU, TCN, LSTM, DeepAR, DilatedRNN, BiTCN, MLP, NBEATS, NBEATSx, NHITS, DLinear, NLinear, TiDE, DeepNPTS, TFT, VanillaTransformer, Informer, Autoformer, FEDformer, TimesNet, iTransformer, KAN, RMoK, StemGNN, TSMixer, TSMixerx, MLPMultivariate, SOFTS, TimeMixer]\n", 229 | "\n", 230 | "logging.getLogger(\"pytorch_lightning\").setLevel(logging.ERROR)\n", 231 | "logging.getLogger(\"lightning_fabric\").setLevel(logging.ERROR)\n", 232 | "with warnings.catch_warnings():\n", 233 | " warnings.simplefilter(\"ignore\")\n", 234 | " for model in models:\n", 235 | " check_model(model, checks=[\"losses\"])" 236 | ] 237 | } 238 | ], 239 | "metadata": { 240 | "kernelspec": { 241 | "display_name": "python3", 242 | "language": "python", 243 | "name": "python3" 244 | } 245 | }, 246 | "nbformat": 4, 247 | "nbformat_minor": 4 248 | } 249 | -------------------------------------------------------------------------------- /nbs/compat.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "#| default_exp compat" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": null, 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "#| export\n", 19 | "try:\n", 20 | " from pyspark.sql import DataFrame as SparkDataFrame\n", 21 | "except ImportError:\n", 22 | " class SparkDataFrame: ..." 23 | ] 24 | } 25 | ], 26 | "metadata": {}, 27 | "nbformat": 4, 28 | "nbformat_minor": 2 29 | } 30 | -------------------------------------------------------------------------------- /nbs/custom.yml: -------------------------------------------------------------------------------- 1 | website: 2 | logo: https://github.com/Nixtla/styles/blob/b9ea432cfa2dae20fc84d8634cae6db902f9ca3f/images/Nixtla_Blanco.png 3 | reader-mode: false 4 | navbar: 5 | collapse-below: lg 6 | left: 7 | - text: "Get Started" 8 | href: docs/getting-started/02_quickstart.ipynb 9 | - text: "Help" 10 | menu: 11 | - text: "Report an Issue" 12 | icon: bug 13 | href: https://github.com/nixtla/neuralforecast/issues 14 | - text: "Slack Nixtla" 15 | icon: chat-right-text 16 | href: https://join.slack.com/t/nixtlaworkspace/shared_invite/zt-135dssye9-fWTzMpv2WBthq8NK0Yvu6A 17 | right: 18 | - icon: twitter 19 | href: https://twitter.com/nixtlainc 20 | aria-label: Nixtla Twitter 21 | -------------------------------------------------------------------------------- /nbs/docs/api-reference/.notest: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/docs/api-reference/.notest -------------------------------------------------------------------------------- /nbs/docs/api-reference/01_neuralforecast_map.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "attachments": {}, 5 | "cell_type": "markdown", 6 | "metadata": {}, 7 | "source": [ 8 | "# NeuralForecast Map\n", 9 | "> Modules of the NeuralForecast library" 10 | ] 11 | }, 12 | { 13 | "attachments": {}, 14 | "cell_type": "markdown", 15 | "metadata": {}, 16 | "source": [ 17 | "The `neuralforecast` library provides a comprehensive set of state-of-the-art deep learning models designed to power-up time series forecasting pipelines.\n", 18 | "\n", 19 | "The library is constructed using a modular approach, where different responsibilities are isolated within specific modules. These modules include the user interface functions (`core`), data processing and loading (`tsdataset`), scalers, losses, and base classes for models.\n", 20 | "\n", 21 | "This tutorial aims to explain the library's structure and to describe how the different modules interact with each other." 22 | ] 23 | }, 24 | { 25 | "attachments": {}, 26 | "cell_type": "markdown", 27 | "metadata": {}, 28 | "source": [ 29 | "## I. Map" 30 | ] 31 | }, 32 | { 33 | "attachments": {}, 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "The following diagram presents the modules of the `neuralforecast` library and their relations." 38 | ] 39 | }, 40 | { 41 | "attachments": {}, 42 | "cell_type": "markdown", 43 | "metadata": {}, 44 | "source": [ 45 | "![Neuralforecast map](../../imgs_indx/nf_map.png)" 46 | ] 47 | }, 48 | { 49 | "attachments": {}, 50 | "cell_type": "markdown", 51 | "metadata": {}, 52 | "source": [ 53 | "## II. Modules" 54 | ] 55 | }, 56 | { 57 | "attachments": {}, 58 | "cell_type": "markdown", 59 | "metadata": {}, 60 | "source": [ 61 | "### 1. Core (`core.py`)\n", 62 | "\n", 63 | "The `core` module acts as the primary interaction point for users of the `neuralforecast` library. It houses the `NeuralForecast` class, which incorporates a range of key user interface functions designed to simplify the process of training and forecasting models. Functions include `fit`, `predict`, `cross_validation`, and `predict_insample`, each one constructed to be intuitive and user-friendly. The design of the `NeuralForecast` class is centered around enabling users to streamline their forecasting pipelines and to comfortably train and evaluate models.\n", 64 | "\n", 65 | "### 2. Dataset and Loader (`tsdataset.py`)\n", 66 | "\n", 67 | "The `TimeSeriesDataset` class, located within the `tsdataset` module, is responsible for the storage and preprocessing of the input time series dataset. Once the `TimeSeriesDataset` class has prepared the data, it's then consumed by the `TimeSeriesLoader` class, which samples batches (or subsets) of the time series during the training and inference stages.\n", 68 | "\n", 69 | "### 3. Base Model (`common`)\n", 70 | "\n", 71 | "The `common` module contains three `BaseModel` classes, which serve as the foundation for all the model structures provided in the library. These base classes allow for a level of abstraction and code-reusability in the design of the models. We currently support three type of models:\n", 72 | "\n", 73 | " * `BaseWindows`: designed for window-based models like `NBEATS` and `Transformers`.\n", 74 | " * `BaseRecurrent`: designed for recurrent models like `RNN` and `LSTM`.\n", 75 | " * `BaseMultivariate`: caters to multivariate models like `StemGNN`.\n", 76 | "\n", 77 | "### 4. Model (`models`)\n", 78 | "\n", 79 | "The `models` module encompasses all the specific model classes available for use in the library. These include a variety of both simple and complex models such as `RNN`, `NHITS`, `LSTM`, `StemGNN`, and `TFT`. Each model in this module extends from one of the `BaseModel` classes in the `common` module.\n", 80 | "\n", 81 | "### 5. Losses (`losses`)\n", 82 | "\n", 83 | "The `losses` module includes both `numpy` and `pytorch` losses, used for evalaution and training respectively. The module contains a wide range of losses, including `MAE`, `MSE`, `MAPE`, `HuberLoss`, among many others. \n", 84 | "\n", 85 | "### 6. Scalers (`_scalers.py`)\n", 86 | "\n", 87 | "The `_scalers.py` module houses the `TemporalNorm` class. This class is responsible for the scaling (normalization) and de-scaling (reversing the normalization) of time series data. This step is crucial because it ensures all data fed to the model have a similar range, leading to more stable and efficient training processes." 88 | ] 89 | }, 90 | { 91 | "attachments": {}, 92 | "cell_type": "markdown", 93 | "metadata": {}, 94 | "source": [ 95 | "## III. Flow" 96 | ] 97 | }, 98 | { 99 | "attachments": {}, 100 | "cell_type": "markdown", 101 | "metadata": {}, 102 | "source": [ 103 | "The `user` first instantiates a model and the `NeuralForecast` core class. When they call the `fit` method, the following flow is executed:\n", 104 | "\n", 105 | "1. The `fit` method instantiates a `TimeSeriesDataset` object to store and pre-process the input time series dataset, and the `TimeSeriesLoader` object to sample batches.\n", 106 | "2. The `fit` method calls the model's `fit` method (in the `BaseModel` class).\n", 107 | "3. The model's `fit` method instantiates a Pytorch-Lightning `Trainer` object, in charge of training the model. \n", 108 | "4. The `Trainer` method samples a batch from the `TimeSeriesLoader` object, and calls the model's `training_step` method (in the `BaseModel` class).\n", 109 | "5. The model's `training_step`:\n", 110 | " * Samples windows from the original batch.\n", 111 | " * Normalizes the windows with the `scaler` module.\n", 112 | " * Calls the model's `forward` method.\n", 113 | " * Computes the loss using the `losses` module.\n", 114 | " * Returns the loss.\n", 115 | "6. The `Trainer` object repeats step 4 and 5 until `max_steps` iterations are completed.\n", 116 | "7. The model is fitted, and can be used for forecasting future values (with the `predict` method) or recover insample predictions (using the `predict_insample` method)." 117 | ] 118 | }, 119 | { 120 | "attachments": {}, 121 | "cell_type": "markdown", 122 | "metadata": {}, 123 | "source": [ 124 | "## IV. Next Steps: add your own model" 125 | ] 126 | }, 127 | { 128 | "attachments": {}, 129 | "cell_type": "markdown", 130 | "metadata": {}, 131 | "source": [ 132 | "Congratulations! You now know the internal details of the `neuralforecast` library.\n", 133 | "\n", 134 | "With this knowledge you can easily add new models to the library, by just creating a `model` class which only requires the `init` and `forward` methods.\n", 135 | "\n", 136 | "Check our detailed guide on how to add new models!\n" 137 | ] 138 | } 139 | ], 140 | "metadata": { 141 | "kernelspec": { 142 | "display_name": "python3", 143 | "language": "python", 144 | "name": "python3" 145 | } 146 | }, 147 | "nbformat": 4, 148 | "nbformat_minor": 4 149 | } 150 | -------------------------------------------------------------------------------- /nbs/docs/capabilities/.notest: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/docs/capabilities/.notest -------------------------------------------------------------------------------- /nbs/docs/capabilities/01_overview.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Forecasting Models" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "NeuralForecast currently offers the following models.\n", 15 | "\n", 16 | "| Model1 | AutoModel2 | Family3 | Univariate / Multivariate4 | Forecast Type5 | Exogenous6 |\n", 17 | "|:------|:----------|:-------------|:--------------------------|:--------------|:----------------|\n", 18 | "|`Autoformer` | `AutoAutoformer` | Transformer | Univariate | Direct | F |\n", 19 | "|`BiTCN` | `AutoBiTCN` | CNN | Univariate | Direct | F/H/S | \n", 20 | "|`DeepAR` | `AutoDeepAR` | RNN | Univariate | Direct | F/S | \n", 21 | "|`DeepNPTS` | `AutoDeepNPTS` | MLP | Univariate | Direct | F/H/S | \n", 22 | "|`DilatedRNN` | `AutoDilatedRNN` | RNN | Univariate | Direct | F/H/S | \n", 23 | "|`FEDformer` | `AutoFEDformer` | Transformer | Univariate | Direct | F | \n", 24 | "|`GRU` | `AutoGRU` | RNN | Univariate | Both8 | F/H/S | \n", 25 | "|`HINT` | `AutoHINT` | Any7 | Both7 | Both7 | F/H/S | \n", 26 | "|`Informer` | `AutoInformer` | Transformer | Univariate | Direct | F | \n", 27 | "|`iTransformer` | `AutoiTransformer` | Transformer | Multivariate | Direct | - | \n", 28 | "|`KAN` | `AutoKAN` | KAN | Univariate | Direct | F/H/S | \n", 29 | "|`LSTM` | `AutoLSTM` | RNN | Univariate | Both8 | F/H/S | \n", 30 | "|`MLP` | `AutoMLP` | MLP | Univariate | Direct | F/H/S | \n", 31 | "|`MLPMultivariate` | `AutoMLPMultivariate` | MLP | Multivariate | Direct | F/H/S | \n", 32 | "|`NBEATS` | `AutoNBEATS` | MLP | Univariate | Direct | - | \n", 33 | "|`NBEATSx` | `AutoNBEATSx` | MLP | Univariate | Direct | F/H/S | \n", 34 | "|`NHITS` | `AutoNHITS` | MLP | Univariate | Direct | F/H/S | \n", 35 | "|`NLinear` | `AutoNLinear` | MLP | Univariate | Direct | - | \n", 36 | "|`PatchTST` | `AutoPatchTST` | Transformer | Univariate | Direct | - | \n", 37 | "|`RMoK` | `AutoRMoK` | KAN | Multivariate | Direct | - |\n", 38 | "|`RNN` | `AutoRNN` | RNN | Univariate | Both8 | F/H/S | \n", 39 | "|`SOFTS` | `AutoSOFTS` | MLP | Multivariate | Direct | - | \n", 40 | "|`StemGNN` | `AutoStemGNN` | GNN | Multivariate | Direct | - | \n", 41 | "|`TCN` | `AutoTCN` | CNN | Univariate | Direct | F/H/S | \n", 42 | "|`TFT` | `AutoTFT` | Transformer | Univariate | Direct | F/H/S | \n", 43 | "|`TiDE` | `AutoTiDE` | MLP | Univariate | Direct | F/H/S | \n", 44 | "|`TimeMixer` | `AutoTimeMixer` | MLP | Multivariate | Direct | - | \n", 45 | "|`TimeLLM` | - | LLM | Univariate | Direct | - | \n", 46 | "|`TimesNet` | `AutoTimesNet` | CNN | Univariate | Direct | F |\n", 47 | "|`TimeXer` | `AutoTimeXer` | Transformer | Multivariate | Direct | F | \n", 48 | "|`TSMixer` | `AutoTSMixer` | MLP | Multivariate | Direct | - | \n", 49 | "|`TSMixerx` | `AutoTSMixerx` | MLP | Multivariate | Direct | F/H/S | \n", 50 | "|`VanillaTransformer` | `AutoVanillaTransformer` | Transformer | Univariate | Direct | F | \n", 51 | "\n", 52 | "1. **Model**: The model name.\n", 53 | "2. **AutoModel**: NeuralForecast offers most models also in an Auto* version, in which the hyperparameters of the underlying model are automatically optimized and the best-performing model for a validation set is selected. The optimization methods include grid search, random search, and Bayesian optimization.\n", 54 | "3. **Family**: The main neural network architecture underpinning the model. \n", 55 | "4. **Univariate / Multivariate**: A multivariate model explicitly models the interactions between multiple time series in a dataset and will provide predictions for multiple time series concurrently. In contrast, a univariate model trained on multiple time series implicitly models interactions between multiple time series and provides predictions for single time series concurrently. Multivariate models are typically computationally expensive and empirically do not necessarily offer better forecasting performance compared to using a univariate model.\n", 56 | "5. **Forecast Type**: Direct forecast models are models that produce all steps in the forecast horizon at once. In contrast, recursive forecast models predict one-step ahead, and subsequently use the prediction to compute the next step in the forecast horizon, and so forth. Direct forecast models typically suffer less from bias and variance propagation as compared to recursive forecast models, whereas recursive models can be computationally less expensive.\n", 57 | "6. **Exogenous**: Whether the model accepts exogenous variables. This can be exogenous variables that contain information about the past and future (F), about the past only (_historical_, H), or that contain static information (_static_, S).\n", 58 | "7. __HINT__ is a modular framework that can combine any type of neural architecture with task-specialized mixture probability and advanced hierarchical reconciliation strategies.\n", 59 | "8. Models that can produce forecasts recursively and direct. For example, the RNN model uses an RNN to encode the past sequence, and subsequently the user can choose between producing forecasts recursively using the RNN or direct using an MLP that uses the encoded sequence as input. The models feature an `recursive=False` feature that sets how they produce forecasts." 60 | ] 61 | }, 62 | { 63 | "cell_type": "markdown", 64 | "metadata": {}, 65 | "source": [] 66 | } 67 | ], 68 | "metadata": {}, 69 | "nbformat": 4, 70 | "nbformat_minor": 2 71 | } 72 | -------------------------------------------------------------------------------- /nbs/docs/capabilities/02_objectives.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Optimization Objectives" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "NeuralForecast is a highly modular framework capable of augmenting a wide variety of robust neural network architectures with different point or probability outputs as defined by their optimization objectives.\n", 15 | "\n", 16 | "## Point losses \n", 17 | "\n", 18 | "| Scale-Dependent | Percentage-Errors | Scale-Independent | Robust |\n", 19 | "|:-------------------------------------------------------------|:----------------------------------------------------------------------|:---------------------------------------------------------------|:-------------------------------------------------------|\n", 20 | "|[**MAE**](../../losses.pytorch.html#mean-absolute-error-mae) |[**MAPE**](../../losses.pytorch.html#mean-absolute-percentage-error-mape) |[**MASE**](../../losses.pytorch.html#mean-absolute-scaled-error-mase)|[**Huber**](../losses.pytorch.html#huber-loss) |\n", 21 | "|[**MSE**](../../losses.pytorch.html#mean-squared-error-mse) |[**sMAPE**](../../losses.pytorch.html#symmetric-mape-smape) | |[**Tukey**](../../losses.pytorch.html#tukey-loss) |\n", 22 | "|[**RMSE**](../../losses.pytorch.html#root-mean-squared-error-rmse) | | |[**HuberMQLoss**](../../losses.pytorch.html#huberized-mqloss)|\n", 23 | "\n", 24 | "## Probabilistic losses\n", 25 | "\n", 26 | "|Parametric Probabilities | Non-Parametric Probabilities |\n", 27 | "|:-------------------------------------------------------------|:-------------------------------------------------------------|\n", 28 | "|[**Normal**](../../losses.pytorch.html#distributionloss) |[**QuantileLoss**](../../losses.pytorch.html#quantile-loss) |\n", 29 | "|[**StudenT**](../../losses.pytorch.html#distributionloss) |[**MQLoss**](../../losses.pytorch.html#multi-quantile-loss-mqloss) |\n", 30 | "|[**Poisson**](../../losses.pytorch.html#distributionloss) |[**HuberQLoss**](../../losses.pytorch.html#huberized-quantile-loss)|\n", 31 | "|[**Negative Binomial**](../../losses.pytorch.html#distributionloss)|[**HuberMQLoss**](../../losses.pytorch.html#huberized-mqloss) |\n", 32 | "|[**Tweedie**](../../losses.pytorch.html#distributionloss) |[**IQLoss**](../../losses.pytorch.html#iqloss) |\n", 33 | "|[**PMM**](../../losses.pytorch.html#poisson-mixture-mesh-pmm) | [**HuberIQLoss**](../../losses.pytorch.html#huberized-iqloss)|\n", 34 | "|[**GMM**](../../losses.pytorch.html#gaussian-mixture-mesh-gmm) | [**ISQF**](../../losses.pytorch.html#isqf) |\n", 35 | "|[**NBMM**](../../losses.pytorch.html#negative-binomial-mixture-mesh-nbmm) | |" 36 | ] 37 | } 38 | ], 39 | "metadata": {}, 40 | "nbformat": 4, 41 | "nbformat_minor": 2 42 | } 43 | -------------------------------------------------------------------------------- /nbs/docs/getting-started/.notest: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/docs/getting-started/.notest -------------------------------------------------------------------------------- /nbs/docs/getting-started/04_installation.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "attachments": {}, 5 | "cell_type": "markdown", 6 | "id": "14f5686c-449b-4376-8c58-fc8141f4b0f8", 7 | "metadata": {}, 8 | "source": [ 9 | "# Installation\n", 10 | "\n", 11 | "> Install NeuralForecast with pip or conda" 12 | ] 13 | }, 14 | { 15 | "attachments": {}, 16 | "cell_type": "markdown", 17 | "id": "0f1d1483-6da7-4372-8390-84c9c280109e", 18 | "metadata": {}, 19 | "source": [ 20 | "You can install the *released version* of `NeuralForecast` from the [Python package index](https://pypi.org) with:\n", 21 | "\n", 22 | "```shell\n", 23 | "pip install neuralforecast\n", 24 | "```\n", 25 | "\n", 26 | "or \n", 27 | "\n", 28 | "```shell\n", 29 | "conda install -c conda-forge neuralforecast\n", 30 | "``` \n", 31 | "\n", 32 | ":::{.callout-tip}\n", 33 | "Neural Forecasting methods profit from using GPU computation. Be sure to have Cuda installed.\n", 34 | ":::\n", 35 | "\n", 36 | ":::{.callout-warning}\n", 37 | "We are constantly updating neuralforecast, so we suggest fixing the version to avoid issues. `pip install neuralforecast==\"1.0.0\"`\n", 38 | ":::\n", 39 | "\n", 40 | ":::{.callout-tip}\n", 41 | "We recommend installing your libraries inside a python virtual or [conda environment](https://docs.conda.io/projects/conda/en/latest/user-guide/install/macos.html).\n", 42 | ":::\n", 43 | "\n", 44 | "## Extras\n", 45 | "You can use the following extras to add optional functionality:\n", 46 | "\n", 47 | "* distributed training with spark: `pip install neuralforecast[spark]`\n", 48 | "* saving and loading from S3: `pip install neuralforecast[aws]`\n", 49 | "\n", 50 | "#### User our env (optional)\n", 51 | "\n", 52 | "If you don't have a Conda environment and need tools like Numba, Pandas, NumPy, Jupyter, Tune, and Nbdev you can use ours by following these steps:\n", 53 | "\n", 54 | "1. Clone the NeuralForecast repo: \n", 55 | "\n", 56 | "```bash \n", 57 | "$ git clone https://github.com/Nixtla/neuralforecast.git && cd neuralforecast\n", 58 | "```\n", 59 | "\n", 60 | "2. Create the environment using the `environment.yml` file: \n", 61 | "\n", 62 | "```bash \n", 63 | "$ conda env create -f environment.yml\n", 64 | "```\n", 65 | "\n", 66 | "3. Activate the environment:\n", 67 | "```bash\n", 68 | "$ conda activate neuralforecast\n", 69 | "```\n", 70 | "\n", 71 | "4. Install NeuralForecast Dev\n", 72 | "```bash\n", 73 | "$ pip install -e \".[dev]\"\n", 74 | "```" 75 | ] 76 | }, 77 | { 78 | "cell_type": "markdown", 79 | "id": "b9084b7a", 80 | "metadata": {}, 81 | "source": [] 82 | } 83 | ], 84 | "metadata": { 85 | "kernelspec": { 86 | "display_name": "python3", 87 | "language": "python", 88 | "name": "python3" 89 | } 90 | }, 91 | "nbformat": 4, 92 | "nbformat_minor": 5 93 | } 94 | -------------------------------------------------------------------------------- /nbs/docs/tutorials/.notest: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/docs/tutorials/.notest -------------------------------------------------------------------------------- /nbs/docs/use-cases/.notest: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/docs/use-cases/.notest -------------------------------------------------------------------------------- /nbs/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/favicon.png -------------------------------------------------------------------------------- /nbs/favicon_png.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/favicon_png.png -------------------------------------------------------------------------------- /nbs/imgs_indx/logo_mid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_indx/logo_mid.png -------------------------------------------------------------------------------- /nbs/imgs_indx/logo_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_indx/logo_new.png -------------------------------------------------------------------------------- /nbs/imgs_indx/nbeats_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_indx/nbeats_example.png -------------------------------------------------------------------------------- /nbs/imgs_indx/nf_map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_indx/nf_map.png -------------------------------------------------------------------------------- /nbs/imgs_indx/predict_insample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_indx/predict_insample.png -------------------------------------------------------------------------------- /nbs/imgs_losses/gmm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_losses/gmm.png -------------------------------------------------------------------------------- /nbs/imgs_losses/hmq_loss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_losses/hmq_loss.png -------------------------------------------------------------------------------- /nbs/imgs_losses/huber_loss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_losses/huber_loss.png -------------------------------------------------------------------------------- /nbs/imgs_losses/huber_qloss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_losses/huber_qloss.png -------------------------------------------------------------------------------- /nbs/imgs_losses/mae_loss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_losses/mae_loss.png -------------------------------------------------------------------------------- /nbs/imgs_losses/mape_loss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_losses/mape_loss.png -------------------------------------------------------------------------------- /nbs/imgs_losses/mase_loss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_losses/mase_loss.png -------------------------------------------------------------------------------- /nbs/imgs_losses/mq_loss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_losses/mq_loss.png -------------------------------------------------------------------------------- /nbs/imgs_losses/mse_loss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_losses/mse_loss.png -------------------------------------------------------------------------------- /nbs/imgs_losses/pmm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_losses/pmm.png -------------------------------------------------------------------------------- /nbs/imgs_losses/q_loss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_losses/q_loss.png -------------------------------------------------------------------------------- /nbs/imgs_losses/rmae_loss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_losses/rmae_loss.png -------------------------------------------------------------------------------- /nbs/imgs_losses/rmse_loss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_losses/rmse_loss.png -------------------------------------------------------------------------------- /nbs/imgs_losses/tukey_loss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_losses/tukey_loss.png -------------------------------------------------------------------------------- /nbs/imgs_losses/turbofan_engine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_losses/turbofan_engine.png -------------------------------------------------------------------------------- /nbs/imgs_models/StemGNN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/StemGNN.png -------------------------------------------------------------------------------- /nbs/imgs_models/autoformer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/autoformer.png -------------------------------------------------------------------------------- /nbs/imgs_models/bitcn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/bitcn.png -------------------------------------------------------------------------------- /nbs/imgs_models/data_splits.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/data_splits.png -------------------------------------------------------------------------------- /nbs/imgs_models/deepar.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/deepar.jpeg -------------------------------------------------------------------------------- /nbs/imgs_models/dilated_rnn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/dilated_rnn.png -------------------------------------------------------------------------------- /nbs/imgs_models/dlinear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/dlinear.png -------------------------------------------------------------------------------- /nbs/imgs_models/fedformer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/fedformer.png -------------------------------------------------------------------------------- /nbs/imgs_models/gru.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/gru.png -------------------------------------------------------------------------------- /nbs/imgs_models/hint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/hint.png -------------------------------------------------------------------------------- /nbs/imgs_models/hint_notation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/hint_notation.png -------------------------------------------------------------------------------- /nbs/imgs_models/iTransformer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/iTransformer.png -------------------------------------------------------------------------------- /nbs/imgs_models/informer_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/informer_architecture.png -------------------------------------------------------------------------------- /nbs/imgs_models/kan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/kan.png -------------------------------------------------------------------------------- /nbs/imgs_models/lstm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/lstm.png -------------------------------------------------------------------------------- /nbs/imgs_models/mlp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/mlp.png -------------------------------------------------------------------------------- /nbs/imgs_models/nbeats.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/nbeats.png -------------------------------------------------------------------------------- /nbs/imgs_models/nbeatsx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/nbeatsx.png -------------------------------------------------------------------------------- /nbs/imgs_models/nhits.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/nhits.png -------------------------------------------------------------------------------- /nbs/imgs_models/patchtst.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/patchtst.png -------------------------------------------------------------------------------- /nbs/imgs_models/rmok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/rmok.png -------------------------------------------------------------------------------- /nbs/imgs_models/rnn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/rnn.png -------------------------------------------------------------------------------- /nbs/imgs_models/softs_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/softs_architecture.png -------------------------------------------------------------------------------- /nbs/imgs_models/tcn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/tcn.png -------------------------------------------------------------------------------- /nbs/imgs_models/temporal_norm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/temporal_norm.png -------------------------------------------------------------------------------- /nbs/imgs_models/tft_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/tft_architecture.png -------------------------------------------------------------------------------- /nbs/imgs_models/tft_grn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/tft_grn.png -------------------------------------------------------------------------------- /nbs/imgs_models/tft_vsn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/tft_vsn.png -------------------------------------------------------------------------------- /nbs/imgs_models/tide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/tide.png -------------------------------------------------------------------------------- /nbs/imgs_models/timellm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/timellm.png -------------------------------------------------------------------------------- /nbs/imgs_models/timemixer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/timemixer.png -------------------------------------------------------------------------------- /nbs/imgs_models/timesnet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/timesnet.png -------------------------------------------------------------------------------- /nbs/imgs_models/timexer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/timexer.png -------------------------------------------------------------------------------- /nbs/imgs_models/tsmixer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/tsmixer.png -------------------------------------------------------------------------------- /nbs/imgs_models/tsmixerx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/tsmixerx.png -------------------------------------------------------------------------------- /nbs/imgs_models/vanilla_transformer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/nbs/imgs_models/vanilla_transformer.png -------------------------------------------------------------------------------- /nbs/mint.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://mintlify.com/schema.json", 3 | "name": "Nixtla", 4 | "logo": { 5 | "light": "/light.png", 6 | "dark": "/dark.png" 7 | }, 8 | "favicon": "/favicon.svg", 9 | "colors": { 10 | "primary": "#0E0E0E", 11 | "light": "#FAFAFA", 12 | "dark": "#0E0E0E", 13 | "anchors": { 14 | "from": "#2AD0CA", 15 | "to": "#0E00F8" 16 | } 17 | }, 18 | "topbarCtaButton": { 19 | "type": "github", 20 | "url": "https://github.com/Nixtla/neuralforecast" 21 | }, 22 | "topAnchor": { 23 | "name": "NeuralForecast", 24 | "icon": "brain-circuit" 25 | }, 26 | "navigation": [ 27 | { 28 | "group": "Getting Started", 29 | "pages": [ 30 | "docs/getting-started/introduction.html", 31 | "docs/getting-started/quickstart.html", 32 | "docs/getting-started/installation.html", 33 | "docs/getting-started/datarequirements.html" 34 | ] 35 | }, 36 | { 37 | "group": "Capabilities", 38 | "pages": [ 39 | "docs/capabilities/overview.html", 40 | "docs/capabilities/objectives.html", 41 | "docs/capabilities/exogenous_variables.html", 42 | "docs/capabilities/cross_validation.html", 43 | "docs/capabilities/hyperparameter_tuning.html", 44 | "docs/capabilities/predictinsample.html", 45 | "docs/capabilities/save_load_models.html", 46 | "docs/capabilities/time_series_scaling.html" 47 | ] 48 | }, 49 | { 50 | "group": "Tutorials", 51 | "pages": [ 52 | { 53 | "group":"Forecasting", 54 | "pages":[ 55 | "docs/tutorials/getting_started_complete.html", 56 | "docs/tutorials/cross_validation_tutorial.html", 57 | "docs/tutorials/longhorizon_nhits.html", 58 | "docs/tutorials/longhorizon_transformers.html", 59 | "docs/tutorials/forecasting_tft.html", 60 | "docs/tutorials/multivariate_tsmixer.html" 61 | ] 62 | }, 63 | { 64 | "group":"Probabilistic Forecasting", 65 | "pages":[ 66 | "docs/tutorials/uncertainty_quantification.html", 67 | "docs/tutorials/longhorizon_probabilistic.html", 68 | "docs/tutorials/conformal_prediction.html" 69 | ] 70 | }, 71 | { 72 | "group":"Special Topics", 73 | "pages":[ 74 | "docs/tutorials/hierarchical_forecasting.html", 75 | "docs/tutorials/distributed_neuralforecast.html", 76 | "docs/tutorials/intermittent_data.html", 77 | "docs/tutorials/using_mlflow.html", 78 | "docs/tutorials/robust_forecasting.html", 79 | "docs/tutorials/interpretable_decompositions.html", 80 | "docs/tutorials/comparing_methods.html", 81 | "docs/tutorials/temporal_classification.html", 82 | "docs/tutorials/transfer_learning.html", 83 | "docs/tutorials/adding_models.html", 84 | "docs/tutorials/large_datasets.html" 85 | ] 86 | } 87 | ] 88 | }, 89 | { 90 | "group": "Use cases", 91 | "pages": [ 92 | "docs/use-cases/electricity_peak_forecasting.html", 93 | "docs/use-cases/predictive_maintenance.html" 94 | ] 95 | }, 96 | { 97 | "group": "API Reference", 98 | "pages": [ 99 | "docs/tutorials/neuralforecasting_map.html", 100 | "core.html", 101 | { 102 | "group": "Models", 103 | "pages": [ 104 | "models.autoformer.html", 105 | "models.bitcn.html", 106 | "models.deepar.html", 107 | "models.deepnpts.html", 108 | "models.dilated_rnn.html", 109 | "models.dlinear.html", 110 | "models.fedformer.html", 111 | "models.gru.html", 112 | "models.hint.html", 113 | "models.informer.html", 114 | "models.itransformer.html", 115 | "models.kan.html", 116 | "models.lstm.html", 117 | "models.mlp.html", 118 | "models.mlpmultivariate.html", 119 | "models.nbeats.html", 120 | "models.nbeatsx.html", 121 | "models.nhits.html", 122 | "models.nlinear.html", 123 | "models.patchtst.html", 124 | "models.rmok.html", 125 | "models.rnn.html", 126 | "models.softs.html", 127 | "models.stemgnn.html", 128 | "models.tcn.html", 129 | "models.tft.html", 130 | "models.tide.html", 131 | "models.timellm.html", 132 | "models.timemixer.html", 133 | "models.timesnet.html", 134 | "models.timexer.html", 135 | "models.tsmixer.html", 136 | "models.tsmixerx.html", 137 | "models.vanillatransformer.html" 138 | ] 139 | }, 140 | "models.html", 141 | { 142 | "group": "Train/Evaluation", 143 | "pages": [ 144 | "losses.pytorch.html", 145 | "losses.numpy.html" 146 | ] 147 | }, 148 | { 149 | "group": "Common Components", 150 | "pages": [ 151 | "common.base_auto.html", 152 | "common.base_recurrent.html", 153 | "common.base_windows.html", 154 | "common.scalers.html", 155 | "common.modules.html" 156 | ] 157 | }, 158 | { 159 | "group": "Utils", 160 | "pages": [ 161 | "tsdataset.html", 162 | "utils.html" 163 | ] 164 | } 165 | ] 166 | } 167 | ] 168 | } 169 | -------------------------------------------------------------------------------- /nbs/nbdev.yml: -------------------------------------------------------------------------------- 1 | project: 2 | output-dir: _docs 3 | 4 | website: 5 | title: "neuralforecast" 6 | site-url: "https://nixtlaverse.nixtla.io/neuralforecast/" 7 | description: "Time series forecasting suite using deep learning models" 8 | repo-branch: main 9 | repo-url: "https://github.com/Nixtla/neuralforecast/" 10 | -------------------------------------------------------------------------------- /nbs/sidebar.yml: -------------------------------------------------------------------------------- 1 | website: 2 | reader-mode: false 3 | sidebar: 4 | collapse-level: 1 5 | contents: 6 | - text: "--" 7 | - section: "Getting Started" 8 | contents: docs/getting-started/* 9 | - section: "Capabilities" 10 | contents: docs/capabilities/* 11 | # - section: "Deployment" 12 | # contents: docs/deployment/* 13 | - section: "Tutorials" 14 | contents: docs/tutorials/* 15 | - section: "Use cases" 16 | contents: docs/use-cases/* 17 | - section: "API Reference" 18 | contents: 19 | - docs/api-reference/01_neuralforecast_map.ipynb 20 | - core.ipynb 21 | - section: Models 22 | contents: 23 | - models.autoformer.ipynb 24 | - models.bitcn.ipynb 25 | - models.deepar.ipynb 26 | - models.deepnpts.ipynb 27 | - models.dilated_rnn.ipynb 28 | - models.dlinear.ipynb 29 | - models.fedformer.ipynb 30 | - models.gru.ipynb 31 | - models.hint.ipynb 32 | - models.informer.ipynb 33 | - models.itransformer.ipynb 34 | - models.kan.ipynb 35 | - models.lstm.ipynb 36 | - models.mlp.ipynb 37 | - models.mlpmultivariate.ipynb 38 | - models.nbeats.ipynb 39 | - models.nbeatsx.ipynb 40 | - models.nhits.ipynb 41 | - models.nlinear.ipynb 42 | - models.patchtst.ipynb 43 | - models.rmok.ipynb 44 | - models.rnn.ipynb 45 | - models.softs.ipynb 46 | - models.stemgnn.ipynb 47 | - models.tcn.ipynb 48 | - models.tft.ipynb 49 | - models.tide.ipynb 50 | - models.timemixer.ipynb 51 | - models.timellm.ipynb 52 | - models.timesnet.ipynb 53 | - models.timexer.ipynb 54 | - models.tsmixer.ipynb 55 | - models.tsmixerx.ipynb 56 | - models.vanillatransformer.ipynb 57 | - models.ipynb 58 | - section: Train/Evaluation 59 | contents: 60 | - losses.pytorch.ipynb 61 | - losses.numpy.ipynb 62 | - section: Common Components 63 | contents: 64 | - common.base_auto.ipynb 65 | - common.base_recurrent.ipynb 66 | - common.base_windows.ipynb 67 | - common.scalers.ipynb 68 | - common.modules.ipynb 69 | - section: Utils 70 | contents: 71 | - tsdataset.ipynb 72 | - utils.ipynb 73 | - section: Community 74 | contents: 75 | - Contributing -------------------------------------------------------------------------------- /nbs/styles.css: -------------------------------------------------------------------------------- 1 | .cell-output pre { 2 | margin-left: 0.8rem; 3 | margin-top: 0; 4 | background: none; 5 | border-left: 2px solid lightsalmon; 6 | border-top-left-radius: 0; 7 | border-top-right-radius: 0; 8 | } 9 | 10 | .cell-output .sourceCode { 11 | background: none; 12 | margin-top: 0; 13 | } 14 | 15 | .cell > .sourceCode { 16 | margin-bottom: 0; 17 | } 18 | -------------------------------------------------------------------------------- /neuralforecast/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = "3.0.1" 2 | __all__ = ['NeuralForecast'] 3 | from .core import NeuralForecast 4 | from .common._base_model import DistributedConfig # noqa: F401 5 | -------------------------------------------------------------------------------- /neuralforecast/common/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/neuralforecast/common/__init__.py -------------------------------------------------------------------------------- /neuralforecast/common/_model_checks.py: -------------------------------------------------------------------------------- 1 | # AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/common.model_checks.ipynb. 2 | 3 | # %% auto 0 4 | __all__ = ['seed', 'test_size', 'FREQ', 'N_SERIES_1', 'df', 'max_ds', 'Y_TRAIN_DF_1', 'Y_TEST_DF_1', 'N_SERIES_2', 'Y_TRAIN_DF_2', 5 | 'Y_TEST_DF_2', 'N_SERIES_3', 'STATIC_3', 'Y_TRAIN_DF_3', 'Y_TEST_DF_3', 'N_SERIES_4', 'STATIC_4', 6 | 'Y_TRAIN_DF_4', 'Y_TEST_DF_4', 'check_loss_functions', 'check_airpassengers', 'check_model'] 7 | 8 | # %% ../../nbs/common.model_checks.ipynb 4 9 | import pandas as pd 10 | import neuralforecast.losses.pytorch as losses 11 | 12 | from .. import NeuralForecast 13 | from neuralforecast.utils import ( 14 | AirPassengersPanel, 15 | AirPassengersStatic, 16 | generate_series, 17 | ) 18 | 19 | # %% ../../nbs/common.model_checks.ipynb 5 20 | seed = 0 21 | test_size = 14 22 | FREQ = "D" 23 | 24 | # 1 series, no exogenous 25 | N_SERIES_1 = 1 26 | df = generate_series(n_series=N_SERIES_1, seed=seed, freq=FREQ, equal_ends=True) 27 | max_ds = df.ds.max() - pd.Timedelta(test_size, FREQ) 28 | Y_TRAIN_DF_1 = df[df.ds < max_ds] 29 | Y_TEST_DF_1 = df[df.ds >= max_ds] 30 | 31 | # 5 series, no exogenous 32 | N_SERIES_2 = 5 33 | df = generate_series(n_series=N_SERIES_2, seed=seed, freq=FREQ, equal_ends=True) 34 | max_ds = df.ds.max() - pd.Timedelta(test_size, FREQ) 35 | Y_TRAIN_DF_2 = df[df.ds < max_ds] 36 | Y_TEST_DF_2 = df[df.ds >= max_ds] 37 | 38 | # 1 series, with static and temporal exogenous 39 | N_SERIES_3 = 1 40 | df, STATIC_3 = generate_series( 41 | n_series=N_SERIES_3, 42 | n_static_features=2, 43 | n_temporal_features=2, 44 | seed=seed, 45 | freq=FREQ, 46 | equal_ends=True, 47 | ) 48 | max_ds = df.ds.max() - pd.Timedelta(test_size, FREQ) 49 | Y_TRAIN_DF_3 = df[df.ds < max_ds] 50 | Y_TEST_DF_3 = df[df.ds >= max_ds] 51 | 52 | # 5 series, with static and temporal exogenous 53 | N_SERIES_4 = 5 54 | df, STATIC_4 = generate_series( 55 | n_series=N_SERIES_4, 56 | n_static_features=2, 57 | n_temporal_features=2, 58 | seed=seed, 59 | freq=FREQ, 60 | equal_ends=True, 61 | ) 62 | max_ds = df.ds.max() - pd.Timedelta(test_size, FREQ) 63 | Y_TRAIN_DF_4 = df[df.ds < max_ds] 64 | Y_TEST_DF_4 = df[df.ds >= max_ds] 65 | 66 | 67 | # Generic test for a given config for a model 68 | def _run_model_tests(model_class, config): 69 | if model_class.RECURRENT: 70 | config["inference_input_size"] = config["input_size"] 71 | 72 | # DF_1 73 | if model_class.MULTIVARIATE: 74 | config["n_series"] = N_SERIES_1 75 | if isinstance(config["loss"], losses.relMSE): 76 | config["loss"].y_train = Y_TRAIN_DF_1["y"].values 77 | if isinstance(config["valid_loss"], losses.relMSE): 78 | config["valid_loss"].y_train = Y_TRAIN_DF_1["y"].values 79 | 80 | model = model_class(**config) 81 | fcst = NeuralForecast(models=[model], freq=FREQ) 82 | fcst.fit(df=Y_TRAIN_DF_1, val_size=24) 83 | _ = fcst.predict(futr_df=Y_TEST_DF_1) 84 | # DF_2 85 | if model_class.MULTIVARIATE: 86 | config["n_series"] = N_SERIES_2 87 | if isinstance(config["loss"], losses.relMSE): 88 | config["loss"].y_train = Y_TRAIN_DF_2["y"].values 89 | if isinstance(config["valid_loss"], losses.relMSE): 90 | config["valid_loss"].y_train = Y_TRAIN_DF_2["y"].values 91 | model = model_class(**config) 92 | fcst = NeuralForecast(models=[model], freq=FREQ) 93 | fcst.fit(df=Y_TRAIN_DF_2, val_size=24) 94 | _ = fcst.predict(futr_df=Y_TEST_DF_2) 95 | 96 | if model.EXOGENOUS_STAT and model.EXOGENOUS_FUTR: 97 | # DF_3 98 | if model_class.MULTIVARIATE: 99 | config["n_series"] = N_SERIES_3 100 | if isinstance(config["loss"], losses.relMSE): 101 | config["loss"].y_train = Y_TRAIN_DF_3["y"].values 102 | if isinstance(config["valid_loss"], losses.relMSE): 103 | config["valid_loss"].y_train = Y_TRAIN_DF_3["y"].values 104 | model = model_class(**config) 105 | fcst = NeuralForecast(models=[model], freq=FREQ) 106 | fcst.fit(df=Y_TRAIN_DF_3, static_df=STATIC_3, val_size=24) 107 | _ = fcst.predict(futr_df=Y_TEST_DF_3) 108 | 109 | # DF_4 110 | if model_class.MULTIVARIATE: 111 | config["n_series"] = N_SERIES_4 112 | if isinstance(config["loss"], losses.relMSE): 113 | config["loss"].y_train = Y_TRAIN_DF_4["y"].values 114 | if isinstance(config["valid_loss"], losses.relMSE): 115 | config["valid_loss"].y_train = Y_TRAIN_DF_4["y"].values 116 | model = model_class(**config) 117 | fcst = NeuralForecast(models=[model], freq=FREQ) 118 | fcst.fit(df=Y_TRAIN_DF_4, static_df=STATIC_4, val_size=24) 119 | _ = fcst.predict(futr_df=Y_TEST_DF_4) 120 | 121 | 122 | # Tests a model against every loss function 123 | def check_loss_functions(model_class): 124 | loss_list = [ 125 | losses.MAE(), 126 | losses.MSE(), 127 | losses.RMSE(), 128 | losses.MAPE(), 129 | losses.SMAPE(), 130 | losses.MASE(seasonality=7), 131 | losses.QuantileLoss(q=0.5), 132 | losses.MQLoss(), 133 | losses.IQLoss(), 134 | losses.HuberIQLoss(), 135 | losses.DistributionLoss("Normal"), 136 | losses.DistributionLoss("StudentT"), 137 | losses.DistributionLoss("Poisson"), 138 | losses.DistributionLoss("NegativeBinomial"), 139 | losses.DistributionLoss("Tweedie", rho=1.5), 140 | losses.DistributionLoss("ISQF"), 141 | losses.PMM(), 142 | losses.PMM(weighted=True), 143 | losses.GMM(), 144 | losses.GMM(weighted=True), 145 | losses.NBMM(), 146 | losses.NBMM(weighted=True), 147 | losses.HuberLoss(), 148 | losses.TukeyLoss(), 149 | losses.HuberQLoss(q=0.5), 150 | losses.HuberMQLoss(), 151 | ] 152 | for loss in loss_list: 153 | test_name = f"{model_class.__name__}: checking {loss._get_name()}" 154 | print(f"{test_name}") 155 | config = { 156 | "max_steps": 2, 157 | "h": 7, 158 | "input_size": 28, 159 | "loss": loss, 160 | "valid_loss": None, 161 | "enable_progress_bar": False, 162 | "enable_model_summary": False, 163 | "val_check_steps": 2, 164 | } 165 | try: 166 | _run_model_tests(model_class, config) 167 | except RuntimeError: 168 | raise Exception(f"{test_name} failed.") 169 | except Exception: 170 | print(f"{test_name} skipped on raised Exception.") 171 | pass 172 | 173 | 174 | # Tests a model against the AirPassengers dataset 175 | def check_airpassengers(model_class): 176 | print(f"{model_class.__name__}: checking forecast AirPassengers dataset") 177 | Y_train_df = AirPassengersPanel[ 178 | AirPassengersPanel.ds < AirPassengersPanel["ds"].values[-12] 179 | ] # 132 train 180 | Y_test_df = AirPassengersPanel[ 181 | AirPassengersPanel.ds >= AirPassengersPanel["ds"].values[-12] 182 | ].reset_index( 183 | drop=True 184 | ) # 12 test 185 | 186 | config = { 187 | "max_steps": 2, 188 | "h": 12, 189 | "input_size": 24, 190 | "enable_progress_bar": False, 191 | "enable_model_summary": False, 192 | "val_check_steps": 2, 193 | } 194 | 195 | if model_class.MULTIVARIATE: 196 | config["n_series"] = Y_train_df["unique_id"].nunique() 197 | # Normal forecast 198 | fcst = NeuralForecast(models=[model_class(**config)], freq="M") 199 | fcst.fit(df=Y_train_df, static_df=AirPassengersStatic) 200 | _ = fcst.predict(futr_df=Y_test_df) 201 | 202 | # Cross-validation 203 | fcst = NeuralForecast(models=[model_class(**config)], freq="M") 204 | _ = fcst.cross_validation( 205 | df=AirPassengersPanel, static_df=AirPassengersStatic, n_windows=2, step_size=12 206 | ) 207 | 208 | 209 | # Add unit test functions to this function 210 | def check_model(model_class, checks=["losses", "airpassengers"]): 211 | """ 212 | Check model with various tests. Options for checks are:
213 | "losses": test the model against all loss functions
214 | "airpassengers": test the model against the airpassengers dataset for forecasting and cross-validation
215 | 216 | """ 217 | if "losses" in checks: 218 | check_loss_functions(model_class) 219 | if "airpassengers" in checks: 220 | try: 221 | check_airpassengers(model_class) 222 | except RuntimeError: 223 | raise Exception( 224 | f"{model_class.__name__}: AirPassengers forecast test failed." 225 | ) 226 | -------------------------------------------------------------------------------- /neuralforecast/compat.py: -------------------------------------------------------------------------------- 1 | # AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/compat.ipynb. 2 | 3 | # %% auto 0 4 | __all__ = [] 5 | 6 | # %% ../nbs/compat.ipynb 1 7 | try: 8 | from pyspark.sql import DataFrame as SparkDataFrame 9 | except ImportError: 10 | 11 | class SparkDataFrame: ... 12 | -------------------------------------------------------------------------------- /neuralforecast/losses/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nixtla/neuralforecast/0f16b3a8c93ef67be2fed28c5257e5098cfb4815/neuralforecast/losses/__init__.py -------------------------------------------------------------------------------- /neuralforecast/models/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['RNN', 'GRU', 'LSTM', 'TCN', 'DeepAR', 'DilatedRNN', 2 | 'MLP', 'NHITS', 'NBEATS', 'NBEATSx', 'DLinear', 'NLinear', 3 | 'TFT', 'VanillaTransformer', 'Informer', 'Autoformer', 'PatchTST', 'FEDformer', 4 | 'StemGNN', 'HINT', 'TimesNet', 'TimeLLM', 'TSMixer', 'TSMixerx', 'MLPMultivariate', 5 | 'iTransformer', 'BiTCN', 'TiDE', 'DeepNPTS', 'SOFTS', 'TimeMixer', 'KAN', 'RMoK', 6 | 'TimeXer', 7 | ] 8 | 9 | from .rnn import RNN 10 | from .gru import GRU 11 | from .lstm import LSTM 12 | from .tcn import TCN 13 | from .deepar import DeepAR 14 | from .dilated_rnn import DilatedRNN 15 | from .mlp import MLP 16 | from .nhits import NHITS 17 | from .nbeats import NBEATS 18 | from .nbeatsx import NBEATSx 19 | from .dlinear import DLinear 20 | from .nlinear import NLinear 21 | from .tft import TFT 22 | from .stemgnn import StemGNN 23 | from .vanillatransformer import VanillaTransformer 24 | from .informer import Informer 25 | from .autoformer import Autoformer 26 | from .fedformer import FEDformer 27 | from .patchtst import PatchTST 28 | from .hint import HINT 29 | from .timesnet import TimesNet 30 | from .timellm import TimeLLM 31 | from .tsmixer import TSMixer 32 | from .tsmixerx import TSMixerx 33 | from .mlpmultivariate import MLPMultivariate 34 | from .itransformer import iTransformer 35 | from .bitcn import BiTCN 36 | from .tide import TiDE 37 | from .deepnpts import DeepNPTS 38 | from .softs import SOFTS 39 | from .timemixer import TimeMixer 40 | from .kan import KAN 41 | from .rmok import RMoK 42 | from .timexer import TimeXer 43 | -------------------------------------------------------------------------------- /neuralforecast/models/deepnpts.py: -------------------------------------------------------------------------------- 1 | # AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/models.deepnpts.ipynb. 2 | 3 | # %% auto 0 4 | __all__ = ['DeepNPTS'] 5 | 6 | # %% ../../nbs/models.deepnpts.ipynb 3 7 | import torch 8 | import torch.nn as nn 9 | import torch.nn.functional as F 10 | import neuralforecast.losses.pytorch as losses 11 | from typing import Optional 12 | 13 | 14 | from ..common._base_model import BaseModel 15 | from ..losses.pytorch import MAE 16 | 17 | # %% ../../nbs/models.deepnpts.ipynb 6 18 | class DeepNPTS(BaseModel): 19 | """DeepNPTS 20 | 21 | Deep Non-Parametric Time Series Forecaster (`DeepNPTS`) is a baseline model for time-series forecasting. This model generates predictions by (weighted) sampling from the empirical distribution according to a learnable strategy. The strategy is learned by exploiting the information across multiple related time series. 22 | 23 | **Parameters:**
24 | `h`: int, Forecast horizon.
25 | `input_size`: int, autorregresive inputs size, y=[1,2,3,4] input_size=2 -> y_[t-2:t]=[1,2].
26 | `hidden_size`: int=32, hidden size of dense layers.
27 | `batch_norm`: bool=True, if True, applies Batch Normalization after each dense layer in the network.
28 | `dropout`: float=0.1, dropout.
29 | `n_layers`: int=2, number of dense layers.
30 | `stat_exog_list`: str list, static exogenous columns.
31 | `hist_exog_list`: str list, historic exogenous columns.
32 | `futr_exog_list`: str list, future exogenous columns.
33 | `exclude_insample_y`: bool=False, the model skips the autoregressive features y[t-input_size:t] if True.
34 | `loss`: PyTorch module, instantiated train loss class from [losses collection](https://nixtla.github.io/neuralforecast/losses.pytorch.html).
35 | `valid_loss`: PyTorch module=`loss`, instantiated valid loss class from [losses collection](https://nixtla.github.io/neuralforecast/losses.pytorch.html).
36 | `max_steps`: int=1000, maximum number of training steps.
37 | `learning_rate`: float=1e-3, Learning rate between (0, 1).
38 | `num_lr_decays`: int=-1, Number of learning rate decays, evenly distributed across max_steps.
39 | `early_stop_patience_steps`: int=-1, Number of validation iterations before early stopping.
40 | `val_check_steps`: int=100, Number of training steps between every validation loss check.
41 | `batch_size`: int=32, number of different series in each batch.
42 | `valid_batch_size`: int=None, number of different series in each validation and test batch, if None uses batch_size.
43 | `windows_batch_size`: int=1024, number of windows to sample in each training batch, default uses all.
44 | `inference_windows_batch_size`: int=-1, number of windows to sample in each inference batch, -1 uses all.
45 | `start_padding_enabled`: bool=False, if True, the model will pad the time series with zeros at the beginning, by input size.
46 | `step_size`: int=1, step size between each window of temporal data.
47 | `scaler_type`: str='identity', type of scaler for temporal inputs normalization see [temporal scalers](https://nixtla.github.io/neuralforecast/common.scalers.html).
48 | `random_seed`: int, random_seed for pytorch initializer and numpy generators.
49 | `drop_last_loader`: bool=False, if True `TimeSeriesDataLoader` drops last non-full batch.
50 | `alias`: str, optional, Custom name of the model.
51 | `optimizer`: Subclass of 'torch.optim.Optimizer', optional, user specified optimizer instead of the default choice (Adam).
52 | `optimizer_kwargs`: dict, optional, list of parameters used by the user specified `optimizer`.
53 | `lr_scheduler`: Subclass of 'torch.optim.lr_scheduler.LRScheduler', optional, user specified lr_scheduler instead of the default choice (StepLR).
54 | `lr_scheduler_kwargs`: dict, optional, list of parameters used by the user specified `lr_scheduler`.
55 | `dataloader_kwargs`: dict, optional, list of parameters passed into the PyTorch Lightning dataloader by the `TimeSeriesDataLoader`.
56 | `**trainer_kwargs`: int, keyword trainer arguments inherited from [PyTorch Lighning's trainer](https://pytorch-lightning.readthedocs.io/en/stable/api/pytorch_lightning.trainer.trainer.Trainer.html?highlight=trainer).
57 | 58 | **References**
59 | - [Rangapuram, Syama Sundar, Jan Gasthaus, Lorenzo Stella, Valentin Flunkert, David Salinas, Yuyang Wang, and Tim Januschowski (2023). "Deep Non-Parametric Time Series Forecaster". arXiv.](https://arxiv.org/abs/2312.14657)
60 | 61 | """ 62 | 63 | # Class attributes 64 | EXOGENOUS_FUTR = True 65 | EXOGENOUS_HIST = True 66 | EXOGENOUS_STAT = True 67 | MULTIVARIATE = False # If the model produces multivariate forecasts (True) or univariate (False) 68 | RECURRENT = ( 69 | False # If the model produces forecasts recursively (True) or direct (False) 70 | ) 71 | 72 | def __init__( 73 | self, 74 | h, 75 | input_size: int, 76 | hidden_size: int = 32, 77 | batch_norm: bool = True, 78 | dropout: float = 0.1, 79 | n_layers: int = 2, 80 | stat_exog_list=None, 81 | hist_exog_list=None, 82 | futr_exog_list=None, 83 | exclude_insample_y=False, 84 | loss=MAE(), 85 | valid_loss=MAE(), 86 | max_steps: int = 1000, 87 | learning_rate: float = 1e-3, 88 | num_lr_decays: int = 3, 89 | early_stop_patience_steps: int = -1, 90 | val_check_steps: int = 100, 91 | batch_size: int = 32, 92 | valid_batch_size: Optional[int] = None, 93 | windows_batch_size: int = 1024, 94 | inference_windows_batch_size: int = 1024, 95 | start_padding_enabled=False, 96 | step_size: int = 1, 97 | scaler_type: str = "standard", 98 | random_seed: int = 1, 99 | drop_last_loader=False, 100 | alias: Optional[str] = None, 101 | optimizer=None, 102 | optimizer_kwargs=None, 103 | lr_scheduler=None, 104 | lr_scheduler_kwargs=None, 105 | dataloader_kwargs=None, 106 | **trainer_kwargs 107 | ): 108 | 109 | if exclude_insample_y: 110 | raise Exception("DeepNPTS has no possibility for excluding y.") 111 | 112 | if loss.outputsize_multiplier > 1: 113 | raise Exception( 114 | "DeepNPTS only supports point loss functions (MAE, MSE, etc) as loss function." 115 | ) 116 | 117 | if valid_loss is not None and not isinstance(valid_loss, losses.BasePointLoss): 118 | raise Exception( 119 | "DeepNPTS only supports point loss functions (MAE, MSE, etc) as valid loss function." 120 | ) 121 | 122 | # Inherit BaseWindows class 123 | super(DeepNPTS, self).__init__( 124 | h=h, 125 | input_size=input_size, 126 | stat_exog_list=stat_exog_list, 127 | hist_exog_list=hist_exog_list, 128 | futr_exog_list=futr_exog_list, 129 | exclude_insample_y=exclude_insample_y, 130 | loss=loss, 131 | valid_loss=valid_loss, 132 | max_steps=max_steps, 133 | learning_rate=learning_rate, 134 | num_lr_decays=num_lr_decays, 135 | early_stop_patience_steps=early_stop_patience_steps, 136 | val_check_steps=val_check_steps, 137 | batch_size=batch_size, 138 | valid_batch_size=valid_batch_size, 139 | windows_batch_size=windows_batch_size, 140 | inference_windows_batch_size=inference_windows_batch_size, 141 | start_padding_enabled=start_padding_enabled, 142 | step_size=step_size, 143 | scaler_type=scaler_type, 144 | random_seed=random_seed, 145 | drop_last_loader=drop_last_loader, 146 | alias=alias, 147 | optimizer=optimizer, 148 | optimizer_kwargs=optimizer_kwargs, 149 | lr_scheduler=lr_scheduler, 150 | lr_scheduler_kwargs=lr_scheduler_kwargs, 151 | dataloader_kwargs=dataloader_kwargs, 152 | **trainer_kwargs 153 | ) 154 | 155 | self.h = h 156 | self.hidden_size = hidden_size 157 | self.dropout = dropout 158 | 159 | input_dim = ( 160 | input_size * (1 + self.futr_exog_size + self.hist_exog_size) 161 | + self.stat_exog_size 162 | + self.h * self.futr_exog_size 163 | ) 164 | 165 | # Create DeepNPTSNetwork 166 | modules = [] 167 | for i in range(n_layers): 168 | modules.append(nn.Linear(input_dim if i == 0 else hidden_size, hidden_size)) 169 | modules.append(nn.ReLU()) 170 | if batch_norm: 171 | modules.append(nn.BatchNorm1d(hidden_size)) 172 | if dropout > 0.0: 173 | modules.append(nn.Dropout(dropout)) 174 | 175 | modules.append(nn.Linear(hidden_size, input_size * self.h)) 176 | self.deepnptsnetwork = nn.Sequential(*modules) 177 | 178 | def forward(self, windows_batch): 179 | # Parse windows_batch 180 | x = windows_batch["insample_y"] # [B, L, 1] 181 | hist_exog = windows_batch["hist_exog"] # [B, L, X] 182 | futr_exog = windows_batch["futr_exog"] # [B, L + h, F] 183 | stat_exog = windows_batch["stat_exog"] # [B, S] 184 | 185 | batch_size, seq_len = x.shape[:2] # B = batch_size, L = seq_len 186 | insample_y = windows_batch["insample_y"] 187 | 188 | # Concatenate x_t with future exogenous of input 189 | if self.futr_exog_size > 0: 190 | x = torch.cat( 191 | (x, futr_exog[:, :seq_len]), dim=2 192 | ) # [B, L, 1] + [B, L, F] -> [B, L, 1 + F] 193 | 194 | # Concatenate x_t with historic exogenous 195 | if self.hist_exog_size > 0: 196 | x = torch.cat( 197 | (x, hist_exog), dim=2 198 | ) # [B, L, 1 + F] + [B, L, X] -> [B, L, 1 + F + X] 199 | 200 | x = x.reshape(batch_size, -1) # [B, L, 1 + F + X] -> [B, L * (1 + F + X)] 201 | 202 | # Concatenate x with static exogenous 203 | if self.stat_exog_size > 0: 204 | x = torch.cat( 205 | (x, stat_exog), dim=1 206 | ) # [B, L * (1 + F + X)] + [B, S] -> [B, L * (1 + F + X) + S] 207 | 208 | # Concatenate x_t with future exogenous of horizon 209 | if self.futr_exog_size > 0: 210 | futr_exog = futr_exog[:, seq_len:] # [B, L + h, F] -> [B, h, F] 211 | futr_exog = futr_exog.reshape( 212 | batch_size, -1 213 | ) # [B, L + h, F] -> [B, h * F] 214 | x = torch.cat( 215 | (x, futr_exog), dim=1 216 | ) # [B, L * (1 + F + X) + S] + [B, h * F] -> [B, L * (1 + F + X) + S + h * F] 217 | 218 | # Run through DeepNPTSNetwork 219 | weights = self.deepnptsnetwork( 220 | x 221 | ) # [B, L * (1 + F + X) + S + h * F] -> [B, L * h] 222 | 223 | # Apply softmax for weighted input predictions 224 | weights = weights.reshape(batch_size, seq_len, -1) # [B, L * h] -> [B, L, h] 225 | x = ( 226 | F.softmax(weights, dim=1) * insample_y 227 | ) # [B, L, h] * [B, L, 1] = [B, L, h] 228 | forecast = torch.sum(x, dim=1).unsqueeze(-1) # [B, L, h] -> [B, h, 1] 229 | 230 | return forecast 231 | -------------------------------------------------------------------------------- /neuralforecast/models/dlinear.py: -------------------------------------------------------------------------------- 1 | # AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/models.dlinear.ipynb. 2 | 3 | # %% auto 0 4 | __all__ = ['MovingAvg', 'SeriesDecomp', 'DLinear'] 5 | 6 | # %% ../../nbs/models.dlinear.ipynb 5 7 | from typing import Optional 8 | 9 | import torch 10 | import torch.nn as nn 11 | 12 | from ..common._base_model import BaseModel 13 | 14 | from ..losses.pytorch import MAE 15 | 16 | # %% ../../nbs/models.dlinear.ipynb 8 17 | class MovingAvg(nn.Module): 18 | """ 19 | Moving average block to highlight the trend of time series 20 | """ 21 | 22 | def __init__(self, kernel_size, stride): 23 | super(MovingAvg, self).__init__() 24 | self.kernel_size = kernel_size 25 | self.avg = nn.AvgPool1d(kernel_size=kernel_size, stride=stride, padding=0) 26 | 27 | def forward(self, x): 28 | # padding on the both ends of time series 29 | front = x[:, 0:1].repeat(1, (self.kernel_size - 1) // 2) 30 | end = x[:, -1:].repeat(1, (self.kernel_size - 1) // 2) 31 | x = torch.cat([front, x, end], dim=1) 32 | x = self.avg(x) 33 | return x 34 | 35 | 36 | class SeriesDecomp(nn.Module): 37 | """ 38 | Series decomposition block 39 | """ 40 | 41 | def __init__(self, kernel_size): 42 | super(SeriesDecomp, self).__init__() 43 | self.MovingAvg = MovingAvg(kernel_size, stride=1) 44 | 45 | def forward(self, x): 46 | moving_mean = self.MovingAvg(x) 47 | res = x - moving_mean 48 | return res, moving_mean 49 | 50 | # %% ../../nbs/models.dlinear.ipynb 10 51 | class DLinear(BaseModel): 52 | """DLinear 53 | 54 | *Parameters:*
55 | `h`: int, forecast horizon.
56 | `input_size`: int, maximum sequence length for truncated train backpropagation.
57 | `stat_exog_list`: str list, static exogenous columns.
58 | `hist_exog_list`: str list, historic exogenous columns.
59 | `futr_exog_list`: str list, future exogenous columns.
60 | `exclude_insample_y`: bool=False, the model skips the autoregressive features y[t-input_size:t] if True.
61 | `moving_avg_window`: int=25, window size for trend-seasonality decomposition. Should be uneven.
62 | `loss`: PyTorch module, instantiated train loss class from [losses collection](https://nixtla.github.io/neuralforecast/losses.pytorch.html).
63 | `valid_loss`: PyTorch module=`loss`, instantiated valid loss class from [losses collection](https://nixtla.github.io/neuralforecast/losses.pytorch.html).
64 | `max_steps`: int=1000, maximum number of training steps.
65 | `learning_rate`: float=1e-3, Learning rate between (0, 1).
66 | `num_lr_decays`: int=-1, Number of learning rate decays, evenly distributed across max_steps.
67 | `early_stop_patience_steps`: int=-1, Number of validation iterations before early stopping.
68 | `val_check_steps`: int=100, Number of training steps between every validation loss check.
69 | `batch_size`: int=32, number of different series in each batch.
70 | `valid_batch_size`: int=None, number of different series in each validation and test batch, if None uses batch_size.
71 | `windows_batch_size`: int=1024, number of windows to sample in each training batch, default uses all.
72 | `inference_windows_batch_size`: int=1024, number of windows to sample in each inference batch.
73 | `start_padding_enabled`: bool=False, if True, the model will pad the time series with zeros at the beginning, by input size.
74 | `step_size`: int=1, step size between each window of temporal data.
75 | `scaler_type`: str='robust', type of scaler for temporal inputs normalization see [temporal scalers](https://nixtla.github.io/neuralforecast/common.scalers.html).
76 | `random_seed`: int=1, random_seed for pytorch initializer and numpy generators.
77 | `drop_last_loader`: bool=False, if True `TimeSeriesDataLoader` drops last non-full batch.
78 | `alias`: str, optional, Custom name of the model.
79 | `optimizer`: Subclass of 'torch.optim.Optimizer', optional, user specified optimizer instead of the default choice (Adam).
80 | `optimizer_kwargs`: dict, optional, list of parameters used by the user specified `optimizer`.
81 | `lr_scheduler`: Subclass of 'torch.optim.lr_scheduler.LRScheduler', optional, user specified lr_scheduler instead of the default choice (StepLR).
82 | `lr_scheduler_kwargs`: dict, optional, list of parameters used by the user specified `lr_scheduler`.
83 | `dataloader_kwargs`: dict, optional, list of parameters passed into the PyTorch Lightning dataloader by the `TimeSeriesDataLoader`.
84 | `**trainer_kwargs`: int, keyword trainer arguments inherited from [PyTorch Lighning's trainer](https://pytorch-lightning.readthedocs.io/en/stable/api/pytorch_lightning.trainer.trainer.Trainer.html?highlight=trainer).
85 | 86 | *References*
87 | - Zeng, Ailing, et al. "Are transformers effective for time series forecasting?." Proceedings of the AAAI conference on artificial intelligence. Vol. 37. No. 9. 2023." 88 | """ 89 | 90 | # Class attributes 91 | EXOGENOUS_FUTR = False 92 | EXOGENOUS_HIST = False 93 | EXOGENOUS_STAT = False 94 | MULTIVARIATE = False # If the model produces multivariate forecasts (True) or univariate (False) 95 | RECURRENT = ( 96 | False # If the model produces forecasts recursively (True) or direct (False) 97 | ) 98 | 99 | def __init__( 100 | self, 101 | h: int, 102 | input_size: int, 103 | stat_exog_list=None, 104 | hist_exog_list=None, 105 | futr_exog_list=None, 106 | exclude_insample_y=False, 107 | moving_avg_window: int = 25, 108 | loss=MAE(), 109 | valid_loss=None, 110 | max_steps: int = 5000, 111 | learning_rate: float = 1e-4, 112 | num_lr_decays: int = -1, 113 | early_stop_patience_steps: int = -1, 114 | val_check_steps: int = 100, 115 | batch_size: int = 32, 116 | valid_batch_size: Optional[int] = None, 117 | windows_batch_size=1024, 118 | inference_windows_batch_size=1024, 119 | start_padding_enabled=False, 120 | step_size: int = 1, 121 | scaler_type: str = "identity", 122 | random_seed: int = 1, 123 | drop_last_loader: bool = False, 124 | alias: Optional[str] = None, 125 | optimizer=None, 126 | optimizer_kwargs=None, 127 | lr_scheduler=None, 128 | lr_scheduler_kwargs=None, 129 | dataloader_kwargs=None, 130 | **trainer_kwargs 131 | ): 132 | super(DLinear, self).__init__( 133 | h=h, 134 | input_size=input_size, 135 | hist_exog_list=hist_exog_list, 136 | stat_exog_list=stat_exog_list, 137 | futr_exog_list=futr_exog_list, 138 | exclude_insample_y=exclude_insample_y, 139 | loss=loss, 140 | valid_loss=valid_loss, 141 | max_steps=max_steps, 142 | learning_rate=learning_rate, 143 | num_lr_decays=num_lr_decays, 144 | early_stop_patience_steps=early_stop_patience_steps, 145 | val_check_steps=val_check_steps, 146 | batch_size=batch_size, 147 | windows_batch_size=windows_batch_size, 148 | valid_batch_size=valid_batch_size, 149 | inference_windows_batch_size=inference_windows_batch_size, 150 | start_padding_enabled=start_padding_enabled, 151 | step_size=step_size, 152 | scaler_type=scaler_type, 153 | drop_last_loader=drop_last_loader, 154 | alias=alias, 155 | random_seed=random_seed, 156 | optimizer=optimizer, 157 | optimizer_kwargs=optimizer_kwargs, 158 | lr_scheduler=lr_scheduler, 159 | lr_scheduler_kwargs=lr_scheduler_kwargs, 160 | dataloader_kwargs=dataloader_kwargs, 161 | **trainer_kwargs 162 | ) 163 | 164 | # Architecture 165 | if moving_avg_window % 2 == 0: 166 | raise Exception("moving_avg_window should be uneven") 167 | 168 | self.c_out = self.loss.outputsize_multiplier 169 | self.output_attention = False 170 | self.enc_in = 1 171 | self.dec_in = 1 172 | 173 | # Decomposition 174 | self.decomp = SeriesDecomp(moving_avg_window) 175 | 176 | self.linear_trend = nn.Linear( 177 | self.input_size, self.loss.outputsize_multiplier * h, bias=True 178 | ) 179 | self.linear_season = nn.Linear( 180 | self.input_size, self.loss.outputsize_multiplier * h, bias=True 181 | ) 182 | 183 | def forward(self, windows_batch): 184 | # Parse windows_batch 185 | insample_y = windows_batch["insample_y"].squeeze(-1) 186 | 187 | # Parse inputs 188 | batch_size = len(insample_y) 189 | seasonal_init, trend_init = self.decomp(insample_y) 190 | 191 | trend_part = self.linear_trend(trend_init) 192 | seasonal_part = self.linear_season(seasonal_init) 193 | 194 | # Final 195 | forecast = trend_part + seasonal_part 196 | forecast = forecast.reshape(batch_size, self.h, self.loss.outputsize_multiplier) 197 | return forecast 198 | -------------------------------------------------------------------------------- /neuralforecast/models/itransformer.py: -------------------------------------------------------------------------------- 1 | # AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/models.itransformer.ipynb. 2 | 3 | # %% auto 0 4 | __all__ = ['iTransformer'] 5 | 6 | # %% ../../nbs/models.itransformer.ipynb 6 7 | import torch 8 | import torch.nn as nn 9 | import torch.nn.functional as F 10 | 11 | from typing import Optional 12 | from ..losses.pytorch import MAE 13 | from ..common._base_model import BaseModel 14 | 15 | from neuralforecast.common._modules import ( 16 | TransEncoder, 17 | TransEncoderLayer, 18 | AttentionLayer, 19 | FullAttention, 20 | DataEmbedding_inverted, 21 | ) 22 | 23 | # %% ../../nbs/models.itransformer.ipynb 8 24 | class iTransformer(BaseModel): 25 | """iTransformer 26 | 27 | **Parameters:**
28 | `h`: int, Forecast horizon.
29 | `input_size`: int, autorregresive inputs size, y=[1,2,3,4] input_size=2 -> y_[t-2:t]=[1,2].
30 | `n_series`: int, number of time-series.
31 | `futr_exog_list`: str list, future exogenous columns.
32 | `hist_exog_list`: str list, historic exogenous columns.
33 | `stat_exog_list`: str list, static exogenous columns.
34 | `exclude_insample_y`: bool=False, the model skips the autoregressive features y[t-input_size:t] if True.
35 | `hidden_size`: int, dimension of the model.
36 | `n_heads`: int, number of heads.
37 | `e_layers`: int, number of encoder layers.
38 | `d_layers`: int, number of decoder layers.
39 | `d_ff`: int, dimension of fully-connected layer.
40 | `factor`: int, attention factor.
41 | `dropout`: float, dropout rate.
42 | `use_norm`: bool, whether to normalize or not.
43 | `loss`: PyTorch module, instantiated train loss class from [losses collection](https://nixtla.github.io/neuralforecast/losses.pytorch.html).
44 | `valid_loss`: PyTorch module=`loss`, instantiated valid loss class from [losses collection](https://nixtla.github.io/neuralforecast/losses.pytorch.html).
45 | `max_steps`: int=1000, maximum number of training steps.
46 | `learning_rate`: float=1e-3, Learning rate between (0, 1).
47 | `num_lr_decays`: int=-1, Number of learning rate decays, evenly distributed across max_steps.
48 | `early_stop_patience_steps`: int=-1, Number of validation iterations before early stopping.
49 | `val_check_steps`: int=100, Number of training steps between every validation loss check.
50 | `batch_size`: int=32, number of different series in each batch.
51 | `valid_batch_size`: int=None, number of different series in each validation and test batch, if None uses batch_size.
52 | `windows_batch_size`: int=32, number of windows to sample in each training batch, default uses all.
53 | `inference_windows_batch_size`: int=32, number of windows to sample in each inference batch, -1 uses all.
54 | `start_padding_enabled`: bool=False, if True, the model will pad the time series with zeros at the beginning, by input size.
55 | `step_size`: int=1, step size between each window of temporal data.
56 | `scaler_type`: str='identity', type of scaler for temporal inputs normalization see [temporal scalers](https://nixtla.github.io/neuralforecast/common.scalers.html).
57 | `random_seed`: int=1, random_seed for pytorch initializer and numpy generators.
58 | `drop_last_loader`: bool=False, if True `TimeSeriesDataLoader` drops last non-full batch.
59 | `alias`: str, optional, Custom name of the model.
60 | `optimizer`: Subclass of 'torch.optim.Optimizer', optional, user specified optimizer instead of the default choice (Adam).
61 | `optimizer_kwargs`: dict, optional, list of parameters used by the user specified `optimizer`.
62 | `lr_scheduler`: Subclass of 'torch.optim.lr_scheduler.LRScheduler', optional, user specified lr_scheduler instead of the default choice (StepLR).
63 | `lr_scheduler_kwargs`: dict, optional, list of parameters used by the user specified `lr_scheduler`.
64 | `dataloader_kwargs`: dict, optional, list of parameters passed into the PyTorch Lightning dataloader by the `TimeSeriesDataLoader`.
65 | `**trainer_kwargs`: int, keyword trainer arguments inherited from [PyTorch Lighning's trainer](https://pytorch-lightning.readthedocs.io/en/stable/api/pytorch_lightning.trainer.trainer.Trainer.html?highlight=trainer).
66 | 67 | **References**
68 | - [Yong Liu, Tengge Hu, Haoran Zhang, Haixu Wu, Shiyu Wang, Lintao Ma, Mingsheng Long. "iTransformer: Inverted Transformers Are Effective for Time Series Forecasting"](https://arxiv.org/abs/2310.06625) 69 | """ 70 | 71 | # Class attributes 72 | EXOGENOUS_FUTR = False 73 | EXOGENOUS_HIST = False 74 | EXOGENOUS_STAT = False 75 | MULTIVARIATE = True 76 | RECURRENT = False 77 | 78 | def __init__( 79 | self, 80 | h, 81 | input_size, 82 | n_series, 83 | futr_exog_list=None, 84 | hist_exog_list=None, 85 | stat_exog_list=None, 86 | exclude_insample_y=False, 87 | hidden_size: int = 512, 88 | n_heads: int = 8, 89 | e_layers: int = 2, 90 | d_layers: int = 1, 91 | d_ff: int = 2048, 92 | factor: int = 1, 93 | dropout: float = 0.1, 94 | use_norm: bool = True, 95 | loss=MAE(), 96 | valid_loss=None, 97 | max_steps: int = 1000, 98 | learning_rate: float = 1e-3, 99 | num_lr_decays: int = -1, 100 | early_stop_patience_steps: int = -1, 101 | val_check_steps: int = 100, 102 | batch_size: int = 32, 103 | valid_batch_size: Optional[int] = None, 104 | windows_batch_size=32, 105 | inference_windows_batch_size=32, 106 | start_padding_enabled=False, 107 | step_size: int = 1, 108 | scaler_type: str = "identity", 109 | random_seed: int = 1, 110 | drop_last_loader: bool = False, 111 | alias: Optional[str] = None, 112 | optimizer=None, 113 | optimizer_kwargs=None, 114 | lr_scheduler=None, 115 | lr_scheduler_kwargs=None, 116 | dataloader_kwargs=None, 117 | **trainer_kwargs 118 | ): 119 | 120 | super(iTransformer, self).__init__( 121 | h=h, 122 | input_size=input_size, 123 | n_series=n_series, 124 | futr_exog_list=futr_exog_list, 125 | hist_exog_list=hist_exog_list, 126 | stat_exog_list=stat_exog_list, 127 | exclude_insample_y=exclude_insample_y, 128 | loss=loss, 129 | valid_loss=valid_loss, 130 | max_steps=max_steps, 131 | learning_rate=learning_rate, 132 | num_lr_decays=num_lr_decays, 133 | early_stop_patience_steps=early_stop_patience_steps, 134 | val_check_steps=val_check_steps, 135 | batch_size=batch_size, 136 | valid_batch_size=valid_batch_size, 137 | windows_batch_size=windows_batch_size, 138 | inference_windows_batch_size=inference_windows_batch_size, 139 | start_padding_enabled=start_padding_enabled, 140 | step_size=step_size, 141 | scaler_type=scaler_type, 142 | random_seed=random_seed, 143 | drop_last_loader=drop_last_loader, 144 | alias=alias, 145 | optimizer=optimizer, 146 | optimizer_kwargs=optimizer_kwargs, 147 | lr_scheduler=lr_scheduler, 148 | lr_scheduler_kwargs=lr_scheduler_kwargs, 149 | dataloader_kwargs=dataloader_kwargs, 150 | **trainer_kwargs 151 | ) 152 | 153 | self.enc_in = n_series 154 | self.dec_in = n_series 155 | self.c_out = n_series 156 | self.hidden_size = hidden_size 157 | self.n_heads = n_heads 158 | self.e_layers = e_layers 159 | self.d_layers = d_layers 160 | self.d_ff = d_ff 161 | self.factor = factor 162 | self.dropout = dropout 163 | self.use_norm = use_norm 164 | 165 | # Architecture 166 | self.enc_embedding = DataEmbedding_inverted( 167 | input_size, self.hidden_size, self.dropout 168 | ) 169 | 170 | self.encoder = TransEncoder( 171 | [ 172 | TransEncoderLayer( 173 | AttentionLayer( 174 | FullAttention( 175 | False, self.factor, attention_dropout=self.dropout 176 | ), 177 | self.hidden_size, 178 | self.n_heads, 179 | ), 180 | self.hidden_size, 181 | self.d_ff, 182 | dropout=self.dropout, 183 | activation=F.gelu, 184 | ) 185 | for l in range(self.e_layers) 186 | ], 187 | norm_layer=torch.nn.LayerNorm(self.hidden_size), 188 | ) 189 | 190 | self.projector = nn.Linear( 191 | self.hidden_size, h * self.loss.outputsize_multiplier, bias=True 192 | ) 193 | 194 | def forecast(self, x_enc): 195 | if self.use_norm: 196 | # Normalization from Non-stationary Transformer 197 | means = x_enc.mean(1, keepdim=True).detach() 198 | x_enc = x_enc - means 199 | stdev = torch.sqrt( 200 | torch.var(x_enc, dim=1, keepdim=True, unbiased=False) + 1e-5 201 | ) 202 | x_enc /= stdev 203 | 204 | _, _, N = x_enc.shape # B L N 205 | # B: batch_size; E: hidden_size; 206 | # L: input_size; S: horizon(h); 207 | # N: number of variate (tokens), can also includes covariates 208 | 209 | # Embedding 210 | # B L N -> B N E (B L N -> B L E in the vanilla Transformer) 211 | enc_out = self.enc_embedding( 212 | x_enc, None 213 | ) # covariates (e.g timestamp) can be also embedded as tokens 214 | 215 | # B N E -> B N E (B L E -> B L E in the vanilla Transformer) 216 | # the dimensions of embedded time series has been inverted, and then processed by native attn, layernorm and ffn modules 217 | enc_out, attns = self.encoder(enc_out, attn_mask=None) 218 | 219 | # B N E -> B N S -> B S N 220 | dec_out = self.projector(enc_out).permute(0, 2, 1)[ 221 | :, :, :N 222 | ] # filter the covariates 223 | 224 | if self.use_norm: 225 | # De-Normalization from Non-stationary Transformer 226 | dec_out = dec_out * ( 227 | stdev[:, 0, :] 228 | .unsqueeze(1) 229 | .repeat(1, self.h * self.loss.outputsize_multiplier, 1) 230 | ) 231 | dec_out = dec_out + ( 232 | means[:, 0, :] 233 | .unsqueeze(1) 234 | .repeat(1, self.h * self.loss.outputsize_multiplier, 1) 235 | ) 236 | 237 | return dec_out 238 | 239 | def forward(self, windows_batch): 240 | insample_y = windows_batch["insample_y"] 241 | 242 | y_pred = self.forecast(insample_y) 243 | y_pred = y_pred.reshape(insample_y.shape[0], self.h, -1) 244 | 245 | return y_pred 246 | -------------------------------------------------------------------------------- /neuralforecast/models/mlp.py: -------------------------------------------------------------------------------- 1 | # AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/models.mlp.ipynb. 2 | 3 | # %% auto 0 4 | __all__ = ['MLP'] 5 | 6 | # %% ../../nbs/models.mlp.ipynb 5 7 | from typing import Optional 8 | 9 | import torch 10 | import torch.nn as nn 11 | 12 | from ..losses.pytorch import MAE 13 | from ..common._base_model import BaseModel 14 | 15 | # %% ../../nbs/models.mlp.ipynb 6 16 | class MLP(BaseModel): 17 | """MLP 18 | 19 | Simple Multi Layer Perceptron architecture (MLP). 20 | This deep neural network has constant units through its layers, each with 21 | ReLU non-linearities, it is trained using ADAM stochastic gradient descent. 22 | The network accepts static, historic and future exogenous data, flattens 23 | the inputs and learns fully connected relationships against the target variable. 24 | 25 | **Parameters:**
26 | `h`: int, forecast horizon.
27 | `input_size`: int, considered autorregresive inputs (lags), y=[1,2,3,4] input_size=2 -> lags=[1,2].
28 | `stat_exog_list`: str list, static exogenous columns.
29 | `hist_exog_list`: str list, historic exogenous columns.
30 | `futr_exog_list`: str list, future exogenous columns.
31 | `exclude_insample_y`: bool=False, the model skips the autoregressive features y[t-input_size:t] if True.
32 | `num_layers`: int, number of layers for the MLP.
33 | `hidden_size`: int, number of units for each layer of the MLP.
34 | `loss`: PyTorch module, instantiated train loss class from [losses collection](https://nixtla.github.io/neuralforecast/losses.pytorch.html).
35 | `valid_loss`: PyTorch module=`loss`, instantiated valid loss class from [losses collection](https://nixtla.github.io/neuralforecast/losses.pytorch.html).
36 | `max_steps`: int=1000, maximum number of training steps.
37 | `learning_rate`: float=1e-3, Learning rate between (0, 1).
38 | `num_lr_decays`: int=-1, Number of learning rate decays, evenly distributed across max_steps.
39 | `early_stop_patience_steps`: int=-1, Number of validation iterations before early stopping.
40 | `val_check_steps`: int=100, Number of training steps between every validation loss check.
41 | `batch_size`: int=32, number of different series in each batch.
42 | `valid_batch_size`: int=None, number of different series in each validation and test batch, if None uses batch_size.
43 | `windows_batch_size`: int=1024, number of windows to sample in each training batch, default uses all.
44 | `inference_windows_batch_size`: int=-1, number of windows to sample in each inference batch, -1 uses all.
45 | `start_padding_enabled`: bool=False, if True, the model will pad the time series with zeros at the beginning, by input size.
46 | `step_size`: int=1, step size between each window of temporal data.
47 | `scaler_type`: str='identity', type of scaler for temporal inputs normalization see [temporal scalers](https://nixtla.github.io/neuralforecast/common.scalers.html).
48 | `random_seed`: int=1, random_seed for pytorch initializer and numpy generators.
49 | `drop_last_loader`: bool=False, if True `TimeSeriesDataLoader` drops last non-full batch.
50 | `alias`: str, optional, Custom name of the model.
51 | `optimizer`: Subclass of 'torch.optim.Optimizer', optional, user specified optimizer instead of the default choice (Adam).
52 | `optimizer_kwargs`: dict, optional, list of parameters used by the user specified `optimizer`.
53 | `lr_scheduler`: Subclass of 'torch.optim.lr_scheduler.LRScheduler', optional, user specified lr_scheduler instead of the default choice (StepLR).
54 | `lr_scheduler_kwargs`: dict, optional, list of parameters used by the user specified `lr_scheduler`.
55 | `dataloader_kwargs`: dict, optional, list of parameters passed into the PyTorch Lightning dataloader by the `TimeSeriesDataLoader`.
56 | `**trainer_kwargs`: int, keyword trainer arguments inherited from [PyTorch Lighning's trainer](https://pytorch-lightning.readthedocs.io/en/stable/api/pytorch_lightning.trainer.trainer.Trainer.html?highlight=trainer).
57 | """ 58 | 59 | # Class attributes 60 | EXOGENOUS_FUTR = True 61 | EXOGENOUS_HIST = True 62 | EXOGENOUS_STAT = True 63 | MULTIVARIATE = False # If the model produces multivariate forecasts (True) or univariate (False) 64 | RECURRENT = ( 65 | False # If the model produces forecasts recursively (True) or direct (False) 66 | ) 67 | 68 | def __init__( 69 | self, 70 | h, 71 | input_size, 72 | stat_exog_list=None, 73 | hist_exog_list=None, 74 | futr_exog_list=None, 75 | exclude_insample_y=False, 76 | num_layers=2, 77 | hidden_size=1024, 78 | loss=MAE(), 79 | valid_loss=None, 80 | max_steps: int = 1000, 81 | learning_rate: float = 1e-3, 82 | num_lr_decays: int = -1, 83 | early_stop_patience_steps: int = -1, 84 | val_check_steps: int = 100, 85 | batch_size: int = 32, 86 | valid_batch_size: Optional[int] = None, 87 | windows_batch_size=1024, 88 | inference_windows_batch_size=-1, 89 | start_padding_enabled=False, 90 | step_size: int = 1, 91 | scaler_type: str = "identity", 92 | random_seed: int = 1, 93 | drop_last_loader: bool = False, 94 | alias: Optional[str] = None, 95 | optimizer=None, 96 | optimizer_kwargs=None, 97 | lr_scheduler=None, 98 | lr_scheduler_kwargs=None, 99 | dataloader_kwargs=None, 100 | **trainer_kwargs 101 | ): 102 | 103 | # Inherit BaseWindows class 104 | super(MLP, self).__init__( 105 | h=h, 106 | input_size=input_size, 107 | stat_exog_list=stat_exog_list, 108 | hist_exog_list=hist_exog_list, 109 | futr_exog_list=futr_exog_list, 110 | exclude_insample_y=exclude_insample_y, 111 | loss=loss, 112 | valid_loss=valid_loss, 113 | max_steps=max_steps, 114 | learning_rate=learning_rate, 115 | num_lr_decays=num_lr_decays, 116 | early_stop_patience_steps=early_stop_patience_steps, 117 | val_check_steps=val_check_steps, 118 | batch_size=batch_size, 119 | valid_batch_size=valid_batch_size, 120 | windows_batch_size=windows_batch_size, 121 | inference_windows_batch_size=inference_windows_batch_size, 122 | start_padding_enabled=start_padding_enabled, 123 | step_size=step_size, 124 | scaler_type=scaler_type, 125 | random_seed=random_seed, 126 | drop_last_loader=drop_last_loader, 127 | alias=alias, 128 | optimizer=optimizer, 129 | optimizer_kwargs=optimizer_kwargs, 130 | lr_scheduler=lr_scheduler, 131 | lr_scheduler_kwargs=lr_scheduler_kwargs, 132 | dataloader_kwargs=dataloader_kwargs, 133 | **trainer_kwargs 134 | ) 135 | 136 | # Architecture 137 | self.num_layers = num_layers 138 | self.hidden_size = hidden_size 139 | 140 | input_size_first_layer = ( 141 | input_size 142 | + self.hist_exog_size * input_size 143 | + self.futr_exog_size * (input_size + h) 144 | + self.stat_exog_size 145 | ) 146 | 147 | # MultiLayer Perceptron 148 | layers = [ 149 | nn.Linear(in_features=input_size_first_layer, out_features=hidden_size) 150 | ] 151 | for i in range(num_layers - 1): 152 | layers += [nn.Linear(in_features=hidden_size, out_features=hidden_size)] 153 | self.mlp = nn.ModuleList(layers) 154 | 155 | # Adapter with Loss dependent dimensions 156 | self.out = nn.Linear( 157 | in_features=hidden_size, out_features=h * self.loss.outputsize_multiplier 158 | ) 159 | 160 | def forward(self, windows_batch): 161 | 162 | # Parse windows_batch 163 | insample_y = windows_batch["insample_y"].squeeze(-1) 164 | futr_exog = windows_batch["futr_exog"] 165 | hist_exog = windows_batch["hist_exog"] 166 | stat_exog = windows_batch["stat_exog"] 167 | 168 | # Flatten MLP inputs [B, L+H, C] -> [B, (L+H)*C] 169 | # Contatenate [ Y_t, | X_{t-L},..., X_{t} | F_{t-L},..., F_{t+H} | S ] 170 | batch_size = len(insample_y) 171 | if self.hist_exog_size > 0: 172 | insample_y = torch.cat( 173 | (insample_y, hist_exog.reshape(batch_size, -1)), dim=1 174 | ) 175 | 176 | if self.futr_exog_size > 0: 177 | insample_y = torch.cat( 178 | (insample_y, futr_exog.reshape(batch_size, -1)), dim=1 179 | ) 180 | 181 | if self.stat_exog_size > 0: 182 | insample_y = torch.cat( 183 | (insample_y, stat_exog.reshape(batch_size, -1)), dim=1 184 | ) 185 | 186 | y_pred = insample_y.clone() 187 | for layer in self.mlp: 188 | y_pred = torch.relu(layer(y_pred)) 189 | y_pred = self.out(y_pred) 190 | 191 | y_pred = y_pred.reshape(batch_size, self.h, self.loss.outputsize_multiplier) 192 | return y_pred 193 | -------------------------------------------------------------------------------- /neuralforecast/models/mlpmultivariate.py: -------------------------------------------------------------------------------- 1 | # AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/models.mlpmultivariate.ipynb. 2 | 3 | # %% auto 0 4 | __all__ = ['MLPMultivariate'] 5 | 6 | # %% ../../nbs/models.mlpmultivariate.ipynb 5 7 | import torch 8 | import torch.nn as nn 9 | 10 | from typing import Optional 11 | from ..losses.pytorch import MAE 12 | from ..common._base_model import BaseModel 13 | 14 | # %% ../../nbs/models.mlpmultivariate.ipynb 6 15 | class MLPMultivariate(BaseModel): 16 | """MLPMultivariate 17 | 18 | Simple Multi Layer Perceptron architecture (MLP) for multivariate forecasting. 19 | This deep neural network has constant units through its layers, each with 20 | ReLU non-linearities, it is trained using ADAM stochastic gradient descent. 21 | The network accepts static, historic and future exogenous data, flattens 22 | the inputs and learns fully connected relationships against the target variables. 23 | 24 | **Parameters:**
25 | `h`: int, forecast horizon.
26 | `input_size`: int, considered autorregresive inputs (lags), y=[1,2,3,4] input_size=2 -> lags=[1,2].
27 | `n_series`: int, number of time-series.
28 | `stat_exog_list`: str list, static exogenous columns.
29 | `hist_exog_list`: str list, historic exogenous columns.
30 | `futr_exog_list`: str list, future exogenous columns.
31 | `num_layers`: int, number of layers for the MLP.
32 | `hidden_size`: int, number of units for each layer of the MLP.
33 | `loss`: PyTorch module, instantiated train loss class from [losses collection](https://nixtla.github.io/neuralforecast/losses.pytorch.html).
34 | `valid_loss`: PyTorch module=`loss`, instantiated valid loss class from [losses collection](https://nixtla.github.io/neuralforecast/losses.pytorch.html).
35 | `max_steps`: int=1000, maximum number of training steps.
36 | `learning_rate`: float=1e-3, Learning rate between (0, 1).
37 | `num_lr_decays`: int=-1, Number of learning rate decays, evenly distributed across max_steps.
38 | `early_stop_patience_steps`: int=-1, Number of validation iterations before early stopping.
39 | `val_check_steps`: int=100, Number of training steps between every validation loss check.
40 | `batch_size`: int=32, number of different series in each batch.
41 | `valid_batch_size`: int=None, number of different series in each validation and test batch, if None uses batch_size.
42 | `windows_batch_size`: int=32, number of windows to sample in each training batch, default uses all.
43 | `inference_windows_batch_size`: int=32, number of windows to sample in each inference batch, -1 uses all.
44 | `start_padding_enabled`: bool=False, if True, the model will pad the time series with zeros at the beginning, by input size.
45 | `step_size`: int=1, step size between each window of temporal data.
46 | `scaler_type`: str='identity', type of scaler for temporal inputs normalization see [temporal scalers](https://nixtla.github.io/neuralforecast/common.scalers.html).
47 | `random_seed`: int=1, random_seed for pytorch initializer and numpy generators.
48 | `drop_last_loader`: bool=False, if True `TimeSeriesDataLoader` drops last non-full batch.
49 | `alias`: str, optional, Custom name of the model.
50 | `optimizer`: Subclass of 'torch.optim.Optimizer', optional, user specified optimizer instead of the default choice (Adam).
51 | `optimizer_kwargs`: dict, optional, list of parameters used by the user specified `optimizer`.
52 | `lr_scheduler`: Subclass of 'torch.optim.lr_scheduler.LRScheduler', optional, user specified lr_scheduler instead of the default choice (StepLR).
53 | `lr_scheduler_kwargs`: dict, optional, list of parameters used by the user specified `lr_scheduler`.
54 | `dataloader_kwargs`: dict, optional, list of parameters passed into the PyTorch Lightning dataloader by the `TimeSeriesDataLoader`.
55 | `**trainer_kwargs`: int, keyword trainer arguments inherited from [PyTorch Lighning's trainer](https://pytorch-lightning.readthedocs.io/en/stable/api/pytorch_lightning.trainer.trainer.Trainer.html?highlight=trainer).
56 | """ 57 | 58 | # Class attributes 59 | EXOGENOUS_FUTR = True 60 | EXOGENOUS_HIST = True 61 | EXOGENOUS_STAT = True 62 | MULTIVARIATE = True # If the model produces multivariate forecasts (True) or univariate (False) 63 | RECURRENT = ( 64 | False # If the model produces forecasts recursively (True) or direct (False) 65 | ) 66 | 67 | def __init__( 68 | self, 69 | h, 70 | input_size, 71 | n_series, 72 | stat_exog_list=None, 73 | hist_exog_list=None, 74 | futr_exog_list=None, 75 | exclude_insample_y=False, 76 | num_layers=2, 77 | hidden_size=1024, 78 | loss=MAE(), 79 | valid_loss=None, 80 | max_steps: int = 1000, 81 | learning_rate: float = 1e-3, 82 | num_lr_decays: int = -1, 83 | early_stop_patience_steps: int = -1, 84 | val_check_steps: int = 100, 85 | batch_size: int = 32, 86 | valid_batch_size: Optional[int] = None, 87 | windows_batch_size=32, 88 | inference_windows_batch_size=32, 89 | start_padding_enabled=False, 90 | step_size: int = 1, 91 | scaler_type: str = "identity", 92 | random_seed: int = 1, 93 | drop_last_loader: bool = False, 94 | alias: Optional[str] = None, 95 | optimizer=None, 96 | optimizer_kwargs=None, 97 | lr_scheduler=None, 98 | lr_scheduler_kwargs=None, 99 | dataloader_kwargs=None, 100 | **trainer_kwargs 101 | ): 102 | 103 | # Inherit BaseMultivariate class 104 | super(MLPMultivariate, self).__init__( 105 | h=h, 106 | input_size=input_size, 107 | n_series=n_series, 108 | stat_exog_list=stat_exog_list, 109 | hist_exog_list=hist_exog_list, 110 | futr_exog_list=futr_exog_list, 111 | exclude_insample_y=exclude_insample_y, 112 | loss=loss, 113 | valid_loss=valid_loss, 114 | max_steps=max_steps, 115 | learning_rate=learning_rate, 116 | num_lr_decays=num_lr_decays, 117 | early_stop_patience_steps=early_stop_patience_steps, 118 | val_check_steps=val_check_steps, 119 | batch_size=batch_size, 120 | valid_batch_size=valid_batch_size, 121 | windows_batch_size=windows_batch_size, 122 | inference_windows_batch_size=inference_windows_batch_size, 123 | start_padding_enabled=start_padding_enabled, 124 | step_size=step_size, 125 | scaler_type=scaler_type, 126 | random_seed=random_seed, 127 | drop_last_loader=drop_last_loader, 128 | alias=alias, 129 | optimizer=optimizer, 130 | optimizer_kwargs=optimizer_kwargs, 131 | lr_scheduler=lr_scheduler, 132 | lr_scheduler_kwargs=lr_scheduler_kwargs, 133 | dataloader_kwargs=dataloader_kwargs, 134 | **trainer_kwargs 135 | ) 136 | 137 | # Architecture 138 | self.num_layers = num_layers 139 | self.hidden_size = hidden_size 140 | 141 | input_size_first_layer = n_series * ( 142 | input_size 143 | + self.hist_exog_size * input_size 144 | + self.futr_exog_size * (input_size + h) 145 | + self.stat_exog_size 146 | ) 147 | 148 | # MultiLayer Perceptron 149 | layers = [ 150 | nn.Linear(in_features=input_size_first_layer, out_features=hidden_size) 151 | ] 152 | for i in range(num_layers - 1): 153 | layers += [nn.Linear(in_features=hidden_size, out_features=hidden_size)] 154 | self.mlp = nn.ModuleList(layers) 155 | 156 | # Adapter with Loss dependent dimensions 157 | self.out = nn.Linear( 158 | in_features=hidden_size, 159 | out_features=h * self.loss.outputsize_multiplier * n_series, 160 | ) 161 | 162 | def forward(self, windows_batch): 163 | 164 | # Parse windows_batch 165 | x = windows_batch[ 166 | "insample_y" 167 | ] # [batch_size (B), input_size (L), n_series (N)] 168 | hist_exog = windows_batch["hist_exog"] # [B, hist_exog_size (X), L, N] 169 | futr_exog = windows_batch["futr_exog"] # [B, futr_exog_size (F), L + h, N] 170 | stat_exog = windows_batch["stat_exog"] # [N, stat_exog_size (S)] 171 | 172 | # Flatten MLP inputs [B, C, L+H, N] -> [B, C * (L+H) * N] 173 | # Contatenate [ Y^1_t, ..., Y^N_t | X^1_{t-L},..., X^1_{t}, ..., X^N_{t} | F^1_{t-L},..., F^1_{t+H}, ...., F^N_{t+H} | S^1, ..., S^N ] 174 | batch_size = x.shape[0] 175 | x = x.reshape(batch_size, -1) 176 | if self.hist_exog_size > 0: 177 | x = torch.cat((x, hist_exog.reshape(batch_size, -1)), dim=1) 178 | 179 | if self.futr_exog_size > 0: 180 | x = torch.cat((x, futr_exog.reshape(batch_size, -1)), dim=1) 181 | 182 | if self.stat_exog_size > 0: 183 | stat_exog = stat_exog.reshape(-1) # [N, S] -> [N * S] 184 | stat_exog = stat_exog.unsqueeze(0).repeat( 185 | batch_size, 1 186 | ) # [N * S] -> [B, N * S] 187 | x = torch.cat((x, stat_exog), dim=1) 188 | 189 | for layer in self.mlp: 190 | x = torch.relu(layer(x)) 191 | x = self.out(x) 192 | 193 | forecast = x.reshape(batch_size, self.h, -1) 194 | 195 | return forecast 196 | -------------------------------------------------------------------------------- /neuralforecast/models/nlinear.py: -------------------------------------------------------------------------------- 1 | # AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/models.nlinear.ipynb. 2 | 3 | # %% auto 0 4 | __all__ = ['NLinear'] 5 | 6 | # %% ../../nbs/models.nlinear.ipynb 5 7 | from typing import Optional 8 | 9 | import torch.nn as nn 10 | 11 | from ..common._base_model import BaseModel 12 | 13 | from ..losses.pytorch import MAE 14 | 15 | # %% ../../nbs/models.nlinear.ipynb 7 16 | class NLinear(BaseModel): 17 | """NLinear 18 | 19 | *Parameters:*
20 | `h`: int, forecast horizon.
21 | `input_size`: int, maximum sequence length for truncated train backpropagation.
22 | `stat_exog_list`: str list, static exogenous columns.
23 | `hist_exog_list`: str list, historic exogenous columns.
24 | `futr_exog_list`: str list, future exogenous columns.
25 | `exclude_insample_y`: bool=False, the model skips the autoregressive features y[t-input_size:t] if True.
26 | `loss`: PyTorch module, instantiated train loss class from [losses collection](https://nixtla.github.io/neuralforecast/losses.pytorch.html).
27 | `valid_loss`: PyTorch module=`loss`, instantiated valid loss class from [losses collection](https://nixtla.github.io/neuralforecast/losses.pytorch.html).
28 | `max_steps`: int=1000, maximum number of training steps.
29 | `learning_rate`: float=1e-3, Learning rate between (0, 1).
30 | `num_lr_decays`: int=-1, Number of learning rate decays, evenly distributed across max_steps.
31 | `early_stop_patience_steps`: int=-1, Number of validation iterations before early stopping.
32 | `val_check_steps`: int=100, Number of training steps between every validation loss check.
33 | `batch_size`: int=32, number of different series in each batch.
34 | `valid_batch_size`: int=None, number of different series in each validation and test batch, if None uses batch_size.
35 | `windows_batch_size`: int=1024, number of windows to sample in each training batch, default uses all.
36 | `inference_windows_batch_size`: int=1024, number of windows to sample in each inference batch.
37 | `start_padding_enabled`: bool=False, if True, the model will pad the time series with zeros at the beginning, by input size.
38 | `step_size`: int=1, step size between each window of temporal data.
39 | `scaler_type`: str='robust', type of scaler for temporal inputs normalization see [temporal scalers](https://nixtla.github.io/neuralforecast/common.scalers.html).
40 | `random_seed`: int=1, random_seed for pytorch initializer and numpy generators.
41 | `drop_last_loader`: bool=False, if True `TimeSeriesDataLoader` drops last non-full batch.
42 | `alias`: str, optional, Custom name of the model.
43 | `optimizer`: Subclass of 'torch.optim.Optimizer', optional, user specified optimizer instead of the default choice (Adam).
44 | `optimizer_kwargs`: dict, optional, list of parameters used by the user specified `optimizer`.
45 | `lr_scheduler`: Subclass of 'torch.optim.lr_scheduler.LRScheduler', optional, user specified lr_scheduler instead of the default choice (StepLR).
46 | `lr_scheduler_kwargs`: dict, optional, list of parameters used by the user specified `lr_scheduler`.
47 | `dataloader_kwargs`: dict, optional, list of parameters passed into the PyTorch Lightning dataloader by the `TimeSeriesDataLoader`.
48 | `**trainer_kwargs`: int, keyword trainer arguments inherited from [PyTorch Lighning's trainer](https://pytorch-lightning.readthedocs.io/en/stable/api/pytorch_lightning.trainer.trainer.Trainer.html?highlight=trainer).
49 | 50 | *References*
51 | - Zeng, Ailing, et al. "Are transformers effective for time series forecasting?." Proceedings of the AAAI conference on artificial intelligence. Vol. 37. No. 9. 2023." 52 | """ 53 | 54 | # Class attributes 55 | EXOGENOUS_FUTR = False 56 | EXOGENOUS_HIST = False 57 | EXOGENOUS_STAT = False 58 | MULTIVARIATE = False # If the model produces multivariate forecasts (True) or univariate (False) 59 | RECURRENT = ( 60 | False # If the model produces forecasts recursively (True) or direct (False) 61 | ) 62 | 63 | def __init__( 64 | self, 65 | h: int, 66 | input_size: int, 67 | stat_exog_list=None, 68 | hist_exog_list=None, 69 | futr_exog_list=None, 70 | exclude_insample_y=False, 71 | loss=MAE(), 72 | valid_loss=None, 73 | max_steps: int = 5000, 74 | learning_rate: float = 1e-4, 75 | num_lr_decays: int = -1, 76 | early_stop_patience_steps: int = -1, 77 | val_check_steps: int = 100, 78 | batch_size: int = 32, 79 | valid_batch_size: Optional[int] = None, 80 | windows_batch_size=1024, 81 | inference_windows_batch_size=1024, 82 | start_padding_enabled=False, 83 | step_size: int = 1, 84 | scaler_type: str = "identity", 85 | random_seed: int = 1, 86 | drop_last_loader: bool = False, 87 | alias: Optional[str] = None, 88 | optimizer=None, 89 | optimizer_kwargs=None, 90 | lr_scheduler=None, 91 | lr_scheduler_kwargs=None, 92 | dataloader_kwargs=None, 93 | **trainer_kwargs 94 | ): 95 | super(NLinear, self).__init__( 96 | h=h, 97 | input_size=input_size, 98 | stat_exog_list=stat_exog_list, 99 | hist_exog_list=hist_exog_list, 100 | futr_exog_list=futr_exog_list, 101 | exclude_insample_y=exclude_insample_y, 102 | loss=loss, 103 | valid_loss=valid_loss, 104 | max_steps=max_steps, 105 | learning_rate=learning_rate, 106 | num_lr_decays=num_lr_decays, 107 | early_stop_patience_steps=early_stop_patience_steps, 108 | val_check_steps=val_check_steps, 109 | batch_size=batch_size, 110 | windows_batch_size=windows_batch_size, 111 | valid_batch_size=valid_batch_size, 112 | inference_windows_batch_size=inference_windows_batch_size, 113 | start_padding_enabled=start_padding_enabled, 114 | step_size=step_size, 115 | scaler_type=scaler_type, 116 | random_seed=random_seed, 117 | drop_last_loader=drop_last_loader, 118 | alias=alias, 119 | optimizer=optimizer, 120 | optimizer_kwargs=optimizer_kwargs, 121 | lr_scheduler=lr_scheduler, 122 | lr_scheduler_kwargs=lr_scheduler_kwargs, 123 | dataloader_kwargs=dataloader_kwargs, 124 | **trainer_kwargs 125 | ) 126 | 127 | # Architecture 128 | self.c_out = self.loss.outputsize_multiplier 129 | self.output_attention = False 130 | self.enc_in = 1 131 | self.dec_in = 1 132 | 133 | self.linear = nn.Linear( 134 | self.input_size, self.loss.outputsize_multiplier * h, bias=True 135 | ) 136 | 137 | def forward(self, windows_batch): 138 | # Parse windows_batch 139 | insample_y = windows_batch["insample_y"].squeeze(-1) 140 | 141 | # Parse inputs 142 | batch_size = len(insample_y) 143 | 144 | # Input normalization 145 | last_value = insample_y[:, -1:] 146 | norm_insample_y = insample_y - last_value 147 | 148 | # Final 149 | forecast = self.linear(norm_insample_y) + last_value 150 | forecast = forecast.reshape(batch_size, self.h, self.loss.outputsize_multiplier) 151 | return forecast 152 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.ruff] 2 | target-version = "py39" 3 | line-length = 88 4 | # Enable Pyflakes `E` and `F` codes by default. 5 | lint.select = [ 6 | # "E", 7 | # "W", # see: https://pypi.org/project/pycodestyle 8 | "F", # see: https://pypi.org/project/pyflakes 9 | # "I", #see: https://pypi.org/project/isort/ 10 | # "D", # see: https://pypi.org/project/pydocstyle 11 | # "N", # see: https://pypi.org/project/pep8-naming 12 | # "S", # see: https://pypi.org/project/flake8-bandit 13 | ] 14 | 15 | 16 | [tool.mypy] 17 | ignore_missing_imports = true 18 | [[tool.mypy.overrides]] 19 | module = 'neuralforecast.compat' 20 | ignore_errors = true 21 | -------------------------------------------------------------------------------- /settings.ini: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | host = github 3 | lib_name = neuralforecast 4 | user = Nixtla 5 | description = Time series forecasting suite using deep learning models 6 | keywords = time-series forecasting deep-learning 7 | author = Nixtla 8 | author_email = business@nixtla.io 9 | copyright = Nixtla Inc. 10 | branch = main 11 | version = 3.0.1 12 | min_python = 3.9 13 | audience = Developers 14 | language = English 15 | custom_sidebar = True 16 | license = apache2 17 | status = 2 18 | requirements = coreforecast>=0.0.6 fsspec numpy>=1.21.6 pandas>=1.3.5 torch>=2.0.0,<=2.6.0 pytorch-lightning>=2.0.0 ray[tune]>=2.2.0 optuna utilsforecast>=0.2.3 19 | spark_requirements = fugue pyspark>=3.5 20 | aws_requirements = fsspec[s3] 21 | dev_requirements = black fastcore<=1.7.29 gitpython hyperopt ipython<=8.32.0 matplotlib mypy nbdev==2.3.25 polars pre-commit pyarrow ruff s3fs transformers 22 | nbs_path = nbs 23 | doc_path = _docs 24 | recursive = True 25 | doc_host = https://nixtlaverse.nixtla.io 26 | doc_baseurl = /neuralforecast/ 27 | git_url = https://github.com/Nixtla/neuralforecast/ 28 | lib_path = neuralforecast 29 | title = neuralforecast 30 | black_formatting = True 31 | jupyter_hooks = True 32 | clean_ids = True 33 | tst_flags = polars 34 | readme_nb = index.ipynb 35 | allowed_metadata_keys = 36 | allowed_cell_metadata_keys = 37 | clear_all = False 38 | put_version_in_init = True 39 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from pkg_resources import parse_version 2 | from configparser import ConfigParser 3 | import setuptools 4 | assert parse_version(setuptools.__version__)>=parse_version('36.2') 5 | 6 | # note: all settings are in settings.ini; edit there, not here 7 | config = ConfigParser(delimiters=['=']) 8 | config.read('settings.ini') 9 | cfg = config['DEFAULT'] 10 | 11 | cfg_keys = 'version description keywords author author_email'.split() 12 | expected = cfg_keys + "lib_name user branch license status min_python audience language".split() 13 | for o in expected: assert o in cfg, "missing expected setting: {}".format(o) 14 | setup_cfg = {o:cfg[o] for o in cfg_keys} 15 | 16 | licenses = { 17 | 'apache2': ('Apache Software License 2.0','OSI Approved :: Apache Software License'), 18 | 'mit': ('MIT License', 'OSI Approved :: MIT License'), 19 | 'gpl2': ('GNU General Public License v2', 'OSI Approved :: GNU General Public License v2 (GPLv2)'), 20 | 'gpl3': ('GNU General Public License v3', 'OSI Approved :: GNU General Public License v3 (GPLv3)'), 21 | 'bsd3': ('BSD License', 'OSI Approved :: BSD License'), 22 | } 23 | statuses = [ '1 - Planning', '2 - Pre-Alpha', '3 - Alpha', 24 | '4 - Beta', '5 - Production/Stable', '6 - Mature', '7 - Inactive' ] 25 | py_versions = '3.9 3.10 3.11 3.12'.split() 26 | 27 | requirements = cfg.get('requirements','').split() 28 | aws_requirements = cfg['aws_requirements'].split() 29 | spark_requirements = cfg['spark_requirements'].split() 30 | if cfg.get('pip_requirements'): requirements += cfg.get('pip_requirements','').split() 31 | min_python = cfg['min_python'] 32 | lic = licenses.get(cfg['license'].lower(), (cfg['license'], None)) 33 | dev_requirements = (cfg.get('dev_requirements') or '').split() 34 | 35 | setuptools.setup( 36 | name = 'neuralforecast', 37 | license = lic[0], 38 | classifiers = [ 39 | 'Development Status :: ' + statuses[int(cfg['status'])], 40 | 'Intended Audience :: ' + cfg['audience'].title(), 41 | 'Natural Language :: ' + cfg['language'].title(), 42 | ] + ['Programming Language :: Python :: '+o for o in py_versions[py_versions.index(min_python):]] + (['License :: ' + lic[1] ] if lic[1] else []), 43 | url = cfg['git_url'], 44 | packages = setuptools.find_packages(), 45 | include_package_data = True, 46 | install_requires = requirements, 47 | extras_require={ 48 | 'aws': aws_requirements, 49 | 'spark': spark_requirements, 50 | 'dev': dev_requirements, 51 | }, 52 | dependency_links = cfg.get('dep_links','').split(), 53 | python_requires = '>=' + cfg['min_python'], 54 | long_description = open('README.md', encoding='utf8').read(), 55 | long_description_content_type = 'text/markdown', 56 | zip_safe = False, 57 | entry_points = { 58 | 'console_scripts': cfg.get('console_scripts','').split(), 59 | 'nbdev': [f'{cfg.get("lib_path")}={cfg.get("lib_path")}._modidx:d'] 60 | }, 61 | **setup_cfg) 62 | 63 | 64 | -------------------------------------------------------------------------------- /test/test_iqloss.py: -------------------------------------------------------------------------------- 1 | #%% Test IQLoss for all types of architectures 2 | from neuralforecast import NeuralForecast 3 | from neuralforecast.models import NBEATSx, NHITS, TSMixerx, LSTM, BiTCN 4 | from neuralforecast.losses.pytorch import IQLoss 5 | from neuralforecast.utils import AirPassengersPanel, AirPassengersStatic 6 | import matplotlib.pyplot as plt 7 | import pandas as pd 8 | 9 | Y_train_df = AirPassengersPanel[AirPassengersPanel.ds=AirPassengersPanel['ds'].values[-12]].reset_index(drop=True) # 12 test 11 | max_steps = 1000 12 | 13 | fcst = NeuralForecast( 14 | models=[ 15 | NBEATSx(h=12, 16 | input_size=24, 17 | loss=IQLoss(), 18 | valid_loss=IQLoss(), 19 | max_steps=max_steps, 20 | scaler_type='standard', 21 | futr_exog_list=['y_[lag12]'], 22 | hist_exog_list=None, 23 | stat_exog_list=['airline1'], 24 | early_stop_patience_steps=3, 25 | ), 26 | NHITS(h=12, 27 | input_size=24, 28 | loss=IQLoss(), 29 | valid_loss=IQLoss(), 30 | max_steps=max_steps, 31 | scaler_type='standard', 32 | futr_exog_list=['y_[lag12]'], 33 | hist_exog_list=None, 34 | stat_exog_list=['airline1'], 35 | early_stop_patience_steps=3, 36 | ), 37 | TSMixerx(h=12, 38 | input_size=24, 39 | n_series=2, 40 | loss=IQLoss(), 41 | valid_loss=IQLoss(), 42 | max_steps=max_steps, 43 | scaler_type='identity', 44 | futr_exog_list=['y_[lag12]'], 45 | hist_exog_list=None, 46 | stat_exog_list=['airline1'], 47 | early_stop_patience_steps=3, 48 | ), 49 | LSTM(h=12, 50 | input_size=24, 51 | loss=IQLoss(), 52 | valid_loss=IQLoss(), 53 | max_steps=max_steps, 54 | scaler_type='standard', 55 | futr_exog_list=['y_[lag12]'], 56 | hist_exog_list=None, 57 | stat_exog_list=['airline1'], 58 | early_stop_patience_steps=3, 59 | ), 60 | BiTCN(h=12, 61 | input_size=24, 62 | loss=IQLoss(), 63 | valid_loss=IQLoss(), 64 | max_steps=max_steps, 65 | scaler_type='standard', 66 | futr_exog_list=['y_[lag12]'], 67 | hist_exog_list=None, 68 | stat_exog_list=['airline1'], 69 | early_stop_patience_steps=3, 70 | ), 71 | ], 72 | freq='M' 73 | ) 74 | fcst.fit(df=Y_train_df, static_df=AirPassengersStatic, val_size=12) 75 | #%% Test IQLoss prediction with multiple quantiles for different architectures 76 | # Test IQLoss 77 | forecasts_q10 = fcst.predict(futr_df=Y_test_df, quantile=0.1) 78 | forecasts_q50 = fcst.predict(futr_df=Y_test_df, quantile=0.5) 79 | forecasts_q90 = fcst.predict(futr_df=Y_test_df, quantile=0.9) 80 | 81 | #%% Plot quantile predictions 82 | forecasts = forecasts_q50.reset_index() 83 | forecasts = forecasts.merge(forecasts_q10.reset_index()) 84 | forecasts = forecasts.merge(forecasts_q90.reset_index()) 85 | Y_hat_df = forecasts.reset_index(drop=True).drop(columns=['unique_id', 'ds']) 86 | plot_df = pd.concat([Y_test_df, Y_hat_df], axis=1) 87 | plot_df = pd.concat([Y_train_df, plot_df]) 88 | 89 | model = 'NHITS' 90 | plot_df = plot_df[plot_df.unique_id=='Airline1'].drop('unique_id', axis=1) 91 | plt.plot(plot_df['ds'], plot_df['y'], c='black', label='True') 92 | plt.plot(plot_df['ds'], plot_df[f'{model}_ql0.5'], c='blue', label='median') 93 | plt.fill_between(x=plot_df['ds'][-12:], 94 | y1=plot_df[f'{model}_ql0.1'][-12:].values, 95 | y2=plot_df[f'{model}_ql0.9'][-12:].values, 96 | alpha=0.4, label='level 90') 97 | plt.legend() 98 | plt.grid() -------------------------------------------------------------------------------- /test/test_isqfdistribution.py: -------------------------------------------------------------------------------- 1 | #%% Test IQLoss for all types of architectures 2 | from neuralforecast import NeuralForecast 3 | from neuralforecast.models import NHITS, BiTCN 4 | from neuralforecast.losses.pytorch import DistributionLoss 5 | from neuralforecast.utils import AirPassengersPanel, AirPassengersStatic 6 | import matplotlib.pyplot as plt 7 | import pandas as pd 8 | 9 | Y_train_df = AirPassengersPanel[AirPassengersPanel.ds=AirPassengersPanel['ds'].values[-12]].reset_index(drop=True) # 12 test 11 | max_steps = 1000 12 | 13 | fcst = NeuralForecast( 14 | models=[ 15 | NHITS(h=12, 16 | input_size=24, 17 | loss=DistributionLoss(distribution="ISQF", level=[10, 20, 30, 40, 50, 60, 70, 80, 90], num_pieces=1), 18 | learning_rate=1e-4, 19 | max_steps=max_steps, 20 | scaler_type='robust', 21 | futr_exog_list=['y_[lag12]'], 22 | hist_exog_list=None, 23 | stat_exog_list=['airline1'], 24 | early_stop_patience_steps=3, 25 | ), 26 | BiTCN(h=12, 27 | input_size=24, 28 | loss=DistributionLoss(distribution="ISQF", level=[10, 20, 30, 40, 50, 60, 70, 80, 90], num_pieces=1), 29 | dropout=0.1, 30 | max_steps=max_steps, 31 | scaler_type='standard', 32 | futr_exog_list=['y_[lag12]'], 33 | hist_exog_list=None, 34 | stat_exog_list=['airline1'], 35 | early_stop_patience_steps=3, 36 | ), 37 | ], 38 | freq='M' 39 | ) 40 | fcst.fit(df=Y_train_df, static_df=AirPassengersStatic, val_size=12) 41 | #%% 42 | forecasts = fcst.predict(futr_df=Y_test_df) 43 | #%% 44 | Y_hat_df = forecasts.reset_index(drop=False).drop(columns=['unique_id','ds']) 45 | plot_df = pd.concat([Y_test_df, Y_hat_df], axis=1) 46 | plot_df = pd.concat([Y_train_df, plot_df]) 47 | 48 | # model = 'BiTCN' 49 | model = 'NHITS' 50 | level = 90 51 | plot_df = plot_df[plot_df.unique_id=='Airline1'].drop('unique_id', axis=1) 52 | plt.plot(plot_df['ds'], plot_df['y'], c='black', label='True') 53 | plt.plot(plot_df['ds'], plot_df[f'{model}-median'], c='blue', label='median') 54 | plt.fill_between(x=plot_df['ds'][-12:], 55 | y1=plot_df[f'{model}-lo-{level}'][-12:].values, 56 | y2=plot_df[f'{model}-hi-{level}'][-12:].values, 57 | alpha=0.4, label=f'level {level}') 58 | plt.legend() 59 | plt.grid() --------------------------------------------------------------------------------