Compare commits
13 Commits
88215c73a8
...
1095e2efdc
| Author | SHA1 | Date |
|---|---|---|
|
|
1095e2efdc | |
|
|
949488fdb4 | |
|
|
e41bf7cacf | |
|
|
b330a4d5c0 | |
|
|
b956bad49f | |
|
|
d698c06fc5 | |
|
|
984a2c3611 | |
|
|
d4b67f1783 | |
|
|
ba10c4d17b | |
|
|
cb08b9300b | |
|
|
56d4cddfd8 | |
|
|
ccb352f901 | |
|
|
99c26e9043 |
11
README.md
11
README.md
|
|
@ -230,6 +230,10 @@ minimind2系列旧模型均经过权重映射+(微调训练)QKVO线性层校
|
|||
# 克隆仓库、安装依赖
|
||||
git clone --depth 1 https://github.com/jingyaogong/minimind
|
||||
cd minimind && pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple
|
||||
#部分老版本Windows powershell不能识别&&,Windows用户应将&&链式命令改为分行写法
|
||||
git clone --depth 1 https://github.com/jingyaogong/minimind
|
||||
cd minimind
|
||||
pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple
|
||||
```
|
||||
|
||||
## Ⅰ 🚀 模型推理
|
||||
|
|
@ -242,6 +246,13 @@ cd minimind && pip install -r requirements.txt -i https://mirrors.aliyun.com/pyp
|
|||
modelscope download --model gongjy/minimind-3 --local_dir ./minimind-3
|
||||
# 方式2
|
||||
git clone https://huggingface.co/jingyaogong/minimind-3
|
||||
# 国内网络进入huggingface网站存在困难,可使用镜像站
|
||||
# Windows
|
||||
$env:HF_ENDPOINT = "https://hf-mirror.com"
|
||||
git clone https://hf-mirror.com/jingyaogong/minimind-3
|
||||
#Linux
|
||||
export HF_ENDPOINT="https://hf-mirror.com"
|
||||
git clone https://hf-mirror.com/jingyaogong/minimind-3
|
||||
```
|
||||
|
||||
### 2' CLI 推理
|
||||
|
|
|
|||
|
|
@ -229,6 +229,10 @@ After this update, maintenance for the entire `minimind-v1` series will be disco
|
|||
# Clone repository and install dependencies
|
||||
git clone --depth 1 https://github.com/jingyaogong/minimind
|
||||
cd minimind && pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple
|
||||
#Tip:Some older versions of Windows PowerShell (v5.1 and below) do not recognize the && operator.
|
||||
git clone --depth 1 https://github.com/jingyaogong/minimind
|
||||
cd minimind
|
||||
pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple
|
||||
```
|
||||
|
||||
## Ⅰ 🚀 Model Inference
|
||||
|
|
|
|||
|
|
@ -28,10 +28,13 @@ def pre_processing_chat(conversations, add_system_ratio=0.2):
|
|||
return [{'role': 'system', 'content': random.choice(SYSTEM_PROMPTS)}] + conversations
|
||||
return conversations
|
||||
|
||||
def post_processing_chat(prompt_content, empty_think_ratio=0.2):
|
||||
def post_processing_chat(prompt_content, empty_think_ratio=0.2, remove_empty_think=None):
|
||||
# 以80%概率移除空思考标签
|
||||
if '<think>\n\n</think>\n\n' in prompt_content and random.random() > empty_think_ratio:
|
||||
prompt_content = prompt_content.replace('<think>\n\n</think>\n\n', '')
|
||||
if '<think>\n\n</think>\n\n' in prompt_content:
|
||||
if remove_empty_think is None:
|
||||
remove_empty_think = random.random() > empty_think_ratio
|
||||
if remove_empty_think:
|
||||
prompt_content = prompt_content.replace('<think>\n\n</think>\n\n', '')
|
||||
return prompt_content
|
||||
|
||||
class PretrainDataset(Dataset):
|
||||
|
|
@ -139,12 +142,13 @@ class DPODataset(Dataset):
|
|||
chosen_prompt = self.tokenizer.apply_chat_template(
|
||||
chosen, tokenize=False, add_generation_prompt=False
|
||||
)
|
||||
chosen_prompt = post_processing_chat(chosen_prompt)
|
||||
|
||||
rejected_prompt = self.tokenizer.apply_chat_template(
|
||||
rejected, tokenize=False, add_generation_prompt=False
|
||||
)
|
||||
rejected_prompt = post_processing_chat(rejected_prompt)
|
||||
remove_empty_think = random.random() > 0.2
|
||||
chosen_prompt = post_processing_chat(chosen_prompt, remove_empty_think=remove_empty_think)
|
||||
rejected_prompt = post_processing_chat(rejected_prompt, remove_empty_think=remove_empty_think)
|
||||
chosen_encoding = self.tokenizer(
|
||||
chosen_prompt, truncation=True, max_length=self.max_length, padding='max_length'
|
||||
)
|
||||
|
|
@ -253,4 +257,4 @@ class AgentRLDataset(Dataset):
|
|||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pass
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -125,10 +125,14 @@ 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)):
|
||||
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:
|
||||
scores = (xq @ xk.transpose(-2, -1)) / math.sqrt(self.head_dim)
|
||||
# 先转 float32 再加 mask:-1e9 超出 float16 的表示范围(min≈-65504),
|
||||
# 在 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 attention_mask is not None: scores += (1.0 - attention_mask.unsqueeze(1).unsqueeze(2)) * -1e9
|
||||
output = self.attn_dropout(F.softmax(scores.float(), dim=-1).type_as(xq)) @ xv
|
||||
output = self.attn_dropout(F.softmax(scores, dim=-1).type_as(xq)) @ xv
|
||||
output = output.transpose(1, 2).reshape(bsz, seq_len, -1)
|
||||
output = self.resid_dropout(self.o_proj(output))
|
||||
return output, past_kv
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ streamlit==1.50.0
|
|||
einops==0.8.1
|
||||
swanlab==0.9.8
|
||||
modelscope==1.37.0
|
||||
fastapi==0.120.1
|
||||
uvicorn==0.38.0
|
||||
# torch==2.6.0
|
||||
# torchvision==0.21.0
|
||||
# peft==0.7.1
|
||||
|
|
|
|||
|
|
@ -202,7 +202,7 @@ async def chat_completions(request: ChatRequest):
|
|||
with torch.no_grad():
|
||||
generated_ids = model.generate(
|
||||
inputs["input_ids"],
|
||||
max_length=inputs["input_ids"].shape[1] + request.max_tokens,
|
||||
max_new_tokens=request.max_tokens,
|
||||
do_sample=True,
|
||||
attention_mask=inputs["attention_mask"],
|
||||
pad_token_id=tokenizer.pad_token_id,
|
||||
|
|
|
|||
|
|
@ -8,18 +8,39 @@ DATA_PATH = '../dataset/sft_t2t_mini.jsonl'
|
|||
TOKENIZER_DIR = '../model_learn_tokenizer/'
|
||||
VOCAB_SIZE = 6400
|
||||
SPECIAL_TOKENS_NUM = 36
|
||||
MAX_LINES = 0 # 0 表示读取全部;设为正数则只取前 N 条(快速试跑)
|
||||
|
||||
def get_texts(data_path):
|
||||
def get_texts(data_path, max_lines=MAX_LINES):
|
||||
"""逐行产出用于训练 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:
|
||||
for i, line in enumerate(f):
|
||||
if i >= 10000: break # 选10000行测试
|
||||
for line in f:
|
||||
if max_lines and used >= max_lines:
|
||||
break
|
||||
try:
|
||||
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:
|
||||
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):
|
||||
tokenizer = Tokenizer(models.BPE())
|
||||
|
|
|
|||
Loading…
Reference in New Issue