Time Series Forecasting
Transformers
Safetensors
fela-pdm
feature-extraction
fela
fourier-neural-operator
fno
cpu
on-device
predictive-maintenance
time-series
anomaly-detection
custom_code
Instructions to use lowdown-labs/fela-pdm with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use lowdown-labs/fela-pdm with Transformers:
# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("lowdown-labs/fela-pdm", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
Commit ·
031d0d7
0
Parent(s):
initial commit
Browse files- .gitattributes +1 -0
- .gitignore +6 -0
- LICENSE +474 -0
- README.md +378 -0
- cmapss_FD001.safetensors +3 -0
- cmapss_FD002.safetensors +3 -0
- cmapss_FD003.safetensors +3 -0
- cmapss_FD004.safetensors +3 -0
- config.json +114 -0
- configuration_pdm.py +68 -0
- cwru.safetensors +3 -0
- model.safetensors +3 -0
- modeling.py +238 -0
- modeling_pdm.py +43 -0
- quickstart/README.md +40 -0
- quickstart/requirements.txt +6 -0
- quickstart/run.py +56 -0
- space/README.md +38 -0
- space/app.py +101 -0
- space/requirements.txt +5 -0
- streaming/cmapss_FD001_fp16.safetensors +3 -0
- streaming/cmapss_FD002_fp16.safetensors +3 -0
- streaming/cmapss_FD003_fp16.safetensors +3 -0
- streaming/cmapss_FD004_fp16.safetensors +3 -0
- streaming/cwru_fp16.safetensors +3 -0
- streaming/manifest.json +49 -0
- streaming/model_fp16.safetensors +3 -0
- train.py +280 -0
- verify.py +71 -0
.gitattributes
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.ruff_cache/
|
| 2 |
+
.venv/
|
| 3 |
+
__pycache__/
|
| 4 |
+
*.swp
|
| 5 |
+
*.kate-swp
|
| 6 |
+
*~
|
LICENSE
ADDED
|
@@ -0,0 +1,474 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Lowdown Labs Lovely License 1.0 (LLLL-1.0)
|
| 2 |
+
|
| 3 |
+
Everything in this repository (the model weights, the configuration, and the code) is
|
| 4 |
+
released by Lowdown Labs under two licenses that apply at the same time. To use this work
|
| 5 |
+
you must comply with BOTH of them. Where a term in one is stricter than the other, the
|
| 6 |
+
stricter term controls. "Lowdown Labs Lovely License 1.0" is a convenience name for this
|
| 7 |
+
exact pair; it is not a new legal instrument.
|
| 8 |
+
|
| 9 |
+
SPDX-License-Identifier: CC-BY-NC-4.0 AND LicenseRef-Hippocratic-3.0
|
| 10 |
+
|
| 11 |
+
Commercial licensing. The grant below is non-commercial only. Commercial licenses are
|
| 12 |
+
sold separately by Lowdown Labs on a per-customer basis. To use this work, its weights, or
|
| 13 |
+
its outputs for any commercial purpose, contact Lowdown Labs to purchase a commercial
|
| 14 |
+
license. A commercial license does not remove the Hippocratic ethical-use obligations in
|
| 15 |
+
Part 2; those apply to commercial licensees as well.
|
| 16 |
+
|
| 17 |
+
==============================================================================
|
| 18 |
+
Part 1 of 2. Creative Commons Attribution-NonCommercial 4.0 International (CC BY-NC 4.0)
|
| 19 |
+
==============================================================================
|
| 20 |
+
|
| 21 |
+
You may share and adapt this work for non-commercial purposes, with attribution to
|
| 22 |
+
Lowdown Labs. Commercial use is not granted under this license.
|
| 23 |
+
|
| 24 |
+
Full legal text: https://creativecommons.org/licenses/by-nc/4.0/legalcode
|
| 25 |
+
Plain-language summary: https://creativecommons.org/licenses/by-nc/4.0/
|
| 26 |
+
SPDX-License-Identifier: CC-BY-NC-4.0
|
| 27 |
+
|
| 28 |
+
==============================================================================
|
| 29 |
+
Part 2 of 2. The Hippocratic License 3.0 (ethical use)
|
| 30 |
+
==============================================================================
|
| 31 |
+
|
| 32 |
+
Module set enabled: bds, cl, eco, extr, ffd, law, media, mil, my, soc, sup, sv, usta.
|
| 33 |
+
Canonical build: https://firstdonoharm.dev/build/?modules=bds,cl,eco,extr,ffd,law,media,mil,my,soc,sup,sv,usta
|
| 34 |
+
|
| 35 |
+
The verbatim official Hippocratic License 3.0 text for exactly this module set follows,
|
| 36 |
+
between the markers.
|
| 37 |
+
|
| 38 |
+
--------------------- BEGIN OFFICIAL HIPPOCRATIC LICENSE 3.0 TEXT ---------------------
|
| 39 |
+
|
| 40 |
+
HIPPOCRATIC LICENSE
|
| 41 |
+
|
| 42 |
+
Version 3.0, October 2021
|
| 43 |
+
|
| 44 |
+
https://firstdonoharm.dev/version/3/0/bds-cl-eco-extr-ffd-law-media-mil-my-soc-sup-sv-usta.txt
|
| 45 |
+
|
| 46 |
+
TERMS AND CONDITIONS
|
| 47 |
+
|
| 48 |
+
TERMS AND CONDITIONS FOR USE, COPY, MODIFICATION, PREPARATION OF DERIVATIVE
|
| 49 |
+
WORK, REPRODUCTION, AND DISTRIBUTION:
|
| 50 |
+
|
| 51 |
+
1. DEFINITIONS:
|
| 52 |
+
|
| 53 |
+
This section defines certain terms used throughout this license agreement.
|
| 54 |
+
|
| 55 |
+
1.1. “License” means the terms and conditions, as stated herein, for use, copy,
|
| 56 |
+
modification, preparation of derivative work, reproduction, and distribution of
|
| 57 |
+
Software (as defined below).
|
| 58 |
+
|
| 59 |
+
1.2. “Licensor” means the copyright and/or patent owner or entity authorized by
|
| 60 |
+
the copyright and/or patent owner that is granting the License.
|
| 61 |
+
|
| 62 |
+
1.3. “Licensee” means the individual or entity exercising permissions granted by
|
| 63 |
+
this License, including the use, copy, modification, preparation of derivative
|
| 64 |
+
work, reproduction, and distribution of Software (as defined below).
|
| 65 |
+
|
| 66 |
+
1.4. “Software” means any copyrighted work, including but not limited to
|
| 67 |
+
software code, authored by Licensor and made available under this License.
|
| 68 |
+
|
| 69 |
+
1.5. “Supply Chain” means the sequence of processes involved in the production
|
| 70 |
+
and/or distribution of a commodity, good, or service offered by the Licensee.
|
| 71 |
+
|
| 72 |
+
1.6. “Supply Chain Impacted Party” or “Supply Chain Impacted Parties” means any
|
| 73 |
+
person(s) directly impacted by any of Licensee’s Supply Chain, including the
|
| 74 |
+
practices of all persons or entities within the Supply Chain prior to a good or
|
| 75 |
+
service reaching the Licensee.
|
| 76 |
+
|
| 77 |
+
1.7. “Duty of Care” is defined by its use in tort law, delict law, and/or
|
| 78 |
+
similar bodies of law closely related to tort and/or delict law, including
|
| 79 |
+
without limitation, a requirement to act with the watchfulness, attention,
|
| 80 |
+
caution, and prudence that a reasonable person in the same or similar
|
| 81 |
+
circumstances would use towards any Supply Chain Impacted Party.
|
| 82 |
+
|
| 83 |
+
1.8. “Worker” is defined to include any and all permanent, temporary, and agency
|
| 84 |
+
workers, as well as piece-rate, salaried, hourly paid, legal young (minors),
|
| 85 |
+
part-time, night, and migrant workers.
|
| 86 |
+
|
| 87 |
+
2. INTELLECTUAL PROPERTY GRANTS:
|
| 88 |
+
|
| 89 |
+
This section identifies intellectual property rights granted to a Licensee.
|
| 90 |
+
|
| 91 |
+
2.1. Grant of Copyright License: Subject to the terms and conditions of this
|
| 92 |
+
License, Licensor hereby grants to Licensee a worldwide, non-exclusive,
|
| 93 |
+
no-charge, royalty-free copyright license to use, copy, modify, prepare
|
| 94 |
+
derivative work, reproduce, or distribute the Software, Licensor authored
|
| 95 |
+
modified software, or other work derived from the Software.
|
| 96 |
+
|
| 97 |
+
2.2. Grant of Patent License: Subject to the terms and conditions of this
|
| 98 |
+
License, Licensor hereby grants Licensee a worldwide, non-exclusive, no-charge,
|
| 99 |
+
royalty-free patent license to make, have made, use, offer to sell, sell,
|
| 100 |
+
import, and otherwise transfer Software.
|
| 101 |
+
|
| 102 |
+
3. ETHICAL STANDARDS:
|
| 103 |
+
|
| 104 |
+
This section lists conditions the Licensee must comply with in order to have
|
| 105 |
+
rights under this License.
|
| 106 |
+
|
| 107 |
+
The rights granted to the Licensee by this License are expressly made subject to
|
| 108 |
+
the Licensee’s ongoing compliance with the following conditions:
|
| 109 |
+
|
| 110 |
+
* 3.1. The Licensee SHALL NOT, whether directly or indirectly, through agents
|
| 111 |
+
or assigns:
|
| 112 |
+
|
| 113 |
+
* 3.1.1. Infringe upon any person’s right to life or security of person,
|
| 114 |
+
engage in extrajudicial killings, or commit murder, without lawful cause
|
| 115 |
+
(See Article 3, United Nations Universal Declaration of Human Rights;
|
| 116 |
+
Article 6, International Covenant on Civil and Political Rights)
|
| 117 |
+
|
| 118 |
+
* 3.1.2. Hold any person in slavery, servitude, or forced labor (See Article
|
| 119 |
+
4, United Nations Universal Declaration of Human Rights; Article 8,
|
| 120 |
+
International Covenant on Civil and Political Rights);
|
| 121 |
+
|
| 122 |
+
* 3.1.3. Contribute to the institution of slavery, slave trading, forced
|
| 123 |
+
labor, or unlawful child labor (See Article 4, United Nations Universal
|
| 124 |
+
Declaration of Human Rights; Article 8, International Covenant on Civil and
|
| 125 |
+
Political Rights);
|
| 126 |
+
|
| 127 |
+
* 3.1.4. Torture or subject any person to cruel, inhumane, or degrading
|
| 128 |
+
treatment or punishment (See Article 5, United Nations Universal
|
| 129 |
+
Declaration of Human Rights; Article 7, International Covenant on Civil and
|
| 130 |
+
Political Rights);
|
| 131 |
+
|
| 132 |
+
* 3.1.5. Discriminate on the basis of sex, gender, sexual orientation, race,
|
| 133 |
+
ethnicity, nationality, religion, caste, age, medical disability or
|
| 134 |
+
impairment, and/or any other like circumstances (See Article 7, United
|
| 135 |
+
Nations Universal Declaration of Human Rights; Article 2, International
|
| 136 |
+
Covenant on Economic, Social and Cultural Rights; Article 26, International
|
| 137 |
+
Covenant on Civil and Political Rights);
|
| 138 |
+
|
| 139 |
+
* 3.1.6. Prevent any person from exercising his/her/their right to seek an
|
| 140 |
+
effective remedy by a competent court or national tribunal (including
|
| 141 |
+
domestic judicial systems, international courts, arbitration bodies, and
|
| 142 |
+
other adjudicating bodies) for actions violating the fundamental rights
|
| 143 |
+
granted to him/her/them by applicable constitutions, applicable laws, or by
|
| 144 |
+
this License (See Article 8, United Nations Universal Declaration of Human
|
| 145 |
+
Rights; Articles 9 and 14, International Covenant on Civil and Political
|
| 146 |
+
Rights);
|
| 147 |
+
|
| 148 |
+
* 3.1.7. Subject any person to arbitrary arrest, detention, or exile (See
|
| 149 |
+
Article 9, United Nations Universal Declaration of Human Rights; Article 9,
|
| 150 |
+
International Covenant on Civil and Political Rights);
|
| 151 |
+
|
| 152 |
+
* 3.1.8. Subject any person to arbitrary interference with a person’s
|
| 153 |
+
privacy, family, home, or correspondence without the express written
|
| 154 |
+
consent of the person (See Article 12, United Nations Universal Declaration
|
| 155 |
+
of Human Rights; Article 17, International Covenant on Civil and Political
|
| 156 |
+
Rights);
|
| 157 |
+
|
| 158 |
+
* 3.1.9. Arbitrarily deprive any person of his/her/their property (See
|
| 159 |
+
Article 17, United Nations Universal Declaration of Human Rights);
|
| 160 |
+
|
| 161 |
+
* 3.1.10. Forcibly remove indigenous peoples from their lands or territories
|
| 162 |
+
or take any action with the aim or effect of dispossessing indigenous
|
| 163 |
+
peoples from their lands, territories, or resources, including without
|
| 164 |
+
limitation the intellectual property or traditional knowledge of indigenous
|
| 165 |
+
peoples, without the free, prior, and informed consent of indigenous
|
| 166 |
+
peoples concerned (See Articles 8 and 10, United Nations Declaration on the
|
| 167 |
+
Rights of Indigenous Peoples);
|
| 168 |
+
* 3.1.11. Fossil Fuel Divestment: Be an individual or entity, or a
|
| 169 |
+
representative, agent, affiliate, successor, attorney, or assign of an
|
| 170 |
+
individual or entity, on the FFI Solutions Carbon Underground 200 list
|
| 171 |
+
[https://www.ffisolutions.com/research-analytics-index-solutions/research-screening/the-carbon-underground-200/?cn-reloaded=1];
|
| 172 |
+
|
| 173 |
+
* 3.1.12. Ecocide: Commit ecocide:
|
| 174 |
+
|
| 175 |
+
* 3.1.12.1. For the purpose of this section, “ecocide” means unlawful or
|
| 176 |
+
wanton acts committed with knowledge that there is a substantial
|
| 177 |
+
likelihood of severe and either widespread or long-term damage to the
|
| 178 |
+
environment being caused by those acts;
|
| 179 |
+
|
| 180 |
+
* 3.1.12.2. For the purpose of further defining ecocide and the terms
|
| 181 |
+
contained in the previous paragraph:
|
| 182 |
+
|
| 183 |
+
* 3.1.12.2.1. “Wanton” means with reckless disregard for damage which
|
| 184 |
+
would be clearly excessive in relation to the social and economic
|
| 185 |
+
benefits anticipated;
|
| 186 |
+
|
| 187 |
+
* 3.1.12.2.2. “Severe” means damage which involves very serious adverse
|
| 188 |
+
changes, disruption, or harm to any element of the environment,
|
| 189 |
+
including grave impacts on human life or natural, cultural, or
|
| 190 |
+
economic resources;
|
| 191 |
+
|
| 192 |
+
* 3.1.12.2.3. “Widespread” means damage which extends beyond a limited
|
| 193 |
+
geographic area, crosses state boundaries, or is suffered by an entire
|
| 194 |
+
ecosystem or species or a large number of human beings;
|
| 195 |
+
|
| 196 |
+
* 3.1.12.2.4. “Long-term” means damage which is irreversible or which
|
| 197 |
+
cannot be redressed through natural recovery within a reasonable
|
| 198 |
+
period of time; and
|
| 199 |
+
|
| 200 |
+
* 3.1.12.2.5. “Environment” means the earth, its biosphere, cryosphere,
|
| 201 |
+
lithosphere, hydrosphere, and atmosphere, as well as outer space
|
| 202 |
+
|
| 203 |
+
(See Section II, Independent Expert Panel for the Legal Definition of
|
| 204 |
+
Ecocide, Stop Ecocide Foundation and the Promise Institute for Human
|
| 205 |
+
Rights at UCLA School of Law, June 2021);
|
| 206 |
+
|
| 207 |
+
* 3.1.13. Extractive Industries: Be an individual or entity, or a
|
| 208 |
+
representative, agent, affiliate, successor, attorney, or assign of an
|
| 209 |
+
individual or entity, that engages in fossil fuel or mineral exploration,
|
| 210 |
+
extraction, development, or sale;
|
| 211 |
+
|
| 212 |
+
* 3.1.14. Boycott / Divestment / Sanctions: Be an individual or entity, or a
|
| 213 |
+
representative, agent, affiliate, successor, attorney, or assign of an
|
| 214 |
+
individual or entity, identified by the Boycott, Divestment, Sanctions
|
| 215 |
+
(“BDS”) movement on its website (https://bdsmovement.net/
|
| 216 |
+
[https://bdsmovement.net/] and
|
| 217 |
+
https://bdsmovement.net/get-involved/what-to-boycott
|
| 218 |
+
[https://bdsmovement.net/get-involved/what-to-boycott]) as a target for
|
| 219 |
+
boycott;
|
| 220 |
+
|
| 221 |
+
* 3.1.15. Myanmar: Be an individual or entity that:
|
| 222 |
+
|
| 223 |
+
* 3.1.15.1. engages in any commercial transactions with the
|
| 224 |
+
Myanmar/Burmese military junta; or
|
| 225 |
+
|
| 226 |
+
* 3.1.15.2. is a representative, agent, affiliate, successor, attorney, or
|
| 227 |
+
assign of the Myanmar/Burmese government;
|
| 228 |
+
|
| 229 |
+
* 3.1.16. US Tariff Act: Be an individual or entity:
|
| 230 |
+
|
| 231 |
+
* 3.1.16.1. which U.S. Customs and Border Protection (CBP) has currently
|
| 232 |
+
issued a Withhold Release Order (WRO) or finding against based on
|
| 233 |
+
reasonable suspicion of forced labor; or
|
| 234 |
+
|
| 235 |
+
* 3.1.16.2. that is a representative, agent, affiliate, successor,
|
| 236 |
+
attorney, or assign of an individual or entity that does business with
|
| 237 |
+
an individual or entity which currently has a WRO or finding from CBP
|
| 238 |
+
issued against it based on reasonable suspicion of forced labor;
|
| 239 |
+
|
| 240 |
+
* 3.1.17. Mass Surveillance: Be a government agency or multinational
|
| 241 |
+
corporation, or a representative, agent, affiliate, successor, attorney,
|
| 242 |
+
or assign of a government or multinational corporation, which participates
|
| 243 |
+
in mass surveillance programs;
|
| 244 |
+
|
| 245 |
+
* 3.1.18. Military Activities: Be an entity or a representative, agent,
|
| 246 |
+
affiliate, successor, attorney, or assign of an entity which conducts
|
| 247 |
+
military activities;
|
| 248 |
+
|
| 249 |
+
* 3.1.19. Law Enforcement: Be an individual or entity, or a representative,
|
| 250 |
+
agent, affiliate, successor, attorney, or assign of an individual or
|
| 251 |
+
entity, that provides good or services to, or otherwise enters into any
|
| 252 |
+
commercial contracts with, any local, state, or federal law enforcement
|
| 253 |
+
agency;
|
| 254 |
+
|
| 255 |
+
* 3.1.20. Media: Be an individual or entity, or a representative, agent,
|
| 256 |
+
affiliate, successor, attorney, or assign of an individual or entity, that
|
| 257 |
+
broadcasts messages promoting killing, torture, or other forms of extreme
|
| 258 |
+
violence;
|
| 259 |
+
|
| 260 |
+
* 3.1.21. Interfere with Workers’ free exercise of the right to organize and
|
| 261 |
+
associate (See Article 20, United Nations Universal Declaration of Human
|
| 262 |
+
Rights; C087 - Freedom of Association and Protection of the Right to
|
| 263 |
+
Organise Convention, 1948 (No. 87), International Labour Organization;
|
| 264 |
+
Article 8, International Covenant on Economic, Social and Cultural Rights);
|
| 265 |
+
and
|
| 266 |
+
|
| 267 |
+
* 3.1.22. Harm the environment in a manner inconsistent with local, state,
|
| 268 |
+
national, or international law.
|
| 269 |
+
|
| 270 |
+
* 3.2. The Licensee SHALL:
|
| 271 |
+
|
| 272 |
+
* 3.2.1. Social Auditing: Only use social auditing mechanisms that adhere to
|
| 273 |
+
Worker-Driven Social Responsibility Network’s Statement of Principles
|
| 274 |
+
(https://wsr-network.org/what-is-wsr/statement-of-principles/
|
| 275 |
+
[https://wsr-network.org/what-is-wsr/statement-of-principles/]) over
|
| 276 |
+
traditional social auditing mechanisms, to the extent the Licensee uses
|
| 277 |
+
any social auditing mechanisms at all;
|
| 278 |
+
|
| 279 |
+
* 3.2.2. Supply Chain: Provide clear, accessible supply chain data to the
|
| 280 |
+
public in accordance with the following conditions:
|
| 281 |
+
|
| 282 |
+
* 3.2.2.1. All data will be on Licensee’s website and/or, to the extent
|
| 283 |
+
Licensee is a representative, agent, affiliate, successor, attorney,
|
| 284 |
+
subsidiary, or assign, on Licensee’s principal’s or parent’s website or
|
| 285 |
+
some other online platform accessible to the public via an internet
|
| 286 |
+
search on a common internet search engine; and
|
| 287 |
+
|
| 288 |
+
* 3.2.2.2. Data published will include, where applicable, manufacturers,
|
| 289 |
+
top tier suppliers, subcontractors, cooperatives, component parts
|
| 290 |
+
producers, and farms;
|
| 291 |
+
|
| 292 |
+
* 3.2.3. Provide equal pay for equal work where the performance of such work
|
| 293 |
+
requires equal skill, effort, and responsibility, and which are performed
|
| 294 |
+
under similar working conditions, except where such payment is made
|
| 295 |
+
pursuant to:
|
| 296 |
+
|
| 297 |
+
* 3.2.3.1. A seniority system;
|
| 298 |
+
|
| 299 |
+
* 3.2.3.2. A merit system;
|
| 300 |
+
|
| 301 |
+
* 3.2.3.3. A system which measures earnings by quantity or quality of
|
| 302 |
+
production; or
|
| 303 |
+
|
| 304 |
+
* 3.2.3.4. A differential based on any other factor other than sex, gender,
|
| 305 |
+
sexual orientation, race, ethnicity, nationality, religion, caste, age,
|
| 306 |
+
medical disability or impairment, and/or any other like circumstances
|
| 307 |
+
(See 29 U.S.C.A. § 206(d)(1); Article 23, United Nations Universal
|
| 308 |
+
Declaration of Human Rights; Article 7, International Covenant on
|
| 309 |
+
Economic, Social and Cultural Rights; Article 26, International Covenant
|
| 310 |
+
on Civil and Political Rights); and
|
| 311 |
+
|
| 312 |
+
* 3.2.4. Allow for reasonable limitation of working hours and periodic
|
| 313 |
+
holidays with pay (See Article 24, United Nations Universal Declaration of
|
| 314 |
+
Human Rights; Article 7, International Covenant on Economic, Social and
|
| 315 |
+
Cultural Rights).
|
| 316 |
+
|
| 317 |
+
4. SUPPLY CHAIN IMPACTED PARTIES:
|
| 318 |
+
|
| 319 |
+
This section identifies additional individuals or entities that a Licensee could
|
| 320 |
+
harm as a result of violating the Ethical Standards section, the condition that
|
| 321 |
+
the Licensee must voluntarily accept a Duty of Care for those individuals or
|
| 322 |
+
entities, and the right to a private right of action that those individuals or
|
| 323 |
+
entities possess as a result of violations of the Ethical Standards section.
|
| 324 |
+
|
| 325 |
+
4.1. In addition to the above Ethical Standards, Licensee voluntarily accepts a
|
| 326 |
+
Duty of Care for Supply Chain Impacted Parties of this License, including
|
| 327 |
+
individuals and communities impacted by violations of the Ethical Standards. The
|
| 328 |
+
Duty of Care is breached when a provision within the Ethical Standards section
|
| 329 |
+
is violated by a Licensee, one of its successors or assigns, or by an individual
|
| 330 |
+
or entity that exists within the Supply Chain prior to a good or service
|
| 331 |
+
reaching the Licensee.
|
| 332 |
+
|
| 333 |
+
4.2. Breaches of the Duty of Care, as stated within this section, shall create a
|
| 334 |
+
private right of action, allowing any Supply Chain Impacted Party harmed by the
|
| 335 |
+
Licensee to take legal action against the Licensee in accordance with applicable
|
| 336 |
+
negligence laws, whether they be in tort law, delict law, and/or similar bodies
|
| 337 |
+
of law closely related to tort and/or delict law, regardless if Licensee is
|
| 338 |
+
directly responsible for the harms suffered by a Supply Chain Impacted Party.
|
| 339 |
+
Nothing in this section shall be interpreted to include acts committed by
|
| 340 |
+
individuals outside of the scope of his/her/their employment.
|
| 341 |
+
|
| 342 |
+
5. NOTICE: This section explains when a Licensee must notify others of the
|
| 343 |
+
License.
|
| 344 |
+
|
| 345 |
+
5.1. Distribution of Notice: Licensee must ensure that everyone who receives a
|
| 346 |
+
copy of or uses any part of Software from Licensee, with or without changes,
|
| 347 |
+
also receives the License and the copyright notice included with Software (and
|
| 348 |
+
if included by the Licensor, patent, trademark, and attribution notice).
|
| 349 |
+
Licensee must ensure that License is prominently displayed so that any
|
| 350 |
+
individual or entity seeking to download, copy, use, or otherwise receive any
|
| 351 |
+
part of Software from Licensee is notified of this License and its terms and
|
| 352 |
+
conditions. Licensee must cause any modified versions of the Software to carry
|
| 353 |
+
prominent notices stating that Licensee changed the Software.
|
| 354 |
+
|
| 355 |
+
5.2. Modified Software: Licensee is free to create modifications of the Software
|
| 356 |
+
and distribute only the modified portion created by Licensee, however, any
|
| 357 |
+
derivative work stemming from the Software or its code must be distributed
|
| 358 |
+
pursuant to this License, including this Notice provision.
|
| 359 |
+
|
| 360 |
+
5.3. Recipients as Licensees: Any individual or entity that uses, copies,
|
| 361 |
+
modifies, reproduces, distributes, or prepares derivative work based upon the
|
| 362 |
+
Software, all or part of the Software’s code, or a derivative work developed by
|
| 363 |
+
using the Software, including a portion of its code, is a Licensee as defined
|
| 364 |
+
above and is subject to the terms and conditions of this License.
|
| 365 |
+
|
| 366 |
+
6. REPRESENTATIONS AND WARRANTIES:
|
| 367 |
+
|
| 368 |
+
6.1. Disclaimer of Warranty: TO THE FULL EXTENT ALLOWED BY LAW, THIS SOFTWARE
|
| 369 |
+
COMES “AS IS,” WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED, AND LICENSOR SHALL NOT
|
| 370 |
+
BE LIABLE TO ANY PERSON OR ENTITY FOR ANY DAMAGES OR OTHER LIABILITY ARISING
|
| 371 |
+
FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THIS LICENSE, UNDER ANY
|
| 372 |
+
LEGAL CLAIM.
|
| 373 |
+
|
| 374 |
+
6.2. Limitation of Liability: LICENSEE SHALL HOLD LICENSOR HARMLESS AGAINST ANY
|
| 375 |
+
AND ALL CLAIMS, DEBTS, DUES, LIABILITIES, LIENS, CAUSES OF ACTION, DEMANDS,
|
| 376 |
+
OBLIGATIONS, DISPUTES, DAMAGES, LOSSES, EXPENSES, ATTORNEYS’ FEES, COSTS,
|
| 377 |
+
LIABILITIES, AND ALL OTHER CLAIMS OF EVERY KIND AND NATURE WHATSOEVER, WHETHER
|
| 378 |
+
KNOWN OR UNKNOWN, ANTICIPATED OR UNANTICIPATED, FORESEEN OR UNFORESEEN, ACCRUED
|
| 379 |
+
OR UNACCRUED, DISCLOSED OR UNDISCLOSED, ARISING OUT OF OR RELATING TO LICENSEE’S
|
| 380 |
+
USE OF THE SOFTWARE. NOTHING IN THIS SECTION SHOULD BE INTERPRETED TO REQUIRE
|
| 381 |
+
LICENSEE TO INDEMNIFY LICENSOR, NOR REQUIRE LICENSOR TO INDEMNIFY LICENSEE.
|
| 382 |
+
|
| 383 |
+
7. TERMINATION
|
| 384 |
+
|
| 385 |
+
7.1. Violations of Ethical Standards or Breaching Duty of Care: If Licensee
|
| 386 |
+
violates the Ethical Standards section or Licensee, or any other person or
|
| 387 |
+
entity within the Supply Chain prior to a good or service reaching the Licensee,
|
| 388 |
+
breaches its Duty of Care to Supply Chain Impacted Parties, Licensee must remedy
|
| 389 |
+
the violation or harm caused by Licensee within 30 days of being notified of the
|
| 390 |
+
violation or harm. If Licensee fails to remedy the violation or harm within 30
|
| 391 |
+
days, all rights in the Software granted to Licensee by License will be null and
|
| 392 |
+
void as between Licensor and Licensee.
|
| 393 |
+
|
| 394 |
+
7.2. Failure of Notice: If any person or entity notifies Licensee in writing
|
| 395 |
+
that Licensee has not complied with the Notice section of this License, Licensee
|
| 396 |
+
can keep this License by taking all practical steps to comply within 30 days
|
| 397 |
+
after the notice of noncompliance. If Licensee does not do so, Licensee’s
|
| 398 |
+
License (and all rights licensed hereunder) will end immediately.
|
| 399 |
+
|
| 400 |
+
7.3. Judicial Findings: In the event Licensee is found by a civil, criminal,
|
| 401 |
+
administrative, or other court of competent jurisdiction, or some other
|
| 402 |
+
adjudicating body with legal authority, to have committed actions which are in
|
| 403 |
+
violation of the Ethical Standards or Supply Chain Impacted Party sections of
|
| 404 |
+
this License, all rights granted to Licensee by this License will terminate
|
| 405 |
+
immediately.
|
| 406 |
+
|
| 407 |
+
7.4. Patent Litigation: If Licensee institutes patent litigation against any
|
| 408 |
+
entity (including a cross-claim or counterclaim in a suit) alleging that the
|
| 409 |
+
Software, all or part of the Software’s code, or a derivative work developed
|
| 410 |
+
using the Software, including a portion of its code, constitutes direct or
|
| 411 |
+
contributory patent infringement, then any patent license, along with all other
|
| 412 |
+
rights, granted to Licensee under this License will terminate as of the date
|
| 413 |
+
such litigation is filed.
|
| 414 |
+
|
| 415 |
+
7.5. Additional Remedies: Termination of the License by failing to remedy harms
|
| 416 |
+
in no way prevents Licensor or Supply Chain Impacted Party from seeking
|
| 417 |
+
appropriate remedies at law or in equity.
|
| 418 |
+
|
| 419 |
+
8. MISCELLANEOUS:
|
| 420 |
+
|
| 421 |
+
8.1. Conditions: Sections 3, 4.1, 5.1, 5.2, 7.1, 7.2, 7.3, and 7.4 are
|
| 422 |
+
conditions of the rights granted to Licensee in the License.
|
| 423 |
+
|
| 424 |
+
8.2. Equitable Relief: Licensor and any Supply Chain Impacted Party shall be
|
| 425 |
+
entitled to equitable relief, including injunctive relief or specific
|
| 426 |
+
performance of the terms hereof, in addition to any other remedy to which they
|
| 427 |
+
are entitled at law or in equity.
|
| 428 |
+
|
| 429 |
+
8.3. Copyleft: Modified software, source code, or other derivative work must be
|
| 430 |
+
licensed, in its entirety, under the exact same conditions as this License.
|
| 431 |
+
|
| 432 |
+
8.4. Severability: If any term or provision of this License is determined to be
|
| 433 |
+
invalid, illegal, or unenforceable by a court of competent jurisdiction, any
|
| 434 |
+
such determination of invalidity, illegality, or unenforceability shall not
|
| 435 |
+
affect any other term or provision of this License or invalidate or render
|
| 436 |
+
unenforceable such term or provision in any other jurisdiction. If the
|
| 437 |
+
determination of invalidity, illegality, or unenforceability by a court of
|
| 438 |
+
competent jurisdiction pertains to the terms or provisions contained in the
|
| 439 |
+
Ethical Standards section of this License, all rights in the Software granted to
|
| 440 |
+
Licensee shall be deemed null and void as between Licensor and Licensee.
|
| 441 |
+
|
| 442 |
+
8.5. Section Titles: Section titles are solely written for organizational
|
| 443 |
+
purposes and should not be used to interpret the language within each section.
|
| 444 |
+
|
| 445 |
+
8.6. Citations: Citations are solely written to provide context for the source
|
| 446 |
+
of the provisions in the Ethical Standards.
|
| 447 |
+
|
| 448 |
+
8.7. Section Summaries: Some sections have a brief italicized description which
|
| 449 |
+
is provided for the sole purpose of briefly describing the section and should
|
| 450 |
+
not be used to interpret the terms of the License.
|
| 451 |
+
|
| 452 |
+
8.8. Entire License: This is the entire License between the Licensor and
|
| 453 |
+
Licensee with respect to the claims released herein and that the consideration
|
| 454 |
+
stated herein is the only consideration or compensation to be paid or exchanged
|
| 455 |
+
between them for this License. This License cannot be modified or amended except
|
| 456 |
+
in a writing signed by Licensor and Licensee.
|
| 457 |
+
|
| 458 |
+
8.9. Successors and Assigns: This License shall be binding upon and inure to the
|
| 459 |
+
benefit of the Licensor’s and Licensee’s respective heirs, successors, and
|
| 460 |
+
assigns.
|
| 461 |
+
---------------------- END OFFICIAL HIPPOCRATIC LICENSE 3.0 TEXT ----------------------
|
| 462 |
+
|
| 463 |
+
==============================================================================
|
| 464 |
+
Attribution, commercial use, and warranty
|
| 465 |
+
==============================================================================
|
| 466 |
+
|
| 467 |
+
Attribution: cite this work as described in the model card (README.md), section
|
| 468 |
+
"How to cite". Attribution to Lowdown Labs is required under CC BY-NC 4.0.
|
| 469 |
+
|
| 470 |
+
Commercial use: CC BY-NC 4.0 does not grant commercial rights. Commercial licenses are
|
| 471 |
+
sold by Lowdown Labs; contact Lowdown Labs to purchase one.
|
| 472 |
+
|
| 473 |
+
No warranty: this work is provided as is, without warranty of any kind. See the model card
|
| 474 |
+
for the intended use, the evaluated conditions, and the known limitations.
|
README.md
ADDED
|
@@ -0,0 +1,378 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: other
|
| 3 |
+
license_name: lowdown-labs-lovely-license-1.0
|
| 4 |
+
license_link: LICENSE
|
| 5 |
+
tags:
|
| 6 |
+
- fela
|
| 7 |
+
- fourier-neural-operator
|
| 8 |
+
- fno
|
| 9 |
+
- cpu
|
| 10 |
+
- on-device
|
| 11 |
+
- predictive-maintenance
|
| 12 |
+
- time-series
|
| 13 |
+
- anomaly-detection
|
| 14 |
+
library_name: transformers
|
| 15 |
+
pipeline_tag: time-series-forecasting
|
| 16 |
+
---
|
| 17 |
+
|
| 18 |
+
# DISCLAIMER
|
| 19 |
+
|
| 20 |
+
This model is a research preview. The CWRU bearing dataset publishes no explicit license and
|
| 21 |
+
grants no commercial use rights, so respect that before any commercial use. Lowdown Labs has
|
| 22 |
+
put together this model in the interest of advancing public science.
|
| 23 |
+
|
| 24 |
+
# FELA-PdM: on device predictive maintenance for rotating machines
|
| 25 |
+
|
| 26 |
+
FELA-PdM watches the raw signal from a vibration or sensor stream on a machine (a bearing,
|
| 27 |
+
motor, pump, gearbox, or engine) and tells a maintenance team two things: what is wrong, and
|
| 28 |
+
how much longer the machine is likely to keep running.
|
| 29 |
+
|
| 30 |
+
It is small enough to run on a
|
| 31 |
+
$3 to $10 microcontroller sitting next to the sensor, so a plant does not have to stream raw
|
| 32 |
+
data to the cloud.
|
| 33 |
+
|
| 34 |
+
# What goes in, what comes out
|
| 35 |
+
|
| 36 |
+
There are two trained tasks. Pick by the question you are asking. The bearing fault task ships as
|
| 37 |
+
one head (CWRU); the remaining useful life task ships as four heads, one per C-MAPSS subset
|
| 38 |
+
(FD001 to FD004).
|
| 39 |
+
|
| 40 |
+
- Bearing fault classification (trained on CWRU): in is a window of vibration samples from an
|
| 41 |
+
accelerometer, shape `(1, 2048, 1)` (2048 raw samples, one channel, sampled at 12 kHz).
|
| 42 |
+
Out is a fault class, one of healthy, inner race defect, rolling element (ball) defect, or
|
| 43 |
+
outer race defect, at one of three defect sizes (0.007, 0.014, 0.021 inch); 10 classes
|
| 44 |
+
total. In plain terms: "this bearing has an inner race defect" or "this bearing is healthy."
|
| 45 |
+
- Remaining useful life (trained on NASA C-MAPSS turbofan): in is a short history of per cycle
|
| 46 |
+
sensor readings, shape `(1, 30, 14)` (30 cycles, 14 sensors). Out is an estimate of how many
|
| 47 |
+
operating cycles remain before failure. A reliability engineer reads it as "this unit has
|
| 48 |
+
roughly N cycles left, plan the swap."
|
| 49 |
+
|
| 50 |
+
# Why we built it this way
|
| 51 |
+
|
| 52 |
+
The sequence mixer is a Fourier Neural Operator, a filter the model learns and applies in the
|
| 53 |
+
frequency domain. A failing bearing or gear shows up as periodic, high frequency vibration, and
|
| 54 |
+
reading frequencies is exactly what this kind of operator does well, so it fits the problem. There
|
| 55 |
+
is no all pairs attention, so the working memory stays small and fixed however long the machine
|
| 56 |
+
runs. That is what lets it sit on a cheap microcontroller next to the sensor, on a battery or
|
| 57 |
+
panel powered node, with no cloud connection.
|
| 58 |
+
|
| 59 |
+
# Performance
|
| 60 |
+
|
| 61 |
+
Speed and footprint, measured on CPU (AMD EPYC 9555, batch size 1, median of 20 runs).
|
| 62 |
+
|
| 63 |
+
## Bearing fault (CWRU), input `(1, 2048, 1)`
|
| 64 |
+
|
| 65 |
+
| Format | Size on disk | Peak working RAM | Latency 1 core | Latency 4 core | Device class |
|
| 66 |
+
|---|---|---|---|---|---|
|
| 67 |
+
| fp32 | 0.53 MB | 0 MB | 2.358 ms | 2.955 ms | Microcontroller (STM32H7 / ESP32-S3) class |
|
| 68 |
+
|
| 69 |
+
## Remaining useful life (C-MAPSS FD001), input `(1, 30, 14)`
|
| 70 |
+
|
| 71 |
+
| Format | Size on disk | Peak working RAM | Latency 1 core | Latency 4 core | Device class |
|
| 72 |
+
|---|---|---|---|---|---|
|
| 73 |
+
| fp32 | 0.5 MB | 0 MB | 0.405 ms | 0.722 ms | Microcontroller (STM32H7 / ESP32-S3) class |
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
int8 here compresses about 2.5 to 2.8x rather than the full 4x, because the learned Fourier
|
| 77 |
+
filters are kept in fp32 and only the linear layers are quantized. We expect that
|
| 78 |
+
quantizing the spectral filters hurts accuracy for little size gain at this scale.
|
| 79 |
+
|
| 80 |
+
# Accuracy
|
| 81 |
+
|
| 82 |
+
Numbers below are from our own training runs on the public datasets, on CPU. The "published
|
| 83 |
+
range" column is the typical range reported in the literature for the same protocol, given for
|
| 84 |
+
context, not as a controlled head to head.
|
| 85 |
+
|
| 86 |
+
## Bearing fault classification (CWRU, 12 kHz drive end)
|
| 87 |
+
|
| 88 |
+
Protocol: 10 class problem (healthy plus inner race, ball, and outer race faults at three
|
| 89 |
+
defect diameters), all four motor loads pooled, raw vibration windows of 2048 samples with
|
| 90 |
+
50 percent overlap, random 75/25 train/test split, per signal normalization. This is the
|
| 91 |
+
common CWRU window split protocol.
|
| 92 |
+
|
| 93 |
+
| Model | Metric | This model | Published range | Source |
|
| 94 |
+
|---|---|---|---|---|
|
| 95 |
+
| FELA-PdM (pure FNO) | test accuracy | 100.0% | 98 to 100% | measured (ours) |
|
| 96 |
+
| FELA-PdM (FNO + GLA) | test accuracy | 100.0% | 98 to 100% | measured (ours) |
|
| 97 |
+
|
| 98 |
+
The window split CWRU benchmark is close to saturated in the literature; strong models
|
| 99 |
+
routinely report 99 to 100 percent. FELA-PdM reaches the ceiling with a 132.6 thousand
|
| 100 |
+
parameter model. This protocol is known to be optimistic, because windows from the same
|
| 101 |
+
recording can land in both the train and the test set. Harder cross load and
|
| 102 |
+
cross fault size protocols were not run and are listed under Limitations.
|
| 103 |
+
|
| 104 |
+
## Remaining useful life (NASA C-MAPSS turbofan)
|
| 105 |
+
|
| 106 |
+
Protocol: 14 informative sensors, min max normalized on the training set, sliding window of
|
| 107 |
+
30 cycles, piecewise linear remaining useful life target capped at 125 cycles (the common
|
| 108 |
+
Heimes convention). Metric is RMSE in cycles on the official test set (one prediction per
|
| 109 |
+
test engine at its last available cycle), and the NASA PHM08 asymmetric score (lower is
|
| 110 |
+
better, late predictions penalized more).
|
| 111 |
+
|
| 112 |
+
| Subset | Metric | FELA-PdM RMSE | FELA-PdM score | Published RMSE range | Source |
|
| 113 |
+
|---|---|---|---|---|---|
|
| 114 |
+
| FD001 | RMSE / PHM08 score | 11.16 | 192 | 11 to 18 (CNN ~18.4, LSTM ~16.1, recent transformers ~11 to 13) | measured (ours) |
|
| 115 |
+
| FD002 | RMSE / PHM08 score | 19.64 | 2041 | 17 to 24 | measured (ours) |
|
| 116 |
+
| FD003 | RMSE / PHM08 score | 11.68 | 357 | 12 to 17 | measured (ours) |
|
| 117 |
+
| FD004 | RMSE / PHM08 score | 19.45 | 2217 | 19 to 25 | measured (ours) |
|
| 118 |
+
|
| 119 |
+
FD001 (single operating condition, single fault mode) is the canonical benchmark. FELA-PdM
|
| 120 |
+
reaches 11.16 RMSE, at the strong end of the published range and ahead of the classic CNN and
|
| 121 |
+
LSTM baselines, with a 124.5 thousand parameter model. FD003 (single condition) matches that
|
| 122 |
+
strong result. FD002 and FD004 (six operating conditions) are harder; those numbers sit inside
|
| 123 |
+
the published band rather than ahead of it. The FD002 to FD004 numbers were measured with the
|
| 124 |
+
same recipe as FD001 (pure FNO, 40 epochs, seed 0). All four C-MAPSS heads (FD001 to FD004) ship
|
| 125 |
+
as separate safetensors files, so every row above loads and reproduces from the shipped weights.
|
| 126 |
+
|
| 127 |
+
# How to run it
|
| 128 |
+
|
| 129 |
+
See `quickstart/` for a runnable example. The short version:
|
| 130 |
+
|
| 131 |
+
```python
|
| 132 |
+
from modeling import load_model
|
| 133 |
+
# a directory holding <variant>.safetensors + config.json (or a Hugging Face repo id):
|
| 134 |
+
m = load_model("/path/to/weights_dir", variant="cmapss_FD001")
|
| 135 |
+
window = ... # (1, 30 cycles, 14 sensors); see modeling.preprocess_cmapss
|
| 136 |
+
remaining_cycles = m.predict(window) # remaining useful life estimate
|
| 137 |
+
```
|
| 138 |
+
|
| 139 |
+
Pass `variant="cwru"` instead to load the bearing fault head, or `variant="cmapss_FD002"` (through
|
| 140 |
+
`FD004`) for the other C-MAPSS subsets. The weights ship one safetensors file per head
|
| 141 |
+
(`cmapss_FD001.safetensors` through `cmapss_FD004.safetensors`, and `cwru.safetensors`) beside
|
| 142 |
+
`config.json`. For an interactive playground, see the Hugging Face Space in `space/`.
|
| 143 |
+
|
| 144 |
+
## Formats
|
| 145 |
+
|
| 146 |
+
- fp32: reference and CPU.
|
| 147 |
+
- int8: on device deployment format (AVX512-VNNI on x86, NEON dot product on ARM, and the
|
| 148 |
+
only realistic format on a microcontroller). About 0.21 MB (bearing) and 0.18 MB (RUL).
|
| 149 |
+
- bf16: server and GPU inference only; most commodity ARM and microcontroller CPUs lack
|
| 150 |
+
native bf16, so it is not the on device format.
|
| 151 |
+
|
| 152 |
+
# Training data
|
| 153 |
+
|
| 154 |
+
- CWRU bearing dataset (Case Western Reserve University Bearing Data Center): 12 kHz
|
| 155 |
+
drive end vibration recordings, four motor loads, used for the fault classifier. Public
|
| 156 |
+
research dataset.
|
| 157 |
+
- NASA C-MAPSS turbofan degradation simulation (Saxena et al. 2008), subsets FD001 to FD004,
|
| 158 |
+
used for the remaining useful life regressor. Public NASA dataset.
|
| 159 |
+
- MIMII (Purohit et al. 2019), machine sound, valve 6 dB subset: loader implemented, acoustic
|
| 160 |
+
head not shipped and not measured.
|
| 161 |
+
|
| 162 |
+
## Training data, splits and licensing
|
| 163 |
+
|
| 164 |
+
The training and evaluation splits are defined in `train.py` in this repo, which covers all five
|
| 165 |
+
trained variants (C-MAPSS FD001 to FD004 plus CWRU). Both loaders and the exact split boundaries
|
| 166 |
+
are reproduced there, and a `--smoke` flag rebuilds each split, asserts the audited window count,
|
| 167 |
+
and exits before training.
|
| 168 |
+
|
| 169 |
+
### CWRU bearing fault (classifier)
|
| 170 |
+
|
| 171 |
+
- Dataset: Case Western Reserve University Bearing Data Center, 12 kHz Drive End (DE_time)
|
| 172 |
+
vibration recordings. Version: the standard 40 file, 10 class, four motor load collection
|
| 173 |
+
(Normal plus inner race, ball, and outer race faults at 0.007 / 0.014 / 0.021 inch).
|
| 174 |
+
- Source: https://engineering.case.edu/bearingdatacenter
|
| 175 |
+
- Split: sliding windows of 2048 samples, stride 1024, per signal z normalization, all four
|
| 176 |
+
loads pooled; random 75/25 train/test split, seed 0. Total 5886 windows to 4415 train /
|
| 177 |
+
1471 test, 10 classes. Split defined in train.py line 106 (the assertion `len(x) == 5886`
|
| 178 |
+
after `cwru_split`).
|
| 179 |
+
- License: NO explicit license is published by CWRU for this data. It is widely used and freely
|
| 180 |
+
downloadable, but the Bearing Data Center pages and the CWRU site wide legal notice grant no
|
| 181 |
+
reuse or commercial use rights.
|
| 182 |
+
- Commercial verdict: UNCLEAR / UNSTATED, no license grant. For commercial use, obtain
|
| 183 |
+
written permission from the Case School of Engineering. Third party mirrors (Kaggle, Zenodo)
|
| 184 |
+
do not establish CWRU's terms.
|
| 185 |
+
|
| 186 |
+
### NASA C-MAPSS turbofan (remaining useful life regressor, FD001 to FD004)
|
| 187 |
+
|
| 188 |
+
- Dataset: NASA C-MAPSS Turbofan Engine Degradation Simulation Data Set (Saxena & Goebel 2008),
|
| 189 |
+
subsets FD001 to FD004, from the NASA Prognostics Center of Excellence (PCoE) data repository.
|
| 190 |
+
- Source: https://www.nasa.gov/intelligent-systems-division/discovery-and-systems-health/pcoe/pcoe-data-set-repository/
|
| 191 |
+
- Split: 14 informative sensors, min max normalized on the training set, sliding window of 30
|
| 192 |
+
cycles, piecewise linear RUL capped at 125 (Heimes convention). Train = all overlapping
|
| 193 |
+
30 cycle windows per engine; test = the last window per engine scored against the official
|
| 194 |
+
provided RUL truth (NASA test protocol). FD001 train has 17731 windows (test 100 engines).
|
| 195 |
+
Split defined in train.py line 135 (the assertion `len(xtr) == 17731` for FD001 after
|
| 196 |
+
`load_cmapss`). Metric: RMSE and NASA PHM08 asymmetric score.
|
| 197 |
+
- License: no explicit license line on the PCoE repository. The data is a NASA authored
|
| 198 |
+
simulation (a US Government work), which under 17 U.S.C. section 105 is not protected
|
| 199 |
+
by US copyright and may be used, including commercially, without permission. Attribution to
|
| 200 |
+
NASA / Saxena & Goebel (2008) is requested.
|
| 201 |
+
- Commercial verdict: ALLOWED (US Government public domain work; attribution requested). Note:
|
| 202 |
+
US only public domain status; outside the US it is not guaranteed.
|
| 203 |
+
|
| 204 |
+
### MIMII (acoustic head, not shipped)
|
| 205 |
+
|
| 206 |
+
- Dataset: MIMII (Purohit et al. 2019), valve 6 dB subset. Loader implemented; acoustic head
|
| 207 |
+
not shipped and not measured, so no split or license verdict is claimed for a released model
|
| 208 |
+
here.
|
| 209 |
+
|
| 210 |
+
## Loading with standard tooling
|
| 211 |
+
|
| 212 |
+
The repo ships `config.json` (architecture hyperparameters for all five heads) and a
|
| 213 |
+
self contained `modeling.py` with a `load_model` / `from_pretrained` entry point. A few lines
|
| 214 |
+
load the model from a Hugging Face repo, a local directory, or a checkpoint:
|
| 215 |
+
|
| 216 |
+
```python
|
| 217 |
+
from huggingface_hub import hf_hub_download
|
| 218 |
+
from modeling import load_model
|
| 219 |
+
# from a local dir holding model.safetensors + config.json:
|
| 220 |
+
m = load_model("/path/to/weights_dir", variant="cmapss_FD001")
|
| 221 |
+
# or straight from a HF repo id (downloads config.json + model.safetensors):
|
| 222 |
+
m = load_model("lowdown-labs/fela-pdm", variant="cwru")
|
| 223 |
+
```
|
| 224 |
+
|
| 225 |
+
The weights are shipped one safetensors file per head (`cmapss_FD001.safetensors` through
|
| 226 |
+
`cmapss_FD004.safetensors`, and `cwru.safetensors`; not pickle); pass `variant=` to pick the
|
| 227 |
+
head. The preprocessing the model
|
| 228 |
+
expects, and input validation that fails clearly on the wrong shape or channel count, are in
|
| 229 |
+
`modeling.py` (`preprocess_cwru`, `preprocess_cmapss`, `validate_window`).
|
| 230 |
+
|
| 231 |
+
## Serving artifacts
|
| 232 |
+
|
| 233 |
+
- `cmapss_FD001.safetensors` through `cmapss_FD004.safetensors` and `cwru.safetensors`, plus
|
| 234 |
+
`config.json`, for the safetensors load path (fp32).
|
| 235 |
+
- `verify.py` runs a fixed sample input and checks the output shape and a verification value.
|
| 236 |
+
|
| 237 |
+
For serving at scale, use the separate CPU native FELA server (https://github.com/Lowdown-Labs/fela_server). It
|
| 238 |
+
runs this model on CPU with no GPU required. The quickstart in this repo is the minimal
|
| 239 |
+
single process path; the FELA server is the production serving path. On a microcontroller or
|
| 240 |
+
Pi the deploy path is an ONNX or TFLite export of the model.
|
| 241 |
+
|
| 242 |
+
# Citations and licenses
|
| 243 |
+
|
| 244 |
+
This section consolidates the formal references and the direct links to the real license
|
| 245 |
+
text for every dataset and method used, verified from source.
|
| 246 |
+
|
| 247 |
+
## Datasets
|
| 248 |
+
|
| 249 |
+
- **NASA C-MAPSS Turbofan Engine Degradation Simulation Data Set** (FD001 to FD004): the
|
| 250 |
+
remaining useful life regressor.
|
| 251 |
+
- Reference: Saxena, A., Goebel, K., Simon, D., & Eklund, N. (2008). Damage propagation
|
| 252 |
+
modeling for aircraft engine run-to-failure simulation. *International Conference on
|
| 253 |
+
Prognostics and Health Management (PHM08)*, 1 to 9.
|
| 254 |
+
DOI: [10.1109/PHM.2008.4711414](https://doi.org/10.1109/PHM.2008.4711414)
|
| 255 |
+
- Data: NASA Prognostics Center of Excellence (PCoE) data repository,
|
| 256 |
+
[NASA PCoE data set repository](https://www.nasa.gov/intelligent-systems-division/discovery-and-systems-health/pcoe/pcoe-data-set-repository/).
|
| 257 |
+
- **License: NASA data policy, US Government work, PUBLIC DOMAIN.** As a NASA authored
|
| 258 |
+
simulation, the data is a US Government work and under
|
| 259 |
+
[17 U.S.C. § 105](https://www.copyright.gov/title17/92chap1.html#105) is not protected by US
|
| 260 |
+
copyright; NASA's open data terms permit use, including commercial use, without permission.
|
| 261 |
+
See NASA's data usage guidelines:
|
| 262 |
+
[nasa.gov/nasa-open-data-and-usage-guidelines](https://www.nasa.gov/nasa-open-data-and-usage-guidelines/).
|
| 263 |
+
Attribution to NASA / Saxena & Goebel (2008) is requested. Public domain status is US only;
|
| 264 |
+
outside the US it is not guaranteed.
|
| 265 |
+
- **CWRU bearing dataset (Case Western Reserve University Bearing Data Center)**: 12 kHz
|
| 266 |
+
drive end vibration recordings, the bearing fault classifier.
|
| 267 |
+
- Data / use terms: [Case Western Reserve University Bearing Data Center](https://engineering.case.edu/bearingdatacenter),
|
| 268 |
+
which references the CWRU site wide legal notice:
|
| 269 |
+
[case.edu/utilities/privacy-legal](https://case.edu/utilities/privacy-legal).
|
| 270 |
+
- **License: NO explicit license or use terms grant is published by CWRU.** The data is freely
|
| 271 |
+
downloadable and widely used, but the Bearing Data Center pages and the CWRU legal notice grant
|
| 272 |
+
no reuse or commercial use rights. Commercial verdict UNCLEAR/UNSTATED; for commercial use,
|
| 273 |
+
obtain written permission from the Case School of Engineering. Third party mirrors (Kaggle,
|
| 274 |
+
Zenodo) do not establish CWRU's terms.
|
| 275 |
+
- **MIMII** (acoustic head: loader only, no weights shipped, no released model license claimed).
|
| 276 |
+
Purohit, H., Tanabe, R., Ichige, K., et al. (2019). MIMII Dataset: Sound Dataset for
|
| 277 |
+
Malfunctioning Industrial Machine Investigation and Inspection. *DCASE Workshop*.
|
| 278 |
+
[arXiv:1909.09347](https://arxiv.org/abs/1909.09347) (dataset is CC BY-SA 4.0 on Zenodo, cited
|
| 279 |
+
here for completeness only).
|
| 280 |
+
|
| 281 |
+
## Methods and code
|
| 282 |
+
|
| 283 |
+
- **Fourier Neural Operator (FNO)**: the sequence mixer at the core of both heads.
|
| 284 |
+
Li, Z., et al. (2021). Fourier Neural Operator for Parametric Partial Differential Equations.
|
| 285 |
+
*ICLR*. [arXiv:2010.08895](https://arxiv.org/abs/2010.08895)
|
| 286 |
+
- **Gated Linear Attention (GLA)**: the optional gated recall mixer in the FNO+GLA variant
|
| 287 |
+
(`gla_chunk` in `config.json` / `modeling.py`). Yang, S., Wang, B., Shen, Y., Panda, R., & Kim, Y.
|
| 288 |
+
(2024). Gated Linear Attention Transformers with Hardware-Efficient Training.
|
| 289 |
+
[arXiv:2312.06635](https://arxiv.org/abs/2312.06635)
|
| 290 |
+
- **PyTorch**: training and inference framework. Paszke, A., et al. (2019). *NeurIPS*.
|
| 291 |
+
[arXiv:1912.01703](https://arxiv.org/abs/1912.01703)
|
| 292 |
+
- **ONNX Runtime / TFLite**: the on device export and runtime path (opset 17).
|
| 293 |
+
[onnxruntime.ai](https://onnxruntime.ai/),
|
| 294 |
+
[ai.google.dev/edge/litert](https://ai.google.dev/edge/litert).
|
| 295 |
+
|
| 296 |
+
The deployable default is the pure FNO head; the FNO+GLA variant is the one that additionally
|
| 297 |
+
uses Gated Linear Attention. Landmark Attention and Gated DeltaNet are not used in this model.
|
| 298 |
+
|
| 299 |
+
# Intended use, limitations, and safety
|
| 300 |
+
|
| 301 |
+
What it is for: on device predictive maintenance running on a PLC, a sensor gateway, or an
|
| 302 |
+
industrial IoT node, with no dependence on the cloud. Typical buyers are equipment makers who
|
| 303 |
+
sell machines with downtime guarantees, and plants that cannot or will not stream raw
|
| 304 |
+
vibration data off site.
|
| 305 |
+
|
| 306 |
+
What it is not for: this is not a safety critical controller and not a substitute for a
|
| 307 |
+
certified protection system. The remaining useful life number is a planning aid. Do not use it
|
| 308 |
+
as the sole basis for a safety critical decision (for example deciding a machine is safe to
|
| 309 |
+
keep running) without independent validation against your own field data and your existing
|
| 310 |
+
condition monitoring practice.
|
| 311 |
+
|
| 312 |
+
Privacy: the model runs on the device next to the sensor. Raw vibration and sensor data do not
|
| 313 |
+
have to leave the device, which is the point for plants that cannot send data off site.
|
| 314 |
+
|
| 315 |
+
Evaluated conditions and known failure modes:
|
| 316 |
+
|
| 317 |
+
- The CWRU window split protocol is optimistic: windows from one recording can appear in both
|
| 318 |
+
train and test, so 100 percent accuracy reflects an easy protocol, not a solved problem.
|
| 319 |
+
Cross load and cross fault size generalization (train on one motor load, test on another) is
|
| 320 |
+
the honest next test and is not yet reported.
|
| 321 |
+
- The C-MAPSS multi condition subsets FD002 and FD004 are not ahead of the literature; they sit
|
| 322 |
+
inside the published band. FD001 and FD003 (single condition) are the strong results.
|
| 323 |
+
- Remaining useful life is only as good as the run to failure data it was trained on. C-MAPSS
|
| 324 |
+
is simulated. Real machines fail in ways the training distribution may not cover.
|
| 325 |
+
- The MIMII acoustic head is not shipped; the path exists but the AUC is not measured.
|
| 326 |
+
- Quantization was dynamic int8 on linear layers only. A true microcontroller deployment needs
|
| 327 |
+
a fixed point FFT (for example CMSIS-DSP on Cortex-M) and on target validation, which is not
|
| 328 |
+
done here. Latency and size were measured on an x86 server CPU; the microcontroller claim is
|
| 329 |
+
the size plus compute envelope, run there via the ONNX or TFLite export.
|
| 330 |
+
- No real world field data was used. All benchmarks are public research datasets.
|
| 331 |
+
|
| 332 |
+
# How to cite
|
| 333 |
+
|
| 334 |
+
```bibtex
|
| 335 |
+
@misc{lowdownlabs_felapdm,
|
| 336 |
+
title = {FELA-PdM: on-device Fourier Neural Operator models for predictive maintenance},
|
| 337 |
+
author = {Lowdown Labs},
|
| 338 |
+
year = {2026},
|
| 339 |
+
note = {Model card}
|
| 340 |
+
}
|
| 341 |
+
```
|
| 342 |
+
|
| 343 |
+
You must also cite the datasets used:
|
| 344 |
+
|
| 345 |
+
- Saxena, A., Goebel, K., Simon, D., Eklund, N. (2008). Damage propagation modeling for
|
| 346 |
+
aircraft engine run-to-failure simulation (C-MAPSS / NASA turbofan). International
|
| 347 |
+
Conference on Prognostics and Health Management.
|
| 348 |
+
- Case Western Reserve University Bearing Data Center (CWRU bearing dataset).
|
| 349 |
+
- Purohit, H., Tanabe, R., Ichige, K., et al. (2019). MIMII Dataset: Sound Dataset for
|
| 350 |
+
Malfunctioning Industrial Machine Investigation and Inspection. DCASE Workshop.
|
| 351 |
+
|
| 352 |
+
# Acknowledgements and references
|
| 353 |
+
|
| 354 |
+
- C-MAPSS / NASA turbofan: Saxena et al. (2008).
|
| 355 |
+
- CWRU bearing dataset: Case Western Reserve University Bearing Data Center.
|
| 356 |
+
- MIMII: Purohit et al. (2019), DCASE.
|
| 357 |
+
- Fourier Neural Operator: Li, Z., Kovachki, N., Azizzadenesheli, K., et al. (2021). Fourier
|
| 358 |
+
Neural Operator for Parametric Partial Differential Equations. ICLR.
|
| 359 |
+
- PyTorch: Paszke et al. (2019), NeurIPS.
|
| 360 |
+
|
| 361 |
+
# Model family
|
| 362 |
+
|
| 363 |
+
This is part of the FELA family from Lowdown Labs: one FNO architecture across many
|
| 364 |
+
modalities, all CPU native and subquadratic. This repo is published as
|
| 365 |
+
`lowdown-labs/fela-pdm`. The sibling repos are:
|
| 366 |
+
|
| 367 |
+
- `lowdown-labs/fela-genomics`: DNA sequence classification.
|
| 368 |
+
- `lowdown-labs/fela-pdm` (this repo): rotating machinery and turbofan health.
|
| 369 |
+
- `lowdown-labs/fela-power-grid`: probabilistic solar and wind power forecasting.
|
| 370 |
+
- `lowdown-labs/fela-video`: video moment retrieval and temporal grounding.
|
| 371 |
+
- `lowdown-labs/fela-streaming-asr`: streaming CPU speech recognition.
|
| 372 |
+
|
| 373 |
+
These are grouped under the FELA Collection on Hugging Face. The models are independently
|
| 374 |
+
trained per modality and do not share weights, so none carries a `base_model` link.
|
| 375 |
+
|
| 376 |
+
# License
|
| 377 |
+
|
| 378 |
+
Released under the Lowdown Labs Lovely License 1.0 (CC BY-NC 4.0 plus Hippocratic License 3.0). See LICENSE. For most LL models, a commercial license may be available; contact Lowdown Labs.
|
cmapss_FD001.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a9b625af024097d37f50d381bab3b6106363798746487538077787ac1d956ccc
|
| 3 |
+
size 500788
|
cmapss_FD002.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:02367e2a5905d58ae075db3f67f19b49fb3fa720057d43c280619e9a1832093e
|
| 3 |
+
size 500788
|
cmapss_FD003.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f65c4517d7e645c21bd4baed01ade87dfdaffe0492d56835f82f416d34b313f0
|
| 3 |
+
size 500788
|
cmapss_FD004.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c3705ce4f528830dcfdfceb897f94a48e2e11e4fdab4d1db8f739047af08f91c
|
| 3 |
+
size 500788
|
config.json
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"model_type": "fela-pdm",
|
| 3 |
+
"library_name": "pytorch",
|
| 4 |
+
"architectures": [
|
| 5 |
+
"FelaPdmModel"
|
| 6 |
+
],
|
| 7 |
+
"auto_map": {
|
| 8 |
+
"AutoConfig": "configuration_pdm.FelaPdmConfig",
|
| 9 |
+
"AutoModel": "modeling_pdm.FelaPdmModel"
|
| 10 |
+
},
|
| 11 |
+
"default_variant": "cmapss_FD001",
|
| 12 |
+
"note": "Five trained heads ship, one safetensors file per head (cmapss_FD001, cmapss_FD002, cmapss_FD003, cmapss_FD004, cwru). Pick variants[<name>] to match the head; load_model(dir, variant=<name>) resolves <name>.safetensors.",
|
| 13 |
+
"variants": {
|
| 14 |
+
"cmapss_FD001": {
|
| 15 |
+
"task": "rul",
|
| 16 |
+
"in_channels": 14,
|
| 17 |
+
"patch": 1,
|
| 18 |
+
"n_embd": 64,
|
| 19 |
+
"n_layer": 4,
|
| 20 |
+
"n_head": 4,
|
| 21 |
+
"fno_modes": 32,
|
| 22 |
+
"gla_chunk": 32,
|
| 23 |
+
"ffn_hidden": 128,
|
| 24 |
+
"dropout": 0.0,
|
| 25 |
+
"use_gdn": false,
|
| 26 |
+
"gdn_every": 4,
|
| 27 |
+
"n_classes": 0,
|
| 28 |
+
"rul_head": true,
|
| 29 |
+
"seq_len": 30,
|
| 30 |
+
"rul_cap": 125,
|
| 31 |
+
"input_shape": [1, 30, 14],
|
| 32 |
+
"input_desc": "30 cycles of 14 informative C-MAPSS sensors, min max normalized on training stats"
|
| 33 |
+
},
|
| 34 |
+
"cmapss_FD002": {
|
| 35 |
+
"task": "rul",
|
| 36 |
+
"in_channels": 14,
|
| 37 |
+
"patch": 1,
|
| 38 |
+
"n_embd": 64,
|
| 39 |
+
"n_layer": 4,
|
| 40 |
+
"n_head": 4,
|
| 41 |
+
"fno_modes": 32,
|
| 42 |
+
"gla_chunk": 32,
|
| 43 |
+
"ffn_hidden": 128,
|
| 44 |
+
"dropout": 0.0,
|
| 45 |
+
"use_gdn": false,
|
| 46 |
+
"gdn_every": 4,
|
| 47 |
+
"n_classes": 0,
|
| 48 |
+
"rul_head": true,
|
| 49 |
+
"seq_len": 30,
|
| 50 |
+
"rul_cap": 125,
|
| 51 |
+
"input_shape": [1, 30, 14],
|
| 52 |
+
"input_desc": "30 cycles of 14 informative C-MAPSS sensors, min max normalized on training stats"
|
| 53 |
+
},
|
| 54 |
+
"cmapss_FD003": {
|
| 55 |
+
"task": "rul",
|
| 56 |
+
"in_channels": 14,
|
| 57 |
+
"patch": 1,
|
| 58 |
+
"n_embd": 64,
|
| 59 |
+
"n_layer": 4,
|
| 60 |
+
"n_head": 4,
|
| 61 |
+
"fno_modes": 32,
|
| 62 |
+
"gla_chunk": 32,
|
| 63 |
+
"ffn_hidden": 128,
|
| 64 |
+
"dropout": 0.0,
|
| 65 |
+
"use_gdn": false,
|
| 66 |
+
"gdn_every": 4,
|
| 67 |
+
"n_classes": 0,
|
| 68 |
+
"rul_head": true,
|
| 69 |
+
"seq_len": 30,
|
| 70 |
+
"rul_cap": 125,
|
| 71 |
+
"input_shape": [1, 30, 14],
|
| 72 |
+
"input_desc": "30 cycles of 14 informative C-MAPSS sensors, min max normalized on training stats"
|
| 73 |
+
},
|
| 74 |
+
"cmapss_FD004": {
|
| 75 |
+
"task": "rul",
|
| 76 |
+
"in_channels": 14,
|
| 77 |
+
"patch": 1,
|
| 78 |
+
"n_embd": 64,
|
| 79 |
+
"n_layer": 4,
|
| 80 |
+
"n_head": 4,
|
| 81 |
+
"fno_modes": 32,
|
| 82 |
+
"gla_chunk": 32,
|
| 83 |
+
"ffn_hidden": 128,
|
| 84 |
+
"dropout": 0.0,
|
| 85 |
+
"use_gdn": false,
|
| 86 |
+
"gdn_every": 4,
|
| 87 |
+
"n_classes": 0,
|
| 88 |
+
"rul_head": true,
|
| 89 |
+
"seq_len": 30,
|
| 90 |
+
"rul_cap": 125,
|
| 91 |
+
"input_shape": [1, 30, 14],
|
| 92 |
+
"input_desc": "30 cycles of 14 informative C-MAPSS sensors, min max normalized on training stats"
|
| 93 |
+
},
|
| 94 |
+
"cwru": {
|
| 95 |
+
"task": "cls",
|
| 96 |
+
"in_channels": 1,
|
| 97 |
+
"patch": 4,
|
| 98 |
+
"n_embd": 64,
|
| 99 |
+
"n_layer": 4,
|
| 100 |
+
"n_head": 4,
|
| 101 |
+
"fno_modes": 64,
|
| 102 |
+
"gla_chunk": 32,
|
| 103 |
+
"ffn_hidden": 128,
|
| 104 |
+
"dropout": 0.0,
|
| 105 |
+
"use_gdn": false,
|
| 106 |
+
"gdn_every": 4,
|
| 107 |
+
"n_classes": 10,
|
| 108 |
+
"rul_head": false,
|
| 109 |
+
"seq_len": 2048,
|
| 110 |
+
"input_shape": [1, 2048, 1],
|
| 111 |
+
"input_desc": "2048 raw vibration samples at 12 kHz, single channel, per signal standardized"
|
| 112 |
+
}
|
| 113 |
+
}
|
| 114 |
+
}
|
configuration_pdm.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from transformers import PretrainedConfig
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class FelaPdmConfig(PretrainedConfig):
|
| 5 |
+
model_type = "fela-pdm"
|
| 6 |
+
|
| 7 |
+
def __init__(
|
| 8 |
+
self,
|
| 9 |
+
variant="cmapss_FD001",
|
| 10 |
+
task="rul",
|
| 11 |
+
in_channels=14,
|
| 12 |
+
patch=1,
|
| 13 |
+
n_embd=64,
|
| 14 |
+
n_layer=4,
|
| 15 |
+
n_head=4,
|
| 16 |
+
fno_modes=32,
|
| 17 |
+
gla_chunk=32,
|
| 18 |
+
ffn_hidden=128,
|
| 19 |
+
dropout=0.0,
|
| 20 |
+
use_gdn=False,
|
| 21 |
+
gdn_every=4,
|
| 22 |
+
n_classes=0,
|
| 23 |
+
rul_head=True,
|
| 24 |
+
seq_len=30,
|
| 25 |
+
default_variant=None,
|
| 26 |
+
variants=None,
|
| 27 |
+
**kwargs,
|
| 28 |
+
):
|
| 29 |
+
if isinstance(variants, dict):
|
| 30 |
+
name = variant or default_variant
|
| 31 |
+
if name in variants:
|
| 32 |
+
v = variants[name]
|
| 33 |
+
task = v.get("task", task)
|
| 34 |
+
in_channels = v.get("in_channels", in_channels)
|
| 35 |
+
patch = v.get("patch", patch)
|
| 36 |
+
n_embd = v.get("n_embd", n_embd)
|
| 37 |
+
n_layer = v.get("n_layer", n_layer)
|
| 38 |
+
n_head = v.get("n_head", n_head)
|
| 39 |
+
fno_modes = v.get("fno_modes", fno_modes)
|
| 40 |
+
gla_chunk = v.get("gla_chunk", gla_chunk)
|
| 41 |
+
ffn_hidden = v.get("ffn_hidden", ffn_hidden)
|
| 42 |
+
dropout = v.get("dropout", dropout)
|
| 43 |
+
use_gdn = v.get("use_gdn", use_gdn)
|
| 44 |
+
gdn_every = v.get("gdn_every", gdn_every)
|
| 45 |
+
n_classes = v.get("n_classes", n_classes)
|
| 46 |
+
rul_head = v.get("rul_head", rul_head)
|
| 47 |
+
seq_len = v.get("seq_len", seq_len)
|
| 48 |
+
self.variant = variant
|
| 49 |
+
self.task = task
|
| 50 |
+
self.in_channels = in_channels
|
| 51 |
+
self.patch = patch
|
| 52 |
+
self.n_embd = n_embd
|
| 53 |
+
self.n_layer = n_layer
|
| 54 |
+
self.n_head = n_head
|
| 55 |
+
self.fno_modes = fno_modes
|
| 56 |
+
self.gla_chunk = gla_chunk
|
| 57 |
+
self.ffn_hidden = ffn_hidden
|
| 58 |
+
self.dropout = dropout
|
| 59 |
+
self.use_gdn = use_gdn
|
| 60 |
+
self.gdn_every = gdn_every
|
| 61 |
+
self.n_classes = n_classes
|
| 62 |
+
self.rul_head = rul_head
|
| 63 |
+
self.seq_len = seq_len
|
| 64 |
+
if default_variant is not None:
|
| 65 |
+
self.default_variant = default_variant
|
| 66 |
+
if variants is not None:
|
| 67 |
+
self.variants = variants
|
| 68 |
+
super().__init__(**kwargs)
|
cwru.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:aa5a9d9972f3c6895cc4a7fcf37cb3227154ef75bb9626c4a72047867a665509
|
| 3 |
+
size 533344
|
model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a9b625af024097d37f50d381bab3b6106363798746487538077787ac1d956ccc
|
| 3 |
+
size 500788
|
modeling.py
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
import json
|
| 3 |
+
import os
|
| 4 |
+
from dataclasses import dataclass, asdict
|
| 5 |
+
import torch
|
| 6 |
+
import torch.nn as nn
|
| 7 |
+
import torch.nn.functional as F
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
@dataclass
|
| 11 |
+
class PDMConfig:
|
| 12 |
+
in_channels: int = 1
|
| 13 |
+
patch: int = 1
|
| 14 |
+
n_embd: int = 64
|
| 15 |
+
n_layer: int = 4
|
| 16 |
+
n_head: int = 4
|
| 17 |
+
fno_modes: int = 64
|
| 18 |
+
gla_chunk: int = 32
|
| 19 |
+
ffn_hidden: int = 128
|
| 20 |
+
dropout: float = 0.0
|
| 21 |
+
use_gdn: bool = False
|
| 22 |
+
gdn_every: int = 4
|
| 23 |
+
n_classes: int = 0
|
| 24 |
+
rul_head: bool = False
|
| 25 |
+
seq_len: int = 2048
|
| 26 |
+
|
| 27 |
+
def __post_init__(self):
|
| 28 |
+
assert self.n_embd % self.n_head == 0
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
class FNOSeqMixer(nn.Module):
|
| 32 |
+
def __init__(self, cfg: PDMConfig):
|
| 33 |
+
super().__init__()
|
| 34 |
+
self.M = cfg.fno_modes
|
| 35 |
+
self.filter_td = nn.Parameter(torch.empty(cfg.n_embd, cfg.fno_modes))
|
| 36 |
+
self.out_scale = nn.Linear(cfg.n_embd, cfg.n_embd, bias=False)
|
| 37 |
+
nn.init.normal_(self.filter_td, std=0.02)
|
| 38 |
+
|
| 39 |
+
def forward(self, x):
|
| 40 |
+
B, T, C = x.shape
|
| 41 |
+
n_use = min(self.M, T)
|
| 42 |
+
h = self.filter_td.new_zeros(2 * T, C)
|
| 43 |
+
h[:n_use] = self.filter_td[:, :n_use].T
|
| 44 |
+
xp = F.pad(x, (0, 0, 0, T))
|
| 45 |
+
Y = torch.fft.rfft(xp, dim=1) * torch.fft.rfft(h, dim=0).unsqueeze(0)
|
| 46 |
+
return self.out_scale(torch.fft.irfft(Y, n=2 * T, dim=1)[:, :T])
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
class SwiGLU(nn.Module):
|
| 50 |
+
def __init__(self, cfg: PDMConfig):
|
| 51 |
+
super().__init__()
|
| 52 |
+
d, hd = (cfg.n_embd, cfg.ffn_hidden)
|
| 53 |
+
self.gate = nn.Linear(d, hd, bias=False)
|
| 54 |
+
self.up = nn.Linear(d, hd, bias=False)
|
| 55 |
+
self.down = nn.Linear(hd, d, bias=False)
|
| 56 |
+
self.drop = nn.Dropout(cfg.dropout)
|
| 57 |
+
|
| 58 |
+
def forward(self, x):
|
| 59 |
+
return self.drop(self.down(F.silu(self.gate(x)) * self.up(x)))
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
class PDMBlock(nn.Module):
|
| 63 |
+
def __init__(self, cfg: PDMConfig):
|
| 64 |
+
super().__init__()
|
| 65 |
+
self.mixer = FNOSeqMixer(cfg)
|
| 66 |
+
self.ffn = SwiGLU(cfg)
|
| 67 |
+
self.ln1 = nn.RMSNorm(cfg.n_embd)
|
| 68 |
+
self.ln2 = nn.RMSNorm(cfg.n_embd)
|
| 69 |
+
|
| 70 |
+
def forward(self, x):
|
| 71 |
+
x = x + self.mixer(self.ln1(x))
|
| 72 |
+
x = x + self.ffn(self.ln2(x))
|
| 73 |
+
return x
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
class FELAPDM(nn.Module):
|
| 77 |
+
def __init__(self, cfg: PDMConfig):
|
| 78 |
+
super().__init__()
|
| 79 |
+
self.cfg = cfg
|
| 80 |
+
self.patch = cfg.patch
|
| 81 |
+
self.embed = nn.Linear(cfg.in_channels * cfg.patch, cfg.n_embd)
|
| 82 |
+
self.blocks = nn.ModuleList([PDMBlock(cfg) for _ in range(cfg.n_layer)])
|
| 83 |
+
self.ln_out = nn.RMSNorm(cfg.n_embd)
|
| 84 |
+
if cfg.n_classes > 0:
|
| 85 |
+
self.cls_head = nn.Linear(cfg.n_embd, cfg.n_classes)
|
| 86 |
+
if cfg.rul_head:
|
| 87 |
+
self.rul_head = nn.Linear(cfg.n_embd, 1)
|
| 88 |
+
|
| 89 |
+
def _patchify(self, x):
|
| 90 |
+
B, T, Cin = x.shape
|
| 91 |
+
p = self.patch
|
| 92 |
+
if p > 1:
|
| 93 |
+
T2 = T // p * p
|
| 94 |
+
x = x[:, :T2].reshape(B, T2 // p, p * Cin)
|
| 95 |
+
return x
|
| 96 |
+
|
| 97 |
+
def _backbone(self, x):
|
| 98 |
+
x = self._patchify(x)
|
| 99 |
+
x = self.embed(x)
|
| 100 |
+
x = F.rms_norm(x, (x.size(-1),))
|
| 101 |
+
for blk in self.blocks:
|
| 102 |
+
x = blk(x)
|
| 103 |
+
return self.ln_out(x)
|
| 104 |
+
|
| 105 |
+
def forward(self, x, task: str = None):
|
| 106 |
+
task = task or ("rul" if self.cfg.rul_head else "cls")
|
| 107 |
+
h = self._backbone(x)
|
| 108 |
+
if task == "cls":
|
| 109 |
+
return self.cls_head(h.mean(dim=1))
|
| 110 |
+
if task == "rul":
|
| 111 |
+
return self.rul_head(h[:, -1, :]).squeeze(-1)
|
| 112 |
+
raise ValueError(task)
|
| 113 |
+
|
| 114 |
+
@torch.no_grad()
|
| 115 |
+
def predict(self, x, task: str = None):
|
| 116 |
+
self.eval()
|
| 117 |
+
validate_window(x, self.cfg)
|
| 118 |
+
out = self.forward(x, task=task)
|
| 119 |
+
if (task or ("rul" if self.cfg.rul_head else "cls")) == "cls":
|
| 120 |
+
probs = torch.softmax(out.float(), dim=-1)[0]
|
| 121 |
+
idx = int(probs.argmax())
|
| 122 |
+
return (idx, float(probs[idx]))
|
| 123 |
+
return float(out.reshape(-1)[0])
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
def validate_window(x: torch.Tensor, cfg: PDMConfig):
|
| 127 |
+
if x.dim() != 3:
|
| 128 |
+
raise ValueError(
|
| 129 |
+
f"expected a 3D tensor (batch, time, channels), got shape {tuple(x.shape)}"
|
| 130 |
+
)
|
| 131 |
+
if x.shape[-1] != cfg.in_channels:
|
| 132 |
+
raise ValueError(
|
| 133 |
+
f"expected {cfg.in_channels} channels in the last dimension, got {x.shape[-1]}. CWRU vibration is 1 channel; C-MAPSS is 14 sensors."
|
| 134 |
+
)
|
| 135 |
+
if x.shape[1] < cfg.patch:
|
| 136 |
+
raise ValueError(
|
| 137 |
+
f"time dimension {x.shape[1]} is shorter than the patch size {cfg.patch}"
|
| 138 |
+
)
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
def preprocess_cwru(samples, expected_len: int = 2048) -> torch.Tensor:
|
| 142 |
+
t = torch.as_tensor(samples, dtype=torch.float32).reshape(-1)
|
| 143 |
+
if t.numel() != expected_len:
|
| 144 |
+
raise ValueError(
|
| 145 |
+
f"CWRU window must be {expected_len} samples (12 kHz), got {t.numel()}"
|
| 146 |
+
)
|
| 147 |
+
t = (t - t.mean()) / (t.std() + 1e-06)
|
| 148 |
+
return t.reshape(1, expected_len, 1)
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
def preprocess_cmapss(cycles, sensor_min, sensor_max, window: int = 30) -> torch.Tensor:
|
| 152 |
+
t = torch.as_tensor(cycles, dtype=torch.float32)
|
| 153 |
+
if t.dim() != 2 or t.shape[1] != 14:
|
| 154 |
+
raise ValueError(f"C-MAPSS input must be (window, 14), got {tuple(t.shape)}")
|
| 155 |
+
if t.shape[0] != window:
|
| 156 |
+
raise ValueError(f"C-MAPSS window must be {window} cycles, got {t.shape[0]}")
|
| 157 |
+
lo = torch.as_tensor(sensor_min, dtype=torch.float32)
|
| 158 |
+
hi = torch.as_tensor(sensor_max, dtype=torch.float32)
|
| 159 |
+
t = (t - lo) / (hi - lo + 1e-06)
|
| 160 |
+
return t.reshape(1, window, 14)
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
def _load_state(path: str):
|
| 164 |
+
if path.endswith(".safetensors"):
|
| 165 |
+
from safetensors.torch import load_file
|
| 166 |
+
|
| 167 |
+
return load_file(path)
|
| 168 |
+
ck = torch.load(path, map_location="cpu", weights_only=False)
|
| 169 |
+
return ck["model"] if isinstance(ck, dict) and "model" in ck else ck
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
_CONFIG_FIELDS = set(PDMConfig.__dataclass_fields__.keys())
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
def _to_pdm_config(cfg_dict: dict, variant: str = None) -> PDMConfig:
|
| 176 |
+
if "variants" in cfg_dict:
|
| 177 |
+
variant = variant or cfg_dict.get("default_variant")
|
| 178 |
+
if variant not in cfg_dict["variants"]:
|
| 179 |
+
raise ValueError(
|
| 180 |
+
f"unknown variant {variant!r}; choose one of {list(cfg_dict['variants'])}"
|
| 181 |
+
)
|
| 182 |
+
cfg_dict = cfg_dict["variants"][variant]
|
| 183 |
+
return PDMConfig(**{k: v for k, v in cfg_dict.items() if k in _CONFIG_FIELDS})
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
def load_model(path_or_repo: str, config: dict = None, variant: str = None):
|
| 187 |
+
cfg_dict = config
|
| 188 |
+
weights_path = None
|
| 189 |
+
if os.path.isdir(path_or_repo):
|
| 190 |
+
cfg_dict = cfg_dict or _read_json(os.path.join(path_or_repo, "config.json"))
|
| 191 |
+
v = variant or (
|
| 192 |
+
cfg_dict.get("default_variant") if isinstance(cfg_dict, dict) else None
|
| 193 |
+
)
|
| 194 |
+
cand = os.path.join(path_or_repo, f"{v}.safetensors") if v else None
|
| 195 |
+
if cand and os.path.isfile(cand):
|
| 196 |
+
weights_path = cand
|
| 197 |
+
else:
|
| 198 |
+
weights_path = os.path.join(path_or_repo, "model.safetensors")
|
| 199 |
+
elif os.path.isfile(path_or_repo):
|
| 200 |
+
if path_or_repo.endswith(".safetensors"):
|
| 201 |
+
beside = os.path.join(os.path.dirname(path_or_repo), "config.json")
|
| 202 |
+
cfg_dict = cfg_dict or _read_json(beside)
|
| 203 |
+
weights_path = path_or_repo
|
| 204 |
+
else:
|
| 205 |
+
ck = torch.load(path_or_repo, map_location="cpu", weights_only=False)
|
| 206 |
+
cfg_dict = cfg_dict or ck["cfg"]
|
| 207 |
+
model = FELAPDM(_to_pdm_config(cfg_dict, variant))
|
| 208 |
+
model.load_state_dict(ck["model"])
|
| 209 |
+
model.eval()
|
| 210 |
+
return model
|
| 211 |
+
else:
|
| 212 |
+
from huggingface_hub import hf_hub_download
|
| 213 |
+
|
| 214 |
+
cfg_path = hf_hub_download(path_or_repo, "config.json")
|
| 215 |
+
cfg_dict = cfg_dict or _read_json(cfg_path)
|
| 216 |
+
v = variant or (
|
| 217 |
+
cfg_dict.get("default_variant") if isinstance(cfg_dict, dict) else None
|
| 218 |
+
)
|
| 219 |
+
try:
|
| 220 |
+
weights_path = (
|
| 221 |
+
hf_hub_download(path_or_repo, f"{v}.safetensors")
|
| 222 |
+
if v
|
| 223 |
+
else hf_hub_download(path_or_repo, "model.safetensors")
|
| 224 |
+
)
|
| 225 |
+
except Exception:
|
| 226 |
+
weights_path = hf_hub_download(path_or_repo, "model.safetensors")
|
| 227 |
+
model = FELAPDM(_to_pdm_config(cfg_dict, variant))
|
| 228 |
+
model.load_state_dict(_load_state(weights_path))
|
| 229 |
+
model.eval()
|
| 230 |
+
return model
|
| 231 |
+
|
| 232 |
+
|
| 233 |
+
from_pretrained = load_model
|
| 234 |
+
|
| 235 |
+
|
| 236 |
+
def _read_json(path: str) -> dict:
|
| 237 |
+
with open(path) as f:
|
| 238 |
+
return json.load(f)
|
modeling_pdm.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import sys
|
| 3 |
+
|
| 4 |
+
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
| 5 |
+
from transformers import PreTrainedModel
|
| 6 |
+
from transformers.modeling_outputs import CausalLMOutput
|
| 7 |
+
|
| 8 |
+
from .configuration_pdm import FelaPdmConfig
|
| 9 |
+
from .modeling import FELAPDM, PDMConfig
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class FelaPdmModel(PreTrainedModel):
|
| 13 |
+
config_class = FelaPdmConfig
|
| 14 |
+
base_model_prefix = "model"
|
| 15 |
+
main_input_name = "x"
|
| 16 |
+
|
| 17 |
+
def __init__(self, config):
|
| 18 |
+
super().__init__(config)
|
| 19 |
+
cfg = PDMConfig(
|
| 20 |
+
in_channels=config.in_channels,
|
| 21 |
+
patch=config.patch,
|
| 22 |
+
n_embd=config.n_embd,
|
| 23 |
+
n_layer=config.n_layer,
|
| 24 |
+
n_head=config.n_head,
|
| 25 |
+
fno_modes=config.fno_modes,
|
| 26 |
+
gla_chunk=config.gla_chunk,
|
| 27 |
+
ffn_hidden=config.ffn_hidden,
|
| 28 |
+
dropout=config.dropout,
|
| 29 |
+
use_gdn=config.use_gdn,
|
| 30 |
+
gdn_every=config.gdn_every,
|
| 31 |
+
n_classes=config.n_classes,
|
| 32 |
+
rul_head=config.rul_head,
|
| 33 |
+
seq_len=config.seq_len,
|
| 34 |
+
)
|
| 35 |
+
self.model = FELAPDM(cfg)
|
| 36 |
+
self.task = config.task
|
| 37 |
+
self.post_init()
|
| 38 |
+
|
| 39 |
+
def forward(self, x=None, input_values=None, task=None, **kwargs):
|
| 40 |
+
if x is None:
|
| 41 |
+
x = input_values
|
| 42 |
+
out = self.model(x, task=task or self.task)
|
| 43 |
+
return CausalLMOutput(logits=out)
|
quickstart/README.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Quickstart
|
| 2 |
+
|
| 3 |
+
Load a FELA-PdM head and run one real sensor window on CPU. Uses the self contained loader
|
| 4 |
+
in `../modeling.py`.
|
| 5 |
+
|
| 6 |
+
## Steps
|
| 7 |
+
|
| 8 |
+
1. Install the pinned requirements (CPU PyTorch):
|
| 9 |
+
|
| 10 |
+
```bash
|
| 11 |
+
pip install -r requirements.txt
|
| 12 |
+
```
|
| 13 |
+
|
| 14 |
+
2. Get the weights. One safetensors file per head ships in this repo
|
| 15 |
+
(`cmapss_FD001.safetensors`, `cwru.safetensors`) beside `config.json`. Point at the repo
|
| 16 |
+
directory and pass `--variant` to pick the head:
|
| 17 |
+
|
| 18 |
+
```bash
|
| 19 |
+
export FELA_PDM_WEIGHTS=/path/to/weights_dir # holds <variant>.safetensors + config.json
|
| 20 |
+
```
|
| 21 |
+
|
| 22 |
+
3. Run:
|
| 23 |
+
|
| 24 |
+
```bash
|
| 25 |
+
python run.py --variant cmapss_FD001 # remaining useful life
|
| 26 |
+
python run.py --variant cwru # bearing fault class
|
| 27 |
+
```
|
| 28 |
+
|
| 29 |
+
## Few line load from Python
|
| 30 |
+
|
| 31 |
+
```python
|
| 32 |
+
from modeling import load_model
|
| 33 |
+
m = load_model("/path/to/weights_dir", variant="cmapss_FD001") # dir, .pt, or HF repo id
|
| 34 |
+
rul = m.predict(window) # window: (1, 30, 14) sensor cycles
|
| 35 |
+
```
|
| 36 |
+
|
| 37 |
+
The C-MAPSS RUL head expects 30 cycles of 14 sensors (min max normalized on the training
|
| 38 |
+
statistics). The CWRU head expects 2048 raw vibration samples (12 kHz, per signal
|
| 39 |
+
standardized). See `modeling.preprocess_cmapss` and `modeling.preprocess_cwru` for the exact
|
| 40 |
+
preprocessing, and `modeling.validate_window` for input validation.
|
quickstart/requirements.txt
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Pinned for reproducible CPU inference. On a machine without CUDA, `pip install torch`
|
| 2 |
+
# pulls the matching CPU wheel. The quickstart runs from a clean venv with only these.
|
| 3 |
+
torch==2.8.0
|
| 4 |
+
numpy==2.1.3
|
| 5 |
+
safetensors==0.5.3
|
| 6 |
+
huggingface-hub==0.34.4
|
quickstart/run.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
import os
|
| 3 |
+
import sys
|
| 4 |
+
import torch
|
| 5 |
+
|
| 6 |
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
| 7 |
+
from modeling import load_model, validate_window
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def main():
|
| 11 |
+
ap = argparse.ArgumentParser()
|
| 12 |
+
ap.add_argument(
|
| 13 |
+
"--variant",
|
| 14 |
+
default="cmapss_FD001",
|
| 15 |
+
choices=[
|
| 16 |
+
"cmapss_FD001",
|
| 17 |
+
"cmapss_FD002",
|
| 18 |
+
"cmapss_FD003",
|
| 19 |
+
"cmapss_FD004",
|
| 20 |
+
"cwru",
|
| 21 |
+
],
|
| 22 |
+
)
|
| 23 |
+
ap.add_argument(
|
| 24 |
+
"--weights",
|
| 25 |
+
default=os.environ.get("FELA_PDM_WEIGHTS", "."),
|
| 26 |
+
help="directory with <variant>.safetensors + config.json, or a .pt checkpoint path",
|
| 27 |
+
)
|
| 28 |
+
args = ap.parse_args()
|
| 29 |
+
src = args.weights
|
| 30 |
+
variant_file = os.path.join(src, f"{args.variant}.safetensors")
|
| 31 |
+
if os.path.isdir(src) and os.path.isfile(variant_file):
|
| 32 |
+
model = load_model(src, variant=args.variant)
|
| 33 |
+
elif os.path.isfile(src):
|
| 34 |
+
model = load_model(src, variant=args.variant)
|
| 35 |
+
else:
|
| 36 |
+
raise SystemExit(
|
| 37 |
+
f"No weights at {src}. Set FELA_PDM_WEIGHTS to a directory holding {args.variant}.safetensors and config.json (or pass a .pt checkpoint path). Weights are in lowdown-labs/FELA-pdm."
|
| 38 |
+
)
|
| 39 |
+
if args.variant.startswith("cmapss"):
|
| 40 |
+
window = torch.randn(1, 30, 14)
|
| 41 |
+
validate_window(window, model.cfg)
|
| 42 |
+
rul = model.predict(window, task="rul")
|
| 43 |
+
print(f"Variant: {args.variant}")
|
| 44 |
+
print(f"Input shape: {tuple(window.shape)} (30 cycles, 14 sensors)")
|
| 45 |
+
print(f"Estimated remaining useful life: {rul:.1f} cycles (capped at 125)")
|
| 46 |
+
else:
|
| 47 |
+
window = torch.randn(1, 2048, 1)
|
| 48 |
+
validate_window(window, model.cfg)
|
| 49 |
+
idx, prob = model.predict(window, task="cls")
|
| 50 |
+
print(f"Variant: {args.variant}")
|
| 51 |
+
print(f"Input shape: {tuple(window.shape)} (2048 vibration samples, 1 channel)")
|
| 52 |
+
print(f"Predicted fault class index: {idx} (probability {prob:.4f})")
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
if __name__ == "__main__":
|
| 56 |
+
main()
|
space/README.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: FELA-PdM playground
|
| 3 |
+
colorFrom: yellow
|
| 4 |
+
colorTo: red
|
| 5 |
+
sdk: gradio
|
| 6 |
+
sdk_version: 4.44.0
|
| 7 |
+
app_file: app.py
|
| 8 |
+
pinned: false
|
| 9 |
+
license: other
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
# FELA-PdM playground
|
| 13 |
+
|
| 14 |
+
Feed a sensor window and see the model's call. Two tabs: remaining useful life (C-MAPSS, 30
|
| 15 |
+
cycles of 14 sensors) and bearing fault (CWRU, 2048 vibration samples). Runs on CPU.
|
| 16 |
+
|
| 17 |
+
## The demo data (and why it shows generalization)
|
| 18 |
+
|
| 19 |
+
The remaining useful life head was trained on C-MAPSS subset FD001 (a single operating
|
| 20 |
+
condition). The other subsets, FD002, FD003, and FD004, use different operating conditions and
|
| 21 |
+
fault modes and were not in this head's training set, so a window from those subsets is a real
|
| 22 |
+
out of distribution test. They are public NASA data from the Prognostics Data Repository
|
| 23 |
+
(Saxena et al. 2008); download a window and paste it into the RUL tab.
|
| 24 |
+
|
| 25 |
+
The bundled examples (the synthetic RUL window and the synthetic vibration window) are clearly
|
| 26 |
+
labeled as synthetic and illustrative. They let the Space load fast without a large download.
|
| 27 |
+
They are not real measurements and should not be read as benchmark results.
|
| 28 |
+
|
| 29 |
+
## Weights
|
| 30 |
+
|
| 31 |
+
Set `FELA_PDM_WEIGHTS` (or a Space secret) to a directory holding the per head safetensors
|
| 32 |
+
(`cmapss_FD001.safetensors`, `cwru.safetensors`) and `config.json`. The weights are in the
|
| 33 |
+
Hugging Face repo `lowdown-labs/FELA-pdm`.
|
| 34 |
+
|
| 35 |
+
## Note
|
| 36 |
+
|
| 37 |
+
For research and illustration only. This is not a safety critical controller. Do not act on the
|
| 38 |
+
remaining useful life number without independent validation against your own field data.
|
space/app.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import sys
|
| 3 |
+
import gradio as gr
|
| 4 |
+
import numpy as np
|
| 5 |
+
import torch
|
| 6 |
+
|
| 7 |
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
| 8 |
+
from modeling import load_model, validate_window
|
| 9 |
+
|
| 10 |
+
WEIGHTS = os.environ.get("FELA_PDM_WEIGHTS", ".")
|
| 11 |
+
MODELS = {}
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def _try_load(variant):
|
| 15 |
+
try:
|
| 16 |
+
return load_model(WEIGHTS, variant=variant)
|
| 17 |
+
except Exception:
|
| 18 |
+
return None
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
for v in ("cmapss_FD001", "cwru"):
|
| 22 |
+
MODELS[v] = _try_load(v)
|
| 23 |
+
EXAMPLE_RUL = np.tile(np.linspace(0.3, 0.7, 30)[:, None], (1, 14))
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def score_rul(text):
|
| 27 |
+
m = MODELS.get("cmapss_FD001")
|
| 28 |
+
if m is None:
|
| 29 |
+
return "RUL weights not found. Set FELA_PDM_WEIGHTS to a dir with cmapss_FD001.safetensors + config.json."
|
| 30 |
+
try:
|
| 31 |
+
rows = [r for r in text.strip().splitlines() if r.strip()]
|
| 32 |
+
arr = np.array(
|
| 33 |
+
[[float(x) for x in r.replace(",", " ").split()] for r in rows],
|
| 34 |
+
dtype=np.float32,
|
| 35 |
+
)
|
| 36 |
+
except Exception:
|
| 37 |
+
return "Could not parse. Provide 30 rows of 14 numbers (whitespace or comma separated)."
|
| 38 |
+
if arr.shape != (30, 14):
|
| 39 |
+
return f"Expected a (30, 14) window, got {arr.shape}."
|
| 40 |
+
x = torch.from_numpy(arr).reshape(1, 30, 14)
|
| 41 |
+
validate_window(x, m.cfg)
|
| 42 |
+
rul = m.predict(x, task="rul")
|
| 43 |
+
return f"estimated remaining useful life: {rul:.1f} cycles (capped at 125)"
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def load_rul_example():
|
| 47 |
+
return "\n".join((" ".join((f"{v:.3f}" for v in row)) for row in EXAMPLE_RUL))
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def score_cwru(text, use_synth):
|
| 51 |
+
m = MODELS.get("cwru")
|
| 52 |
+
if m is None:
|
| 53 |
+
return "Bearing weights not found. Set FELA_PDM_WEIGHTS to a dir with cwru.safetensors + config.json."
|
| 54 |
+
if use_synth or not text.strip():
|
| 55 |
+
t = np.linspace(0, 1, 2048)
|
| 56 |
+
sig = np.sin(2 * np.pi * 120 * t) + 0.2 * np.random.randn(2048)
|
| 57 |
+
else:
|
| 58 |
+
try:
|
| 59 |
+
sig = np.array(
|
| 60 |
+
[float(x) for x in text.replace(",", " ").split()], dtype=np.float32
|
| 61 |
+
)
|
| 62 |
+
except Exception:
|
| 63 |
+
return (
|
| 64 |
+
"Could not parse. Provide 2048 whitespace- or comma-separated samples."
|
| 65 |
+
)
|
| 66 |
+
if sig.size != 2048:
|
| 67 |
+
return f"Expected 2048 samples, got {sig.size}."
|
| 68 |
+
sig = (sig - sig.mean()) / (sig.std() + 1e-06)
|
| 69 |
+
x = torch.from_numpy(sig.astype(np.float32)).reshape(1, 2048, 1)
|
| 70 |
+
validate_window(x, m.cfg)
|
| 71 |
+
idx, prob = m.predict(x, task="cls")
|
| 72 |
+
return f"predicted fault class index: {idx} (probability {prob:.4f})"
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
with gr.Blocks(title="FELA-PdM playground") as demo:
|
| 76 |
+
gr.Markdown(
|
| 77 |
+
"# FELA-PdM playground\nOn-device predictive maintenance. Feed a sensor window and see the model's call. For research and illustration only, not a safety-critical controller; do not act on the remaining-useful-life number without independent validation."
|
| 78 |
+
)
|
| 79 |
+
with gr.Tab("Remaining useful life (C-MAPSS)"):
|
| 80 |
+
gr.Markdown(
|
| 81 |
+
"Paste 30 cycles of 14 normalized sensor values (30 rows, 14 numbers each). The FD001 head was not trained on FD002/FD003/FD004, so a window from those public NASA subsets is a real out-of-distribution test. The example below is synthetic and illustrative."
|
| 82 |
+
)
|
| 83 |
+
rul_in = gr.Textbox(label="sensor window (30 x 14)", lines=8)
|
| 84 |
+
rul_out = gr.Textbox(label="result")
|
| 85 |
+
with gr.Row():
|
| 86 |
+
gr.Button("Load synthetic example").click(load_rul_example, outputs=rul_in)
|
| 87 |
+
gr.Button("Score").click(score_rul, inputs=rul_in, outputs=rul_out)
|
| 88 |
+
with gr.Tab("Bearing fault (CWRU)"):
|
| 89 |
+
gr.Markdown(
|
| 90 |
+
"Paste 2048 raw vibration samples (12 kHz), or tick the box to score a synthetic illustrative window. The output is a fault-class index (10 classes)."
|
| 91 |
+
)
|
| 92 |
+
cwru_in = gr.Textbox(label="vibration window (2048 samples)", lines=4)
|
| 93 |
+
cwru_synth = gr.Checkbox(
|
| 94 |
+
label="use a synthetic illustrative window", value=True
|
| 95 |
+
)
|
| 96 |
+
cwru_out = gr.Textbox(label="result")
|
| 97 |
+
gr.Button("Score").click(
|
| 98 |
+
score_cwru, inputs=[cwru_in, cwru_synth], outputs=cwru_out
|
| 99 |
+
)
|
| 100 |
+
if __name__ == "__main__":
|
| 101 |
+
demo.launch()
|
space/requirements.txt
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
torch==2.8.0
|
| 2 |
+
numpy==2.1.3
|
| 3 |
+
safetensors==0.5.3
|
| 4 |
+
huggingface-hub==0.34.4
|
| 5 |
+
gradio==4.44.0
|
streaming/cmapss_FD001_fp16.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b605009813d95d5e4f4994e7dc878a70774bbce0ebbdb1ac1405f31b94a4b65b
|
| 3 |
+
size 251818
|
streaming/cmapss_FD002_fp16.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:16c15598bee3e12b1784c8fccbaeaa3b95fe90ed23154f3f737e5b26b5aabc28
|
| 3 |
+
size 251818
|
streaming/cmapss_FD003_fp16.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:aec256a567108ee5a5b3aa4cdda5c94bfa674b1ec7e5f38c9c2c93e9868a2642
|
| 3 |
+
size 251818
|
streaming/cmapss_FD004_fp16.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c98243efa2b098460503f98081fb6c09025db71af5119a93b734db109060b95c
|
| 3 |
+
size 251818
|
streaming/cwru_fp16.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:35a2536bf6d03672e1c828d69e71bbdca935058a29e7e0aaad9ee705c9418add
|
| 3 |
+
size 268100
|
streaming/manifest.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"model": "predictive-maintenance",
|
| 3 |
+
"format": "fp16-streaming",
|
| 4 |
+
"note": "load order is smallest-first for progressive/streaming load",
|
| 5 |
+
"files": [
|
| 6 |
+
{
|
| 7 |
+
"file": "cmapss_FD001_fp16.safetensors",
|
| 8 |
+
"source": "cmapss_FD001.safetensors",
|
| 9 |
+
"dtype": "fp16",
|
| 10 |
+
"bytes": 251818,
|
| 11 |
+
"approx_mb": 0.24
|
| 12 |
+
},
|
| 13 |
+
{
|
| 14 |
+
"file": "cmapss_FD002_fp16.safetensors",
|
| 15 |
+
"source": "cmapss_FD002.safetensors",
|
| 16 |
+
"dtype": "fp16",
|
| 17 |
+
"bytes": 251818,
|
| 18 |
+
"approx_mb": 0.24
|
| 19 |
+
},
|
| 20 |
+
{
|
| 21 |
+
"file": "cmapss_FD003_fp16.safetensors",
|
| 22 |
+
"source": "cmapss_FD003.safetensors",
|
| 23 |
+
"dtype": "fp16",
|
| 24 |
+
"bytes": 251818,
|
| 25 |
+
"approx_mb": 0.24
|
| 26 |
+
},
|
| 27 |
+
{
|
| 28 |
+
"file": "cmapss_FD004_fp16.safetensors",
|
| 29 |
+
"source": "cmapss_FD004.safetensors",
|
| 30 |
+
"dtype": "fp16",
|
| 31 |
+
"bytes": 251818,
|
| 32 |
+
"approx_mb": 0.24
|
| 33 |
+
},
|
| 34 |
+
{
|
| 35 |
+
"file": "model_fp16.safetensors",
|
| 36 |
+
"source": "model.safetensors",
|
| 37 |
+
"dtype": "fp16",
|
| 38 |
+
"bytes": 251818,
|
| 39 |
+
"approx_mb": 0.24
|
| 40 |
+
},
|
| 41 |
+
{
|
| 42 |
+
"file": "cwru_fp16.safetensors",
|
| 43 |
+
"source": "cwru.safetensors",
|
| 44 |
+
"dtype": "fp16",
|
| 45 |
+
"bytes": 268100,
|
| 46 |
+
"approx_mb": 0.256
|
| 47 |
+
}
|
| 48 |
+
]
|
| 49 |
+
}
|
streaming/model_fp16.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b605009813d95d5e4f4994e7dc878a70774bbce0ebbdb1ac1405f31b94a4b65b
|
| 3 |
+
size 251818
|
train.py
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse, glob, os, time
|
| 2 |
+
import numpy as np, torch, torch.nn as nn, torch.nn.functional as F
|
| 3 |
+
import scipy.io as sio
|
| 4 |
+
from modeling import FELAPDM, PDMConfig
|
| 5 |
+
|
| 6 |
+
cwru_classes = [
|
| 7 |
+
"Normal",
|
| 8 |
+
"IR007",
|
| 9 |
+
"B007",
|
| 10 |
+
"OR007",
|
| 11 |
+
"IR014",
|
| 12 |
+
"B014",
|
| 13 |
+
"OR014",
|
| 14 |
+
"IR021",
|
| 15 |
+
"B021",
|
| 16 |
+
"OR021",
|
| 17 |
+
]
|
| 18 |
+
cwru_idx = {c: i for i, c in enumerate(cwru_classes)}
|
| 19 |
+
sensor_cols = [6, 7, 8, 11, 12, 13, 15, 16, 17, 18, 19, 21, 24, 25]
|
| 20 |
+
rul_cap = 125
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def cwru_label(f):
|
| 24 |
+
b = os.path.basename(f).split("_")[0]
|
| 25 |
+
if b in cwru_idx:
|
| 26 |
+
return cwru_idx[b]
|
| 27 |
+
raise ValueError(f)
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def load_cwru(d, win=2048, stride=1024, loads=("0", "1", "2", "3"), seed=0):
|
| 31 |
+
rng = np.random.default_rng(seed)
|
| 32 |
+
xs, ys = ([], [])
|
| 33 |
+
for f in sorted(glob.glob(os.path.join(d, "*.mat"))):
|
| 34 |
+
load = os.path.basename(f).split("_")[1].replace(".mat", "")
|
| 35 |
+
if load not in loads:
|
| 36 |
+
continue
|
| 37 |
+
m = sio.loadmat(f)
|
| 38 |
+
de = [k for k in m if k.endswith("DE_time")]
|
| 39 |
+
if not de:
|
| 40 |
+
continue
|
| 41 |
+
sig = m[de[0]].ravel().astype(np.float32)
|
| 42 |
+
sig = (sig - sig.mean()) / (sig.std() + 1e-08)
|
| 43 |
+
lab = cwru_label(f)
|
| 44 |
+
for s in range(0, len(sig) - win + 1, stride):
|
| 45 |
+
xs.append(sig[s : s + win])
|
| 46 |
+
ys.append(lab)
|
| 47 |
+
x = np.stack(xs).astype(np.float32)[..., None]
|
| 48 |
+
y = np.array(ys, np.int64)
|
| 49 |
+
idx = rng.permutation(len(x))
|
| 50 |
+
return (x[idx], y[idx])
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def cwru_split(x, y, test_frac=0.25, seed=0):
|
| 54 |
+
rng = np.random.default_rng(seed)
|
| 55 |
+
n = len(x)
|
| 56 |
+
perm = rng.permutation(n)
|
| 57 |
+
nte = int(n * test_frac)
|
| 58 |
+
te, tr = (perm[:nte], perm[nte:])
|
| 59 |
+
return (x[tr], y[tr], x[te], y[te])
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def read_cmapss(p):
|
| 63 |
+
return np.loadtxt(p)
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
def load_cmapss(d, subset="FD001", win=30, cap=rul_cap):
|
| 67 |
+
train = read_cmapss(os.path.join(d, f"train_{subset}.txt"))
|
| 68 |
+
test = read_cmapss(os.path.join(d, f"test_{subset}.txt"))
|
| 69 |
+
truth = read_cmapss(os.path.join(d, f"RUL_{subset}.txt")).ravel()
|
| 70 |
+
s = sensor_cols
|
| 71 |
+
tr_s = train[:, s]
|
| 72 |
+
smin, smax = (tr_s.min(0), tr_s.max(0))
|
| 73 |
+
rng = smax - smin
|
| 74 |
+
rng[rng == 0] = 1.0
|
| 75 |
+
|
| 76 |
+
def norm(a):
|
| 77 |
+
return (a[:, s] - smin) / rng
|
| 78 |
+
|
| 79 |
+
xtr, ytr = ([], [])
|
| 80 |
+
for u in np.unique(train[:, 0]):
|
| 81 |
+
eng = train[train[:, 0] == u]
|
| 82 |
+
feats = norm(eng).astype(np.float32)
|
| 83 |
+
L = len(feats)
|
| 84 |
+
mc = eng[:, 1].max()
|
| 85 |
+
for e in range(win, L + 1):
|
| 86 |
+
xtr.append(feats[e - win : e])
|
| 87 |
+
ytr.append(min(mc - eng[e - 1, 1], cap))
|
| 88 |
+
if L < win:
|
| 89 |
+
pad = np.zeros((win - L, len(s)), np.float32)
|
| 90 |
+
xtr.append(np.concatenate([pad, feats], 0))
|
| 91 |
+
ytr.append(min(mc - eng[-1, 1], cap))
|
| 92 |
+
xtr = np.stack(xtr).astype(np.float32)
|
| 93 |
+
ytr = np.array(ytr, np.float32)
|
| 94 |
+
xte, yte = ([], [])
|
| 95 |
+
for i, u in enumerate(np.unique(test[:, 0])):
|
| 96 |
+
eng = test[test[:, 0] == u]
|
| 97 |
+
feats = norm(eng).astype(np.float32)
|
| 98 |
+
L = len(feats)
|
| 99 |
+
if L >= win:
|
| 100 |
+
w = feats[L - win :]
|
| 101 |
+
else:
|
| 102 |
+
pad = np.zeros((win - L, len(s)), np.float32)
|
| 103 |
+
w = np.concatenate([pad, feats], 0)
|
| 104 |
+
xte.append(w)
|
| 105 |
+
yte.append(min(truth[i], cap))
|
| 106 |
+
return (xtr, ytr, np.stack(xte).astype(np.float32), np.array(yte, np.float32))
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
def cmapss_score(yt, yp):
|
| 110 |
+
d = yp - yt
|
| 111 |
+
s = np.where(d < 0, np.exp(-d / 13.0) - 1.0, np.exp(d / 10.0) - 1.0)
|
| 112 |
+
return (float(np.sqrt(np.mean(d**2))), float(np.sum(s)))
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
def batches(x, y, bs, seed=0):
|
| 116 |
+
idx = np.arange(len(x))
|
| 117 |
+
np.random.default_rng(seed).shuffle(idx)
|
| 118 |
+
for i in range(0, len(x), bs):
|
| 119 |
+
j = idx[i : i + bs]
|
| 120 |
+
yield (x[j], y[j])
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
def cfg_cwru(**kw):
|
| 124 |
+
c = PDMConfig(
|
| 125 |
+
in_channels=1,
|
| 126 |
+
patch=4,
|
| 127 |
+
n_embd=64,
|
| 128 |
+
n_layer=4,
|
| 129 |
+
n_head=4,
|
| 130 |
+
fno_modes=64,
|
| 131 |
+
ffn_hidden=128,
|
| 132 |
+
n_classes=10,
|
| 133 |
+
seq_len=2048,
|
| 134 |
+
)
|
| 135 |
+
for k, v in kw.items():
|
| 136 |
+
setattr(c, k, v)
|
| 137 |
+
return c
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
def cfg_cmapss(**kw):
|
| 141 |
+
c = PDMConfig(
|
| 142 |
+
in_channels=14,
|
| 143 |
+
patch=1,
|
| 144 |
+
n_embd=64,
|
| 145 |
+
n_layer=4,
|
| 146 |
+
n_head=4,
|
| 147 |
+
fno_modes=32,
|
| 148 |
+
ffn_hidden=128,
|
| 149 |
+
rul_head=True,
|
| 150 |
+
seq_len=64,
|
| 151 |
+
)
|
| 152 |
+
for k, v in kw.items():
|
| 153 |
+
setattr(c, k, v)
|
| 154 |
+
return c
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
def train_cwru(a):
|
| 158 |
+
dev = torch.device(a.device)
|
| 159 |
+
x, y = load_cwru(a.data, win=2048, stride=a.stride, seed=a.seed)
|
| 160 |
+
xtr, ytr, xte, yte = cwru_split(x, y, test_frac=0.25, seed=a.seed)
|
| 161 |
+
assert len(x) == 5886
|
| 162 |
+
if a.smoke:
|
| 163 |
+
print(
|
| 164 |
+
f"Cwru total {len(x)} train {len(xtr)} test {len(xte)} classes {len(set(y.tolist()))}"
|
| 165 |
+
)
|
| 166 |
+
return
|
| 167 |
+
cfg = cfg_cwru(dropout=a.dropout)
|
| 168 |
+
m = FELAPDM(cfg).to(dev)
|
| 169 |
+
opt = torch.optim.AdamW(m.parameters(), lr=a.lr, weight_decay=0.0001)
|
| 170 |
+
sch = torch.optim.lr_scheduler.CosineAnnealingLR(opt, a.epochs)
|
| 171 |
+
xte_t = torch.from_numpy(xte).to(dev)
|
| 172 |
+
yte_t = torch.from_numpy(yte).to(dev)
|
| 173 |
+
best = 0.0
|
| 174 |
+
for ep in range(a.epochs):
|
| 175 |
+
m.train()
|
| 176 |
+
for xb, yb in batches(xtr, ytr, a.bs, seed=a.seed + ep):
|
| 177 |
+
xb = torch.from_numpy(xb).to(dev)
|
| 178 |
+
yb = torch.from_numpy(yb).to(dev)
|
| 179 |
+
loss = F.cross_entropy(m(xb, task="cls"), yb)
|
| 180 |
+
opt.zero_grad()
|
| 181 |
+
loss.backward()
|
| 182 |
+
nn.utils.clip_grad_norm_(m.parameters(), 1.0)
|
| 183 |
+
opt.step()
|
| 184 |
+
sch.step()
|
| 185 |
+
m.eval()
|
| 186 |
+
with torch.no_grad():
|
| 187 |
+
pr = torch.cat(
|
| 188 |
+
[
|
| 189 |
+
m(xte_t[i : i + 256], task="cls").argmax(-1)
|
| 190 |
+
for i in range(0, len(xte_t), 256)
|
| 191 |
+
]
|
| 192 |
+
)
|
| 193 |
+
acc = (pr == yte_t).float().mean().item()
|
| 194 |
+
if acc > best:
|
| 195 |
+
best = acc
|
| 196 |
+
torch.save(
|
| 197 |
+
{"cfg": cfg.__dict__, "model": m.state_dict(), "classes": cwru_classes},
|
| 198 |
+
a.out,
|
| 199 |
+
)
|
| 200 |
+
print(f"Ep {ep:02d} acc {acc * 100:.2f} best {best * 100:.2f}")
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
def train_cmapss(a):
|
| 204 |
+
dev = torch.device(a.device)
|
| 205 |
+
xtr, ytr, xte, yte = load_cmapss(a.data, subset=a.subset, win=a.win)
|
| 206 |
+
if a.subset == "FD001":
|
| 207 |
+
assert len(xtr) == 17731
|
| 208 |
+
if a.smoke:
|
| 209 |
+
print(f"Cmapss {a.subset} train {xtr.shape} test {xte.shape}")
|
| 210 |
+
return
|
| 211 |
+
cfg = cfg_cmapss(dropout=a.dropout, seq_len=a.win)
|
| 212 |
+
m = FELAPDM(cfg).to(dev)
|
| 213 |
+
opt = torch.optim.AdamW(m.parameters(), lr=a.lr, weight_decay=0.0001)
|
| 214 |
+
sch = torch.optim.lr_scheduler.CosineAnnealingLR(opt, a.epochs)
|
| 215 |
+
cap = 125.0
|
| 216 |
+
xte_t = torch.from_numpy(xte).to(dev)
|
| 217 |
+
best = 1000000000.0
|
| 218 |
+
for ep in range(a.epochs):
|
| 219 |
+
m.train()
|
| 220 |
+
for xb, yb in batches(xtr, ytr / cap, a.bs, seed=a.seed + ep):
|
| 221 |
+
xb = torch.from_numpy(xb).to(dev)
|
| 222 |
+
yb = torch.from_numpy(yb).to(dev)
|
| 223 |
+
loss = F.mse_loss(m(xb, task="rul"), yb)
|
| 224 |
+
opt.zero_grad()
|
| 225 |
+
loss.backward()
|
| 226 |
+
nn.utils.clip_grad_norm_(m.parameters(), 1.0)
|
| 227 |
+
opt.step()
|
| 228 |
+
sch.step()
|
| 229 |
+
m.eval()
|
| 230 |
+
with torch.no_grad():
|
| 231 |
+
pr = np.clip(
|
| 232 |
+
torch.cat(
|
| 233 |
+
[
|
| 234 |
+
m(xte_t[i : i + 256], task="rul")
|
| 235 |
+
for i in range(0, len(xte_t), 256)
|
| 236 |
+
]
|
| 237 |
+
)
|
| 238 |
+
.cpu()
|
| 239 |
+
.numpy()
|
| 240 |
+
* cap,
|
| 241 |
+
0,
|
| 242 |
+
cap,
|
| 243 |
+
)
|
| 244 |
+
rmse, score = cmapss_score(yte, pr)
|
| 245 |
+
if rmse < best:
|
| 246 |
+
best = rmse
|
| 247 |
+
torch.save(
|
| 248 |
+
{"cfg": cfg.__dict__, "model": m.state_dict(), "rul_cap": cap}, a.out
|
| 249 |
+
)
|
| 250 |
+
print(f"Ep {ep:02d} RMSE {rmse:.2f} score {score:.0f} best {best:.2f}")
|
| 251 |
+
|
| 252 |
+
|
| 253 |
+
def main():
|
| 254 |
+
ap = argparse.ArgumentParser()
|
| 255 |
+
ap.add_argument("--task", choices=["cwru", "cmapss"], required=True)
|
| 256 |
+
ap.add_argument("--data", default=None)
|
| 257 |
+
ap.add_argument("--subset", default="FD001")
|
| 258 |
+
ap.add_argument("--out", default=None)
|
| 259 |
+
ap.add_argument("--epochs", type=int, default=40)
|
| 260 |
+
ap.add_argument("--bs", type=int, default=64)
|
| 261 |
+
ap.add_argument("--lr", type=float, default=0.002)
|
| 262 |
+
ap.add_argument("--win", type=int, default=30)
|
| 263 |
+
ap.add_argument("--stride", type=int, default=1024)
|
| 264 |
+
ap.add_argument("--dropout", type=float, default=0.0)
|
| 265 |
+
ap.add_argument("--device", default="cpu")
|
| 266 |
+
ap.add_argument("--seed", type=int, default=0)
|
| 267 |
+
ap.add_argument("--smoke", action="store_true")
|
| 268 |
+
a = ap.parse_args()
|
| 269 |
+
if a.task == "cwru":
|
| 270 |
+
a.data = a.data or "../data/cwru"
|
| 271 |
+
a.out = a.out or "./pdm_cwru_fno.pt"
|
| 272 |
+
train_cwru(a)
|
| 273 |
+
else:
|
| 274 |
+
a.data = a.data or "../data/cmapss"
|
| 275 |
+
a.out = a.out or f"./pdm_cmapss_{a.subset}_fno.pt"
|
| 276 |
+
train_cmapss(a)
|
| 277 |
+
|
| 278 |
+
|
| 279 |
+
if __name__ == "__main__":
|
| 280 |
+
main()
|
verify.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
import os
|
| 3 |
+
import sys
|
| 4 |
+
import torch
|
| 5 |
+
|
| 6 |
+
sys.path.insert(0, os.path.dirname(__file__))
|
| 7 |
+
from modeling import load_model
|
| 8 |
+
|
| 9 |
+
SAMPLES = {
|
| 10 |
+
"cmapss_FD001": torch.full((1, 30, 14), 0.5),
|
| 11 |
+
"cmapss_FD002": torch.full((1, 30, 14), 0.5),
|
| 12 |
+
"cmapss_FD003": torch.full((1, 30, 14), 0.5),
|
| 13 |
+
"cmapss_FD004": torch.full((1, 30, 14), 0.5),
|
| 14 |
+
"cwru": torch.linspace(-1, 1, 2048).reshape(1, 2048, 1),
|
| 15 |
+
}
|
| 16 |
+
VERIFICATION = {
|
| 17 |
+
"cmapss_FD001": {"value": 0.390998, "tol": 0.001},
|
| 18 |
+
"cmapss_FD002": {"value": 0.038257, "tol": 0.001},
|
| 19 |
+
"cmapss_FD003": {"value": 0.788864, "tol": 0.001},
|
| 20 |
+
"cmapss_FD004": {"value": 0.414077, "tol": 0.001},
|
| 21 |
+
"cwru": {"value": 0, "tol": 0.001},
|
| 22 |
+
}
|
| 23 |
+
EXPECTED_SHAPE = {
|
| 24 |
+
"cmapss_FD001": (1,),
|
| 25 |
+
"cmapss_FD002": (1,),
|
| 26 |
+
"cmapss_FD003": (1,),
|
| 27 |
+
"cmapss_FD004": (1,),
|
| 28 |
+
"cwru": (1, 10),
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def main():
|
| 33 |
+
ap = argparse.ArgumentParser()
|
| 34 |
+
ap.add_argument("--variant", default="cmapss_FD001", choices=list(SAMPLES))
|
| 35 |
+
ap.add_argument("--weights", default=os.environ.get("FELA_PDM_WEIGHTS", "."))
|
| 36 |
+
args = ap.parse_args()
|
| 37 |
+
model = load_model(args.weights, variant=args.variant)
|
| 38 |
+
x = SAMPLES[args.variant]
|
| 39 |
+
with torch.no_grad():
|
| 40 |
+
out = model(x)
|
| 41 |
+
exp = EXPECTED_SHAPE[args.variant]
|
| 42 |
+
if tuple(out.shape) != exp:
|
| 43 |
+
print(f"Fail: output shape {tuple(out.shape)} != expected {exp}")
|
| 44 |
+
sys.exit(1)
|
| 45 |
+
print(f"Shape OK: {tuple(out.shape)}")
|
| 46 |
+
g = VERIFICATION[args.variant]
|
| 47 |
+
if g["value"] is None:
|
| 48 |
+
if args.variant.startswith("cmapss"):
|
| 49 |
+
captured = float(out.reshape(-1)[0])
|
| 50 |
+
else:
|
| 51 |
+
captured = int(out.argmax(-1).item())
|
| 52 |
+
print(f"Captured output: {captured}")
|
| 53 |
+
print(
|
| 54 |
+
"Verification value is a placeholder. Paste this captured value into VERIFICATION and re-run to enable the check. Shape check passed."
|
| 55 |
+
)
|
| 56 |
+
return
|
| 57 |
+
if args.variant.startswith("cmapss"):
|
| 58 |
+
got = float(out.reshape(-1)[0])
|
| 59 |
+
if abs(got - g["value"]) > g["tol"]:
|
| 60 |
+
print(f"Fail: RUL {got} vs verification {g['value']} (tol {g['tol']})")
|
| 61 |
+
sys.exit(1)
|
| 62 |
+
else:
|
| 63 |
+
got = int(out.argmax(-1).item())
|
| 64 |
+
if got != g["value"]:
|
| 65 |
+
print(f"Fail: class {got} vs verification {g['value']}")
|
| 66 |
+
sys.exit(1)
|
| 67 |
+
print("Verification check OK")
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
if __name__ == "__main__":
|
| 71 |
+
main()
|