【DataWhale】快樂學習大模型 | 202507,Task05筆記

前言

今天是Transformer的編碼實戰階段,照著示例代碼執行一遍吧

embedding

self.tok_embeddings = nn.Embedding(args.vocab_size, args.dim)

把token向量轉為embedding矩陣(一個token一個embedding向量)

位置編碼

為了解決“我喜歡你”和“你喜歡我”結果一致的問題,加入了位置的影響。

小技巧

import pdb
pdb.set_trace()

在需要打斷點的地方加這個,可以暫停python程序進行調試。

Transformer源碼和結構圖

來自:happy-llm,代碼部分做了第2章代碼的合并總結,方便本地直接運行。
在這里插入圖片描述

import torch
import torch.nn as nn
import torch.nn.functional as F
import math'''多頭自注意力計算模塊'''
class MultiHeadAttention(nn.Module):def __init__(self, args, is_causal=False):# 構造函數# args: 配置對象super().__init__()# 隱藏層維度必須是頭數的整數倍,因為后面我們會將輸入拆成頭數個矩陣assert args.dim % args.n_heads == 0# 模型并行處理大小,默認為1。model_parallel_size = 1# 本地計算頭數,等于總頭數除以模型并行處理大小。self.n_local_heads = args.n_heads // model_parallel_size# 每個頭的維度,等于模型維度除以頭的總數。self.head_dim = args.dim // args.n_heads# Wq, Wk, Wv 參數矩陣,每個參數矩陣為 n_embd x n_embd# 這里通過三個組合矩陣來代替了n個參數矩陣的組合,其邏輯在于矩陣內積再拼接其實等同于拼接矩陣再內積,# 不理解的讀者可以自行模擬一下,每一個線性層其實相當于n個參數矩陣的拼接self.wq = nn.Linear(args.dim, args.n_heads * self.head_dim, bias=False)self.wk = nn.Linear(args.dim, args.n_heads * self.head_dim, bias=False)self.wv = nn.Linear(args.dim, args.n_heads * self.head_dim, bias=False)# 輸出權重矩陣,維度為 dim x n_embd(head_dim = n_embeds / n_heads)self.wo = nn.Linear(args.n_heads * self.head_dim, args.dim, bias=False)# 注意力的 dropoutself.attn_dropout = nn.Dropout(args.dropout)# 殘差連接的 dropoutself.resid_dropout = nn.Dropout(args.dropout)self.is_causal = is_causal# 創建一個上三角矩陣,用于遮蔽未來信息# 注意,因為是多頭注意力,Mask 矩陣比之前我們定義的多一個維度if is_causal:mask = torch.full((1, 1, args.max_seq_len, args.max_seq_len), float("-inf"))mask = torch.triu(mask, diagonal=1)# 注冊為模型的緩沖區self.register_buffer("mask", mask)def forward(self, q: torch.Tensor, k: torch.Tensor, v: torch.Tensor):# 獲取批次大小和序列長度,[batch_size, seq_len, dim]bsz, seqlen, _ = q.shape# 計算查詢(Q)、鍵(K)、值(V),輸入通過參數矩陣層,維度為 (B, T, n_embed) x (n_embed, n_embed) -> (B, T, n_embed)xq, xk, xv = self.wq(q), self.wk(k), self.wv(v)# 將 Q、K、V 拆分成多頭,維度為 (B, T, n_head, C // n_head),然后交換維度,變成 (B, n_head, T, C // n_head)# 因為在注意力計算中我們是取了后兩個維度參與計算# 為什么要先按B*T*n_head*C//n_head展開再互換1、2維度而不是直接按注意力輸入展開,是因為view的展開方式是直接把輸入全部排開,# 然后按要求構造,可以發現只有上述操作能夠實現我們將每個頭對應部分取出來的目標xq = xq.view(bsz, seqlen, self.n_local_heads, self.head_dim)xk = xk.view(bsz, seqlen, self.n_local_heads, self.head_dim)xv = xv.view(bsz, seqlen, self.n_local_heads, self.head_dim)xq = xq.transpose(1, 2)xk = xk.transpose(1, 2)xv = xv.transpose(1, 2)# 注意力計算# 計算 QK^T / sqrt(d_k),維度為 (B, nh, T, hs) x (B, nh, hs, T) -> (B, nh, T, T)scores = torch.matmul(xq, xk.transpose(2, 3)) / math.sqrt(self.head_dim)# 掩碼自注意力必須有注意力掩碼if self.is_causal:assert hasattr(self, 'mask')# 這里截取到序列長度,因為有些序列可能比 max_seq_len 短scores = scores + self.mask[:, :, :seqlen, :seqlen]# 計算 softmax,維度為 (B, nh, T, T)scores = F.softmax(scores.float(), dim=-1).type_as(xq)# 做 Dropoutscores = self.attn_dropout(scores)# V * Score,維度為(B, nh, T, T) x (B, nh, T, hs) -> (B, nh, T, hs)output = torch.matmul(scores, xv)# 恢復時間維度并合并頭。# 將多頭的結果拼接起來, 先交換維度為 (B, T, n_head, C // n_head),再拼接成 (B, T, n_head * C // n_head)# contiguous 函數用于重新開辟一塊新內存存儲,因為Pytorch設置先transpose再view會報錯,# 因為view直接基于底層存儲得到,然而transpose并不會改變底層存儲,因此需要額外存儲output = output.transpose(1, 2).contiguous().view(bsz, seqlen, -1)# 最終投影回殘差流。output = self.wo(output)output = self.resid_dropout(output)return outputclass MLP(nn.Module):'''前饋神經網絡'''def __init__(self, dim: int, hidden_dim: int, dropout: float):super().__init__()# 定義第一層線性變換,從輸入維度到隱藏維度self.w1 = nn.Linear(dim, hidden_dim, bias=False)# 定義第二層線性變換,從隱藏維度到輸入維度self.w2 = nn.Linear(hidden_dim, dim, bias=False)# 定義dropout層,用于防止過擬合self.dropout = nn.Dropout(dropout)def forward(self, x):# 前向傳播函數# 首先,輸入x通過第一層線性變換和RELU激活函數# 然后,結果乘以輸入x通過第三層線性變換的結果# 最后,通過第二層線性變換和dropout層return self.dropout(self.w2(F.relu(self.w1(x))))class LayerNorm(nn.Module):''' Layer Norm 層'''def __init__(self, features, eps=1e-6):super(LayerNorm, self).__init__()# 線性矩陣做映射self.a_2 = nn.Parameter(torch.ones(features))self.b_2 = nn.Parameter(torch.zeros(features))self.eps = epsdef forward(self, x):# 在統計每個樣本所有維度的值,求均值和方差mean = x.mean(-1, keepdim=True) # mean: [bsz, max_len, 1]std = x.std(-1, keepdim=True) # std: [bsz, max_len, 1]# 注意這里也在最后一個維度發生了廣播return self.a_2 * (x - mean) / (std + self.eps) + self.b_2class EncoderLayer(nn.Module):'''Encoder層'''def __init__(self, args):super().__init__()# 一個 Layer 中有兩個 LayerNorm,分別在 Attention 之前和 MLP 之前self.attention_norm = LayerNorm(args.n_embd)# Encoder 不需要掩碼,傳入 is_causal=Falseself.attention = MultiHeadAttention(args, is_causal=False)self.fnn_norm = LayerNorm(args.n_embd)self.feed_forward = MLP(args.n_embd, args.hidden_dim, args.dropout)def forward(self, x):# Layer Normnorm_x = self.attention_norm(x)# 自注意力h = x + self.attention.forward(norm_x, norm_x, norm_x)# 經過前饋神經網絡out = h + self.feed_forward.forward(self.fnn_norm(h))return outclass Encoder(nn.Module):'''Encoder 塊'''def __init__(self, args):super(Encoder, self).__init__() # 一個 Encoder 由 N 個 Encoder Layer 組成self.layers = nn.ModuleList([EncoderLayer(args) for _ in range(args.n_layer)])self.norm = LayerNorm(args.n_embd)def forward(self, x):"分別通過 N 層 Encoder Layer"for layer in self.layers:x = layer(x)return self.norm(x)class DecoderLayer(nn.Module):'''解碼層'''def __init__(self, args):super().__init__()# 一個 Layer 中有三個 LayerNorm,分別在 Mask Attention 之前、Self Attention 之前和 MLP 之前self.attention_norm_1 = LayerNorm(args.n_embd)# Decoder 的第一個部分是 Mask Attention,傳入 is_causal=Trueself.mask_attention = MultiHeadAttention(args, is_causal=True)self.attention_norm_2 = LayerNorm(args.n_embd)# Decoder 的第二個部分是 類似于 Encoder 的 Attention,傳入 is_causal=Falseself.attention = MultiHeadAttention(args, is_causal=False)self.ffn_norm = LayerNorm(args.n_embd)# 第三個部分是 MLPself.feed_forward = MLP(args.n_embd, args.hidden_dim, args.dropout)def forward(self, x, enc_out):# Layer Normnorm_x = self.attention_norm_1(x)# 掩碼自注意力x = x + self.mask_attention.forward(norm_x, norm_x, norm_x)# 多頭注意力norm_x = self.attention_norm_2(x)h = x + self.attention.forward(norm_x, enc_out, enc_out)# 經過前饋神經網絡out = h + self.feed_forward.forward(self.ffn_norm(h))return outclass Decoder(nn.Module):'''解碼器'''def __init__(self, args):super(Decoder, self).__init__() # 一個 Decoder 由 N 個 Decoder Layer 組成self.layers = nn.ModuleList([DecoderLayer(args) for _ in range(args.n_layer)])self.norm = LayerNorm(args.n_embd)def forward(self, x, enc_out):"Pass the input (and mask) through each layer in turn."for layer in self.layers:x = layer(x, enc_out)return self.norm(x)class PositionalEncoding(nn.Module):'''位置編碼模塊'''def __init__(self, args):super(PositionalEncoding, self).__init__()# Dropout 層self.dropout = nn.Dropout(p=args.dropout)# block size 是序列的最大長度pe = torch.zeros(args.block_size, args.n_embd)position = torch.arange(0, args.block_size).unsqueeze(1)# 計算 thetadiv_term = torch.exp(torch.arange(0, args.n_embd, 2) * -(math.log(10000.0) / args.n_embd))# 分別計算 sin、cos 結果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)def forward(self, x):# 將位置編碼加到 Embedding 結果上x = x + self.pe[:, : x.size(1)].requires_grad_(False)return self.dropout(x)class Transformer(nn.Module):'''整體模型'''def __init__(self, args):super().__init__()# 必須輸入詞表大小和 block sizeassert args.vocab_size is not Noneassert args.block_size is not Noneself.args = argsself.transformer = nn.ModuleDict(dict(wte = nn.Embedding(args.vocab_size, args.n_embd),wpe = PositionalEncoding(args),drop = nn.Dropout(args.dropout),encoder = Encoder(args),decoder = Decoder(args),))# 最后的線性層,輸入是 n_embd,輸出是詞表大小self.lm_head = nn.Linear(args.n_embd, args.vocab_size, bias=False)# 初始化所有的權重self.apply(self._init_weights)# 查看所有參數的數量print("number of parameters: %.2fM" % (self.get_num_params()/1e6,))'''統計所有參數的數量'''def get_num_params(self, non_embedding=False):# non_embedding: 是否統計 embedding 的參數n_params = sum(p.numel() for p in self.parameters())# 如果不統計 embedding 的參數,就減去if non_embedding:n_params -= self.transformer.wpe.weight.numel()return n_params'''初始化權重'''def _init_weights(self, module):# 線性層和 Embedding 層初始化為正則分布if isinstance(module, nn.Linear):torch.nn.init.normal_(module.weight, mean=0.0, std=0.02)if module.bias is not None:torch.nn.init.zeros_(module.bias)elif isinstance(module, nn.Embedding):torch.nn.init.normal_(module.weight, mean=0.0, std=0.02)'''前向計算函數'''def forward(self, idx, targets=None):# 輸入為 idx,維度為 (batch size, sequence length, 1);targets 為目標序列,用于計算 lossdevice = idx.deviceb, t = idx.size()assert t <= self.args.block_size, f"不能計算該序列,該序列長度為 {t}, 最大序列長度只有 {self.args.block_size}"# 通過 self.transformer# 首先將輸入 idx 通過 Embedding 層,得到維度為 (batch size, sequence length, n_embd)print("idx",idx.size())# 通過 Embedding 層tok_emb = self.transformer.wte(idx)print("tok_emb",tok_emb.size())# 然后通過位置編碼pos_emb = self.transformer.wpe(tok_emb) # 再進行 Dropoutx = self.transformer.drop(pos_emb)# 然后通過 Encoderprint("x after wpe:",x.size())enc_out = self.transformer.encoder(x)print("enc_out:",enc_out.size())# 再通過 Decoderx = self.transformer.decoder(x, enc_out)print("x after decoder:",x.size())if targets is not None:# 訓練階段,如果我們給了 targets,就計算 loss# 先通過最后的 Linear 層,得到維度為 (batch size, sequence length, vocab size)logits = self.lm_head(x)# 再跟 targets 計算交叉熵loss = F.cross_entropy(logits.view(-1, logits.size(-1)), targets.view(-1), ignore_index=-1)else:# 推理階段,我們只需要 logits,loss 為 None# 取 -1 是只取序列中的最后一個作為輸出logits = self.lm_head(x[:, [-1], :]) # note: using list [-1] to preserve the time dimloss = Nonereturn logits, lossif __name__ == '__main__':class Args:vocab_size = 10000block_size = 128n_embd = 256dropout = 0.1n_layer = 6n_heads = 8dim = 256hidden_dim = 512dropout = 0.1max_seq_len = 128args = Args()model = Transformer(args)print(model)# 訓練模型是有targetprint("########## Training ##########")target = torch.randint(0, args.vocab_size, (2, args.block_size))logits, loss = model(target, target)print("Training logits shape:", logits.shape)print("logits shape:", logits.shape)print("loss:", loss)# 測試模型的前向計算print("########## Inference ##########")idx = torch.randint(0, args.vocab_size, (2, args.block_size))logits, loss = model(idx)print("Inference logits shape:", logits.shape)print("logits shape:", logits.shape)print("loss:", loss)

將上面代碼保存為 transformer_demo.py 后,直接運行即可

python .\transformer_demo.py

運行結果

在這里插入圖片描述
可以看到訓練模式下計算了loss(真實GPU訓練還會再稍復雜一些)。推理模式下直接推理了下一個的輸出,[2,1,10000]中,10000是詞表大小,2是batch(如同時2個人在用),1是下一次token。10000中最大的數值最大的位置就是下次概率最大的token。

參考鏈接

1、happy-llm/docs/chapter2/第二章 Transformer架構.md

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/915622.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/915622.shtml
英文地址,請注明出處:http://en.pswp.cn/news/915622.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

用ffmpeg 進行視頻的拼接

author: hjjdebug date: 2025年 07月 22日 星期二 17:06:02 CST descrip: 用ffmpeg 進行視頻的拼接 文章目錄1. 指定協議為concat 方式.1.1 協議為concat 模式,會調用 concat_open 函數1.2 當讀數據時,會調用concat_read2. 指定file_format 為 concat 方式2.1 調用concat_read_…

HTTP與HTTPS技術細節及TLS密鑰交換與證書校驗全流程

HTTP與HTTPS技術細節及TLS密鑰交換與證書校驗全流程 引言 文檔目的與范圍 核心技術棧概述 本文檔的核心技術棧圍繞傳輸層安全協議&#xff08;TLS&#xff09;展開。TLS協議作為安全套接字層&#xff08;SSL&#xff09;的后繼標準&#xff0c;是現代網絡安全通信的基礎&am…

廣播分發中心-廣播注冊流程

廣播是怎么注冊的呢&#xff1f;階段組件/數據結構作用描述存儲位置/關聯關系App進程階段BroadcastReceiver開發者自定義的廣播接收器&#xff0c;實現onReceive方法處理事件。App進程&#xff08;Activity/Service等組件內&#xff09;ReceiverDispatcher將BroadcastReceiver封…

OpenCV計算機視覺實戰(16)——圖像分割技術

OpenCV計算機視覺實戰&#xff08;16&#xff09;——圖像分割技術0. 前言1. 分水嶺算法1.1 應用場景1.2 實現過程2. GrabCut 交互式分割2.1 應用場景2.2 實現過程3. FloodFill3.1 應用場景3.2 實現過程小結系列鏈接0. 前言 圖像分割是計算機視覺中將像素劃分為具有特定語義或…

Coturn打洞服務器

* 概念理解&#xff1a;1. SDP協議&#xff1a;會話描述協議&#xff0c;視頻通話的雙方通過交換SDP信息進行媒體協商&#xff0c;從而選擇使用某一相同的媒體協議進行通信&#xff1b;TLS協議&#xff1a;基于TCP的安全層傳輸協議DTLS協議&#xff1a;基于UDP的安全層傳輸協議…

python flusk 監控

# 創建虛擬環境目錄 python3 -m venv /sda1/xunjian/venv # 激活虛擬環境 source /sda1/xunjian/venv/bin/activate # 激活后終端會顯示 (venv)創建虛擬環境&#xff08;在當前目錄&#xff09;&#xff1a;bashpython3 -m venv venv激活虛擬環境&#xff1a;bashsource venv/b…

VUE2 項目學習筆記 ? 語法 v-if/v-show

?語法頁面渲染的時候&#xff0c;需要服務器傳過來的對象中的一個屬性&#xff0c;然后根據這個屬性用v-for渲染標簽&#xff0c;這里寫的v-for".... in dataList.goodsList"但是當解析到這行語法的時候&#xff0c;dataList還沒返回&#xff0c;因此控制臺會報錯找…

使用qemu命令啟動虛擬機

1. 安裝相關軟件 yum install qemu edk2* libvirt -y 啟動libvirt服務 systemctl start libvirtd systemctl status libvirtd2. 創建虛擬機 2.1. qemu啟動命令示例 /usr/bin/qemu-system-loongarch64 \-machine virt,accelkvm \-nodefaults \-m 2048 \-smp 2,maxcpus4,co…

大模型系統化學習路線

人工智能大模型系統化學習路線一、基礎理論筑基&#xff08;1-2個月) 目標&#xff1a;建立大模型核心認知框架 核心內容&#xff1a; 深度學習基礎&#xff1a;神經網絡原理、CNN/RNN結構、梯度下降算法大模型本質&#xff1a;Transformer架構&#xff08;重點掌握注意力機制、…

LLaMA-Factory 微調可配置的模型基本參數

LLaMA-Factory 微調可配置的模型基本參數 flyfish 基本參數 一、模型加載與路徑配置參數名類型描述默認值model_name_or_pathOptional[str]模型路徑&#xff08;本地路徑或 Huggingface/ModelScope 路徑&#xff09;。Noneadapter_name_or_pathOptional[str]適配器路徑&#xf…

Ubuntu 22 安裝 ZooKeeper 3.9.3 記錄

Ubuntu 22 安裝 ZooKeeper 3.9.3 記錄 本文記錄在 Ubuntu 22.04 系統上安裝 ZooKeeper 3.9.3 的過程&#xff0c;包含 Java 環境準備、配置文件調整、啟動與停機操作、以及如何將 ZooKeeper 注冊為系統服務。 一、準備環境 ZooKeeper 3.9.x 要求 Java 11 或更高版本&#xff…

FreeSwitch通過Websocket(流式雙向語音)對接AI實時語音大模型技術方案(mod_ppy_aduio_stream)

FreeSwitch通過WebSocket對接AI實時語音大模型插件技術方案1. 方案概述 基于FreeSWITCH的實時通信能力&#xff0c;通過WebSocket協議橋接AI大模型服務&#xff0c;實現低延遲、高并發的智能語音交互系統。支持雙向語音流處理、實時ASR/TTS轉換和動態業務指令執行。 1753095153…

航班調度優化策略全局概覽

在機場關閉場景下的航班恢復工作&#xff0c;是將機場關閉期間所有的航班進行取消然后恢復還是將機場關閉期間航班全部延誤而后調整呢&#xff1f;簡單來說&#xff0c;在實際操作中&#xff0c;既不是無差別地全部取消&#xff0c;也不是無差別地全部延誤。這兩種“一刀切”的…

spring boot 異步線程@Async 傳遞 threadLocal數據

將父類的 threadLocal 的數據 在線程池時&#xff0c;可以轉給子線程使用。 Async 的使用。 第一步在啟動服務加上 EnableAsync 注解。 EnableAsync public class NetCoreApplication {... ... }第二步&#xff1a;導入阿里 線程工具類<dependency><groupId>com.a…

AI產品經理成長記《零號列車》第一集 邂逅0XAI列車

《零號列車》絕非傳統意義上的 AI 產品經理教程 —— 它是我沉淀二十多年跨行業數字化轉型與工業 4.0 實戰經驗后,首創的100集大型小說體培養指南。那些曾在千行百業驗證過的知識與經驗,不再是枯燥的文字堆砌,而是化作一場沉浸式的學習旅程。? 這里沒有生硬的理論灌輸,而…

[C++11]范圍for循環/using使用

范圍for循環 范圍for循環&#xff08;Range-based for loop&#xff09;是 C11 引入的一種簡潔的循環語法&#xff0c;用于遍歷容器中的元素或者其他支持迭代的數據結構。 范圍for循環可以讓代碼更加簡潔和易讀&#xff0c;避免了傳統for循環中索引的操作。 下面是范圍for循環的…

簡單了解下npm、yarn 和 pnpm 中 add 與 install(i) 命令的區別(附上兩圖帶你一目明了)

目錄 pnpm 中 add 和 i 的區別 npm 中 add 和 i 的區別 yarn 中 add 和 i 的區別 附上兩圖帶你一目明了&#xff1a; npm、yarn和pnpm的三者區別圖&#xff1a; i 和 add 的核心區別圖&#xff1a; 個人建議&#xff1a;在項目中保持命令使用的一致性&#xff0c;選擇一種…

ESP32-S3學習筆記<2>:GPIO的應用

ESP32-S3學習筆記&#xff1c;2&#xff1e;&#xff1a;GPIO的應用1. 頭文件包含2. GPIO的配置2.1 pin_bit_mask2.2 mode2.3 pull_up_en和pull_down_en2.4 intr_type3. 設置GPIO輸出/獲取GPIO輸入4. 中斷的使用4.1 gpio_install_isr_service4.2 gpio_isr_handler_add4.3 gpio_…

得物視覺算法面試30問全景精解

得物視覺算法面試30問全景精解 ——潮流電商 商品鑒別 視覺智能&#xff1a;得物視覺算法面試核心考點全覽 前言 得物App作為中國領先的潮流電商與鑒別平臺&#xff0c;持續推動商品識別、真假鑒別、圖像搜索、內容審核、智能推薦等視覺AI技術的創新與落地。得物視覺算法崗位…

[Linux入門] Linux 賬號和權限管理入門:從基礎到實踐

一、Linux 用戶賬號&#xff1a;誰能訪問系統&#xff1f; 1??超級用戶&#xff08;root&#xff09; 2??普通用戶 3??程序用戶 二、組賬號&#xff1a;讓用戶管理更高效 1??組的類型 2??特殊組 三、用戶與組的 “身份證”&#xff1a;UID 和 GID 四、配置文…