<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Anshuman Das on Medium]]></title>
        <description><![CDATA[Stories by Anshuman Das on Medium]]></description>
        <link>https://medium.com/@zicodasanshuman?source=rss-6bf9c9b887df------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*jlOWY8NlCY4GaHah.jpg</url>
            <title>Stories by Anshuman Das on Medium</title>
            <link>https://medium.com/@zicodasanshuman?source=rss-6bf9c9b887df------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 21:43:18 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@zicodasanshuman/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[From Helicopter Physics to an Engine Health Review Dashboard]]></title>
            <link>https://medium.com/@zicodasanshuman/from-helicopter-physics-to-an-engine-health-review-dashboard-7d85ec6a8338?source=rss-6bf9c9b887df------2</link>
            <guid isPermaLink="false">https://medium.com/p/7d85ec6a8338</guid>
            <category><![CDATA[predictive-maintenance]]></category>
            <category><![CDATA[data-science]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[aerospace]]></category>
            <dc:creator><![CDATA[Anshuman Das]]></dc:creator>
            <pubDate>Fri, 02 Oct 2026 11:01:15 GMT</pubDate>
            <atom:updated>2026-10-02T11:01:15.904Z</atom:updated>
            <content:encoded><![CDATA[<p><em>How we combined engineering intuition, statistical features and machine learning — and then tested where the model stops being dependable.</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5ZxEU13lCv5EtoUwAutDTA.png" /></figure><p>A helicopter engine measurement needs context.</p><p>A temperature that looks unusual in one situation might be expected in another. A torque reading means more when we know the conditions under which it was produced. Before asking an algorithm whether an engine is healthy, we need to understand the physical system it is observing.</p><p>That was the starting point for this project: use the PHM Society’s 2024 helicopter turbine engine data challenge to build a complete modelling and review workflow. The work grew from exploratory analysis into a reproducible harness covering feature engineering, model selection, uncertainty, operating-condition diagnostics and a local review dashboard.</p><p>The examples below use invented numbers to explain the ideas. The results section identifies the actual experiment results separately.</p><h3>A short physics primer</h3><h3>The engine supplies the turning effort; the rotor produces lift</h3><p>Many turbine-powered helicopters use a turboshaft engine. Air enters a compressor, fuel burns in a combustion chamber, and expanding gases drive turbines. Through a shaft and transmission, the engine supplies mechanical power to the rotor system. The spinning rotor blades produce aerodynamic lift. [1]</p><p>Two useful concepts are <strong>torque</strong> and <strong>power</strong>. Torque is twisting effort — like the effort you apply when turning a wrench. Power describes how quickly work is being done. For a rotating shaft, power equals torque multiplied by angular speed. A torque measurement is therefore informative, but without a correctly interpreted shaft speed it is not a complete power measurement.</p><h3>Operating conditions change the picture</h3><p>Helicopter performance depends on the engine and rotor together. Air density, aircraft weight and wind matter. Hot conditions and higher altitude can reduce air density; a heavier helicopter requires more power to hover. Airflow through the rotor also changes the power needed, so hovering and forward flight cannot be treated as equivalent situations. [2]</p><p>Imagine comparing an engine on a cool day near sea level with an engine working in hot, less dense air. Different readings need not mean one engine is damaged. Some differences may reflect different operating demands and environmental conditions.</p><p>That creates the central modelling challenge: distinguish changes associated with operating conditions from patterns associated with the labelled engine health state.</p><p>Our dataset does not contain every quantity needed for a complete physical explanation. We do not have fuel flow, aircraft weight, wind, a full engine map or all the internal pressures and temperatures. Physics can guide useful comparisons, but it cannot fill in missing measurements by assumption.</p><h3>Torque margin compares output with a reference</h3><p>The challenge defines torque margin by comparing measured torque with a design target for the operating conditions: [3]</p><pre>Torque margin (%) = 100 × (measured torque − target torque) / target torque</pre><p>Suppose the target is 100 units and the engine produces 95. The margin is −5%. If it produces 103, the margin is +3%.</p><p>This is a <strong>reference-relative performance measure</strong>. It is not automatically an aircraft’s available reserve power, and it is not a direct permission to continue operating.</p><p>It also does not completely determine the fault label. Our data contains healthy observations with negative margins and faulty observations with nonnegative margins. We therefore predict the margin and the fault label as two related outcomes.</p><h3>The business problem</h3><p>The challenge asks for a health classification and a probability distribution for torque margin. It supplies observations from seven engines of the same make and model: four contribute training data and three contribute the external datasets. Engine identities are removed and the observations are shuffled. [3]</p><p>We translated that task into a practical engineering question:</p><p><strong>Can we turn engine measurements into a useful review signal, while showing when the model’s evidence is weak?</strong></p><p>A useful result should tell a reviewer the estimated fault probability, the predicted margin, the uncertainty around that estimate and whether the measurements resemble the model’s reference experience.</p><p>The potential users are maintenance engineers, engine health monitoring teams, reliability analysts and fleet planners. Earlier attention to concerning observations could support inspection planning and focused engineering review. But those are intended benefits, not measured savings from this project. We have not established avoided failures, reduced downtime or a financial return.</p><p>Missing a fault and creating an unnecessary alert have different consequences. A high accuracy number does not settle that tradeoff. The eventual review action and acceptable alert burden need agreement with the people who will use the system.</p><h3>Step 1 — Build a harness around the experiment</h3><p>A harness is the machinery that makes an experiment repeatable.</p><p>Instead of relying on a notebook’s current state, our Python harness reads the data, checks its structure, creates partitions, trains candidates, compares them, saves the selected model and writes evaluation outputs.</p><p>It also records the configuration, data hashes and software environment. A data hash is a fingerprint of a file’s contents. It helps reveal whether the input changed between experiments.</p><p>For example, if an experiment improves after someone replaces a CSV, we need to distinguish a better method from different data. Keeping those records makes the comparison reviewable.</p><p>Cursor is the development workspace for editing and running the code. The harness itself is an ordinary Python project; it does not depend on Cursor to make predictions.</p><h3>Step 2 — Check what the data actually contains</h3><p>Exploratory data analysis, or EDA, is the first investigation of the dataset.</p><p>We inspect missing values, distributions, extreme values, repeated measurements, target relationships and differences between the training and external data. We also check IDs and join features to labels by ID rather than assuming row order matches.</p><p>The original labelled dataset contains 742,625 observations and seven predictors: measured torque, outside air temperature, mean gas temperature, PA, indicated airspeed, NP and compressor speed.</p><p>An important finding is that the data has no usable timestamps or engine identifiers. We cannot treat adjacent rows as adjacent moments in a flight.</p><p>For example, subtracting row 101’s temperature from row 100’s temperature would not measure a temperature change over time. Those observations may come from unrelated situations. That rules out rolling averages, degradation trajectories and meaningful frequency analysis in this harness.</p><p>Another finding is that fault labels are not simply determined by whether torque margin is negative. A sign-based rule would discard information that the model needs to learn.</p><h3>Step 3 — Resolve meanings before writing physics equations</h3><p>Column names can be misleading.</p><p>The organizer describes PA as power available. A published participant paper interprets PA as pressure altitude. Those meanings are incompatible. [3, 4]</p><p>Our default configuration therefore treats PA as <strong>opaque</strong>: it uses the numbers as predictors without assuming an altitude interpretation. NP is also retained, but its physical interpretation requires further confirmation.</p><p>Suppose a PA value is 500. If it is altitude in metres, it might support an atmospheric calculation. If it is a power-related quantity, that calculation would be meaningless.</p><p>The harness allows optional altitude and air-density features only when the meaning and units are explicitly configured. Celsius temperature interpretation is also an explicit assumption, rather than an invisible convention.</p><p>This is where a data owner or domain engineer becomes essential. A plausible numerical range does not establish what a sensor measures.</p><h3>Step 4 — Separate learning from examination</h3><p>Models need an examination they cannot study for.</p><p>We create four partitions: approximately 60% for initial fitting, 15% for selection, 10% for calibration and 15% for final evaluation. The proportions are based on groups, so row counts do not match those percentages exactly.</p><p>The fitting partition teaches candidate models. The selection partition compares approaches. The calibration partition adjusts probabilities, interval scale and the conventional fault threshold. The final holdout assesses the chosen approach.</p><p>We keep exact repeated measurements together. Otherwise, a model might train on one copy and be examined on an identical copy.</p><p>That is like giving a student a question in a practice paper and repeating it word for word in the exam. The result overstates how well the student handles unfamiliar questions.</p><p>This grouping reduces duplicate leakage. It does not prove engine independence: different measurements from the same unknown engine can still appear in different partitions. Our internal holdout cannot establish performance on new engines.</p><h3>Step 5 — Give the model physics-informed clues</h3><p>Feature engineering creates useful attributes from the original measurements.</p><p>One example is the difference between mean gas temperature and outside air temperature. If both temperatures use the same units, their difference can describe a thermal relationship more clearly than either value alone.</p><p>Another example is a temperature-adjusted compressor-speed proxy. With outside temperature expressed in Kelvin, we compare NG after a simple temperature normalization. It is an approximate engineering clue, not a complete correction using all engine inlet conditions.</p><p>We also construct torque ratios, temperature ratios and interactions. An interaction lets the model consider two measurements together — for example, whether the relationship between torque and compressor speed changes across operating conditions.</p><p>Imagine two observations with the same torque but very different thermal readings. A derived relationship may help the model distinguish them.</p><p>These attributes are <strong>physics-informed proxies</strong>. We do not call a torque-to-temperature ratio an efficiency measurement: the required energy-flow information is absent. Likewise, zero indicated airspeed is an operating-state clue, not proof that the helicopter is hovering.</p><h3>Step 6 — Add statistical context</h3><p>Physics suggests relationships. Statistics describes what is typical in the observed data.</p><p>We calculate robust deviations from typical values, empirical percentile positions and tail indicators. A percentile tells us where a reading sits relative to the reference observations.</p><p>For example, a temperature near the 98th percentile is high relative to the fitting data. That does not make it faulty; it tells the model and analyst that the reading is unusual in that population.</p><p>We also group observations into six operating regimes using OAT, PA and IAS. A regime is a cluster of similar conditions, not a recovered engine identity.</p><p>This makes it possible to ask whether a gas-temperature reading is unusual <strong>for its operating neighbourhood</strong>, instead of comparing it only with one global average.</p><p>The statistical reference values are learned from fitting data. During validation, the held-out observations do not get to redefine what counts as typical.</p><h3>Step 7 — Compare approaches instead of assuming complexity wins</h3><p>We test candidates using raw inputs, physics-informed features, statistical features and combinations of them.</p><p>A simpler Ridge margin model provides a control. The main nonlinear models use histogram gradient boosting: successive small decision trees improve predictions by learning patterns left unexplained by earlier trees.</p><p>We also test a design-torque reconstruction approach. On labelled fitting data, the margin equation can be rearranged to recover a supervised design-torque target. A model predicts that target from environmental inputs, and the margin is then reconstructed.</p><p>Crucially, the true margin is used to construct a training target, never as an input at prediction time.</p><p>The reconstruction approach did not win the full experiment. That is useful evidence. An elegant physical hypothesis can still be too restrictive or incomplete for the available data.</p><p>The winning approach was a reduced model combining raw and derived features with direct margin prediction. We did not establish that every physics feature helps, or that a fully physical model is superior.</p><h3>Step 8 — Let predicted margin help classify faults</h3><p>The classifier uses the original and engineered attributes plus a predicted torque margin. This is called stacking: one model’s output becomes another model’s input.</p><p>There is a trap. If we predict margins on the same observations used to train the margin model, those predictions can be unrealistically good. The classifier may learn to trust that clue too much.</p><p>We use <strong>out-of-fold predictions</strong> instead.</p><p>Divide fitting data into five group-aware folds. Train the margin model on four folds and predict the fifth. Repeat until every observation has a prediction from a model that did not train on its group. Learned features are also refitted inside each fold.</p><p>It is similar to asking a tutor to evaluate students using questions the tutor has not already rehearsed with them.</p><p>The same held-out errors help train the uncertainty model. At inference, the margin model has been fitted on all allowed fitting rows, so its behaviour may differ somewhat from the fold models. Operational evaluation needs to check that this difference remains acceptable.</p><h3>Step 9 — Test whether fewer features are enough</h3><p>Derived features often overlap. Several ratios may carry much of the same information.</p><p>We measure classifier permutation importance on selection data: shuffle one feature, then observe how much AUC drops. AUC describes how well probabilities rank faulty observations above healthy ones.</p><p>If shuffling a feature damages that ranking, the classifier was using it helpfully. We retain the raw measurements, select up to 22 positively ranked features, remove duplicates and train a reduced candidate. The predicted margin remains a separate classifier input.</p><p>The reduced model must compete with its larger parent. We compare them using a joint development utility that considers classification ranking, probability quality and margin-distribution quality.</p><p>For example, dropping redundant features might preserve predictions while making the model smaller. But correlated features can share credit, so a low individual importance is not proof that a feature is useless.</p><p>The same selection partition ranks features and compares candidates. That can favour choices suited to that particular sample. The separate final holdout provides another check; repeated development splits would be useful if feature choices prove unstable.</p><h3>Step 10 — Calibrate probabilities and uncertainty</h3><p>A model can rank observations well while expressing too much confidence.</p><p>Suppose it assigns 80% fault probability to a collection of observations. If only about half are actually faulty, the ranking might still be useful, but the probabilities need correction.</p><p>We fit a smooth probability calibration on the separate calibration partition. We also adjust the predicted margin scale using calibration errors.</p><p>The margin output is a Gaussian distribution with a predicted centre and a positive scale. An illustrative output might be a mean margin of −3 percentage points and a 90% interval from −5 to −1. The interval communicates the spread of the empirical predictive distribution.</p><p>A 90% interval is not a 90% fault probability, and it is not a guaranteed bound for an individual engine. We check how often intervals contain true margins across evaluation observations, especially under changed conditions.</p><p>The conventional fault threshold is chosen using F2, which places more emphasis on recall than precision. It is a development choice, not an approved maintenance cost tradeoff. The challenge submission uses a separate score-optimized class and confidence; that confidence must not be confused with calibrated fault probability.</p><h3>Step 11 — Examine more than accuracy</h3><p>After choosing the configuration, we refit the final model on fitting plus selection rows, calibrate it separately and assess it on the final holdout.</p><p>The full experiment supplied the following internal results:</p><p>MeasureFull run internal holdoutObservations111,381Classification accuracy99.78%Fault recall99.88%Missed faulty observations52False positive observations192Torque margin MAE0.535 percentage pointsCoverage of the 90% margin intervals90.63%</p><p>Recall tells us how many labelled faulty observations were detected. Precision tells us how many flags were correct. MAE describes the typical absolute margin error. Interval coverage checks the uncertainty estimates.</p><p>An MAE of 0.535 percentage points means, for example, that predicting a margin of 4% when it is actually 4.5% produces an error of 0.5 percentage points. It does not mean the aircraft’s power is known within 0.535%.</p><p>The uploaded full-run outputs support this results table; its executable model remains in the user’s local workspace. Separately, the bundled sample model has reproducible verification evidence. Neither population supplies labelled external challenge accuracy or operational fleet accuracy.</p><h3>Step 12 — Ask where the model loses support</h3><p>An impressive average can hide a weak operating region.</p><p>We deliberately remove an entire operating regime from fitting and calibration, then evaluate observations from that regime. This asks what happens when the chosen approach loses coverage of particular joint conditions.</p><p>Regime 5 was particularly revealing. In the original exclusion experiment, fault recall fell to about 89.7%, and coverage of the nominal 90% intervals fell to about 64.4%.</p><p>A subsequent reproduction compared models on the same 24,456 regime observations. The main model missed 11 faults. The regime-excluded model missed 617. A model with a similar amount of training data removed randomly by duplicate group missed 4.</p><p>The reproduction’s membership differs slightly from the original experiment, so it is not an exact recovery. The comparison nevertheless suggests that losing a particular part of operating-condition coverage mattered much more than simply losing the same amount of data.</p><p>Nearly all those observations remained inside individual measurement ranges. Their <strong>combinations</strong> were poorly covered.</p><p>Think of someone who has driven in heavy rain and someone who has driven on steep roads. Neither fact proves experience driving on a steep road in heavy rain. Individual conditions can be familiar while their combination is unfamiliar.</p><p>The investigation is exploratory, with hidden engine effects and only one random-removal control seed. It identifies a coverage concern rather than proving a failure mechanism. It also does not mean every regime 5 observation should be rejected: the normal full model had that region in training.</p><h3>Step 13 — Freeze the baseline and build reliability checks</h3><p>Once we move into a pilot, the model must stop changing underneath the evaluation.</p><p>Freezing preserves the predictive artifact, calibration and conventional threshold. A separate monitoring sidecar describes reference support. It checks marginal ranges, distance from similar measurements, unusual interval width and calibration evidence for operating regimes.</p><p>For example, two rows may receive the same fault probability, but one resembles many reference observations while the other has an unfamiliar joint combination. The latter deserves additional attention to the prediction’s support.</p><p>These are provisional reliability checks. They flag concerns; they do not estimate a certified probability that the model is wrong.</p><p>We also check the software environment before loading the artifact. A model trained under one scikit-learn version should not silently run under a different version. The full artifact requires its compatible environment; the sample has its own pinned environment.</p><p>Model identity is protected with hashes. Those hashes reveal changes relative to the manifest, but they are not a security signature. Serialized model files must come from a trusted source.</p><h3>Step 14 — Replay existing data through the complete system</h3><p>Local replay runs the frozen prediction workflow on CSVs already available on the computer.</p><p>It produces predictions, reliability reasons and dashboard records. Where labels are available, it also measures errors. The purpose is to demonstrate that ingestion, inference, monitoring, evaluation and review fit together.</p><p>An observation might produce an estimated fault probability, a margin interval and a warning that its environmental combination is far from the reference measurements. That complete record is more useful for review than a bare label of 0 or 1.</p><p>The bundled milestone includes verified labelled and unlabelled replays and 34 passing software tests in the sample environment. The tests check contracts such as invalid input rejection, frozen prediction reproduction and label isolation. They do not establish operational suitability.</p><h3>Step 15 — Move to operational shadow mode and delayed labels</h3><p>Operational shadow means making predictions on real operational observations while the established engineering and maintenance process continues.</p><p>Suppose measurements arrive on Monday and inspection confirms their labelled outcome on Friday. The system saves Monday’s predictions and later evaluates those exact predictions against Friday’s labels. It does not rerun a newer model and rewrite what the system previously believed.</p><p>Each label evaluation creates a new snapshot. Partial labels remain partial; cumulative coverage requires a cumulative export. Invalid rows and exact fitting or calibration measurement matches are counted, with those matches excluded from reported evaluation metrics. This prevents direct reuse of known measurements from inflating the assessment, but it does not establish engine independence.</p><p>Shadow mode requires confirmed units, real engine and flight identifiers, timestamps, quality indicators and outcome provenance. It can start with batches; a live streaming connection is not required.</p><p>We have not yet completed a labelled operational study. The current delayed-label demonstration and temporary test fixtures exercise the software workflow, not real maintenance evidence.</p><h3>Step 16 — Give engineers a review queue</h3><p>The dashboard presents predictions alongside their support information and review history.</p><p>Its provisional ordering puts a fault flag with a reliability warning first. Supported fault flags and rejected measurements follow, then warnings without fault flags, then routine observations.</p><p>For example, an unsupported fault flag needs someone to examine both the possible fault and whether the model has enough relevant experience. A rejected row instead needs a data-quality correction.</p><p>These priorities describe workflow, not physical fault severity. True labels and prediction errors do not determine the operational ordering, because they would often be unavailable when review begins.</p><p>Reviewers can record follow-up or completion. Completing a review removes it from the open queue without changing the saved prediction. Review history is scoped to the model and prediction cohort so reused observation IDs cannot mix unrelated batches.</p><p>The web app runs locally with bounded requests and session checks. Shared deployment would require authenticated users, protected transport, roles, backups and operational ownership. It is currently an engineering pilot, not a fleet-wide production service.</p><h3>What would make this useful in practice?</h3><p>The next milestone is independent operational evidence.</p><p>We need an agreed data contract, representative engines and flights, trustworthy delayed labels and a clearly defined review action. Before inspecting outcomes, stakeholders should agree acceptable missed-fault rates, false-alert burden, interval coverage, data rejection and minimum evidence counts.</p><p>Evaluation should report both overall performance and performance in difficult or poorly supported conditions. Row counts alone are insufficient: thousands of correlated observations from one engine do not replace experience across independent engines.</p><p>We also need to measure whether the queue fits reviewer capacity and whether warnings actually concentrate errors. Earlier warning, persistence and remaining useful life need event and time information that the challenge data does not provide.</p><p>Retraining remains possible, but it creates a new candidate. The old baseline and its prediction history stay intact. A candidate should earn promotion through independent comparison and an explicit release decision.</p><p>The Statistical Design and Decision Record and Architecture Decision Record preserve why these choices were made, their alternatives and the evidence that should trigger reconsideration.</p><p>The project now connects a sensor row to a reproducible prediction, an uncertainty estimate, a support check and a review record. The next test is whether that chain helps engineers make better decisions on real observations, with enough independent evidence to trust its limits.</p><h3>Sources and project evidence</h3><ol><li>FAA, <a href="https://www.faa.gov/sites/faa.gov/files/regulations_policies/handbooks_manuals/aviation/helicopter_flying_handbook/hfh_ch04.pdf">Helicopter Flying Handbook, Chapter 4: Helicopter Components, Sections, and Systems</a>. General engine and rotor-system context.</li><li>FAA, <a href="https://www.faa.gov/sites/faa.gov/files/regulations_policies/handbooks_manuals/aviation/helicopter_flying_handbook/hfh_ch07.pdf">Helicopter Flying Handbook, Chapter 7: Helicopter Performance</a>. Environmental and operating performance context.</li><li>PHM Society, <a href="https://data.phmsociety.org/phm2024-conference-data-challenge/">2024 Conference Data Challenge</a> and <a href="https://data.phmsociety.org/phmdata-forums/topic/understanding-the-training-dataset/">organizer discussion of training columns</a>. Challenge definition and field descriptions.</li><li>Ozeki and colleagues, <a href="https://papers.phmsociety.org/index.php/phmconf/article/download/4191/phmc_24_4191">Estimating the health of turbine engine based on domain knowledge and machine learning</a>. Participant interpretation of PA. Its reported performance is not attributed to this project.</li></ol><p>Project-specific behaviour is grounded in the complete harness implementation, feature catalogue and decision records. Numerical results come from the uploaded full-run metrics and regime investigation outputs. Software verification claims come from the bundled sample milestone and test log. Reference filenames include final_metrics.json, metric_comparison.csv, FEATURE_CATALOG.md and MILESTONE_REPORT.md.</p><p><em>Project status: local replay and engineering shadow-pilot workflow. Labelled operational validation and stakeholder acceptance remain pending.</em></p><h3>Explore the code and join the discussion</h3><p>The complete project is available on GitHub: <a href="https://github.com/zico1992/phm2024-complete-harness">phm2024-complete-harness</a>.</p><p>Questions, feedback and collaboration ideas are welcome. If you are working on engine health monitoring, physics-informed machine learning or operational model validation, I would be interested in discussing your experience.</p><p><strong>Contact Anshuman:</strong> <a href="/service/mailto:zicodasanshuman@gmail.com">zicodasanshuman@gmail.com</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7d85ec6a8338" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How I Built a Predictive Maintenance Pipeline for Aircraft Engines — and What I Learned Along the…]]></title>
            <link>https://medium.com/@zicodasanshuman/how-i-built-a-predictive-maintenance-pipeline-for-aircraft-engines-and-what-i-learned-along-the-df7138639d91?source=rss-6bf9c9b887df------2</link>
            <guid isPermaLink="false">https://medium.com/p/df7138639d91</guid>
            <category><![CDATA[aerospace]]></category>
            <category><![CDATA[data-science]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[predictive-maintenance]]></category>
            <category><![CDATA[machine-learning]]></category>
            <dc:creator><![CDATA[Anshuman Das]]></dc:creator>
            <pubDate>Sun, 06 Sep 2026 06:44:26 GMT</pubDate>
            <atom:updated>2026-09-06T06:44:26.669Z</atom:updated>
            <content:encoded><![CDATA[<h3>How I Built a Predictive Maintenance Pipeline for Aircraft Engines — and What I Learned Along the Way</h3><p>Predictive maintenance often gets described in one sentence:</p><blockquote><em>Use sensor data to predict when a machine will need maintenance.</em></blockquote><p>That sounds straightforward.</p><p>The reality is much messier.</p><p>When I started working on the <a href="https://data.phmsociety.org/phm-north-america-2025-conference-data-challenge/"><strong>PHM North America 2025 Data Challenge</strong></a>, I quickly realized that the hardest part was not choosing between LightGBM, CatBoost, or an LSTM. The Full implementation is in the my <a href="https://github.com/zico1992/PHM"><strong>github repo</strong></a>.</p><p>The harder questions were:</p><ul><li>What information would actually be available at prediction time?</li><li>How do I make the training data look like the test data?</li><li>How do I validate a model when I only have a handful of engines?</li><li>What does “good” even mean when late predictions are more expensive than early ones?</li><li>And how do I know whether a strong validation score is genuinely trustworthy?</li></ul><p>This article walks through the first version of the pipeline I built, why it worked, and the changes I would make before calling it production-grade.</p><h3>The Problem: Predict Three Different Maintenance Events</h3><p>The challenge is based on aircraft engine health data.</p><p>The goal is to predict how many cycles remain before three different maintenance events:</p><ul><li>High Pressure Turbine shop visit</li><li>High Pressure Compressor shop visit</li><li>HPC water wash</li></ul><p>At first glance, that looks like a standard regression problem.</p><p>You have sensor values.</p><p>You have targets.</p><p>Train a model.</p><p>Predict remaining cycles.</p><p>But there is an important twist.</p><p>The training data contains long engine histories.</p><p>The test data does not.</p><p>Instead, each test file contains only a relatively short strip of engine behaviour — around 1,500 cycles.</p><p>Even more importantly, the cycle count inside those files is reset.</p><p>So the model might see:</p><pre>0 → 1500</pre><p>even if the actual engine has already operated for several thousand cycles.</p><p>That immediately removes one of the easiest shortcuts:</p><pre>&quot;Just use engine age.&quot;</pre><p>The model has to infer health from behaviour, not from a hidden odometer.</p><p>That is where the problem became interesting.</p><h3>The First Big Decision: Make Training Look Like Inference</h3><p>One of the most useful decisions I made was also one of the simplest.</p><p>The test data comes as 1,500-cycle windows.</p><p>So I decided the training data should look the same.</p><p>Instead of training directly on the complete 20,000-cycle history of each engine, I cut the training engines into overlapping 1,500-cycle windows.</p><p>For example:</p><pre>Window 1<br>0 ---------------- 1500</pre><pre>Window 2<br>   30 ---------------- 1530</pre><pre>Window 3<br>      60 ---------------- 1560</pre><p>For every window, the model predicts the remaining maintenance cycles at the end of that window.</p><p>This turned four long engine histories into roughly 2,468 training examples.</p><p>More importantly, every training example now resembled the shape of the actual test problem.</p><p>That was a useful reminder:</p><blockquote><em>Matching the training setup to the inference setup can matter more than choosing a more sophisticated algorithm.</em></blockquote><h3>Before Modeling: Stop the Model From Seeing the Future</h3><p>Time-series leakage can make a model look brilliant very quickly.</p><p>Imagine I want to make a prediction at cycle 1,000.</p><p>A safe feature can use:</p><pre>950 → 1000</pre><p>A leaking feature might accidentally use:</p><pre>950 → 1050</pre><p>The model now knows something about the future.</p><p>That is especially easy to do with rolling statistics, normalization, or maintenance metadata.</p><p>So I added a simple rule to the pipeline:</p><blockquote><em>For a prediction at cycle </em><em>t, every feature must be computable using only information available at or before </em><em>t.</em></blockquote><p>That meant excluding things like:</p><ul><li>centered rolling windows</li><li>future maintenance information</li><li>target-derived features</li><li>sensors unavailable in test data</li><li>absolute engine age</li><li>full-engine normalization using future observations</li></ul><p>I also removed some cumulative maintenance counters because I could not guarantee that they would have the same meaning at inference time.</p><p>This made the model slightly harder to build — but much easier to trust.</p><h3>Raw Sensors Were Not Enough</h3><p>Aircraft-engine sensors do not operate in isolation.</p><p>A temperature reading can change because of:</p><ul><li>altitude</li><li>Mach number</li><li>outside air temperature</li><li>engine load</li><li>flight phase</li></ul><p>So a high temperature does not automatically mean:</p><pre>&quot;the engine is degrading&quot;</pre><p>It may simply mean:</p><pre>&quot;the operating condition changed&quot;</pre><p>This is where feature engineering became the core of the solution.</p><p>For each sensor, I started creating features that answered three different questions:</p><h3>1. What is happening right now?</h3><pre>latest sensor value</pre><h3>2. How has it changed recently?</h3><pre>difference from previous cycle<br>rolling mean<br>rolling standard deviation<br>rolling slope</pre><h3>3. How different is it from the start of the observed window?</h3><pre>current value - early-window baseline</pre><p>For example, instead of only using:</p><pre>T45 = 527</pre><p>I might also use:</p><pre>T45 increased by 27<br>T45 rolling slope = positive<br>T45 variability is increasing</pre><p>Those features are much closer to the language of degradation.</p><h3>I Kept the Different Operating Snapshots Separate</h3><p>Each engine cycle could contain multiple snapshots.</p><p>A tempting option would be to average them.</p><p>I decided not to do that initially.</p><p>If different snapshots correspond to different engine operating states, averaging can destroy useful information.</p><p>So I created features such as:</p><pre>T45_snapshot_1<br>T45_snapshot_4<br>T45_snapshot_7</pre><p>and also differences such as:</p><pre>T45_snapshot_1 - T45_snapshot_4</pre><p>The intuition is simple.</p><p>An engine can look normal under one operating condition and abnormal under another.</p><p>Those cross-condition differences may reveal deterioration earlier than a simple average.</p><h3>The Most Interesting Feature Family: Normal Behaviour Residuals</h3><p>One part of the pipeline became particularly interesting.</p><p>Instead of directly asking:</p><blockquote><em>What is the sensor value?</em></blockquote><p>I tried asking:</p><blockquote><em>What should the sensor value have been under these operating conditions?</em></blockquote><p>Suppose altitude, Mach, and ambient temperature suggest that T45 should be around:</p><pre>520</pre><p>but the actual measured value is:</p><pre>545</pre><p>Then:</p><pre>Residual = 545 - 520 = +25</pre><p>That +25 may be a better health indicator than the raw temperature itself.</p><p>Conceptually:</p><pre>Operating conditions<br>        ↓<br>Normal Behaviour Model<br>        ↓<br>Expected sensor response<br>        ↓<br>Actual - Expected<br>        ↓<br>Health residual</pre><p>I built residual features for variables such as:</p><ul><li>T45</li><li>T3</li><li>core speed</li><li>Ps3</li></ul><p>Then I created additional features from those residuals:</p><pre>current residual<br>rolling residual mean<br>residual slope<br>residual change from start of window</pre><p>This moved the model closer to condition-adjusted health monitoring rather than simple sensor regression.</p><h3>Validation: Hold Out Entire Engines</h3><p>With only four labelled engines, validation becomes tricky.</p><p>A random train-test split would be misleading.</p><p>Why?</p><p>Because two overlapping windows from the same engine can look almost identical.</p><p>If one lands in training and another in validation, the model may effectively be tested on something it has already seen.</p><p>So I used leave-one-engine-out validation.</p><p>For example:</p><pre>Train:<br>ESN101<br>ESN102<br>ESN103</pre><pre>Validate:<br>ESN104</pre><p>Then rotate.</p><p>This asks a much more meaningful question:</p><blockquote><em>Can the model generalize to an entirely unseen engine?</em></blockquote><p>That is much closer to what the challenge actually requires.</p><h3>The Baseline Was Intentionally Boring</h3><p>Before using a machine learning model, I built a simple median baseline.</p><p>Its job was not to be clever.</p><p>Its job was to answer:</p><blockquote><em>Can the real model beat something extremely simple?</em></blockquote><p>The median baseline produced an official score of:</p><pre>117.15</pre><p>That gave me a clean reference point.</p><h3>Why I Used Three Separate Models</h3><p>The three maintenance targets are not really the same problem.</p><p>Water wash is relatively frequent.</p><p>HPC and HPT shop visits happen over much longer time horizons.</p><p>So I trained separate models for:</p><pre>Cycles_to_WW<br>Cycles_to_HPC_SV<br>Cycles_to_HPT_SV</pre><p>The first model family I used was LightGBM.</p><p>But instead of ordinary regression, I used quantile regression.</p><p>That decision came directly from the competition metric.</p><h3>The Metric Changed the Entire Modeling Strategy</h3><p>This was probably the most important insight from the challenge.</p><p>The competition does not penalize early and late predictions equally.</p><p>Suppose the true remaining life is:</p><pre>100 cycles</pre><p>Prediction A:</p><pre>80</pre><p>Prediction B:</p><pre>120</pre><p>Both are wrong by 20 cycles.</p><p>But prediction B is late.</p><p>It says maintenance is further away than it really is.</p><p>The challenge penalizes that more heavily.</p><p>So the best model is not necessarily the one with the lowest MAE.</p><p>It may be better to be slightly conservative.</p><p>That is why I tested quantiles:</p><pre>0.30<br>0.35<br>0.40<br>0.45<br>0.50</pre><p>The lower the quantile, the more conservative the prediction.</p><p>Interestingly, 0.30 performed best for all three targets.</p><h3>I Then Added Another Conservative Adjustment</h3><p>After choosing the 0.30 quantile, I tested whether a small negative offset could improve the official score further.</p><p>For example:</p><pre>Raw HPC prediction = 4500<br>Offset = -560</pre><pre>Final prediction = 3940</pre><p>The selected offsets were:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/777/1*C-FjdwF-y99f2NypS2fRLg.png" /></figure><p>This intentionally makes the model predict maintenance earlier.</p><p>That would be undesirable if the goal were unbiased RUL estimation.</p><p>But under this specific scoring function, it can be rational.</p><h3>The First Result Was Promising</h3><p>The median baseline scored:</p><pre>117.15</pre><p>The LightGBM quantile model with calibration scored:</p><pre>26.44</pre><p>That is roughly a 4.4× improvement on the metric that matters.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bxEi9ClbXMFzFKo0gqpaVA.png" /><figcaption>The figure above summarizes the first version of the solution: how the 1,500-cycle windows were created, how residual-based features were built, how engine-level validation works, and how the model compared with the baseline.</figcaption></figure><p>The target-level results looked like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/777/1*MvWfmzD7Wo4xJRJaFCR3Sw.png" /></figure><p>HPC was clearly the hardest target.</p><p>And that also made intuitive sense.</p><p>There are fewer HPC maintenance events in the training data, so the model has fewer examples of what an approaching HPC shop visit actually looks like.</p><h3>The Most Counterintuitive Result: Worse MAE, Better Competition Score</h3><p>Water Wash gave me a result that initially looked strange.</p><p>The median baseline had an MAE of roughly:</p><pre>250 cycles</pre><p>The LightGBM model had an MAE of roughly:</p><pre>309 cycles</pre><p>So by MAE, the model was worse.</p><p>But the official scores were:</p><pre>Median baseline: 42.48<br>LightGBM:       19.95</pre><p>Why?</p><p>Because the median is relatively unbiased.</p><p>It predicts late about half the time.</p><p>The LightGBM model is intentionally early most of the time.</p><p>Roughly:</p><pre>76% early predictions</pre><p>Since late mistakes are more expensive, the conservative model wins under the challenge metric.</p><p>That was one of the biggest lessons from the experiment:</p><blockquote><em>A model is only “better” relative to the decision objective you actually care about.</em></blockquote><h3>Where the Current Pipeline Is Still Weak</h3><p>The first result is strong enough to be interesting.</p><p>But I would not yet call 26.44 a fully trustworthy estimate of generalization.</p><p>There are a few important reasons.</p><h3>Learning 1: The Whole Pipeline Must Live Inside Cross-Validation</h3><p>The Normal Behavior Model was fitted using early-life training data.</p><p>That sounds safe.</p><p>But there is a subtle problem.</p><p>If I fit that residual model using early-life data from all four engines, and then hold out one engine for validation, the held-out engine has still influenced the feature transformation.</p><p>No labels leaked.</p><p>But information did.</p><p>The correct approach is:</p><pre>Outer validation engine:<br>ESN104</pre><pre>Fit residual model only on:<br>ESN101<br>ESN102<br>ESN103</pre><pre>Then apply it to:<br>ESN104</pre><p>This is a broader machine learning lesson:</p><blockquote><em>Cross-validation should wrap the entire learned preprocessing pipeline, not just the final estimator.</em></blockquote><h3>Learning 2: My 26.44 Score Is Partly an Optimization Score</h3><p>I used out-of-fold predictions to:</p><ul><li>choose the best quantile</li><li>choose the additive offset</li><li>report the final score</li></ul><p>That means the same validation predictions influenced model decisions and performance reporting.</p><p>The estimate is useful.</p><p>But it is not fully untouched.</p><p>The cleaner solution is nested cross-validation.</p><h3>What Nested Validation Looks Like</h3><p>Imagine I want to evaluate Engine 104.</p><p>I keep it completely untouched.</p><p>Then I use:</p><pre>101<br>102<br>103</pre><p>to determine:</p><ul><li>best quantile</li><li>best features</li><li>model hyperparameters</li><li>calibration offset</li></ul><p>Only after those decisions are frozen do I make predictions for Engine 104.</p><p>So the structure becomes:</p><pre>Outer holdout: 104</pre><pre>Inner development:<br>101 / 102 / 103<br>        ↓<br>choose model<br>        ↓<br>retrain on 101 + 102 + 103<br>        ↓<br>predict 104</pre><p>Then repeat for every engine.</p><p>At the end, concatenate all untouched outer predictions and calculate one final score.</p><p>That score will be much more meaningful than the current development score.</p><h3>Learning 3: 2,468 Windows Does Not Mean 2,468 Independent Examples</h3><p>My training windows overlap heavily.</p><p>For example:</p><pre>Window A<br>0 ---------------- 1500</pre><pre>Window B<br>   30 ---------------- 1530</pre><p>Those two windows share almost all of their observations.</p><p>So while the model technically sees thousands of windows, the true amount of independent information is much smaller.</p><p>This matters because it can create a false sense of sample size.</p><p>The next experiment should test different strides:</p><pre>30<br>100<br>250<br>500 cycles</pre><p>If the model remains strong even when windows overlap much less, that is a much better sign that it is learning genuine degradation behaviour.</p><h3>Learning 4: Quantile and Offset Are Really the Same Decision Layer</h3><p>Right now the pipeline does this:</p><pre>choose quantile<br>      ↓<br>choose offset</pre><p>But both mechanisms push the prediction earlier.</p><p>So:</p><pre>q = 0.30<br>offset = -560</pre><p>might be similar to:</p><pre>q = 0.22<br>offset = 0</pre><p>A better formulation would be:</p><pre>Final Prediction<br>=<br>a × QuantilePrediction + b</pre><p>Then optimize:</p><pre>quantile<br>a<br>b</pre><p>together.</p><p>That makes the decision layer more explicit and may generalize better.</p><h3>Learning 5: WW, HPC, and HPT Probably Need Different Feature Strategies</h3><p>The first pipeline uses broadly similar features for all three targets.</p><p>That is convenient.</p><p>But probably not optimal.</p><p>For Water Wash, the model may benefit most from recent changes:</p><pre>short rolling slope<br>temperature reset<br>recent residual trend</pre><p>For HPC, slow degradation may matter much more:</p><pre>full-window slope<br>first-quarter vs last-quarter shift<br>long-term health-index change</pre><p>For HPT, another combination may be better again.</p><p>So the next pipeline should ask:</p><blockquote><em>Which feature family is best for each maintenance mechanism?</em></blockquote><p>rather than:</p><blockquote><em>Which one feature matrix should I use for everything?</em></blockquote><h3>Learning 6: HPC Is the Problem I Would Focus on Next</h3><p>HPC currently has the weakest score:</p><pre>35.18</pre><p>That makes it the highest-value target for improvement.</p><p>I would test features such as:</p><pre>median(last 20% of window)<br>-<br>median(first 20% of window)</pre><p>along with:</p><ul><li>long-window slope</li><li>residual slope</li><li>trend curvature</li><li>PCA health-index movement</li><li>cross-snapshot deterioration</li><li>time spent above abnormal residual thresholds</li></ul><p>These features may capture slow degradation better than short rolling statistics.</p><h3>Learning 7: The Normal Behavior Model May Be More Valuable Than the Final Regressor</h3><p>The residual approach deserves much more attention.</p><p>Raw sensor prediction asks:</p><pre>What is T45?</pre><p>Condition-adjusted monitoring asks:</p><pre>How abnormal is T45 under these operating conditions?</pre><p>That second question is much closer to how real equipment-health monitoring is often approached.</p><p>A future version could turn residuals into their own degradation system:</p><pre>Residual now<br>Residual rolling mean<br>Residual slope<br>Residual volatility<br>Residual change from window start</pre><p>This may become especially useful for HPC and HPT.</p><h3>Learning 8: Try Cat-Boost Before Reaching for Deep Learning</h3><p>It is very tempting to say:</p><blockquote><em>This is a sequence problem. Let’s use an LSTM.</em></blockquote><p>But there are only a few independent engines.</p><p>Thousands of rows do not magically create thousands of independent assets.</p><p>So before moving to LSTM, GRU, or Transformers, I would first try Cat-Boost on the exact same validation folds.</p><p>Then compare:</p><pre>LightGBM<br>vs<br>CatBoost</pre><p>If they make different mistakes, there may be value in blending them.</p><p>For example:</p><pre>Final prediction<br>=<br>w × LightGBM<br>+<br>(1 - w) × CatBoost</pre><p>The weight w should be learned from out-of-fold predictions rather than chosen manually.</p><h3>Learning 9: 700 Features Need to Earn Their Place</h3><p>The current feature matrix has roughly 700 columns.</p><p>That is not automatically a good thing.</p><p>The next step should include feature-family ablation.</p><p>For example:</p><pre>Model A<br>Raw sensors only</pre><pre>Model B<br>Raw + rolling features</pre><pre>Model C<br>Raw + rolling + residuals</pre><pre>Model D<br>Raw + rolling + residuals + maintenance features</pre><p>If one feature family can be removed with no loss in score, the model becomes:</p><ul><li>simpler</li><li>faster</li><li>easier to explain</li><li>less likely to overfit</li></ul><p>That is usually a win.</p><h3>What the Next Version Should Look Like</h3><p>The next iteration of the pipeline would be more rigorous:</p><pre>Raw data<br>   ↓<br>Profile<br>   ↓<br>Leakage audit<br>   ↓<br>Outer engine holdout<br>   ↓<br>Fit all learned preprocessing<br>on outer-training engines only<br>   ↓<br>Create 1500-cycle windows<br>   ↓<br>Build causal features<br>   ↓<br>Inner validation<br>   ↓<br>Optimize:<br>features<br>LightGBM/CatBoost<br>quantile<br>calibration<br>   ↓<br>Freeze choices<br>   ↓<br>Retrain on outer-training engines<br>   ↓<br>Predict untouched outer engine<br>   ↓<br>Repeat<br>   ↓<br>Calculate nested validation score<br>   ↓<br>Retrain final pipeline on all labelled engines<br>   ↓<br>Generate test predictions</pre><p>That pipeline is slower.</p><p>But the final score means much more.</p><h3>The Bigger Lesson</h3><p>The most useful lesson from this challenge has nothing to do with Light-GBM.</p><p>It is that predictive maintenance is often less about choosing a fancy model and more about getting five things right:</p><pre>How the data is represented<br>+<br>What information exists at prediction time<br>+<br>How degradation is encoded<br>+<br>How validation mimics reality<br>+<br>What the business metric actually rewards</pre><p>The first version of the pipeline moved the internal score from:</p><pre>117.15</pre><p>to:</p><pre>26.44</pre><p>That is encouraging.</p><p>But the next goal is not simply:</p><blockquote><em>Get below 26.</em></blockquote><p>The next goal is:</p><blockquote><em>Make sure 26 means what I think it means.</em></blockquote><p>That means stronger validation, cleaner fold-specific preprocessing, less correlated training windows, target-specific features, model comparison, and better out-of-fold calibration.</p><p>If the score remains strong after those changes, then the result becomes much more interesting.</p><p>It stops being just a competition model.</p><p>It starts looking like a reusable blueprint for real predictive-maintenance systems where equipment age is hidden, operating conditions change constantly, and only a short recent history is available.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=df7138639d91" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>