end000's picture
Release Sol014 mathematical specialization with paired evaluations, regressions and exact replay artifacts
fc32096 verified
Raw History Blame Contribute Delete
4.58 kB
import argparse
import hashlib
import json
import numpy as np
import torch
from common import ROOT, inside, permit_compute, save_json
from weights import load, save_edit, export, project_writers
permit_compute()
p = argparse.ArgumentParser()
p.add_argument('--analysis', required=True)
p.add_argument('--layers', required=True)
p.add_argument('--alpha', type=float, required=True)
p.add_argument('--topk', type=int, default=64)
p.add_argument('--name', required=True)
p.add_argument('--control', choices=['targeted','orthogonal','random_columns'], default='targeted')
p.add_argument('--seed', type=int, default=1701)
p.add_argument('--hypothesis', required=True)
p.add_argument('--protect-coefficients', action='store_true')
p.add_argument('--match-protected-norm', action='store_true')
a = p.parse_args()
if not a.name or any(c not in 'abcdefghijklmnopqrstuvwxyz0123456789_-' for c in a.name):
raise ValueError('Используй простое уникальное имя кандидата')
directory, model = ROOT/'edits'/a.name, ROOT/'models'/a.name
if directory.exists() or model.exists():
raise ValueError('Имя уже занято')
z = np.load(inside(a.analysis)/'directions_and_writers.npz')
records = []
for layer in map(int,a.layers.split(',')):
if not 0 <= layer < 24:
raise ValueError('Недопустимый слой')
name = f'model.language_model.layers.{layer}.mlp.down_proj.weight'
weight = load(ROOT/'models/original', name).float()
u = torch.from_numpy(z[f'u_{layer}'])
scores = z[f'scores_{layer}']
k = a.topk or weight.shape[1]
if not 1 <= k <= weight.shape[1]:
raise ValueError('Недопустимое число столбцов')
columns = np.argsort(scores)[-k:].copy()
targeted = project_writers(weight, u, columns, a.alpha)
candidate = targeted
rng = np.random.default_rng(a.seed+layer)
if a.control == 'orthogonal':
v = torch.tensor(rng.normal(size=u.numel()),dtype=torch.float32)
v = v-u*torch.dot(u,v)
v = v/v.norm()
delta = weight-project_writers(weight,v,columns,1)
candidate = weight-delta*((weight-targeted).norm()/delta.norm())
elif a.control == 'random_columns':
selected = rng.choice(weight.shape[1],size=k,replace=False)
delta = weight-project_writers(weight,u,selected,1)
candidate = weight-delta*((weight-targeted).norm()/delta.norm())
columns = selected
protection = None
if a.protect_coefficients:
if a.control != 'targeted':
raise ValueError('Coefficient protection currently requires targeted control')
productive = z[f'productive_mlp_{layer}'][:,columns].astype(np.float64)
_, singular, basis = np.linalg.svd(productive,full_matrices=False)
basis = basis[singular>singular[0]*1e-6]
coefficients = (u@weight).numpy()[columns].astype(np.float64)
projected = coefficients-basis.T@(basis@coefficients)
retained = float(np.linalg.norm(projected)/np.linalg.norm(coefficients))
if a.match_protected_norm:
if retained < 1e-8:
raise ValueError('Protected coefficient vector vanishes')
projected = projected/retained
vector = torch.zeros(weight.shape[1],dtype=torch.float32)
vector[columns] = torch.from_numpy(projected.astype(np.float32))
candidate = weight-a.alpha*torch.outer(u,vector)
protection = {'constraint':'coefficient vector orthogonal to original productive MLP means on selected columns',
'retained_coefficient_norm_fraction_before_matching':retained,
'norm_matched':a.match_protected_norm,
'max_constraint_residual':float(np.max(np.abs(productive@projected)))}
record = save_edit(ROOT/'models/original',directory,name,candidate,a.hypothesis)
record.update({'layer':layer,'alpha':a.alpha,'control':a.control,'columns':columns.tolist(),'coefficient_protection':protection})
records.append(record)
save_json(directory/'candidate.json',{'origin':'independent_sol','analysis':a.analysis,'hypothesis':a.hypothesis,
'analysis_sha256':hashlib.sha256((inside(a.analysis)/'directions_and_writers.npz').read_bytes()).hexdigest(),
'control':a.control,'records':records,'status':'unconfirmed'})
export(ROOT/'models/original',directory,model)
print(json.dumps({'candidate':a.name,'model':str(model),'changed_entries':sum(r['changed_entries'] for r in records)},ensure_ascii=False))