├── .gitattributes ├── selection ├── data │ ├── __init__.py │ └── utils.py ├── __init__.py ├── models │ ├── __init__.py │ ├── mlp.py │ ├── utils.py │ └── train.py └── layers │ ├── __init__.py │ ├── utils.py │ ├── concrete_selector.py │ ├── concrete_new.py │ ├── concrete_mask.py │ ├── concrete_max.py │ └── concrete_gates.py ├── .gitignore ├── LICENSE ├── setup.py ├── README.md └── mnist selection.ipynb /.gitattributes: -------------------------------------------------------------------------------- 1 | *.ipynb linguist-language=Python 2 | -------------------------------------------------------------------------------- /selection/data/__init__.py: -------------------------------------------------------------------------------- 1 | from .utils import TabularDataset 2 | -------------------------------------------------------------------------------- /selection/__init__.py: -------------------------------------------------------------------------------- 1 | from . import models 2 | from . import layers 3 | from . import data 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.pyc 3 | .DS_Store 4 | .ipynb_checkpoints 5 | .eggs 6 | build 7 | dist 8 | dl_selection.egg-info 9 | MNIST -------------------------------------------------------------------------------- /selection/models/__init__.py: -------------------------------------------------------------------------------- 1 | from .mlp import MLP 2 | from .mlp import SelectorMLP 3 | from . import train 4 | from . import utils 5 | -------------------------------------------------------------------------------- /selection/layers/__init__.py: -------------------------------------------------------------------------------- 1 | from .concrete_mask import ConcreteMask 2 | from .concrete_selector import ConcreteSelector 3 | from .concrete_gates import ConcreteGates 4 | from .concrete_max import ConcreteMax 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Ian Covert 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | setuptools.setup( 4 | name="dl-selection", 5 | version="0.0.3", 6 | author="Ian Covert", 7 | author_email="icovert@cs.washington.edu", 8 | description="Feature selection for deep learning models.", 9 | long_description=""" 10 | The **dl-selection** package contains tools for performing feature 11 | selection with deep learning models. It supports several input layers 12 | for selecting features, each of which relies on a stochastic relaxation 13 | of the feature selection problem. See the 14 | [GitHub page](https://github.com/icc2115/dl-selection/) for more 15 | details. 16 | """, 17 | long_description_content_type="text/markdown", 18 | url="https://github.com/icc2115/dl-selection/", 19 | packages=setuptools.find_packages(), 20 | install_requires=[ 21 | 'numpy', 22 | 'torch' 23 | ], 24 | classifiers=[ 25 | "Programming Language :: Python :: 3", 26 | "License :: OSI Approved :: MIT License", 27 | "Operating System :: OS Independent", 28 | "Intended Audience :: Science/Research", 29 | "Topic :: Scientific/Engineering" 30 | ], 31 | python_requires='>=3.6', 32 | ) 33 | -------------------------------------------------------------------------------- /selection/layers/utils.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn.functional as F 3 | 4 | 5 | def clamp_probs(probs): 6 | eps = torch.finfo(probs.dtype).eps 7 | return torch.clamp(probs, min=eps, max=1-eps) 8 | 9 | def concrete_sample(logits, temperature, shape=torch.Size([])): 10 | ''' 11 | Sampling for Concrete distribution. 12 | 13 | See Eq. 10 of Maddison et al., 2017. 14 | ''' 15 | uniform_shape = torch.Size(shape) + logits.shape 16 | u = clamp_probs(torch.rand(uniform_shape, dtype=torch.float32, 17 | device=logits.device)) 18 | gumbels = - torch.log(- torch.log(u)) 19 | scores = (logits + gumbels) / temperature 20 | return scores.softmax(dim=-1) 21 | 22 | def bernoulli_concrete_sample(logits, temperature, shape=torch.Size([])): 23 | ''' 24 | Sampling for BinConcrete distribution. 25 | 26 | See PyTorch source code, differs from Eq. 16 of Maddison et al., 2017. 27 | ''' 28 | uniform_shape = torch.Size(shape) + logits.shape 29 | u = clamp_probs(torch.rand(uniform_shape, dtype=torch.float32, 30 | device=logits.device)) 31 | return torch.sigmoid((F.logsigmoid(logits) - F.logsigmoid(-logits) 32 | + torch.log(u) - torch.log(1 - u)) / temperature) 33 | -------------------------------------------------------------------------------- /selection/layers/concrete_selector.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | from selection.layers import utils 4 | 5 | 6 | class ConcreteSelector(nn.Module): 7 | ''' 8 | Input layer that selects features by learning a binary matrix, based on [2]. 9 | 10 | [2] Concrete Autoencoders for Differentiable Feature Selection and 11 | Reconstruction (Balin et al., 2019) 12 | 13 | Args: 14 | input_size: number of inputs. 15 | k: number of features to be selected. 16 | temperature: temperature for Concrete samples. 17 | ''' 18 | def __init__(self, input_size, k, temperature=10.0): 19 | super().__init__() 20 | self.logits = nn.Parameter( 21 | torch.zeros(k, input_size, dtype=torch.float32, requires_grad=True)) 22 | self.input_size = input_size 23 | self.k = k 24 | self.output_size = k 25 | self.temperature = temperature 26 | 27 | @property 28 | def probs(self): 29 | return self.logits.softmax(dim=1) 30 | 31 | def forward(self, x, n_samples=None, **kwargs): 32 | # Sample selection matrix. 33 | n = n_samples if n_samples else 1 34 | M = self.sample(sample_shape=(n, len(x))) 35 | 36 | # Apply selection matrix. 37 | x = torch.matmul(x.unsqueeze(1), M.permute(0, 1, 3, 2)).squeeze(2) 38 | 39 | # Post processing. 40 | if not n_samples: 41 | x = x.squeeze(0) 42 | 43 | return x 44 | 45 | def sample(self, n_samples=None, sample_shape=None): 46 | '''Sample approximate binary matrices.''' 47 | if n_samples: 48 | sample_shape = torch.Size([n_samples]) 49 | return utils.concrete_sample(self.logits, self.temperature, 50 | sample_shape) 51 | 52 | def get_inds(self, **kwargs): 53 | inds = torch.argmax(self.logits, dim=1) 54 | return torch.sort(inds)[0].cpu().data.numpy() 55 | 56 | def extra_repr(self): 57 | return 'input_size={}, temperature={}, k={}'.format( 58 | self.input_size, self.temperature, self.k) 59 | -------------------------------------------------------------------------------- /selection/layers/concrete_new.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | from selection.layers import utils 4 | 5 | 6 | class ConcreteNew(nn.Module): 7 | ''' 8 | Input layer that selects features by learning a k-hot vector. 9 | 10 | Args: 11 | input_size: number of inputs. 12 | k: number of features to be selected. 13 | temperature: temperature for Concrete samples. 14 | ''' 15 | def __init__(self, input_size, k, temperature=10.0, append=False): 16 | super().__init__() 17 | self.logits = nn.Parameter( 18 | torch.randn(k, input_size, dtype=torch.float32, requires_grad=True)) 19 | self.input_size = input_size 20 | self.k = k 21 | self.output_size = k 22 | self.temperature = temperature 23 | self.append = append 24 | 25 | @property 26 | def probs(self): 27 | probs = torch.softmax(self.logits / self.temperature, dim=1) 28 | return torch.clamp(torch.sum(probs, dim=0), max=1.0) 29 | 30 | def forward(self, x, n_samples=None, return_mask=False): 31 | # Sample mask. 32 | n = n_samples if n_samples else 1 33 | m = self.sample(sample_shape=(n, len(x))) 34 | 35 | # Apply mask. 36 | x = x * m 37 | 38 | # Post processing. 39 | if self.append: 40 | x = torch.cat((x, m), dim=-1) 41 | 42 | if not n_samples: 43 | x = x.squeeze(0) 44 | m = m.squeeze(0) 45 | 46 | if return_mask: 47 | return x, m 48 | else: 49 | return x 50 | 51 | def sample(self, n_samples=None, sample_shape=None): 52 | '''Sample approximate binary masks.''' 53 | if n_samples: 54 | sample_shape = torch.Size([n_samples]) 55 | return utils.concrete_bernoulli_sample(self.probs, self.temperature, 56 | sample_shape) 57 | 58 | def get_inds(self, **kwargs): 59 | return torch.argsort(self.probs)[-self.k:].cpu().data.numpy() 60 | 61 | def extra_repr(self): 62 | return 'input_size={}, temperature={}, k={}'.format( 63 | self.input_size, self.temperature, self.k) 64 | -------------------------------------------------------------------------------- /selection/data/utils.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import numpy as np 3 | from torch.utils.data import Dataset 4 | 5 | 6 | class TabularDataset(Dataset): 7 | ''' 8 | Dataset capable of using subset of inputs and outputs. 9 | 10 | Args: 11 | data: inputs (np.ndarray or torch.Tensor). 12 | targets: outputs (np.ndarray or torch.Tensor) 13 | ''' 14 | def __init__(self, 15 | data, 16 | targets): 17 | self.input_size = data.shape[1] 18 | if isinstance(data, np.ndarray): 19 | # Conversions for numpy. 20 | self.data = data.astype(np.float32) 21 | if len(targets.shape) == 1: 22 | self.output_size = len(np.unique(targets)) 23 | self.targets = targets.astype(np.long) 24 | else: 25 | self.output_size = targets.shape[1] 26 | self.targets = targets.astype(np.float32) 27 | elif isinstance(data, torch.Tensor): 28 | # Conversions for PyTorch. 29 | self.data = data.float() 30 | if len(targets.shape) == 1: 31 | self.output_size = len(torch.unique(targets)) 32 | self.targets = targets.long() 33 | else: 34 | self.output_size = targets.shape[1] 35 | self.targets = targets.float() 36 | self.set_inds(None) 37 | self.set_output_inds(None) 38 | 39 | def set_inds(self, inds=None): 40 | '''Set input indices to be returned.''' 41 | data = self.data 42 | if inds is not None: 43 | inds = np.array([i in inds for i in range(self.input_size)]) 44 | data = data[:, inds] 45 | self.input = data 46 | 47 | def set_output_inds(self, inds=None): 48 | '''Set output indices to be returned.''' 49 | output = self.targets 50 | if inds is not None: 51 | assert len(output.shape) == 2, 'only for multitask regression tasks' 52 | inds = np.array([i in inds for i in range(self.output_size)]) 53 | output = output[:, inds] 54 | self.output = output 55 | 56 | def __len__(self): 57 | return len(self.data) 58 | 59 | def __getitem__(self, index): 60 | return self.input[index], self.output[index] 61 | -------------------------------------------------------------------------------- /selection/layers/concrete_mask.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | from selection.layers import utils 4 | 5 | 6 | # Implicit temperature for the link function to accelerate optimization. 7 | implicit_temp = 1 / 3.0 8 | 9 | 10 | class ConcreteMask(nn.Module): 11 | ''' 12 | Input layer that selects features by learning a k-hot mask. 13 | 14 | Args: 15 | input_size: number of inputs. 16 | k: number of features to be selected. 17 | temperature: temperature for Concrete samples. 18 | append: whether to append the mask to the input on forward pass. 19 | ''' 20 | def __init__(self, input_size, k, temperature=10.0, append=False): 21 | super().__init__() 22 | self.logits = nn.Parameter( 23 | torch.zeros(k, input_size, dtype=torch.float32, requires_grad=True)) 24 | self.input_size = input_size 25 | self.k = k 26 | self.output_size = 2 * input_size if append else input_size 27 | self.temperature = temperature 28 | self.append = append 29 | 30 | @property 31 | def probs(self): 32 | return (self.logits / implicit_temp).softmax(dim=1) 33 | 34 | def forward(self, x, n_samples=None, return_mask=False): 35 | # Sample mask. 36 | n = n_samples if n_samples else 1 37 | m = self.sample(sample_shape=(n, len(x))) 38 | 39 | # Apply mask. 40 | x = x * m 41 | 42 | # Post processing. 43 | if self.append: 44 | x = torch.cat((x, m), dim=-1) 45 | 46 | if not n_samples: 47 | x = x.squeeze(0) 48 | m = m.squeeze(0) 49 | 50 | if return_mask: 51 | return x, m 52 | else: 53 | return x 54 | 55 | def sample(self, n_samples=None, sample_shape=None): 56 | '''Sample approximate k-hot vectors.''' 57 | if n_samples: 58 | sample_shape = torch.Size([n_samples]) 59 | elif not sample_shape: 60 | raise ValueError('n_samples or sample_shape must be specified') 61 | samples = utils.concrete_sample(self.logits / implicit_temp, 62 | self.temperature, sample_shape) 63 | return torch.max(samples, dim=-2).values 64 | 65 | def get_inds(self, **kwargs): 66 | inds = torch.argmax(self.logits, dim=1) 67 | return torch.sort(inds)[0].cpu().data.numpy() 68 | 69 | def extra_repr(self): 70 | return 'input_size={}, temperature={}, k={}, append={}'.format( 71 | self.input_size, self.temperature, self.k, self.append) 72 | -------------------------------------------------------------------------------- /selection/layers/concrete_max.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | from selection.layers import utils 4 | 5 | 6 | # Implicit temperature for the link function to accelerate optimization. 7 | implicit_temp = 1 / 5.0 8 | 9 | 10 | class ConcreteMax(nn.Module): 11 | ''' 12 | Input layer that selects features by learning probabilities for independent 13 | sampling from a Concrete variable, based on [3]. 14 | 15 | [3] Learning to Explain: An Information Theoretic Perspective on Model 16 | Interpretation (Chen et al., 2018) 17 | 18 | Args: 19 | input_size: number of inputs. 20 | k: number of features to be selected. 21 | temperature: temperature for Concrete samples. 22 | append: whether to append the mask to the input on forward pass. 23 | ''' 24 | def __init__(self, input_size, k, temperature=10.0, append=False): 25 | super().__init__() 26 | self.logits = nn.Parameter( 27 | torch.randn(1, input_size, dtype=torch.float32, requires_grad=True)) 28 | self.input_size = input_size 29 | self.k = k 30 | self.output_size = 2 * input_size if append else input_size 31 | self.temperature = temperature 32 | self.append = append 33 | 34 | @property 35 | def probs(self): 36 | return (self.logits / implicit_temp).softmax(dim=1)[0] 37 | 38 | def forward(self, x, n_samples=None, return_mask=False): 39 | # Sample mask. 40 | n = n_samples if n_samples else 1 41 | m = self.sample(sample_shape=(n, len(x))) 42 | 43 | # Apply mask. 44 | x = x * m 45 | 46 | # Post processing. 47 | if self.append: 48 | x = torch.cat((x, m), dim=-1) 49 | 50 | if not n_samples: 51 | x = x.squeeze(0) 52 | m = m.squeeze(0) 53 | 54 | if return_mask: 55 | return x, m 56 | else: 57 | return x 58 | 59 | def sample(self, n_samples=None, sample_shape=None): 60 | '''Sample approximate k-hot vectors.''' 61 | if n_samples: 62 | sample_shape = torch.Size([n_samples]) 63 | elif not sample_shape: 64 | raise ValueError('n_samples or sample_shape must be specified') 65 | samples = utils.concrete_sample(self.logits.repeat(self.k, 1) / implicit_temp, 66 | self.temperature, sample_shape) 67 | return torch.max(samples, dim=-2).values 68 | 69 | def get_inds(self, **kwargs): 70 | inds = torch.argsort(self.logits[0])[-self.k:] 71 | return torch.sort(inds)[0].cpu().data.numpy() 72 | 73 | def extra_repr(self): 74 | return 'input_size={}, temperature={}, k={}, append={}'.format( 75 | self.input_size, self.temperature, self.k, self.append) 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deep learning feature selection 2 | 3 | The **dl-selection** repository contains tools for performing feature selection with deep learning models. It currently has four mechanisms for selecting features, each of which relies on a stochastic relaxation of the feature selection problem. Each mechanism is a learnable input layer that determines which features to select throughout the course of training. 4 | 5 | **1. Concrete Mask:** selects a user-specified number of features `k` by learning a `k`-hot vector `m` for element-wise multiplication with the input `x`. The layer is composed with a separate network that learns to make predictions using the masked input `x * m`. 6 | 7 | **2. Concrete Selector:** selects a user-specified number of features `k` by learning a binary matrix `M` that selects features from `x`. The layer is composed with a separate network that learns to make predictions using the selected features `Mx`. 8 | 9 | **3. Concrete Gates:** selects features subject to a L0 penalty by learning binary gates `m1, m2, ...` for each feature. The layer is composed with a separate network that learns to make predictions using the masked input `x * m`. 10 | 11 | **4. Concrete Max:** selects a user-specified number of features `k` by learning a Categorical distribution over `(1, 2, ..., d)` from which features are sampled. The most probable features are selected after training. 12 | 13 | ## Usage 14 | 15 | The module `selection.models` implements a class `SelectorMLP` for automatically creating a model that composes the user-specified input layer with a prediction network. The model has a built-in `train` method, so it can be used like this: 16 | 17 | ```python 18 | import torch.nn as nn 19 | from selection import models 20 | 21 | # Load data 22 | train_dataset, val_dataset = ... 23 | input_size, output_size = ... 24 | 25 | # Set up model 26 | model = models.SelectorMLP( 27 | input_layer='concrete_mask', 28 | k=20, 29 | input_size=input_size, 30 | output_size=output_size, 31 | hidden=[512, 512], 32 | activation='elu') 33 | 34 | # Train model 35 | model.learn( 36 | train_dataset, 37 | val_dataset, 38 | lr=1e-3, 39 | mbsize=64, 40 | max_nepochs=300, 41 | start_temperature=10.0, 42 | end_temperature=0.01, 43 | loss_fn=nn.CrossEntropyLoss()) 44 | 45 | # Extract selected indices 46 | inds = model.get_inds() 47 | ``` 48 | 49 | Check out the [mnist selection.ipynb](https://github.com/icc2115/dl-selection/blob/master/mnist%20selection.ipynb) notebook for examples of how to use each of the layers. 50 | 51 | ## Installation 52 | 53 | The easiest way to install this package is with pip: 54 | 55 | ``` 56 | pip install dl-selection 57 | ``` 58 | 59 | Or, you can clone the repository to get the most recent version of the code. 60 | 61 | ## Authors 62 | 63 | - Ian Covert () 64 | - Uygar Sümbül 65 | - Su-In Lee 66 | 67 | -------------------------------------------------------------------------------- /selection/layers/concrete_gates.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | from selection.layers import utils 4 | 5 | 6 | # Implicit temperature for the link function to accelerate optimization. 7 | implicit_temp = 1 / 2.0 8 | 9 | 10 | class ConcreteGates(nn.Module): 11 | ''' 12 | Input layer that selects features by learning binary gates for each feature, 13 | based on [1]. 14 | 15 | [1] Dropout Feature Ranking for Deep Learning Models (Chang et al., 2017) 16 | 17 | Args: 18 | input_size: number of inputs. 19 | k: number of features to be selected. 20 | temperature: temperature for Concrete samples. 21 | init: initial value for each gate's probability of being 1. 22 | append: whether to append the mask to the input on forward pass. 23 | ''' 24 | def __init__(self, input_size, temperature=1.0, init=0.99, append=False): 25 | super().__init__() 26 | init_logit = - torch.log(1 / torch.tensor(init) - 1) * implicit_temp 27 | self.logits = nn.Parameter(torch.full( 28 | (input_size,), init_logit, dtype=torch.float32, requires_grad=True)) 29 | self.input_size = input_size 30 | self.output_size = 2 * input_size if append else input_size 31 | self.temperature = temperature 32 | self.append = append 33 | 34 | @property 35 | def probs(self): 36 | return torch.sigmoid(self.logits / implicit_temp) 37 | 38 | def forward(self, x, n_samples=None, return_mask=False): 39 | # Sample mask. 40 | n = n_samples if n_samples else 1 41 | m = self.sample(sample_shape=(n, len(x))) 42 | 43 | # Apply mask. 44 | x = x * m 45 | 46 | # Post processing. 47 | if self.append: 48 | x = torch.cat((x, m), dim=-1) 49 | 50 | if not n_samples: 51 | x = x.squeeze(0) 52 | m = m.squeeze(0) 53 | 54 | if return_mask: 55 | return x, m 56 | else: 57 | return x 58 | 59 | def sample(self, n_samples=None, sample_shape=None): 60 | '''Sample approximate binary masks.''' 61 | if n_samples: 62 | sample_shape = torch.Size([n_samples]) 63 | return utils.bernoulli_concrete_sample(self.logits / implicit_temp, 64 | self.temperature, sample_shape) 65 | 66 | def get_inds(self, num_features=None, threshold=None, **kwargs): 67 | if num_features: 68 | inds = torch.argsort(self.probs)[-num_features:] 69 | elif threshold: 70 | inds = (self.probs > threshold).nonzero()[:, 0] 71 | else: 72 | raise ValueError('num_features or threshold must be specified') 73 | return torch.sort(inds)[0].cpu().data.numpy() 74 | 75 | def extra_repr(self): 76 | return 'input_size={}, temperature={}, append={}'.format( 77 | self.input_size, self.temperature, self.append) 78 | -------------------------------------------------------------------------------- /selection/models/mlp.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | import selection.layers as layers 3 | from selection.models import utils 4 | from selection.models import train 5 | from torch.utils.data import DataLoader 6 | 7 | 8 | class MLP(nn.Module): 9 | ''' 10 | Multilayer perceptron (MLP) model. 11 | 12 | Args: 13 | input_size: number of inputs. 14 | output_size: number of outputs. 15 | hidden: list of hidden layer widths. 16 | activation: nonlinearity between hidden layers. 17 | output_activation: nonlinearity at output. 18 | ''' 19 | def __init__(self, 20 | input_size, 21 | output_size, 22 | hidden, 23 | activation, 24 | output_activation=None, 25 | batch_norm=False): 26 | super().__init__() 27 | 28 | # Fully connected layers. 29 | self.input_size = input_size 30 | self.output_size = output_size 31 | fc_layers = [nn.Linear(d_in, d_out) for d_in, d_out in 32 | zip([input_size] + hidden, hidden + [output_size])] 33 | self.fc = nn.ModuleList(fc_layers) 34 | 35 | # Activation functions. 36 | self.activation = utils.get_activation(activation) 37 | self.output_activation = utils.get_activation(output_activation) 38 | 39 | # Set up batch norm. 40 | if batch_norm: 41 | layer_normalizers = [nn.BatchNorm1d(d) for d in hidden] 42 | else: 43 | layer_normalizers = [nn.Identity() for d in hidden] 44 | self.layer_normalizers = nn.ModuleList(layer_normalizers) 45 | 46 | # Set up training. 47 | self.learn = train.Training(self) 48 | 49 | def forward(self, x): 50 | for fc, norm in zip(self.fc, self.layer_normalizers): 51 | x = fc(x) 52 | x = self.activation(x) 53 | x = norm(x) 54 | 55 | return self.output_activation(self.fc[-1](x)) 56 | 57 | def evaluate(self, dataset, loss_fn, mbsize=None): 58 | training = self.training 59 | self.eval() 60 | mbsize = mbsize if mbsize else len(dataset) 61 | loader = DataLoader(dataset, batch_size=mbsize) 62 | loss = utils.validate(self, loader, loss_fn) 63 | if training: 64 | self.train() 65 | return loss 66 | 67 | def extra_repr(self): 68 | return 'hidden={}'.format([fc.in_features for fc in self.fc[1:]]) 69 | 70 | 71 | class SelectorMLP(nn.Module): 72 | '''MLP with input layer selection. 73 | 74 | Args: 75 | input_layer: input layer type (e.g., 'concrete_gates'). 76 | input_size: number of inputs. 77 | output_size: number of outputs. 78 | hidden: list of hidden layer widths. 79 | activation: nonlinearity between hidden layers. 80 | output_activation: nonlinearity at output. 81 | kwargs: additional arguments (e.g., k, init, append). Some are optional, 82 | but k is required for ConcreteMask and ConcreteGates. 83 | ''' 84 | def __init__(self, 85 | input_layer, 86 | input_size, 87 | output_size, 88 | hidden, 89 | activation, 90 | output_activation=None, 91 | batch_norm=False, 92 | **kwargs): 93 | super().__init__() 94 | 95 | # Set up input layer. 96 | if input_layer == 'concrete_mask': 97 | k = kwargs.get('k') 98 | append = kwargs.get('append', True) 99 | kwargs['append'] = append 100 | mlp_input_size = 2 * input_size if append else input_size 101 | self.input_layer = layers.ConcreteMask(input_size, **kwargs) 102 | elif input_layer == 'concrete_selector': 103 | k = kwargs.get('k') 104 | mlp_input_size = k 105 | self.input_layer = layers.ConcreteSelector(input_size, **kwargs) 106 | elif input_layer == 'concrete_gates': 107 | append = kwargs.get('append', True) 108 | kwargs['append'] = append 109 | mlp_input_size = 2 * input_size if append else input_size 110 | self.input_layer = layers.ConcreteGates(input_size, **kwargs) 111 | elif input_layer == 'concrete_max': 112 | append = kwargs.get('append', True) 113 | kwargs['append'] = append 114 | mlp_input_size = 2 * input_size if append else input_size 115 | self.input_layer = layers.ConcreteMax(input_size, **kwargs) 116 | else: 117 | raise ValueError('unsupported input layer: {}'.format(input_layer)) 118 | 119 | # Set up MLP. 120 | self.mlp = MLP(mlp_input_size, output_size, hidden, activation, 121 | output_activation, batch_norm) 122 | 123 | # Set up training. 124 | self.learn = train.AnnealedTemperatureTraining(self) 125 | 126 | def forward(self, x, **kwargs): 127 | return_mask = kwargs.get('return_mask', False) 128 | if return_mask: 129 | assert ( 130 | isinstance(self.input_layer, layers.ConcreteMask) or 131 | isinstance(self.input_layer, layers.ConcreteGates)) 132 | x, m = self.input_layer(x, **kwargs) 133 | return self.mlp(x), m 134 | else: 135 | return self.mlp(self.input_layer(x, **kwargs)) 136 | 137 | def evaluate(self, dataset, loss_fn, mbsize=None, **kwargs): 138 | training = self.training 139 | self.eval() 140 | mbsize = mbsize if mbsize else len(dataset) 141 | loader = DataLoader(dataset, batch_size=mbsize) 142 | loss = utils.validate_input_layer(self, loader, loss_fn, **kwargs) 143 | if training: 144 | self.train() 145 | return loss 146 | 147 | def get_inds(self, **kwargs): 148 | return self.input_layer.get_inds(**kwargs) 149 | -------------------------------------------------------------------------------- /selection/models/utils.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torch.optim as optim 4 | import selection.layers as layers 5 | 6 | 7 | class MSELoss(nn.Module): 8 | '''MSE loss that sums over output dimensions and allows weights.''' 9 | def __init__(self, reduction='mean'): 10 | super().__init__() 11 | assert reduction in ('none', 'mean') 12 | self.reduction = reduction 13 | 14 | def forward(self, pred, target, weights=None): 15 | if weights is not None: 16 | loss = torch.sum(weights * ((pred - target) ** 2), dim=-1) 17 | else: 18 | loss = torch.sum((pred - target) ** 2, dim=-1) 19 | if self.reduction == 'mean': 20 | return torch.mean(loss) 21 | else: 22 | return loss 23 | 24 | 25 | class Accuracy(nn.Module): 26 | '''0-1 loss.''' 27 | def __init__(self): 28 | super().__init__() 29 | 30 | def forward(self, pred, target): 31 | return (torch.argmax(pred, dim=1) == target).float().mean() 32 | 33 | 34 | def get_activation(activation): 35 | '''Get activation function.''' 36 | if activation == 'sigmoid': 37 | return nn.Sigmoid() 38 | elif activation == 'tanh': 39 | return nn.Tanh() 40 | elif activation == 'relu': 41 | return nn.ReLU() 42 | elif activation == 'elu': 43 | return nn.ELU() 44 | elif activation is None: 45 | return nn.Identity() 46 | else: 47 | raise ValueError('unsupported activation: {}'.format(activation)) 48 | 49 | 50 | def get_optimizer(optimizer, params, lr): 51 | '''Get optimizer.''' 52 | if optimizer == 'SGD': 53 | return optim.SGD(params, lr=lr) 54 | elif optimizer == 'Momentum': 55 | return optim.SGD(params, lr=lr, momentum=0.9, nesterov=True) 56 | elif optimizer == 'Adam': 57 | return optim.Adam(params, lr=lr) 58 | elif optimizer == 'Adagrad': 59 | return optim.Adagrad(params, lr=lr) 60 | elif optimizer == 'RMSprop': 61 | return optim.RMSprop(params, lr=lr) 62 | else: 63 | raise ValueError('unsupported optimizer: {}'.format(optimizer)) 64 | 65 | 66 | def validate(model, loader, loss_fn): 67 | '''Calculate average loss.''' 68 | device = next(model.parameters()).device 69 | mean_loss = 0 70 | N = 0 71 | with torch.no_grad(): 72 | for x, y in loader: 73 | # Move to GPU. 74 | x = x.to(device=device) 75 | y = y.to(device=device) 76 | n = len(x) 77 | 78 | # Calculate loss. 79 | loss = loss_fn(model(x), y) 80 | mean_loss = (N * mean_loss + n * loss) / (N + n) 81 | N += n 82 | 83 | return mean_loss 84 | 85 | 86 | def validate_input_layer(model, loader, loss_fn, n_samples=None, 87 | mask_output=False): 88 | '''Calculate average loss.''' 89 | device = next(model.parameters()).device 90 | mean_loss = 0 91 | N = 0 92 | with torch.no_grad(): 93 | for x, y in loader: 94 | # Move to GPU. 95 | x = x.to(device=device) 96 | y = y.to(device=device) 97 | n = len(x) 98 | 99 | # Forward pass. 100 | if mask_output: 101 | pred, m = model(x, n_samples=n_samples, return_mask=True) 102 | else: 103 | pred = model(x, n_samples=n_samples) 104 | 105 | # Calculate loss. 106 | if mask_output: 107 | loss = loss_fn(pred, y, weights=1-m) 108 | else: 109 | loss = loss_fn(pred, y) 110 | mean_loss = (N * mean_loss + n * loss) / (N + n) 111 | N += n 112 | 113 | return mean_loss 114 | 115 | 116 | def input_layer_converged(input_layer, tol=1e-3, n_samples=None): 117 | '''Determine whether the input layer has converged.''' 118 | with torch.no_grad(): 119 | if isinstance(input_layer, layers.ConcreteMask): 120 | m = input_layer.sample(n_samples=n_samples) 121 | mean = torch.mean(m, dim=0) 122 | return torch.sort(mean).values[-input_layer.k] > 1 - tol 123 | 124 | elif isinstance(input_layer, layers.ConcreteSelector): 125 | M = input_layer.sample(n_samples=n_samples) 126 | mean = torch.mean(M, dim=0) 127 | return torch.min(torch.max(mean, dim=1).values) > 1 - tol 128 | 129 | elif isinstance(input_layer, layers.ConcreteGates): 130 | m = input_layer.sample(n_samples=n_samples) 131 | mean = torch.mean(m, dim=0) 132 | return torch.max(torch.min(mean, 1 - mean)) < tol 133 | 134 | elif isinstance(input_layer, layers.ConcreteMax): 135 | return False 136 | 137 | 138 | def input_layer_fix(input_layer): 139 | '''Fix collisions in the input layer.''' 140 | needs_reset = ( 141 | isinstance(input_layer, layers.ConcreteMask) or 142 | isinstance(input_layer, layers.ConcreteSelector)) 143 | if needs_reset: 144 | # Extract logits. 145 | logits = input_layer.logits 146 | argmax = torch.argmax(logits, dim=1).cpu().data.numpy() 147 | 148 | # Locate collisions and reinitialize. 149 | for i in range(len(argmax) - 1): 150 | if argmax[i] in argmax[i+1:]: 151 | logits.data[i] = torch.randn( 152 | logits[i].shape, dtype=logits.dtype, device=logits.device) 153 | 154 | 155 | def input_layer_penalty(input_layer, m): 156 | '''Calculate the regularization term for the input layer.''' 157 | assert isinstance(input_layer, layers.ConcreteGates) 158 | return torch.mean(torch.sum(m, dim=-1)) 159 | 160 | 161 | def input_layer_summary(input_layer, n_samples=None): 162 | '''Provide a short summary of the input layer's convergence.''' 163 | with torch.no_grad(): 164 | if isinstance(input_layer, layers.ConcreteMask): 165 | m = input_layer.sample(n_samples=n_samples) 166 | mean = torch.mean(m, dim=0) 167 | relevant = torch.sort(mean, descending=True).values[:input_layer.k] 168 | return 'Max = {:.2f}, Mean = {:.2f}, Min = {:.2f}'.format( 169 | relevant[0].item(), torch.mean(relevant).item(), 170 | relevant[-1].item()) 171 | 172 | elif isinstance(input_layer, layers.ConcreteSelector): 173 | M = input_layer.sample(n_samples=n_samples) 174 | mean = torch.mean(M, dim=0) 175 | relevant = torch.max(mean, dim=1).values 176 | return 'Max = {:.2f}, Mean = {:.2f}, Min = {:.2f}'.format( 177 | torch.max(relevant).item(), torch.mean(relevant).item(), 178 | torch.min(relevant).item()) 179 | 180 | elif isinstance(input_layer, layers.ConcreteGates): 181 | m = input_layer.sample(n_samples=n_samples) 182 | mean = torch.mean(m, dim=0) 183 | dist = torch.min(mean, 1 - mean) 184 | return 'Mean dist = {:.2f}, Max dist = {:.2f}, Num sel = {}'.format( 185 | torch.mean(dist).item(), 186 | torch.max(dist).item(), 187 | int(torch.sum((mean > 0.5).float()).item())) 188 | 189 | elif isinstance(input_layer, layers.ConcreteMax): 190 | m = input_layer.sample(n_samples=n_samples) 191 | mean = torch.mean(m, dim=0) 192 | relevant = torch.sort(mean, descending=True).values[:input_layer.k] 193 | return 'Max = {:.2f}, Mean = {:.2f}, Min = {:.2f}'.format( 194 | relevant[0].item(), torch.mean(relevant).item(), 195 | relevant[-1].item()) 196 | 197 | 198 | def restore_parameters(model, best_model): 199 | '''Move parameter values from best_model to model.''' 200 | for params, best_params in zip(model.parameters(), best_model.parameters()): 201 | params.data = best_params 202 | -------------------------------------------------------------------------------- /selection/models/train.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import numpy as np 3 | import selection.layers as layers 4 | from selection.models import utils 5 | from copy import deepcopy 6 | from torch.utils.data import DataLoader 7 | 8 | 9 | class Training: 10 | ''' 11 | Class for training PyTorch models. 12 | 13 | Args: 14 | model: the model to be trained. 15 | ''' 16 | def __init__(self, model): 17 | self.model = model 18 | self.trained = False 19 | 20 | def __call__(self, 21 | train_dataset, 22 | val_dataset, 23 | lr, 24 | mbsize, 25 | max_nepochs, 26 | loss_fn, 27 | optimizer='Adam', 28 | lookback=5, 29 | check_every=1, 30 | verbose=True): 31 | ''' 32 | Train the model. 33 | 34 | Args: 35 | train_dataset: training dataset. 36 | val_dataset: validation dataset. 37 | lr: learning rate. 38 | mbsize: minibatch size. 39 | max_nepochs: maximum number of epochs. 40 | loss_fn: loss function. 41 | optimizer: optimizer type. 42 | lookback: number of epochs to wait for improvement before stopping. 43 | check_every: number of epochs between loss value checks. 44 | verbose: verbosity. 45 | ''' 46 | # Ensure model has not yet been trained. 47 | assert not self.trained 48 | self.trained = True 49 | 50 | # Set up optimizer. 51 | optimizer = utils.get_optimizer(optimizer, self.model.parameters(), lr) 52 | 53 | # Set up data loaders. 54 | train_loader = DataLoader(train_dataset, batch_size=mbsize, 55 | shuffle=True, drop_last=True) 56 | val_loader = DataLoader(val_dataset, batch_size=mbsize) 57 | 58 | # Determine device. 59 | device = next(self.model.parameters()).device 60 | 61 | # For tracking loss. 62 | self.train_loss = [] 63 | self.val_loss = [] 64 | best_model = None 65 | best_loss = np.inf 66 | best_epoch = None 67 | 68 | # Begin training. 69 | for epoch in range(max_nepochs): 70 | for x, y in train_loader: 71 | # Move to device. 72 | x = x.to(device) 73 | y = y.to(device) 74 | 75 | # Forward pass. 76 | pred = self.model(x) 77 | 78 | # Calculate loss. 79 | loss = loss_fn(pred, y) 80 | 81 | # Gradient step. 82 | loss.backward() 83 | optimizer.step() 84 | self.model.zero_grad() 85 | 86 | # Check progress. 87 | with torch.no_grad(): 88 | # Calculate loss. 89 | self.model.eval() 90 | train_loss = utils.validate( 91 | self.model, train_loader, loss_fn).item() 92 | val_loss = utils.validate( 93 | self.model, val_loader, loss_fn).item() 94 | self.model.train() 95 | 96 | # Record loss. 97 | self.train_loss.append(train_loss) 98 | self.val_loss.append(val_loss) 99 | 100 | if verbose and ((epoch + 1) % check_every == 0): 101 | print('{}Epoch = {}{}'.format('-' * 8, epoch + 1, '-' * 8)) 102 | print('Train loss = {:.4f}'.format(train_loss)) 103 | print('Val loss = {:.4f}'.format(val_loss)) 104 | 105 | # Check for early stopping. 106 | if val_loss < best_loss: 107 | best_loss = val_loss 108 | best_model = deepcopy(self.model) 109 | best_epoch = epoch 110 | elif (epoch - best_epoch) > lookback: 111 | if verbose: 112 | print('Stopping early') 113 | break 114 | 115 | # Restore model parameters. 116 | utils.restore_parameters(self.model, best_model) 117 | 118 | 119 | class AnnealedTemperatureTraining: 120 | ''' 121 | Class for training PyTorch models with a temperature parameter. 122 | 123 | Args: 124 | model: the model to be trained. 125 | ''' 126 | def __init__(self, model): 127 | self.model = model 128 | self.trained = False 129 | 130 | def __call__(self, 131 | train_dataset, 132 | val_dataset, 133 | lr, 134 | mbsize, 135 | max_nepochs, 136 | start_temperature, 137 | end_temperature, 138 | loss_fn, 139 | optimizer='Adam', 140 | check_every=1, 141 | verbose=True, 142 | **kwargs): 143 | ''' 144 | Train the model. 145 | 146 | Args: 147 | train_dataset: training dataset. 148 | val_dataset: validation dataset. 149 | lr: learning rate. 150 | mbsize: minibatch size. 151 | max_nepochs: maximum number of epochs. 152 | start_temperature: 153 | end_temperature: 154 | loss_fn: loss function. 155 | optimizer: optimizer type. 156 | lookback: number of epochs to wait for improvement before stopping. 157 | check_every: number of epochs between loss value checks. 158 | verbose: verbosity. 159 | kwargs: additional arguments (e.g. n_samples, mask_output, lam). These 160 | are optional, except lam is required for ConcreteGates. 161 | ''' 162 | # Ensure model has not yet been trained. 163 | assert not self.trained 164 | self.trained = True 165 | 166 | # Get additional arguments. 167 | mask_output = kwargs.get('mask_output', False) 168 | n_samples = kwargs.get('n_samples', None) 169 | lam = kwargs.get('lam', None) 170 | 171 | # Verify arguments. 172 | if mask_output: 173 | # Verify that model is based on mask or gates. 174 | assert ( 175 | isinstance(self.model.input_layer, layers.ConcreteMask) or 176 | isinstance(self.model.input_layer, layers.ConcreteMax) or 177 | isinstance(self.model.input_layer, layers.ConcreteGates)) 178 | 179 | if lam is not None: 180 | # Verify that model is based on gates. 181 | assert isinstance(self.model.input_layer, layers.ConcreteGates) 182 | else: 183 | # Verify that model is not based on gates. 184 | assert not isinstance(self.model.input_layer, layers.ConcreteGates) 185 | 186 | # Determine whether or not to require mask return. 187 | return_mask = lam or mask_output 188 | 189 | # Set up optimizer. 190 | optimizer = utils.get_optimizer(optimizer, self.model.parameters(), lr) 191 | 192 | # Set up data loaders. 193 | train_loader = DataLoader(train_dataset, batch_size=mbsize, 194 | shuffle=True, drop_last=True) 195 | val_loader = DataLoader(val_dataset, batch_size=mbsize) 196 | 197 | # Determine device. 198 | device = next(self.model.parameters()).device 199 | 200 | # Set temperature and determine rate for decreasing. 201 | self.model.input_layer.temperature = start_temperature 202 | r = np.power(end_temperature / start_temperature, 203 | 1 / ((len(train_dataset) // mbsize) * max_nepochs)) 204 | 205 | # For tracking loss. 206 | self.train_loss = [] 207 | self.val_loss = [] 208 | 209 | # Begin training. 210 | for epoch in range(max_nepochs): 211 | for x, y in train_loader: 212 | # Move to device. 213 | x = x.to(device) 214 | y = y.to(device) 215 | 216 | # Forward pass. 217 | if return_mask: 218 | pred, m = self.model(x, n_samples=n_samples, 219 | return_mask=True) 220 | else: 221 | pred = self.model(x, n_samples=n_samples) 222 | 223 | # Reshape to handle n_samples if necessary. 224 | if n_samples: 225 | pred = pred.permute(1, 0, 2).reshape(n_samples * len(y), -1) 226 | y = y.repeat(n_samples, 0) 227 | 228 | # Calculate loss. 229 | if mask_output: 230 | loss = loss_fn(pred, y, weights=1-m) 231 | else: 232 | loss = loss_fn(pred, y) 233 | 234 | # Calculate penalty if necessary. 235 | if lam: 236 | penalty = lam * utils.input_layer_penalty( 237 | self.model.input_layer, m) 238 | loss = loss + penalty 239 | 240 | # Gradient step. 241 | loss.backward() 242 | optimizer.step() 243 | self.model.zero_grad() 244 | 245 | # Adjust temperature. 246 | self.model.input_layer.temperature *= r 247 | 248 | # Check progress. 249 | with torch.no_grad(): 250 | # Calculate loss. 251 | self.model.eval() 252 | train_loss = utils.validate_input_layer( 253 | self.model, train_loader, loss_fn).item() 254 | val_loss = utils.validate_input_layer( 255 | self.model, val_loader, loss_fn).item() 256 | self.model.train() 257 | 258 | # Calculate penalty if necessary. 259 | if lam: 260 | penalty = lam * utils.input_layer_penalty( 261 | self.model.input_layer, m) 262 | train_loss = train_loss + penalty 263 | val_loss = val_loss + penalty 264 | 265 | # Record loss. 266 | self.train_loss.append(train_loss) 267 | self.val_loss.append(val_loss) 268 | 269 | if verbose and ((epoch + 1) % check_every == 0): 270 | print('{}Epoch = {}{}'.format('-' * 8, epoch + 1, '-' * 8)) 271 | print('Train loss = {:.4f}'.format(train_loss)) 272 | print('Val loss = {:.4f}'.format(val_loss)) 273 | print(utils.input_layer_summary( 274 | self.model.input_layer, n_samples=mbsize)) 275 | 276 | # Check for early stopping. 277 | if utils.input_layer_converged(self.model.input_layer, 278 | n_samples=mbsize): 279 | if verbose: 280 | print('Stopping early') 281 | break 282 | 283 | # Fix input layer if necessary. 284 | utils.input_layer_fix(self.model.input_layer) 285 | -------------------------------------------------------------------------------- /mnist selection.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import torch\n", 10 | "import torch.nn as nn\n", 11 | "import numpy as np\n", 12 | "import matplotlib.pyplot as plt\n", 13 | "import torchvision.datasets as dsets\n", 14 | "from selection import models, data" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": 2, 20 | "metadata": {}, 21 | "outputs": [], 22 | "source": [ 23 | "# Load train set\n", 24 | "train = dsets.MNIST('./', train=True, download=True)\n", 25 | "imgs = train.data.reshape(-1, 784) / 255.0\n", 26 | "labels = train.targets\n", 27 | "\n", 28 | "# Shuffle and split into train and val\n", 29 | "inds = torch.randperm(len(train))\n", 30 | "imgs = imgs[inds]\n", 31 | "labels = labels[inds]\n", 32 | "val_x, val_y = imgs[:6000], labels[:6000]\n", 33 | "train_x, train_y = imgs[6000:], labels[6000:]\n", 34 | "\n", 35 | "# Load test set\n", 36 | "test = dsets.MNIST('./', train=False, download=True)\n", 37 | "test_x = test.data.reshape(-1, 784) / 255.0\n", 38 | "test_y = test.targets\n", 39 | "\n", 40 | "# Create TabularDatasets (for specifying feature indices)\n", 41 | "train_set = data.TabularDataset(train_x, train_y)\n", 42 | "val_set = data.TabularDataset(val_x, val_y)\n", 43 | "test_set = data.TabularDataset(test_x, test_y)\n", 44 | "input_size = train_set.input_size\n", 45 | "output_size = train_set.output_size" 46 | ] 47 | }, 48 | { 49 | "cell_type": "markdown", 50 | "metadata": {}, 51 | "source": [ 52 | "# Concrete mask" 53 | ] 54 | }, 55 | { 56 | "cell_type": "code", 57 | "execution_count": 3, 58 | "metadata": { 59 | "scrolled": true 60 | }, 61 | "outputs": [ 62 | { 63 | "name": "stdout", 64 | "output_type": "stream", 65 | "text": [ 66 | "--------Epoch = 30--------\n", 67 | "Train loss = 0.1628\n", 68 | "Val loss = 0.1940\n", 69 | "Max = 0.01, Mean = 0.01, Min = 0.01\n", 70 | "--------Epoch = 60--------\n", 71 | "Train loss = 0.1127\n", 72 | "Val loss = 0.1629\n", 73 | "Max = 0.04, Mean = 0.03, Min = 0.02\n", 74 | "--------Epoch = 90--------\n", 75 | "Train loss = 0.1780\n", 76 | "Val loss = 0.2209\n", 77 | "Max = 0.14, Mean = 0.10, Min = 0.08\n", 78 | "--------Epoch = 120--------\n", 79 | "Train loss = 0.2774\n", 80 | "Val loss = 0.3060\n", 81 | "Max = 0.38, Mean = 0.25, Min = 0.19\n", 82 | "--------Epoch = 150--------\n", 83 | "Train loss = 0.3300\n", 84 | "Val loss = 0.3789\n", 85 | "Max = 0.87, Mean = 0.50, Min = 0.31\n", 86 | "--------Epoch = 180--------\n", 87 | "Train loss = 0.2480\n", 88 | "Val loss = 0.3169\n", 89 | "Max = 1.00, Mean = 0.86, Min = 0.54\n", 90 | "--------Epoch = 210--------\n", 91 | "Train loss = 0.1065\n", 92 | "Val loss = 0.3424\n", 93 | "Max = 1.00, Mean = 0.99, Min = 0.95\n", 94 | "--------Epoch = 240--------\n", 95 | "Train loss = 0.0418\n", 96 | "Val loss = 0.4971\n", 97 | "Max = 1.00, Mean = 1.00, Min = 0.99\n", 98 | "Stopping early\n" 99 | ] 100 | } 101 | ], 102 | "source": [ 103 | "# Create model\n", 104 | "model = models.SelectorMLP(\n", 105 | " input_layer='concrete_mask',\n", 106 | " k=20,\n", 107 | " input_size=input_size,\n", 108 | " output_size=output_size,\n", 109 | " hidden=[512, 512],\n", 110 | " activation='elu').cuda()\n", 111 | "\n", 112 | "# Train model\n", 113 | "model.learn(\n", 114 | " train_set,\n", 115 | " val_set,\n", 116 | " lr=1e-3,\n", 117 | " mbsize=256,\n", 118 | " max_nepochs=300,\n", 119 | " start_temperature=10.0,\n", 120 | " end_temperature=0.01,\n", 121 | " loss_fn=nn.CrossEntropyLoss(),\n", 122 | " check_every=30)" 123 | ] 124 | }, 125 | { 126 | "cell_type": "code", 127 | "execution_count": 4, 128 | "metadata": {}, 129 | "outputs": [ 130 | { 131 | "data": { 132 | "image/png": "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\n", 133 | "text/plain": [ 134 | "
" 135 | ] 136 | }, 137 | "metadata": { 138 | "needs_background": "light" 139 | }, 140 | "output_type": "display_data" 141 | } 142 | ], 143 | "source": [ 144 | "# Verify convergence\n", 145 | "m = model.input_layer.sample(n_samples=256)\n", 146 | "values = torch.mean(m, dim=0)\n", 147 | "sorted_values = torch.sort(values, descending=True).values\n", 148 | "\n", 149 | "# Plot\n", 150 | "fig, axarr = plt.subplots(1, 2, figsize=(16, 6))\n", 151 | "\n", 152 | "ax = axarr[0]\n", 153 | "ax.plot(np.arange(input_size), sorted_values.cpu().data, marker='o')\n", 154 | "ax.axvline(model.input_layer.k - 0.5, color='black', linestyle='--')\n", 155 | "ax.set_xlim(-0.5, 2 * model.input_layer.k)\n", 156 | "ax.set_title('Mask values', fontsize=20)\n", 157 | "ax.set_xlabel('Mask position (sorted by value)', fontsize=16)\n", 158 | "ax.set_ylabel('Mask value', fontsize=16)\n", 159 | "ax.tick_params('both', labelsize=14)\n", 160 | "\n", 161 | "ax = axarr[1]\n", 162 | "ax.imshow(np.reshape(values.cpu().data, (28, 28)))\n", 163 | "ax.set_title('Selected pixels', fontsize=20)\n", 164 | "ax.set_xticks([])\n", 165 | "ax.set_yticks([])\n", 166 | "\n", 167 | "plt.tight_layout()\n", 168 | "plt.show()" 169 | ] 170 | }, 171 | { 172 | "cell_type": "code", 173 | "execution_count": 5, 174 | "metadata": {}, 175 | "outputs": [ 176 | { 177 | "name": "stdout", 178 | "output_type": "stream", 179 | "text": [ 180 | "Accuracy = 0.912\n" 181 | ] 182 | } 183 | ], 184 | "source": [ 185 | "# Extract select inds\n", 186 | "inds = model.get_inds()\n", 187 | "\n", 188 | "# Restrict data to selected indices\n", 189 | "train_set.set_inds(inds)\n", 190 | "val_set.set_inds(inds)\n", 191 | "test_set.set_inds(inds)\n", 192 | "\n", 193 | "# Train debiased model\n", 194 | "model = models.MLP(\n", 195 | " input_size=len(inds),\n", 196 | " output_size=output_size,\n", 197 | " hidden=[512, 512],\n", 198 | " activation='elu').cuda()\n", 199 | "\n", 200 | "model.learn(\n", 201 | " train_set,\n", 202 | " val_set,\n", 203 | " lr=1e-3,\n", 204 | " mbsize=256,\n", 205 | " max_nepochs=100,\n", 206 | " loss_fn=nn.CrossEntropyLoss(),\n", 207 | " verbose=False)\n", 208 | "\n", 209 | "# Calculate loss on test set\n", 210 | "print('Accuracy = {:.3f}'.format(\n", 211 | " model.evaluate(test_set, models.utils.Accuracy()).item()))\n", 212 | "\n", 213 | "# Reset data\n", 214 | "train_set.set_inds()\n", 215 | "val_set.set_inds()\n", 216 | "test_set.set_inds()" 217 | ] 218 | }, 219 | { 220 | "cell_type": "markdown", 221 | "metadata": {}, 222 | "source": [ 223 | "# Concrete selector" 224 | ] 225 | }, 226 | { 227 | "cell_type": "code", 228 | "execution_count": 6, 229 | "metadata": { 230 | "scrolled": true 231 | }, 232 | "outputs": [ 233 | { 234 | "name": "stdout", 235 | "output_type": "stream", 236 | "text": [ 237 | "--------Epoch = 30--------\n", 238 | "Train loss = 0.4622\n", 239 | "Val loss = 0.4773\n", 240 | "Max = 0.00, Mean = 0.00, Min = 0.00\n", 241 | "--------Epoch = 60--------\n", 242 | "Train loss = 0.3636\n", 243 | "Val loss = 0.3665\n", 244 | "Max = 0.01, Mean = 0.01, Min = 0.00\n", 245 | "--------Epoch = 90--------\n", 246 | "Train loss = 0.4361\n", 247 | "Val loss = 0.4387\n", 248 | "Max = 0.12, Mean = 0.06, Min = 0.01\n", 249 | "--------Epoch = 120--------\n", 250 | "Train loss = 0.3232\n", 251 | "Val loss = 0.3548\n", 252 | "Max = 0.82, Mean = 0.52, Min = 0.20\n", 253 | "--------Epoch = 150--------\n", 254 | "Train loss = 0.2223\n", 255 | "Val loss = 0.3321\n", 256 | "Max = 0.99, Mean = 0.85, Min = 0.35\n", 257 | "--------Epoch = 180--------\n", 258 | "Train loss = 0.1574\n", 259 | "Val loss = 0.3781\n", 260 | "Max = 1.00, Mean = 0.94, Min = 0.46\n", 261 | "--------Epoch = 210--------\n", 262 | "Train loss = 0.0999\n", 263 | "Val loss = 0.4632\n", 264 | "Max = 1.00, Mean = 0.98, Min = 0.74\n", 265 | "--------Epoch = 240--------\n", 266 | "Train loss = 0.0689\n", 267 | "Val loss = 0.5715\n", 268 | "Max = 1.00, Mean = 0.99, Min = 0.84\n", 269 | "--------Epoch = 270--------\n", 270 | "Train loss = 0.0615\n", 271 | "Val loss = 0.7051\n", 272 | "Max = 1.00, Mean = 0.99, Min = 0.90\n", 273 | "--------Epoch = 300--------\n", 274 | "Train loss = 0.0454\n", 275 | "Val loss = 0.8005\n", 276 | "Max = 1.00, Mean = 0.99, Min = 0.91\n" 277 | ] 278 | } 279 | ], 280 | "source": [ 281 | "# Create model\n", 282 | "model = models.SelectorMLP(\n", 283 | " input_layer='concrete_selector',\n", 284 | " k=20,\n", 285 | " input_size=input_size,\n", 286 | " output_size=output_size,\n", 287 | " hidden=[512, 512],\n", 288 | " activation='elu').cuda()\n", 289 | "\n", 290 | "# Train model\n", 291 | "model.learn(\n", 292 | " train_set,\n", 293 | " val_set,\n", 294 | " lr=1e-3,\n", 295 | " mbsize=256,\n", 296 | " max_nepochs=300,\n", 297 | " start_temperature=10.0,\n", 298 | " end_temperature=0.01,\n", 299 | " loss_fn=nn.CrossEntropyLoss(),\n", 300 | " check_every=30)" 301 | ] 302 | }, 303 | { 304 | "cell_type": "code", 305 | "execution_count": 7, 306 | "metadata": {}, 307 | "outputs": [ 308 | { 309 | "data": { 310 | "image/png": "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\n", 311 | "text/plain": [ 312 | "
" 313 | ] 314 | }, 315 | "metadata": { 316 | "needs_background": "light" 317 | }, 318 | "output_type": "display_data" 319 | } 320 | ], 321 | "source": [ 322 | "# Verify convergence\n", 323 | "M = model.input_layer.sample(n_samples=256)\n", 324 | "values = torch.mean(M, dim=0)\n", 325 | "sorted_values = torch.sort(values, dim=1, descending=True).values\n", 326 | "\n", 327 | "# Plot\n", 328 | "fig, axarr = plt.subplots(1, 2, figsize=(16, 6))\n", 329 | "\n", 330 | "ax = axarr[0]\n", 331 | "for i in range(model.input_layer.k):\n", 332 | " ax.plot(np.arange(input_size), sorted_values[i].cpu().data,\n", 333 | " marker='o', alpha=0.5)\n", 334 | "ax.set_xlim(-0.4, 3)\n", 335 | "ax.set_xticks(np.arange(4))\n", 336 | "ax.axvline(0.5, color='black', linestyle='--')\n", 337 | "ax.set_title('Selector values', fontsize=20)\n", 338 | "ax.set_xlabel('Selector position (sorted)', fontsize=16)\n", 339 | "ax.set_ylabel('Selector value', fontsize=16)\n", 340 | "ax.tick_params('both', labelsize=14)\n", 341 | "\n", 342 | "ax = axarr[1]\n", 343 | "ax.imshow(np.reshape(torch.sum(values, dim=0).cpu().data,\n", 344 | " (28, 28)))\n", 345 | "ax.set_title('Selected pixels', fontsize=20)\n", 346 | "ax.set_xticks([])\n", 347 | "ax.set_yticks([])\n", 348 | "\n", 349 | "plt.tight_layout()\n", 350 | "plt.show()" 351 | ] 352 | }, 353 | { 354 | "cell_type": "code", 355 | "execution_count": 8, 356 | "metadata": {}, 357 | "outputs": [ 358 | { 359 | "name": "stdout", 360 | "output_type": "stream", 361 | "text": [ 362 | "Accuracy = 0.908\n" 363 | ] 364 | } 365 | ], 366 | "source": [ 367 | "# Extract select inds\n", 368 | "inds = model.get_inds()\n", 369 | "\n", 370 | "# Restrict data to selected indices\n", 371 | "train_set.set_inds(inds)\n", 372 | "val_set.set_inds(inds)\n", 373 | "test_set.set_inds(inds)\n", 374 | "\n", 375 | "# Train debiased model\n", 376 | "model = models.MLP(\n", 377 | " input_size=len(inds),\n", 378 | " output_size=output_size,\n", 379 | " hidden=[512, 512],\n", 380 | " activation='elu').cuda()\n", 381 | "\n", 382 | "model.learn(\n", 383 | " train_set,\n", 384 | " val_set,\n", 385 | " lr=1e-3,\n", 386 | " mbsize=256,\n", 387 | " max_nepochs=100,\n", 388 | " loss_fn=nn.CrossEntropyLoss(),\n", 389 | " verbose=False)\n", 390 | "\n", 391 | "# Calculate loss on test set\n", 392 | "print('Accuracy = {:.3f}'.format(\n", 393 | " model.evaluate(test_set, models.utils.Accuracy()).item()))\n", 394 | "\n", 395 | "# Reset data\n", 396 | "train_set.set_inds()\n", 397 | "val_set.set_inds()\n", 398 | "test_set.set_inds()" 399 | ] 400 | }, 401 | { 402 | "cell_type": "markdown", 403 | "metadata": {}, 404 | "source": [ 405 | "# Concrete gates" 406 | ] 407 | }, 408 | { 409 | "cell_type": "code", 410 | "execution_count": 9, 411 | "metadata": { 412 | "scrolled": true 413 | }, 414 | "outputs": [ 415 | { 416 | "name": "stdout", 417 | "output_type": "stream", 418 | "text": [ 419 | "--------Epoch = 30--------\n", 420 | "Train loss = 0.4232\n", 421 | "Val loss = 0.4506\n", 422 | "Mean dist = 0.06, Max dist = 0.48, Num sel = 1\n", 423 | "--------Epoch = 60--------\n", 424 | "Train loss = 0.3747\n", 425 | "Val loss = 0.4128\n", 426 | "Mean dist = 0.05, Max dist = 0.49, Num sel = 19\n", 427 | "--------Epoch = 90--------\n", 428 | "Train loss = 0.3208\n", 429 | "Val loss = 0.3838\n", 430 | "Mean dist = 0.03, Max dist = 0.50, Num sel = 29\n", 431 | "--------Epoch = 120--------\n", 432 | "Train loss = 0.2641\n", 433 | "Val loss = 0.3821\n", 434 | "Mean dist = 0.02, Max dist = 0.49, Num sel = 30\n", 435 | "--------Epoch = 150--------\n", 436 | "Train loss = 0.2223\n", 437 | "Val loss = 0.4085\n", 438 | "Mean dist = 0.01, Max dist = 0.50, Num sel = 31\n", 439 | "--------Epoch = 180--------\n", 440 | "Train loss = 0.2115\n", 441 | "Val loss = 0.4675\n", 442 | "Mean dist = 0.01, Max dist = 0.49, Num sel = 30\n", 443 | "--------Epoch = 210--------\n", 444 | "Train loss = 0.1814\n", 445 | "Val loss = 0.4987\n", 446 | "Mean dist = 0.01, Max dist = 0.44, Num sel = 29\n", 447 | "--------Epoch = 240--------\n", 448 | "Train loss = 0.1735\n", 449 | "Val loss = 0.5488\n", 450 | "Mean dist = 0.00, Max dist = 0.43, Num sel = 29\n", 451 | "--------Epoch = 270--------\n", 452 | "Train loss = 0.1643\n", 453 | "Val loss = 0.5985\n", 454 | "Mean dist = 0.00, Max dist = 0.49, Num sel = 28\n", 455 | "--------Epoch = 300--------\n", 456 | "Train loss = 0.1627\n", 457 | "Val loss = 0.6583\n", 458 | "Mean dist = 0.00, Max dist = 0.43, Num sel = 28\n" 459 | ] 460 | } 461 | ], 462 | "source": [ 463 | "# Create model\n", 464 | "model = models.SelectorMLP(\n", 465 | " input_layer='concrete_gates',\n", 466 | " input_size=input_size,\n", 467 | " output_size=output_size,\n", 468 | " hidden=[512, 512],\n", 469 | " activation='elu').cuda()\n", 470 | "\n", 471 | "# Train model\n", 472 | "model.learn(\n", 473 | " train_set,\n", 474 | " val_set,\n", 475 | " lam=0.005,\n", 476 | " lr=1e-3,\n", 477 | " mbsize=256,\n", 478 | " max_nepochs=300,\n", 479 | " start_temperature=1.0,\n", 480 | " end_temperature=0.001,\n", 481 | " loss_fn=nn.CrossEntropyLoss(),\n", 482 | " check_every=30)" 483 | ] 484 | }, 485 | { 486 | "cell_type": "code", 487 | "execution_count": 10, 488 | "metadata": {}, 489 | "outputs": [ 490 | { 491 | "data": { 492 | "image/png": "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\n", 493 | "text/plain": [ 494 | "
" 495 | ] 496 | }, 497 | "metadata": { 498 | "needs_background": "light" 499 | }, 500 | "output_type": "display_data" 501 | } 502 | ], 503 | "source": [ 504 | "# Verify convergence\n", 505 | "m = model.input_layer.sample(n_samples=256)\n", 506 | "values = torch.mean(m, dim=0)\n", 507 | "sorted_values = torch.sort(values, descending=True).values\n", 508 | "\n", 509 | "# Plot\n", 510 | "fig, axarr = plt.subplots(1, 2, figsize=(16, 6))\n", 511 | "\n", 512 | "ax = axarr[0]\n", 513 | "ax.plot(np.arange(input_size), sorted_values.cpu().data,\n", 514 | " marker='o')\n", 515 | "ind = (sorted_values < 0.5).nonzero()[0].item()\n", 516 | "ax.axvline(ind - 0.5, color='black', linestyle='--')\n", 517 | "ax.set_xlim(-0.5, min(input_size, 2 * ind))\n", 518 | "ax.set_title('Mask values', fontsize=20)\n", 519 | "ax.set_xlabel('Mask position (sorted by value)', fontsize=16)\n", 520 | "ax.set_ylabel('Mask value', fontsize=16)\n", 521 | "ax.tick_params('both', labelsize=14)\n", 522 | "\n", 523 | "ax = axarr[1]\n", 524 | "ax.imshow(np.reshape(values.cpu().data, (28, 28)))\n", 525 | "ax.set_title('Selected pixels', fontsize=20)\n", 526 | "ax.set_xticks([])\n", 527 | "ax.set_yticks([])\n", 528 | "\n", 529 | "plt.tight_layout()\n", 530 | "plt.show()" 531 | ] 532 | }, 533 | { 534 | "cell_type": "code", 535 | "execution_count": 11, 536 | "metadata": {}, 537 | "outputs": [ 538 | { 539 | "name": "stdout", 540 | "output_type": "stream", 541 | "text": [ 542 | "Accuracy = 0.936\n" 543 | ] 544 | } 545 | ], 546 | "source": [ 547 | "# Extract selected inds\n", 548 | "inds = model.get_inds(threshold=0.5)\n", 549 | "\n", 550 | "# Restrict data to selected indices\n", 551 | "train_set.set_inds(inds)\n", 552 | "val_set.set_inds(inds)\n", 553 | "test_set.set_inds(inds)\n", 554 | "\n", 555 | "# Train debiased model\n", 556 | "model = models.MLP(\n", 557 | " input_size=len(inds),\n", 558 | " output_size=output_size,\n", 559 | " hidden=[512, 512],\n", 560 | " activation='elu').cuda()\n", 561 | "\n", 562 | "model.learn(\n", 563 | " train_set,\n", 564 | " val_set,\n", 565 | " lr=1e-3,\n", 566 | " mbsize=256,\n", 567 | " max_nepochs=100,\n", 568 | " loss_fn=nn.CrossEntropyLoss(),\n", 569 | " verbose=False)\n", 570 | "\n", 571 | "# Calculate loss on test set\n", 572 | "print('Accuracy = {:.3f}'.format(\n", 573 | " model.evaluate(test_set, models.utils.Accuracy()).item()))\n", 574 | "\n", 575 | "# Reset data\n", 576 | "train_set.set_inds()\n", 577 | "val_set.set_inds()\n", 578 | "test_set.set_inds()" 579 | ] 580 | }, 581 | { 582 | "cell_type": "markdown", 583 | "metadata": {}, 584 | "source": [ 585 | "# Concrete max" 586 | ] 587 | }, 588 | { 589 | "cell_type": "code", 590 | "execution_count": 12, 591 | "metadata": { 592 | "scrolled": true 593 | }, 594 | "outputs": [ 595 | { 596 | "name": "stdout", 597 | "output_type": "stream", 598 | "text": [ 599 | "--------Epoch = 30--------\n", 600 | "Train loss = 0.1909\n", 601 | "Val loss = 0.2271\n", 602 | "Max = 0.01, Mean = 0.01, Min = 0.01\n", 603 | "--------Epoch = 60--------\n", 604 | "Train loss = 0.1347\n", 605 | "Val loss = 0.1850\n", 606 | "Max = 0.03, Mean = 0.03, Min = 0.02\n", 607 | "--------Epoch = 90--------\n", 608 | "Train loss = 0.2218\n", 609 | "Val loss = 0.2505\n", 610 | "Max = 0.14, Mean = 0.09, Min = 0.07\n", 611 | "--------Epoch = 120--------\n", 612 | "Train loss = 0.3114\n", 613 | "Val loss = 0.3256\n", 614 | "Max = 0.25, Mean = 0.18, Min = 0.15\n", 615 | "--------Epoch = 150--------\n", 616 | "Train loss = 0.3836\n", 617 | "Val loss = 0.4023\n", 618 | "Max = 0.43, Mean = 0.27, Min = 0.21\n", 619 | "--------Epoch = 180--------\n", 620 | "Train loss = 0.4570\n", 621 | "Val loss = 0.4976\n", 622 | "Max = 0.54, Mean = 0.37, Min = 0.29\n", 623 | "--------Epoch = 210--------\n", 624 | "Train loss = 0.4890\n", 625 | "Val loss = 0.5192\n", 626 | "Max = 0.52, Mean = 0.40, Min = 0.32\n", 627 | "--------Epoch = 240--------\n", 628 | "Train loss = 0.5251\n", 629 | "Val loss = 0.5488\n", 630 | "Max = 0.55, Mean = 0.42, Min = 0.35\n", 631 | "--------Epoch = 270--------\n", 632 | "Train loss = 0.5137\n", 633 | "Val loss = 0.5473\n", 634 | "Max = 0.57, Mean = 0.44, Min = 0.35\n", 635 | "--------Epoch = 300--------\n", 636 | "Train loss = 0.5147\n", 637 | "Val loss = 0.5587\n", 638 | "Max = 0.55, Mean = 0.44, Min = 0.38\n" 639 | ] 640 | } 641 | ], 642 | "source": [ 643 | "# Create model\n", 644 | "model = models.SelectorMLP(\n", 645 | " input_layer='concrete_max',\n", 646 | " k=20,\n", 647 | " input_size=input_size,\n", 648 | " output_size=output_size,\n", 649 | " hidden=[512, 512],\n", 650 | " activation='elu').cuda()\n", 651 | "\n", 652 | "# Train model\n", 653 | "model.learn(\n", 654 | " train_set,\n", 655 | " val_set,\n", 656 | " lr=1e-3,\n", 657 | " mbsize=256,\n", 658 | " max_nepochs=300,\n", 659 | " start_temperature=10.0,\n", 660 | " end_temperature=0.01,\n", 661 | " loss_fn=nn.CrossEntropyLoss(),\n", 662 | " check_every=30)" 663 | ] 664 | }, 665 | { 666 | "cell_type": "code", 667 | "execution_count": 13, 668 | "metadata": {}, 669 | "outputs": [ 670 | { 671 | "data": { 672 | "image/png": "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\n", 673 | "text/plain": [ 674 | "
" 675 | ] 676 | }, 677 | "metadata": { 678 | "needs_background": "light" 679 | }, 680 | "output_type": "display_data" 681 | } 682 | ], 683 | "source": [ 684 | "# Verify convergence\n", 685 | "m = model.input_layer.sample(n_samples=256)\n", 686 | "values = torch.mean(m, dim=0)\n", 687 | "sorted_values = torch.sort(values, descending=True).values\n", 688 | "\n", 689 | "# Plot\n", 690 | "fig, axarr = plt.subplots(1, 2, figsize=(16, 6))\n", 691 | "\n", 692 | "ax = axarr[0]\n", 693 | "ax.plot(np.arange(input_size), sorted_values.cpu().data, marker='o')\n", 694 | "ax.axvline(model.input_layer.k - 0.5, color='black', linestyle='--')\n", 695 | "ax.set_xlim(-0.5, 2 * model.input_layer.k)\n", 696 | "ax.set_title('Mean mask values', fontsize=20)\n", 697 | "ax.set_xlabel('Mask position (sorted by value)', fontsize=16)\n", 698 | "ax.set_ylabel('Mask value', fontsize=16)\n", 699 | "ax.tick_params('both', labelsize=14)\n", 700 | "\n", 701 | "ax = axarr[1]\n", 702 | "ax.imshow(np.reshape(values.cpu().data, (28, 28)), vmin=0, vmax=1)\n", 703 | "ax.set_title('Commonly selected pixels', fontsize=20)\n", 704 | "ax.set_xticks([])\n", 705 | "ax.set_yticks([])\n", 706 | "\n", 707 | "plt.tight_layout()\n", 708 | "plt.show()" 709 | ] 710 | }, 711 | { 712 | "cell_type": "code", 713 | "execution_count": 14, 714 | "metadata": {}, 715 | "outputs": [ 716 | { 717 | "name": "stdout", 718 | "output_type": "stream", 719 | "text": [ 720 | "Accuracy = 0.906\n" 721 | ] 722 | } 723 | ], 724 | "source": [ 725 | "# Extract select inds\n", 726 | "inds = model.get_inds()\n", 727 | "\n", 728 | "# Restrict data to selected indices\n", 729 | "train_set.set_inds(inds)\n", 730 | "val_set.set_inds(inds)\n", 731 | "test_set.set_inds(inds)\n", 732 | "\n", 733 | "# Train debiased model\n", 734 | "model = models.MLP(\n", 735 | " input_size=len(inds),\n", 736 | " output_size=output_size,\n", 737 | " hidden=[512, 512],\n", 738 | " activation='elu').cuda()\n", 739 | "\n", 740 | "model.learn(\n", 741 | " train_set,\n", 742 | " val_set,\n", 743 | " lr=1e-3,\n", 744 | " mbsize=256,\n", 745 | " max_nepochs=100,\n", 746 | " loss_fn=nn.CrossEntropyLoss(),\n", 747 | " verbose=False)\n", 748 | "\n", 749 | "# Calculate loss on test set\n", 750 | "print('Accuracy = {:.3f}'.format(\n", 751 | " model.evaluate(test_set, models.utils.Accuracy()).item()))\n", 752 | "\n", 753 | "# Reset data\n", 754 | "train_set.set_inds()\n", 755 | "val_set.set_inds()\n", 756 | "test_set.set_inds()" 757 | ] 758 | }, 759 | { 760 | "cell_type": "code", 761 | "execution_count": null, 762 | "metadata": {}, 763 | "outputs": [], 764 | "source": [] 765 | } 766 | ], 767 | "metadata": { 768 | "kernelspec": { 769 | "display_name": "Python 3", 770 | "language": "python", 771 | "name": "python3" 772 | }, 773 | "language_info": { 774 | "codemirror_mode": { 775 | "name": "ipython", 776 | "version": 3 777 | }, 778 | "file_extension": ".py", 779 | "mimetype": "text/x-python", 780 | "name": "python", 781 | "nbconvert_exporter": "python", 782 | "pygments_lexer": "ipython3", 783 | "version": "3.6.8" 784 | } 785 | }, 786 | "nbformat": 4, 787 | "nbformat_minor": 2 788 | } 789 | --------------------------------------------------------------------------------