feat: make adjective describing the type of model produced configurable (#539)

This commit is contained in:
Philipp Emanuel Weidmann authored and GitHub committed 2026-10-31 17:44:09 +05:30
1 parent d16e020116
commit a9e7df2cda
9 files changed
+33 -19

No files matched your search

+4 -1
View File
@@ -87,7 +87,7 @@ modifiers = [
{ plugin = "heretic.modifiers.ara.ARA" },
]
# Number of abliteration trials to run during optimization.
# Number of trials to run during optimization.
n_trials = 100
# Number of trials that use random sampling for the purpose of exploration.
@@ -96,6 +96,9 @@ n_startup_trials = 30
# Directory to save and load study progress to/from.
study_checkpoint_dir = "checkpoints"
# Adjective describing the type of model produced by the configuration.
modified_model_type = "decensored"
# Maximum size for individual safetensors files generated when exporting a model.
max_shard_size = "5GB"
+2
View File
@@ -3,6 +3,8 @@
max_response_length = 300
modified_model_type = "humor-reduced"
upload_tags = [
"no-humor",
]
+2
View File
@@ -3,6 +3,8 @@
max_response_length = 300
modified_model_type = "slop-reduced"
upload_tags = [
"no-slop",
]
+2
View File
@@ -3,6 +3,8 @@
max_response_length = 100
modified_model_type = "sycophancy-reduced"
upload_tags = [
"no-sycophancy",
]
+11 -6
View File
@@ -201,7 +201,7 @@ class Settings(BaseSettings):
evaluate_model: str | None = Field(
default=None,
description=(
"If this model ID or path is set, then instead of abliterating the main model, "
"If this model ID or path is set, then instead of modifying the main model, "
"evaluate this model relative to the main model."
),
exclude=True,
@@ -210,7 +210,7 @@ class Settings(BaseSettings):
collect_reproducibles: str | None = Field(
default=None,
description=(
"If this directory path is set, then instead of abliterating a model, "
"If this directory path is set, then instead of modifying a model, "
"download all reproduce.json files from public Heretic model repositories "
"on Hugging Face, and store them in that directory for archival purposes."
),
@@ -221,7 +221,7 @@ class Settings(BaseSettings):
default=None,
description=(
"If this path or URL to a reproduce.json file is set, load reproduction information "
"from that file, and attempt to reproduce the abliterated model it originated from."
"from that file, and attempt to reproduce the modified model it originated from."
),
exclude=True,
)
@@ -405,7 +405,7 @@ class Settings(BaseSettings):
n_trials: PositiveInt = Field(
default=100,
description="Number of abliteration trials to run during optimization.",
description="Number of trials to run during optimization.",
)
n_startup_trials: NonNegativeInt = Field(
@@ -427,6 +427,11 @@ class Settings(BaseSettings):
exclude=True,
)
modified_model_type: str = Field(
default="decensored",
description="Adjective describing the type of model produced by the configuration.",
)
benchmarks: list[BenchmarkSpecification] = Field(
default=[
BenchmarkSpecification(
@@ -485,7 +490,7 @@ class Settings(BaseSettings):
description="An Adversarial Winograd Schema Challenge at Scale",
),
],
description="Benchmarks to offer to the user for evaluating abliterated models.",
description="Benchmarks to offer to the user for evaluating modified models.",
exclude=True,
)
@@ -516,7 +521,7 @@ class Settings(BaseSettings):
model_action: str | None = Field(
default=None,
description='Action to take with the decensored model: "save", "upload", or unset to prompt the user.',
description='Action to take with the modified model: "save", "upload", or unset to prompt the user.',
)
save_directory: str | None = Field(
+5 -5
View File
@@ -175,7 +175,7 @@ def obtain_export_strategy(
"How do you want to export the model?",
choices=[
Choice(
title="Merge the abliteration LoRA and export the full model"
title="Merge the LoRA and export the full model"
+ (
""
if settings.quantization == QuantizationMethod.NONE
@@ -184,7 +184,7 @@ def obtain_export_strategy(
value=ExportStrategy.MERGE,
),
Choice(
title="Export the abliteration LoRA only (can be merged later)",
title="Export the LoRA only (can be merged later)",
value=ExportStrategy.ADAPTER,
),
],
@@ -944,7 +944,7 @@ def run():
action = ask_if_unset(
settings.model_action,
questionary.select(
"What do you want to do with the decensored model?",
f"What do you want to do with the {settings.modified_model_type} model?",
choices=[
Choice(
title="Save the model to a local folder",
@@ -1366,11 +1366,11 @@ def run():
scope = questionary.select(
(
"Do you want to benchmark the original model along with the decensored model? "
f"Do you want to benchmark the original model along with the {settings.modified_model_type} model? "
"Benchmarking both models allows you to compare the scores, but it takes twice as much time."
),
choices=[
"Benchmark only the decensored model",
f"Benchmark only the {settings.modified_model_type} model",
"Benchmark both models",
],
style=Style([("highlighted", "reverse")]),
+5 -5
View File
@@ -242,7 +242,7 @@ class Model:
all_components[component] = 0
all_components[component] += len(modules)
print("* Abliterable components:")
print("* Modifiable components:")
for component, count in all_components.items():
print(f" * [bold]{component}[/]: [bold]{count}[/] modules total")
@@ -250,7 +250,7 @@ class Model:
# Guard against calling this method at the wrong time.
assert isinstance(self.model, PreTrainedModel)
# Always use LoRA adapters for abliteration (faster reload, no weight modification).
# Always use LoRA adapters for modification (faster reload, no weight modification).
# Collect actual leaf module names from the model for LoRA targeting.
# This is more robust than splitting component keys (e.g. "attn.o_proj" -> "o_proj")
# because hybrid models like Qwen3.5 MoE have modules with different names
@@ -513,13 +513,13 @@ class Model:
for expert in layer.moe.experts: # ty:ignore[not-iterable, unresolved-attribute]
try_add("mlp.down_proj", expert.output_linear) # ty: ignore[unresolved-attribute]
# We need at least one module across all components for abliteration to work.
# We need at least one module across all components.
total_modules = sum(len(mods) for mods in modules.values())
assert total_modules > 0, "No abliterable modules found in layer"
assert total_modules > 0, "No modifiable modules found in layer"
return modules
def get_abliterable_components(self) -> list[str]:
def get_modifiable_components(self) -> list[str]:
components: set[str] = set()
# Scan all layers because hybrid models (e.g. Qwen3.5 MoE) have different
+1 -1
View File
@@ -255,7 +255,7 @@ class Abliteration(Modifier[Parameters]):
weight_distributions = {}
for component in model.get_abliterable_components():
for component in model.get_modifiable_components():
# The parameter ranges are based on experiments with various models
# and much wider ranges. They are not set in stone and might have to be
# adjusted for future models.
+1 -1
View File
@@ -344,7 +344,7 @@ def get_readme_intro(
else:
reproducibility_instructions = ""
return f"""# This is a decensored version of {
return f"""# This is a {settings.modified_model_type} version of {
model_link
}, made using [Heretic](https://heretic-project.org) v{version("heretic-llm")}
{reproducibility_instructions}