1. 词向量:单词的"身份证"
首先,我们定义了四个单词的词向量,每个向量维度为3。你可以把这些词向量想象成每个单词的"身份证"。每个身份证上有3个特征,用来描述这个单词的"性格"或"特点"。
word_1 = np.array([1, 0, 0])
word_2 = np.array([0, 1, 0])
word_3 = np.array([1, 1, 0])
word_4 = np.array([0, 0, 1])
这些词向量可以堆叠成一个矩阵 words
,就像把一堆身份证放在一个文件夹里,方便我们一次性处理。
words = np.array([word_1, word_2, word_3, word_4])
2. 权重矩阵:单词的"变身器"
接下来,我们生成了三个权重矩阵 W_Q
, W_K
, W_V
。你可以把这些权重矩阵想象成"变身器"。每个单词通过这个"变身器"后,会变成不同的形态:查询(Query)、键(Key)和值(Value)。
W_Q = np.random.randint(3, size=(3, 3))
W_K = np.random.randint(3, size=(3, 3))
W_V = np.random.randint(3, size=(3, 3))
3. 计算查询、键和值:单词的"变身"
通过矩阵乘法,我们把每个单词的"身份证"通过"变身器"变成了查询、键和值。这就像把每个单词的身份证放进变身器,然后得到了三个不同的"变身形态"。
Q = words @ W_Q
K = words @ W_K
V = words @ W_V
4. 计算得分:单词之间的"相亲"
接下来,我们计算得分矩阵 scores
,这个矩阵表示每个查询向量(Query)和键向量(Key)之间的"相亲"得分。得分越高,表示这两个单词越"般配"。
scores = Q @ K.T
5. 计算权重:用Softmax"打分"
为了让这些得分更加合理,我们使用Softmax函数将得分转换为概率分布。这就像给每个"相亲"打分,得分高的"相亲"会得到更多的关注。
weights = softmax(scores / np.sqrt(K.shape[1]), axis=1)
6. 计算注意力输出:加权求和
最后,我们通过加权和的方式计算注意力输出。这就像把所有"相亲"的结果综合起来,得到一个最终的"相亲报告"。
attention = weights @ V
7. 自注意力机制:单词的"自我对话"
在自注意力机制中,每个单词会和其他所有单词进行"对话",看看谁更重要。这就像在一个会议上,每个人都会和其他人交流,最终决定谁的意见最重要。
attention_scores = torch.matmul(query, key.T) / torch.sqrt(torch.tensor(query.size(-1), dtype=torch.float32))
attention_weights = F.softmax(attention_scores, dim=-1)
output = torch.matmul(attention_weights, value)
8. 多头注意力机制:多角度"相亲"
多头注意力机制就像有多个人同时进行"相亲",每个人从不同的角度去看待这些单词,最后把所有人的意见综合起来。
class MultiHeadAttention(nn.Module):
def __init__(self, heads, d_model, dropout=0.1):
super().__init__()
self.d_model = d_model
self.d_k = d_model // heads
self.h = heads
self.q_linear = nn.Linear(d_model, d_model)
self.k_linear = nn.Linear(d_model, d_model)
self.v_linear = nn.Linear(d_model, d_model)
self.dropout = nn.Dropout(dropout)
self.out = nn.Linear(d_model, d_model)
9. 位置编码:给单词加上"时间戳"
为了让模型知道单词的顺序,我们给每个单词加上一个"时间戳",这就是位置编码。这就像给每个单词加上一个"出生时间",让模型知道谁先谁后。
class PositionalEncoding(nn.Module):
def __init__(self, d_model, max_len=5000):
super(PositionalEncoding, self).__init__()
pe = torch.zeros(max_len, d_model)
position = torch.arange(0, max_len, 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)
pe = pe.unsqueeze(0)
self.register_buffer('pe', pe)
10. Transformer模型:一个完整的"翻译官"
最后,我们把所有这些组件组合在一起,形成了一个完整的Transformer模型。这个模型就像一个"翻译官",能够把一种语言翻译成另一种语言。
class Transformer(nn.Module):
def __init__(self, src_vocab_size, tgt_vocab_size, d_model, num_heads, num_layers, d_ff, max_len, dropout):
super(Transformer, self).__init__()
self.encoder_embedding = nn.Embedding(src_vocab_size, d_model)
self.decoder_embedding = nn.Embedding(tgt_vocab_size, d_model)
self.positional_encoding = PositionalEncoding(d_model, max_len)
self.encoder_layers = nn.ModuleList([EncoderLayer(d_model, num_heads, d_ff, dropout) for _ in range(num_layers)])
self.decoder_layers = nn.ModuleList([DecoderLayer(d_model, num_heads, d_ff, dropout) for _ in range(num_layers)])
self.linear = nn.Linear(d_model, tgt_vocab_size)
self.dropout = nn.Dropout(dropout)
总结
通过这些步骤,我们实现了一个完整的注意力机制和Transformer模型。整个过程就像是一个复杂的"相亲"过程,每个单词通过"变身器"变成不同的形态,然后通过"相亲"来决定谁更重要,最后综合所有人的意见,得到一个最终的"翻译结果"。