Detecting leakage in machine learning pipelines using NANs/complex numbers | by Abhay Pawar | Towards Data Science

Data leakage in machine learning pipelines can cause havoc for your model. In this post, I’m going to share an amazingly simple way to detect data leakages using NANs and complex numbers while treating your ML pipeline as a black box. I’ll talk very briefly about what data leakage is. I’ll also talk about leak-detect, a python package I’m releasing to do all this in one line code.
The most precise way to describe data leakage could be this:
Data leakage in an ML model occurs when data used to create predictor variables during training time is unavailable at the time of inference.
Clearly, using data(features) unavailable at inference time during training leads to model underperforming in production. This under-performance could mean millions of lost dollars depending on the scale of your company!
What are some ways feature creation pipelines can introduce data leakage?
Using target or data used to create target for feature engineering.Using data from future periods for feature engineering.
Using target or data used to create target for feature engineering.
Using data from future periods for feature engineering.
First is generally easier to detect and keep track of. So, let’s try to understand the second one using an example. Consider you are trying to predict stock price of a company after 5 days. Our data contains date and daily open price.
## target (to be predicted): Open price after 5 days.data['target'] = data['open_price'].shift(-5)
We want to create various hand-made features for this task. Say, one feature we want is ‘price on the previous day’.
data['price_previous_day'] = data['open_price'].shift(1)# .shift(1) gives value from previous row
But instead of doing .shift(1), let’s say by mistake we did .shift(-1)and used values from the next row of open price as a feature. We just created ‘price on the next day’ instead. This is a leaky feature because it uses data from a future period.
There are many best practices to follow to avoid leakage, but none of these can make you 100% sure that your pipeline is not leaky. This is where NANs and complex numbers come in! This methodology can be looked at as a unit test for data leakages.
Before getting to the methodology, let’s do an analogy first :).
Let’s say you have two tanks connected through a pipe which is closed. How can we detect that this pipe is indeed closed and is not leaky without inspecting the pipe? You can add color to one tank and check if the other tank also gets that color. Just like watercolors, NANs and complex numbers are ideal for leakage detection tasks because they have the ability to persist after any operation with real numbers. Operations like addition, subtraction, etc between a real number and NAN or complex number yield NAN or complex number respectively. Of course, there are exceptions to this and we will come to that later.
In the stock price example, let’s say we set open price on a specific day D to NAN and create our features using this data. ‘Price on next day’ (leaky feature) will have NAN value for day D-1, whereas ‘price on previous day’ (non-leaky) will have it for day D+1.
So for the leaky feature, we will observe an extra NAN in the feature before day D. Essentially, if open-price on day D is being used to create a feature for days before D (which is leakage), we will see extra NAN in that feature before day D. This is true if the feature is being created using any operation which yields NAN when one input is NAN.
What if we want to check if data from day D+1, D+2, etc is being used to create features for days before D? Well, we set them all to NAN and just count NANs in final features before day D.
This methodology can be summarized in 4 simple steps:
1. Define an imaginary leakage partition that splits your data into the upper and lower half. In our case, we don’t want lower half data (future) to be used to create features for dates in the upper half (past).
2. Run the data creation pipeline on raw data and count the number of NANs in all features in the upper half. Our data creation pipeline here creates both leaky and non-leaky features described above. Our non-leaky feature (previous day price) has 1 NAN because our data starts from 1-Jan-2020 and leaky one (price on next day) has 0 NANs.
3. Now set all the columns in raw data used to create features to NAN below the leakage partition.
4. Run the data creation pipeline on this raw data with NANs. Count the number of NANs above partition again. For our leaky feature, there’s 1 extra NAN!
The number of NANs in the upper half for non-leaky feature stayed the same but increased by 1 for the leaky feature. Where did this extra NAN come from? The only place it can come from is the lower half which is all NANs. And we just detected leakage in our leaky feature! This methodology works for any feature because it’s not dependent on the definition of a feature.
The same process above can be repeated by adding an imaginary component instead of replacing with NAN and counting the number of rows with an imaginary component. Pandas and numpy already support all operations with NANs and complex numbers. So, you don’t have to change your code at all! Also, this detects leakage only in your pipelines and not leakages which might be already present in raw data.
Why use both NANs and complex numbers?
It’s important to run this test with both NANs and complex numbers because your pipeline could be replacing NANs with a value. Or numpy converts complex numbers to real numbers for some operations. I’ve tested it for different features that involve addition, multiplication, max, min operations and leakage detection works in all these cases. Still, it’s important to keep an eye out for special cases.
I’m also releasing a python package:leak-detect to do all this with one line code! leak-detect can detect horizontal (from target to features) and vertical (from future to past) leakage occurring due to buggy code. In the example below, we are creating two leaky features which use data from the future: ‘return_2day_leaky’ and ‘open_10day_before_leaky’. Both get detected to have vertical leakage. It also prints out the number of previous rows data is leaking into. For the first feature, it’s 2 because it uses price after 2 days.
The leak-detect example.ipynb notebook in the repo lists more such examples. You can install the package by doing pip install leak-detect. Here’s the github repo.
Leak-detect works only on data similar to the stock price data. In your case, you could be using multiple datasets to create features or maybe even SQL. Whatever your pipeline is, the idea still remains the same. You would have to write your own custom functions which can be replicated by looking at leak-detect code here.
Hope this is something that would be useful to you. Let me know if you have any thoughts in the comments! You can also reach out to me through abhayspawar on Twitter, Linkedin and Gmail. Thanks a lot for reading. Stay safe!