Compare commits

..

1 Commits

Author SHA1 Message Date
Neil Ge a1080ec370
Merge 9cb07dd1ec into cc312c1cc6 2026-09-19 09:03:44 +08:00
4 changed files with 9 additions and 36 deletions

View File

@ -125,14 +125,10 @@ class Attention(nn.Module):
if self.flash and (seq_len > 1) and (not self.is_causal or past_key_value is None) and (attention_mask is None or torch.all(attention_mask == 1)): if self.flash and (seq_len > 1) and (not self.is_causal or past_key_value is None) and (attention_mask is None or torch.all(attention_mask == 1)):
output = F.scaled_dot_product_attention(xq, xk, xv, dropout_p=self.dropout if self.training else 0.0, is_causal=self.is_causal) output = F.scaled_dot_product_attention(xq, xk, xv, dropout_p=self.dropout if self.training else 0.0, is_causal=self.is_causal)
else: else:
# 先转 float32 再加 mask:-1e9 超出 float16 的表示范围(min≈-65504), scores = (xq @ xk.transpose(-2, -1)) / math.sqrt(self.head_dim)
# 在 float16 张量上累加会饱和成 -inf。若某个 query 在因果范围内可见的
# key 全被 mask 掉(左 padding 时必然出现),softmax 整行就是 NaN,
# 并在下一层通过 K/V 投影扩散到同序列里有效的 token 上。
scores = (xq @ xk.transpose(-2, -1)).float() / math.sqrt(self.head_dim)
if self.is_causal: scores[:, :, :, -seq_len:] += torch.full((seq_len, seq_len), float("-inf"), device=scores.device).triu(1) if self.is_causal: scores[:, :, :, -seq_len:] += torch.full((seq_len, seq_len), float("-inf"), device=scores.device).triu(1)
if attention_mask is not None: scores += (1.0 - attention_mask.unsqueeze(1).unsqueeze(2)) * -1e9 if attention_mask is not None: scores += (1.0 - attention_mask.unsqueeze(1).unsqueeze(2)) * -1e9
output = self.attn_dropout(F.softmax(scores, dim=-1).type_as(xq)) @ xv output = self.attn_dropout(F.softmax(scores.float(), dim=-1).type_as(xq)) @ xv
output = output.transpose(1, 2).reshape(bsz, seq_len, -1) output = output.transpose(1, 2).reshape(bsz, seq_len, -1)
output = self.resid_dropout(self.o_proj(output)) output = self.resid_dropout(self.o_proj(output))
return output, past_kv return output, past_kv

View File

@ -28,8 +28,6 @@ streamlit==1.50.0
einops==0.8.1 einops==0.8.1
swanlab==0.9.8 swanlab==0.9.8
modelscope==1.37.0 modelscope==1.37.0
fastapi==0.120.1
uvicorn==0.38.0
# torch==2.6.0 # torch==2.6.0
# torchvision==0.21.0 # torchvision==0.21.0
# peft==0.7.1 # peft==0.7.1

View File

@ -202,7 +202,7 @@ async def chat_completions(request: ChatRequest):
with torch.no_grad(): with torch.no_grad():
generated_ids = model.generate( generated_ids = model.generate(
inputs["input_ids"], inputs["input_ids"],
max_new_tokens=request.max_tokens, max_length=inputs["input_ids"].shape[1] + request.max_tokens,
do_sample=True, do_sample=True,
attention_mask=inputs["attention_mask"], attention_mask=inputs["attention_mask"],
pad_token_id=tokenizer.pad_token_id, pad_token_id=tokenizer.pad_token_id,

View File

@ -8,39 +8,18 @@ DATA_PATH = '../dataset/sft_t2t_mini.jsonl'
TOKENIZER_DIR = '../model_learn_tokenizer/' TOKENIZER_DIR = '../model_learn_tokenizer/'
VOCAB_SIZE = 6400 VOCAB_SIZE = 6400
SPECIAL_TOKENS_NUM = 36 SPECIAL_TOKENS_NUM = 36
MAX_LINES = 0 # 0 表示读取全部;设为正数则只取前 N 条(快速试跑)
def get_texts(data_path, max_lines=MAX_LINES): def get_texts(data_path):
"""逐行产出用于训练 BPE 的文本,兼容两种主线数据格式。
pretrain_t2t(_mini).jsonl 是 {"text": ...},
sft_t2t(_mini).jsonl 是 {"conversations": [{"role":..., "content":...}, ...]}。
原先只解析 conversations,所以把 DATA_PATH 指向预训练语料时一条文本都取不到,
BPE 仅基于 initial_alphabet 训练,产出一个没有任何 merge 的词表,且全程不报错。
"""
used = 0
with open(data_path, 'r', encoding='utf-8', errors='ignore') as f: with open(data_path, 'r', encoding='utf-8', errors='ignore') as f:
for line in f: for i, line in enumerate(f):
if max_lines and used >= max_lines: if i >= 10000: break # 选10000行测试
break
try: try:
data = json.loads(line) data = json.loads(line)
contents = [item.get('content') for item in data.get('conversations', []) if item.get('content')]
if contents:
yield "\n".join(contents)
except json.JSONDecodeError: except json.JSONDecodeError:
continue continue
if 'text' in data:
text = str(data['text'])
else:
contents = [item.get('content') for item in data.get('conversations', []) if item.get('content')]
text = "\n".join(contents) if contents else ''
if text.strip():
used += 1
yield text
if used == 0:
raise ValueError(
f'{data_path} 中没有可用文本:每行应为 {{"text": ...}} 或 '
f'{{"conversations": [{{"role":..., "content":...}}, ...]}}。'
f'继续训练只会得到一个空词表。'
)
def train_tokenizer(data_path, tokenizer_dir, vocab_size, special_tokens_num=SPECIAL_TOKENS_NUM): def train_tokenizer(data_path, tokenizer_dir, vocab_size, special_tokens_num=SPECIAL_TOKENS_NUM):
tokenizer = Tokenizer(models.BPE()) tokenizer = Tokenizer(models.BPE())