NovaAI6868 commited on
Commit
cd75f6e
·
verified ·
1 Parent(s): fb98c0c

Upload folder using huggingface_hub

Browse files
Files changed (6) hide show
  1. all.jsonl +0 -0
  2. checkpoints/checkpoint_final.pt +3 -0
  3. infer.py +70 -0
  4. spm.model +3 -0
  5. spm.vocab +0 -0
  6. train.py +237 -0
all.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
checkpoints/checkpoint_final.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5944f5d325c8c7f6e666aca78f6883eab157f9d416fc4edd748f0308a2c409f0
3
+ size 284829424
infer.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ==========================
2
+ # infer.py
3
+ # ==========================
4
+ # Usage:
5
+ # python infer.py --checkpoint checkpoints/checkpoint_epoch3.pt --spm_model spm.model
6
+
7
+ import argparse
8
+ import torch
9
+ import sentencepiece as spm
10
+ from train import GPT, GPTConfig
11
+
12
+ def top_k_logits(logits, k):
13
+ v, ix = torch.topk(logits, k)
14
+ out = logits.clone()
15
+ out[out < v[..., -1, None]] = -float('Inf')
16
+ return out
17
+
18
+ def generate(model, sp, device, prompt, max_new_tokens=128, temperature=1.0, top_k=50):
19
+ model.eval()
20
+ ids = sp.EncodeAsIds('<s>' + prompt + '<sep>')
21
+ context = torch.tensor(ids, dtype=torch.long, device=device).unsqueeze(0)
22
+
23
+ for _ in range(max_new_tokens):
24
+ seq = context if context.size(1) <= model.block_size else context[:, -model.block_size:]
25
+ logits, _ = model(seq)
26
+ logits = logits[:, -1, :] / max(temperature, 1e-8)
27
+ if top_k is not None:
28
+ logits = top_k_logits(logits, top_k)
29
+ probs = torch.softmax(logits, dim=-1)
30
+ next_id = torch.multinomial(probs, num_samples=1)
31
+ context = torch.cat([context, next_id], dim=1)
32
+ if next_id.item() == sp.EncodeAsIds('</s>')[0]:
33
+ break
34
+ out_ids = context[0].tolist()
35
+ text = sp.DecodeIds([i for i in out_ids if i != 0])
36
+ if '<sep>' in text:
37
+ parts = text.split('<sep>')
38
+ if len(parts) >= 2:
39
+ return parts[1].replace('</s>','').strip()
40
+ return text
41
+
42
+ def main():
43
+ parser = argparse.ArgumentParser()
44
+ parser.add_argument('--checkpoint', type=str, required=True)
45
+ parser.add_argument('--spm_model', type=str, default='spm.model')
46
+ parser.add_argument('--device', type=str, default='cuda' if torch.cuda.is_available() else 'cpu')
47
+ parser.add_argument('--prompt', type=str, default='你好,请自我介绍。')
48
+ parser.add_argument('--max_new_tokens', type=int, default=1024)
49
+ parser.add_argument('--temperature', type=float, default=0.8)
50
+ parser.add_argument('--top_k', type=int, default=40)
51
+ args = parser.parse_args()
52
+
53
+ sp = spm.SentencePieceProcessor()
54
+ sp.Load(args.spm_model)
55
+
56
+ ckpt = torch.load(args.checkpoint, map_location=args.device)
57
+ cfg = ckpt.get('config')
58
+ config = GPTConfig(vocab_size=cfg['vocab_size'], n_layer=cfg['n_layer'], n_head=cfg['n_head'], n_embd=cfg['n_embd'], block_size=cfg['block_size'], dropout=cfg['dropout'])
59
+ model = GPT(config).to(args.device)
60
+ model.load_state_dict(ckpt['model_state'])
61
+
62
+ param_count = sum(p.numel() for p in model.parameters())
63
+ print(f"Loaded model parameters: {param_count:,} ({param_count/1e9:.3f} B)")
64
+
65
+ ans = generate(model, sp, args.device, args.prompt, max_new_tokens=args.max_new_tokens, temperature=args.temperature, top_k=args.top_k)
66
+ print('\n=== RESPONSE ===\n')
67
+ print(ans)
68
+
69
+ if __name__ == '__main__':
70
+ main()
spm.model ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:478beeaf55eb5dedd0b87f0cd5c032341faec7b3500f7636d55407f5cc6cc809
3
+ size 989403
spm.vocab ADDED
The diff for this file is too large to render. See raw diff
 
train.py ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ==========================
2
+ # train.py
3
+ # ==========================
4
+ # Usage:
5
+ # python train.py --data_path all.jsonl --spm_model spm.model
6
+ # Requirements:
7
+ # pip install torch sentencepiece tqdm
8
+
9
+ import os
10
+ import json
11
+ import sentencepiece as spm
12
+ from argparse import ArgumentParser
13
+ from tqdm import tqdm
14
+
15
+ import torch
16
+ from torch import nn
17
+ from torch.utils.data import Dataset, DataLoader
18
+
19
+ # --------------------------
20
+ # Simple Decoder-only Transformer (GPT-like)
21
+ # --------------------------
22
+
23
+ class GPTConfig:
24
+ def __init__(self, vocab_size, n_layer=12, n_head=12, n_embd=768, block_size=1024, dropout=0.1):
25
+ self.vocab_size = vocab_size
26
+ self.n_layer = n_layer
27
+ self.n_head = n_head
28
+ self.n_embd = n_embd
29
+ self.block_size = block_size
30
+ self.dropout = dropout
31
+
32
+
33
+ class CausalSelfAttention(nn.Module):
34
+ def __init__(self, config):
35
+ super().__init__()
36
+ assert config.n_embd % config.n_head == 0
37
+ self.c_attn = nn.Linear(config.n_embd, 3 * config.n_embd)
38
+ self.c_proj = nn.Linear(config.n_embd, config.n_embd)
39
+ self.n_head = config.n_head
40
+ self.dropout = nn.Dropout(config.dropout)
41
+
42
+ def forward(self, x, attn_mask=None):
43
+ B, T, C = x.size()
44
+ qkv = self.c_attn(x) # (B, T, 3*C)
45
+ q, k, v = qkv.split(C, dim=2)
46
+ # reshape for multi-head
47
+ q = q.view(B, T, self.n_head, C // self.n_head).transpose(1,2) # (B, nh, T, hs)
48
+ k = k.view(B, T, self.n_head, C // self.n_head).transpose(1,2)
49
+ v = v.view(B, T, self.n_head, C // self.n_head).transpose(1,2)
50
+
51
+ att = (q @ k.transpose(-2, -1)) / (C // self.n_head) ** 0.5 # (B, nh, T, T)
52
+ # causal mask
53
+ mask = torch.tril(torch.ones(T, T, device=x.device)).view(1, 1, T, T)
54
+ att = att.masked_fill(mask == 0, float('-inf'))
55
+ att = torch.softmax(att, dim=-1)
56
+ att = self.dropout(att)
57
+
58
+ y = att @ v # (B, nh, T, hs)
59
+ y = y.transpose(1,2).contiguous().view(B, T, C)
60
+ y = self.c_proj(y)
61
+ y = self.dropout(y)
62
+ return y
63
+
64
+
65
+ class Block(nn.Module):
66
+ def __init__(self, config):
67
+ super().__init__()
68
+ self.ln1 = nn.LayerNorm(config.n_embd)
69
+ self.attn = CausalSelfAttention(config)
70
+ self.ln2 = nn.LayerNorm(config.n_embd)
71
+ self.mlp = nn.Sequential(
72
+ nn.Linear(config.n_embd, 4 * config.n_embd),
73
+ nn.GELU(),
74
+ nn.Linear(4 * config.n_embd, config.n_embd),
75
+ nn.Dropout(config.dropout),
76
+ )
77
+
78
+ def forward(self, x):
79
+ x = x + self.attn(self.ln1(x))
80
+ x = x + self.mlp(self.ln2(x))
81
+ return x
82
+
83
+
84
+ class GPT(nn.Module):
85
+ def __init__(self, config):
86
+ super().__init__()
87
+ self.tok_emb = nn.Embedding(config.vocab_size, config.n_embd)
88
+ self.pos_emb = nn.Parameter(torch.zeros(1, config.block_size, config.n_embd))
89
+ self.drop = nn.Dropout(config.dropout)
90
+ self.blocks = nn.ModuleList([Block(config) for _ in range(config.n_layer)])
91
+ self.ln_f = nn.LayerNorm(config.n_embd)
92
+ self.head = nn.Linear(config.n_embd, config.vocab_size, bias=False)
93
+ self.block_size = config.block_size
94
+
95
+ # initialize
96
+ self.apply(self._init_weights)
97
+
98
+ def _init_weights(self, module):
99
+ if isinstance(module, nn.Linear):
100
+ nn.init.normal_(module.weight, mean=0.0, std=0.02)
101
+ if module.bias is not None:
102
+ nn.init.zeros_(module.bias)
103
+ elif isinstance(module, nn.Embedding):
104
+ nn.init.normal_(module.weight, mean=0.0, std=0.02)
105
+
106
+ def forward(self, idx, targets=None):
107
+ B, T = idx.size()
108
+ assert T <= self.block_size
109
+ token_embeddings = self.tok_emb(idx) # (B, T, C)
110
+ x = token_embeddings + self.pos_emb[:, :T, :]
111
+ x = self.drop(x)
112
+ for block in self.blocks:
113
+ x = block(x)
114
+ x = self.ln_f(x)
115
+ logits = self.head(x)
116
+
117
+ loss = None
118
+ if targets is not None:
119
+ # shift logits and targets for next-token prediction
120
+ logits = logits[:, :-1, :].contiguous()
121
+ targets = targets[:, 1:].contiguous()
122
+ loss = nn.functional.cross_entropy(logits.view(-1, logits.size(-1)), targets.view(-1))
123
+ return logits, loss
124
+
125
+
126
+ # --------------------------
127
+ # Dataset and helpers
128
+ # --------------------------
129
+
130
+ class QADataset(Dataset):
131
+ def __init__(self, path, sp_model, block_size=1024):
132
+ self.examples = []
133
+ self.block_size = block_size
134
+ self.sp = sp_model
135
+ with open(path, 'r', encoding='utf-8') as f:
136
+ for line in f:
137
+ obj = json.loads(line)
138
+ q = obj.get('question','')
139
+ a = obj.get('answer','')
140
+ # format: <bos> question <sep> answer <eos>
141
+ text = "<s>" + q + "<sep>" + a + "</s>"
142
+ ids = self.sp.EncodeAsIds(text)
143
+ if len(ids) > 2:
144
+ # truncate or pad later
145
+ self.examples.append(ids)
146
+
147
+ def __len__(self):
148
+ return len(self.examples)
149
+
150
+ def __getitem__(self, idx):
151
+ ids = self.examples[idx]
152
+ # pad/truncate to block_size
153
+ if len(ids) > self.block_size:
154
+ ids = ids[:self.block_size]
155
+ else:
156
+ ids = ids + [0] * (self.block_size - len(ids))
157
+ return torch.tensor(ids, dtype=torch.long)
158
+
159
+
160
+ def collate_fn(batch):
161
+ batch = torch.stack(batch, dim=0)
162
+ return batch, batch # inputs and targets are same sequence for causal LM
163
+
164
+
165
+ # --------------------------
166
+ # Main training loop
167
+ # --------------------------
168
+
169
+
170
+ def train(args):
171
+ # prepare sentencepiece model (if not exists, train it)
172
+ if not os.path.exists(args.spm_model):
173
+ print('Training SentencePiece model...')
174
+ # create a temporary file with concatenated text
175
+ tmp_txt = 'spm_input.txt'
176
+ with open(args.data_path, 'r', encoding='utf-8') as fin, open(tmp_txt, 'w', encoding='utf-8') as fout:
177
+ for line in fin:
178
+ obj = json.loads(line)
179
+ text = obj.get('question','') + '\n' + obj.get('answer','') + '\n'
180
+ fout.write(text)
181
+ spm.SentencePieceTrainer.Train(f'--input={tmp_txt} --model_prefix=spm --vocab_size={args.vocab_size} --model_type=bpe --character_coverage=0.9995')
182
+ os.remove(tmp_txt)
183
+ sp = spm.SentencePieceProcessor()
184
+ sp.Load('spm.model')
185
+ else:
186
+ sp = spm.SentencePieceProcessor()
187
+ sp.Load(args.spm_model)
188
+
189
+ dataset = QADataset(args.data_path, sp, block_size=args.block_size)
190
+ print(f"Loaded {len(dataset)} examples")
191
+ dataloader = DataLoader(dataset, batch_size=args.batch_size, shuffle=True, collate_fn=lambda x: collate_fn(x))
192
+
193
+ config = GPTConfig(vocab_size=args.vocab_size, n_layer=args.n_layer, n_head=args.n_head, n_embd=args.n_embd, block_size=args.block_size, dropout=args.dropout)
194
+ model = GPT(config).to(args.device)
195
+
196
+ # print parameter count
197
+ param_count = sum(p.numel() for p in model.parameters())
198
+ print(f"Model parameters: {param_count:,} ({param_count/1e9:.3f} B)")
199
+
200
+ optimizer = torch.optim.AdamW(model.parameters(), lr=args.lr)
201
+
202
+ model.train()
203
+ for epoch in range(args.epochs):
204
+ pbar = tqdm(dataloader, desc=f"Epoch {epoch+1}/{args.epochs}")
205
+ for batch_inputs, batch_targets in pbar:
206
+ batch_inputs = batch_inputs.to(args.device)
207
+ batch_targets = batch_targets.to(args.device)
208
+ logits, loss = model(batch_inputs, targets=batch_targets)
209
+ optimizer.zero_grad()
210
+ loss.backward()
211
+ torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
212
+ optimizer.step()
213
+ pbar.set_postfix(loss=loss.item())
214
+
215
+ # save checkpoint each epoch
216
+ os.makedirs(args.out_dir, exist_ok=True)
217
+ torch.save({'model_state': model.state_dict(), 'sp_model': args.spm_model, 'config': vars(config)}, os.path.join(args.out_dir, f'checkpoint_final.pt'))
218
+
219
+
220
+ if __name__ == '__main__':
221
+ parser = ArgumentParser()
222
+ parser.add_argument('--data_path', type=str, default='all.jsonl')
223
+ parser.add_argument('--spm_model', type=str, default='spm.model')
224
+ parser.add_argument('--vocab_size', type=int, default=32000)
225
+ parser.add_argument('--block_size', type=int, default=1024)
226
+ parser.add_argument('--n_layer', type=int, default=3)
227
+ parser.add_argument('--n_head', type=int, default=3)
228
+ parser.add_argument('--n_embd', type=int, default=768)
229
+ parser.add_argument('--batch_size', type=int, default=30)
230
+ parser.add_argument('--epochs', type=int, default=300)
231
+ parser.add_argument('--lr', type=float, default=3e-4)
232
+ parser.add_argument('--dropout', type=float, default=0.1)
233
+ parser.add_argument('--device', type=str, default='cuda' if torch.cuda.is_available() else 'cpu')
234
+ parser.add_argument('--out_dir', type=str, default='checkpoints')
235
+ args = parser.parse_args()
236
+ train(args)
237
+