nur-dev's picture
Publish reproducibility artifacts (part 2)
fe31b61 verified
Raw History Blame Contribute Delete
2.92 kB
"""Matched compact direct classifiers, including source-only calibration."""
from __future__ import annotations
import torch
from torch import nn
from .factorial import hypotheses
def train(x,h,sizes,seed,nonlinear,steps=400):
torch.cuda.manual_seed(seed);d=x.shape[1];out=sum(sizes)
if nonlinear:
width=(19984-out)//(d+1+out)
net=nn.Sequential(nn.Linear(d,width,device=x.device),nn.SiLU(),nn.Linear(width,out,device=x.device))
else:net=nn.Linear(d,out,device=x.device)
opt=torch.optim.AdamW(net.parameters(),lr=.003,weight_decay=.01)
counts=[torch.bincount(h[:,i],minlength=k).double()+1 for i,k in enumerate(sizes)]
with torch.enable_grad():
for _ in range(steps):
opt.zero_grad(set_to_none=True);logits=net(x.float());loss=x.new_zeros(());offset=0
for i,size in enumerate(sizes):
weights=(1/counts[i]).float();weights/=weights.mean()
loss=loss+nn.functional.cross_entropy(logits[:,offset:offset+size],h[:,i],weight=weights)/len(sizes);offset+=size
loss.backward();opt.step()
return net
def fit(x,h,sizes,kind,seed,steps=400):
nonlinear=kind=='factorized_classifier';n=len(x);out=sum(sizes)
network=train(x,h,sizes,seed,nonlinear,steps)
oof=x.new_zeros((n,out));fold=torch.arange(n,device=x.device)%4
for f in range(4):
partial=train(x[fold!=f],h[fold!=f],sizes,seed+101+f,nonlinear,steps)
with torch.no_grad():oof[fold==f]=partial(x[fold==f].float()).double()
temperatures=[];offset=0
for f,size in enumerate(sizes):
options=x.new_tensor([.5,1.,2.,4.]);logits=oof[:,None,offset:offset+size]/options[None,:,None]
loss=-logits.log_softmax(-1).gather(-1,h[:,f,None,None].expand(-1,len(options),1)).squeeze(-1).mean(0)
temperatures.append(options[loss.argmin()]);offset+=size
parameters=sum(p.numel() for p in network.parameters());assert parameters+len(sizes)<=20000
return dict(kind=kind,sizes=tuple(sizes),state_dict=network.state_dict(),
nonlinear=nonlinear,input_dimensions=x.shape[1],grid=hypotheses(sizes,x.device),
temperatures=torch.stack(temperatures),neural_parameters=parameters+len(sizes),adaptation_fits=5)
def posterior(model,x,observed):
# The registered direct-classifier comparison is full-evidence only.
assert len(list(observed))==x.shape[1]
state=model['state_dict']
if model['nonlinear']:
hidden=nn.functional.silu(x.float()@state['0.weight'].T+state['0.bias'])
logits=hidden@state['2.weight'].T+state['2.bias']
else:logits=x.float()@state['weight'].T+state['bias']
out=logits.new_zeros((len(x),len(model['grid'])));offset=0
for f,size in enumerate(model['sizes']):
logp=(logits[:,offset:offset+size]/model['temperatures'][f]).log_softmax(-1)
out+=logp[:,model['grid'][:,f]];offset+=size
return out.double().softmax(-1)