Transformer、PyTorch、自然语言处理·

从零开始打造一个简单 Transformer 语言模型 [译]

本文将手把手教你如何用 PyTorch 搭建一个基础的 Transformer 语言模型。我们会把每个部分拆开讲清楚,让你不仅能看懂它怎么工作,还能在 Google Colab 上自己动手实现一个,哪怕你是编程或 AI 的新手也没关系!

嗨,大家好!今天我们要一起动手,用 PyTorch 这个强大的工具,打造一个简单但完整的 Transformer 语言模型。别担心,我会一步步拆解每个部分,详细解释它的作用和原理。到最后,你不仅能搞懂 Transformer 是怎么回事,还能在 Google Colab 这个免费的在线平台上自己跑一个模型——就算你是第一次接触编程或者 AI,也没问题!

Transformer 是什么?简单聊聊它的背景

Transformer 是一种特别厉害的工具,专门用来处理语言,比如翻译、生成文章等。它在 2017 年由一篇名叫「Attention Is All You Need」的论文提出后,迅速成了自然语言处理(简称 NLP)的明星。像 GPT(生成对话的模型)还有 BERT(理解文本的模型)这些大名鼎鼎的家伙,都是基于 Transformer 构建的。

它的核心“魔法”在于一个叫「注意力机制」的东西。简单来说,这个机制能让模型在处理一句话时,聪明地“关注”到最重要的词,而不是一视同仁地看待每个词。比如在“我喜欢吃苹果”这句话里,模型可能会特别注意“喜欢”和“苹果”,因为它们决定了句子的意思。

先准备好工具:设置 Google Colab 环境

在我们写代码之前,得先把“工作台”搭好。我们用 Google Colab——一个免费的在线工具,能让你在浏览器里写代码,还自带 PyTorch,不用自己装软件。操作很简单:

  1. 打开浏览器,进入 Google Colab
  2. 点击“新建笔记本”,就像新建一个 Word 文档一样
  3. PyTorch 已经预装好了,但如果你需要其他工具(比如 numpy),可以运行下面这行命令:
Bash
# 如果需要额外安装工具,运行这行代码就行 #
!pip install torch numpy

好了,环境搭好了!接下来,我们一步步把 Transformer 模型搭起来。

第一步:准备好编程“零件”——导入库

就像盖房子需要砖头和水泥,我们写模型也需要一些基础工具。这些工具藏在 Python 的“库”里,我们先把它们“拿出来”:

Python
import math
import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np
from torch.utils.data import Dataset, DataLoader
  • math:数学计算的小帮手,比如开平方啥的
  • torch:PyTorch 的核心库,专门用来造神经网络
  • torch.nn(简称 nn):神经网络的“零件箱”,里面有各种现成的模块
  • torch.nn.functional(简称 F):一些方便的函数,比如激活函数
  • numpy:处理数字和数组的好工具
  • DatasetDataLoader:帮我们整理数据,喂给模型用

这些“零件”齐了,我们就可以开始组装模型了。

第二步:打造核心部件——多头注意力机制

Transformer 的“心脏”就是「多头注意力机制」。这个名字听起来有点复杂,但其实不难理解。想象你在读一本书,有些词(比如主角的名字)比其他词更重要,注意力机制就是让模型学会“挑重点”。

“多头”是什么意思呢?就是模型可以同时从不同角度看问题。比如,一个“头”关注句子的主语和动词,另一个“头”关注修饰词和名词。这样,模型就能更全面地理解句子。我们来写代码实现它:

Python
class MultiHeadAttention(nn.Module):
    def __init__(self, d_model, num_heads):
        super(MultiHeadAttention, self).__init__()
        assert d_model % num_heads == 0, "d_model 必须能被 num_heads 整除"
        
        self.d_model = d_model  # 嵌入向量的维度,比如每个词用多长的数字表示
        self.num_heads = num_heads  # 有几个“头”来分担注意力
        self.d_k = d_model // num_heads  # 每个头的维度
        
        # 为查询(Q)、键(K)、值(V)准备线性变换工具
        self.w_q = nn.Linear(d_model, d_model)
        self.w_k = nn.Linear(d_model, d_model)
        self.w_v = nn.Linear(d_model, d_model)
        self.w_o = nn.Linear(d_model, d_model)  # 最后的输出层

这段代码在干什么呢?我们先初始化一个类:

  • d_model 是词向量的长度(比如 512)
  • num_heads 是注意力的“头数”(比如 8)
  • d_k 是每个头负责的维度(512 ÷ 8 = 64)
  • w_qw_kw_v 是三个线性层,把输入变成“查询”、“键”和“值”(简称 Q、K、V),这是注意力机制的关键概念
  • w_o 是把结果整合输出的层

接下来,我们要把输入分成多个“头”:

Python
    def split_heads(self, x, batch_size):
        # 把输入从 (batch_size, seq_len, d_model) 变成 (batch_size, num_heads, seq_len, d_k)
        x = x.view(batch_size, -1, self.num_heads, self.d_k)
        return x.permute(0, 2, 1, 3)  # 调整顺序,方便计算

这里我们把输入“切块”,让每个“头”都能独立处理一部分信息。比如原来是个 512 维的向量,分成 8 个头后,每个头看 64 维。

现在进入核心计算部分:

Python
    def forward(self, q, k, v, mask=None):
        batch_size = q.size(0)
        
        # 把 Q、K、V 分成多个头
        q = self.split_heads(self.w_q(q), batch_size)  # 输出:(batch_size, num_heads, seq_len_q, d_k)
        k = self.split_heads(self.w_k(k), batch_size)
        v = self.split_heads(self.w_v(v), batch_size)
        
        # 计算注意力分数(“缩放点积注意力”)
        scores = torch.matmul(q, k.transpose(-2, -1))  # 形状:(batch_size, num_heads, seq_len_q, seq_len_k)
        scores = scores / math.sqrt(self.d_k)  # 缩放一下,防止数字太大

这部分是注意力机制的“灵魂”:

  1. 把输入变成 Q、K、V 三组向量
  2. 用 Q 和 K 做点积(torch.matmul),算出每个词对其他词的“重要性分数”
  3. 除以 sqrt(d_k) 是为了让数字稳定,不然太大会影响后续计算
Python
        # 如果有掩码,就用上(比如不想让模型偷看未来的词)
        if mask is not None:
            scores = scores.masked_fill(mask == 0, -1e9)
        
        # 用 softmax 把分数变成概率
        attn_weights = F.softmax(scores, dim=-1)
        
        # 用概率“加权”值 V
        context = torch.matmul(attn_weights, v)  # 输出:(batch_size, num_heads, seq_len_q, d_k)

这里:

  1. 如果有 mask(掩码),就把不该看的地方设成一个超级小的数(-1e9),这样 softmax 后几乎是 0
  2. softmax 把分数变成概率,加起来等于 1
  3. 用这些概率去“加权” V,得出每个位置的“关注结果”
Python
        # 把多个头的输出拼回去
        context = context.permute(0, 2, 1, 3).contiguous()
        context = context.view(batch_size, -1, self.d_model)
        
        # 最后通过一个线性层输出
        output = self.w_o(context)
        
        return output

最后:

  1. 把多个头的结果重新拼成一个完整的向量
  2. 通过 w_o 输出最终结果

这个多头注意力机制就像模型的“眼睛”,能同时从不同角度看输入,找出最重要的信息。

第三步:加个“加工厂”——逐位置前馈网络

注意力机制算完后,我们需要一个“加工厂”来进一步处理信息。这个部分叫「逐位置前馈网络」,它会对序列里的每个位置(比如每个词)单独处理一遍。

Python
class PositionwiseFeedForward(nn.Module):
    def __init__(self, d_model, d_ff):
        super(PositionwiseFeedForward, self).__init__()
        self.fc1 = nn.Linear(d_model, d_ff)  # 第一层
        self.fc2 = nn.Linear(d_ff, d_model)  # 第二层
        
    def forward(self, x):
        return self.fc2(F.relu(self.fc1(x)))  # 先放大,再缩小,中间加个激活

这个网络很简单:

  • d_model 是输入的维度(比如 512)
  • d_ff 是中间层的维度(通常更大,比如 2048)
  • 输入经过 fc1 变“大”,用 relu 激活(让负数变成 0),再通过 fc2 变回原来大小

它就像一个“放大镜”,先把信息“放大”看细节,再“缩小”整合输出。

第四步:给词加上“位置标签”——位置编码

Transformer 有个小问题:它不像人,能自然看出词的顺序。为了解决这个,我们得给每个词加个“位置标签”,告诉模型“这是第几个词”。这就靠「位置编码」来实现。

Python
class PositionalEncoding(nn.Module):
    def __init__(self, d_model, max_seq_length=5000):
        super(PositionalEncoding, self).__init__()
        
        # 创建一个位置编码表
        pe = torch.zeros(max_seq_length, d_model)
        position = torch.arange(0, max_seq_length, dtype=torch.float).unsqueeze(1)
        div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))
        
        pe[:, 0::2] = torch.sin(position * div_term)  # 偶数位置用正弦
        pe[:, 1::2] = torch.cos(position * div_term)  # 奇数位置用余弦
        
        # 注册为缓冲区(不会被训练更新)
        self.register_buffer('pe', pe.unsqueeze(0))
        
    def forward(self, x):
        # 把位置信息加到输入上
        return x + self.pe[:, :x.size(1)]

这里用了点数学“魔法”:

  • 用正弦(sin)和余弦(cos)函数生成一堆数字,每个位置的数字都不一样
  • 偶数维度用正弦,奇数维度用余弦,这样每个位置的“标签”都有独特模式
  • 这些标签直接加到词向量上,模型就能知道哪个词在前面,哪个在后面

第五步:组装“信息加工站”——编码器层

现在,我们把注意力机制和前馈网络装进一个“加工站”,叫「编码器层」。它会处理输入的句子,把原始信息变成更有用的形式。

Python
class EncoderLayer(nn.Module):
    def __init__(self, d_model, num_heads, d_ff, dropout=0.1):
        super(EncoderLayer, self).__init__()
        
        self.self_attn = MultiHeadAttention(d_model, num_heads)  # 自注意力
        self.feed_forward = PositionwiseFeedForward(d_model, d_ff)  # 前馈网络
        
        self.norm1 = nn.LayerNorm(d_model)  # 归一化
        self.norm2 = nn.LayerNorm(d_model)
        
        self.dropout1 = nn.Dropout(dropout)  # 随机丢弃,防止过拟合
        self.dropout2 = nn.Dropout(dropout)

这里我们准备了:

  • 自注意力模块(看句子内部的关系)
  • 前馈网络(加工每个位置的信息)
  • 两个归一化层(让数字稳定)
  • 两个 dropout(随机丢掉一些数据,增强模型鲁棒性)
Python
    def forward(self, x, mask=None):
        # 自注意力 + 残差连接 + 归一化
        attn_output = self.self_attn(x, x, x, mask)
        x = self.norm1(x + self.dropout1(attn_output))
        
        # 前馈网络 + 残差连接 + 归一化
        ff_output = self.feed_forward(x)
        x = self.norm2(x + self.dropout2(ff_output))
        
        return x

处理流程是:

  1. 用自注意力看一遍输入(Q、K、V 都是同一个 x)
  2. 加个“残差连接”(把原始输入加回来,防止信息丢失)
  3. 归一化(调整数字大小)
  4. 再过一遍前馈网络,重复残差和归一化

这些“残差”和“归一化”就像安全带,保证模型训练时不会“跑偏”。

第六步:打造“翻译官”——解码器层

解码器层和编码器差不多,但多了一个任务:它不仅要看自己的输入,还要“偷看”编码器的输出。这就像翻译时既要看原文,又要根据翻译目标调整。

Python
class DecoderLayer(nn.Module):
    def __init__(self, d_model, num_heads, d_ff, dropout=0.1):
        super(DecoderLayer, self).__init__()
        
        self.self_attn = MultiHeadAttention(d_model, num_heads)  # 自注意力
        self.cross_attn = MultiHeadAttention(d_model, num_heads)  # 交叉注意力
        self.feed_forward = PositionwiseFeedForward(d_model, d_ff)  # 前馈网络
        
        self.norm1 = nn.LayerNorm(d_model)
        self.norm2 = nn.LayerNorm(d_model)
        self.norm3 = nn.LayerNorm(d_model)
        
        self.dropout1 = nn.Dropout(dropout)
        self.dropout2 = nn.Dropout(dropout)
        self.dropout3 = nn.Dropout(dropout)

多出来的 cross_attn 是用来关注编码器输出的。

Python
    def forward(self, x, enc_output, src_mask=None, tgt_mask=None):
        # 自注意力
        self_attn_output = self.self_attn(x, x, x, tgt_mask)
        x = self.norm1(x + self.dropout1(self_attn_output))
        
        # 交叉注意力(看编码器输出)
        cross_attn_output = self.cross_attn(x, enc_output, enc_output, src_mask)
        x = self.norm2(x + self.dropout2(cross_attn_output))
        
        # 前馈网络
        ff_output = self.feed_forward(x)
        x = self.norm3(x + self.dropout3(ff_output))
        
        return x

流程多了个“交叉注意力”:

  1. 自注意力(看自己的输入,用 tgt_mask 防止偷看未来)
  2. 交叉注意力(结合编码器的输出)
  3. 前馈网络处理

每个步骤都有残差和归一化,确保信息流畅。

第七步:堆叠起来——完整的编码器和解码器

一个层可能不够,我们把多个编码器层和解码器层叠起来,增强模型能力。

Python
class Encoder(nn.Module):
    def __init__(self, d_model, num_heads, d_ff, num_layers, dropout=0.1):
        super(Encoder, self).__init__()
        
        self.layers = nn.ModuleList([
            EncoderLayer(d_model, num_heads, d_ff, dropout)
            for _ in range(num_layers)
        ])
        
    def forward(self, x, mask=None):
        for layer in self.layers:
            x = layer(x, mask)
        return x

编码器就是把多个编码器层串起来,依次处理输入。

Python
class Decoder(nn.Module):
    def __init__(self, d_model, num_heads, d_ff, num_layers, dropout=0.1):
        super(Decoder, self).__init__()
        
        self.layers = nn.ModuleList([
            DecoderLayer(d_model, num_heads, d_ff, dropout)
            for _ in range(num_layers)
        ])
        
    def forward(self, x, enc_output, src_mask=None, tgt_mask=None):
        for layer in self.layers:
            x = layer(x, enc_output, src_mask, tgt_mask)
        return x

解码器也是堆叠多个层,但每次都要带上编码器的输出。

第八步:组装语言模型——完整的 TransformerLM

我们这次的目标是做一个语言模型(像 GPT 那样生成文本),所以只用解码器就够了。它会接收一段文字,预测下一个词。

Python
class TransformerLM(nn.Module):
    def __init__(self, vocab_size, d_model, num_heads, d_ff, num_layers, dropout=0.1):
        super(TransformerLM, self).__init__()
        
        self.d_model = d_model
        
        # 词嵌入和位置编码
        self.embedding = nn.Embedding(vocab_size, d_model)
        self.positional_encoding = PositionalEncoding(d_model)
        
        # 解码器堆栈
        self.decoder = Decoder(d_model, num_heads, d_ff, num_layers, dropout)
        
        # 输出层
        self.fc_out = nn.Linear(d_model, vocab_size)
        
        self.dropout = nn.Dropout(dropout)

这里:

  • vocab_size 是词汇表大小(有多少个不同的词或字符)
  • 嵌入层把词变成向量,位置编码加顺序信息
  • 解码器处理信息,输出层把结果变回词的概率
Python
    def forward(self, x, mask=None):
        # 嵌入 + 位置编码
        x = self.embedding(x) * math.sqrt(self.d_model)
        x = self.positional_encoding(x)
        x = self.dropout(x)
        
        # 通过解码器(仅用解码器架构)
        x = self.decoder(x, x, None, mask)
        
        # 输出词概率
        output = self.fc_out(x)
        
        return output

流程是:

  1. 把词变成向量,放大后加位置信息
  2. 通过解码器(这里 Q 和 K 用同一个 x,因为是语言模型)
  3. 输出每个词的概率

放大(* math.sqrt(d_model))是为了让嵌入和位置编码的规模匹配。

第九步:别偷看未来——因果掩码

语言模型预测下一个词时,不能“作弊”看后面的词。我们用「因果掩码」来限制它:

Python
def create_causal_mask(seq_len):
    """创建因果掩码,防止模型看到未来的词"""
    mask = torch.triu(torch.ones(seq_len, seq_len), diagonal=1)
    return mask == 0  # True 的地方是可以关注的

这会生成一个三角形掩码,确保每个位置只看前面的词和自己。

第十步:文本变数字——简单字符分词器

模型只认识数字,我们得把文本变成数字。这靠「分词器」完成,我们用一个简单的字符级分词器:

Python
class SimpleTokenizer:
    def __init__(self, texts=None):
        self.char_to_idx = {}
        self.idx_to_char = {}
        
        if texts:
            self.fit(texts)
    
    def fit(self, texts):
        # 收集所有独特字符
        unique_chars = set()
        for text in texts:
            unique_chars.update(text)
        
        # 创建字符到数字的映射
        self.char_to_idx = {char: idx for idx, char in enumerate(sorted(unique_chars))}
        self.idx_to_char = {idx: char for char, idx in self.char_to_idx.items()}
    
    def encode(self, text):
        return [self.char_to_idx[char] for char in text]  # 文本变数字
    
    def decode(self, indices):
        return ''.join([self.idx_to_char[idx] for idx in indices])  # 数字变文本
    
    @property
    def vocab_size(self):
        return len(self.char_to_idx)  # 词汇表大小

它会:

  • 找出文本里所有不同的字符
  • 给每个字符一个编号
  • 提供文本和数字的互转功能

简单,但够用(比起高级的分词器稍微慢点)。

第十一步:准备“教材”——数据集

模型要学习,得有“教材”。我们把文本切成小段,告诉模型“看这个,猜下一个”。

Python
class TextDataset(Dataset):
    def __init__(self, texts, tokenizer, seq_length):
        self.tokenizer = tokenizer
        self.seq_length = seq_length  # 一次看多长的序列
        
        # 把所有文本变成数字并连起来
        self.data = []
        for text in texts:
            self.data.extend(tokenizer.encode(text))
        
        # 算能切出多少段
        self.num_sequences = max(0, len(self.data) - seq_length)

这里把文本编码后连成一个长序列。

Python
    def __len__(self):
        return self.num_sequences
    
    def __getitem__(self, idx):
        # 取一段输入和对应的目标
        input_seq = self.data[idx:idx + self.seq_length]
        target_seq = self.data[idx + 1:idx + self.seq_length + 1]
        
        return (
            torch.tensor(input_seq, dtype=torch.long),
            torch.tensor(target_seq, dtype=torch.long)
        )

每次取一段(比如 20 个字符),目标是往后移一位的序列,教模型预测下一个字符。

第十二步:开始学习——训练函数

现在让模型“上学”,通过反复练习提高预测能力。

Python
def train_transformer_lm(model, dataloader, num_epochs, learning_rate, device):
    model.to(device)  # 搬到 GPU 或 CPU
    optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)  # 优化器
    criterion = nn.CrossEntropyLoss()  # 损失函数
    
    for epoch in range(num_epochs):  # 训练几轮
        model.train()
        total_loss = 0

我们准备:

  • 把模型放对地方(GPU 更快)
  • 用 Adam 优化器调整参数
  • 用交叉熵损失衡量预测的好坏
Python
        for batch_idx, (inputs, targets) in enumerate(dataloader):
            inputs, targets = inputs.to(device), targets.to(device)
            
            # 创建因果掩码
            seq_len = inputs.size(1)
            causal_mask = create_causal_mask(seq_len).to(device)
            
            # 前向传播
            optimizer.zero_grad()
            outputs = model(inputs, causal_mask)

每次处理一批数据:

  • 搬到设备上
  • 生成掩码,防止偷看
  • 清零梯度,跑一遍模型
Python
            # 计算损失
            loss = criterion(outputs.view(-1, outputs.size(-1)), targets.view(-1))
            
            # 反向传播,更新参数
            loss.backward()
            optimizer.step()
            
            total_loss += loss.item()

然后:

  • 计算预测和目标的差距(损失)
  • 根据差距调整模型
  • 累加损失,跟踪进度
Python
            if (batch_idx + 1) % 10 == 0:
                print(f'第 {epoch+1}/{num_epochs} 轮, 批次 {batch_idx+1}/{len(dataloader)}, '
                      f'损失: {total_loss / (batch_idx + 1):.4f}')
        
        print(f'第 {epoch+1}/{num_epochs} 轮, 平均损失: {total_loss / len(dataloader):.4f}')
    
    return model

每隔 10 个批次汇报一次,最后返回训练好的模型。

第十三步:展示才艺——生成文本

模型学好了,我们让它“写”点东西看看。

Python
def generate_text(model, tokenizer, start_text, max_length, temperature=1.0, device='cpu'):
    model.eval()  # 评估模式
    
    # 把起始文本变成数字
    input_seq = tokenizer.encode(start_text)
    input_tensor = torch.tensor([input_seq], dtype=torch.long).to(device)

先把开头准备好。

Python
    # 一个个生成新字符
    for _ in range(max_length):
        seq_len = input_tensor.size(1)
        causal_mask = create_causal_mask(seq_len).to(device)
        
        # 预测下一个
        with torch.no_grad():
            output = model(input_tensor, causal_mask)
        
        next_token_logits = output[0, -1, :] / temperature

每次:

  • 用掩码限制视线
  • 预测下一个字符的概率(logits
  • temperature 控制随机性(越大越随机)
Python
        # 概率化并采样
        probabilities = F.softmax(next_token_logits, dim=-1)
        next_token = torch.multinomial(probabilities, 1).item()
        
        # 加到序列里
        input_tensor = torch.cat([
            input_tensor, 
            torch.tensor([[next_token]], dtype=torch.long).to(device)
        ], dim=1)
  • 把概率变成分布,随机挑一个字符
  • 加到当前序列,继续预测
Python
    # 解码成文本
    generated_tokens = input_tensor[0].tolist()
    generated_text = tokenizer.decode(generated_tokens)
    
    return generated_text

最后把数字变回文字,展示成果。

第十四步:大功告成——整合所有步骤

我们写个 main() 函数,把所有步骤串起来:

Python
def main():
    # 一些示例文本
    texts = [
        "Hello, how are you doing today?",
        "Transformers are powerful neural network architectures.",
        "Language models can generate coherent text.",
        "PyTorch is a popular deep learning framework."
    ]

先准备点“教材”。

Python
    # 设置参数
    seq_length = 20  # 一次看 20 个字符
    batch_size = 4   # 每次训练 4 组
    d_model = 64     # 词向量长度
    num_heads = 4    # 4 个注意力头
    d_ff = 256       # 前馈网络维度
    num_layers = 2   # 2 层解码器
    dropout = 0.1    # 丢弃率
    num_epochs = 10  # 训练 10 轮
    learning_rate = 0.001  # 学习速度

这些参数决定了模型的大小和训练方式。

Python
    # 选设备
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    print(f"使用设备: {device}")
    
    # 初始化分词器
    tokenizer = SimpleTokenizer(texts)
    print(f"词汇表大小: {tokenizer.vocab_size}")

检查用 CPU 还是 GPU(有 GPU 更快),准备分词器。

Python
    # 准备数据
    dataset = TextDataset(texts, tokenizer, seq_length)
    dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True)
    
    # 创建模型
    model = TransformerLM(
        vocab_size=tokenizer.vocab_size,
        d_model=d_model,
        num_heads=num_heads,
        d_ff=d_ff,
        num_layers=num_layers,
        dropout=dropout
    )

把数据和模型准备好。

Python
    # 看看模型长啥样
    print(model)
    print(f"参数数量: {sum(p.numel() for p in model.parameters())}")
    
    # 开始训练
    model = train_transformer_lm(model, dataloader, num_epochs, learning_rate, device)
    
    # 生成文本试试
    start_text = "Hello"
    generated_text = generate_text(model, tokenizer, start_text, max_length=50, device=device)
    print(f"生成文本:\n{generated_text}")

最后:

  • 打印模型结构和参数量
  • 训练模型
  • 用“Hello”开头,生成一段文本

它是怎么工作的?

简单说,Transformer 就像一个“预测大师”。你给它几个词,它猜下一个词。它的步骤是:

  1. 词变数字:把文字变成向量
  2. 加位置:告诉模型词的顺序
  3. 找重点:用注意力机制挑重要信息
  4. 加工信息:用前馈网络处理
  5. 猜词:输出每个词的概率,选一个
  6. 重复:不断预测下一个

训练时,我们给它看很多例子,让它学会“猜得准”。

恭喜你!一个 Transformer 语言模型诞生了

你从零开始造了个模型!虽然它比不上 GPT 那样的“大块头”,但核心原理是一样的。你现在明白了注意力机制、位置编码这些“黑科技”是怎么回事。

Transformer 是 NLP 的“万能钥匙”,能用来翻译、写文章、做摘要……学会它,你就打开了 AI 世界的大门。继续加油吧!

想看完整代码?点这里:> https://colab.research.google.com/drive/1KKQA70Pscp4UXazUJEDRzV1fMwgw6vjr?usp=sharing

原文链接:https://www.k-a.in/llm3.html


© 2025 智人飞扬