OSCR

Temporal coding enables hyperacuity in event-based vision.

Code ↔ Paper

8 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 8 matches
  1. [1] § Methods › Model implementation and training details ↔ eb_models.py, lines 523–557 · score 0.87 · residual connections, layer normalization, multi head, dropout rate, Transformer layers, query
  2. [2] § Methods › Model implementation and training details ↔ losses.py, lines 11–49 · score 0.67 · Cross Entropy, cosine, Temperature, Loss, embedding, logits
  3. [3] § Methods › Model implementation and training details ↔ eb_models.py, lines 111–251 · score 0.63 · projection head, classification head, logits, batches, dimensional, network
  4. [4] § Methods › Setup ↔ data_collection/events/syclop_acquire_dataset_20230623.ipynb, lines 552–577 · score 0.62 · TGY D003HV, Maestro, motors, rotations, event
  5. [5] § Methods › Model implementation and training details ↔ eb_train.py, lines 241–256 · score 0.59 · decay, exponential, Adam, warmup, Optimization, schedule
  6. [6] § Methods › Model implementation and training details ↔ eb_models.py, lines 289–383 · score 0.54 · GRU layer, classification head, model
  7. [7] § Methods › Model implementation and training details ↔ eb_contrastive.py, lines 81–127 · score 0.53 · decay, exponential, Adam, schedule, batch, linear
  8. [8] § Results › Accurate event timing contains task-relevant information ↔ notebooks/fig_acc_vs_jitter_allDS_supp.ipynb, lines 407–454 · score 0.50 · tailed Wilcoxon rank, jitter training, sum, accuracy, EB, event

Paper

Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

Python · 588 lines · 25 KB · other · 3 matches

  1. import torch
  2. import torch.nn as nn
  3. import torch.nn.functional as F
  4. import torchvision.transforms as transforms
  5. from torchvision.models import resnet18, ResNet18_Weights
  6. class TimeSeriesTransformer(nn.Module):
  7. def __init__(self,
  8. n_timesteps = 64,
  9. d_timeseries = 4,
  10. d_k = 256,
  11. d_mlp = 512,
  12. d_mlp2 = 256,
  13. n_layers = 8,
  14. num_heads = 4,
  15. num_classes = 10,
  16. dropout_rate = 0.1,
  17. offsets = None,
  18. scalings = None,
  19. dropout_ffn_from_layer = 4,
  20. subseq_len = None,
  21. model_head = 'cls_mlp',
  22. model_head_init_method = None,
  23. input_dense_init_method = None):
  24. '''
  25. Transformer for time series data.
  26. :param n_timesteps: number of timesteps in the input
  27. :param d_timeseries: dimensionality of the timeseries
  28. :param d_k: dimensionality of the key and query vectors
  29. :param d_mlp: dimensionality of the MLP in the feedforward layer
  30. :param d_mlp2: dimensionality of the MLP in the classification head
  31. :param n_layers: number of layers in the Transformer
  32. :param num_heads: number of heads in the Multi-Head Attention
  33. :param num_classes: number of classes in the classification head
  34. :param dropout_rate: dropout rate
  35. :param offsets: offsets to be added to the input
  36. :param scalings: scalings to be multiplied to the input
  37. :param dropout_ffn_from_layer: layer from which to apply dropout to the feedforward network
  38. :param subseq_len: list or tuple. when not None, the input is split into subsequences according to this list, each subsequence is processed by the transformer, and the outputs are concatenated
  39. :param model_head: head architecture. 'cls_mlp' or 'cls_avgpool'
  40. Receives input of shape (batch_size, n_timesteps, d_timeseries), returns logits of shape (batch_size, num_classes)
  41. '''
  42. super(TimeSeriesTransformer, self).__init__()
  43. if offsets is None:
  44. offsets = torch.zeros(1, 1, d_timeseries)
  45. if scalings is None:
  46. scalings = torch.ones(1, 1, d_timeseries)
  47. self.offsets = offsets
  48. self.scalings = scalings
  49. self.n_layers = n_layers
  50. self.dropout_ffn_from_layer = dropout_ffn_from_layer
  51. self.subseq_len = subseq_len
  52. self.model_head_init_method = model_head_init_method
  53. # Initial dense layer to expand dimensions
  54. #todo: add option to use dedicated input dense layer per subsequence in a case of multiple subsequences
  55. self.input_dense = nn.Linear(d_timeseries, d_k)
  56. if input_dense_init_method is not None:
  57. apply_init_method([self.input_dense], input_dense_init_method)
  58. # Defining n_layers of Transformer Layers
  59. self.transformer_layers = nn.ModuleList([
  60. TransformerLayer(d_k, num_heads, d_mlp, dropout_rate) for _ in range(n_layers)
  61. ])
  62. if model_head == 'cls_mlp':
  63. self.cls_head = ClassifierHead_MLP(d_k * n_timesteps,
  64. d_mlp2, num_classes, dropout_rate,do_flatten=True)
  65. elif model_head == 'cls_avgpool':
  66. self.cls_head = ClassifierHead_AvgPool(d_k, d_mlp2, num_classes, dropout_rate, init_method=model_head_init_method)
  67. elif model_head == 'proj_avgpool':
  68. self.cls_head = ProjectionHead_AvgPool(d_k, d_mlp2)
  69. elif model_head == 'none':
  70. self.cls_head = nn.Identity()
  71. else:
  72. raise ValueError('Unknown cls_head_arch')
  73. def forward(self, x, attn_mask=None, padding_mask=None):
  74. # Add offsets and scaling
  75. #convert to torch tensor locally to avoid problems with multiple GPUs
  76. torch_offsets = torch.tensor(self.offsets, device=x.device, dtype=x.dtype)
  77. torch_scalings = torch.tensor(self.scalings, device=x.device, dtype=x.dtype)
  78. if self.subseq_len is not None and (attn_mask is not None or padding_mask is not None):
  79. raise ValueError('subseq_len cannot be used with attn_mask or key_padding_mask')
  80. if self.subseq_len is not None:
  81. #in a case of multiple subsequences, we create an attention mask to prevent attention between subsequences
  82. #this mask is a matrix of shape (n_timesteps, n_timesteps) it is false between all pairs of timesteps that belong to different subsequences
  83. #otherwise, the mask is None
  84. attn_mask = torch.ones(x.shape[1], x.shape[1], dtype=torch.bool, device=x.device)
  85. this_position = 0
  86. for subseq_len in self.subseq_len:
  87. attn_mask[this_position:this_position+subseq_len, this_position:this_position+subseq_len] = False
  88. this_position += subseq_len
  89. x = (x + torch_offsets) * torch_scalings
  90. x = self.input_dense(x)
  91. for i, layer in enumerate(self.transformer_layers):
  92. x = layer(x, apply_dropout=(i >= self.dropout_ffn_from_layer), attn_mask=attn_mask, key_padding_mask=padding_mask)
  93. x = self.cls_head(x, mask=padding_mask)
  94. return x
  95. class TimeSeriesTransformerWithSubseq(nn.Module):
  96. def __init__(self,
  97. n_timesteps=64,
  98. d_timeseries=4,
  99. d_k=256,
  100. d_mlp=512,
  101. d_mlp2=256,
  102. n_layers=8,
  103. num_heads=4,
  104. num_classes=10,
  105. dropout_rate=0.1,
  106. offsets=None,
  107. scalings=None,
  108. dropout_ffn_from_layer=4,
  109. subseq_len=None,
  110. model_head='cls_mlp',
  111. shared_input_dense=False,
  112. drop_subseq_prob=0.5,
  113. extra_dim_for_subsamples=False):
  114. '''
  115. Transformer for time series data. With option to split the input into multiple subsequences.
  116. :param n_timesteps: number of timesteps in the input
  117. :param d_timeseries: dimensionality of the timeseries
  118. :param d_k: dimensionality of the key and query vectors
  119. :param d_mlp: dimensionality of the MLP in the feedforward layer
  120. :param d_mlp2: dimensionality of the MLP in the classification head
  121. :param n_layers: number of layers in the Transformer
  122. :param num_heads: number of heads in the Multi-Head Attention
  123. :param num_classes: number of classes in the classification head
  124. :param dropout_rate: dropout rate
  125. :param offsets: offsets to be added to the input
  126. :param scalings: scalings to be multiplied to the input
  127. :param dropout_ffn_from_layer: layer from which to apply dropout to the feedforward network
  128. :param subseq_len: list or tuple. when not None, the input is split into subsequences according to this list, each subsequence is processed by the transformer, and the outputs are concatenated
  129. :param model_head: head architecture. 'cls_mlp' or 'cls_avgpool'
  130. :param shared_input_dense: if True, the input is processed by a single dense layer before being split into subsequences. If False, each subsequence is processed by a dedicated dense layer.
  131. :param drop_subseq_prob: probability of dropping a subsequence during training
  132. Receives input of shape (batch_size, n_timesteps, d_timeseries), returns logits of shape (batch_size, num_classes)
  133. '''
  134. super(TimeSeriesTransformerWithSubseq, self).__init__()
  135. if offsets is None:
  136. offsets = torch.zeros(1, 1, d_timeseries)
  137. if scalings is None:
  138. scalings = torch.ones(1, 1, d_timeseries)
  139. self.offsets = offsets
  140. self.scalings = scalings
  141. self.n_layers = n_layers
  142. self.dropout_ffn_from_layer = dropout_ffn_from_layer
  143. self.subseq_len = subseq_len
  144. self.shared_input_dense = shared_input_dense
  145. self.drop_subseq_prob = drop_subseq_prob
  146. self.extra_dim_for_subsamples = extra_dim_for_subsamples
  147. # Initial dense layer to expand dimensions
  148. if shared_input_dense:
  149. self.input_dense = nn.Linear(d_timeseries, d_k)
  150. else:
  151. self.input_dense = nn.ModuleList([nn.Linear(d_timeseries, d_k) for _ in range(len(subseq_len))])
  152. if subseq_len is None:
  153. raise NotImplementedError
  154. # Defining n_layers of Transformer Layers
  155. # self.transformer_layers = nn.ModuleList([
  156. # TransformerLayer(d_k, num_heads, d_mlp, dropout_rate) for _ in range(n_layers)
  157. # ])
  158. else:
  159. # in case of multiple subsequences, we need to split the input into subsequences
  160. # each subsequence is processed by a dedicated transformer, and the outputs are concatenated
  161. for i in range(len(subseq_len)):
  162. setattr(self, 'transformer_stack_{}'.format(i), TransformerCore(d_k=d_k,
  163. num_heads=num_heads,
  164. d_mlp=d_mlp,
  165. n_layers=n_layers,
  166. dropout_rate=dropout_rate,
  167. dropout_ffn_from_layer=dropout_ffn_from_layer))
  168. if model_head == 'cls_mlp':
  169. self.cls_head = ClassifierHead_MLP(d_k * (len(subseq_len) if subseq_len is not None else 1),
  170. d_mlp2, num_classes, dropout_rate,do_flatten=extra_dim_for_subsamples)
  171. elif model_head == 'cls_avgpool':
  172. self.cls_head = ClassifierHead_AvgPool(d_k, d_mlp2, num_classes, dropout_rate)
  173. elif model_head == 'proj_avgpool':
  174. self.cls_head = ProjectionHead_AvgPool(d_k, d_mlp2, skip_mean=True)
  175. elif model_head == 'none':
  176. self.cls_head = nn.Identity()
  177. else:
  178. raise ValueError('Unknown cls_head_arch')
  179. def forward(self, x):
  180. # Add offsets and scaling
  181. #convert to torch tensor locally to avoid problems with multiple GPUs
  182. torch_offsets = torch.tensor(self.offsets, device=x.device, dtype=x.dtype)
  183. torch_scalings = torch.tensor(self.scalings, device=x.device, dtype=x.dtype)
  184. x = (x + torch_offsets) * torch_scalings
  185. if self.shared_input_dense:
  186. x = self.input_dense(x)
  187. if self.subseq_len is None:
  188. raise NotImplementedError
  189. # Pass through each Transformer Layer
  190. # for i, layer in enumerate(self.transformer_layers):
  191. # x = layer(x, apply_dropout=(i >= self.dropout_ffn_from_layer))
  192. else:
  193. # in case of multiple subsequences, we need to split the input into subsequences
  194. # each subsequence is processed by a dedicated transformer, and the outputs are passed through average
  195. # pooling and concatenated
  196. x_list = []
  197. this_position = 0
  198. for i, subseq_len in enumerate(self.subseq_len):
  199. if self.extra_dim_for_subsamples:
  200. x_subseq = x[:, i, ...]
  201. else:
  202. x_subseq = x[:, this_position:this_position+subseq_len, :]
  203. this_position += subseq_len
  204. if not self.shared_input_dense:
  205. x_subseq = self.input_dense[i](x_subseq)
  206. x_subseq = getattr(self, 'transformer_stack_{}'.format(i))(x_subseq)
  207. x_subseq = torch.mean(x_subseq, dim=1)
  208. x_list.append(x_subseq)
  209. # if in training mode with probability self.drop_subseq_prob pick at most one subsequence and set it to zero
  210. # this is done by creating a subsequence of zeros and replacing one random subsequence
  211. # out of the original subsequences with zeros.
  212. if self.training:
  213. if torch.rand(1) < self.drop_subseq_prob:
  214. x_list[torch.randint(len(x_list),(1,))] = torch.zeros_like(x_list[0])
  215. #scale all other subsequences to compensate for the zero subsequence
  216. x_list = [x * (len(x_list) / (len(x_list) - 1)) for x in x_list]
  217. if self.extra_dim_for_subsamples:
  218. x = torch.stack(x_list, dim=1)
  219. else:
  220. x = torch.cat(x_list, dim=1)
  221. # Classification head
  222. x = self.cls_head(x)
  223. return x
  224. class TransformerCore(nn.Module):
  225. def __init__(self,
  226. d_k=256,
  227. d_mlp=512,
  228. n_layers=8,
  229. num_heads=4,
  230. dropout_rate=0.1,
  231. dropout_ffn_from_layer=4):
  232. '''
  233. Core of the Transformer. No classification head or input preprocessing.
  234. :param d_k: dimensionality of the key and query vectors
  235. :param d_mlp: dimensionality of the MLP in the feedforward layer
  236. :param n_layers: number of layers in the Transformer
  237. :param num_heads: number of heads in the Multi-Head Attention
  238. :param dropout_rate: dropout rate
  239. :param dropout_ffn_from_layer: layer from which to apply dropout to the feedforward network
  240. Receives input of shape (batch_size, n_timesteps, d_timeseries), returns logits of shape (batch_size, num_classes)
  241. '''
  242. super(TransformerCore, self).__init__()
  243. self.n_layers = n_layers
  244. self.dropout_ffn_from_layer = dropout_ffn_from_layer
  245. # Defining n_layers of Transformer Layers
  246. self.transformer_layers = nn.ModuleList([
  247. TransformerLayer(d_k, num_heads, d_mlp, dropout_rate) for _ in range(n_layers)
  248. ])
  249. def forward(self, x):
  250. # Pass through each Transformer Layer
  251. for i, layer in enumerate(self.transformer_layers):
  252. x = layer(x, apply_dropout=(i >= self.dropout_ffn_from_layer))
  253. return x
  254. class TimeSeriesGRU(nn.Module):
  255. def __init__(self,
  256. n_timesteps=64,
  257. d_timeseries=4,
  258. d_hidden=256,
  259. d_mlp=512,
  260. d_mlp2=256,
  261. n_layers=8,
  262. num_classes=10,
  263. dropout_rate=0.1,
  264. offsets=None,
  265. scalings=None,
  266. dropout_ffn_from_layer=4,
  267. subseq_len=None,
  268. model_head='cls_mlp',
  269. model_head_init_method=None,
  270. input_dense_init_method=None):
  271. '''
  272. GRU-based model for time series data.
  273. :param n_timesteps: number of timesteps in the input
  274. :param d_timeseries: dimensionality of the timeseries
  275. :param d_hidden: dimensionality of the hidden state in the GRU
  276. :param d_mlp: dimensionality of the MLP in the feedforward layer
  277. :param d_mlp2: dimensionality of the MLP in the classification head
  278. :param n_layers: number of GRU layers
  279. :param num_classes: number of classes in the classification head
  280. :param dropout_rate: dropout rate
  281. :param offsets: offsets to be added to the input
  282. :param scalings: scalings to be multiplied to the input
  283. :param dropout_ffn_from_layer: layer from which to apply dropout to the feedforward network
  284. :param subseq_len: list or tuple. when not None, the input is split into subsequences according to this list, each subsequence is processed by the GRU, and the outputs are concatenated
  285. :param model_head: head architecture. 'cls_mlp' or 'cls_avgpool'
  286. Receives input of shape (batch_size, n_timesteps, d_timeseries), returns logits of shape (batch_size, num_classes)
  287. '''
  288. super(TimeSeriesGRU, self).__init__()
  289. if offsets is None:
  290. offsets = torch.zeros(1, 1, d_timeseries)
  291. if scalings is None:
  292. scalings = torch.ones(1, 1, d_timeseries)
  293. self.offsets = offsets
  294. self.scalings = scalings
  295. self.n_layers = n_layers
  296. self.dropout_ffn_from_layer = dropout_ffn_from_layer
  297. self.subseq_len = subseq_len
  298. self.model_head_init_method = model_head_init_method
  299. # Initial dense layer to expand dimensions
  300. self.input_dense = nn.Linear(d_timeseries, d_hidden)
  301. if input_dense_init_method is not None:
  302. apply_init_method([self.input_dense], input_dense_init_method)
  303. # GRU layer
  304. self.gru_layers = nn.ModuleList([
  305. nn.GRU(input_size=d_hidden, hidden_size=d_hidden, num_layers=1, batch_first=True) for _ in range(n_layers)
  306. ])
  307. # Dropout layer
  308. self.dropout = nn.Dropout(dropout_rate)
  309. # Classification head
  310. if model_head == 'cls_mlp':
  311. self.cls_head = ClassifierHead_MLP(d_hidden * n_timesteps, d_mlp2, num_classes, dropout_rate,
  312. do_flatten=True)
  313. elif model_head == 'cls_avgpool':
  314. self.cls_head = ClassifierHead_AvgPool(d_hidden, d_mlp2, num_classes, dropout_rate,
  315. init_method=model_head_init_method)
  316. elif model_head == 'proj_avgpool':
  317. self.cls_head = ProjectionHead_AvgPool(d_hidden, d_mlp2)
  318. elif model_head == 'none':
  319. self.cls_head = nn.Identity()
  320. else:
  321. raise ValueError('Unknown cls_head_arch')
  322. def forward(self, x, attn_mask=None, padding_mask=None):
  323. # Add offsets and scaling
  324. torch_offsets = torch.tensor(self.offsets, device=x.device, dtype=x.dtype)
  325. torch_scalings = torch.tensor(self.scalings, device=x.device, dtype=x.dtype)
  326. if self.subseq_len is not None and (attn_mask is not None or padding_mask is not None):
  327. raise ValueError('subseq_len cannot be used with attn_mask or key_padding_mask')
  328. x = (x + torch_offsets) * torch_scalings
  329. x = self.input_dense(x)
  330. # GRU forward pass with conditional dropout
  331. for i, gru_layer in enumerate(self.gru_layers):
  332. x, _ = gru_layer(x)
  333. if i >= self.dropout_ffn_from_layer:
  334. x = self.dropout(x)
  335. # Apply the classification head
  336. x = self.cls_head(x.contiguous(), mask=padding_mask)
  337. return x
  338. class VanillaCNN(nn.Module):
  339. def __init__(self, input_shape = (10,10),
  340. num_classes = 10):
  341. super(VanillaCNN, self).__init__()
  342. self.resize = nn.Upsample(size=(56, 56), mode='bilinear', align_corners=False)
  343. self.conv1 = nn.Conv2d(1, 32, kernel_size=3)
  344. self.conv2 = nn.Conv2d(32, 64, kernel_size=3)
  345. self.conv3 = nn.Conv2d(64, 64, kernel_size=2)
  346. self.pool = nn.MaxPool2d(2)
  347. # Calculate the size of the flattened features
  348. self.flat_features = self._get_flat_features()
  349. self.dropout1 = nn.Dropout(0.3)
  350. self.fc1 = nn.Linear(self.flat_features, 128)
  351. self.dropout2 = nn.Dropout(0.3)
  352. self.fc2 = nn.Linear(128, num_classes)
  353. def forward(self, x):
  354. if len(x.shape) == 3:
  355. x = x.unsqueeze(1)
  356. x = self.resize(x)
  357. x = F.relu(self.conv1(x))
  358. x = self.pool(x)
  359. x = F.relu(self.conv2(x))
  360. x = self.pool(x)
  361. x = F.relu(self.conv3(x))
  362. x = x.view(-1, self.flat_features)
  363. x = self.dropout1(x)
  364. x = F.relu(self.fc1(x))
  365. x = self.dropout2(x)
  366. x = self.fc2(x)
  367. return x
  368. def _get_flat_features(self):
  369. # Helper method to calculate the size of the flattened features
  370. with torch.no_grad():
  371. x = torch.zeros(1, 1, 56, 56) # Dummy input
  372. x = F.relu(self.conv1(x))
  373. x = self.pool(x)
  374. x = F.relu(self.conv2(x))
  375. x = self.pool(x)
  376. x = F.relu(self.conv3(x))
  377. return x.numel()
  378. class GrayscaleResNet18(nn.Module):
  379. def __init__(self, input_shape = (10,10),
  380. num_classes = 10):
  381. super(GrayscaleResNet18, self).__init__()
  382. # Load pretrained ResNet18
  383. self.resnet = resnet18(weights=None)
  384. # Modify the first convolutional layer to accept grayscale input
  385. self.resnet.conv1 = nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3, bias=False)
  386. # Modify the final fully connected layer if needed
  387. if num_classes != 1000:
  388. self.resnet.fc = nn.Linear(self.resnet.fc.in_features, num_classes)
  389. # Define preprocessing transforms
  390. self.preprocess = transforms.Compose([
  391. transforms.Resize(224, antialias=True),
  392. # transforms.CenterCrop(224),
  393. transforms.Normalize(mean=[0.485], std=[0.229])
  394. ])
  395. def forward(self, x):
  396. # x is expected to be a grayscale image tensor of shape (B, 1, H, W)
  397. if len(x.shape) == 3:
  398. x = x.unsqueeze(1)
  399. x = self.preprocess(x)
  400. return self.resnet(x)
  401. class ClassifierHead_MLP(nn.Module):
  402. def __init__(self, d_flatten, d_mlp, num_classes, dropout_rate, do_flatten=True):
  403. super(ClassifierHead_MLP, self).__init__()
  404. self.do_flatten = do_flatten
  405. self.additional_dense = nn.Linear(d_flatten, d_mlp)
  406. self.output_dense = nn.Linear(d_mlp, num_classes)
  407. self.dropout = nn.Dropout(dropout_rate)
  408. def forward(self, x, mask=None):
  409. if mask is not None:
  410. raise ValueError('mask is not supported for MLP head')
  411. if self.do_flatten:
  412. x = x.view(x.size(0), -1) # Flatten
  413. x = self.dropout(x)
  414. x = F.relu(self.additional_dense(x))
  415. x = self.dropout(x)
  416. x = self.output_dense(x)
  417. return x
  418. class ClassifierHead_AvgPool(nn.Module):
  419. def __init__(self, d_k, d_mlp, num_classes, dropout_rate, init_method=None):
  420. super(ClassifierHead_AvgPool, self).__init__()
  421. self.additional_dense = nn.Linear(d_k, d_mlp)
  422. self.output_dense = nn.Linear(d_mlp, num_classes)
  423. self.dropout = nn.Dropout(dropout_rate)
  424. if init_method is not None:
  425. apply_init_method([self.additional_dense, self.output_dense], init_method)
  426. def forward(self, x, mask=None):
  427. x = masked_mean(x, mask, dim=1)
  428. x = self.dropout(x)
  429. x = F.relu(self.additional_dense(x))
  430. x = self.dropout(x)
  431. x = self.output_dense(x)
  432. return x
  433. class ProjectionHead_AvgPool(nn.Module):
  434. def __init__(self, d_k, d_mlp, skip_mean=False):
  435. super(ProjectionHead_AvgPool, self).__init__()
  436. self.additional_dense = nn.Linear(d_k, d_mlp)
  437. self.output_dense = nn.Linear(d_mlp, d_mlp)
  438. self.skip_mean = skip_mean
  439. def forward(self, x, mask=None):
  440. if not self.skip_mean:
  441. x = masked_mean(x, mask=mask, dim=1)
  442. x = F.relu(self.additional_dense(x))
  443. x = self.output_dense(x)
  444. return x
  445. class TransformerLayer(nn.Module):
  446. def __init__(self, d_k, num_heads, d_mlp, dropout_rate):
  447. super(TransformerLayer, self).__init__()
  448. self.query_projection = nn.Linear(d_k, d_k)
  449. self.key_projection = nn.Linear(d_k, d_k)
  450. self.value_projection = nn.Linear(d_k, d_k)
  451. self.attention = nn.MultiheadAttention(d_k, num_heads, batch_first=True)
  452. self.norm1 = nn.LayerNorm(d_k)
  453. self.norm2 = nn.LayerNorm(d_k)
  454. self.ffn = nn.Sequential(
  455. nn.Linear(d_k, d_mlp),
  456. nn.ReLU(),
  457. nn.Linear(d_mlp, d_k)
  458. )
  459. self.dropout = nn.Dropout(dropout_rate)
  460. def forward(self, x, apply_dropout=False,attn_mask=None,key_padding_mask=None):
  461. # Projecting to query, key, and value
  462. query = self.query_projection(x)
  463. key = self.key_projection(x)
  464. value = self.value_projection(x)
  465. # Multi-Head Attention and Residual Connection
  466. att_out, _ = self.attention(query, key, value,attn_mask=attn_mask,key_padding_mask=key_padding_mask)
  467. x = self.norm1(x + att_out)
  468. # Feed-Forward Network and Residual Connection
  469. ffn_out = self.ffn(x)
  470. if apply_dropout:
  471. ffn_out = self.dropout(ffn_out)
  472. x = self.norm2(x + ffn_out)
  473. return x
  474. def masked_mean(x,mask=None,dim=1):
  475. #mask indicates which elements should be excluded from the mean
  476. if mask is None:
  477. x = torch.mean(x, dim=dim)
  478. else:
  479. #expand mask to the same dimentionality as x
  480. mask = mask.unsqueeze(-1)
  481. x = torch.sum(x * ~mask, dim=dim) / torch.sum(~mask, dim=dim)
  482. return x
  483. def apply_init_method(layers, init_method):
  484. if init_method == 'xavier':
  485. for layer in layers:
  486. nn.init.xavier_uniform_(layer.weight)
  487. nn.init.zeros_(layer.bias)
  488. elif init_method == 'kaiming':
  489. for layer in layers:
  490. nn.init.kaiming_uniform_(layer.weight)
  491. nn.init.zeros_(layer.bias)
  492. elif init_method == 'small_weights':
  493. for layer in layers:
  494. nn.init.uniform_(layer.weight, -0.01, 0.01)
  495. nn.init.zeros_(layer.bias)
  496. elif init_method == 'diag':
  497. for layer in layers:
  498. nn.init.eye_(layer.weight)
  499. nn.init.zeros_(layer.bias)
  500. else:
  501. raise ValueError('Unknown init method')

eb_models.py at commit c2b7506, under other · at the source

Overview

Authors: Eldad Assa1, Alexander Rivkind1, Michael Kreiserman1, Fahad Shahbaz Khan2, Salman Khan2, Ehud Ahissar1
  1. Weizmann Institute of Science, Department of Brain Sciences, Rehovot, Israel
  2. Mohamed bin Zayed University of Artificial Intelligence, Abu Dhabi, United Arab Emirates
Journal: Nature communications, volume 17, issue 1, article 9951
Dates: received 14 May 2025; accepted 7 August 2026; published online 20 August 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1038/s41467-026-76878-6 · PMID 42754584 · PMCID PMC13586331 · OpenAlex W4408866247
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: human (organism), cognitive (subfield)
Methods: Connectivity, Statistics, Machine learning
Keywords: Retina, Neural encoding, Oculomotor system
MeSH: Vision, Ocular*, Visual Acuity*, Visual Perception*, Humans, Neural Networks, Computer, Photic Stimulation, Retina (* major topic)
Topic: Computability, Logic, AI Algorithms (Computational Theory and Mathematics, Computer Science), according to OpenAlex
Funding: European Research Council (786949)
Citations: not cited yet (Europe PMC); 78 references in the paper

Abstract

Although the eye may appear static during fixation, it is in constant motion–transforming visual input into rich spatio-temporal streams of neuronal activity. These movements challenge the classical view that visual acuity derives from spatial sampling, suggesting that hyperacuity can emerge from precise temporal encoding. Here we show that artificial systems can exploit this principle using retina-like event-based (EB) sensing, a neuromorphic approach in which the sensor emits asynchronous spikes in response to luminance changes. Using an EB camera undergoing controlled “fixational” motion over tiny, pixelated images, we generated datasets in which recognition depends on sub-pixel information. We found that artificial neural networks trained on these spatio-temporal event streams relied crucially on the precise temporal information and outperformed conventional frame-based models. The learned representations also supported Vernier-style sub-pixel discrimination, demonstrating hyperacuity-like behavior in the artificial bio-mimetic system. These results show that active event-based sensing can use precise timing to recover spatial details that are unrecoverable from a static single frame. Together, these findings offer a new perspective on visual perception, open pathways for advancing neuromorphic engineering and energy-efficient AI vision systems, and provide a framework for testing hypotheses about biological vision.

Reproduced under the paper's license (CC BY), from the paper cited above.

Repositories

Its files are read in the Code ↔ Paper reader above, with 8 matches between paragraphs and lines of code.

zalandoresearch/fashion-mnist

License: MIT
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: b2617bb6d3ffa2e429640350f613e3291e10b141, 21 March 2022
Languages: Python (11), JavaScript (1)
Size: 51 files, 12 scripts
Software Heritage: archived
Found in: “Data availability”
Holds: README, license file, environment (Dockerfile, requirements.txt), documentation
Not found: CITATION.cff, tests, continuous integration
Tools: NumPy (5 files), scikit-learn (2 files), TensorFlow (2 files), Matplotlib (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
14 files

ahissar-lab/event-based-hyperacuity

License: other
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: c2b75060e1bb0f1334ca37da23b672483aae9e37, 9 July 2026
Languages: Jupyter (20), Python (8), Shell (2)
Size: 51 files, 30 scripts
Software Heritage: not archived
Found in: “Code availability”
Holds: README, license file, environment (environment.yml, environment_old.yml), 20 notebooks
Not found: CITATION.cff, tests, continuous integration, documentation
Tools: NumPy (23 files), Matplotlib (17 files), pandas (14 files), PyTorch (11 files), SciPy (9 files), scikit-learn (8 files), OpenCV (2 files), TensorFlow (2 files)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
32 files

Zenodo 20777652

License: CC-BY-4.0
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Size: 1 file
Software Heritage: not checked
Found in: “Code availability”
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: NumPy (23 files), Matplotlib (17 files), pandas (14 files), PyTorch (11 files), SciPy (9 files), scikit-learn (8 files), OpenCV (2 files), TensorFlow (2 files)
Availability: 1 check, the latest on 27 September 2026: the link answers (HTTP 200)
  • 27 September 2026: the link answers (HTTP 200)
32 files
At the source:

Code availability

The code generated in this study is publicly available at Github (https://github.com/ahissar-lab/event-based-hyperacuity) and Zenodo (https://doi.org/10.5281/zenodo.20777652)78. The repository include also the in-house python code used for datasets collections and the python code used to generate the figures. The code can be used freely for academic research.

Reproduced under the paper's license (CC BY), from the paper cited above.

Tracing map

Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.

What the map holds:

  • 3 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 72 scripts, each with its path and the digest of its content;
  • 8 matches between paragraphs of the paper and lines of the code (method lexical-v1);
  • neither the text of the paper nor the code itself.

Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.

Data

Datasets cited

Data Availability Statement

The datasets generated and used in this study (including tiny event-based and tiny frame-based versions) have been deposited in Zenodo and are publicly available at https://doi.org/10.5281/zenodo.2097375639. The three original FB datasets that were converted are also publicly available (MNIST (https://www.kaggle.com/datasets/hojjatk/mnist-dataset)36, Fashion-MNIST (https://github.com/zalandoresearch/fashion-mnist)37, and Kuzushiji-MNIST (https://github.com/rois-codh/kmnist)38), as well as N-MNIST (https://www.kaggle.com/datasets/khoahongg/n-mnist)30. Source data are provided with this paper.

The code generated in this study is publicly available at Github (https://github.com/ahissar-lab/event-based-hyperacuity) and Zenodo (https://doi.org/10.5281/zenodo.20777652)78. The repository include also the in-house python code used for datasets collections and the python code used to generate the figures. The code can be used freely for academic research.

Reproduced under the paper's license (CC BY), from the paper cited above.

Versions

The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.

Version 1, 27 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 6 authors, 3 keywords, 7 MeSH terms, 1 funder, 49 references.

Cite

This paper

Assa, E., Rivkind, A., Kreiserman, M., Khan, F. S., Khan, S., & Ahissar, E. (2026). Temporal coding enables hyperacuity in event-based vision. Nature communications, 17(1), 9951. https://doi.org/10.1038/s41467-026-76878-6

BibTeX

@article{assa2026temporal,
author = {Assa, Eldad and Rivkind, Alexander and Kreiserman, Michael and Khan, Fahad Shahbaz and Khan, Salman and Ahissar, Ehud},
title = {{Temporal coding enables hyperacuity in event-based vision}},
journal = {Nature communications},
year = {2026},
month = aug,
volume = {17},
number = {1},
pages = {9951},
publisher = {Nature Publishing Group},
issn = {2041-1723},
doi = {10.1038/s41467-026-76878-6},
url = {https://doi.org/10.1038/s41467-026-76878-6},
pmid = {42754584},
pmcid = {PMC13586331}
}

RIS

TY - JOUR
AU - Assa, Eldad
AU - Rivkind, Alexander
AU - Kreiserman, Michael
AU - Khan, Fahad Shahbaz
AU - Khan, Salman
AU - Ahissar, Ehud
TI - Temporal coding enables hyperacuity in event-based vision
T2 - Nature communications
J2 - Nat Commun
PY - 2026
DA - 2026/08/20
VL - 17
IS - 1
SP - 9951
SN - 2041-1723
PB - Nature Publishing Group
DO - 10.1038/s41467-026-76878-6
UR - https://doi.org/10.1038/s41467-026-76878-6
LA - en
ER -

CSL-JSON

{
"id": "10.1038/s41467-026-76878-6",
"type": "article-journal",
"title": "Temporal coding enables hyperacuity in event-based vision",
"container-title": "Nature communications",
"author": [
{
"family": "Assa",
"given": "Eldad"
},
{
"family": "Rivkind",
"given": "Alexander"
},
{
"family": "Kreiserman",
"given": "Michael"
},
{
"family": "Khan",
"given": "Fahad Shahbaz"
},
{
"family": "Khan",
"given": "Salman"
},
{
"family": "Ahissar",
"given": "Ehud"
}
],
"container-title-short": "Nat Commun",
"volume": "17",
"issue": "1",
"page": "9951",
"DOI": "10.1038/s41467-026-76878-6",
"PMID": "42754584",
"PMCID": "PMC13586331",
"ISSN": "2041-1723",
"publisher": "Nature Publishing Group",
"URL": "https://doi.org/10.1038/s41467-026-76878-6",
"language": "en",
"issued": {
"date-parts": [
[
2026,
8,
20
]
]
}
}

The tracing map gets a citation of its own once an author has validated it and it has a DOI.

Similar papers

The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.

[1] doi:10.1038/s41467-026-72057-9 [code]
Sex-specific behavioral feedback modulates sensorimotor processing and drives flexible social behavior.
Journal: Nature communications
In common: TensorFlow, OpenCV, PyTorch, 5 other tools, 1 reference
[2] doi:10.1038/s41467-026-74358-5 [code]
Brain-inspired spatial intelligence for embodied agents.
Journal: Nature communications
In common: TensorFlow, OpenCV, PyTorch, 5 other tools, cognitive
[3] doi:10.1038/s41598-026-43529-1 [code]
A spiking neural network inspired by neuroscience and psychology for Western mode- and key-conditioned music learning and composition.
Journal: Scientific reports
In common: TensorFlow, OpenCV, PyTorch, 5 other tools, cognitive
[4] doi:10.1162/imag.a.1207 [code]
Investigating the temporal dynamics and modeling of mid-level feature representations in humans.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: OpenCV, PyTorch, scikit-learn, 4 other tools, cognitive, 1 reference
[5] doi:10.1038/s42003-026-10957-8 [code]
Brain defence by the extracellular matrix protein Cochlin.
Journal: Communications biology
In common: TensorFlow, OpenCV, PyTorch, 5 other tools
[6] doi:10.1371/journal.pcbi.1014571 [code]
SynAPSeg: A novel dataset and image analysis framework for deep learning-based synapse detection and quantification.
Journal: PLoS computational biology
In common: TensorFlow, OpenCV, PyTorch, 5 other tools
[7] doi:10.1093/jnen/nlaf152 [code]
Clinical and pathologic correlations of machine learning quantification of Aβ deposits across 3 brain regions of decedents with Alzheimer disease.
Journal: Journal of neuropathology and experimental neurology
In common: TensorFlow, OpenCV, PyTorch, 5 other tools
[8] doi:10.1016/j.patter.2026.101538 [code]
A multi-modal foundation model for brain disease diagnosis and medical imaging.
Journal: Patterns (New York, N.Y.)
In common: TensorFlow, OpenCV, PyTorch, 5 other tools
[9] doi:10.1038/s41597-025-05174-7 [code]
A large-scale MEG and EEG dataset for object recognition in naturalistic scenes
Journal: n/a
In common: TensorFlow, OpenCV, PyTorch, 5 other tools
[10] doi:10.1126/sciadv.aed3650 [code]
Truthful visualizations for mass spectrometry imaging enable high-spatial-resolution interactive &lt;i&gt;m/z&lt;/i&gt; mapping and exploration.
Journal: Science advances
In common: TensorFlow, OpenCV, PyTorch, 5 other tools

Contribute

The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.

Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.

Request its removal

To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).

Discussion, reproductions, activity

Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.

Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.