<?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 PyC Team on Medium]]></title>
        <description><![CDATA[Stories by PyC Team on Medium]]></description>
        <link>https://medium.com/@pyc.devteam?source=rss-bd88c5331b4e------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*XMMJtnXFQrh7vzia4F2ozg.png</url>
            <title>Stories by PyC Team on Medium</title>
            <link>https://medium.com/@pyc.devteam?source=rss-bd88c5331b4e------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 04:47:02 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@pyc.devteam/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[Quantitative metrics in concept interpretability]]></title>
            <link>https://medium.com/@pyc.devteam/quantitative-metrics-in-concept-interpretability-e3cdc0833cbe?source=rss-bd88c5331b4e------2</link>
            <guid isPermaLink="false">https://medium.com/p/e3cdc0833cbe</guid>
            <category><![CDATA[interpretability]]></category>
            <category><![CDATA[concept-based-learning]]></category>
            <category><![CDATA[explainable-ai]]></category>
            <category><![CDATA[deep-learning]]></category>
            <category><![CDATA[pytorch]]></category>
            <dc:creator><![CDATA[PyC Team]]></dc:creator>
            <pubDate>Fri, 14 Feb 2025 14:25:58 GMT</pubDate>
            <atom:updated>2025-02-14T14:25:58.773Z</atom:updated>
            <content:encoded><![CDATA[<h4>Concept-based interpretable deep learning in PyTorch (Chapter 3)</h4><p>In the “<a href="https://medium.com/@pyc.devteam/the-tale-of-the-deep-learning-model-that-failed-my-driving-exam-3a7748144230">The tale of the deep learning model that failed my driving exam</a>”, we compared the decisions of an 18-year-old driver and a deep learning (DL) model when facing a tricky intersection where the driver had a green light, but an ambulance unexpectedly crossed. While both drivers stopped at the intersection, when the driver evaluator asked the model to explain how it came to such a decision, he realized the model’s responses were purely technical, focused on pixel values and activations rather than a true understanding of traffic rules or emergency vehicles:</p><blockquote><em>“If pixel 2890 had an RGB value of (28, 178, 111), the model would have chosen to cross.” [Example of a DL driver explanation]</em></blockquote><p>To help the driving evaluator, in the last chapter, we introduced <a href="https://medium.com/@pyc.devteam/concept-bottleneck-models-321b5b6801a6?source=friends_link&amp;sk=502d0f2f8734a088caeef3a0e5a99b79"><strong>Concept Bottleneck Models</strong></a><strong> (CBMs)</strong>:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/0*1hXrsvpkP5LmikuF" /><figcaption>A concept bottleneck model maps raw image features (pixels) to human-understandable concepts (e.g., ambulance crossing) and then relies on these predicted concepts to make decisions (e.g., whether to cross). Image created by the author with assistance from GPT4-o.</figcaption></figure><p>CBMs allow the evaluator to gain insights into a DL model’s decision-making process as:</p><ul><li><strong>Task predictions can be traced back to the activation of human-interpretable concepts</strong>. This enables the model to answer the driving evaluator’s question by saying, “I decided to cross as I saw a green light and there was no ambulance”.</li><li><strong>Altering concept values changes the model’s decisions</strong>. This allows the model to respond to the evaluator’s question with, “In the same scenario, if there is an ambulance, I would cross”.</li></ul><p>While these qualitative answers reassured the evaluator, he now needs a <strong>quantitative</strong> way to assess how well the model responds in different situations. To show this, we use three key metrics in concept-based interpretability: concept/task <strong>predictive performance</strong>, <strong>intervention effectiveness</strong>, and <strong>concept completeness</strong>.</p><p>In the following, we assume we have a CBM (θ₍g₎, θ₍f₎) and a dataset of i.i.d. triples (input, concepts, task) 𝒟 = {(𝒙, 𝒄, 𝓎)}, which we use to compute these metrics.</p><h3>1. Predictive performance</h3><h4>Task performance</h4><p>In the driving test, task performance [1] measures how well the model predicts whether to cross or stop at the intersection, based on concepts like traffic light color and the ambulance. A high task performance indicates that the model generally makes the correct decision. Task performance is usually represented as the likelihood:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*odl2rIPalXNW0UPHsCWpDA.png" /></figure><p>Notice that in common classification settings, this likelihood becomes the <strong>empirical test task accuracy of the model</strong>.</p><p><strong>Limitations:</strong> High task performance alone doesn’t guarantee interpretability. The model might achieve high performance by relying on uninterpretable or poorly defined concepts, potentially missing key information. For example, a CBM could correctly predict whether to cross or stop, but its decisions might be unaffected by changing the value of the concept “ambulance”.</p><h4>Concept performance</h4><p>Concept performance [1] measures how well the model correctly predicts the concepts from the input data. In our driving example, this evaluates how well the model detects traffic light color or the ambulance’s presence. Similarly to task performance, concept performance can be measured as the likelihood:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DgUjZPCaK5Sj7csu2uF-7w.png" /></figure><p>In practice, this likelihood may be replaced with proxy metrics such as mean <a href="https://en.wikipedia.org/wiki/Receiver_operating_characteristic">Area Under the Receiving Operating Characteristic curve (AUC-ROC)</a> across all concepts. This is particularly useful in situations where concepts are highly imbalanced, and therefore a majority classifier may achieve high likelihood without really learning anything.</p><p><strong>Limitations:</strong> While high concept performance means the model can correctly identify the traffic light color and ambulance presence, it doesn’t always imply that the model will make the right final decision. For example, even if the model accurately identifies a green light and no ambulance, it might still incorrectly predict that the car should stop.</p><h3>2. Intervention effectiveness</h3><p>In the coding practice of the previous chapter, we observed how changing the predicted value of a concept affects task predictions. This property allows human experts to intervene on mispredicted concept values and correct them to improve the model’s task performance. Intervention effectiveness [1] measures how well a CBM improves task performance after such corrections. This can be achieved by computing the <strong>area under the intervention curve</strong> as follows:</p><ul><li>consider a subset of the powerset of the concept indices <em>I</em> ⊆ 𝒫({1, …, |𝒞|}) to intervene on;</li><li>for each concept group <em>I</em>′ ∈ <em>I</em>, replace the subset of predictions g(𝒙)[<em>I</em>′] with ground truth values 𝒄[<em>I</em>′];</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*gh8WCvw7jUs8hMAop3XVnA.png" /></figure><p>In the driving test scenario, imagine the model incorrectly predicts that the ambulance is absent, leading to a wrong decision to cross. This metric would evaluate whether correcting the concept by setting “ambulance = 1” leads the model to change its prediction to the correct one (i.e., stopping). After this human intervention, we expect the model to revise its decision and recommend stopping the car.</p><p>Intervention effectiveness is particularly useful when human experts need to intervene and adjust concept values to improve the model’s decisions, ensuring the model responds accurately after corrections.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*7T3q2t4nUlm0hnYt.jpg" /><figcaption>Much like placing a hand in a row of falling dominoes stops their motion, a concept intervention pauses the inference process. This pause allows human experts to correct the model’s mistakes, after which the inference can resume from that point onward. <em>Image credit: needpix.com (public domain).</em></figcaption></figure><p><strong>Limitations:</strong> This metric is highly dependent on the quality of the interventions provided by humans. Poorly chosen interventions can reduce the model’s performance, even if the concepts themselves are correct. Moreover, if the concept predictive accuracy is already very high, then this metric may not give you any new insights as interventions would have very little effect on the actual bottleneck.</p><h3>3. Concept completeness</h3><p>In the driving test example, the driving evaluator may wonder whether a CBM underperforms compared to a standard black-box model that does not use concepts. This question can be answered using concept completeness [2], a metric that helps determining if the concepts alone provide sufficient information for the model to solve the task when compared to a black box.</p><p>Concept completeness1 is defined as the ratio of the task performance of a CBM to that of a black-box model (θ₍b₎):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*G8Xd3wDMBYFAIE4SvP7U5A.png" /></figure><p>In the driving test scenario, if the CBM uses concepts like “traffic light color” and “ambulance presence” and achieves a task accuracy that is close to or greater than a black-box model trained directly on raw input (e.g., image pixels), it implies that the concepts provide the necessary information to complete the task. For instance, if the CBM’s accuracy in predicting whether to stop or cross is 95% and the black-box model’s accuracy is 96%, the high concept completeness score shows that interpretability is achieved with minimal loss in task accuracy.</p><p><strong>Limitations:</strong> High concept completeness does not guarantee that the concepts themselves are fully interpretable or well-defined; a model might achieve high completeness while relying on concepts that are difficult for humans to understand.</p><h3>Coding practice: quantitative metrics for concept-based models</h3><p>In this practice, we will evaluate a CBM’s predictive performance, intervention effectiveness, and concept completeness in a simple traffic light scenario where decisions to cross or stop depend on two concepts: the traffic light being green and the presence of an ambulance. The model predicts these concepts and uses them to make decisions.</p><blockquote><em>⚠️ </em><strong><em>Warning: </em></strong><em>This section assumes familiarity with the first chapter on CBMs and PyC (PyC must be installed before running this code). If you’re new to these concepts, it’s recommended to start with </em><a href="https://medium.com/@pyc.devteam/concept-bottleneck-models-321b5b6801a6?source=friends_link&amp;sk=502d0f2f8734a088caeef3a0e5a99b79"><em>this introductory chapter</em></a><em> for foundational understanding.</em></blockquote><h3>Step #1: Train a CBM</h3><p>We begin this practice by re-training the Concept Bottleneck Model (CBM) used in the previous coding exercise.</p><pre>import numpy as np<br>import torch<br><br>from torch_concepts.nn import LinearConceptLayer<br>from torch_concepts.data import TrafficLights<br><br># Fix seeds first<br>seed = 42<br>torch.manual_seed(seed)<br>np.random.seed(seed)<br><br>n_samples = 500<br><br># Loading training dataset<br>dataset = TrafficLights(<br>    n_samples=n_samples,<br>    possible_starting_directions=[&#39;west&#39;],<br>    resize_final_image=0.05,<br>    selected_concepts=[<br>        &#39;green light on selected lane&#39;,<br>        &#39;car in intersection&#39;,<br>        &#39;ambulance seen&#39;,<br>        &#39;ambulance approaching perpendicular to selected car&#39;,<br>    ],<br>    split=&#39;train&#39;,<br>)<br>concept_names, task_names = dataset.concept_names, dataset.task_names<br>n_concepts = len(concept_names)<br><br># Loading testing dataset<br># Generate the test dataset<br>test_dataset = TrafficLights(<br>    n_samples=n_samples,<br>    possible_starting_directions=[&#39;west&#39;],<br>    resize_final_image=0.05,<br>    selected_concepts=[<br>        &#39;green light on selected lane&#39;,<br>        &#39;car in intersection&#39;,<br>        &#39;ambulance seen&#39;,<br>        &#39;ambulance approaching perpendicular to selected car&#39;,<br>    ],<br>    split=&#39;test&#39;,<br>)<br>print(<br>    f&quot;Training set has {len(dataset)} samples while test set &quot;<br>    f&quot;has {len(test_dataset)} samples&quot;<br>)</pre><p>Output:</p><pre>Training set has 300 samples while test set has 100 samples</pre><pre># Define the CBM<br><br>latent_dims = 32<br><br># The encoder extracts a low-dimensional representation of the input<br>encoder = torch.nn.Sequential(<br>    # A 3x3 convolution with 4 output channels<br>    torch.nn.Conv2d(3, 4, (3, 3), padding=&#39;same&#39;),<br>    torch.nn.LeakyReLU(),<br><br>    # A 3x3 convolution with 4 output channels with a batch norm<br>    torch.nn.Conv2d(4, 4, (3, 3), padding=&#39;same&#39;),<br>    torch.nn.LeakyReLU(),<br>    torch.nn.BatchNorm2d(4),<br><br>    # A 3x3 convolution with 4 output channels<br>    torch.nn.Conv2d(4, 4, (3, 3), padding=&#39;same&#39;),<br>    torch.nn.LeakyReLU(),<br><br>    # A 3x3 convolution with 4 output channels with a batch norm<br>    torch.nn.Conv2d(4, 4, (3, 3), padding=&#39;same&#39;),<br>    torch.nn.LeakyReLU(),<br>    torch.nn.BatchNorm2d(4),<br><br>    # A 5x5 max pooling layer<br>    torch.nn.MaxPool2d((5, 5)),<br><br>    # Finally, we flatten and map it to a known latent space size<br>    torch.nn.Flatten(start_dim=1, end_dim=-1),<br>    torch.nn.Linear(576, latent_dims),  # 576 comes from the size after flattening<br>    torch.nn.LeakyReLU(),<br>)<br># The concept scorer predicts concept logits {traffic light color, ambulance presence}<br>c_layer = LinearConceptLayer(<br>    in_features=latent_dims,<br>    out_annotations=concept_names,<br>)<br><br># The task predictor determines the value of the downstream label {cross}<br>y_predictor = torch.nn.Sequential(<br>    torch.nn.Linear(n_concepts, latent_dims),<br>    torch.nn.LeakyReLU(),<br>    torch.nn.Linear(latent_dims, 1), # output shape 1 as it is a binary task<br>)<br>model = torch.nn.Sequential(encoder, c_layer, y_predictor)<br><br># Print it out<br>model</pre><p>Output:</p><pre>Sequential(<br>  (0): Sequential(<br>    (0): Conv2d(3, 4, kernel_size=(3, 3), stride=(1, 1), padding=same)<br>    (1): LeakyReLU(negative_slope=0.01)<br>    (2): Conv2d(4, 4, kernel_size=(3, 3), stride=(1, 1), padding=same)<br>    (3): LeakyReLU(negative_slope=0.01)<br>    (4): BatchNorm2d(4, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)<br>    (5): Conv2d(4, 4, kernel_size=(3, 3), stride=(1, 1), padding=same)<br>    (6): LeakyReLU(negative_slope=0.01)<br>    (7): Conv2d(4, 4, kernel_size=(3, 3), stride=(1, 1), padding=same)<br>    (8): LeakyReLU(negative_slope=0.01)<br>    (9): BatchNorm2d(4, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)<br>    (10): MaxPool2d(kernel_size=(5, 5), stride=(5, 5), padding=0, dilation=1, ceil_mode=False)<br>    (11): Flatten(start_dim=1, end_dim=-1)<br>    (12): Linear(in_features=576, out_features=32, bias=True)<br>    (13): LeakyReLU(negative_slope=0.01)<br>  )<br>  (1): LinearConceptLayer(<br>    (transform): Sequential(<br>      (0): Linear(in_features=32, out_features=4, bias=True)<br>      (1): Unflatten(dim=-1, unflattened_size=[4])<br>      (2): Annotate()<br>    )<br>  )<br>  (2): Sequential(<br>    (0): Linear(in_features=4, out_features=32, bias=True)<br>    (1): LeakyReLU(negative_slope=0.01)<br>    (2): Linear(in_features=32, out_features=1, bias=True)<br>  )<br>)</pre><p>We train the model:</p><pre>from torch.utils.data import DataLoader<br><br>n_epochs = 20<br>concept_loss_weight = 10<br>lr = 0.01<br>batch_size = 50<br><br># Define optimizer and loss function<br>model = torch.nn.Sequential(encoder, c_layer, y_predictor)<br>optimizer = torch.optim.AdamW(model.parameters(), lr=lr)<br>loss_fn = torch.nn.BCELoss()<br><br># Make a batch dataset loader<br>dataloader = DataLoader(<br>    dataset,<br>    batch_size=batch_size,<br>    shuffle=True,<br>    drop_last=False,<br>    num_workers=2,<br>)<br><br># Standard PyTorch learning cycle<br>model.train()<br>for epoch in range(n_epochs):<br>    for batch_idx, (x, y, c, _, _) in enumerate(dataloader):<br>        # Encode input, then predict concept and downstream tasks activations<br>        emb = encoder(x)<br>        c_pred = c_layer(emb).sigmoid()<br>        y_pred = y_predictor(c_pred).sigmoid().view(-1)<br><br>        # Double loss on concepts and tasks<br>        loss = loss_fn(y_pred, y) + concept_loss_weight * loss_fn(c_pred, c)<br><br>        # Perform the update<br>        optimizer.zero_grad()<br>        loss.backward()<br>        optimizer.step()<br>        task_acc = torch.mean(((y_pred &gt; 0.5) == y).type(torch.float))<br>        task_acc = task_acc.detach().cpu().numpy()<br>        if ((epoch + 1) % 5 == 0) and (batch_idx == 0):<br>            print(<br>                f&quot;Epoch [{epoch+1}/{n_epochs}], &quot;<br>                f&quot;Step [{batch_idx+1}/{len(dataloader)}], &quot;<br>                f&quot;Loss: {loss.item():.4f}, &quot;<br>                f&quot;Task Accuracy: {task_acc * 100:.2f}%, &quot;<br>            )</pre><p>Output:</p><pre>Epoch [5/20], Step [1/6], Loss: 3.0958, Task Accuracy: 96.00%, <br>Epoch [10/20], Step [1/6], Loss: 1.9020, Task Accuracy: 90.00%, <br>Epoch [15/20], Step [1/6], Loss: 1.8252, Task Accuracy: 90.00%, <br>Epoch [20/20], Step [1/6], Loss: 0.8952, Task Accuracy: 94.00%,</pre><p>We then evaluate this trained model on the test dataset. For this, we first load the test set into memory (important note: we would only recommend this for very small datasets like the toy example here. With large dataset, you should use batched data loaders instead):</p><pre># Load the train set to memory<br>x_train = []<br>c_train = []<br>y_train = []<br>for (x, y, c, _, _) in dataset:<br>    x_train.append(x.unsqueeze(0))<br>    y_train.append(y.unsqueeze(0))<br>    c_train.append(c.unsqueeze(0))<br>x_train = torch.concat(x_train, dim=0)<br>y_train = torch.concat(y_train, dim=0)<br>c_train = torch.concat(c_train, dim=0)<br><br># Load the test set to memory<br>x_test = []<br>c_test = []<br>y_test = []<br>for (x, y, c, _, _) in test_dataset:<br>    x_test.append(x.unsqueeze(0))<br>    y_test.append(y.unsqueeze(0))<br>    c_test.append(c.unsqueeze(0))<br>x_test = torch.concat(x_test, dim=0)<br>y_test = torch.concat(y_test, dim=0)<br>c_test = torch.concat(c_test, dim=0)</pre><p>And we can now evaluate the test concept and label predictions:</p><pre>model.eval()<br>c_pred = c_layer(encoder(x_test)).sigmoid()<br>y_pred = y_predictor(c_pred).sigmoid()<br>print(&quot;Average task prediction:&quot;, y_pred.mean(0).detach().cpu().numpy())<br>print(&quot;Average concept prediction:&quot;, c_pred.mean(0).detach().cpu().numpy())</pre><p>Output:</p><pre>Average task prediction: [0.46639043]<br>Average concept prediction: [0.74174714 0.5710389  0.32018384 0.2333349 ]</pre><h3>Step #2: Compute task and concept performance</h3><p>In our traffic light dataset, both concepts (“traffic light color” and “ambulance presence”) and the output (“cross”) are binary variables. This allows us to evaluate task and concept performance using the Area Under the Receiver Operating Characteristic Curve (ROC AUC) from prediction scores, a standard metric for binary classification problems.</p><pre>from sklearn.metrics import roc_auc_score<br><br>concept_performance = roc_auc_score(c_test, c_pred.detach())<br>task_performance = roc_auc_score(y_test, y_pred.detach())<br><br>print(f&#39;Task performance: {task_performance*100:.2f}%&#39;)<br>print(f&#39;Concept performance: {concept_performance*100:.2f}%&#39;)</pre><p>Output:</p><pre>Task performance: 91.62%<br>Concept performance: 94.39%</pre><p>After a few epochs, the CBM is already able to accurately predict both concepts and tasks.</p><h3>Step #3: Compute intervention effectiveness</h3><p>In our traffic light scenario, we can evaluate how intervening by correcting mispredicted concepts — such as “traffic light color” or “ambulance presence” — improves the model’s decision on whether to “cross.” By replacing predicted concept values with ground truth values and recalculating model performance, we measure how each intervention impacts downstream task accuracy. In PyC, this requires specifying a list of concept index groups to intervene on.</p><pre>from torch_concepts.metrics import intervention_score<br><br>intervention_groups = [[], [0], [1], [0, 1]]<br><br># Evaluate intervention effectiveness of each concept group individually<br>intervention_scores = intervention_score(<br>    y_predictor,<br>    c_pred,<br>    c_test,<br>    y_test,<br>    intervention_groups,<br>    auc=False,<br>)<br>print(f&#39;Individual intervention scores: {intervention_scores}&#39;)<br><br># Evaluate the global intervention effectiveness as the AUC<br>intervention_auc = intervention_score(<br>    y_predictor,<br>    c_pred,<br>    c_test,<br>    y_test,<br>    intervention_groups,<br>)<br>print(f&#39;Intervention AUC: {intervention_auc:.4f}&#39;)</pre><p>Output:</p><pre>Individual intervention scores: [0.9161793372319689, 0.9207277452891488, 0.9285250162443145, 0.9285250162443145]<br>Intervention AUC: 0.9235</pre><p>The high intervention AUC indicates that concept interventions effectively improve the model’s task performance. Individual intervention scores show that correcting the “traffic light color” concept provides the most significant improvement in downstream task performance.</p><h3>Step #4: Compute concept completeness</h3><p>To compute concept completeness, we need a black-box baseline model that uses both raw features and concept labels, as this matches the information provided to the CBM. The following code implements a simple black-box model with a similar parameter count to the CBM for a fair comparison.</p><pre># Make maps containing the values of each concept<br>c_train_maps = c_train.unsqueeze(-1).unsqueeze(-1)<br>c_train_maps = c_train_maps.expand(-1, -1, 64, 64)<br>c_test_maps = c_test.unsqueeze(-1).unsqueeze(-1)<br>c_test_maps = c_test_maps.expand(-1, -1, 64, 64)<br><br># Put them together with the input features<br>xc_train = torch.concat((x_train, c_train_maps), dim=1)<br>xc_test = torch.concat((x_test, c_test_maps), dim=1)<br><br># Defining a balck box baseline<br>baseline = torch.nn.Sequential(<br>    # A 3x3 convolution with 4 output channels<br>    torch.nn.Conv2d(3 + n_concepts, 4, (3, 3), padding=&#39;same&#39;),<br>    torch.nn.LeakyReLU(),<br><br>    # A 3x3 convolution with 4 output channels with a batch norm<br>    torch.nn.Conv2d(4, 4, (3, 3), padding=&#39;same&#39;),<br>    torch.nn.LeakyReLU(),<br>    torch.nn.BatchNorm2d(4),<br><br>    # A 3x3 convolution with 4 output channels<br>    torch.nn.Conv2d(4, 4, (3, 3), padding=&#39;same&#39;),<br>    torch.nn.LeakyReLU(),<br><br>    # A 3x3 convolution with 4 output channels with a batch norm<br>    torch.nn.Conv2d(4, 4, (3, 3), padding=&#39;same&#39;),<br>    torch.nn.LeakyReLU(),<br>    torch.nn.BatchNorm2d(4),<br><br>    # A 5x5 max pooling layer<br>    torch.nn.MaxPool2d((5, 5)),<br><br>    # Finally, we flatten and map it to a known latent space size<br>    torch.nn.Flatten(start_dim=1, end_dim=-1),<br>    torch.nn.Linear(576, 1),<br>)<br><br># Define optimizer and loss function<br>optimizer = torch.optim.AdamW(baseline.parameters(), lr=lr)<br>loss_fn = torch.nn.BCELoss()<br><br># Standard PyTorch learning cycle<br>baseline.train()<br>for epoch in range(n_epochs):<br>   optimizer.zero_grad()<br><br>   # Encode input, then predict concept and downstream tasks activations<br>   y_pred_baseline = baseline(xc_train).sigmoid().view(-1)<br><br>   # Double loss on concepts and tasks<br>   loss = loss_fn(y_pred_baseline, y_train)<br>   loss.backward()<br>   optimizer.step()<br><br>baseline.eval()<br>y_pred_baseline = baseline(xc_test).sigmoid()<br>task_performance_baseline = roc_auc_score(y_test, y_pred_baseline.detach())</pre><p>We can then compute concept completeness as the ratio between the CBM’s downstream task performance and that of the black-box baseline. This ratio indicates the cost of incorporating a concept layer within the network to improve interpretability compared to an equivalent black-box model.</p><pre>from torch_concepts.metrics import completeness_score<br><br>concept_completeness = completeness_score(y_test, y_pred_baseline, y_pred)<br><br>print(f&#39;Task performance: {task_performance*100:.2f}%&#39;)<br>print(f&#39;Task performance baseline: {task_performance_baseline*100:.2f}%&#39;)<br>print(f&#39;Concept completeness: {concept_completeness*100:.2f}%&#39;)</pre><p>Output:</p><pre>Task performance: 91.62%<br>Task performance baseline: 96.69%<br>Concept completeness: 94.76%</pre><p>The resulting concept completeness score suggests that the CBM reflects a moderate drop in downstream task performance compared to the black-box baseline, in exchange for the increased interpretability.</p><h3>Take Home Message</h3><p>In summary, this chapter demonstrated how to quantitatively evaluate CBMs using metrics such as task and concept performance, intervention effectiveness, and concept completeness. These metrics provide a insight into the model’s accuracy and interpretability as follows:</p><ul><li><strong>Task and concept performance:</strong> This metric quantifies a CBM’s predictive performance, assessing how accurately the model predicts concepts and downstream tasks.</li><li><strong>Intervention effectiveness:</strong> This metric quantifies the impact of concepts on a CBM’s task predictions. Specifically, concept interventions can be used to assess how correcting mispredicted concepts can improve downstream task performance.</li><li><strong>Concept completeness:</strong> This metric quantifies the cost in downstream task performance of introducing a concept layer compared to a black-box model.</li></ul><p>The next chapter will discuss the accuracy-explainability trade-off, a potential challenge in concept-based interpretability, and strategies to mitigate its impact on CBMs.</p><h3>References</h3><p>If you found this chapter useful for your blog post, research article or product, we would be grateful if you would cite it like this:</p><pre>Barbiero P., Ciravegna G., Debot D., Diligenti M., <br>Dominici G., Espinosa Zarlenga M., Giannini F., Marra G. (2024).<br>Concept-based Interpretable Deep Learning in Python.<br>https://pyc-team.github.io/pyc-book/intro.html</pre><p>Or use the following bibtex entry:</p><pre>@book{pycteam2024concept,<br>  title      = {Concept-based Interpretable Deep Learning in Python},<br>  author     = {Pietro Barbiero, Gabriele Ciravegna, David Debot, Michelangelo Diligenti, Gabriele Dominici, Mateo Espinosa Zarlenga, Francesco Giannini, Giuseppe Marra},<br>  year       = {2024},<br>  url        = {https://pyc-team.github.io/pyc-book/intro.html}<br>}</pre><p><strong>Bibliography</strong></p><p>[1] Koh, Pang Wei, et al. “Concept bottleneck models.” International conference on machine learning. PMLR, 2020.</p><p>[2] Yeh, Chih-Kuan, et al. “On completeness-aware concept-based explanations in deep neural networks.” Advances in neural information processing systems 33 (2020): 20554–20565.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e3cdc0833cbe" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Concept Bottleneck Models]]></title>
            <link>https://medium.com/@pyc.devteam/concept-bottleneck-models-321b5b6801a6?source=rss-bd88c5331b4e------2</link>
            <guid isPermaLink="false">https://medium.com/p/321b5b6801a6</guid>
            <category><![CDATA[concept-based-learning]]></category>
            <category><![CDATA[pytorch]]></category>
            <category><![CDATA[explainable-ai]]></category>
            <category><![CDATA[interpretability]]></category>
            <category><![CDATA[deep-learning]]></category>
            <dc:creator><![CDATA[PyC Team]]></dc:creator>
            <pubDate>Fri, 14 Feb 2025 14:25:49 GMT</pubDate>
            <atom:updated>2025-02-14T14:25:49.313Z</atom:updated>
            <content:encoded><![CDATA[<h4>Concept-based interpretable deep learning in PyTorch (Chapter 2)</h4><p>In the “<a href="https://medium.com/@pyc.devteam/the-tale-of-the-deep-learning-model-that-failed-my-driving-exam-3a7748144230">The tale of the deep learning model that failed my driving exam</a>”, we compared the decisions of an 18-year-old driver and a deep learning (DL) model when facing a tricky intersection where the driver had a green light, but an ambulance unexpectedly crossed.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*csC0A_HS39ebdoSPOMceoA.png" /><figcaption>Driving test scenario: the traffic light is green, but just as the driver gets closer, an ambulance speeds through and crosses the intersection. Image generated with GPT4-o.</figcaption></figure><p>While both drivers stopped at the intersection, the teenager could explain how she arrived at that conclusion in terms of “<strong><em>CONCEPTS”</em></strong> like the light color and the presence (or absence) of the ambulance:</p><blockquote><em>“I would have crossed if the ambulance wasn’t there, but I know for sure that I would never cross in the presence of an ambulance.” [Example of a human driver explanation]</em></blockquote><p>In contrast, the DL model, despite making the correct decision and having been trained on millions of instances where lights and ambulances were around, lacked a satisfying decision-making process for the driving evaluator, as the model’s explanations were based on raw pixel activations:</p><blockquote><em>“If pixel 2890 had an RGB value of (28, 178, 111), the model would have chosen to cross.” [Example of a DL driver explanation]</em></blockquote><p>This example highlights the need for developing DL models with a transparent decision-making process like the teenager’s, especially in high-stakes fields such as medicine (deciding whether to give a certain treatment), finance (deciding whether to approve a loan), and law (checking whether a hiring system is fair).</p><h3>Blueprint for concept-based deep learning models</h3><blockquote>⚠️ <strong>Warning:</strong> The following paragraphs assume a basic understanding of probability.</blockquote><p>From a technical standpoint, the problem can be described as follows: we aim to model a relationship between a set of random variables 𝑋 ∈ 𝒳 representing low-level perceptive features (such as pixels) and a set of random variables corresponding to decisions 𝑌 ∈ 𝒴 (whether to cross or stop).</p><p>The DL model in the tale modeled the relationship as p(𝑌 = ‘cross’ ∣ 𝑋 = ‘image’s pixels’), directly mapping raw image data to a decision.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/564/1*b471SW3RGg6EXsC1nupIDg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AmJyGGAH790xI_rEI0wpYQ.png" /><figcaption>A DL model directly maps raw image features (pixels) to a decision (e.g., whether to cross). Image created by the author with assistance from GPT4-o.</figcaption></figure><p>The teenager, instead, modeled the same problem using intermediate <strong>higher-level variables — referred to as “concepts”, like the light color and the ambulance — leading to a more human-interpretable decision-making process</strong>. Based on such concepts, her reasoning could be expressed as p(𝑌 = “cross” ∣ 𝐶₁ = “light color”, 𝐶₂ = “ambulance”), where the concepts 𝐶 ∈ 𝒞 provided insight into <strong>how</strong> she made a particular decision.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/810/1*uxzekneh4dFzrb-TNmyzYw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1022/0*AMieBogeYXRf2VWa" /><figcaption>A human driver bases their decisions (e.g., whether to cross) on concepts (e.g., ambulance crossing). Image created by the author.</figcaption></figure><p>To emulate the teenager’s approach, we re-write the probability distribution p(Y ∣ X) as if it were obtained by marginalizing the joint distribution p(Y, C ∣ X) over the concepts C. Now, assuming that Y is conditionally independent of X given C, we can obtain the following factorization:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/896/1*UulxDMK5fNsa2zLxVxEzAw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*CI8La61G_Obaa0Gq" /><figcaption>A concept bottleneck model maps raw image features (pixels) to human-understandable concepts (e.g., ambulance crossing) and then relies on these predicted concepts to make decisions (e.g., whether to cross). Image created by the author with assistance from GPT4-o.</figcaption></figure><p>In this formulation, the model observes the input features <em>X</em> (such as pixels from an image) and maps them to a set of interpretable, high-level variables <em>C</em>, known as “<a href="https://arxiv.org/abs/1711.11279"><strong><em>concepts</em></strong></a>” [1,2,3]. These concepts are analogous to the reasoning elements identified by the teenager — such as the traffic light color or the presence of an ambulance. The second part of the model then uses these concepts to determine the decision <em>Y</em> (whether to cross or stop). This class of models is known as a <a href="https://arxiv.org/abs/2007.04612"><strong>Concept Bottleneck Model</strong></a> (CBM) [4]. CBMs parametrize the conditional distributions with a pair of neural networks:</p><ul><li>A “concept encoder” 𝑔(⋅) that takes as an input a sample 𝐱 ∈ 𝒳, and predicts a set of k concepts 𝐜 ∈ 𝒞. This network parameterizes the concept distribution.</li><li>A “task predictor” 𝑓(⋅) that takes as an input a concept vector 𝐜 ∈ 𝒞 and predicts an output label 𝓎 ∈ 𝒴. This network parameterizes the output distribution.</li></ul><p>Given a concept-based dataset of i.i.d. triples (input, concepts, task) 𝐷 = {(𝐱, 𝐜, 𝓎)}, the CBM’s parameters (θ₍g₎ and θ₍f₎) are usually optimized via gradient descent by maximizing the empirical log-likelihood of the training data:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*TNNRHNPbm97mI_Eg7pCIZA.png" /></figure><p>In most instances (but not all), we are trying to model a scenario where we are given 𝑘 different concept annotations during training and the output task can be one of 𝐿 different labels (e.g., multi-task classification). In that case, it is common to model the data distribution by interpreting the output of 𝑔 and 𝑓 as probabilities over their output domains. That is</p><ol><li>The concept encoder 𝑔(𝑥) = ĉ ∈ [0, 1]^𝑘 outputs a 𝑘-dimensional vector where ĉᵢ = p(𝑐ᵢ = 1 ∣ 𝑋 = 𝑥), and</li><li>The label predictor 𝑓(𝑐) = ŷ ∈ [0, 1]^𝐿 outputs an 𝐿-dimensional vector where 𝐿 is the number of output labels, ∑ᵢ ŷᵢ = 1 (e.g., a softmax activation is used), and ŷᵢ = p(𝑌 = i ∣ 𝐶 = 𝑐).</li></ol><p>In this instance, the likelihood above can be expressed as:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qVqW4nyW8ySy7zgRS_wp_w.png" /></figure><p>Where ℒ₍CE₎ and ℒ₍BCE₎ stand for the standard <a href="https://en.wikipedia.org/wiki/Cross-entropy">cross entropy and binary cross entropy losses</a> (notice the arg max turned into an arg min as we are working with negative log-likelihoods when using the cross entropy losses).</p><p>In CBMs there are three different ways to optimize this objective function:</p><ol><li><strong>Independent training</strong> optimizes f and g independently: f is trained using ground truth concepts c as input. At test time, f takes g(x) as input.</li><li><strong>Sequential training</strong> first optimizes g independently. Once trained, the parameters of the concept encoder g are frozen, and f is trained using g(x) as input.</li><li><strong>Joint training</strong> optimizes f and g at the same time and f takes as input g(x).</li></ol><p>The following coding practice introduces you to implementing CBMs and shows how to query CBMs to understand the model’s decision-making process.</p><h3>Coding practice</h3><p>In this practice, we implement a Concept Bottleneck Model (CBM) for a simple traffic light scenario where decisions to cross or stop depend on two concepts: the traffic light being green and the presence of an ambulance. The model predicts these concepts and uses them to make decisions.</p><blockquote><em>⚠️ </em><strong><em>Warning:</em></strong><em> This section assumes a basic understanding of programming machine learning scripts using deep learning frameworks, specifically PyTorch. If you’re new to PyTorch, consider starting with </em><a href="https://www.google.com/url?q=https%3A%2F%2Fpytorch.org%2Ftutorials%2Fbeginner%2Fbasics%2Fintro.html"><em>these introductory tutorials</em></a><em> to get up to speed.</em></blockquote><h3>Step #1: Install PyC</h3><p>First, we install the necessary Python packages required to implement CBMs. This includes our library pytorch-concepts which runs on top of standard deep learning libraries (PyTorch and torch_geometric).</p><pre>pip install torch<br>pip install -i https://test.pypi.org/simple/ --upgrade pytorch-concepts</pre><h3>Step #2: Load traffic scenario</h3><p>Next, we load the dataset for our traffic light scenario. This toy synthetic dataset, programmatically generated in the call below, comprises a collection of images of a single intersection under different conditions. Each image is an image of an intersection where the “target” or “selected” car (the car we need to make the decision for) is approaching the intersection from the left (moving in a west-to-east direction). The task is to decide whether this car should “stop” (label is 0) or “continue” (task label is 1).</p><p>To help with learning concept-based models, this dataset’s images are annotated with the following concept labels:</p><ul><li><strong>Green Light on Selected Lane</strong>: Indicates whether the traffic light on the lane of the selected car is green or not.</li><li><strong>Car in Intersection</strong>: Specifies whether another car is visible in the intersection.</li><li><strong>Ambulance Seen</strong>: Specifies whether an ambulance is visible in the scene.</li><li><strong>Ambulance Approaching Perpendicular to Selected Car</strong>: Specifies whether an ambulance is approaching in a north-to-south or south-to-north direction (i.e., it is moving perpendicularly with respect to the target car).</li></ul><pre># Making seeds fixed for determinism<br><br>import numpy as np<br>import torch<br><br>seed = 42<br>print(f&quot;Setting random seeds to {seed}&quot;)<br>torch.manual_seed(seed)<br>np.random.seed(seed)</pre><pre>from torch_concepts.data import TrafficLights<br><br># In practice, we need datasets that are significantly larger, but we will<br># not use those for this example as we want to focus on the main ideas rather<br># than on performane<br>n_samples = 500<br><br># Loading dataset<br>dataset = TrafficLights(<br>    n_samples=n_samples,<br>    possible_starting_directions=[&#39;west&#39;],<br>    resize_final_image=0.05,<br>    selected_concepts=[<br>        &#39;green light on selected lane&#39;,<br>        &#39;car in intersection&#39;,<br>        &#39;ambulance seen&#39;,<br>        &#39;ambulance approaching perpendicular to selected car&#39;,<br>    ],<br>    split=&#39;train&#39;,<br>)<br>concept_names, task_names = dataset.concept_names, dataset.task_names<br>n_concepts = len(concept_names)<br><br># Example of scenario<br>print(f&quot;Input shape: {dataset.input_shape}&quot;)<br>print(f&quot;Using {n_concepts} concepts with semantics: {concept_names}&quot;)<br>print(f&quot;Task has {len(task_names)} downstream tasks: {task_names}&quot;)</pre><p>Output:</p><pre>Input shape: (3, 64, 64)<br>Using 4 concepts with semantics: [&#39;green light on selected lane&#39;, &#39;car in intersection&#39;, &#39;ambulance seen&#39;, &#39;ambulance approaching perpendicular to selected car&#39;]<br>Task has 1 downstream tasks: [&#39;continue&#39;]</pre><p>We can see some of examples of this dataset to get a sense as to how they look:</p><pre>import matplotlib.pyplot as plt<br>import random<br><br># Select a random sample of indices for the grid<br>grid_size = (3, 3)<br>indices = random.sample(range(len(dataset)), grid_size[0] * grid_size[1])<br>sampled_images = [dataset[i][0] for i in indices]<br>sampled_labels = [dataset[i][1] for i in indices]<br>label_names = [&#39;stop&#39;, &#39;continue&#39;]<br><br># Plot images in a grid<br>fig, axes = plt.subplots(grid_size[0], grid_size[1], figsize=(10, 10))<br>axes = axes.flatten()  # Flatten to easily iterate over axes if needed<br><br>for i, ax in enumerate(axes):<br>    ax.imshow(<br>        sampled_images[i].detach().cpu().numpy().transpose(1, 2, 0),<br>        cmap=None,<br>    )<br>    ax.set_title(<br>        f&quot;Label: {label_names[int(sampled_labels[i])]}&quot;,<br>        fontsize=10,<br>    )<br>    ax.axis(&#39;off&#39;)  # Hide axes<br><br>plt.tight_layout()<br>plt.show()</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/969/1*4SKuGkZsWAkzUn-aQisZ1Q.png" /></figure><h3>Step #3: Define the CBM</h3><p>We are now ready to construct our first CBM. The model comprises three main components: encoder, concept predictor, and task predictor. These components are combined sequentially to form the complete CBM architecture.</p><p><strong>Encoder</strong>: The encoder takes as input low-level features 𝑋 and produces a lower-dimensional representation. The specific architecture of the encoder depends on the input data type; it could be a convolutional neural network, a recurrent neural network, a transformer, or another suitable model.</p><p>Given that we will be working with images, we will use a very simple <strong>Convolutional Neural Network (CNN)</strong>:</p><pre>import torch<br><br>latent_dims = 32<br><br># The encoder extracts a low-dimensional representation of the input<br>encoder = torch.nn.Sequential(<br>    # A 3x3 convolution with 4 output channels<br>    torch.nn.Conv2d(3, 4, (3, 3), padding=&#39;same&#39;),<br>    torch.nn.LeakyReLU(),<br><br>    # A 3x3 convolution with 4 output channels with a batch norm<br>    torch.nn.Conv2d(4, 4, (3, 3), padding=&#39;same&#39;),<br>    torch.nn.LeakyReLU(),<br>    torch.nn.BatchNorm2d(4),<br><br>    # A 3x3 convolution with 4 output channels<br>    torch.nn.Conv2d(4, 4, (3, 3), padding=&#39;same&#39;),<br>    torch.nn.LeakyReLU(),<br><br>    # A 3x3 convolution with 4 output channels with a batch norm<br>    torch.nn.Conv2d(4, 4, (3, 3), padding=&#39;same&#39;),<br>    torch.nn.LeakyReLU(),<br>    torch.nn.BatchNorm2d(4),<br><br>    # A 5x5 max pooling layer<br>    torch.nn.MaxPool2d((5, 5)),<br><br>    # Finally, we flatten and map it to a known latent space size<br>    torch.nn.Flatten(start_dim=1, end_dim=-1),<br>    torch.nn.Linear(576, latent_dims),  # 576 comes from the size after flattening<br>    torch.nn.LeakyReLU(),<br>)<br>encoder</pre><p>Output:</p><pre>Sequential(<br>  (0): Conv2d(3, 4, kernel_size=(3, 3), stride=(1, 1), padding=same)<br>  (1): LeakyReLU(negative_slope=0.01)<br>  (2): Conv2d(4, 4, kernel_size=(3, 3), stride=(1, 1), padding=same)<br>  (3): LeakyReLU(negative_slope=0.01)<br>  (4): BatchNorm2d(4, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)<br>  (5): Conv2d(4, 4, kernel_size=(3, 3), stride=(1, 1), padding=same)<br>  (6): LeakyReLU(negative_slope=0.01)<br>  (7): Conv2d(4, 4, kernel_size=(3, 3), stride=(1, 1), padding=same)<br>  (8): LeakyReLU(negative_slope=0.01)<br>  (9): BatchNorm2d(4, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)<br>  (10): MaxPool2d(kernel_size=(5, 5), stride=(5, 5), padding=0, dilation=1, ceil_mode=False)<br>  (11): Flatten(start_dim=1, end_dim=-1)<br>  (12): Linear(in_features=576, out_features=32, bias=True)<br>  (13): LeakyReLU(negative_slope=0.01)<br>)</pre><p><strong>Linear Concept Layer</strong>: A linear concept layer takes an embedding as input and generates a set of concept representations, which in this case correspond to concept logits. To implement concept layers, we use the <a href="https://github.com/pyc-team/pytorch_concepts">PyC</a> library.</p><p>To define this layer, the following inputs are required:</p><ul><li><strong>in_features</strong>: The size of the embedding generated by the encoder.</li><li><strong>annotations</strong>: A list of integers or a list where each entry represents an axis of the output concept tensor, and, if it is a list, each element in that list represents the semantics assigned to the neuron at that corresponding index for that dimension.</li></ul><p>For example, we can make a LinearConceptLayer that takes as an input a latent representation of size 8 and outputs two neurons aligned to concepts [&quot;ambulance&quot;, &quot;green light&quot;] through the following calls:</p><pre>c_layer_annotated = LinearConceptLayer(<br>    in_features=2,<br>    out_annotations=[&quot;ambulance&quot;, &quot;green light&quot;],  # If we wish to annotate the output neurons with known concepts<br>)</pre><p>or</p><pre>c_layer_unannotated = LinearConceptLayer(<br>    in_features=2,<br>    out_annotations=2,  # If we do not wish to annotate the output neurons<br>)</pre><p>In our example, this layer produces two concept logits corresponding to “traffic light green” and “ambulance crossing”.</p><pre>from torch_concepts.nn import LinearConceptLayer<br><br># The concept scorer predicts concept logits {traffic light color, ambulance presence}<br>c_layer = LinearConceptLayer(<br>    in_features=latent_dims,<br>    out_annotations=concept_names,<br>)<br>print(&#39;Architecture:\n&#39;, c_layer)<br>print(&#39;Expected concepts in output tensor:\n&#39;, c_layer.annotations)</pre><p>Output:</p><pre>Architecture:<br> LinearConceptLayer(<br>  (transform): Sequential(<br>    (0): Linear(in_features=32, out_features=4, bias=True)<br>    (1): Unflatten(dim=-1, unflattened_size=[4])<br>    (2): Annotate()<br>  )<br>)<br>Expected concepts in output tensor:<br> [[&#39;green light on selected lane&#39;, &#39;car in intersection&#39;, &#39;ambulance seen&#39;, &#39;ambulance approaching perpendicular to selected car&#39;]]</pre><p>In PyC concept layers produce as output an object of type AnnotatedTensor whose dimensions are labeled with the provided concept names:</p><pre>concept_tensor = c_layer(torch.randn(1, latent_dims))<br>print(concept_tensor)</pre><p>Output:</p><pre>AnnotatedTensor of shape torch.Size([1, 4]), dtype torch.float32, and annotations [None, [&#39;green light on selected lane&#39;, &#39;car in intersection&#39;, &#39;ambulance seen&#39;, &#39;ambulance approaching perpendicular to selected car&#39;]] for each dimension.</pre><p>The object AnnotatedTensor has built-in methods to extract concept representations from specific dimensions which might be practical for in-depth analysis:</p><pre>concept_traffic_light = concept_tensor.extract_by_annotations([&#39;green light on selected lane&#39;])<br>print(concept_traffic_light)</pre><p>Output:</p><pre>AnnotatedTensor of shape torch.Size([1, 1]), dtype torch.float32, and annotations [None, [&#39;green light on selected lane&#39;]] for each dimension.</pre><p><strong>Task Predictor</strong>: Takes as input the predicted concept representations to determine the final decision on whether to cross or not. In this case, we implement the task predictor with a multi-layer perceptron, but we will discuss more advanced (and interpretable) task predictors later on in this series.</p><pre># The task predictor determines the value of the downstream label {cross}<br>y_predictor = torch.nn.Sequential(<br>    torch.nn.Linear(n_concepts, latent_dims),<br>    torch.nn.LeakyReLU(),<br>    torch.nn.Linear(latent_dims, 1), # output shape 1 as it is a binary task<br>)<br>y_predictor</pre><p>Output:</p><pre>Sequential(<br>  (0): Linear(in_features=4, out_features=32, bias=True)<br>  (1): LeakyReLU(negative_slope=0.01)<br>  (2): Linear(in_features=32, out_features=1, bias=True)<br>)</pre><h3>Step #4: Train the CBM</h3><p>We set the training parameters, including the number of epochs (n_epochs), the batch size (batch_size) and learning rate (lr). Using the AdamW optimizer and Binary Cross-Entropy Loss, we train the CBM through a standard PyTorch loop. Here we will train the model using its <strong>joint</strong> training, where the concept encoder and the label predictor are trained in an end-to-end manner.</p><p>Specifically, in each epoch the model performs a forward pass to predict concepts and the final decision, computes the combined loss, performs backpropagation, and updates the model parameters. The concept regularization weight (concept_loss_weight) controls the weight of the concept loss w.r.t. the downstream task loss:</p><pre>from torch.utils.data import DataLoader<br><br>n_epochs = 20<br>concept_loss_weight = 10<br>lr = 0.01<br>batch_size = 50<br><br># Define optimizer and loss function<br>model = torch.nn.Sequential(encoder, c_layer, y_predictor)<br>optimizer = torch.optim.AdamW(model.parameters(), lr=lr)<br>loss_fn = torch.nn.BCELoss()<br><br># Make a batch dataset loader<br>dataloader = DataLoader(<br>    dataset,<br>    batch_size=batch_size,<br>    shuffle=True,<br>    drop_last=False,<br>    num_workers=2,<br>)<br><br># Standard PyTorch learning cycle<br>model.train()<br>for epoch in range(n_epochs):<br>    for batch_idx, (x, y, c, _, _) in enumerate(dataloader):<br>        # Encode input, then predict concept and downstream tasks activations<br>        emb = encoder(x)<br>        c_pred = c_layer(emb).sigmoid()<br>        y_pred = y_predictor(c_pred).sigmoid().view(-1)<br><br>        # Double loss on concepts and tasks<br>        loss = loss_fn(y_pred, y) + concept_loss_weight * loss_fn(c_pred, c)<br><br>        # Perform the update<br>        optimizer.zero_grad()<br>        loss.backward()<br>        optimizer.step()<br>        task_acc = torch.mean(((y_pred &gt; 0.5) == y).type(torch.float))<br>        task_acc = task_acc.detach().cpu().numpy()<br>        if ((epoch + 1) % 5 == 0) and (batch_idx == 0):<br>            print(<br>                f&quot;Epoch [{epoch+1}/{n_epochs}], &quot;<br>                f&quot;Step [{batch_idx+1}/{len(dataloader)}], &quot;<br>                f&quot;Loss: {loss.item():.4f}, &quot;<br>                f&quot;Task Accuracy: {task_acc * 100:.2f}%, &quot;<br>            )</pre><p>Output:</p><pre>Epoch [5/20], Step [1/6], Loss: 3.3699, Task Accuracy: 82.00%, <br>Epoch [10/20], Step [1/6], Loss: 1.8929, Task Accuracy: 96.00%, <br>Epoch [15/20], Step [1/6], Loss: 1.2713, Task Accuracy: 94.00%, <br>Epoch [20/20], Step [1/6], Loss: 0.6090, Task Accuracy: 100.00%,</pre><h3>Step #5: Trace task prediction back to concept activations</h3><p>We can examine how the concept activations (green light and ambulance presence) influence the downstream task prediction.</p><p>For example, let’s look at a sample where there is a green light for the car, there are no ambulances, and no cars in the intersection. For this, we will first find an example in the training set that has all these properties:</p><pre># Load the data to memory for easy use (we would not recommend doing this for<br># large datasets!):<br>x_samples = []<br>c_samples = []<br>y_samples = []<br>for (x, y, c, _, _) in dataset:<br>    x_samples.append(x.unsqueeze(0))<br>    y_samples.append(y.unsqueeze(0))<br>    c_samples.append(c.unsqueeze(0))<br>x_samples = torch.concat(x_samples, dim=0)<br>y_samples = torch.concat(y_samples, dim=0)<br>c_samples = torch.concat(c_samples, dim=0)<br><br>import numpy as np<br><br># Find all training examples where the light is green, there is no ambulance,<br># and there are no cars in the intersection:<br>samples_with_green_light = (c_samples[:, 0] == 1).detach().cpu().numpy()<br>samples_with_int_cars = (c_samples[:, 1] == 1).detach().cpu().numpy()<br>samples_with_ambulance = (c_samples[:, 2] == 1).detach().cpu().numpy()<br>samples_with_perp_ambulance = (c_samples[:, 3] == 1).detach().cpu().numpy()<br>selected_samples = (<br>    samples_with_green_light &amp;<br>    np.logical_not(samples_with_int_cars) &amp;<br>    np.logical_not(samples_with_ambulance) &amp;<br>    np.logical_not(samples_with_perp_ambulance)<br>)<br>print(<br>    &quot;Found&quot;,<br>    np.sum(selected_samples),<br>    &quot;samples with green light, no ambulance, and no cars on the intersection&quot;<br>)</pre><p>Output:</p><pre>Found 82 samples with green light, no ambulance, and no cars on the intersection</pre><pre>import matplotlib.pyplot as plt<br><br># Select one of those examples for visualization:<br>selected_sample = x_samples[selected_samples, :, :, :][0]<br>selected_label = y_samples[selected_samples][0]<br>label_names = [&#39;stop&#39;, &#39;continue&#39;]<br><br># And visualize it<br>plt.imshow(<br>    selected_sample.detach().cpu().numpy().transpose(1, 2, 0),<br>    cmap=None,<br>)<br>plt.title(<br>    f&quot;Label: {label_names[int(selected_label)]}&quot;,<br>    fontsize=10,<br>)<br>plt.axis(&#39;off&#39;)<br><br>plt.tight_layout()<br>plt.show()</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/450/0*TEd7lJL6NGTJoF3D.png" /></figure><p>Now, let’s look at our model’s predictions for this sample:</p><pre>model.eval()<br>emb = encoder(selected_sample.unsqueeze(0))<br>c_pred = c_layer(emb).sigmoid()<br>y_pred = y_predictor(c_pred).sigmoid().view(-1)<br>print(f&quot;Task Prediction: {label_names[(y_pred[0]&gt;0.5)]}&quot;)<br>for idx, concept_name in enumerate(concept_names):<br>    pred_str = &quot;ON&quot; if c_pred[0, idx] &gt; 0.5 else &quot;OFF&quot;<br>    print(<br>        f&quot;\tOur model predicted concept &#39;{concept_name}&#39; to be {pred_str} &quot;<br>        f&quot;(with prob {c_pred[0, idx]})&quot;<br>    )</pre><p>Output:</p><pre>Task Prediction: continue<br> Our model predicted concept &#39;green light on selected lane&#39; to be ON (with prob 0.9999604225158691)<br> Our model predicted concept &#39;car in intersection&#39; to be OFF (with prob 0.01809137873351574)<br> Our model predicted concept &#39;ambulance seen&#39; to be OFF (with prob 3.915355136996368e-06)<br> Our model predicted concept &#39;ambulance approaching perpendicular to selected car&#39; to be OFF (with prob 1.5054481082188431e-05)</pre><p>The model correctly identifies that the traffic light is green and there is no ambulance or cars in the intersection! This can be used as an explanation for why the model decided the car, coming from the left, to cross (task prediction is continue).</p><h3>Step #6: Change concept activations to affect task predictions</h3><p>Finally, we alter the concept activations to different values and observe how these changes influence the downstream task prediction. This demonstrates how the model’s decisions are directly tied to specific concept activations.</p><pre># Intervene changing the value of the concepts &quot;ambulance&quot; and<br># &quot;ambulance approaching perpendicular to selected car&quot; to True<br>c_intervened = c_pred[0].clone().unsqueeze(0)<br>c_intervened[0, 2] = 1 # Ambulance set to True<br>c_intervened[0, 3] = 1 # Ambulance in perpendicular direction set to True<br><br># Compute new task prediction<br>y_intervened = y_predictor(c_intervened).sigmoid()<br><br>print(f&quot;Task Prediction: {label_names[(y_intervened[0]&gt;0.5)]}&quot;)<br>for idx, concept_name in enumerate(concept_names):<br>    pred_str = &quot;ON&quot; if c_intervened[0, idx] &gt; 0.5 else &quot;OFF&quot;<br>    print(<br>        f&quot;\tOur model predicted concept &#39;{concept_name}&#39; to be {pred_str} &quot;<br>        f&quot;(with prob {c_intervened[0, idx]})&quot;<br>    )</pre><p>Output:</p><pre>Task Prediction: stop<br> Our model predicted concept &#39;green light on selected lane&#39; to be ON (with prob 0.9999604225158691)<br> Our model predicted concept &#39;car in intersection&#39; to be OFF (with prob 0.01809137873351574)<br> Our model predicted concept &#39;ambulance seen&#39; to be ON (with prob 1.0)<br> Our model predicted concept &#39;ambulance approaching perpendicular to selected car&#39; to be ON (with prob 1.0)</pre><p>The model effectively reacts to the concept intervention1 predicting that, in the presence of a greenlight and of an ambulance in the direction perpendicular to it, the car should stop (task prediction is 0).</p><p>More interestingly, pehaps, we can look at what happens when there is an ambulance, but it is moving on the same axis as the target car (and therefore we wouldn’t expect it to have to stop at a green light):</p><pre># Intervene changing the value of the concepts &quot;ambulance&quot; and<br># &quot;ambulance approaching perpendicular to selected car&quot; to False<br>c_intervened = c_pred[0].clone().unsqueeze(0)<br>c_intervened[0, 2] = 1 # Ambulance set to True<br>c_intervened[0, 3] = 0 # Ambulance in perpendicular direction set to False<br><br># Compute new task prediction<br>y_intervened = y_predictor(c_intervened).sigmoid()<br><br>print(f&quot;Task Prediction: {label_names[(y_intervened[0]&gt;0.5)]}&quot;)<br>for idx, concept_name in enumerate(concept_names):<br>    pred_str = &quot;ON&quot; if c_intervened[0, idx] &gt; 0.5 else &quot;OFF&quot;<br>    print(<br>        f&quot;\tOur model predicted concept &#39;{concept_name}&#39; to be {pred_str} &quot;<br>        f&quot;(with prob {c_intervened[0, idx]})&quot;<br>    )</pre><p>Output:</p><pre>Task Prediction: continue<br> Our model predicted concept &#39;green light on selected lane&#39; to be ON (with prob 0.9999604225158691)<br> Our model predicted concept &#39;car in intersection&#39; to be OFF (with prob 0.01809137873351574)<br> Our model predicted concept &#39;ambulance seen&#39; to be ON (with prob 1.0)<br> Our model predicted concept &#39;ambulance approaching perpendicular to selected car&#39; to be OFF (with prob 0.0)</pre><p>Here we see that the model decides to continue, providing further evidence that the model is indeed properly capturing the expected reasoning for this setup!</p><h3>Take home message</h3><p>In summary, in this chapter we demonstrated how to implement Concept Bottleneck Models (CBMs). These models are inherently explainable as:</p><ul><li><strong>Task predictions can be traced back to the activation of human-interpretable concepts</strong>. This enables the model to answer the driving evaluator’s question by saying, “I decided to cross as I saw a green light and there was no ambulance”.</li><li><strong>Altering concept values changes the model’s decisions</strong>. This allows the model to respond to the evaluator’s question with, “In the same scenario, if there is an ambulance, I would cross”.</li></ul><p>The <a href="https://medium.com/@pyc.devteam/quantitative-metrics-in-concept-interpretability-e3cdc0833cbe">next chapter</a> will discuss key metrics used in concept-based interpretability to quantitatively evaluate CBM performance.</p><h3>References</h3><p>If you found this chapter useful for your blog post, research article or product, we would be grateful if you would cite it like this:</p><pre>Barbiero P., Ciravegna G., Debot D., Diligenti M., <br>Dominici G., Espinosa Zarlenga M., Giannini F., Marra G. (2024). <br>Concept-based Interpretable Deep Learning in Python. <br>https://pyc-team.github.io/pyc-book/intro.html</pre><p>Or use the following bibtex entry:</p><pre>@book{pycteam2024concept,<br>  title      = {Concept-based Interpretable Deep Learning in Python},<br>  author     = {Pietro Barbiero, Gabriele Ciravegna, David Debot, Michelangelo Diligenti, Gabriele Dominici, Mateo Espinosa Zarlenga, Francesco Giannini, Giuseppe Marra},<br>  year       = {2024},<br>  url        = {https://pyc-team.github.io/pyc-book/intro.html}<br>}</pre><p><strong>Bibliography</strong></p><p>[1] David Bau, Bolei Zhou, Aditya Khosla, Aude Oliva, and Antonio Torralba. Network dissection: quantifying interpretability of deep visual representations. In <em>Proceedings of the IEEE conference on computer vision and pattern recognition</em>, 6541–6549. 2017.</p><p>[2] Ruth Fong and Andrea Vedaldi. Net2vec: quantifying and explaining how concepts are encoded by filters in deep neural networks. In <em>Proceedings of the IEEE conference on computer vision and pattern recognition</em>, 8730–8738. 2018.</p><p>[3] Been Kim, Martin Wattenberg, Justin Gilmer, Carrie Cai, James Wexler, Fernanda Viegas, and others. Interpretability beyond feature attribution: quantitative testing with concept activation vectors (tcav). In <em>International conference on machine learning</em>, 2668–2677. PMLR, 2018.</p><p>[4] Pang Wei Koh, Thao Nguyen, Yew Siang Tang, Stephen Mussmann, Emma Pierson, Been Kim, and Percy Liang. Concept bottleneck models. In <em>International conference on machine learning</em>, 5338–5348. PMLR, 2020.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=321b5b6801a6" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The tale of the deep learning model that failed my driving exam]]></title>
            <link>https://medium.com/@pyc.devteam/the-tale-of-the-deep-learning-model-that-failed-my-driving-exam-3a7748144230?source=rss-bd88c5331b4e------2</link>
            <guid isPermaLink="false">https://medium.com/p/3a7748144230</guid>
            <category><![CDATA[pytorch]]></category>
            <category><![CDATA[deep-learning]]></category>
            <category><![CDATA[explainable-ai]]></category>
            <category><![CDATA[interpretability]]></category>
            <category><![CDATA[concept-based-learning]]></category>
            <dc:creator><![CDATA[PyC Team]]></dc:creator>
            <pubDate>Wed, 04 Dec 2024 17:33:20 GMT</pubDate>
            <atom:updated>2025-02-14T14:24:07.735Z</atom:updated>
            <content:encoded><![CDATA[<h4>Concept-based interpretable deep learning in PyTorch (Chapter 1)</h4><p>It’s exam day, and you are the driving evaluator. Two very different “drivers” are up for their final test, and it’s your job to determine if they’re ready for a driving license. The first candidate is a teenager, Sarah — she’s about 18, a little nervous but focused. The second “driver” is not a person at all; it’s a deep learning model based on a massive Transformer architecture, trained to predict driving actions like turning, braking, and accelerating based on live HD camera input.</p><p>You take both out on a pre-defined driving circuit, where they’ll need to handle basic maneuvers. But there’s one test you’re particularly interested in — a tricky intersection with a catch: as they approach, the traffic light is green, but just as they get closer, an ambulance speeds through and crosses the intersection.</p><blockquote>Driving test scenario: the traffic light is green, but just as the driver gets closer, an ambulance speeds through and crosses the intersection.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*csC0A_HS39ebdoSPOMceoA.png" /><figcaption>Driving test scenario: the traffic light is green, but just as the driver gets closer, an ambulance speeds through and crosses the intersection. Image generated with GPT4-o.</figcaption></figure><p>You ride with Sarah first. As you approach the intersection, the ambulance cuts across. Sarah reacts quickly, easing the car to a stop. You’re pleased — she’s cautious, and made the right call.</p><p>Next, you get into the vehicle controlled by the deep learning model. The system, connected to the car’s camera, processes the scene in real time. It, too, stops smoothly as the ambulance passes, seemingly making the correct decision. You feel a sense of relief — both drivers handled the situation perfectly.</p><p>However, you are a stickler for details. Stopping was the right move, but did they both do it for the right reasons? Was it understanding or just luck? To find out, you turn to your post-drive evaluation questions, aimed at revealing the drivers’ thought processes.</p><p>You start with Sarah. “Can you explain to me why you decided to stop?” you ask.</p><p>“I saw the green light, and I was ready to cross,” she begins. “But then the ambulance appeared. <strong>I would have crossed if the ambulance wasn’t there, but I know for sure that I would never cross in the presence of an ambulance.</strong>” Her response is clear and confident.</p><blockquote><strong>“I would have crossed if the ambulance wasn’t there, but I know for sure that I would never cross in the presence of an ambulance” </strong><em>[Example of human explanation]</em></blockquote><p>Satisfied with Sarah’s reasoning, you grant her the driving license. She clearly understood the situation and could articulate her decision-making process. She’ll be a cautious and responsible driver.</p><p>Now it’s the deep learning model’s turn. You turn to your AI expert friend for help in interpreting the model’s behavior. “Can you explain to me how the model decided to stop?” you ask.</p><p>Your friend accesses the system and provides an explanation: “<strong>When the mean activation of input embedding block 42 exceeds 173, the model usually chooses to stop. However, in this case, if pixel 2890 had an RGB value of (28, 178, 111), the model would have chosen to cross instead</strong>”.</p><blockquote><strong>“If pixel 2890 had an RGB value of (28, 178, 111), the model would have chosen to cross” [Example of deep learning explanation]</strong></blockquote><p>You frown, confused. What does that have to do with stopping for an ambulance? The explanation feels disconnected from the real-world reasoning you’re used to hearing. The model’s response is technical, tied to pixel values and activations, rather than an understanding of traffic rules or emergency vehicles. It stopped, yes, but did it truly understand <em>why</em>?</p><p>As you sit there, contemplating, the question lingers in your mind: would you give the deep learning model a driving license?</p><h3>Series overview</h3><p>This series will cover the following chapters:</p><ul><li><a href="https://medium.com/@pyc.devteam/concept-bottleneck-models-321b5b6801a6?source=friends_link&amp;sk=502d0f2f8734a088caeef3a0e5a99b79"><strong>Concept Bottleneck Models</strong></a>: an introduction to concept-based interpretable architectures.</li><li><a href="https://medium.com/@pyc.devteam/e3cdc0833cbe?source=friends_link&amp;sk=9b74e2ba5fa981452308796002d6c4f8"><strong>Quantitative metrics in concept interpretability</strong></a>: analysis of the most commonly used metrics in concept based interpretability.</li><li>… and more coming soon!</li></ul><h3>Conference highlights</h3><p>The PyC team will also cover highlights from top-tier AI conferences on concept-based interpretability:</p><p><strong>NeurIPS’24</strong></p><ul><li><a href="https://medium.com/@pyc.devteam/interpretable-concept-based-memory-reasoning-meet-us-at-neurips-2024-7a6ef9ff28ad">Interpretable Concept-based Memory Reasoning</a>: accurate, interpretable, verifiable deep learning models</li><li><a href="https://medium.com/@pyc.devteam/relational-concept-bottleneck-models-meet-us-at-neurips-2024-83b05e75f3ac">Relational Concept Bottleneck Models</a>: Graph Neural Networks expressivity + concept-based interpretability</li></ul><p>If you would like to include your poster in this track, please contact us.</p><h3>References</h3><p>If you found this series useful for your blog post, research article or product, we would be grateful if you would cite it like this:</p><pre>Barbiero P., Ciravegna G., Debot D., Diligenti M., <br>Dominici G., Espinosa Zarlenga M., Giannini F., Marra G. (2024). <br>Concept-based Interpretable Deep Learning in Python. <br>https://pyc-team.github.io/pyc-book/intro.html</pre><p>Or use the following bibtex entry:</p><pre>@book{pycteam2024concept,<br>  title      = {Concept-based Interpretable Deep Learning in Python},<br>  author     = {Pietro Barbiero, Gabriele Ciravegna, David Debot, Michelangelo Diligenti, Gabriele Dominici, Mateo Espinosa Zarlenga, Francesco Giannini, Giuseppe Marra},<br>  year       = {2024},<br>  url        = {https://pyc-team.github.io/pyc-book/intro.html}<br>}</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/375/1*YENSXsE7WjAJ5tN9K66KOw.png" /><figcaption>The PyC Team. Image generated by the authors with GPT4.o.</figcaption></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3a7748144230" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Relational Concept Bottleneck Models (meet us at NeurIPS 2024)]]></title>
            <link>https://medium.com/@pyc.devteam/relational-concept-bottleneck-models-meet-us-at-neurips-2024-83b05e75f3ac?source=rss-bd88c5331b4e------2</link>
            <guid isPermaLink="false">https://medium.com/p/83b05e75f3ac</guid>
            <category><![CDATA[deep-learning]]></category>
            <category><![CDATA[interpretability]]></category>
            <category><![CDATA[explainable-ai]]></category>
            <category><![CDATA[concept-based-learning]]></category>
            <category><![CDATA[graph-neural-networks]]></category>
            <dc:creator><![CDATA[PyC Team]]></dc:creator>
            <pubDate>Wed, 04 Dec 2024 17:30:35 GMT</pubDate>
            <atom:updated>2024-12-05T07:16:45.040Z</atom:updated>
            <content:encoded><![CDATA[<h4>Graph Neural Networks expressivity + concept-based interpretability</h4><h3>Problem statement</h3><p>Understanding and taking decisions about complex scenes are very challenging tasks, for both humans and AI. For instance, imagine you are the evaluator at the driving theory exam (see also the <a href="https://medium.com/@pyc.devteam/the-tale-of-the-deep-learning-model-that-failed-my-driving-exam-3a7748144230">post on practice exam</a>!), and you are comparing the answers of a human (<em>Bob</em>) and two AI agents (lets call them <em>Ag</em>1 and <em>Ag</em>2). By checking their answers, you stumbled upon two apparently similar scenarios, requiring to predict the expected behavior of drivers at a crossing. Your curiosity is triggered when you notice that one AI agent provides different responses for the two cases.</p><blockquote><strong>First question:</strong> <em>“Is it true that vehicle </em><strong><em>H </em></strong><em>should be the first to pass in Figure 1?”</em></blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DmynoCwmDyAf5vWrFKbXfw.png" /><figcaption>Figure 1. Driving test intersection. Image by the authors.</figcaption></figure><p>Looking at the responses, you notice that all (humans and AI) candidates correctly answered. However, as you are a meticulous evaluator, you ask them to motivate their answers. <em>Bob</em> correctly explains that the rules of the road state that vehicles should give priority to the right, and for this reason, <strong>H </strong>should pass first, as it is the only vehicle with no other vehicles on its right. <em>Ag</em>1, whose decision mechanism is based on a Concept Bottleneck Model (CBM) [1], similarly explains that <strong>H </strong>should pass as it has no other vehicles on its right. Finally <em>Ag</em>2, whose decision mechanism is based on a Graph Neural Network (GNN) [2], has also answered corrected, but it explains the reason in this way “<strong>The activation of input embedding block 3 exceeds 100, thus the prediction is that H should pass first</strong>”.</p><p>You’re quite convinced about the rationale behind the answers of <em>Bob</em> and <em>Ag</em>1, however you become very skeptical about accepting the correctness of Ag2’s response. You could have stopped here the inquiry, however as you are actually <strong>very</strong> meticulous, you decide also to ask about the answers to the other question, the one referring to the scenario in Figure 2.</p><blockquote><strong>Second question:</strong> <em>“Is it true that vehicle </em><strong><em>B </em></strong><em>should go before vehicle </em><strong><em>O </em></strong><em>, but after that vehicles </em><strong><em>L </em></strong><em>and </em><strong><em>H </em></strong><em>pass together in Figure 2?”</em></blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BxZrWIzP_j29R6d_Aus2UA.png" /><figcaption>Figure 2. Driving test intersection. Image by the authors.</figcaption></figure><p>Even if similar, the two pictures differentiate for a crucial detail, the presence of yields’ signs. Looking at the responses, you notice that <em>Bob</em> and <em>Ag</em>2 still correctly answered, whereas this time <em>Ag</em>1 is wrong. You already know that <em>Ag</em>2 explanations are almost unreadable, thus you ask just <em>Bob</em> and <em>Ag</em>1 to motivate their answers. <em>Bob</em> correctly explains that a yield sign is more important than naively giving priority to right for vehicles, and for this reason, the correct passing order in Figure 2 is: <strong>L </strong>and <strong>H </strong>first, which can go together as they are not affecting the priority of each other, then <strong>B </strong>and finally <strong>O </strong>, as <strong>B </strong>is at the right of <strong>O</strong>. When you ask to <em>Ag</em>1 why it failed to provide this correct answer, it replicates that it knows for sure that <strong>B </strong>should go before vehicle <strong>O </strong>, but it has no idea on how to decide if <strong>L </strong>and <strong>H </strong>can go together, thus it just flipped a coin, and answered <strong>NO</strong>. The reason why <em>Ag</em>1 was not able to answer with precision is that it can only precisely answer about questions concerning a single object, and it has been not designed to decide if two different cars can go together.</p><h3>Relational Concept Bottleneck Models</h3><blockquote>⚠️ <strong>Warning:</strong> This section is easier to follow with a basic understanding of the First-Order Logic (FOL) mathematical notation.</blockquote><p>As we saw from the examples above, while GNNs (like <em>Ag</em>2) are able to correctly take decisions in relational scenarios, they act as black-box and are not capable of explaining their decisions. On the other hand, standard CBMs (like <em>Ag</em>1) are capable of explaining their predictions, but only on decisions involving a single object (i.e. a car) per time.</p><blockquote>Goal: “Design DL models capable of relational reasoning (akin to a GNN), while also being interpretable (akin to a CBM)”.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/453/1*9_OSDQPvGDV5Rxu9cNzFpA.png" /><figcaption>Current Concept Bottleneck Models (CBMs) are not well-suited for addressing relational problems as they can process only one input entity at a time by construction. On the other hand, existing relational DL methods, such as Graph Neural Networks (GNNs), may effectively solve relational problems, but they are unable to explain their predictions as CBMs would do.</figcaption></figure><h3>First-Order Logic Representation</h3><p>A classical CBM would for example say that if a car named <em>car</em>1 has neither another car at its right nor a yield sign, then it can go. Using FOL to express this knowledge, it would be:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/649/1*NVN9UxOCM2-Vfhco_KzLlA.png" /></figure><p>However, it would be problematic to describe the collective behavior at the crossing without expressing relational concepts involving multiple cars. Relational Concept Bottleneck Models (R-CBM) merge CBMs and GNNs into an interpretable relational setting with the ability to model the relations among groups of entities.</p><p>For example, an R-CBM can express the behavior at the intersection using pairwise priorities rules such as:</p><ul><li>a yield sign for a car results for a higher priority for another incoming car:</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/463/1*GE_VZ8lAEDhueOnhP_AHGQ.png" /></figure><ul><li>similarly, a red light for a car, results for a higher priority for another incoming car:</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/506/1*fVJJ-3mQL62KT2Yf5lmszA.png" /></figure><ul><li>if no red light or yield is present the car at the right has priority:</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/915/1*AMpp5jrAXxidV5wpCKbnAw.png" /></figure><ul><li>if a car has not to give priority to any other car, it has right to go:</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/902/1*_DoaJX2jfBWQ3T8RSV6hGA.png" /></figure><h3>Architecture</h3><p>The overall structure of an R-CBM is very similar to the one of a CBM [1] (consisting in the composition of a concept encoder and a task predictor), however R-CBMs do not make a drastic distinction between concepts and tasks, and predicates can be more than unary, thus potentially ground on tuples of single entities. More specifically, given a relational language with entity and predicate symbols, an R-CBM first processes the atoms like <em>RightOf</em>(<em>car</em>1,<em>car</em>2), <em>Yield</em>(<em>car</em>2) by an encoder, and then map them by a task predictor (like a CBM). The final prediction like <em>Go</em>(<em>car</em>1) is computed by aggregating all the ones from separate groups of neighbour atoms, according to a given dependency graph (like a GNN). While the same task predictors of a CBM can be used for an R-CBM, the atom encoder in R-CBMs can be a bit more complex, as dealing with a relational language. For instance, common choices to embed entity, relations and atoms are given by Knowledge Graph Embeddings [5] or GNNs according to the symbolic or feature-based nature of the input data. We refer to the <a href="https://openreview.net/pdf?id=G99BSV9pt5">paper</a> for more technical details.</p><h3>Relational Deep Concept Reasoner (R-DCR)</h3><p>In most real scenarios, the rules regulating an environment are hard to codify. This can be due to multiple factors:</p><ul><li>the rules are not known a priori;</li><li>it can be very tedious for a human expert to list all the rules;</li><li>the rules could have a probabilistic nature, and the machine should learn the contexts where a rule applies.</li></ul><p>Among R-CBMs, R-DCR is a model allowing to learn the rules determining a decision starting from a weaker input information, in the form of a template determining the factors to consider like <em>RedLight</em>(<em>𝑥</em>),<em>Yield</em>(<em>𝑥</em>),<em>RightOf</em>(<em>𝑥</em>,<em>𝑦</em>). For instance, an R-DCR can be trained by observing the behavior of real drivers at a crossing, e.g. by using data from a camera. An R-DCR’s model processes the atoms in the template using an encoder, decides which subset of atoms are relevant for the final decision and how they should be accounted for. Finally, an R-DCR outputs a logic rule which can be executed to determine the final prediction, like whether which car can go. For example, it could use the template to output the rule:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/781/1*m4_dLBfZDleJma9aCQSSXw.png" /></figure><p>to express the fact that a car goes if it has no red light and has no other car at its right or the car at its right has a red light. A sketch of the overall computation of an R-DCR for this example is shown in Figure 3.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Xn4zUUbPG4vY-pnGdBGQjg.png" /><figcaption>Relational Concept Bottleneck Models on a driving test intersection. Image by the authors.</figcaption></figure><p><strong>If interested, please have a look at the full </strong><a href="https://openreview.net/pdf?id=G99BSV9pt5"><strong>paper</strong></a><strong> or look to our </strong><a href="https://neurips.cc/virtual/2024/poster/95923"><strong>poster</strong></a><strong> at NeurIPS 2024 conference.</strong></p><p>Please use the following citation if you want to refer to this work:</p><pre>@inproceedings{barbierorelational,<br>  title={Relational Concept Bottleneck Models},<br>  author={Barbiero, Pietro and Giannini, Francesco and Ciravegna, Gabriele and Diligenti, Michelangelo and Marra, Giuseppe},<br>  booktitle={The Thirty-eighth Annual Conference on Neural Information Processing Systems}<br>}</pre><h3>References:</h3><p>[1] Koh, P. W., Nguyen, T., Tang, Y. S., Mussmann, S., Pierson, E., Kim, B., &amp; Liang, P. (2020, November). Concept bottleneck models. In International conference on machine learning (pp. 5338–5348). PMLR. <br> [2] Gilmer, J., Schoenholz, S. S., Riley, P. F., Vinyals, O., &amp; Dahl, G. E. (2020). Message passing neural networks. Machine learning meets quantum physics, 199–214. <br> [3] Barbiero, P., Giannini, F., Ciravegna, G., Diligenti, M., &amp; Marra, G. Relational Concept Bottleneck Models. In The Thirty-eighth Annual Conference on Neural Information Processing Systems. <br> [4] Wang, Q., Mao, Z., Wang, B., &amp; Guo, L. (2017). Knowledge graph embedding: A survey of approaches and applications. IEEE transactions on knowledge and data engineering, 29(12), 2724–2743.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=83b05e75f3ac" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Interpretable Concept-Based Memory Reasoning (meet us at NeurIPS 2024)]]></title>
            <link>https://medium.com/@pyc.devteam/interpretable-concept-based-memory-reasoning-meet-us-at-neurips-2024-7a6ef9ff28ad?source=rss-bd88c5331b4e------2</link>
            <guid isPermaLink="false">https://medium.com/p/7a6ef9ff28ad</guid>
            <category><![CDATA[concept-based-learning]]></category>
            <category><![CDATA[explainable-ai]]></category>
            <category><![CDATA[interpretability]]></category>
            <category><![CDATA[deep-learning]]></category>
            <category><![CDATA[neurosymbolic-ai]]></category>
            <dc:creator><![CDATA[PyC Team]]></dc:creator>
            <pubDate>Tue, 03 Dec 2024 15:08:23 GMT</pubDate>
            <atom:updated>2024-12-05T11:31:59.875Z</atom:updated>
            <content:encoded><![CDATA[<h4>Accurate, interpretable, verifiable deep learning models</h4><h3>Problem Statement</h3><p>Imagine a smart home lighting system that intelligently manages the lights based on sensory data. This system needs to predict when to <em>flick</em> a light switch. The generally desired behavior (Figure 1) can be described using the following two rules that the system must learn:</p><ol><li>If there is <strong>a person and</strong> the switch is <strong>off</strong>, then <strong>flick</strong> the switch.</li><li>If there is <strong>no person and</strong> the switch is <strong>on</strong>, then <strong>flick</strong> the switch.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*BDjAE2gPxhhI1oVp" /><figcaption>Figure 1: Example scenarios demonstrating the desired behavior. In the left scenario, applying Rule 1 results in flicking the switch to turn the lights on. In the right scenario, applying Rule 2 results in flicking the switch to turn the lights off.</figcaption></figure><p>Additionally, the first rule should be <em>ignored</em> in some situations. For instance, during the day, the light should <em>never</em> be flicked on. The model should learn this behavior as well.</p><p>To build this system, you could rely on a deep neural network. It would likely perform perfectly, but its decision-making process would be a black box — accurate but not interpretable. Alternatively, you could use a concept-based model (CBM), which explains decisions using human-understandable concepts like “person” and “switch is on.” However, existing CBMs face a trade-off: they are either accurate or fully interpretable, but not both. This is because of three main issues:</p><ol><li><strong>Limited expressivity</strong>. Some approaches are interpretable but achieve this by limiting the model’s expressivity (e.g. Concept Bottleneck Model with a linear layer [1]), which costs accuracy in situations that require non-linear behavior. In our running example, the two rules together encode a relationship that is inherently non-linear. The system must combine the concepts of “person” and “switch is off” for Rule 1, and “no person” and “switch is on” for Rule 2, in a way that resembles an XOR operation: the light should only be flicked if exactly one of these conditions is true (“person” XOR “switch is on”). A linear model cannot capture this kind of non-linear dependency, leading to errors in decision-making.</li><li><strong>Information bottleneck</strong>. Many approaches rely <em>only on the concepts</em> for decision-making (e.g. all Concept Bottleneck Models [1]). This limitation reduces accuracy in scenarios where the concepts alone are insufficient to perfectly predict the task. In our running example, such models lack the possibility to incorporate additional context, like whether it is day or night, because the decision-making is constrained to the predefined concepts. As a result, a model that learned Rule 1 might incorrectly use it during the day to turn the light on, even though it should remain off.</li><li><strong>Local interpretability</strong>. For some approaches, only decisions on individual instances can be explained (i.e. <em>local</em>), but the model’s overall reasoning process remains opaque (e.g. Deep Concept Reasoner [2]). For instance, Deep Concept Reasoner predicts a decision rule over the concepts, which is then evaluated using the concepts to make a decision for the target task. In the context of our running example, this model might predict Rule 1 for a specific input. This provides interpretability for that single decision. However, because the model directly predicts the decision rule using a neural network, all possible rules are encoded implicitly in the model’s weights. This means you cannot explicitly know or verify the full set of decision rules the model might follow, making it impossible to interpret its reasoning globally across all scenarios.</li></ol><p>Our work addresses this gap, introducing Concept-Based Memory Reasoner [3], a neurosymbolic concept-based model that is both accurate and globally interpretable. It allows you to understand the system’s reasoning across all scenarios, even enabling formal verification of its rules. This means you can not only trust the system to make the right decisions but also verify and explain why those decisions are made. Figure 2 gives a comparison with the aforementioned models.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*J5taMYkhAOS6o3Wv" /><figcaption>Figure 2: Comparison of explanations for three interpretable models. The correct prediction is <strong>not FLICK</strong>, as it is day.</figcaption></figure><h3>Concept-Based Memory Reasoner</h3><p>Our proposed model, CMR, consists of the following key components:</p><ol><li><strong>Concept Predictor</strong>: Like other concept-based models, CMR uses a neural network to predict human-understandable concepts from the input.</li><li><strong>Memory of Rules</strong>: During training, CMR learns a set of rules that map concepts (like “person” and “switch is on”) to the target task (like “flick the switch”). These rules are stored in a memory, representing all possible reasoning paths the model might use.</li><li><strong>Rule Selector</strong>: Instead of applying all learned rules on the predicted concepts, CMR uses a neural network that acts as a selector. For any given input, this selector chooses a single rule from the memory to make the prediction. This ensures that only the most relevant rule is applied, providing explanations that are concise and task-specific. As the selector takes the image as input (and not just the concepts), it can exploit contextual information that is missing in the concepts (e.g. whether it is day).</li><li><strong>Symbolic Evaluation</strong>: CMR predicts the target task by evaluating the Rule Selector’s chosen rule <em>symbolically</em> using the predicted concepts.</li></ol><p>The result is a model with accuracy comparable to black-box neural networks (regardless of the employed concepts) while remaining globally interpretable. Every possible rule in the memory can be inspected and verified using formal tools, ensuring transparency and trustworthiness. At the same time, the rule selection mechanism ensures that explanations for individual predictions are concise and simple, as only one rule contributed to the final prediction. CMR bridges the gap between interpretability and accuracy, offering a powerful solution for applications that demand both (Figure 3).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/558/1*FPkJJwXvEdMSV4mh4UR3wA.png" /><figcaption>Figure 3: Comparison of CMR with multiple state-of-the-art deep learning models.</figcaption></figure><p><strong>If you’re interested in diving deeper into our work, you can read the full paper </strong><a href="https://arxiv.org/abs/2407.15527"><strong>here</strong></a><strong> or visit our </strong><a href="https://neurips.cc/virtual/2024/poster/94840"><strong>poster</strong></a><strong> at NeurIPS 2024! Our code can be found </strong><a href="https://github.com/daviddebot/CMR"><strong>here</strong></a><strong>.</strong></p><p>Please use the following citation if you want to refer to this work:</p><pre>@inproceedings{debot2024interpretable,<br>  title={Interpretable Concept-Based Memory Reasoning},<br>  author={Debot, David and Barbiero, Pietro and Giannini, Francesco and Ciravegna, Gabriele and Diligenti, Michelangelo and Marra, Giuseppe},<br>  booktitle={Thirty-eight Conference on Neural Information Processing Systems},<br>  year={2024}<br>}</pre><h3>References:</h3><p>[1] Koh, Pang Wei, et al. “Concept bottleneck models.” ICML, 2020.<br>[2] Barbiero, Pietro, et al. “Interpretable neural-symbolic concept reasoning.” ICML, 2023.<br>[3] Debot, David, et al. “Interpretable concept-based memory reasoning.” NeurIPS, 2024.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7a6ef9ff28ad" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>