AliSaadatV commited on
Commit
f3cdb8e
·
verified ·
1 Parent(s): 026ecb1

Upload mutators.py

Browse files
Files changed (1) hide show
  1. mutators.py +163 -0
mutators.py ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Mutation operators for biological language models.
3
+ Adds controlled noise and structural perturbations to model weights.
4
+ """
5
+
6
+ import os
7
+ import torch
8
+ import numpy as np
9
+ import logging
10
+ from typing import Optional
11
+ from transformers import AutoModelForMaskedLM
12
+ from safetensors.torch import save_file, load_file
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class BioMutator:
18
+ """Base mutation operator."""
19
+
20
+ def mutate(self, model_path: str, save_path: str) -> str:
21
+ """Apply mutation to a model and save result."""
22
+ raise NotImplementedError
23
+
24
+
25
+ class GaussianNoiseMutator(BioMutator):
26
+ """Add Gaussian noise to model weights."""
27
+
28
+ def __init__(self, std: float = 0.01, layer_filter: Optional[str] = None):
29
+ self.std = std
30
+ self.layer_filter = layer_filter # If set, only mutate matching layers
31
+
32
+ def mutate(self, model_path: str, save_path: str) -> str:
33
+ logger.info(f"Applying Gaussian noise mutation (std={self.std}) to {model_path}")
34
+
35
+ try:
36
+ state = load_file(os.path.join(model_path, "model.safetensors"))
37
+ except:
38
+ state = torch.load(
39
+ os.path.join(model_path, "pytorch_model.bin"),
40
+ map_location="cpu",
41
+ )
42
+
43
+ mutated = {}
44
+ for key, tensor in state.items():
45
+ if self.layer_filter is not None and self.layer_filter not in key:
46
+ mutated[key] = tensor.clone()
47
+ continue
48
+
49
+ # Generate noise matching tensor shape
50
+ noise = torch.randn_like(tensor) * self.std
51
+ mutated[key] = tensor + noise
52
+
53
+ # Save
54
+ os.makedirs(save_path, exist_ok=True)
55
+ save_file(mutated, os.path.join(save_path, "model.safetensors"))
56
+
57
+ # Copy auxiliary files
58
+ self._copy_auxiliary_files(model_path, save_path)
59
+
60
+ logger.info(f"Mutated model saved to {save_path}")
61
+ return save_path
62
+
63
+ def _copy_auxiliary_files(self, source_path: str, dest_path: str) -> None:
64
+ import shutil
65
+
66
+ for fname in ["config.json", "tokenizer.json", "tokenizer_config.json", "vocab.txt"]:
67
+ src = os.path.join(source_path, fname)
68
+ if os.path.exists(src):
69
+ shutil.copy2(src, os.path.join(dest_path, fname))
70
+
71
+
72
+ class LayerScaleMutator(BioMutator):
73
+ """Scale specific layers by a random factor."""
74
+
75
+ def __init__(self, scale_range: tuple = (0.9, 1.1), target_layers: Optional[list] = None):
76
+ self.scale_range = scale_range
77
+ self.target_layers = target_layers # e.g., ["encoder.layer.0", "encoder.layer.1"]
78
+
79
+ def mutate(self, model_path: str, save_path: str) -> str:
80
+ logger.info(f"Applying layer scale mutation to {model_path}")
81
+
82
+ try:
83
+ state = load_file(os.path.join(model_path, "model.safetensors"))
84
+ except:
85
+ state = torch.load(
86
+ os.path.join(model_path, "pytorch_model.bin"),
87
+ map_location="cpu",
88
+ )
89
+
90
+ mutated = {}
91
+ for key, tensor in state.items():
92
+ # Check if this key matches a target layer
93
+ should_scale = False
94
+ if self.target_layers:
95
+ for layer_name in self.target_layers:
96
+ if layer_name in key:
97
+ should_scale = True
98
+ break
99
+ else:
100
+ # Randomly select ~10% of layers to scale
101
+ should_scale = np.random.random() < 0.1
102
+
103
+ if should_scale:
104
+ scale = np.random.uniform(*self.scale_range)
105
+ mutated[key] = tensor * scale
106
+ logger.debug(f"Scaled {key} by {scale:.4f}")
107
+ else:
108
+ mutated[key] = tensor.clone()
109
+
110
+ os.makedirs(save_path, exist_ok=True)
111
+ save_file(mutated, os.path.join(save_path, "model.safetensors"))
112
+
113
+ self._copy_auxiliary_files(model_path, save_path)
114
+
115
+ logger.info(f"Layer-scaled model saved to {save_path}")
116
+ return save_path
117
+
118
+ def _copy_auxiliary_files(self, source_path: str, dest_path: str) -> None:
119
+ import shutil
120
+
121
+ for fname in ["config.json", "tokenizer.json", "tokenizer_config.json", "vocab.txt"]:
122
+ src = os.path.join(source_path, fname)
123
+ if os.path.exists(src):
124
+ shutil.copy2(src, os.path.join(dest_path, fname))
125
+
126
+
127
+ class DropoutMutator(BioMutator):
128
+ """Randomly zero out a fraction of weights (structural pruning)."""
129
+
130
+ def __init__(self, dropout_rate: float = 0.01):
131
+ self.dropout_rate = dropout_rate
132
+
133
+ def mutate(self, model_path: str, save_path: str) -> str:
134
+ logger.info(f"Applying dropout mutation (rate={self.dropout_rate}) to {model_path}")
135
+
136
+ try:
137
+ state = load_file(os.path.join(model_path, "model.safetensors"))
138
+ except:
139
+ state = torch.load(
140
+ os.path.join(model_path, "pytorch_model.bin"),
141
+ map_location="cpu",
142
+ )
143
+
144
+ mutated = {}
145
+ for key, tensor in state.items():
146
+ mask = torch.rand_like(tensor) > self.dropout_rate
147
+ mutated[key] = tensor * mask
148
+
149
+ os.makedirs(save_path, exist_ok=True)
150
+ save_file(mutated, os.path.join(save_path, "model.safetensors"))
151
+
152
+ self._copy_auxiliary_files(model_path, save_path)
153
+
154
+ logger.info(f"Dropout-mutated model saved to {save_path}")
155
+ return save_path
156
+
157
+ def _copy_auxiliary_files(self, source_path: str, dest_path: str) -> None:
158
+ import shutil
159
+
160
+ for fname in ["config.json", "tokenizer.json", "tokenizer_config.json", "vocab.txt"]:
161
+ src = os.path.join(source_path, fname)
162
+ if os.path.exists(src):
163
+ shutil.copy2(src, os.path.join(dest_path, fname))