Transformers
Safetensors
English
mla
deepseek-moe
mtp
custom-code
tinystories
from-scratch
Eval Results (legacy)
Instructions to use nowordsxiaomu/DeepSeek-Flash-Mini with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use nowordsxiaomu/DeepSeek-Flash-Mini with Transformers:
# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("nowordsxiaomu/DeepSeek-Flash-Mini", device_map="auto") - Notebooks
- Google Colab
- Kaggle
File size: 6,705 Bytes
5e6d9f5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | """DeepSeekMoE:细粒度专家 + 共享专家 + 无辅助损失负载均衡。
三个和普通 MoE 不一样的地方:
1. 细粒度专家(fine-grained):把专家切得更小更多,top-k 也调大。
同样的激活参数量,组合数从 C(8,2) 变成 C(64,8),专家分工能更专一。
2. 共享专家(shared expert):抽出 1 个专家让所有 token 都过。
通用知识(语法、常识)由它承担,路由专家就不必人手一份重复学。
3. 无辅助损失负载均衡(aux-loss-free,V3 的关键改进):
传统做法是加一个负载均衡损失,但它和语言建模目标是打架的,会损伤效果。
V3 改成给每个专家挂一个**不参与梯度**的偏置 b_i,只在 top-k 选择时加上:
选择依据 = s_i + b_i, 聚合权重仍用原始 s_i
每步训练后,谁超载就把谁的 b_i 调小,谁欠载就调大:
b_i += γ * sign(平均负载 - 该专家负载)
于是负载被掰平了,而梯度完全没被污染。
"""
from typing import Tuple
import torch
import torch.nn as nn
import torch.nn.functional as F
from .layers import SwiGLU
class Gate(nn.Module):
"""路由门控:决定每个 token 该交给哪几个专家。"""
def __init__(self, cfg):
super().__init__()
self.dim = cfg.dim
self.n_experts = cfg.n_routed_experts
self.topk = cfg.n_activated_experts
self.n_groups = cfg.n_expert_groups
self.topk_groups = cfg.n_limited_groups
self.score_func = cfg.score_func
self.route_scale = cfg.route_scale
self.aux_alpha = cfg.aux_loss_alpha
self.weight = nn.Parameter(torch.empty(self.n_experts, self.dim))
nn.init.normal_(self.weight, std=0.02)
# 负载均衡偏置:只影响"选谁",不参与反向传播
self.register_buffer("expert_bias", torch.zeros(self.n_experts))
# 统计窗口内每个专家接了多少 token,供 update_bias 使用
self.register_buffer("load_count", torch.zeros(self.n_experts), persistent=False)
self.last_imbalance = 1.0
def forward(self, x: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
"""x: (N, dim) -> (weights (N,topk), indices (N,topk), aux_loss 标量)"""
logits = F.linear(x.float(), self.weight.float())
if self.score_func == "sigmoid":
scores = logits.sigmoid()
else:
scores = logits.softmax(dim=-1)
original = scores
# ---- 选择用的分数 = 原始分数 + 负载偏置 ----
sel = scores + self.expert_bias
# ---- group-limited routing:先选组再选专家,限制跨设备通信量 ----
if self.n_groups > 1:
g = sel.view(-1, self.n_groups, self.n_experts // self.n_groups)
per_group = g.size(-1)
if per_group > 1:
group_score = g.topk(min(2, per_group), dim=-1)[0].sum(dim=-1)
else:
group_score = g.squeeze(-1)
keep = group_score.topk(self.topk_groups, dim=-1)[1] # (N, topk_groups)
gmask = torch.zeros_like(group_score, dtype=torch.bool).scatter_(1, keep, True)
sel = sel.masked_fill(~gmask.unsqueeze(-1).expand_as(g).reshape_as(sel),
torch.finfo(sel.dtype).min)
indices = sel.topk(self.topk, dim=-1)[1] # (N, topk)
weights = original.gather(1, indices)
if self.score_func == "sigmoid":
weights = weights / weights.sum(dim=-1, keepdim=True).clamp_min(1e-9)
weights = weights * self.route_scale
# ---- 统计负载 + 兜底的序列级辅助损失(权重很小)----
onehot = F.one_hot(indices, self.n_experts).sum(dim=1).float() # (N, E)
counts = onehot.sum(dim=0)
with torch.no_grad():
self.load_count += counts.detach()
if self.training and self.aux_alpha > 0:
f = counts / counts.sum().clamp_min(1.0) # 实际负载占比
p = original.mean(dim=0) # 平均路由概率
aux_loss = self.aux_alpha * self.n_experts * (f * p).sum()
else:
aux_loss = x.new_zeros(())
return weights.type_as(x), indices, aux_loss
@torch.no_grad()
def update_bias(self, speed: float):
"""训练循环每步调一次:把超载专家的偏置压低、欠载的抬高。"""
total = self.load_count.sum()
if total <= 0:
return
self.last_imbalance = (self.load_count.max() / total * self.n_experts).item()
target = total / self.n_experts
err = target - self.load_count
self.expert_bias += speed * torch.sign(err)
self.load_count.zero_()
@torch.no_grad()
def load_stats(self) -> dict:
"""负载不均衡度 = 最大负载 / 平均负载。1.0 完全均衡,topk/E 的倒数为最差。"""
total = self.load_count.sum()
if total <= 0:
return {"imbalance": self.last_imbalance}
frac = self.load_count / total
return {"imbalance": (frac.max() * self.n_experts).item()}
class MoE(nn.Module):
def __init__(self, cfg):
super().__init__()
self.dim = cfg.dim
self.n_experts = cfg.n_routed_experts
self.gate = Gate(cfg)
self.experts = nn.ModuleList([SwiGLU(cfg.dim, cfg.moe_inter_dim)
for _ in range(cfg.n_routed_experts)])
self.shared = (SwiGLU(cfg.dim, cfg.moe_inter_dim * cfg.n_shared_experts)
if cfg.n_shared_experts > 0 else None)
def forward(self, x: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
shape = x.shape
x = x.view(-1, self.dim)
weights, indices, aux = self.gate(x)
y = torch.zeros_like(x)
for i, expert in enumerate(self.experts):
token_idx, slot = torch.where(indices == i)
if token_idx.numel() == 0:
continue
y.index_add_(0, token_idx,
expert(x[token_idx]) * weights[token_idx, slot].unsqueeze(-1))
if self.shared is not None:
y = y + self.shared(x)
return y.view(shape), aux
class DenseFFN(nn.Module):
"""前几层用的普通 FFN。V3 的做法:靠前的层先稠密,训练更稳。"""
def __init__(self, cfg):
super().__init__()
self.ffn = SwiGLU(cfg.dim, cfg.dense_inter_dim)
def forward(self, x: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
return self.ffn(x), x.new_zeros(())
|