Zero-Shot Image Classification
Transformers
ONNX
Chinese
English
m2_encoder
feature-extraction
multimodal
image-text-retrieval
bilingual
chinese
english
vision-language
custom-code
custom_code
Eval Results (legacy)
Instructions to use malusama/M2-Encoder-1B with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use malusama/M2-Encoder-1B with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("zero-shot-image-classification", model="malusama/M2-Encoder-1B", trust_remote_code=True) pipe( "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/hub/parrots.png", candidate_labels=["animals", "humans", "landscape"], )# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("malusama/M2-Encoder-1B", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
| # Copyright (c) 2022 Microsoft | |
| # Licensed under The MIT License [see LICENSE for details] | |
| # Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. | |
| # | |
| # This source code is licensed under the BSD license found in the | |
| # LICENSE file in the root directory of this source tree. | |
| # Implementation of Top2Gating described in https://arxiv.org/pdf/2006.16668.pdf | |
| # Code is inspired by Top2GatingOnLogits from lingvo: | |
| # https://github.com/tensorflow/lingvo/blob/21b8106c5f1d30a196c98eedc441d4fd70833b11/lingvo/core/moe_layers.py#L477 | |
| # NOTE: This is a mirror of the code in | |
| # https://github.com/facebookresearch/fairscale/tree/master/fairscale/nn/moe | |
| import math | |
| from typing import Callable, Dict, Optional, Tuple | |
| import torch | |
| import torch.nn.functional as F | |
| from torch import Tensor | |
| from .moe_layer import fused_cumsum_sub_one, has_tutel | |
| # use a fixed temperature to compute balance loss | |
| TEMPERATURE_FOR_L_UAX = 0.07 | |
| # maximum capacity of 1 expert as a fraction of number of tokens in the batch | |
| # Note: setting this to 1.0 causes inference to significantly slow down | |
| EVAL_CAPACITY_TOKEN_FRACTION = 0.25 | |
| # logging | |
| SAMPLE_FRACTION = 0.2 | |
| def top1gating( | |
| logits: torch.Tensor, | |
| input_mask: Optional[torch.Tensor] = None, | |
| use_fp32=False, | |
| capacity_factor=1.0, | |
| eval_mode=False, | |
| moe_eval_capacity_token_fraction=EVAL_CAPACITY_TOKEN_FRACTION, | |
| use_xmoe=False, | |
| gate_obj=None, | |
| ) -> Tuple[Tensor, Tensor, Tensor, Dict]: | |
| """Implements Top2Gating on logits.""" | |
| metadata = {} | |
| if use_fp32: | |
| orig_dtype = logits.dtype | |
| logits = logits.float() | |
| gates = F.softmax(logits, dim=1) | |
| metadata["entropy_gating"] = entropy(probs=gates).mean().detach() | |
| # gates has shape of SE | |
| num_tokens = gates.shape[0] | |
| num_experts = gates.shape[1] | |
| if moe_eval_capacity_token_fraction > 0.0 and eval_mode: | |
| capacity = math.ceil(moe_eval_capacity_token_fraction * num_tokens) | |
| else: | |
| # capacity = capacity_factor * S/E | |
| capacity = int(capacity_factor * math.ceil(num_tokens / num_experts)) | |
| # Create a mask for 1st's expert per token | |
| indices1_s = torch.argmax(gates, dim=1) | |
| mask1 = one_hot(indices1_s, num_classes=num_experts, unsqueeze_indices=True) | |
| if input_mask is not None and input_mask.any(): | |
| nonpadding = ~input_mask | |
| mask1 = mask1 * nonpadding.unsqueeze(-1).to(mask1.dtype) | |
| # for logging (percent of tokens routed to each expert) | |
| expert1_hist = 100 * torch.histc((indices1_s.squeeze() + 1), bins=num_experts, min=1, max=num_experts) / num_tokens | |
| metadata["unused_expert1_count"] = (expert1_hist == 0).sum() | |
| expert1_hist = torch.sort(expert1_hist, dim=0, descending=True).values + torch.finfo(torch.float32).tiny | |
| sample_count = max(math.ceil(num_experts * SAMPLE_FRACTION), 1) | |
| metadata["expert1_balance_top"] = expert1_hist[:sample_count].sum() | |
| metadata["expert1_balance_bottom"] = expert1_hist[-sample_count:].sum() | |
| gates1_s = (gates * mask1).sum(dim=1) | |
| # Compute locations in capacity buffer | |
| locations1 = fused_cumsum_sub_one(mask1) | |
| # Compute l_aux | |
| me = torch.mean(gates, dim=0) | |
| ce = torch.mean(mask1.to(gates.dtype), dim=0) | |
| l_aux = torch.mean(me * ce) | |
| l_aux = l_aux * num_experts * num_experts | |
| if has_tutel: | |
| locations1_s = torch.sum(locations1 * mask1, dim=1) | |
| return ( | |
| l_aux, | |
| metadata, | |
| capacity, | |
| num_experts, | |
| [ | |
| indices1_s, | |
| ], | |
| [ | |
| locations1_s, | |
| ], | |
| [ | |
| gates1_s, | |
| ], | |
| ) | |
| # Remove locations outside capacity from mask | |
| mask1 = mask1 * torch.lt(locations1, capacity) | |
| # Store the capacity location for each token | |
| locations1_s = torch.sum(locations1 * mask1, dim=1) | |
| # Calculate combine_weights and dispatch_mask | |
| gates1 = gates1_s.unsqueeze(-1) * mask1.to(gates1_s.dtype) # einsum("s,se->se") | |
| # locations1_sc = num_tokens * capacity | |
| locations1_sc = one_hot(locations1_s, num_classes=capacity, unsqueeze_indices=True) | |
| combine1_sec = torch.bmm( | |
| # einsum("se,sc->sec") | |
| gates1.unsqueeze(-1), | |
| locations1_sc.to(gates1.dtype).unsqueeze(1), | |
| ) | |
| dispatch_mask = combine1_sec.bool() | |
| if use_fp32: | |
| return l_aux, combine1_sec.to(orig_dtype), dispatch_mask, metadata | |
| else: | |
| return l_aux, combine1_sec, dispatch_mask, metadata | |
| class Top1Gate(torch.nn.Module): | |
| """Gate module which implements Top2Gating as described in Gshard_. | |
| :: | |
| gate = Top2Gate(model_dim, num_experts) | |
| l_aux, combine_weights, dispatch_mask = gate(input) | |
| .. Gshard_: https://arxiv.org/pdf/2006.16668.pdf | |
| Args: | |
| model_dim (int): | |
| size of model embedding dimension | |
| num_experts (ints): | |
| number of experts in model | |
| """ | |
| wg: torch.nn.Linear | |
| def __init__( | |
| self, | |
| model_dim: int, | |
| num_experts: int, | |
| use_fp32=False, | |
| input_noise_type=None, | |
| capacity_factor=1.0, | |
| moe_eval_capacity_token_fraction=EVAL_CAPACITY_TOKEN_FRACTION, | |
| use_xmoe=False, | |
| ) -> None: | |
| # TODO: merge this to top2gate.py | |
| # | |
| super().__init__() | |
| if not use_xmoe: | |
| self.wg = torch.nn.Linear(model_dim, num_experts, bias=False) | |
| else: | |
| self.wg_reduction = torch.nn.Linear(model_dim, 16, bias=False) | |
| wg = torch.empty(num_experts, 16) | |
| torch.nn.init.orthogonal_(wg, gain=0.32) | |
| self.register_parameter("wg", torch.nn.Parameter(wg)) | |
| self.use_xmoe = use_xmoe | |
| self.use_fp32 = use_fp32 | |
| self.input_noise_type = input_noise_type | |
| self.capacity_factor = capacity_factor | |
| self.moe_eval_capacity_token_fraction = moe_eval_capacity_token_fraction | |
| def forward(self, input, mask=None): # type: ignore | |
| if self.use_xmoe: | |
| input = self.wg_reduction(input) | |
| with torch.no_grad(): | |
| wg_norm = self.wg.norm(p=2.0, dim=1, keepdim=True) | |
| self.wg.mul_(1.5 / wg_norm) | |
| logits = self._cosine(input, self.wg) | |
| logits = self._make_finite(logits) | |
| else: | |
| logits = self.wg(input) | |
| return top1gating( | |
| logits, | |
| mask, | |
| use_fp32=self.use_fp32, | |
| capacity_factor=self.capacity_factor, | |
| eval_mode=not self.training, | |
| moe_eval_capacity_token_fraction=self.moe_eval_capacity_token_fraction, | |
| use_xmoe=self.use_xmoe, | |
| gate_obj=self, | |
| ) | |
| def _make_finite(self, scores): | |
| ok = scores.isfinite() | |
| if not ok.all(): | |
| # NaNs here can break the assignment algorithm | |
| scores[~ok] = scores[ok].min() | |
| return scores | |
| def _get_gating_temperature(self, eps=1e-4): | |
| if self.gating_t.data.item() < eps: | |
| return eps | |
| return self.gating_t | |
| def _cosine(self, mat1, mat2, eps=1e-4): | |
| assert mat1.dim() == 2 | |
| assert mat2.dim() == 2 | |
| # mat1 = F.normalize(mat1, p=2.0, dim=1, eps=eps) | |
| mat2 = F.normalize(mat2.float(), p=2.0, dim=1, eps=eps) | |
| return mat1.float().matmul(mat2.transpose(0, 1)).type_as(mat1) | |
| gumbel_map: Dict[torch.device, Callable] = {} | |
| def gumbel_rsample(shape: Tuple, device: torch.device) -> Tensor: | |
| gumbel = gumbel_map.get(device) | |
| if gumbel is None: | |
| one = torch.tensor(1.0, device=device) | |
| zero = torch.tensor(0.0, device=device) | |
| gumbel = torch.distributions.gumbel.Gumbel(zero, one).rsample # type: ignore | |
| gumbel_map[device] = gumbel | |
| return gumbel(shape) | |
| def one_hot(indices: torch.Tensor, num_classes: int, unsqueeze_indices=False) -> Tensor: | |
| if unsqueeze_indices: | |
| indices = indices.unsqueeze(-1) | |
| assert indices.shape[-1] == 1, "last dimension of indices must be have size 1" | |
| output = torch.zeros(indices.shape[:-1] + (num_classes,), device=indices.device, dtype=indices.dtype) | |
| output.scatter_(len(output.shape) - 1, indices, 1) | |
| return output | |
| def entropy(probs): | |
| logits = torch.distributions.utils.probs_to_logits(probs) | |
| p_log_p = probs * logits | |
| return -p_log_p.sum(-1) | |
| def top2gating( | |
| logits: torch.Tensor, | |
| input_mask: Optional[torch.Tensor] = None, | |
| use_fp32=False, | |
| second_expert_policy="sampling", | |
| normalize_gate_prob_before_dropping=False, | |
| eval_mode=False, | |
| moe_eval_capacity_token_fraction=0.25, | |
| batch_prioritized_routing=False, | |
| ) -> Tuple[Tensor, Tensor, Tensor]: | |
| """Implements Top2Gating on logits.""" | |
| metadata = {} | |
| if use_fp32: | |
| orig_dtype = logits.dtype | |
| logits = logits.float() | |
| gates = F.softmax(logits, dim=1) | |
| metadata["entropy_gating"] = entropy(probs=gates).mean().detach() | |
| # gates has shape of SE | |
| num_tokens = gates.shape[0] | |
| num_experts = gates.shape[1] | |
| if moe_eval_capacity_token_fraction > 0.0 and eval_mode: | |
| capacity = math.ceil(moe_eval_capacity_token_fraction * num_tokens) | |
| else: | |
| # capacity = 2S/E | |
| capacity = 2 * math.ceil(num_tokens / num_experts) | |
| # Create a mask for 1st's expert per token | |
| indices1_s = torch.argmax(gates, dim=1, keepdim=True) | |
| mask1 = one_hot(indices1_s, num_experts) | |
| if second_expert_policy == "sampling": | |
| # Create a mask for 2nd's expert per token using Gumbel-max trick | |
| # https://timvieira.github.io/blog/post/2014/07/31/gumbel-max-trick/ | |
| logits_w_noise = logits + gumbel_rsample(logits.shape, device=logits.device) | |
| else: | |
| logits_w_noise = logits | |
| # Replace top-expert with min value | |
| logits_except1 = logits_w_noise.masked_fill(mask1.bool(), float("-inf")) | |
| indices2_s = torch.argmax(logits_except1, dim=1, keepdim=True) | |
| mask2 = one_hot(indices2_s, num_experts) | |
| gates1_s = (gates * mask1).sum(dim=1) | |
| gates2_s = (gates * mask2).sum(dim=1) | |
| if normalize_gate_prob_before_dropping: | |
| # Normalize gate probabilities | |
| denom_s = gates1_s + gates2_s | |
| # Avoid divide-by-zero | |
| denom_s = torch.clamp(denom_s, min=torch.finfo(denom_s.dtype).eps) | |
| gates1_s = gates1_s / denom_s | |
| gates2_s = gates2_s / denom_s | |
| if second_expert_policy == "random": | |
| sampled = (2 * gates2_s) > torch.rand_like(gates2_s) | |
| mask2 = mask2 * sampled.repeat(num_experts, 1).transpose(1, 0) | |
| # Compute locations in capacity buffer | |
| if input_mask is not None and input_mask.any(): | |
| nonpadding = ~input_mask | |
| mask1 = mask1 * nonpadding.unsqueeze(-1).to(mask1.dtype) | |
| mask2 = mask2 * nonpadding.unsqueeze(-1).to(mask1.dtype) | |
| if batch_prioritized_routing: | |
| # if batch_prioritized_routing: | |
| importance_scores = -1 * gates.max(dim=1)[0] | |
| sorted_mask1 = mask1[importance_scores.argsort(dim=0)] | |
| sorted_cumsum1 = fused_cumsum_sub_one(sorted_mask1) * sorted_mask1 | |
| importance_sorted_locations1 = sorted_cumsum1[importance_scores.argsort(dim=0).argsort(dim=0)] | |
| sorted_mask2 = mask2[importance_scores.argsort(dim=0)] | |
| sorted_cumsum2 = fused_cumsum_sub_one(sorted_mask2) * sorted_mask2 | |
| importance_sorted_locations2 = sorted_cumsum2[importance_scores.argsort(dim=0).argsort(dim=0)] | |
| importance_sorted_locations2 += torch.sum(mask1, dim=0, keepdim=True) | |
| locations1, locations2 = ( | |
| importance_sorted_locations1, | |
| importance_sorted_locations2, | |
| ) | |
| else: | |
| locations1 = fused_cumsum_sub_one(mask1) | |
| locations2 = fused_cumsum_sub_one(mask2) | |
| # Update 2nd's location by accounting for locations of 1st | |
| locations2 += torch.sum(mask1, dim=0, keepdim=True) | |
| # Compute l_aux | |
| me = torch.mean(gates, dim=0) | |
| ce = torch.mean(mask1.to(gates.dtype), dim=0) | |
| l_aux = torch.mean(me * ce) | |
| l_aux = l_aux * num_experts * num_experts | |
| # for logging purposes | |
| metadata["overflow_expert1"] = 100 * torch.sum(mask1 * torch.ge(locations1, capacity)) / torch.sum(mask1) | |
| metadata["overflow_expert2"] = 100 * torch.sum(mask2 * torch.ge(locations2, capacity)) / torch.sum(mask2) | |
| # Remove locations outside capacity from mask | |
| mask1_, mask2_ = mask1, mask2 | |
| mask1 = mask1 * torch.lt(locations1, capacity) | |
| mask2 = mask2 * torch.lt(locations2, capacity) | |
| # for logging (percent of tokens routed to each expert) | |
| expert1_hist = 100 * torch.histc((indices1_s.squeeze() + 1), bins=num_experts, min=1, max=num_experts) / num_tokens | |
| metadata["unused_expert1_count"] = (expert1_hist == 0).sum() | |
| expert1_hist = torch.sort(expert1_hist, dim=0, descending=True).values + torch.finfo(torch.float32).tiny | |
| expert2_hist = 100 * torch.histc((indices2_s.squeeze() + 1), bins=num_experts, min=1, max=num_experts) / num_tokens | |
| metadata["unused_expert2_count"] = (expert2_hist == 0).sum() | |
| expert2_hist = torch.sort(expert2_hist, dim=0, descending=True).values + torch.finfo(torch.float32).tiny | |
| sample_count = max(math.ceil(num_experts * SAMPLE_FRACTION), 1) | |
| metadata["expert1_balance_top"] = expert1_hist[:sample_count].sum() | |
| metadata["expert1_balance_bottom"] = expert1_hist[-sample_count:].sum() | |
| metadata["expert2_balance_top"] = expert2_hist[:sample_count].sum() | |
| metadata["expert2_balance_bottom"] = expert2_hist[-sample_count:].sum() | |
| if not normalize_gate_prob_before_dropping: | |
| # Normalize gate probabilities | |
| gates1_s = (gates * mask1).sum(dim=1) | |
| gates2_s = (gates * mask2).sum(dim=1) | |
| denom_s = gates1_s + gates2_s | |
| # Avoid divide-by-zero | |
| denom_s = torch.clamp(denom_s, min=torch.finfo(denom_s.dtype).eps) | |
| gates1_s /= denom_s | |
| gates2_s /= denom_s | |
| if has_tutel: | |
| locations1_s = torch.sum(locations1 * mask1_, dim=1) | |
| locations2_s = torch.sum(locations2 * mask2_, dim=1) | |
| return ( | |
| l_aux, | |
| metadata, | |
| capacity, | |
| num_experts, | |
| [indices1_s, indices2_s], | |
| [locations1_s, locations2_s], | |
| [gates1_s, gates2_s], | |
| ) | |
| # Store the capacity location for each token | |
| locations1_s = torch.sum(locations1 * mask1, dim=1) | |
| locations2_s = torch.sum(locations2 * mask2, dim=1) | |
| # Calculate combine_weights and dispatch_mask | |
| gates1 = gates1_s.unsqueeze(-1) * mask1.to(gates1_s.dtype) # einsum("s,se->se") | |
| gates2 = gates2_s.unsqueeze(-1) * mask2.to(gates2_s.dtype) # einsum("s,se->se") | |
| locations1_sc = one_hot(locations1_s, num_classes=capacity, unsqueeze_indices=True) | |
| locations2_sc = one_hot(locations2_s, num_classes=capacity, unsqueeze_indices=True) | |
| combine1_sec = torch.bmm( | |
| # einsum("se,sc->sec") | |
| gates1.unsqueeze(-1), | |
| locations1_sc.to(gates1.dtype).unsqueeze(1), | |
| ) | |
| combine2_sec = torch.bmm( | |
| # einsum("se,sc->sec") | |
| gates2.unsqueeze(-1), | |
| locations2_sc.to(gates2.dtype).unsqueeze(1), | |
| ) | |
| combine_weights = combine1_sec + combine2_sec | |
| dispatch_mask = combine_weights.bool() | |
| if use_fp32: | |
| return l_aux, combine_weights.to(orig_dtype), dispatch_mask, metadata | |
| else: | |
| return l_aux, combine_weights, dispatch_mask, metadata | |
| class Top2Gate(torch.nn.Module): | |
| """Gate module which implements Top2Gating as described in Gshard_. | |
| :: | |
| gate = Top2Gate(model_dim, num_experts) | |
| l_aux, combine_weights, dispatch_mask = gate(input) | |
| .. Gshard_: https://arxiv.org/pdf/2006.16668.pdf | |
| Args: | |
| model_dim (int): | |
| size of model embedding dimension | |
| num_experts (ints): | |
| number of experts in model | |
| """ | |
| wg: torch.nn.Linear | |
| def __init__( | |
| self, | |
| model_dim: int, | |
| num_experts: int, | |
| use_fp32=False, | |
| second_expert_policy="sampling", | |
| normalize_gate_prob_before_dropping=False, | |
| moe_eval_capacity_token_fraction=0.25, | |
| batch_prioritized_routing=False, | |
| use_xmoe=False, | |
| ) -> None: | |
| super().__init__() | |
| if not use_xmoe: | |
| self.wg = torch.nn.Linear(model_dim, num_experts, bias=False) | |
| else: | |
| self.wg_reduction = torch.nn.Linear(model_dim, 16, bias=False) | |
| wg = torch.empty(num_experts, 16) | |
| torch.nn.init.orthogonal_(wg, gain=0.32) | |
| self.register_parameter("wg", torch.nn.Parameter(wg)) | |
| self.use_fp32 = use_fp32 | |
| self.second_expert_policy = second_expert_policy | |
| self.normalize_gate_prob_before_dropping = normalize_gate_prob_before_dropping | |
| self.moe_eval_capacity_token_fraction = moe_eval_capacity_token_fraction | |
| self.batch_prioritized_routing = batch_prioritized_routing | |
| self.use_xmoe = use_xmoe | |
| def forward(self, input, mask=None): # type: ignore | |
| if self.use_xmoe: | |
| input = self.wg_reduction(input) | |
| with torch.no_grad(): | |
| wg_norm = self.wg.norm(p=2.0, dim=1, keepdim=True) | |
| self.wg.mul_(1.5 / wg_norm) | |
| logits = self._cosine(input, self.wg) | |
| logits = self._make_finite(logits) | |
| else: | |
| logits = self.wg(input) | |
| return top2gating( | |
| logits, | |
| mask, | |
| use_fp32=self.use_fp32, | |
| second_expert_policy=self.second_expert_policy, | |
| normalize_gate_prob_before_dropping=self.normalize_gate_prob_before_dropping, | |
| eval_mode=not self.training, | |
| moe_eval_capacity_token_fraction=self.moe_eval_capacity_token_fraction, | |
| batch_prioritized_routing=self.batch_prioritized_routing, | |
| ) | |
| def _cosine(self, mat1, mat2, eps=1e-4): | |
| assert mat1.dim() == 2 | |
| assert mat2.dim() == 2 | |
| # mat1 = F.normalize(mat1, p=2.0, dim=1, eps=eps) | |
| mat2 = F.normalize(mat2.float(), p=2.0, dim=1, eps=eps) | |
| return mat1.float().matmul(mat2.transpose(0, 1)).type_as(mat1) | |
| def _make_finite(self, scores): | |
| ok = scores.isfinite() | |
| if not ok.all(): | |
| # NaNs here can break the assignment algorithm | |
| scores[~ok] = scores[ok].min() | |
| return scores | |