-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
183 lines (139 loc) · 5.61 KB
/
Copy pathmain.py
File metadata and controls
183 lines (139 loc) · 5.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
import asyncio
import json
import torch
import tiktoken
from model import GPTModel, get_batch, device
from contextlib import asynccontextmanager
enc = tiktoken.get_encoding("p50k_base")
m = None
@asynccontextmanager
async def lifespan():
global enc, m
# Load the ML model
model = GPTModel(enc.n_vocab)
m = model.to(device)
print("Loading model...")
m.load_state_dict(torch.load("mine.pt"))
yield
# Clean up the ML models and release the resources
class CreateCompletionRequest:
def __init__(self, prompt, stream=False, stop=None, max_tokens=100, temperature=1.0):
self.prompt = prompt
self.stream = stream
self.stop = stop or []
self.max_tokens = max_tokens
self.temperature = temperature
def on_generated(token):
return
def completions(body: CreateCompletionRequest):
""" Mimic the Llama-CPP server's completion functionality """
global enc, m
stops = [{"choices": [{"text": enc.encode(stop)[0]}]} for stop in body.stop]
prompt_tokens = enc.encode(body.prompt)
idx = torch.tensor([prompt_tokens], dtype=torch.long, device=device)
ret = m.generate(idx, max_new_tokens=body.max_tokens, stop_tokens=stops, generated=on_generated)
tokens = ret.tolist()[0]
if body.stream:
# This is a terrible hack to do response streaming: just wait until it is done and then dribble out the tokens.
# Couldn't be bothered converting my model to async.
chunks = [json.dumps({"choices": [{"text": enc.decode([token])}]}) for token in tokens]
async def event_generator():
while True:
if len(chunks) == 0:
break
yield chunks.pop(0)
await asyncio.sleep(0.01)
return event_generator() # Return generator function for streaming response
else:
return {"resp": enc.decode(tokens)}
def text_from_path(path):
""" Load all the text files found at this path into one huge lump of text and return it. """
import glob
import os
text = ""
print(f"Searching for text files in: {os.path.abspath(path)}")
files = glob.glob(path+"*.txt")
print(f"Found {len(files)} text files:")
for filename in files:
print(filename)
try:
with open(filename, 'r', encoding='utf-8') as f:
lines = f.readlines()
for line in lines:
text += line
except UnicodeDecodeError:
print(f"Warning: Could not read {filename} with UTF-8 encoding. Trying alternative encodings.")
try:
with open(filename, 'r', encoding='latin-1') as f:
lines = f.readlines()
for line in lines:
text += line
except Exception as e:
print(f"Error reading {filename}: {e}")
print(f"Total text length: {len(text)} characters")
return text
def text_from_file():
""" Open a single file and return its contents. """
text = ""
with open('input.txt') as f:
lines = f.readlines()
for line in lines:
text += line
return text
def train():
""" Train the model and save it at regular checkpoints. """
global enc, m
# Change this path to where your training data is, as a folder full of .txt files
text = text_from_path("./")
tokens = torch.tensor(enc.encode(text), dtype=torch.long)
print(f"Vocab size {enc.n_vocab}")
print(f"Training data size {len(tokens)}")
model = GPTModel(enc.n_vocab)
m = model.to(device)
print(sum(p.numel() for p in m.parameters()) / 1e6, "M parameters")
# Try to load the old model so we can continue training it from where we left off.
try:
m.load_state_dict(torch.load("mine.pt"))
except FileNotFoundError:
print("Model file not found, starting new training...")
print("Loaded model. Beginning training...")
optimizer = torch.optim.AdamW(m.parameters(), lr=1e-3)
epoch = 300
for checkpoints in range(10):
loss_total = 0
for steps in range(epoch):
xb, yb = get_batch(tokens)
logits, loss = m(xb, yb)
optimizer.zero_grad(set_to_none=True)
loss.backward()
optimizer.step()
loss_total += loss.item()
print(loss_total/epoch)
torch.save(model.state_dict(), "mine.pt")
print("Done with training!")
def inference():
""" Generate a stream of text from a starting prompt. """
global m, enc
model = GPTModel(enc.n_vocab)
m = model.to(device)
# This is the model that was trained previously. The hyperparameters in model.py must match exactly to when it was trained, or there'll be an error.
print("Loading model...")
m.load_state_dict(torch.load("mine.pt"))
def on_generated(token):
#print(f"{enc.decode([token])}({token.item()})", end="")
print(enc.decode([token]), end="")
stops = [enc.encode(".")[0], enc.encode("?")[0], enc.encode("!")[0]]
prompt = "Q: Why did the chicken cross the road?\n"
print(f"{prompt}", end="")
prompt_tokens = enc.encode(prompt)
# supress = torch.tensor([0, 930], dtype=torch.long, device=device) # the and and: 290, 262 as a testy=
supress = []
idx = torch.tensor([prompt_tokens], dtype=torch.long, device=device)
m.generate(idx, max_new_tokens=100, stop_tokens=None, generated=on_generated, top_k=16, sample=True, supress_tokens=supress)
print("\n")
if __name__ == '__main__':
print(f"Using device {device}")
# Uncomment this to train the model
train()
# Uncomment this for inference testing
# inference()