复现Andrew Karpathy的nano GPT遇CUDA设备断言触发错误求思路
复现NanoGPT时触发CUDA Device-Side Assert错误的排查思路
我在复现Andrew Karpathy的NanoGPT项目,确认代码和原代码完全一致,无语法错误,但运行时触发RuntimeError: CUDA error: device-side assert triggered错误。以下是完整代码,求排查思路:
import torch import torch.nn as nn from torch.nn import functional as F batch_size =64 block_size =256 max_iters =5000 eval_interval =500 learn_rate=3e-4 device ='cuda' if torch.cuda.is_available() else 'cpu' eval_iters =200 n_embd=384 n_head =6 n_layer=6 dropout =0.2 print('cuda is ok' if torch.cuda.is_available() else 'cuda i`nt ok') #------- torch.manual_seed(1337) with open('input.txt','r',encoding='utf-8') as f: text =f.read() chars =sorted(list(set(text))) vocab_size =len(chars) stoi={ch:i for i,ch in enumerate(chars)} itos={i:ch for i,ch in enumerate(chars)} encode =lambda s:[stoi[c] for c in s] decode = lambda l: ''.join([itos[i] for i in l]) data =torch.tensor(encode(text),dtype=torch.long) n=int(0.9*len(data)) train_data=data[:n] val_data=data[n:] def get_batch(split): data=train_data if split=='train' else val_data ix =torch.randint(len(data)-block_size,(batch_size,)) x=torch.stack([data[i:i+block_size] for i in ix]) y=torch.stack([data[i+1:block_size+i+1] for i in ix]) x,y=x.to(device),y.to(device) return x,y @torch.no_grad() def estimate_loss(): out={} model.eval() for split in ['train','val']: losses =torch.zeros(eval_iters) for k in range(eval_iters): X,Y=get_batch(split) logits,loss =model(X,Y) losses[k]=loss.item() out[split]=losses.mean() model.train() return out class Head(nn.Module): 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) q=self.query(x) wei =q@ k.transpose(-2,-1)*k.shape[-1]**-0.5 wei=wei.masked_fill(self.tril[:T,:T]==0,float('-inf')) wei=F.softmax(wei,dim=-1) wei =self.dropout(wei) v=self.value(x) out=wei@v return out class MutiHeadAttention(nn.Module): 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(head_size*num_heads,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 class FeedForward(nn.Module): 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) class Block(nn.Module): def __init__(self,n_embd,n_head): super().__init__() head_size=n_embd//n_head self.sa =MutiHeadAttention(n_head,head_size) self.ffwd=FeedForward(n_embd) self.ln1=nn.LayerNorm(n_embd) self.ln2=nn.LayerNorm(n_embd) def forward(self,x): x=x+self.sa(self.ln1(x)) x=x+self.ffwd(self.ln2(x)) return x class GPTlanguageModel(nn.Module): def __init__(self): super().__init__() self.token_embedding_table=nn.Embedding(vocab_size,n_embd) self.position_embedding_table =nn.Embedding(vocab_size,n_embd) self.blocks=nn.Sequential(*[Block(n_embd,n_head=n_head) for _ in range(n_layer)]) self.ln_f=nn.LayerNorm(n_embd) self.lm_head=nn.Linear(n_embd,vocab_size) self.apply(self._init_weights) def _init_weights(self,module): 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): B,T=idx.shape tok_emb=self.token_embedding_table(idx) pos_emb=self.position_embedding_table(torch.arange(T,device=device)) x=tok_emb+pos_emb x=self.blocks(x) x=self.ln_f(x) logits=self.lm_head(x) 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): for _ in range(max_new_tokens): idx_cond =idx[:,-block_size:] logits,loss=self(idx_cond) logits=logits[:-1:] probs =F.softmax(logits,dim=-1) idx_next=torch.multinomial(probs,num_samples=1) idx=torch.cat((idx,idx_next),dim=1) return idx model=GPTlanguageModel() m=model.to(device) print(sum(p.numel() for p in m.parameters())/1e6,'M parameters') optimizer= torch.optim.AdamW(model.parameters(),lr=learn_rate) for iter in range(max_iters): if iter % eval_interval ==0 or iter == max_iters-1: losses =estimate_loss() min_train_loss=losses['train'] if losses['train']<=min_train_loss: torch.save(m.state_dict(),'model.pt') min_train_loss=losses['train'] print(f"step{iter}:train loss {losses['train']:.4f},val loss {losses['val']:.4f}") xb,yb =get_batch('train') logits,loss = model(xb,yb) optimizer.zero_grad(set_to_none=True) loss.backward() optimizer.step() model.load_state_dict(torch.load('model.pt')) #context =torch.zeros((1,1),dtype=torch.long,device=device) #print(decode(m.generate(context,max_new_tokens=500)[0].tolist()))
排查思路
- 位置嵌入表维度错误:
GPTlanguageModel初始化中,self.position_embedding_table = nn.Embedding(vocab_size, n_embd)的第一个参数应为block_size,而非vocab_size。位置嵌入的维度对应序列长度(即block_size),原代码会导致pos_emb与tok_emb维度不匹配,触发CUDA断言。 - 生成方法的索引错误:
generate方法里logits=logits[:-1:]是错误切片,正确写法应为logits = logits[:, -1, :],目的是取最后一个时间步的输出。错误切片会导致后续维度不匹配,在CUDA环境下触发断言。 - 切换到CPU排查:将
device设为'cpu'运行代码,CPU会输出更具体的错误信息(比如维度不匹配、索引越界的具体位置),而CUDA的device-side assert仅返回模糊错误。 - 训练保存逻辑优化:训练循环中
min_train_loss=losses['train']每次评估都会重置,应将初始化放在循环外(比如min_train_loss = float('inf')),否则永远触发模型保存条件,虽不直接导致CUDA错误,但属于逻辑缺陷。
内容的提问来源于stack exchange,提问作者open I
相关产品推荐
相关产品推荐

