Showing posts with label Training. Show all posts
Showing posts with label Training. Show all posts

Friday, March 20, 2026

Fixing a Train/Serve Skew in Sentiment Residuals

 

Fixing a Train/Serve Skew in Sentiment Residuals

The signal generation process uses a technique called sentiment residualization — essentially, we remove the portion of the FinBERT sentiment score that can be explained by price momentum alone, leaving behind only the genuine sentiment surprise. A stock that has been running up for 20 days will naturally attract positive news coverage, so we want to isolate the sentiment signal that exists above and beyond what the price action would predict.

The problem was subtle. During training, the residual model was fitted on tens of thousands of rows spanning months of history. But at inference time, the same calculation was being refitted fresh each day on whatever small universe of stocks passed the daily filters — typically around 30 stocks. That's a very different statistical population, which meant the sentiment residuals being fed into the composite ranking signal weren't quite the same thing the model had learned from during training. Classic train/serve skew.

The fix was straightforward — serialize the residual model coefficients to disk at the end of each training run and load those fixed coefficients at inference time rather than refitting. Now the definition of sentiment surprise is consistent from training through to live signal generation.

Friday, August 1, 2025

AI / ML - Modeling Fundamentals - Mistakes Found and Corrected

After adding Earnings Surprise Score data into my 1 year fwd return predicting model, I kind of felt as though I had hit the road on the model. The Earnings Surprise Score did move the needle. But with all of the effort in Feature Engineering I had put into this model, the only thing I really felt I could add to it, was sentiment (news). Given that news is more of a real-time concept, grows stale, and would be relevant for only the latest row of data, I decided to do some final reviews, and move on, or "graduate" to some new things - like maybe trying out a neural network or doing more current or real-time analysis. In fact, I had already tried a Quarterly model, but the R-squared on it was terrible and I decided not to use it - not to even ensemble it with the annual report data model.

So - I asked a few different LLMs to code review my model. And I was horrified to learn that because of using LLMs to continually tweak my model, I had wound up with issues related to "Snippet Integration". 

Specifically, I had some major issues:

1. Train/Test Split Happened Too Early or Multiple Times

  •  Splitting data before full preprocessing (e.g., before feature scaling, imputation, or log-transforming the target).
  •  Redundant train/test splits defined in multiple places — some commented, some active — leading to potential inconsistencies depending on which was used.


2. No Validation Set

  •  Originally, data was split into training and test sets.
    •  This meant that model tuning (e.g. SHAP threshold, hyperparameter selection) was inadvertently leaking test set information. 

  •  Now corrected with a clean train/val/test split.


3. Inconsistent Preprocessing Between Train and Test

  •  Preprocessing steps like imputation, outlier clipping, or scaling were not always applied after the split.
  •  This risked information bleeding from test → train, violating standard ML practice.


4. Improper Handling of Invalid Target Values (fwdreturn)

  •  NaN, inf, and unrealistic values (like ≤ -100%) were not being consistently filtered.
  •  This led to silent corruption of both training and evaluation scores.
  •  Now fixed with a strict invalid_mask and logging/reporting of dropped rows.


5. Redundant or Conflicting Feature Definitions

  •  There were multiple, overlapping blocks like:

                features = [col for col in df.columns if ...]
                X = df[features]

  •  This made it unclear which feature list was actually being used.
  •  Sector z-scored and raw versions were sometimes duplicated or mixed without clarity.


6. Scaling and Z-Scoring Logic Was Not Modular or Controlled

  •  Originally, some features were being z-scored after asset-scaling (which didn’t make sense).
  •  Some metrics were scaled both to assets and z-scored sector-wise, which polluted the modeling signal.
  •  Now addressed with clear separation and feature naming conventions.


7. SHAP Was Applied to a Noisy or Unclean Feature Space

  •  Without proper pruning first (e.g., dropping all-NaN columns), SHAP feature importance included irrelevant or broken columns.
    •  This could inflate feature count or misguide model interpretation.
  •  Now resolved by cleaning feature set before SHAP and applying SHAP-based selection only on valid, imputed data.
One issue, was that the code for modeling was in the main() function which had gotten way too long and hard to read. This function had all of the training, testing, splitting/pruning, it had the model fitting in it, AND all of the scoring. I broke out the process of train/validate/test - and put that process into play for both the full model, as well as the SHAP-pruned model. Then I took the best r-squared from both runs and used the winning model.

Friday, October 20, 2023

Deep Q Learning - Neural Networks - Training the Model Takes Resources

I now am starting to see why those companies with deep pockets have an unfair advantage in the not-so-level playing field of adopting AI.  Resources.  

It takes a LOT of energy and computing resources to train these Artificial Intelligence models.

In Chapter 11 of AI Crash Course (by Hadelin de Ponteves), I did the work. I downloaded, inspected, and ran the examples, which are based on Google's Deep Mind project. The idea is to use an AI to control server temperature, and compare this with an "internal" (no AI) temperature manager.

What you would do, is to train the model (first), and it would produce a model.h5 file, that would then be used when you ran the actual model through testing. 

The problem, though, is that on my rather powerful Mac Pro laptop, the training would never run. I would return HOURS later, only to see [ killed ] on the command prompt. The OS apparently was running out of resources (memory probably).

So I started tinkering with the code.

First, I reduced the number of epochs (from 25 to 10). 

#number_epochs = 25  

number_epochs = 10

Which looked like it helped, but ultimately didn't work.

Then, I reduced the number of times the training loops would run. When I looked at the original code, the number of iterations was enormous.

# STARTING THE LOOP OVER ALL THE TIMESTEPS (1 Timestep = 1 Minute) IN ONE EPOCH

while ((not game_over) and timestep <= 5 * 30 * 24 * 60):

This is 216,000 loop iterations in the inner loop, and of course this needs to be considered from the context of the outer loop (25, or, adjusted down to 10 as I did).  So 216,000 * 25 = 5 million, 400 thousand. If we reduce to 10 the number of Epochs, we are dealing with 2 million, 600 thousand.

I don't know how much memory (Heap) is used over that many iterations but on a consumer machine, you are probably going to tax it pretty hard (remember it has to run the OS and whatever tasks happen to be running on it).

I was FINALLY able to get this to run by reducing the number of Epochs to 10, and reducing the steps to 5 * 30 * 24 (3600). And even with this drastic reduction, you could see the benefits the AI performed over the non-AI temperature control mechanism.

Removing Two Stale Macro Features

  Removing Two Stale Macro Features The model was trained on 11 features, two of which were macroeconomic sentiment indicators sourced from...