聊聊我构建SMoE模型的过程

这篇博客详细讲述了从头开始构建一个稀疏混合专家(sparse mixture of experts)语言模型的过程。该项目深受 Andrej Karpathy 的 “makemore” 项目启发,并借鉴了许多可重用的组件。与 makemore 类似,makeMoE 也是一种按字符顺序生成文本的自动语言模型,但它采用了所谓的 “稀疏混合专家” 架构。

文章后面重点介绍了这种架构的核心要素及其实施方法。希望你通过阅读这篇文章并运行仓库中的代码,直观地理解其运作原理。

随着 Mixtral 的发布和有关 Llama 3 可能是一个混合专家型 LLM 的讨论,人们对这种模型架构越来越感兴趣。但在这种 “稀疏混合专家” 语言模型中,很多元素是与传统的 Transformer 模型共享的。虽然看起来简单,但实际上训练稳定性是这些模型面临的一个主要挑战。像本项目这样的小规模、可自行修改的实现,可能有助于快速尝试新的方法。

在此实现中,我对 makemore 架构进行了一些重要修改:

  • 引入了 “稀疏混合专家” 架构,而不是单纯的前馈神经网络。

  • 实现了 “Top-k 门控” 和 “带噪声的 Top-k 门控”。

  • 在初始化方面,虽然这里使用了 Kaiming He 初始化方法,但这个项目的特点是可以灵活更换,例如尝试 Xavier 或 Glorot 初始化方法。

但是,以下方面保持了与 makemore 的一致:

  • 数据集的选择、预处理(如 tokenization)方式,以及 Andrej 最初选择的语言建模任务 —— 生成类似莎士比亚的文本。

  • 因果自注意力(Casusal self attention)的实现方法。

  • 训练循环和推理逻辑的设计。

稀疏混合专家语言模型,正如其名,依赖于一种被称为自注意力的技术来理解语境。在深入探讨混合专家模块的细节之前,我们先来回顾一下自注意力的基础知识。

代码示例展示了自注意力的工作原理和核心思想,尤其是一种叫做比例点积自注意力的常见形式。在这种方式中,查询(query)、键(key)和值(value)这三组数据都源自同一个输入序列。为了保证自动文本生成过程的连贯性,尤其是在只有解码器的模型中,代码中实现了一种掩蔽技术。这种技术非常关键,因为它隐藏了当前字符之后的信息,使模型的注意力只集中在之前的序列部分。这种注意力机制被称为因果自注意力。

值得注意的是,稀疏混合专家模型并不仅限于 Transformer 模型中的解码器部分。实际上,这个领域的许多重要研究,特别是 Shazeer 等人的工作,都是基于 T5 架构的,它包含了 Transformer 模型中的编码器和解码器两个部分。

因果自注意力和多头因果自注意力的代码结构如下:多头自注意力通过并行运用多个注意力头来提高效率,每个头关注嵌入维度的不同部分。多头自注意力不仅提高了学习效率,还因其并行实现的特点提升了模型训练的效率。值得一提的是,为了防止模型过度拟合现象,我在整个实现过程中使用了 dropout 这种正则化技术。


#Causal scaled dot product self-Attention Head  
n\_embd = 64  
n\_head = 4  
n\_layer = 4  
head\_size = 16  
dropout = 0.1  
  
class Head(nn.Module):  
    """ one head of self-attention """  
  
    def \_\_init\_\_(self, head\_size):  
        super().\_\_init\_\_()  
        self.key = nn.Linear(n\_embd, head\_size, bias=False)  
        self.query = nn.Linear(n\_embd, head\_size, bias=False)  
        self.value = nn.Linear(n\_embd, head\_size, bias=False)  
        self.register\_buffer('tril', torch.tril(torch.ones(block\_size, block\_size)))  
  
        self.dropout = nn.Dropout(dropout)  
  
    def forward(self, x):  
        B,T,C = x.shape  
        k = self.key(x)   # (B,T,C)  
        q = self.query(x) # (B,T,C)  
        # compute attention scores ("affinities")  
        wei = q @ k.transpose(-2,-1) \* C\*\*-0.5 # (B, T, C) @ (B, C, T) -> (B, T, T)  
        wei = wei.masked\_fill(self.tril\[:T, :T\] == 0, float('-inf')) # (B, T, T)  
        wei = F.softmax(wei, dim=-1) # (B, T, T)  
        wei = self.dropout(wei)  
        # perform the weighted aggregation of the values  
        v = self.value(x) # (B,T,C)  
        out = wei @ v # (B, T, T) @ (B, T, C) -> (B, T, C)  
        return out


多头自注意力(Multi-head Self Attention)的实现方式如下:


#Multi-Headed Self Attention  
class MultiHeadAttention(nn.Module):  
    """ multiple heads of self-attention in parallel """  
  
    def \_\_init\_\_(self, num\_heads, head\_size):  
        super().\_\_init\_\_()  
        self.heads = nn.ModuleList(\[Head(head\_size) for \_ in range(num\_heads)\])  
        self.proj = nn.Linear(n\_embd, n\_embd)  
        self.dropout = nn.Dropout(dropout)  
  
    def forward(self, x):  
        out = torch.cat(\[h(x) for h in self.heads\], dim=-1)  
        out = self.dropout(self.proj(out))  
        return out  



首先,我们创建一个称为 “专家模块” 的组件,这实际上是一个结构简单的多层感知器(Multi Layer Perceptron)。在稀疏专家混合(Sparse Mixture of Experts,MoE)架构中,虽然每个 Transformer 块中的自注意力机制保持不变,但块的结构有了显著的改变。原先的标准前馈神经网络被多个 “稀疏激活” 的前馈网络所取代,这些网络被称为 “专家”。

所谓的 “稀疏激活” 是指,序列中的每个 Token 只被分配给有限的几个专家处理 —— 通常是一个或两个 —— 而不是全部可用的专家。**这种方式有助于提升训练和推理速度,因为每次前向传递只需要激活少数专家。**然而,所有的专家网络都需要存储在 GPU 内存中,这在参数总量达到数千亿或数万亿时,会带来一些部署上的挑战。


#Expert module  
class Expert(nn.Module):  
    """ An MLP is a simple linear layer followed by a non-linearity i.e. each Expert """  
  
    def \_\_init\_\_(self, n\_embd):  
        super().\_\_init\_\_()  
        self.net = nn.Sequential(  
            nn.Linear(n\_embd, 4 \* n\_embd),  
            nn.ReLU(),  
            nn.Linear(4 \* n\_embd, n\_embd),  
            nn.Dropout(dropout),  
        )  
  
    def forward(self, x):  
        return self.net(x)


通过一个简单的例子来理解 Top-k 门控(Top-k Gating)的直觉:

门控网络,也就是决定哪个专家网络将接收来自多头自注意力的每个 Token 输出的 “路由器”。假设有 4 个专家,而某个 Token 需要被发送到排名前两位的专家那里。首先,我们通过一个线性层把 Token 输入到门控网络。这一层会把输入张量的形状从(2,4,32)转换为(2,4,4),这里的(2,4,32)代表(批量大小,Token 数量,n_embed),其中 n_embed 是输入的通道维度,而(2,4,4)则代表(批量大小,Token 数量,专家网络数量)。接下来,我们会在这些张量的最后一个维度上找到最高的两个值及其对应的索引,这就是我们所说的 “Top-k 选择”。


#Understanding how gating works  
num\_experts = 4  
top\_k=2  
n\_embed=32  
  
  
#Example multi-head attention output for a simple illustrative example, consider n\_embed=32, context\_length=4 and batch\_size=2  
mh\_output = torch.randn(2, 4, n\_embed)  
  
topkgate\_linear = nn.Linear(n\_embed, num\_experts) # nn.Linear(32, 4)  
  
logits = topkgate\_linear(mh\_output)  
top\_k\_logits, top\_k\_indices = logits.topk(top\_k, dim=-1)  # Get top-k experts  
top\_k\_logits, top\_k\_indices  




#output:  
(tensor(\[\[\[ 0.0246, -0.0190\],  
          \[ 0.1991,  0.1513\],  
          \[ 0.9749,  0.7185\],  
          \[ 0.4406, -0.8357\]\],  
   
         \[\[ 0.6206, -0.0503\],  
          \[ 0.8635,  0.3784\],  
          \[ 0.6828,  0.5972\],  
          \[ 0.4743,  0.3420\]\]\], grad\_fn=<TopkBackward0>),  
 tensor(\[\[\[2, 3\],  
          \[2, 1\],  
          \[3, 1\],  
          \[2, 1\]\],  
   
         \[\[0, 2\],  
          \[0, 3\],  
          \[3, 2\],  
          \[3, 0\]\]\]))  



在稀疏门控机制中,我们通过只保留最后一个维度中每个索引对应的前 k 个值来得到输出。其余的部分被填充为负无穷(-inf),然后通过一个 softmax 激活函数进行处理。这个过程会将负无穷的值变为零,同时让前两个最重要的值更加明显,并确保它们的总和为 1。这种总和为 1 的特性对于专家输出的加权是非常重要的。


zeros = torch.full\_like(logits, float('-inf')) #full\_like clones a tensor and fills it with a specified value (like infinity) for masking or calculations.  
sparse\_logits = zeros.scatter(-1, top\_k\_indices, top\_k\_logits)  
sparse\_logits  



gating\_output= F.softmax(sparse\_logits, dim=-1)  
gating\_output  




#ouput  
tensor(\[\[\[0.0000, 0.0000, 0.5109, 0.4891\],  
         \[0.0000, 0.4881, 0.5119, 0.0000\],  
         \[0.0000, 0.4362, 0.0000, 0.5638\],  
         \[0.0000, 0.2182, 0.7818, 0.0000\]\],  
  
        \[\[0.6617, 0.0000, 0.3383, 0.0000\],  
         \[0.6190, 0.0000, 0.0000, 0.3810\],  
         \[0.0000, 0.0000, 0.4786, 0.5214\],  
         \[0.4670, 0.0000, 0.0000, 0.5330\]\]\], grad\_fn=<SoftmaxBackward0>)



接下来,我们将上述代码进行推广和模块化,并添加了带噪声的 Top-k 门控,以实现负载均衡。

# First define the top k router module   
class TopkRouter(nn.Module):  
    def \_\_init\_\_(self, n\_embed, num\_experts, top\_k):  
        super(TopkRouter, self).\_\_init\_\_()  
        self.top\_k = top\_k  
        self.linear =nn.Linear(n\_embed, num\_experts)  
      
    def forward(self, mh\_ouput):  
        # mh\_ouput is the output tensor from multihead self attention block  
        logits = self.linear(mh\_output)  
        top\_k\_logits, indices = logits.topk(self.top\_k, dim=-1)  
        zeros = torch.full\_like(logits, float('-inf'))  
        sparse\_logits = zeros.scatter(-1, indices, top\_k\_logits)  
        router\_output = F.softmax(sparse\_logits, dim=-1)  
        return router\_output, indices  




现在,让我们用一些样本输入来测试这个功能:

#Testing this out:  
num\_experts = 4  
top\_k = 2  
n\_embd = 32  
  
mh\_output = torch.randn(2, 4, n\_embd)  # Example input  
top\_k\_gate = TopkRouter(n\_embd, num\_experts, top\_k)  
gating\_output, indices = top\_k\_gate(mh\_output)  
gating\_output.shape, gating\_output, indices  
#And it works!!  




#output  
(torch.Size(\[2, 4, 4\]),  
 tensor(\[\[\[0.5284, 0.0000, 0.4716, 0.0000\],  
          \[0.0000, 0.4592, 0.0000, 0.5408\],  
          \[0.0000, 0.3529, 0.0000, 0.6471\],  
          \[0.3948, 0.0000, 0.0000, 0.6052\]\],  
   
         \[\[0.0000, 0.5950, 0.4050, 0.0000\],  
          \[0.4456, 0.0000, 0.5544, 0.0000\],  
          \[0.7208, 0.0000, 0.0000, 0.2792\],  
          \[0.0000, 0.0000, 0.5659, 0.4341\]\]\], grad\_fn=<SoftmaxBackward0>),  
 tensor(\[\[\[0, 2\],  
          \[3, 1\],  
          \[3, 1\],  
          \[3, 0\]\],  
   
         \[\[1, 2\],  
          \[2, 0\],  
          \[0, 3\],  
          \[2, 3\]\]\]))  




尽管最近发布的混合模型论文没有提到,我认为在训练 MoE 模型时,带噪声的 Top-k 门控是一个非常重要的工具。我们的目标不是让所有 Token 都被分配给相同的一组专家,而是希望在专家的利用和探索之间达到平衡。为此,在门控网络的输出中添加标准正态分布的噪声,可以帮助实现负载均衡,从而使训练过程更加高效。

#Changing the above to accomodate noisy top-k gating  
class NoisyTopkRouter(nn.Module):  
    def \_\_init\_\_(self, n\_embed, num\_experts, top\_k):  
        super(NoisyTopkRouter, self).\_\_init\_\_()  
        self.top\_k = top\_k  
        #layer for router logits  
        self.topkroute\_linear = nn.Linear(n\_embed, num\_experts)  
        self.noise\_linear =nn.Linear(n\_embed, num\_experts)  
  
      
    def forward(self, mh\_output):  
        # mh\_ouput is the output tensor from multihead self attention block  
        logits = self.topkroute\_linear(mh\_output)  
  
        #Noise logits  
        noise\_logits = self.noise\_linear(mh\_output)  
  
        #Adding scaled unit gaussian noise to the logits  
        noise = torch.randn\_like(logits)\*F.softplus(noise\_logits)  
        noisy\_logits = logits + noise  
  
        top\_k\_logits, indices = noisy\_logits.topk(self.top\_k, dim=-1)  
        zeros = torch.full\_like(noisy\_logits, float('-inf'))  
        sparse\_logits = zeros.scatter(-1, indices, top\_k\_logits)  
        router\_output = F.softmax(sparse\_logits, dim=-1)  
        return router\_output, indices  




现在,让我们再次对这个实现进行测试。

#Testing this out, again:  
num\_experts = 8  
top\_k = 2  
n\_embd = 16  
  
mh\_output = torch.randn(2, 4, n\_embd)  # Example input  
noisy\_top\_k\_gate = NoisyTopkRouter(n\_embd, num\_experts, top\_k)  
gating\_output, indices = noisy\_top\_k\_gate(mh\_output)  
gating\_output.shape, gating\_output, indices  
#It works!!  




#output  
(torch.Size(\[2, 4, 8\]),  
 tensor(\[\[\[0.4181, 0.0000, 0.5819, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000\],  
          \[0.4693, 0.5307, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000\],  
          \[0.0000, 0.4985, 0.5015, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000\],  
          \[0.0000, 0.0000, 0.0000, 0.2641, 0.0000, 0.7359, 0.0000, 0.0000\]\],  
   
         \[\[0.0000, 0.0000, 0.0000, 0.6301, 0.0000, 0.3699, 0.0000, 0.0000\],  
          \[0.0000, 0.0000, 0.0000, 0.4766, 0.0000, 0.0000, 0.0000, 0.5234\],  
          \[0.0000, 0.0000, 0.0000, 0.6815, 0.0000, 0.0000, 0.3185, 0.0000\],  
          \[0.4482, 0.5518, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000\]\]\],  
        grad\_fn=<SoftmaxBackward0>),  
 tensor(\[\[\[2, 0\],  
          \[1, 0\],  
          \[2, 1\],  
          \[5, 3\]\],  
   
         \[\[3, 5\],  
          \[7, 3\],  
          \[3, 6\],  
          \[1, 0\]\]\]))  




**创建一个稀疏专家混合模块的过程主要涉及门控网络的输出。**在获取这些结果之后,我们会将前 k 个专家的输出与对应的顶部 k 个值进行选择性相乘。这种选择性的相乘形成了一个加权总和,这就是 SparseMoe 模块的输出。**这个过程中的关键挑战是避免不必要的乘法运算。**重要的是仅对那些顶部的 k 个专家进行前向计算,然后计算这个加权和。如果对每个专家都进行前向计算,那么使用稀疏 MoE 的目的就会失效,因为它将不再具有稀疏性。

class SparseMoE(nn.Module):  
    def \_\_init\_\_(self, n\_embed, num\_experts, top\_k):  
        super(SparseMoE, self).\_\_init\_\_()  
        self.router = NoisyTopkRouter(n\_embed, num\_experts, top\_k)  
        self.experts = nn.ModuleList(\[Expert(n\_embed) for \_ in range(num\_experts)\])  
        self.top\_k = top\_k  
  
    def forward(self, x):  
        gating\_output, indices = self.router(x)  
        final\_output = torch.zeros\_like(x)  
  
        # Reshape inputs for batch processing  
        flat\_x = x.view(-1, x.size(-1))  
        flat\_gating\_output = gating\_output.view(-1, gating\_output.size(-1))  
  
        # Process each expert in parallel  
        for i, expert in enumerate(self.experts):  
            # Create a mask for the inputs where the current expert is in top-k  
            expert\_mask = (indices == i).any(dim=-1)  
            flat\_mask = expert\_mask.view(-1)  
  
            if flat\_mask.any():  
                expert\_input = flat\_x\[flat\_mask\]  
                expert\_output = expert(expert\_input)  
  
                # Extract and apply gating scores  
                gating\_scores = flat\_gating\_output\[flat\_mask, i\].unsqueeze(1)  
                weighted\_output = expert\_output \* gating\_scores  
  
                # Update final output  
                # We need to scatter\_add the weighted outputs to their original positions in the batch  
                final\_output.masked\_scatter\_(expert\_mask.unsqueeze(-1), weighted\_output)  
  
        return final\_output.view\_as(x)



为了验证上述实现是否有效,使用样本输入进行测试是一个很好的方法。实际运行以下代码后,我们可以看到它确实有效!

import torch  
import torch.nn as nn  
  
#Let's test this out  
num\_experts = 8  
top\_k = 2  
n\_embd = 16  
dropout=0.1  
  
mh\_output = torch.randn(4, 8, n\_embd)  # Example multi-head attention output  
sparse\_moe = SparseMoE(n\_embd, num\_experts, top\_k)  
final\_output = sparse\_moe(mh\_output)  
print("Shape of the final output:", final\_output.shape)  




Shape of the final output: torch.Size(\[4, 8, 16\])  




强调一点,从路由器 / 门控网络得到的 top_k 专家的输出值的大小同样非常关键。这些 top_k 索引决定了哪些专家被激活,并且在这些 top_k 维度中的值的大小决定了它们的权重分配。这种加权求和的思想在下面的图中得到了更详细的展示。

多头自注意力和稀疏专家混合被整合,形成了一个稀疏专家混合的 Transformer 块。就像标准的 Transformer 块一样,我们添加了跳过连接(skip connections)来确保训练的稳定性,防止像梯度消失这样的问题发生。此外,还采用了层归一化(layer normalization),以进一步稳定学习过程。

#Create a self attention + mixture of experts block, that may be repeated several number of times   
class Block(nn.Module):  
    """ Mixture of Experts Transformer block: communication followed by computation (multi-head self attention + SparseMoE) """  
  
    def \_\_init\_\_(self, n\_embed, n\_head, num\_experts, top\_k):  
        # n\_embed: embedding dimension, n\_head: the number of heads we'd like  
        super().\_\_init\_\_()  
        head\_size = n\_embed // n\_head  
        self.sa = MultiHeadAttention(n\_head, head\_size)  
        self.smoe = SparseMoE(n\_embed, num\_experts, top\_k)  
        self.ln1 = nn.LayerNorm(n\_embed)  
        self.ln2 = nn.LayerNorm(n\_embed)  
  
    def forward(self, x):  
        x = x + self.sa(self.ln1(x))  
        x = x + self.smoe(self.ln2(x))  
        return x  




最后,我们将这些内容整合起来,创建了一个稀疏专家混合语言模型。

class SparseMoELanguageModel(nn.Module):  
  
    def \_\_init\_\_(self):  
        super().\_\_init\_\_()  
        # each token directly reads off the logits for the next token from a lookup table  
        self.token\_embedding\_table = nn.Embedding(vocab\_size, n\_embed)  
        self.position\_embedding\_table = nn.Embedding(block\_size, n\_embed)  
        self.blocks = nn.Sequential(\*\[Block(n\_embed, n\_head=n\_head, num\_experts=num\_experts,top\_k=top\_k) for \_ in range(n\_layer)\])  
        self.ln\_f = nn.LayerNorm(n\_embed) # final layer norm  
        self.lm\_head = nn.Linear(n\_embed, vocab\_size)  
  
    def forward(self, idx, targets=None):  
        B, T = idx.shape  
  
        # idx and targets are both (B,T) tensor of integers  
        tok\_emb = self.token\_embedding\_table(idx) # (B,T,C)  
        pos\_emb = self.position\_embedding\_table(torch.arange(T, device=device)) # (T,C)  
        x = tok\_emb + pos\_emb # (B,T,C)  
        x = self.blocks(x) # (B,T,C)  
        x = self.ln\_f(x) # (B,T,C)  
        logits = self.lm\_head(x) # (B,T,vocab\_size)  
  
        if targets is None:  
            loss = None  
        else:  
            B, T, C = logits.shape  
            logits = logits.view(B\*T, C)  
            targets = targets.view(B\*T)  
            loss = F.cross\_entropy(logits, targets)  
  
        return logits, loss  
  
    def generate(self, idx, max\_new\_tokens):  
        # idx is (B, T) array of indices in the current context  
        for \_ in range(max\_new\_tokens):  
            # crop idx to the last block\_size tokens  
            idx\_cond = idx\[:, -block\_size:\]  
            # get the predictions  
            logits, loss = self(idx\_cond)  
            # focus only on the last time step  
            logits = logits\[:, -1, :\] # becomes (B, C)  
            # apply softmax to get probabilities  
            probs = F.softmax(logits, dim=-1) # (B, C)  
            # sample from the distribution  
            idx\_next = torch.multinomial(probs, num\_samples=1) # (B, 1)  
            # append sampled index to the running sequence  
            idx = torch.cat((idx, idx\_next), dim=1) # (B, T+1)  
        return idx



在深度神经网络的有效训练中,初始化是一个关键步骤。这里我们使用了 Kaiming He 初始化方法,因为专家网络中使用了 ReLU 激活函数。你也可以尝试使用在 Transformer 中更为常见的 Glorot 初始化。Jeremy Howard 的 Fastai 第二部分提供了一堂很棒的课程,从头实现了这些方法:https://course.fast.ai/Lessons/lesson17.html。文献中提到,Glorot 初始化通常用于 Transformer 模型,这可能是一个提升模型性能的机会。

def kaiming\_init\_weights(m):  
    if isinstance (m, (nn.Linear)):   
        init.kaiming\_normal\_(m.weight)  
  
model = SparseMoELanguageModel()  
model.apply(kaiming\_init\_weights)



我使用了 mlflow 来跟踪和记录训练过程中的重要指标和超参数。我展示的训练循环中包含了这部分代码。如果你不想使用 mlflow,makeMoE GitHub 仓库中的笔记本中也提供了不包含 MLFlow 的代码。我个人发现,特别是在进行实验时,使用 mlflow 跟踪参数和指标非常方便。

#Using MLFlow  
m = model.to(device)  
# print the number of parameters in the model  
print(sum(p.numel() for p in m.parameters())/1e6, 'M parameters')  
  
# create a PyTorch optimizer  
optimizer = torch.optim.AdamW(model.parameters(), lr=learning\_rate)  
#mlflow.set\_experiment("makeMoE")  
with mlflow.start\_run():  
    #If you use mlflow.autolog() this will be automatically logged. I chose to explicitly log here for completeness  
    params = {"batch\_size": batch\_size , "block\_size" : block\_size, "max\_iters": max\_iters, "eval\_interval": eval\_interval,  
              "learning\_rate": learning\_rate, "device": device, "eval\_iters": eval\_iters, "dropout" : dropout, "num\_experts": num\_experts, "top\_k": top\_k }  
    mlflow.log\_params(params)  
    for iter in range(max\_iters):  
  
        # every once in a while evaluate the loss on train and val sets  
        if iter % eval\_interval == 0 or iter == max\_iters - 1:  
            losses = estimate\_loss()  
            print(f"step {iter}: train loss {losses\['train'\]:.4f}, val loss {losses\['val'\]:.4f}")  
            metrics = {"train\_loss": losses\['train'\], "val\_loss": losses\['val'\]}  
            mlflow.log\_metrics(metrics, step=iter)  
  
  
        # sample a batch of data  
        xb, yb = get\_batch('train')  
  
        # evaluate the loss  
        logits, loss = model(xb, yb)  
        optimizer.zero\_grad(set\_to\_none=True)  
        loss.backward()  
        optimizer.step()  




8.996545 M parameters  
step 0: train loss 5.3223, val loss 5.3166  
step 100: train loss 2.7351, val loss 2.7429  
step 200: train loss 2.5125, val loss 2.5233  
.  
.  
.  
  
step 4999: train loss 1.5712, val loss 1.7508



记录训练和验证损失可以帮助我们更好地了解训练进展。图表显示,在大约 4500 步时,当验证损失略有上升时,我本应该停止训练。

现在,我们可以使用这个模型逐个字符地生成文本,采用的是自回归的方式。对于一个稀疏激活的约 900 万参数模型来说,效果已经相当不错了。

# generate from the model. Not great. Not too bad either  
context = torch.zeros((1, 1), dtype=torch.long, device=device)  
print(decode(m.generate(context, max\_new\_tokens=2000)\[0\].tolist()))  




DUKE VINCENVENTIO:  
If it ever fecond he town sue kigh now,  
That thou wold'st is steen 't.  
  
SIMNA:  
Angent her; no, my a born Yorthort,  
Romeoos soun and lawf to your sawe with ch a woft ttastly defy,  
To declay the soul art; and meart smad.  
  
CORPIOLLANUS:  
Which I cannot shall do from by born und ot cold warrike,  
What king we best anone wrave's going of heard and good  
Thus playvage; you have wold the grace.  
...  




我希望这个解释有助于你理解稀疏专家混合模型的架构及其组合方式。

整个代码是在 Databricks 平台上,使用单个 A100 显卡开发的。如果你在 Databricks 上运行这个模型,你可以在任何大型 GPU 集群上进行扩展,选择你喜欢的云服务提供商。我选择使用 MLFlow(Databricks 中预安装,也可以在其他地方通过 pip 安装),因为它能方便地跟踪和记录所有必要的指标。当然,使用 MLFlow 完全是可选的。

请注意,这个实现的重点是在于可读性和可修改性,而非最高性能,因此还有很多改进的空间。

基于此,你可以尝试以下几点:

  • 提高专家混合模块的效率。我认为在正确专家的稀疏激活方面可以做出显著改进。

  • 尝试不同的神经网络初始化策略。我提到的 Fastai 第二部分是一个很好的资源。

  • 尝试从字符级别转换到子词标记化。

  • 对专家数量和 top_k(每个 Token 激活的专家数量)进行贝叶斯超参数搜索,这可以归类为神经网络架构搜索。

  • 专家容量在这里没有讨论或实现,但探索这一点是非常有价值的。
    如何学习AI大模型?

我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。

我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

在这里插入图片描述

第一阶段: 从大模型系统设计入手,讲解大模型的主要方法;

第二阶段: 在通过大模型提示词工程从Prompts角度入手更好发挥模型的作用;

第三阶段: 大模型平台应用开发借助阿里云PAI平台构建电商领域虚拟试衣系统;

第四阶段: 大模型知识库应用开发以LangChain框架为例,构建物流行业咨询智能问答系统;

第五阶段: 大模型微调开发借助以大健康、新零售、新媒体领域构建适合当前领域大模型;

第六阶段: 以SD多模态大模型为主,搭建了文生图小程序案例;

第七阶段: 以大模型平台应用与开发为主,通过星火大模型,文心大模型等成熟大模型构建大模型行业应用。

在这里插入图片描述

👉学会后的收获:👈
• 基于大模型全栈工程实现(前端、后端、产品经理、设计、数据分析等),通过这门课可获得不同能力;

• 能够利用大模型解决相关实际项目需求: 大数据时代,越来越多的企业和机构需要处理海量数据,利用大模型技术可以更好地处理这些数据,提高数据分析和决策的准确性。因此,掌握大模型应用开发技能,可以让程序员更好地应对实际项目需求;

• 基于大模型和企业数据AI应用开发,实现大模型理论、掌握GPU算力、硬件、LangChain开发框架和项目实战技能, 学会Fine-tuning垂直训练大模型(数据准备、数据蒸馏、大模型部署)一站式掌握;

• 能够完成时下热门大模型垂直领域模型训练能力,提高程序员的编码能力: 大模型应用开发需要掌握机器学习算法、深度学习框架等技术,这些技术的掌握可以提高程序员的编码能力和分析能力,让程序员更加熟练地编写高质量的代码。

在这里插入图片描述

1.AI大模型学习路线图
2.100套AI大模型商业化落地方案
3.100集大模型视频教程
4.200本大模型PDF书籍
5.LLM面试题合集
6.AI产品经理资源合集

👉获取方式:
😝有需要的小伙伴,可以保存图片到wx扫描二v码免费领取【保证100%免费】🆓

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/723999.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AI与业务的结合 | 使用机器学习预测客户反应,轻松实现市场营销策略优化

01、案例说明 在实际的工作中&#xff0c;除了数据本身需要处理之外&#xff0c;同时也需要对所建立模型进行检验与了解。所以在这个过程之中我们学习2个比较进阶的功能&#xff1a;一个是关于决定属性和结果之间的关系从而判断哪些是更重要的属性&#xff1b;另外一部分则是在…

【CPP】直接选择排序、堆排序

目录 1.选择排序1.1简介1.2代码1.3分析 2.堆排序2.1简介2.2代码2.3分析 1.选择排序 1.1简介 思路&#xff1a;遍历一遍&#xff0c;选出最大值和最小值的下标&#xff0c;然后与第一个和最后一个数字交换位置。 1.2代码 1.3分析 最好复杂度&#xff1a;O(N^2) 最差复杂度&am…

Sui的Fastcrypto加密库刷新速度记录

Sui使用的加密库Fastcrypto打破了许多速度记录&#xff0c;Mysten Labs在基准测试和安全分析中的工作修复了许多安全漏洞&#xff0c;同时通过识别新的优化技巧为创新开辟了道路。 最近在伦敦帝国理工学院举行的国际性能工程会议&#xff08;ICPE&#xff09;基准测试研讨会上…

编译原理:代替LR的MP:2.遇到的问题

用指针加速 MP是multi-pass&#xff0c;多遍分析法&#xff0c;它是从“先乘除后加减”中得来的灵感。在实践中&#xff0c;发现C语言优先级有15级&#xff0c;如果将源代码处理15遍&#xff0c;每一遍都从头开始找&#xff0c;势必很慢。所以&#xff0c;有了用指针加速的想法…

开发者配置项、开发者选项自定义

devOptions.vue源码 <!-- 开发者选项 &#xff08;CtrlAltShiftD&#xff09;--> <template><div :class"$options.name" v-if"visible"><el-dialog:custom-class"sg-el-dialog":append-to-body"true":close-on…

发力采销,京东的“用户关系学”

作者 | 曾响铃 文 | 响铃说 40多岁打扮精致的城市女性&#xff0c;在西藏那曲的偏远农村&#xff0c;坐着藏民的摩托车&#xff0c;行驶在悬崖边的烂泥路上&#xff0c;只因为受顾客的“委托”&#xff0c;要寻找最原生态的藏区某款产品。 30多岁的憨厚中年男性&#xff0c;…

【吊打面试官系列-Mysql面试题】SQL 语言包括哪几部分?每部分都有哪些操作关键字?

大家好&#xff0c;我是锋哥。今天分享关于 【SQL 语言包括哪几部分&#xff1f;每部分都有哪些操作关键字&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; SQL 语言包括哪几部分&#xff1f;每部分都有哪些操作关键字&#xff1f; SQL 语言包括数据定义(DDL)、…

[创业之路-117] :制造业企业的必备管理神器-ERP-是什么?企业经营管理与EPR的主要功能模块与全流程

目录 一、什么是EPR 1.1 跨企业的供应链思想 1.2 EPR的概念&#xff1a;企业资源管理计划 1.2.1 助力企业管理各种资源&#xff1a;人、财、物&#xff08;机、料、法、环&#xff09; 1.2.2 助力企业有效管理 1.2.3 效率的提升 1.4 应用领域 二、ERP的功能模块 三、E…

ROS2学习笔记三:话题Service

目录 前言 1 话题简介 2 常用指令 3 RCLCPP实现实现话题 3.1 创建工作空间 3.2 代码编写 3.2.1 发布端编写 3.2.2 发布端编写 前言 Service是ROS 2提供的一种通信机制&#xff0c;用于在不同节点之间进行请求和响应。 Service允许一个节点向另一个节点发送请求&#…

CSS实现文字上下滚动、间歇滚动和无限滚动

目录 1、连续滚动2、间歇性向上滚动3、任意个数向上滚动 本文主要记录了如何实现文字上下滚动效果&#xff0c;实现主要就是用到了css3的两个属性&#xff1a; framekeys和 animation 1、连续滚动 <div class"scroll-continuous"><div class"content…

计算机专业毕设-在线商城系统

1 项目介绍 在线商城系统&#xff0c;后端java语言&#xff0c;springboot&#xff0c;SSM框架。前端thymeleaf&#xff0c;前后端不分离。本项目已经隐去作者信息&#xff0c;所有代码文件均没有创建人和创建时间&#xff0c;可以放心使用。 系统用户分为两类&#xff0c;管理…

Shopee菲律宾本土店允许中途无理由退货,如何应对退货后库存混乱问题?

Shopee菲律宾本土店最近实施了一项新政策&#xff0c;自2024年6月10日起&#xff0c;允许买家在商品仍在运输途中申请退货与退款&#xff0c;此即“在途退货/退款”功能&#xff0c;主要的目的是为了提升买家的购物体验&#xff0c;增强市场竞争力。 图源&#xff1a;Shopee菲律…

关于Panabit在资产平台中类型划分问题

现场同事问了一个问题&#xff1a;Panabit能不能当做CentOS接入&#xff1f; 我第一反应是&#xff1a;Panabit是个什么鬼&#xff1f;为啥要混编接入&#xff1f;后期维护都是事啊。所以&#xff0c;我就想回答&#xff1a;不能&#xff01; 但是&#xff0c;最好要给出一个…

立讯精密:“果链一哥”怎么摆脱依赖症

AI手机创新赋能&#xff0c;隔岸苹果股价走出历史新高&#xff0c;消费电子有望迎来复苏&#xff1f; 这里我们聊聊苹果产业链代工龙头——立讯精密 作为早早入场的代工企业&#xff0c;立讯精密曾经吃足“果链”红利&#xff0c;如今摆在它面前的是增长、毛利、安全等难题。 …

Linux-PXE批量安装

一、部署 PXE 远程安装服务 在大规模的 Linux 应用环境中&#xff0c;如 Web 群集、分布式计算等&#xff0c;服务器往往并不配备光驱设备&#xff0c;在这种情况下&#xff0c;如何为数十乃至上百台服务器裸机快速安装系统呢&#xff1f;传统的 USB光驱、移动硬盘等安装方法显…

深度解析“科技信贷”:构建科技支行的五维模型

科技信贷是指金融机构为支持科技创新、技术改造和设备更新等领域提供的专项信贷服务&#xff0c;旨在促进科技企业的发展和技术的进步。科技信贷在推动科技企业和创新项目发展方面具有重要作用&#xff0c;其特点在于提供定制化的金融支持&#xff0c;以满足科技创新链条中的融…

【C语言】扫雷游戏

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f4a5;&#x1f4a5;个人主页&#xff1a;奋斗的小羊 &#x1f4a5;&#x1f4a5;所属专栏&#xff1a;C语言 &#x1f680;本系列文章为个人学习…

深信服科技:2023网络钓鱼趋势分析报告

随着互联网的快速发展和广泛应用&#xff0c;网络钓鱼活动带来的安全隐患愈演愈烈。因应威胁发展&#xff0c;我 们编撰了此份分析报告&#xff0c;旨在全面了解其发展态势&#xff0c;并提醒相关部门、企业和公众加强防范。 在本报告中&#xff0c;我们将详细梳理网络钓鱼的近…

成都爱尔周进院长提醒毕业生摘镜,术式如何挑

高考完迎来一个悠长假期&#xff0c;考后放松的同时&#xff0c;也有不少同学开始“准备”。 为奔赴梦想&#xff0c;为了理想的专业和学校&#xff0c;不少人决定摘镜。 不少专业有视力要求&#xff0c;且不同专业方向的要求各有不同。我们先来看看有视力要求的专业有哪些&am…

macOS聚集搜索功能开启与关闭

按下command空格弹出 使用搜索 关闭搜索 sudo mdutil -a -i off 启用搜索 sudo mdutil -a -i on