Spaces:
Runtime error
Runtime error
multimodal
/
transformers
/examples
/research_projects
/movement-pruning
/emmental
/modules
/binarizer.py
| # coding=utf-8 | |
| # Copyright 2020-present, AllenAI Authors, University of Illinois Urbana-Champaign, | |
| # Intel Nervana Systems and the HuggingFace Inc. team. | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| """ | |
| Binarizers take a (real value) matrix as input and produce a binary (values in {0,1}) mask of the same shape. | |
| """ | |
| import torch | |
| from torch import autograd | |
| class ThresholdBinarizer(autograd.Function): | |
| """ | |
| Thresholdd binarizer. | |
| Computes a binary mask M from a real value matrix S such that `M_{i,j} = 1` if and only if `S_{i,j} > \tau` | |
| where `\tau` is a real value threshold. | |
| Implementation is inspired from: | |
| https://github.com/arunmallya/piggyback | |
| Piggyback: Adapting a Single Network to Multiple Tasks by Learning to Mask Weights | |
| Arun Mallya, Dillon Davis, Svetlana Lazebnik | |
| """ | |
| def forward(ctx, inputs: torch.tensor, threshold: float, sigmoid: bool): | |
| """ | |
| Args: | |
| inputs (`torch.FloatTensor`) | |
| The input matrix from which the binarizer computes the binary mask. | |
| threshold (`float`) | |
| The threshold value (in R). | |
| sigmoid (`bool`) | |
| If set to ``True``, we apply the sigmoid function to the `inputs` matrix before comparing to `threshold`. | |
| In this case, `threshold` should be a value between 0 and 1. | |
| Returns: | |
| mask (`torch.FloatTensor`) | |
| Binary matrix of the same size as `inputs` acting as a mask (1 - the associated weight is | |
| retained, 0 - the associated weight is pruned). | |
| """ | |
| nb_elems = inputs.numel() | |
| nb_min = int(0.005 * nb_elems) + 1 | |
| if sigmoid: | |
| mask = (torch.sigmoid(inputs) > threshold).type(inputs.type()) | |
| else: | |
| mask = (inputs > threshold).type(inputs.type()) | |
| if mask.sum() < nb_min: | |
| # We limit the pruning so that at least 0.5% (half a percent) of the weights are remaining | |
| k_threshold = inputs.flatten().kthvalue(max(nb_elems - nb_min, 1)).values | |
| mask = (inputs > k_threshold).type(inputs.type()) | |
| return mask | |
| def backward(ctx, gradOutput): | |
| return gradOutput, None, None | |
| class TopKBinarizer(autograd.Function): | |
| """ | |
| Top-k Binarizer. | |
| Computes a binary mask M from a real value matrix S such that `M_{i,j} = 1` if and only if `S_{i,j}` | |
| is among the k% highest values of S. | |
| Implementation is inspired from: | |
| https://github.com/allenai/hidden-networks | |
| What's hidden in a randomly weighted neural network? | |
| Vivek Ramanujan*, Mitchell Wortsman*, Aniruddha Kembhavi, Ali Farhadi, Mohammad Rastegari | |
| """ | |
| def forward(ctx, inputs: torch.tensor, threshold: float): | |
| """ | |
| Args: | |
| inputs (`torch.FloatTensor`) | |
| The input matrix from which the binarizer computes the binary mask. | |
| threshold (`float`) | |
| The percentage of weights to keep (the rest is pruned). | |
| `threshold` is a float between 0 and 1. | |
| Returns: | |
| mask (`torch.FloatTensor`) | |
| Binary matrix of the same size as `inputs` acting as a mask (1 - the associated weight is | |
| retained, 0 - the associated weight is pruned). | |
| """ | |
| # Get the subnetwork by sorting the inputs and using the top threshold % | |
| mask = inputs.clone() | |
| _, idx = inputs.flatten().sort(descending=True) | |
| j = int(threshold * inputs.numel()) | |
| # flat_out and mask access the same memory. | |
| flat_out = mask.flatten() | |
| flat_out[idx[j:]] = 0 | |
| flat_out[idx[:j]] = 1 | |
| return mask | |
| def backward(ctx, gradOutput): | |
| return gradOutput, None | |
| class MagnitudeBinarizer(object): | |
| """ | |
| Magnitude Binarizer. | |
| Computes a binary mask M from a real value matrix S such that `M_{i,j} = 1` if and only if `S_{i,j}` | |
| is among the k% highest values of |S| (absolute value). | |
| Implementation is inspired from https://github.com/NervanaSystems/distiller/blob/2291fdcc2ea642a98d4e20629acb5a9e2e04b4e6/distiller/pruning/automated_gradual_pruner.py#L24 | |
| """ | |
| def apply(inputs: torch.tensor, threshold: float): | |
| """ | |
| Args: | |
| inputs (`torch.FloatTensor`) | |
| The input matrix from which the binarizer computes the binary mask. | |
| This input marix is typically the weight matrix. | |
| threshold (`float`) | |
| The percentage of weights to keep (the rest is pruned). | |
| `threshold` is a float between 0 and 1. | |
| Returns: | |
| mask (`torch.FloatTensor`) | |
| Binary matrix of the same size as `inputs` acting as a mask (1 - the associated weight is | |
| retained, 0 - the associated weight is pruned). | |
| """ | |
| # Get the subnetwork by sorting the inputs and using the top threshold % | |
| mask = inputs.clone() | |
| _, idx = inputs.abs().flatten().sort(descending=True) | |
| j = int(threshold * inputs.numel()) | |
| # flat_out and mask access the same memory. | |
| flat_out = mask.flatten() | |
| flat_out[idx[j:]] = 0 | |
| flat_out[idx[:j]] = 1 | |
| return mask | |