setu-neural-eeg / scripts /foundation_models.py
arushsinghal's picture
Add files using upload-large-folder tool
4fcc185 verified
Raw History Blame Contribute Delete
4.19 kB
"""Official REVE / CSBrain adapters; real electrode subsets, no virtual channels."""
import os,sys,types,importlib.util,ast,json
from pathlib import Path
os.environ.setdefault('HF_HOME',str(Path('.cache/huggingface').resolve()))
import torch
from torch import nn
def csbrain_class():
# Namespaced import preserves upstream source and avoids CBraMod's `models` collision.
root=Path('external/CSBrain/models').resolve();package='_setu_csbrain'
if package+'.CSBrain' in sys.modules:return sys.modules[package+'.CSBrain'].CSBrain
pkg=types.ModuleType(package);pkg.__path__=[str(root)];sys.modules[package]=pkg
for name in ['CSBrain_transformerlayer','CSBrain_transformer','CSBrain']:
p=root/(name+'.py');code=p.read_text().replace('from models.CSBrain_',f'from {package}.CSBrain_')
mod=types.ModuleType(package+'.'+name);mod.__file__=str(p);mod.__package__=package;sys.modules[mod.__name__]=mod;exec(compile(code,str(p),'exec'),mod.__dict__)
return sys.modules[package+'.CSBrain'].CSBrain
def cs_topology(names):
tree=ast.parse(Path('external/CSBrain/models/model_for_speech.py').read_text());values={}
for node in ast.walk(tree):
if isinstance(node,ast.Assign) and len(node.targets)==1 and isinstance(node.targets[0],ast.Name) and node.targets[0].id in ['brain_regions','electrode_labels','topology']:
values[node.targets[0].id]=ast.literal_eval(node.value)
mapping=dict(zip(values['electrode_labels'],values['brain_regions']));regions=[mapping[n] for n in names]
order=sorted(range(len(names)),key=lambda i:(regions[i],values['topology'][regions[i]].index(names[i])))
return regions,order
class Foundation(nn.Module):
def __init__(self,kind,names,pretrained=True,n_times=400):
super().__init__();self.kind=kind;self.names=list(names);self.n_times=n_times
if kind=='reve':
from transformers import AutoModel,AutoConfig
directory='models/reve-base'
if pretrained:self.backbone,info=AutoModel.from_pretrained(directory,trust_remote_code=True,local_files_only=True,output_loading_info=True)
else:self.backbone=AutoModel.from_config(AutoConfig.from_pretrained(directory,trust_remote_code=True,local_files_only=True),trust_remote_code=True);info={}
assert not info.get('missing_keys') and not info.get('unexpected_keys') and not info.get('mismatched_keys'),str(info)
bank=AutoModel.from_pretrained('models/reve-positions',trust_remote_code=True,local_files_only=True)
positions=bank(names).detach().float();assert positions.shape==(len(names),3) and torch.isfinite(positions).all()
self.register_buffer('positions',positions);self.dimension=len(names)*(1+(n_times-200)//180)*512
elif kind=='csbrain':
regions,order=cs_topology(names);self.backbone=csbrain_class()(brain_regions=regions,sorted_indices=order)
if pretrained:
state=torch.load('models/csbrain_pretrained.pth',map_location='cpu',weights_only=True)
state={k.removeprefix('module.').removeprefix('backbone.'):v for k,v in state.items()}
self.backbone.load_state_dict(state,strict=True)
self.backbone.proj_out=nn.Identity();self.dimension=len(names)*(n_times//200)*200
elif kind=='cbramod':
from cbramod_probe import backbone
self.backbone=backbone();self.dimension=len(names)*(n_times//200)*200
else:raise ValueError(kind)
def forward(self,x):
if self.kind=='reve':return self.backbone(x,self.positions.unsqueeze(0).expand(len(x),-1,-1)).flatten(1)
# FFT in official CSBrain requires float32; incoming samples remain float32 under autocast.
return self.backbone(x.reshape(len(x),len(self.names),self.n_times//200,200)).flatten(1)
class FoundationClassifier(nn.Module):
def __init__(self,kind,names,mode='partial',pretrained=True):
super().__init__();self.encoder=Foundation(kind,names,pretrained);self.head=nn.Sequential(nn.LayerNorm(self.encoder.dimension),nn.Dropout(.5),nn.Linear(self.encoder.dimension,5))
if mode!='full':
for p in self.encoder.parameters():p.requires_grad=False
if mode=='partial':
layers=self.encoder.backbone.transformer.layers if kind=='reve' else self.encoder.backbone.encoder.layers
for p in layers[-2:].parameters():p.requires_grad=True
def forward(self,x):return self.head(self.encoder(x))