<?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" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Josua Naiborhu - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Josua Naiborhu - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Tue, 02 Jun 2026 21:42:30 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/naiborhujosua/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Interpret Black Box Models using LIME (Local Interpretable Model-Agnostic Explanations) ]]>
                </title>
                <description>
                    <![CDATA[ Machine learning models are black box models. By giving input to these models, we can get output based on the particular model we're using.  The way humans interpret things is different from how machines interpret them. So it's helpful to use tools t... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/interpret-black-box-model-using-lime/</link>
                <guid isPermaLink="false">66ba0cfd7fb82b484b253a23</guid>
                
                    <category>
                        <![CDATA[ Machine Learning ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Josua Naiborhu ]]>
                </dc:creator>
                <pubDate>Mon, 17 Oct 2022 15:12:31 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/10/LIME.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Machine learning models are black box models. By giving input to these models, we can get output based on the particular model we're using. </p>
<p>The way humans interpret things is different from how machines interpret them. So it's helpful to use tools that can turn the output from certain machine learning models into something humans or non-technical users can understand.</p>
<p>In a business context, the interpretation of the model plays an important role in making data-driven decisions. The better we interpret the output, the easier it is for non-technical users to understand that output.</p>
<p>So in this tutorial, I will explain one of most popular packages you can use for interpreting the black box model of the output – a package called LIME (Local Interpretable Model-Agnostic Explanations).</p>
<h2 id="heading-what-is-lime">What is LIME?</h2>
<p>LIME is a model-agnostic machine learning tool that helps you interpret your ML models. The term <strong>model-agnostic</strong> means that you can use LIME with any machine learning model when training your data and interpreting the results. </p>
<p>LIME uses "inherently interpretable models" such as decision trees, linear models, and rule-based heuristic models to explain results to non-technical users with visual forms. You can use LIME for regression and classification problems to interpret your black box models. </p>
<h2 id="heading-gathering-the-data">Gathering the Data</h2>
<p>In this tutorial, we will look at a classification problem using the Churn dataset. We will classify whether customers will keep using products or not (churn rate) by looking at some features in the dataset. </p>
<p>You can download the Kaggle <a target="_blank" href="https://www.kaggle.com/datasets/blastchar/telco-customer-churn/code">telco customers churn dataset here</a>. </p>
<h2 id="heading-data-preprocessing">Data Preprocessing</h2>
<p>Because this tutorial is focussing on the implementation of LIME as an interpretability tool, we will just do a few preprocessing steps for various features.</p>
<p>We start preprocessing by going over the columns that aren't relevant to the target outcome (churn). You can delete the CustomerID by using this code:</p>
<pre><code class="lang-py"><span class="hljs-comment"># Dropping all irrelevant columns</span>
df.drop(columns=[<span class="hljs-string">'customerID'</span>], inplace = <span class="hljs-literal">True</span>)
</code></pre>
<p>We also have a few missing values that aren't properly imputing. For simplicity's sake, we just deleted the missing values by using this code:</p>
<pre><code class="lang-py"><span class="hljs-comment"># Dropping missing values</span>
df.dropna(inplace=<span class="hljs-literal">True</span>)
</code></pre>
<p>Another preprocessing step that we should do is to look at the categorical columns. The vast majority of machine learning models can not handle categorical features. So we have to preprocess this type of feature into a numerical representation. </p>
<p>There are various transformations that we can do such as label encoding, hashing tricks, one hot encoding, target encoding, Ordinal encoding, and frequency encoding. </p>
<p>For this tutorial, we're going to use the label encoding technique using the scikit-learn library as shown in the following code:</p>
<pre><code class="lang-py"><span class="hljs-comment"># Label Encoding features </span>
categorical_feat =list(df.select_dtypes(include=[<span class="hljs-string">"object"</span>]))

<span class="hljs-comment"># Using label encoder to transform string categories to integer labels</span>
le = LabelEncoder()
<span class="hljs-keyword">for</span> feat <span class="hljs-keyword">in</span> categorical_feat:
    df[feat] = le.fit_transform(df[feat]).astype(<span class="hljs-string">'int'</span>)
</code></pre>
<p>It is worth noting that when you're working on an actual ML problem/dataset, you'll want to make sure you carry out proper preprocessing, feature engineering, cross validation, hyperparameter tuning, and so on to get a better prediction.</p>
<h2 id="heading-modeling-with-xgboostclassifier-and-lime-implementation">Modeling with XGBoostClassifier and LIME implementation</h2>
<p>XGBoostClassifier is an XGBoost algorithm that you can use for solving classification problems. It works by constructing the data into a decision tree and using the residuals to be constructed again into the next decision tree sequentially.</p>
<p>This algorithm helps you improve the performance of your model's predictions that can resemble the ground truth. This is because it improves misclassified predictions (weak learners) by helping them become strong learners. It does this through learning the misclassified prediction that is used upon the next iteration of next decision tree. </p>
<p>You can check out the following diagram to see how this algorithm works:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/xgboost.png" alt="Image" width="600" height="400" loading="lazy">
_Figure 4. Simplified XGBoost (<a target="_blank" href="https://www.researchgate.net/figure/Simplified-structure-of-XGBoost_fig2_348025909">source</a>)_</p>
<p>We can implement LIME after going through the training process on our training data. </p>
<p>We start the training process by splitting the data into training and test datasets in order to avoid overfitting. We can use the splitting method available in scikit-learn as shown in the following code:</p>
<pre><code class="lang-py">features = df.drop(columns=[<span class="hljs-string">'Churn'</span>])
labels = df[<span class="hljs-string">'Churn'</span>]
<span class="hljs-comment"># Dividing the data into training-test set with 80:20 split ratio</span>
x_train,x_test,y_train,y_test = train_test_split(features,labels,test_size=<span class="hljs-number">0.2</span>, random_state=<span class="hljs-number">123</span>)
</code></pre>
<p>We select the features and the target outcome (<strong>churn</strong>) and split the data into train and test data. Then, we can start training by fitting the <strong>X_train and y_train</strong> based on the instantiating object of the machine learning model that we're using. We're using XGBoostClassifier for this tutorial.</p>
<p>We initialize n_estimators (number of decision trees) and random state for the simplicity of the training process. In a real data science project, there are many parameters that you'll want to tweak in order to maximize the capability of this algorithm. You can refer to <a target="_blank" href="https://xgboost.readthedocs.io/en/stable/parameter.html"><strong>XGBoost parameters</strong></a> to learn more.</p>
<pre><code class="lang-py">model = XGBClassifier(n_estimators = <span class="hljs-number">300</span>, random_state = <span class="hljs-number">123</span>)
model.fit(x_train, y_train)
</code></pre>
<p>After fitting the data through the training process, we will work on interpreting local interpretability. Local Interpretability involves analyzing each feature of a particular data instance. We can select a particular instance/sample to check how the features correlate to the target outcome based on a particular sample. </p>
<pre><code class="lang-py">np.random.seed(<span class="hljs-number">123</span>)
predict_fn = <span class="hljs-keyword">lambda</span> x: model.predict_proba(x)
<span class="hljs-comment"># Defining the LIME explainer object</span>
explainer = lime.lime_tabular.LimeTabularExplainer(df[features.columns].astype(int).values,                                               mode=<span class="hljs-string">'classification'</span>,
class_names=[<span class="hljs-string">'Did not Churn'</span>, <span class="hljs-string">'Churn'</span>],                                                 training_labels=df[<span class="hljs-string">'Churn'</span>],                                                  feature_names=features.columns)
<span class="hljs-comment"># using LIME to get the explanations</span>
i = <span class="hljs-number">5</span>
exp=explainer.explain_instance(df.loc[i,features.columns].astype(int).values, predict_fn, num_features=<span class="hljs-number">5</span>)
exp.show_in_notebook(show_table=<span class="hljs-literal">True</span>)
</code></pre>
<p>To get the explanation for a particular instance, we start by defining a function as the probability score that will be utilized on the LIME framework. We also instantiate the LIME explainer object. </p>
<p>LIME has an attribute lime_tabular that can interpret how the features correlate to the target outcome. We can also specify the mode to classification, training_label to the target outcome (Churn), and the features that we have selected on the training process. </p>
<p>We choose sample 5 and we'll get the explanation for this particular sample. We also choose the 5 most important features that contribute the most to the target outcome on the num_features parameter. </p>
<p>These features are also called feature importance. Feature Importance is the feature that checks the correlation between the input features and the target features. The higher the score of the feature in the feature importance plot, the more important the feature is to be fitted into the machine learning model.</p>
<h2 id="heading-how-to-interpret-local-interpretability">How to Interpret Local Interpretability</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/lime1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Figure 8. Local Interpretability Prediction</em></p>
<p>The above image shows three graphs that each show essential information about our customers and their churn rates. </p>
<p>The left graph shows that sample 5 in the data shows the confidence interval stating that this data is 99% churn whereas only 1% indicates the instance did not churn. </p>
<p>The center graph shows the feature importance scores on this particular sample with <strong>MonthlyCharges</strong> having a <strong>21% feature importance score</strong>, followed by <strong>Contract</strong> with <strong>19%</strong> and <strong>tenure</strong> with <strong>11%</strong>. These features make sense based on our belief that customers tend to churn more on <strong>the higher MonthlyCharge.</strong> </p>
<p>The right graph shows the top five features and their respective values. The features highlighted in orange contribute toward <strong>class</strong> <strong>1 (Churn)</strong> whereas features highlighted in blue contribute toward <strong>class 0 (not Churn)</strong>. </p>
<p>We can also plot another version of the second graph as shown in the following bar plot. It shows the range of local interpretability predictions on sample 5 in which MonthlyCharges for this particular sample are greater than 89, Contract is less than 0, and totalCharges is greater than 401 and less than 1397.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/lime2.png" alt="Image" width="600" height="400" loading="lazy">
<em>Figure 9. The range of Local Interpretability Prediction</em></p>
<h2 id="heading-how-to-interpret-global-interpretability">How to Interpret Global Interpretability</h2>
<p>LIME also provides another explanation through the SP-LIME algorithm that takes representative samples to extract the global perspective from the black box model. </p>
<p>This technique helps non-technical users understand the data not only in a particular instance (local interpretability) but also it also helps them understand the data holistically. By understanding many representative samples and their interpretations, non-technical users can capture the global perspective of the data instances. </p>
<pre><code class="lang-py"><span class="hljs-comment"># Let's use SP-LIME to return explanations on a few sample data sets </span>
<span class="hljs-comment"># and obtain a non-redundant global decision perspective of the black-box model</span>
sp_exp = submodular_pick.SubmodularPick(explainer, 
                                        df[features.columns].values,
                                        predict_fn, 
                                        num_features=<span class="hljs-number">5</span>,
                                        num_exps_desired=<span class="hljs-number">5</span>)
</code></pre>
<p>We use the sub-modular attributes available on SP-LIME to obtain a global perspective of the data instances. Then, we visualize the data to visual global representative samples extracted by the SP-LIME algorithm using this code:</p>
<pre><code class="lang-py">[exp.show_in_notebook() <span class="hljs-keyword">for</span> exp <span class="hljs-keyword">in</span> sp_exp.sp_explanations]
print(<span class="hljs-string">'SP-LIME Explanations.'</span>)
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/lime3-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Figure 12. The range of global interpretability Prediction</em></p>
<p>You can see how SP-LIME constructs interval values for each <strong>representative sample.</strong> For instance, the first representative sample shows the confidence interval is 81% Churn whereas 19% indicates that the instances do not churn. </p>
<p>The features that influence how this instance tends to churn are <strong>MonthlyCharge, Contract, tenure,OnlineSecurity, and TechSupport</strong>. You can see this in the below feature importance bar plot for the first representative sample. We can also plot another version of the middle plot of each representative sample on the preceding graph using this code:</p>
<pre><code class="lang-python">[exp.as_pyplot_figure(label=exp.available_labels()[<span class="hljs-number">0</span>]) <span class="hljs-keyword">for</span> exp <span class="hljs-keyword">in</span> sp_exp.sp_explanations]
print(<span class="hljs-string">'SP-LIME Local Explanations'</span>)
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/lime4-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Figure 14. First and Second Representative samples</em></p>
<p>On the second representative sample on <strong>figure 12,</strong> the confidence interval is 100% not Churn. The features that influence how this instance tends to not churn are <strong>Contract, tenure, MonthlyCharge, TotalCharges, and OnlineSecurity</strong> as shown on the following feature importance bar plot for the second representative sample.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/lab4.png" alt="Image" width="600" height="400" loading="lazy">
<em>Figure 15. Third representative sample</em></p>
<p>On the third representative sample on <strong>figure 12</strong>, the confidence interval is 100% not Churn. The features that influence how this instance tends to not churn are <strong>Contract, OnlineSecurity,</strong> and <strong>OnlineBackup</strong> as shown on the above feature importance plot for the third representative sample. </p>
<p>You can see the implementation of LIME and SP-LIME on the data <a target="_blank" href="https://nbviewer.org/github/naiborhujosua/Blog_Notes/blob/main/notebook/interpreting-black-box-models.ipynb">by looking at this notebook</a>.</p>
<p><a target="_blank" href="https://arxiv.org/abs/1602.04938">Here's an interesting article</a> about trusting models.</p>
<h1 id="heading-thank-you-for-reading">Thank you for reading!</h1>
<p>I really appreciate it! 🤗 . I write about topics related to machine learning and deep learning. I try to keep my posts simple but precise, always providing visualizations and simulations.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
