Spinal-inspired artificial tactile interneuron with high-order burst spiking for intelligent edge interfaces.
The 4 matches
- [1] § Results › AMINs-SNN for multimodal object recognition ↔ SNNmodel/SNN_classification_multi_runs_450.py, lines 80–193 · score 0.60 · cosine annealing, confusion matrix, validation accuracy, schedule, configuration, classification
- [2] § Results › AMINs-SNN for multimodal object recognition ↔ SNNmodel/SNN_classification_multi_runs_450_rebuttal.py, lines 80–193 · score 0.60 · cosine annealing, confusion matrix, validation accuracy, schedule, configuration, classification
- [3] § Results › AMINs-SNN for multimodal object recognition ↔ SNNmodel/SNN_classification_multi_runs_450.py, lines 195–310 · score 0.59 · training epochs, Confusion matrix, Validation accuracy, Heatmaps, pads, classification
- [4] § Results › AMINs-SNN for multimodal object recognition ↔ SNNmodel/SNN_classification_multi_runs_450_rebuttal.py, lines 195–310 · score 0.59 · training epochs, Confusion matrix, Validation accuracy, Heatmaps, pads, classification
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 · 623 lines · 25 KB · no license · 2 matches
- from __future__ import print_function
- import os
- import argparse
- import torch
- import torch.nn as nn
- import torch.nn.functional as F
- import torch.optim as optim
- from torchvision import datasets, transforms
- from torch.optim.lr_scheduler import StepLR
- from sklearn.metrics import confusion_matrix, classification_report
- import numpy as np
- from merge_batchnorm import *
- from units import *
- from models import *
- from torch.optim.lr_scheduler import MultiStepLR
- import matplotlib.pyplot as plt
- from torch.utils.data import random_split
- from torch.optim.lr_scheduler import CosineAnnealingWarmRestarts
- import numpy as np
- import torch
- import torch.nn as nn
- import torch.nn.functional as F
- from torch.utils.data import DataLoader, TensorDataset
- import json
- from datetime import datetime
- import seaborn as sns
- from collections import defaultdict
- import pandas as pd
- import csv
- def train(model, device, train_loader, optimizer, epoch):
- criterion = nn.CrossEntropyLoss(label_smoothing=0.1)
- model.train()
- total_loss = 0
- for batch_idx, (data, target) in enumerate(train_loader):
- data, target = data.to(device), target.to(device)
- onehot = torch.nn.functional.one_hot(target, 20).float()
- optimizer.zero_grad()
- output = model(data)
- loss = criterion(output, target)
- loss.backward()
- optimizer.step()
- total_loss += loss.item()
- # Print less frequently to reduce output
- if batch_idx % 20 == 0:
- print(f'Train Epoch: {epoch} [{batch_idx * len(data)}/{len(train_loader.dataset)}]\tLoss: {loss.item():.6f}')
- return total_loss / len(train_loader)
- def validate(model, device, val_loader, testdataset_=False, return_cm=False):
- model.eval()
- correct = 0
- all_preds = []
- all_targets = []
- with torch.no_grad():
- for batch_idx, (data, target) in enumerate(val_loader):
- data, target = data.to(device), target.to(device)
- output = model(data)
- pred = output.argmax(dim=1, keepdim=False)
- correct += pred.eq(target.view_as(pred)).sum().item()
- all_preds.extend(pred.view(-1).cpu().numpy())
- all_targets.extend(target.cpu().numpy())
- accuracy = 100. * correct / len(val_loader.dataset)
- cm = confusion_matrix(all_targets, all_preds)
- print(f'Accuracy: {accuracy:.2f}%')
- if return_cm:
- return accuracy, cm, all_preds, all_targets
- elif testdataset_:
- return accuracy
- else:
- return accuracy
- def single_run(run_id, args, device):
- """
- Perform a single training run and return results
- """
- print(f"\n{'='*60}")
- print(f"RUN {run_id + 1}/5")
- print(f"{'='*60}")
- # Set different seed for each run
- torch.manual_seed(3407+17+run_id)
- np.random.seed(3407+17+run_id)
- # Load data
- train_data = torch.load('train_data_3dimension_force.pt')
- val_data = torch.load('val_data_3dimension_force.pt')
- test_data = torch.load('test_data_3dimension_force.pt')
- # train_data 300
- # val
- # test_data
- train_loader = DataLoader(TensorDataset(train_data['X'], train_data['y']), batch_size=32, shuffle=True)
- val_loader = DataLoader(TensorDataset(val_data['X'], val_data['y']), batch_size=2000, shuffle=False)
- test_loader = DataLoader(TensorDataset(test_data['X'], test_data['y']), batch_size=2000, shuffle=False)
- # Model configuration
- input_size = 450
- hidden_sizes = [384, 512, 768, 128]
- output_size = 20
- custom_dropouts = [0.10, 0.10, 0.35, 0.25]
- # Create model
- model = Net(input_size=input_size, hidden_sizes=hidden_sizes, output_size=output_size,
- quantize_level=args.T, dropout_rates=custom_dropouts).to(device)
- #optimizer = optim.Adam(model.parameters(), lr=args.lr)
- #scheduler = CosineAnnealingWarmRestarts(optimizer, T_0=10, T_mult=2, eta_min=1e-6)
- optimizer = optim.AdamW(model.parameters(), lr=0.001, weight_decay=1e-4)
- scheduler = optim.lr_scheduler.CosineAnnealingWarmRestarts(optimizer, T_0=10, T_mult=2, eta_min=1e-6)
- # Training tracking
- best_val_acc = 0.0
- best_model_path = f"snn_model_run_{run_id}.pt"
- train_losses = []
- val_accuracies = []
- test_accuracies = []
- print(f"Starting training for run {run_id + 1}...")
- # Training loop
- for epoch in range(1, args.epochs + 1):
- if epoch % 10 == 0: # Print every 10 epochs
- print(f"Run {run_id + 1} - Epoch {epoch}/{args.epochs}, LR: {scheduler.get_last_lr()[0]:.6f}")
- # Train
- train_loss = train(model, device, train_loader, optimizer, epoch)
- train_losses.append(train_loss)
- # Validate
- val_acc = validate(model, device, val_loader, testdataset_=True)
- test_acc = validate(model, device, test_loader, testdataset_=True)
- val_accuracies.append(val_acc)
- test_accuracies.append(test_acc)
- # Save best model
- if val_acc > best_val_acc:
- best_val_acc = val_acc
- torch.save(model.state_dict(), best_model_path)
- #if epoch % 10 == 0:
- print(f"Run {run_id + 1} - New best model saved with validation accuracy: {val_acc:.2f}%")
- scheduler.step()
- # Load best model for final evaluation
- model.load_state_dict(torch.load(best_model_path))
- print(f"\nRun {run_id + 1} - Final ANN evaluation:")
- final_ann_acc, ann_cm, ann_preds, ann_targets = validate(model, device, test_loader, testdataset_=True, return_cm=True)
- # Convert to quantized model
- print(f"Run {run_id + 1} - Converting to quantized model...")
- model_inference = InferenceNet(input_size=input_size, hidden_sizes=hidden_sizes,
- output_size=output_size, quantize_level=args.T).to(device)
- model_inference = transfer_weights_to_inference_model(model, model_inference)
- # Convert to SNN
- print(f"Run {run_id + 1} - Converting to SNN...")
- snn_quantized = Sparrow_SNN(input_size=input_size, hidden_sizes=hidden_sizes,
- output_size=output_size, quantized_index=8, T=args.T,
- Hybrid=args.Hybrid).to(device)
- snn_quantized.load_state_dict(model_inference.state_dict(), strict=False)
- # Final SNN evaluation
- print(f"Run {run_id + 1} - Final SNN evaluation:")
- final_snn_acc, snn_cm, snn_preds, snn_targets = validate(snn_quantized, device, test_loader, testdataset_=True, return_cm=True)
- # Clean up model file
- if os.path.exists(best_model_path):
- os.remove(best_model_path)
- return {
- 'run_id': run_id + 1,
- 'train_losses': train_losses,
- 'val_accuracies': val_accuracies,
- 'test_accuracies': test_accuracies,
- 'best_val_acc': best_val_acc,
- 'final_ann_acc': final_ann_acc,
- 'final_snn_acc': final_snn_acc,
- 'ann_confusion_matrix': ann_cm,
- 'snn_confusion_matrix': snn_cm,
- 'ann_predictions': ann_preds,
- 'ann_targets': ann_targets,
- 'snn_predictions': snn_preds,
- 'snn_targets': snn_targets
- }
- def plot_results(all_results, args):
- """
- Create simplified visualizations focusing on SNN confusion matrix and validation accuracy evolution
- """
- print("\nCreating simplified SNN visualizations...")
- # Create results directory
- results_dir = "results_450d_onlyforce"
- os.makedirs(results_dir, exist_ok=True)
- print(f"Results will be saved to: {results_dir}/")
- # Set publication-quality style
- plt.rcParams.update({
- 'font.size': 12,
- 'font.family': 'serif',
- 'font.serif': ['Times New Roman', 'DejaVu Serif'],
- 'axes.linewidth': 1.2,
- 'axes.spines.top': False,
- 'axes.spines.right': False,
- 'axes.grid': True,
- 'grid.alpha': 0.3,
- 'grid.linewidth': 0.8,
- 'legend.frameon': True,
- 'legend.fancybox': True,
- 'legend.shadow': True,
- 'legend.fontsize': 10,
- 'xtick.direction': 'in',
- 'ytick.direction': 'in',
- 'xtick.major.size': 4,
- 'ytick.major.size': 4,
- 'figure.dpi': 300,
- 'savefig.dpi': 300,
- 'savefig.bbox': 'tight',
- 'savefig.pad_inches': 0.1
- })
- # Create figure with 2 subplots
- fig = plt.figure(figsize=(16, 6))
- # ============ SUBPLOT 1: Average SNN Confusion Matrix ============
- ax1 = plt.subplot(1, 2, 1)
- # Calculate average confusion matrix across all runs
- avg_snn_cm = np.mean([result['snn_confusion_matrix'] for result in all_results], axis=0)
- # Normalize to percentages
- avg_snn_cm_norm = avg_snn_cm / avg_snn_cm.sum(axis=1, keepdims=True) * 100
- # Create heatmap
- im = plt.imshow(avg_snn_cm_norm, cmap='Blues', aspect='auto', vmin=0, vmax=100)
- # Add colorbar
- cbar = plt.colorbar(im, fraction=0.046, pad=0.04)
- cbar.set_label('Classification Accuracy (%)', fontweight='bold')
- # Add text annotations for all elements
- for i in range(avg_snn_cm_norm.shape[0]):
- for j in range(avg_snn_cm_norm.shape[1]):
- if avg_snn_cm_norm[i, j] > 50: # White text for dark cells
- color = 'white'
- else: # Black text for light cells
- color = 'black'
- text = plt.text(j, i, f'{avg_snn_cm_norm[i, j]:.1f}',
- ha="center", va="center", color=color,
- fontsize=8, fontweight='bold')
- # Set labels and title
- plt.xlabel('Predicted Class', fontweight='bold')
- plt.ylabel('True Class', fontweight='bold')
- #plt.title('Average SNN Confusion Matrix (450D, 5 Runs)', fontweight='bold', pad=15)
- # Set tick labels
- num_classes = avg_snn_cm_norm.shape[0]
- plt.xticks(range(num_classes), range(num_classes))
- plt.yticks(range(num_classes), range(num_classes))
- # ============ SUBPLOT 2: Validation Accuracy Evolution Over 310 Epochs ============
- ax2 = plt.subplot(1, 2, 2)
- # Professional color palette for different runs
- colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd']
- # Plot validation accuracy for each run
- for i, result in enumerate(all_results):
- epochs = range(1, len(result['val_accuracies']) + 1)
- plt.plot(epochs, result['val_accuracies'], color=colors[i % len(colors)],
- alpha=0.7, linewidth=1.5, label=f'Run {i+1}')
- plt.xlabel('Training Epoch', fontweight='bold')
- plt.ylabel('Validation Accuracy (%)', fontweight='bold')
- plt.legend(loc='lower right', ncol=2, fontsize=9)
- plt.grid(True, alpha=0.3)
- # Adjust layout
- plt.tight_layout(pad=2.0)
- # Save figure to results directory
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
- plot_filename = os.path.join(results_dir, f'snn_450d_results_{timestamp}.pdf')
- plt.savefig(plot_filename, format='pdf', dpi=300, bbox_inches='tight')
- plt.savefig(plot_filename.replace('.pdf', '.png'), format='png', dpi=300, bbox_inches='tight')
- print(f"450D SNN results figure saved as: {plot_filename}")
- # Calculate overall SNN accuracy from confusion matrix
- overall_accuracy = np.trace(avg_snn_cm_norm) / avg_snn_cm_norm.shape[0]
- print(f"\nAverage SNN Classification Accuracy: {overall_accuracy:.2f}%")
- # Show plot
- plt.show()
- # Reset matplotlib parameters
- plt.rcdefaults()
- return plot_filename
- def save_training_data_to_csv(all_results, results_dir, timestamp):
- """
- Save training process data (validation accuracies, test accuracies, losses) to CSV files
- """
- print("Saving training process data to CSV files...")
- # 1. Save individual run training curves
- for i, result in enumerate(all_results):
- run_data = {
- 'epoch': list(range(1, len(result['val_accuracies']) + 1)),
- 'train_loss': result['train_losses'],
- 'val_accuracy': result['val_accuracies'],
- 'test_accuracy': result['test_accuracies']
- }
- df = pd.DataFrame(run_data)
- csv_file = os.path.join(results_dir, f'training_curves_450d_run_{i+1}_{timestamp}.csv')
- df.to_csv(csv_file, index=False)
- print(f" Run {i+1} training curves saved: {csv_file}")
- # 2. Save combined validation accuracies, test accuracies, and losses across all runs
- max_epochs = max(len(result['val_accuracies']) for result in all_results)
- combined_data = {'epoch': list(range(1, max_epochs + 1))}
- for i, result in enumerate(all_results):
- # Pad with NaN if some runs have fewer epochs
- val_accs = result['val_accuracies'] + [None] * (max_epochs - len(result['val_accuracies']))
- test_accs = result['test_accuracies'] + [None] * (max_epochs - len(result['test_accuracies']))
- train_losses = result['train_losses'] + [None] * (max_epochs - len(result['train_losses']))
- combined_data[f'val_accuracy_run_{i+1}'] = val_accs
- combined_data[f'test_accuracy_run_{i+1}'] = test_accs
- combined_data[f'train_loss_run_{i+1}'] = train_losses
- combined_df = pd.DataFrame(combined_data)
- combined_csv = os.path.join(results_dir, f'all_runs_training_curves_450d_{timestamp}.csv')
- combined_df.to_csv(combined_csv, index=False)
- print(f" Combined training curves saved: {combined_csv}")
- return combined_csv
- def save_confusion_matrices_to_csv(all_results, results_dir, timestamp):
- """
- Save SNN confusion matrices to CSV files
- """
- print("Saving SNN confusion matrices to CSV files...")
- # 1. Save individual SNN confusion matrices
- for i, result in enumerate(all_results):
- # SNN confusion matrix
- snn_cm_df = pd.DataFrame(result['snn_confusion_matrix'],
- index=[f'True_{j}' for j in range(20)],
- columns=[f'Pred_{j}' for j in range(20)])
- snn_csv = os.path.join(results_dir, f'snn_confusion_matrix_450d_run_{i+1}_{timestamp}.csv')
- snn_cm_df.to_csv(snn_csv)
- print(f" Run {i+1} SNN confusion matrix saved: {snn_csv}")
- # 2. Save average SNN confusion matrix
- avg_snn_cm = np.mean([result['snn_confusion_matrix'] for result in all_results], axis=0)
- # Average SNN confusion matrix
- avg_snn_df = pd.DataFrame(avg_snn_cm,
- index=[f'True_{j}' for j in range(20)],
- columns=[f'Pred_{j}' for j in range(20)])
- avg_snn_csv = os.path.join(results_dir, f'avg_snn_confusion_matrix_450d_{timestamp}.csv')
- avg_snn_df.to_csv(avg_snn_csv)
- print(f" Average SNN confusion matrix saved: {avg_snn_csv}")
- # 3. Save normalized SNN confusion matrix (percentages)
- avg_snn_cm_norm = avg_snn_cm / avg_snn_cm.sum(axis=1, keepdims=True) * 100
- avg_snn_norm_df = pd.DataFrame(avg_snn_cm_norm,
- index=[f'True_{j}' for j in range(20)],
- columns=[f'Pred_{j}' for j in range(20)])
- avg_snn_norm_csv = os.path.join(results_dir, f'avg_snn_confusion_matrix_normalized_450d_{timestamp}.csv')
- avg_snn_norm_df.to_csv(avg_snn_norm_csv)
- print(f" Average SNN confusion matrix (normalized) saved: {avg_snn_norm_csv}")
- return avg_snn_csv, avg_snn_norm_csv
- def save_summary_results_to_csv(all_results, results_dir, timestamp):
- """
- Save summary results for each run to CSV
- """
- print("Saving summary results to CSV...")
- summary_data = {
- 'run_id': [],
- 'best_val_accuracy': [],
- 'final_ann_accuracy': [],
- 'final_snn_accuracy': [],
- 'accuracy_drop': []
- }
- for result in all_results:
- summary_data['run_id'].append(result['run_id'])
- summary_data['best_val_accuracy'].append(result['best_val_acc'])
- summary_data['final_ann_accuracy'].append(result['final_ann_acc'])
- summary_data['final_snn_accuracy'].append(result['final_snn_acc'])
- summary_data['accuracy_drop'].append(result['final_ann_acc'] - result['final_snn_acc'])
- # Add statistics
- ann_accuracies = summary_data['final_ann_accuracy']
- snn_accuracies = summary_data['final_snn_accuracy']
- accuracy_drops = summary_data['accuracy_drop']
- # Add summary row
- summary_data['run_id'].append('MEAN')
- summary_data['best_val_accuracy'].append(np.mean([result['best_val_acc'] for result in all_results]))
- summary_data['final_ann_accuracy'].append(np.mean(ann_accuracies))
- summary_data['final_snn_accuracy'].append(np.mean(snn_accuracies))
- summary_data['accuracy_drop'].append(np.mean(accuracy_drops))
- summary_data['run_id'].append('STD')
- summary_data['best_val_accuracy'].append(np.std([result['best_val_acc'] for result in all_results]))
- summary_data['final_ann_accuracy'].append(np.std(ann_accuracies))
- summary_data['final_snn_accuracy'].append(np.std(snn_accuracies))
- summary_data['accuracy_drop'].append(np.std(accuracy_drops))
- summary_df = pd.DataFrame(summary_data)
- summary_csv = os.path.join(results_dir, f'summary_results_450d_onlyforce_{timestamp}.csv')
- summary_df.to_csv(summary_csv, index=False)
- print(f" Summary results saved: {summary_csv}")
- return summary_csv
- def main():
- # Training settings
- parser = argparse.ArgumentParser(description='SNN Multi-Run Classification')
- parser.add_argument('--batch-size', type=int, default=512, metavar='N',
- help='input batch size for training (default: 512)')
- parser.add_argument('--test-batch-size', type=int, default=100, metavar='N',
- help='input batch size for testing (default: 100)')
- parser.add_argument('--epochs', type=int, default=310, metavar='N',
- help='number of epochs to train (default: 50)')
- parser.add_argument('--lr', type=float, default=2e-2, metavar='LR',
- help='learning rate (default: 2e-2)')
- parser.add_argument('--T', type=int, default=31, metavar='LR',
- help='time window size')
- parser.add_argument('--gamma', type=float, default=0.7, metavar='M',
- help='Learning rate step gamma (default: 0.7)')
- parser.add_argument('--no-cuda', action='store_true', default=False,
- help='disables CUDA training')
- parser.add_argument('--seed', type=int, default=1, metavar='S',
- help='random seed (default: 1)')
- parser.add_argument('--Hybrid', type=str, default=False, metavar='RESUME',
- help='Resume model from checkpoint')
- parser.add_argument('--num-runs', type=int, default=5, metavar='N',
- help='number of runs to perform (default: 5)')
- args = parser.parse_args()
- use_cuda = not args.no_cuda and torch.cuda.is_available()
- device = torch.device("cuda" if use_cuda else "cpu")
- print(f"Device: {device}")
- print(f"Running {args.num_runs} independent training runs...")
- print(f"Configuration: {args.epochs} epochs, LR={args.lr}, T={args.T}")
- # Store results from all runs
- all_results = []
- # Run multiple training sessions
- for run_id in range(args.num_runs):
- try:
- result = single_run(run_id, args, device)
- all_results.append(result)
- print(f"Run {run_id + 1} completed successfully!")
- print(f" ANN Accuracy: {result['final_ann_acc']:.2f}%")
- print(f" SNN Accuracy: {result['final_snn_acc']:.2f}%")
- except Exception as e:
- print(f"Run {run_id + 1} failed with error: {e}")
- continue
- if not all_results:
- print("No successful runs completed!")
- return
- # Calculate statistics
- print(f"\n{'='*60}")
- print("FINAL RESULTS SUMMARY")
- print(f"{'='*60}")
- ann_accuracies = [result['final_ann_acc'] for result in all_results]
- snn_accuracies = [result['final_snn_acc'] for result in all_results]
- print(f"\nANN Test Accuracies: {[f'{acc:.2f}%' for acc in ann_accuracies]}")
- print(f"SNN Test Accuracies: {[f'{acc:.2f}%' for acc in snn_accuracies]}")
- # ANN Statistics
- ann_mean = np.mean(ann_accuracies)
- ann_std = np.std(ann_accuracies)
- ann_ci = 1.96 * ann_std / np.sqrt(len(ann_accuracies)) # 95% confidence interval
- print(f"\nANN Results:")
- print(f" Mean ± Std: {ann_mean:.2f}% ± {ann_std:.2f}%")
- print(f" 95% CI: [{ann_mean - ann_ci:.2f}%, {ann_mean + ann_ci:.2f}%]")
- print(f" Range: [{min(ann_accuracies):.2f}%, {max(ann_accuracies):.2f}%]")
- # SNN Statistics
- snn_mean = np.mean(snn_accuracies)
- snn_std = np.std(snn_accuracies)
- snn_ci = 1.96 * snn_std / np.sqrt(len(snn_accuracies))
- print(f"\nSNN Results:")
- print(f" Mean ± Std: {snn_mean:.2f}% ± {snn_std:.2f}%")
- print(f" 95% CI: [{snn_mean - snn_ci:.2f}%, {snn_mean + snn_ci:.2f}%]")
- print(f" Range: [{min(snn_accuracies):.2f}%, {max(snn_accuracies):.2f}%]")
- # Accuracy drop analysis
- accuracy_drops = [ann - snn for ann, snn in zip(ann_accuracies, snn_accuracies)]
- drop_mean = np.mean(accuracy_drops)
- drop_std = np.std(accuracy_drops)
- print(f"\nAccuracy Drop (ANN → SNN):")
- print(f" Mean ± Std: {drop_mean:.2f}% ± {drop_std:.2f}%")
- print(f" Range: [{min(accuracy_drops):.2f}%, {max(accuracy_drops):.2f}%]")
- # Target result
- print(f"\n🎯 AVERAGE SNN TEST ACCURACY: {snn_mean:.2f}%")
- # Create results directory
- results_dir = "results_450d_onlyforce"
- os.makedirs(results_dir, exist_ok=True)
- # Save detailed results to the results directory
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
- results_file = os.path.join(results_dir, f'snn_450d_results_{timestamp}.json')
- # Prepare data for JSON serialization
- save_data = {
- 'timestamp': timestamp,
- 'configuration': {
- 'epochs': args.epochs,
- 'learning_rate': args.lr,
- 'time_window': args.T,
- 'num_runs': len(all_results),
- 'input_size': 450, # Original 450D input
- 'architecture': [384, 512, 768, 128], # Correct hidden layer sizes
- 'dropout_rates': [0.10, 0.10, 0.35, 0.25] # Correct dropout rates
- },
- 'results': {
- 'ann_accuracies': ann_accuracies,
- 'snn_accuracies': snn_accuracies,
- 'accuracy_drops': accuracy_drops
- },
- 'statistics': {
- 'ann': {
- 'mean': float(ann_mean),
- 'std': float(ann_std),
- 'ci_95': [float(ann_mean - ann_ci), float(ann_mean + ann_ci)],
- 'range': [float(min(ann_accuracies)), float(max(ann_accuracies))]
- },
- 'snn': {
- 'mean': float(snn_mean),
- 'std': float(snn_std),
- 'ci_95': [float(snn_mean - snn_ci), float(snn_mean + snn_ci)],
- 'range': [float(min(snn_accuracies)), float(max(snn_accuracies))]
- },
- 'accuracy_drop': {
- 'mean': float(drop_mean),
- 'std': float(drop_std),
- 'range': [float(min(accuracy_drops)), float(max(accuracy_drops))]
- }
- },
- 'all_results': [
- {
- 'run_id': result['run_id'],
- 'final_snn_acc': result['final_snn_acc'],
- 'snn_confusion_matrix': result['snn_confusion_matrix'].tolist()
- }
- for result in all_results
- ]
- }
- with open(results_file, 'w') as f:
- json.dump(save_data, f, indent=2)
- print(f"\nDetailed results saved to: {results_file}")
- # Create comprehensive visualizations
- plot_filename = plot_results(all_results, args)
- print(f"\n{'='*60}")
- print("450D MULTI-RUN ANALYSIS COMPLETE")
- print(f"{'='*60}")
- print(f"📁 Results directory: {results_dir}/")
- print(f"📊 Results file: {results_file}")
- print(f"📈 Main figure: {plot_filename}")
- print(f"🎯 Average SNN accuracy: {snn_mean:.2f}% ± {snn_std:.2f}%")
- print(f"📋 Input dimension: 450D (original)")
- print(f"🔄 Number of runs: {len(all_results)}")
- print(f"⚡ Training epochs: {args.epochs}")
- # List all files in results directory
- import glob
- result_files = glob.glob(os.path.join(results_dir, "*"))
- print(f"\n📂 Files in {results_dir}/:")
- for file in sorted(result_files):
- file_size = os.path.getsize(file) / 1024 # KB
- print(f" {os.path.basename(file)} ({file_size:.1f} KB)")
- #Save training data to CSV
- save_training_data_to_csv(all_results, results_dir, timestamp)
- # Save confusion matrices to CSV
- save_confusion_matrices_to_csv(all_results, results_dir, timestamp)
- # Save summary results to CSV
- save_summary_results_to_csv(all_results, results_dir, timestamp)
- if __name__ == '__main__':
- main()
SNN_classification_multi_runs_450.py at commit 917836f, no license · at the source
Overview
- School of Materials Science and Engineering, Zhejiang University, Hangzhou, China
- Zhejiang Key Laboratory of 3D Micro/Nano Fabrication and Characterization, Department of Electronic and Information Engineering, School of Engineering, Westlake University, Hangzhou, China
- School of Computing, National University of Singapore, Singapore, Singapore
- College of Information Science & Electronic Engineering, Zhejiang University, Hangzhou, China
- Department of Artificial Intelligence, School of Engineering, Westlake University, Hangzhou, China
- Westlake Institute for Optoelectronics, Westlake University, Hangzhou, China
- Institute of High Performance Computing, Agency for Science, Technology and Research, Singapore, Singapore
- Key Laboratory of Wide Band Gap Semiconductor Technology, School of Microelectronics, Xidian University, Xi’an, China
Abstract
The abstract is not reproduced here: the paper's license (CC BY-NC-ND) does not allow it. Read it in the paper, at the publisher or on Europe PMC.
Repository
Its files are read in the Code ↔ Paper reader above, with 4 matches between paragraphs and lines of code.
zhangluyan9/nc-spinal-inspired
917836f723db5aa475aa22aa3f5dbe08bd013be4, 2 June 2026Availability: 1 check, the latest on 27 September 2026: the link answers
- 27 September 2026: the link answers
19 files
- SNNmodel/
SNN_classification_multi , Python, 623 lines, 2 matches_runs_450.py - SNNmodel/
SNN_classification_multi , Python, 623 lines, 2 matches_runs_450_rebuttal.py - SNNmodel/
SNN_classification_multi , Python, 623 lines_runs_450_rebuttal_nonsp ar.py - SNNmodel/
merge_batchnorm.py , Python, 54 lines - SNNmodel/
models.py , Python, 237 lines - SNNmodel/
units.py , Python, 79 lines - data_process/
ISI_IBI_MBI_low_p.py , Python, 140 lines - data_process/
data_process_step1.py , Python, 72 lines - data_process/
data_process_step1_resor , Python, 20 linest.py - data_process/
data_process_step2.py , Python, 56 lines - data_process/
data_process_step2_force , Python, 44 lines_angel.py - data_process/
data_process_step2_gothr , Python, 29 linesoughminefunction.py - data_process/
data_process_step2_gothr , Python, 37 linesoughminefunction_low.py - data_process/
data_process_step2_tem_a , Python, 57 linesngle.py - data_process/
data_process_step2_tem_f , Python, 68 linesorce.py - data_process/
data_process_step2_tem_t , Python, 57 linesemper.py - data_process/
data_process_step3_train , Python, 79 linesing_testing.py - data_process/
data_process_step3_train , Python, 226 linesing_testing_savecsv.py - README.md, Text, 88 lines
Code availability statement
The paper has a code availability statement. Its license (CC BY-NC-ND) does not allow reproducing it here; in short, from what the harvester recognized in it:
- it points to the authors' code: zhangluyan9/
nc-spinal-inspired
Read it in the paper: doi.org/10.1038/s41467-026-76185-0.
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:
- 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
- 18 scripts, each with its path and the digest of its content;
- 4 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
No dataset and no data link were found in the paper.
Data availability statement
The paper has a data availability statement. Its license (CC BY-NC-ND) does not allow reproducing it here; in short, from what the harvester recognized in it:
- no repository, dataset or request procedure was recognized in it
Read it in the paper: doi.org/10.1038/s41467-026-76185-0.
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, 19 authors, 2 keywords, 6 MeSH terms, 2 funders, 38 references.
Cite
This paper
Li, F., Yan, Z., Mao, J., Liu, G., Ren, H., Qin, B., Zhang, Z., Zhang, H., Shen, Y., Zheng, Z., Feng, W., Li, D., Tang, Y., Wang, S., Jin, Y., Luo, T., Wong, W.-f., Wang, H., & Zhu, B. (2026). Spinal-inspired artificial tactile interneuron with high-order burst spiking for intelligent edge interfaces. Nature communications, 17(1), 9285. https://
BibTeX
@article{li2026spinal,
author = {Li, Fanfan and Yan, Zhanglu and Mao, Jiayi and Liu, Guolei and Ren, Huihui and Qin, Bangbang and Zhang, Zhongfang and Zhang, Haiyue and Shen, Yiyang and Zheng, Zeqi and Feng, Weilong and Li, Dingwei and Tang, Yingjie and Wang, Saisai and Jin, Yaochu and Luo, Tao and Wong, Weng-fai and Wang, Hong and Zhu, Bowen},
title = {{Spinal-inspired artificial tactile interneuron with high-order burst spiking for intelligent edge interfaces}},
journal = {Nature communications},
year = {2026},
month = jul,
volume = {17},
number = {1},
pages = {9285},
publisher = {Nature Publishing Group},
issn = {2041-1723},
doi = {10.1038/
url = {https://
pmid = {42669703},
pmcid = {PMC13526877}
}
RIS
TY - JOUR
AU - Li, Fanfan
AU - Yan, Zhanglu
AU - Mao, Jiayi
AU - Liu, Guolei
AU - Ren, Huihui
AU - Qin, Bangbang
AU - Zhang, Zhongfang
AU - Zhang, Haiyue
AU - Shen, Yiyang
AU - Zheng, Zeqi
AU - Feng, Weilong
AU - Li, Dingwei
AU - Tang, Yingjie
AU - Wang, Saisai
AU - Jin, Yaochu
AU - Luo, Tao
AU - Wong, Weng-fai
AU - Wang, Hong
AU - Zhu, Bowen
TI - Spinal-inspired artificial tactile interneuron with high-order burst spiking for intelligent edge interfaces
T2 - Nature communications
J2 - Nat Commun
PY - 2026
DA - 2026/
VL - 17
IS - 1
SP - 9285
SN - 2041-1723
PB - Nature Publishing Group
DO - 10.1038/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1038/
"type": "article-journal",
"title": "Spinal-inspired artificial tactile interneuron with high-order burst spiking for intelligent edge interfaces",
"container-title": "Nature communications",
"author": [
{
"family": "Li",
"given": "Fanfan"
},
{
"family": "Yan",
"given": "Zhanglu"
},
{
"family": "Mao",
"given": "Jiayi"
},
{
"family": "Liu",
"given": "Guolei"
},
{
"family": "Ren",
"given": "Huihui"
},
{
"family": "Qin",
"given": "Bangbang"
},
{
"family": "Zhang",
"given": "Zhongfang"
},
{
"family": "Zhang",
"given": "Haiyue"
},
{
"family": "Shen",
"given": "Yiyang"
},
{
"family": "Zheng",
"given": "Zeqi"
},
{
"family": "Feng",
"given": "Weilong"
},
{
"family": "Li",
"given": "Dingwei"
},
{
"family": "Tang",
"given": "Yingjie"
},
{
"family": "Wang",
"given": "Saisai"
},
{
"family": "Jin",
"given": "Yaochu"
},
{
"family": "Luo",
"given": "Tao"
},
{
"family": "Wong",
"given": "Weng-fai"
},
{
"family": "Wang",
"given": "Hong"
},
{
"family": "Zhu",
"given": "Bowen"
}
],
"container-title-short":
"volume": "17",
"issue": "1",
"page": "9285",
"DOI": "10.1038/
"PMID": "42669703",
"PMCID": "PMC13526877",
"ISSN": "2041-1723",
"publisher": "Nature Publishing Group",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
7,
31
]
]
}
}
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-75979-6 [code]
- Artificial neural manifolds.Journal: Nature communicationsIn common: scikit-learn, Matplotlib, NumPy, 4 references, 2 authors
- [2] doi:10.1038/s41467-026-72428-2 [code]
- Artificial plateau neurons with in-situ spike-malleability for rhythmic quadrupedal locomotion.Journal: Nature communicationsIn common: PyTorch, pandas, NumPy, 3 references
- [3] doi:10.1002/advs.202519479 [code]
- Diminished Signal-to-Noise Ratio Disrupts Somatosensory Population Encoding and Drives Tactile Hyposensitivity in the Fmr1&
lt;sup& gt;-/ y& lt;/ sup& gt; Autism Model. Journal: Advanced science (Weinheim, Baden-Wurttemberg, Germany)In common: seaborn, scikit-learn, pandas, 2 other tools, cognitive, 1 reference - [4] doi:10.1038/s41597-026-07955-0 [code]
- Human neuron activity during an 83-minute movie from 2,286 neurons and 29 patients.Journal: Scientific dataIn common: PyTorch, seaborn, pandas, 2 other tools, 1 reference
- [5] doi:10.1038/s41593-026-02257-5 [code]
- Neural sequences underlying directed turning in Caenorhabditis elegans.Journal: Nature neuroscienceIn common: PyTorch, seaborn, pandas, 2 other tools, 1 reference
- [6] doi:10.1016/j.celrep.2026.117419 [code]
- Conserved role of primary motor cortex in the control of prehension in mice and macaques.Journal: Cell reportsIn common: seaborn, scikit-learn, pandas, 2 other tools, 1 reference
- [7] doi:10.3389/fnins.2026.1874302 [code]
- Architecture-data matching for EEG-EMG decoding: compact deep models match classical spectral decoders on the WAY-EEG-GAL grasp-and-lift dataset.Journal: Frontiers in neuroscienceIn common: PyTorch, scikit-learn, pandas, 2 other tools, 1 reference
- [8] doi:10.1038/s41467-026-73669-x [code]
- Homeostatic dendritic neuron based on co-integrated volatile and non-volatile memristors for neuromorphic processing.Journal: Nature communicationsIn common: PyTorch, scikit-learn, Matplotlib, 1 other tool, 1 reference
- [9] doi:10.7554/elife.106554 [code]
- Enhanced tactile coding in rat neocortex under darkness.Journal: eLifeIn common: PyTorch, scikit-learn, pandas, 1 other tool, 1 reference
- [10] doi:10.1016/j.isci.2026.115375 [code]
- Bursts of regional cortical inhibition during smartphone use.Journal: iScienceIn common: scikit-learn, pandas, Matplotlib, 1 other tool, 1 reference
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.
Claim this paper
Correct its record
Say what each link of this record is, remove the ones that are not the paper's, add the ones that are missing. The correction becomes a new version of the record, in its Versions section.
Validate its tracing map
You validate the map as this page shows it: 1 repository of the authors' code, each at its verified commit and with its license, 18 scripts, and 4 matches between paragraphs and code (see the Code and Map sections). It then receives a DOI on Zenodo, with you (your ORCID iD) and OSCR as its creators; the code itself is not deposited.
The map's fingerprint: sha256:d1f63f081f56ecb7…
Add the badge to its README
The badge links the code to this page. Copy one of these into the README of the paper's code: only you decide where it goes, and nothing is changed for you.
Markdown
[, paste the snippet at the top, then “Commit changes…” and, to review it first, “Create a new branch and start a pull request”. You open the pull request; OSCR asks for no permission.
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.
