mirror of
https://github.com/p-e-w/heretic.git
synced 2026-09-27 14:31:26 -07:00
fix: fix issues in automatic reproduction system (#352)
* fix: Check if a model is gated / accessible * fix: handle unknown gated models * feat: Auto install requirements * simplify * Revert "simplify" This reverts commit10287926e9. * Revert "feat: Auto install requirements" This reverts commitf4be1abd04. * fix: Seed pytorch method * reference, style * simplify token * feat: Export strategy in reproduce.json, v2 * style: Name * simplify export strategy * style: Rename * enumeration * maybe remove seed as well * fix: don't lock settings with permanent strategy * simplify no choice, use try/finally block
This commit is contained in:
@@ -123,10 +123,6 @@ n_trials = 200
|
|||||||
# Number of trials that use random sampling for the purpose of exploration.
|
# Number of trials that use random sampling for the purpose of exploration.
|
||||||
n_startup_trials = 60
|
n_startup_trials = 60
|
||||||
|
|
||||||
# Random seed for reproducible optimization. Set to an integer to enable.
|
|
||||||
# Applies to Python's random module, NumPy, PyTorch, and Optuna.
|
|
||||||
# seed = 75
|
|
||||||
|
|
||||||
# Directory to save and load study progress to/from.
|
# Directory to save and load study progress to/from.
|
||||||
study_checkpoint_dir = "checkpoints"
|
study_checkpoint_dir = "checkpoints"
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,11 @@ class RowNormalization(str, Enum):
|
|||||||
FULL = "full"
|
FULL = "full"
|
||||||
|
|
||||||
|
|
||||||
|
class ExportStrategy(str, Enum):
|
||||||
|
MERGE = "merge"
|
||||||
|
ADAPTER = "adapter"
|
||||||
|
|
||||||
|
|
||||||
class DatasetSpecification(BaseModel):
|
class DatasetSpecification(BaseModel):
|
||||||
dataset: str = Field(
|
dataset: str = Field(
|
||||||
description="Hugging Face dataset ID, or path to dataset on disk."
|
description="Hugging Face dataset ID, or path to dataset on disk."
|
||||||
@@ -412,6 +417,11 @@ class Settings(BaseSettings):
|
|||||||
description="Maximum size for individual safetensors files generated when exporting a model.",
|
description="Maximum size for individual safetensors files generated when exporting a model.",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
export_strategy: ExportStrategy | None = Field(
|
||||||
|
default=None,
|
||||||
|
description='How to export the model: "merge", "adapter", or unset to prompt the user.',
|
||||||
|
)
|
||||||
|
|
||||||
refusal_markers: list[str] = Field(
|
refusal_markers: list[str] = Field(
|
||||||
default=[
|
default=[
|
||||||
"sorry",
|
"sorry",
|
||||||
|
|||||||
+32
-21
@@ -62,7 +62,7 @@ from rich.table import Table
|
|||||||
from rich.traceback import install
|
from rich.traceback import install
|
||||||
|
|
||||||
from .analyzer import Analyzer
|
from .analyzer import Analyzer
|
||||||
from .config import QuantizationMethod
|
from .config import ExportStrategy, QuantizationMethod
|
||||||
from .evaluator import Evaluator
|
from .evaluator import Evaluator
|
||||||
from .model import AbliterationParameters, Model, get_model_class
|
from .model import AbliterationParameters, Model, get_model_class
|
||||||
from .reproduce import (
|
from .reproduce import (
|
||||||
@@ -88,13 +88,19 @@ from .utils import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def obtain_merge_strategy(settings: Settings, model: Model) -> str | None:
|
def obtain_export_strategy(
|
||||||
|
settings: Settings,
|
||||||
|
model: Model,
|
||||||
|
) -> ExportStrategy | None:
|
||||||
"""
|
"""
|
||||||
Prompts the user for how to proceed with saving the model.
|
Gets the export strategy from settings or prompts the user.
|
||||||
Provides info to the user if the model is quantized on memory use.
|
Provides info to the user if the model is quantized on memory use.
|
||||||
Returns "merge", "adapter", or None (if cancelled/invalid).
|
Returns an export strategy, or None if cancelled.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if settings.export_strategy is not None:
|
||||||
|
return settings.export_strategy
|
||||||
|
|
||||||
if settings.quantization == QuantizationMethod.BNB_4BIT:
|
if settings.quantization == QuantizationMethod.BNB_4BIT:
|
||||||
print()
|
print()
|
||||||
print(
|
print(
|
||||||
@@ -148,11 +154,11 @@ def obtain_merge_strategy(settings: Settings, model: Model) -> str | None:
|
|||||||
if settings.quantization == QuantizationMethod.NONE
|
if settings.quantization == QuantizationMethod.NONE
|
||||||
else " (requires sufficient RAM)"
|
else " (requires sufficient RAM)"
|
||||||
),
|
),
|
||||||
value="merge",
|
value=ExportStrategy.MERGE,
|
||||||
),
|
),
|
||||||
Choice(
|
Choice(
|
||||||
title="Save LoRA adapter only (can be merged later)",
|
title="Save LoRA adapter only (can be merged later)",
|
||||||
value="adapter",
|
value=ExportStrategy.ADAPTER,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -224,7 +230,7 @@ def run():
|
|||||||
# FIXME: "Reproduction"/"reproducibility" name inconsistency!
|
# FIXME: "Reproduction"/"reproducibility" name inconsistency!
|
||||||
reproduction_information = load_reproduction_information(settings.reproduce)
|
reproduction_information = load_reproduction_information(settings.reproduce)
|
||||||
|
|
||||||
if reproduction_information["version"] not in ["1"]:
|
if reproduction_information["version"] not in ["1", "2"]:
|
||||||
print(
|
print(
|
||||||
(
|
(
|
||||||
f"[red]Unsupported file format version: [bold]{reproduction_information['version']}[/].[/] "
|
f"[red]Unsupported file format version: [bold]{reproduction_information['version']}[/].[/] "
|
||||||
@@ -865,11 +871,11 @@ def run():
|
|||||||
if not save_directory:
|
if not save_directory:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
strategy = obtain_merge_strategy(settings, model)
|
strategy = obtain_export_strategy(settings, model)
|
||||||
if strategy is None:
|
if strategy is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if strategy == "adapter":
|
if strategy == ExportStrategy.ADAPTER:
|
||||||
print("Saving LoRA adapter...")
|
print("Saving LoRA adapter...")
|
||||||
model.model.save_pretrained(
|
model.model.save_pretrained(
|
||||||
save_directory,
|
save_directory,
|
||||||
@@ -923,7 +929,7 @@ def run():
|
|||||||
continue
|
continue
|
||||||
private = visibility == "Private"
|
private = visibility == "Private"
|
||||||
|
|
||||||
strategy = obtain_merge_strategy(settings, model)
|
strategy = obtain_export_strategy(settings, model)
|
||||||
if strategy is None:
|
if strategy is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -973,7 +979,7 @@ def run():
|
|||||||
else:
|
else:
|
||||||
reproducibility_information = "none"
|
reproducibility_information = "none"
|
||||||
|
|
||||||
if strategy == "adapter":
|
if strategy == ExportStrategy.ADAPTER:
|
||||||
print("Uploading LoRA adapter...")
|
print("Uploading LoRA adapter...")
|
||||||
model.model.push_to_hub(
|
model.model.push_to_hub(
|
||||||
repo_id,
|
repo_id,
|
||||||
@@ -1036,17 +1042,22 @@ def run():
|
|||||||
# Set the number of trials to the number of actual completed trials
|
# Set the number of trials to the number of actual completed trials
|
||||||
# for the reproduction configuration.
|
# for the reproduction configuration.
|
||||||
settings.n_trials = count_completed_trials()
|
settings.n_trials = count_completed_trials()
|
||||||
|
current_export_strategy = settings.export_strategy
|
||||||
|
settings.export_strategy = strategy
|
||||||
|
|
||||||
upload_reproduce_folder(
|
try:
|
||||||
repo_id,
|
upload_reproduce_folder(
|
||||||
settings,
|
repo_id,
|
||||||
token,
|
settings,
|
||||||
checkpoint_path=study_checkpoint_file,
|
token,
|
||||||
trial=trial,
|
checkpoint_path=study_checkpoint_file,
|
||||||
include_system_information=(
|
trial=trial,
|
||||||
reproducibility_information == "full"
|
include_system_information=(
|
||||||
),
|
reproducibility_information == "full"
|
||||||
)
|
),
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
settings.export_strategy = current_export_strategy
|
||||||
|
|
||||||
print(f"Model uploaded to [bold]{repo_id}[/].")
|
print(f"Model uploaded to [bold]{repo_id}[/].")
|
||||||
|
|
||||||
|
|||||||
@@ -539,6 +539,10 @@ class Model:
|
|||||||
W = W - W_org
|
W = W - W_org
|
||||||
# Use a low-rank SVD to get an approximation of the matrix.
|
# Use a low-rank SVD to get an approximation of the matrix.
|
||||||
r = self.peft_config.r
|
r = self.peft_config.r
|
||||||
|
# svd_lowrank is randomized:
|
||||||
|
# https://github.com/pytorch/pytorch/blob/20919052303c0b5ba87f8bf7e19237dc33ab09d3/torch/_lowrank.py#L108-L109
|
||||||
|
# Reseed immediately before the call so restoring a trial is independent of RNG history.
|
||||||
|
torch.manual_seed(self.settings.seed)
|
||||||
U, S, Vh = torch.svd_lowrank(W, q=2 * r + 4, niter=6)
|
U, S, Vh = torch.svd_lowrank(W, q=2 * r + 4, niter=6)
|
||||||
# Truncate it to the part we want to store in the LoRA adapter.
|
# Truncate it to the part we want to store in the LoRA adapter.
|
||||||
# Note: svd_lowrank actually returns V, so transpose it to get Vh.
|
# Note: svd_lowrank actually returns V, so transpose it to get Vh.
|
||||||
|
|||||||
@@ -14,7 +14,11 @@ from urllib.request import urlopen
|
|||||||
import cpuinfo
|
import cpuinfo
|
||||||
import torch
|
import torch
|
||||||
from huggingface_hub import HfApi, hf_hub_download
|
from huggingface_hub import HfApi, hf_hub_download
|
||||||
from huggingface_hub.utils import disable_progress_bars, enable_progress_bars
|
from huggingface_hub.utils import (
|
||||||
|
GatedRepoError,
|
||||||
|
disable_progress_bars,
|
||||||
|
enable_progress_bars,
|
||||||
|
)
|
||||||
from questionary import Choice
|
from questionary import Choice
|
||||||
from rich.table import Table
|
from rich.table import Table
|
||||||
|
|
||||||
@@ -37,6 +41,7 @@ def collect_reproducibles(path: str):
|
|||||||
models = api.list_models(
|
models = api.list_models(
|
||||||
filter=["heretic", "reproducible"],
|
filter=["heretic", "reproducible"],
|
||||||
sort="created_at",
|
sort="created_at",
|
||||||
|
expand=["gated", "tags"],
|
||||||
)
|
)
|
||||||
|
|
||||||
found = 0
|
found = 0
|
||||||
@@ -51,6 +56,12 @@ def collect_reproducibles(path: str):
|
|||||||
if model.tags is not None and "gguf" in model.tags:
|
if model.tags is not None and "gguf" in model.tags:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if model.gated:
|
||||||
|
try:
|
||||||
|
api.auth_check(model.id, repo_type="model")
|
||||||
|
except GatedRepoError:
|
||||||
|
continue
|
||||||
|
|
||||||
print(f"[bold]{model.id}[/]...", end="")
|
print(f"[bold]{model.id}[/]...", end="")
|
||||||
|
|
||||||
user, repository = model.id.split("/")
|
user, repository = model.id.split("/")
|
||||||
|
|||||||
@@ -547,7 +547,7 @@ def generate_reproduce_json(
|
|||||||
version_info = get_heretic_version_info()
|
version_info = get_heretic_version_info()
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
"version": "1", # Version number of the reproduce.json file format, to allow for future changes.
|
"version": "2", # Version number of the reproduce.json file format, to allow for future changes.
|
||||||
"timestamp": timestamp,
|
"timestamp": timestamp,
|
||||||
"system": None, # Defined here to preserve insertion order.
|
"system": None, # Defined here to preserve insertion order.
|
||||||
"environment": {
|
"environment": {
|
||||||
|
|||||||
Reference in New Issue
Block a user