@@ -85,143 +85,192 @@ clear from their name.
8585 )
8686
8787
88- Step 1: Import Libraries
89- -------------------------
88+ Detailed Guides
89+ ------------------------------
9090
91- .. code-block :: python
9291
93- import torch
94- import torch_concepts as pyc
92+ .. dropdown :: Concept Bottleneck Model
93+ :icon: package
9594
96- Step 2: Create Sample Data
97- ---------------------------
95+ **Import Libraries **
9896
99- Generate random inputs and targets for demonstration :
97+ To get started, import | pyc_logo | PyC and | pytorch_logo | PyTorch :
10098
101- .. code-block :: python
99+ .. code-block :: python
102100
103- batch_size = 32
104- input_dim = 64
105- n_concepts = 5
106- n_tasks = 3
101+ import torch
102+ import torch_concepts as pyc
107103
108- # Random input
109- x = torch.randn(batch_size, input_dim)
104+ **Create Sample Data **
110105
111- # Random concept labels (binary)
112- concept_labels = torch.randint(0 , 2 , (batch_size, n_concepts)).float()
106+ Generate random inputs and targets for demonstration:
113107
114- # Random task labels
115- task_labels = torch.randint(0 , n_tasks, (batch_size,))
108+ .. code-block :: python
116109
117- Step 3: Build a Concept Bottleneck Model
118- -----------------------------------------
110+ batch_size = 32
111+ input_dim = 64
112+ n_concepts = 5
113+ n_tasks = 3
119114
120- Use a ModuleDict to combine encoder and predictor:
115+ # Random input
116+ x = torch.randn(batch_size, input_dim)
121117
122- .. code-block :: python
118+ # Random concept labels (binary)
119+ concept_labels = torch.randint(0 , 2 , (batch_size, n_concepts)).float()
123120
124- # Create model using ModuleDict
125- model = torch.nn.ModuleDict({
126- ' encoder' : pyc.nn.LinearZC(
127- in_features = input_dim,
128- out_features = n_concepts
129- ),
130- ' predictor' : pyc.nn.LinearCC(
131- in_features_endogenous = n_concepts,
132- out_features = n_tasks
133- ),
134- })
121+ # Random task labels
122+ task_labels = torch.randint(0 , n_tasks, (batch_size,))
135123
136- Step 4: Forward Pass
137- ---------------------
124+ **Step 3: Build a Concept Bottleneck Model **
138125
139- Compute concept endogenous, then task predictions :
126+ Use a ModuleDict to combine encoder and predictor :
140127
141- .. code-block :: python
128+ .. code-block :: python
142129
143- # Get concept endogenous from input
144- concept_endogenous = model[' encoder' ](input = x)
130+ # Create model using ModuleDict
131+ model = torch.nn.ModuleDict({
132+ ' encoder' : pyc.nn.LinearZC(
133+ in_features = input_dim,
134+ out_features = n_concepts
135+ ),
136+ ' predictor' : pyc.nn.LinearCC(
137+ in_features_endogenous = n_concepts,
138+ out_features = n_tasks
139+ ),
140+ })
145141
146- # Get task predictions from concept endogenous
147- task_endogenous = model[' predictor' ](endogenous = concept_endogenous)
148142
149- print ( f " Concept endogenous shape: { concept_endogenous.shape } " ) # [32, 5]
150- print ( f " Task endogenous shape: { task_endogenous.shape } " ) # [32, 3]
143+ .. dropdown :: Inference and Training
144+ :icon: rocket
151145
152- Step 5: Compute Loss and Train
153- -------------------------------
146+ **Step 1: Inference **
154147
155- Train with both concept and task supervision:
148+ Once a concept bottleneck model is built, we can perform inference by first obtaining
149+ concept activations from the encoder, and then task predictions from the predictor:
156150
157- .. code-block :: python
151+ .. code-block :: python
158152
159- import torch.nn.functional as F
153+ # Get concept endogenous from input
154+ concept_endogenous = model[' encoder' ](input = x)
160155
161- # Compute losses
162- concept_loss = F.binary_cross_entropy(torch.sigmoid(concept_endogenous), concept_labels)
163- task_loss = F.cross_entropy(task_endogenous, task_labels)
164- total_loss = task_loss + 0.5 * concept_loss
156+ # Get task predictions from concept endogenous
157+ task_endogenous = model[' predictor' ](endogenous = concept_endogenous)
165158
166- # Backpropagation
167- total_loss.backward()
159+ print ( f " Concept endogenous shape: { concept_endogenous.shape } " ) # [32, 5]
160+ print ( f " Task endogenous shape: { task_endogenous.shape } " ) # [32, 3]
168161
169- print (f " Concept loss: { concept_loss.item():.4f } " )
170- print (f " Task loss: { task_loss.item():.4f } " )
162+ **Step 2: Compute Loss and Train **
171163
172- Step 6: Perform Interventions
173- ------------------------------
164+ Train with both concept and task supervision:
174165
175- Intervene using the ``intervention `` context manager which replaces the encoder layer temporarily.
176- The context manager takes two main arguments: **strategies ** and **policies **.
166+ .. code-block :: python
177167
178- - Intervention strategies define how the layer behaves during the intervention, e.g., setting concept endogenous to ground truth values.
179- - Intervention policies define the priority/order of concepts to intervene on.
168+ import torch.nn.functional as F
180169
181- .. code-block :: python
170+ # Compute losses
171+ concept_loss = F.binary_cross_entropy(torch.sigmoid(concept_endogenous), concept_labels)
172+ task_loss = F.cross_entropy(task_endogenous, task_labels)
173+ total_loss = task_loss + 0.5 * concept_loss
182174
183- from torch_concepts.nn import GroundTruthIntervention, UniformPolicy
184- from torch_concepts.nn import intervention
175+ # Backpropagation
176+ total_loss.backward()
185177
186- ground_truth = 10 * torch.rand_like(concept_endogenous)
187- strategy = GroundTruthIntervention(model = model[' encoder' ], ground_truth = ground_truth)
188- policy = UniformPolicy(out_features = n_concepts)
178+ print (f " Concept loss: { concept_loss.item():.4f } " )
179+ print (f " Task loss: { task_loss.item():.4f } " )
189180
190- # Apply intervention to encoder
191- with intervention(
192- policies = policy,
193- strategies = strategy,
194- target_concepts = [0 , 2 ]
195- ) as new_encoder_layer:
196- intervened_concepts = new_encoder_layer(input = x)
197- intervened_tasks = model[' predictor' ](endogenous = intervened_concepts)
198181
199- print (f " Original concept endogenous: { concept_endogenous[0 ]} " )
200- print (f " Original task predictions: { task_endogenous[0 ]} " )
201- print (f " Intervened concept endogenous: { intervened_concepts[0 ]} " )
202- print (f " Intervened task predictions: { intervened_tasks[0 ]} " )
182+ .. dropdown :: Interventions
183+ :icon: tools
203184
204- Using Special Layers
205- --------------------
185+ Intervene using the `` intervention `` context manager which replaces the encoder layer temporarily.
186+ The context manager takes two main arguments: ** strategies ** and ** policies **.
206187
207- Add a graph learner to discover concept relationships:
188+ - Intervention strategies define how the layer behaves during the intervention, e.g., setting concept endogenous to ground truth values.
189+ - Intervention policies define the priority/order of concepts to intervene on.
190+
191+ .. code-block :: python
192+
193+ from torch_concepts.nn import GroundTruthIntervention, UniformPolicy
194+ from torch_concepts.nn import intervention
195+
196+ ground_truth = 10 * torch.rand_like(concept_endogenous)
197+ strategy = GroundTruthIntervention(model = model[' encoder' ], ground_truth = ground_truth)
198+ policy = UniformPolicy(out_features = n_concepts)
199+
200+ # Apply intervention to encoder
201+ with intervention(
202+ policies = policy,
203+ strategies = strategy,
204+ target_concepts = [0 , 2 ]
205+ ) as new_encoder_layer:
206+ intervened_concepts = new_encoder_layer(input = x)
207+ intervened_tasks = model[' predictor' ](endogenous = intervened_concepts)
208+
209+ print (f " Original concept endogenous: { concept_endogenous[0 ]} " )
210+ print (f " Original task predictions: { task_endogenous[0 ]} " )
211+ print (f " Intervened concept endogenous: { intervened_concepts[0 ]} " )
212+ print (f " Intervened task predictions: { intervened_tasks[0 ]} " )
208213
209- .. code-block :: python
210214
211- # Define concept and task names
212- concept_names = [ ' round ' , ' smooth ' , ' bright ' , ' large ' , ' centered ' ]
215+ .. dropdown :: (Advanced) Graph Learning
216+ :icon: workflow
213217
214- # Create WANDA graph learner
215- graph_learner = pyc.nn.WANDAGraphLearner(
216- row_labels = concept_names,
217- col_labels = concept_names
218- )
218+ Add a graph learner to discover concept relationships:
219219
220- print (f " Learned graph shape: { graph_learner.weighted_adj} " )
220+ .. code-block :: python
221+
222+ # Define concept and task names
223+ concept_names = [' round' , ' smooth' , ' bright' , ' large' , ' centered' ]
224+
225+ # Create WANDA graph learner
226+ graph_learner = pyc.nn.WANDAGraphLearner(
227+ row_labels = concept_names,
228+ col_labels = concept_names
229+ )
230+
231+ print (f " Learned graph shape: { graph_learner.weighted_adj} " )
232+
233+
234+ The ``graph_learner.weighted_adj `` tensor contains a learnable adjacency matrix representing relationships
235+ between concepts.
236+
237+
238+ .. dropdown :: (Advanced) Verifiable Concept-Based Models
239+ :icon: shield-check
240+
241+ To design more complex concept-based models, you can combine multiple interpretable layers.
242+ For example, to build a verifiable concept-based model we can use an encoder to predict concept activations,
243+ a selector to select relevant exogenous information, and a hyper-network predictor to make final predictions
244+ based on both concept activations and exogenous information.
245+
246+ .. code-block :: python
247+
248+ from torch_concepts.nn import LinearZC, SelectorZU, HyperLinearCUC
249+
250+ memory_size = 7
251+ exogenous_size = 16
252+ embedding_size = 5
253+
254+ # Create model using ModuleDict
255+ model = torch.nn.ModuleDict({
256+ ' encoder' : LinearZC(
257+ in_features = input_dim,
258+ out_features = n_concepts
259+ ),
260+ ' selector' : SelectorZU(
261+ in_features = input_dim,
262+ memory_size = memory_size,
263+ exogenous_size = exogenous_size,
264+ out_features = n_tasks
265+ ),
266+ ' predictor' : HyperLinearCUC(
267+ in_features_endogenous = n_concepts,
268+ in_features_exogenous = exogenous_size,
269+ embedding_size = embedding_size,
270+ )
271+ })
221272
222273
223- The ``graph_learner.weighted_adj `` tensor contains a learnable adjacency matrix representing relationships
224- between concepts.
225274
226275 Next Steps
227276----------
@@ -230,4 +279,3 @@ Next Steps
230279- Try the :doc: `Mid-Level API </guides/using_mid_level_proba >` for probabilistic modeling
231280- Try the :doc: `Mid-Level API </guides/using_mid_level_causal >` for causal modeling
232281- Check out :doc: `example notebooks <https://github.com/pyc-team/pytorch_concepts/tree/master/examples >`
233-
0 commit comments