lora-easy 2.1__tar.gz → 2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {lora_easy-2.1 → lora_easy-2.2}/PKG-INFO +1 -1
- {lora_easy-2.1 → lora_easy-2.2}/lora_easy.egg-info/PKG-INFO +1 -1
- {lora_easy-2.1 → lora_easy-2.2}/lora_ez/model.py +33 -17
- {lora_easy-2.1 → lora_easy-2.2}/lora_ez/session.py +1 -1
- {lora_easy-2.1 → lora_easy-2.2}/pyproject.toml +1 -1
- {lora_easy-2.1 → lora_easy-2.2}/README.md +0 -0
- {lora_easy-2.1 → lora_easy-2.2}/lora_easy.egg-info/SOURCES.txt +0 -0
- {lora_easy-2.1 → lora_easy-2.2}/lora_easy.egg-info/dependency_links.txt +0 -0
- {lora_easy-2.1 → lora_easy-2.2}/lora_easy.egg-info/requires.txt +0 -0
- {lora_easy-2.1 → lora_easy-2.2}/lora_easy.egg-info/top_level.txt +0 -0
- {lora_easy-2.1 → lora_easy-2.2}/lora_ez/__init__.py +0 -0
- {lora_easy-2.1 → lora_easy-2.2}/lora_ez/agent.py +0 -0
- {lora_easy-2.1 → lora_easy-2.2}/lora_ez/commands.py +0 -0
- {lora_easy-2.1 → lora_easy-2.2}/lora_ez/deco.py +0 -0
- {lora_easy-2.1 → lora_easy-2.2}/setup.cfg +0 -0
|
@@ -87,20 +87,22 @@ class LoraModel:
|
|
|
87
87
|
|
|
88
88
|
def __repr__(self) -> str:
|
|
89
89
|
n = sum(p.numel() for p in self.model.parameters())
|
|
90
|
-
return f"{self
|
|
90
|
+
return f"{self:t}: LoraModel('{self:i}', {n/1e9:.1f}B params, lora={'yes' if self._has_lora() else 'no'})"
|
|
91
91
|
|
|
92
92
|
def __str__(self):
|
|
93
93
|
return self.name.title()
|
|
94
94
|
|
|
95
|
-
def __format__(self, spec=
|
|
96
|
-
if spec
|
|
95
|
+
def __format__(self, spec=''):
|
|
96
|
+
if spec == '':
|
|
97
97
|
return self.name
|
|
98
98
|
elif spec == 'l':
|
|
99
99
|
return self.name.lower()
|
|
100
100
|
elif spec == 't':
|
|
101
101
|
return self.name
|
|
102
|
+
elif spec == 'i':
|
|
103
|
+
return self.id_
|
|
102
104
|
else:
|
|
103
|
-
raise ValueError('`spec` should be one of
|
|
105
|
+
raise ValueError('`spec` should be one of [empty] | l | t')
|
|
104
106
|
|
|
105
107
|
def _has_lora(self) -> bool:
|
|
106
108
|
return self.peft_model is not None
|
|
@@ -183,13 +185,12 @@ class LoraModel:
|
|
|
183
185
|
Usage::
|
|
184
186
|
|
|
185
187
|
with model.chat_session("./history.json", auto_save=True) as s:
|
|
186
|
-
s
|
|
187
|
-
s
|
|
188
|
+
s> "Hello"
|
|
189
|
+
s> "What do you think?"
|
|
188
190
|
s.history # all turns so far
|
|
189
191
|
|
|
190
192
|
When the history grows beyond ``max_tokens`` tokens it is automatically
|
|
191
193
|
summarised by the model and replaced with a compact system message.
|
|
192
|
-
``system_prompt`` overrides ``self.system_prompt`` for the session.
|
|
193
194
|
"""
|
|
194
195
|
from .session import _ChatSession
|
|
195
196
|
return _ChatSession(self, save_path, auto_save, max_tokens,
|
|
@@ -197,7 +198,8 @@ class LoraModel:
|
|
|
197
198
|
|
|
198
199
|
# -- Training -----------------------------------------------------------
|
|
199
200
|
|
|
200
|
-
def train(self, conversations: list[dict], save_checkpoints: bool = False,
|
|
201
|
+
def train(self, conversations: list[dict], save_checkpoints: bool = False,
|
|
202
|
+
max_length: int = 256, **kwargs):
|
|
201
203
|
"""Fine-tune with LoRA on ShareGPT-format conversations.
|
|
202
204
|
|
|
203
205
|
Pipeline: render each convo via chat template -> tokenize with pad+trunc ->
|
|
@@ -207,22 +209,31 @@ class LoraModel:
|
|
|
207
209
|
Args:
|
|
208
210
|
save_checkpoints: if set, save a checkpoint per epoch to
|
|
209
211
|
``./lora-output-{name}``. False (default) produces no files.
|
|
212
|
+
max_length: tokenizer truncation/padding length. Raise it when
|
|
213
|
+
conversations are long (multi-turn or long replies) —
|
|
214
|
+
truncation cuts from the END, which would clip the target
|
|
215
|
+
assistant reply. Default 256.
|
|
210
216
|
"""
|
|
211
217
|
if not self.lora_enabled:
|
|
212
218
|
self.enable_lora()
|
|
213
219
|
self.model.config.use_cache = False
|
|
214
220
|
|
|
215
221
|
texts = [self._format(self._render(c), add_gen=False) for c in conversations]
|
|
216
|
-
tok = self.tokenizer(texts, truncation=True, padding="max_length",
|
|
222
|
+
tok = self.tokenizer(texts, truncation=True, padding="max_length",
|
|
223
|
+
max_length=max_length)
|
|
217
224
|
dataset = [{"input_ids": inds, "attention_mask": ms,
|
|
218
225
|
"labels": [-100 if m == 0 else i for i, m in zip(inds, ms)]}
|
|
219
226
|
for inds, ms in zip(tok["input_ids"], tok["attention_mask"])]
|
|
220
227
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
228
|
+
# friendly defaults; translate to TrainingArguments names below
|
|
229
|
+
defaults = {"output_dir": f"./lora-output-{self:l}" if save_checkpoints else "./temp_output",
|
|
230
|
+
"epochs": 30, "lr": 3e-4,
|
|
231
|
+
"per_device_train_batch_size": 4, "logging_steps": 5}
|
|
232
|
+
kwargs = defaults | kwargs
|
|
224
233
|
args = TrainingArguments(
|
|
225
|
-
|
|
234
|
+
num_train_epochs=kwargs.pop("epochs"),
|
|
235
|
+
learning_rate=kwargs.pop("lr"),
|
|
236
|
+
**kwargs,
|
|
226
237
|
save_strategy="no" if not save_checkpoints else "epoch",
|
|
227
238
|
report_to="none",
|
|
228
239
|
)
|
|
@@ -272,10 +283,15 @@ class LoraModel:
|
|
|
272
283
|
(Path(path) / "description.txt").write_text(self.description)
|
|
273
284
|
|
|
274
285
|
def load(self, path: str | None = None):
|
|
275
|
-
# load the adapter
|
|
276
|
-
|
|
277
|
-
|
|
286
|
+
# load the adapter from a LOCAL directory
|
|
287
|
+
p = Path(path or self.save_path)
|
|
288
|
+
if not p.is_dir():
|
|
289
|
+
raise FileNotFoundError(
|
|
290
|
+
f"""Adapter directory not found: '{p}'.
|
|
291
|
+
If it lives elsewhere pass the path explicitly, e.g. m.load('/path/to/ivanka-lora').""")
|
|
292
|
+
self.peft_model = PeftModel.from_pretrained(self.base, str(p),
|
|
293
|
+
is_trainable=True)
|
|
278
294
|
# restore description if it was saved alongside the adapter
|
|
279
|
-
desc_file =
|
|
295
|
+
desc_file = p / "description.txt"
|
|
280
296
|
if desc_file.exists():
|
|
281
297
|
self._description = desc_file.read_text()
|
|
@@ -73,7 +73,7 @@ class _ChatSession:
|
|
|
73
73
|
"""
|
|
74
74
|
self._maybe_summarize(prompt)
|
|
75
75
|
self.history.append({"role": "user", "content": prompt})
|
|
76
|
-
reply = self.model.chat(prompt, history=self.history[:-1],
|
|
76
|
+
reply = self.model.chat(prompt, history=self.history[:-1],
|
|
77
77
|
system_prompt=self.system_prompt, **kwargs)
|
|
78
78
|
self.history.append({"role": "assistant", "content": reply})
|
|
79
79
|
return reply
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|