Download scripts/foundation_models.py from arushsinghal/setu-neural-eeg: direct link, hf CLI and curl.
- Browser
- Download file 4.19 kB
-
https://huggingface.co/arushsinghal/setu-neural-eeg/resolve/main/scripts/foundation_models.py
- Command line
-
hf download hf://arushsinghal/setu-neural-eeg/scripts/foundation_models.py
-
curl -L -o foundation_models.py https://huggingface.co/arushsinghal/setu-neural-eeg/resolve/main/scripts/foundation_models.py
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)) | |