https://github.com/smilies-polito/Spiker
git clone https://github.com/soerenab/AudioMNIST.git
1. **從 pip 套件庫安裝**
執行以下指令即可安裝:
```bash
pip install spikerplus
pip install spikerplus --break-system-packages
pip3 install torch torchvision torchaudio tonic
```
2. **從 GitHub 原始碼庫安裝最新版**
如果你想安裝最新版本,可以依照下列步驟操作:
方法 A:
```bash
git clone https://github.com/smilies-polito/Spiker.git
cd Spiker/spiker
pip install .
```
方法 B(等價於方法 A):
```bash
git clone https://github.com/smilies-polito/Spiker.git
cd Spiker/spiker
python setup.py install
```
以下是如何使用 **spiker** 框架的概述,假設你已經完成安裝與設置。這個 Python 套件旨在將從高階網路描述建立、訓練、優化到生成用於 FPGA 實作脈衝神經網路(SNN)加速器的 VHDL 描述的過程最小化所需的工作量。主要組件包括:
- **NetBuilder**
將高階(以 Python 字典描述)網路結構轉換為一個基於 snnTorch 可訓練的脈衝神經網路。
- **Trainer**
負責訓練網路,可使用 PyTorch 的 dataloader 提供訓練與測試資料。
- **Optimizer**
將網路內部參數轉換為可在目標硬體(如 FPGA)上實作的固定點格式,透過格點搜尋(grid search)探索不同位寬配置,並記錄每組配置的損失與準確率。
- **VHDL Generator**
將優化且訓練完成的網路轉換成對應的 VHDL 描述,以供硬體實作。
以下是一個逐步的使用指南與示例程式碼:
---
### 1. 設定日誌
為了在執行過程中顯示進度訊息,請先配置 Python 內建的 logging 模組:
```python
import logging
logging.basicConfig(level=logging.INFO)
```
---
### 2. 建立網路
首先,以 Python 字典描述你的網路結構。例如:
```python
net_dict = {
"n_cycles": 73,
"n_inputs": 40,
"layer_0": {
"neuron_model": "lif",
"n_neurons": 128,
"alpha": None,
"learn_alpha": False,
"beta": 0.9375,
"learn_beta": False,
"threshold": 1.0,
"learn_threshold": False,
"reset_mechanism": "subtract"
},
"layer_1": {
"neuron_model": "lif",
"n_neurons": 10,
"alpha": None,
"learn_alpha": False,
"beta": 0.9375,
"learn_beta": False,
"threshold": 1.0,
"learn_threshold": False,
"reset_mechanism": "none"
}
}
```
然後,使用 **NetBuilder** 建立網路:
```python
from spikerplus import NetBuilder
net_builder = NetBuilder(net_dict)
snn = net_builder.build()
```
---
### 3. 訓練網路
接下來,利用 **Trainer** 進行網路的訓練。Trainer 需要兩個 PyTorch 的 dataloader(分別用於訓練與測試)。例如:
```python
from spikerplus import Trainer
# 請根據你的資料集定義 train_loader 和 test_loader
trainer = Trainer(snn)
trainer.train(train_loader, test_loader)
```
*注意:* 請確保你的 dataloader 已正確設置。你可以參考相關教學或 snnTorch 的文件來建立 dataloader。
---
### 4. 優化網路
訓練完成後,使用 **Optimizer** 將網路的參數轉換成適合硬體實作的固定點格式。首先,定義優化配置的字典:
```python
optim_config = {
"weights_bw": {
"min": 5,
"max": 6
},
"neurons_bw": {
"min": 5,
"max": 6
},
"fp_dec": {
"min": 2,
"max": 3
}
}
```
然後,執行優化:
```python
from spikerplus import Optimizer
opt = Optimizer(snn, net_dict, optim_config)
opt.optimize(test_loader)
```
在此步驟中,Optimizer 將探索不同的量化配置,並記錄每個配置下的損失與準確率。
---
### 5. 產生 VHDL 程式碼
最後,根據優化器的結果選擇最符合需求的權衡設定,並定義硬體參數:
```python
optim_params = {
"weights_bw": 6,
"neurons_bw": 8,
"fp_dec": 4
}
```
使用 **VhdlGenerator** 生成對應的 VHDL 描述:
```python
from spikerplus import VhdlGenerator
vhdl_generator = VhdlGenerator(snn, optim_params)
vhdl_snn = vhdl_generator.generate()
# 印出生成的 VHDL 程式碼
print(vhdl_snn.code())
```
這段 VHDL 程式碼可以作為後續綜合(synthesizable)你的 FPGA 加速器的基礎。
---
### 總結
1. **匯入套件:**
```python
import spikerplus
```
2. **設定日誌:**
```python
import logging
logging.basicConfig(level=logging.INFO)
```
3. **使用 Python 字典定義網路,並透過 `NetBuilder` 建立網路。**
4. **利用 `Trainer` 並搭配合適的 dataloader 進行網路訓練。**
5. **使用 `Optimizer` 透過格點搜尋優化並量化網路參數。**
6. **藉由 `VhdlGenerator` 指定固定點參數生成 VHDL 程式碼。**
按照以上步驟,你就可以有效地使用 spiker 框架來設計、訓練、優化並生成 FPGA 加速器所需的脈衝神經網路硬體描述。
整體流程:
```
import os
import sys
import logging
import argparse
# ---------------------------
# 1. 停用 IPython/Spyder 的 autoreload(若有)
# ---------------------------
try:
from IPython import get_ipython
ip = get_ipython()
if ip is not None:
ip.run_line_magic("autoreload", "0")
except Exception:
pass
# ---------------------------
# 2. 檢查工作目錄中是否有與 torchvision 同名的檔案或資料夾
# ---------------------------
if os.path.exists(os.path.join(os.getcwd(), "torchvision")):
print("Error: 發現工作目錄中有名為 'torchvision' 的檔案或資料夾。")
print("請將其重新命名或移除,以避免與官方 torchvision 套件衝突。")
sys.exit(1)
# ---------------------------
# 3. 檢查 tonic 模組是否已安裝(spikerplus 所需)
# ---------------------------
try:
import tonic
except ModuleNotFoundError:
print("Error: 模組 'tonic' 尚未安裝!請在命令提示字元中執行:")
print(" pip install tonic")
sys.exit(1)
# ---------------------------
# 4. 匯入 spikerplus 所需模組
# ---------------------------
from spikerplus.dataloaders import AudioMnistDL
from spikerplus import NetBuilder, Trainer, Optimizer, VhdlGenerator
from spikerplus.vhdl import write_vhdl, compile_vhdl
# 設定日誌層級為 INFO
logging.basicConfig(level=logging.INFO, stream=sys.stdout, force=True)
def main():
parser = argparse.ArgumentParser(
description="使用 AudioMNIST 資料集訓練 SNN、優化參數及生成 VHDL 程式碼")
parser.add_argument('--epochs', type=int, default=3,
help="設定訓練的 Epoch 數量")
args = parser.parse_args()
# ---------------------------
# 資料集載入
# ---------------------------
batch_size = 64
# 使用原始字串指定 Windows 路徑,避免反斜線轉義問題
data_dir = r"C:\Users\Potter Wang\Desktop\SNN\AudioMNIST-master\AudioMNIST-master\data"
data_loader = AudioMnistDL(data_dir=data_dir)
train_loader, test_loader = data_loader.load(batch_size=batch_size)
# 取得資料中每筆的時間步數與輸入維度
n_cycles = next(iter(train_loader))[0].shape[1]
n_inputs = next(iter(train_loader))[0].shape[2]
# ---------------------------
# SNN 網路參數設定
# ---------------------------
net_dict = {
"n_cycles": n_cycles,
"n_inputs": n_inputs,
"layer_0": {
"neuron_model": "lif",
"n_neurons": 128,
"alpha": None,
"learn_alpha": False,
"beta": 0.9375,
"learn_beta": False,
"threshold": 1.0,
"learn_threshold": False,
"reset_mechanism": "subtract"
},
"layer_1": {
"neuron_model": "lif",
"n_neurons": 10,
"alpha": None,
"learn_alpha": False,
"beta": 0.9375,
"learn_beta": False,
"threshold": 1.0,
"learn_threshold": False,
"reset_mechanism": "none"
}
}
# 建立 SNN 網路
net_builder = NetBuilder(net_dict)
snn = net_builder.build()
logging.info("Network configured: %s", net_dict)
logging.info("Network ready: %s", snn)
# ---------------------------
# SNN 訓練
# ---------------------------
trainer = Trainer(snn)
for epoch in range(args.epochs):
logging.info("Epoch %d/%d", epoch + 1, args.epochs)
train_loss, train_acc = trainer.train_one_epoch(train_loader)
try:
val_loss, val_acc = trainer.validate(test_loader)
logging.info("Train loss: %.4f, Train acc: %.4f, Val loss: %.4f, Val acc: %.4f",
train_loss, train_acc, val_loss, val_acc)
except AttributeError:
logging.info("Train loss: %.4f, Train acc: %.4f", train_loss, train_acc)
# ---------------------------
# SNN 優化(固定點格式轉換)
# ---------------------------
optim_config = {
"weights_bw": {"min": 8, "max": 8},
"neurons_bw": {"min": 16, "max": 16},
"fp_dec": {"min": 6, "max": 8}
}
optimizer = Optimizer(snn, net_dict, optim_config)
optimizer.optimize(test_loader)
optim_params = {
"weights_bw": 8,
"neurons_bw": 16,
"fp_dec": 6
}
# ---------------------------
# 產生 VHDL 程式碼
# ---------------------------
vhdl_generator = VhdlGenerator(snn, optim_params)
vhdl_snn = vhdl_generator.generate(functional=False, interface=True)
# 輸出 VHDL 程式碼並寫入指定目錄
print(vhdl_snn.code())
write_vhdl(vhdl_snn, output_dir="Spiker", rm=True)
# 若需要,可使用 compile_vhdl 進行編譯:
# compile_vhdl(vhdl_snn, output_dir="Spiker")
if __name__ == "__main__":
main()
```
```
import os
import torch
import sys
import logging
import argparse
# ---------------------------
# 1. 停用 IPython/Spyder 的 autoreload(若有)
# ---------------------------
try:
from IPython import get_ipython
ip = get_ipython()
if ip is not None:
ip.run_line_magic("autoreload", "0")
except Exception:
pass
# ---------------------------
# 2. 檢查工作目錄中是否有與 torchvision 同名的檔案或資料夾
# ---------------------------
if os.path.exists(os.path.join(os.getcwd(), "torchvision")):
print("Error: 發現工作目錄中有名為 'torchvision' 的檔案或資料夾。")
print("請將其重新命名或移除,以避免與官方 torchvision 套件衝突。")
sys.exit(1)
# ---------------------------
# 4. 匯入 spikerplus 所需模組
# ---------------------------
from spikerplus.dataloaders import AudioMnistDL
from spikerplus import NetBuilder, Trainer, Optimizer, VhdlGenerator
from spikerplus.vhdl import write_vhdl, compile_vhdl
from spikerplus.vhdl import NetworkSimulator
# 設定日誌層級為 INFO
logging.basicConfig(level=logging.INFO, stream=sys.stdout, force=True)
def main():
# ---------------------------
# 資料集載入
# ---------------------------
batch_size = 64
data_dir = r"C:\Users\Potter Wang\Desktop\SNN\AudioMNIST-master\AudioMNIST-master\data"
data_loader = AudioMnistDL(data_dir=data_dir)
train_loader, test_loader = data_loader.load(batch_size=batch_size)
n_cycles = next(iter(train_loader))[0].shape[1]
n_inputs = next(iter(train_loader))[0].shape[2]
# ---------------------------
# SNN 網路參數設定
# ---------------------------
net_dict = {
"n_cycles": n_cycles,
"n_inputs": n_inputs,
"layer_0": { # 隱藏層
"neuron_model": "lif",
"n_neurons": 128,
#"alpha": None,
#"learn_alpha": False,
"beta": 0.9375,
#"learn_beta": False,
"n_exc_inputs": n_inputs,
"threshold": 1.0,
#"learn_threshold": False,
"reset_mechanism": "subtract"
},
"layer_1": { # 輸出層
"neuron_model": "lif",
"n_neurons": 10,
#"alpha": None,
#"learn_alpha": False,
"beta": 0.9375,
#"learn_beta": False,
"threshold": 1.0,
#"learn_threshold": False,
"reset_mechanism": "none"
}
}
optim_config = {
"weights_bw": {"min": 8, "max": 8},
"neurons_bw": {"min": 16, "max": 16},
"fp_dec": {"min": 8, "max": 8}
}
optim_params = {
"weights_bw": 8,
"neurons_bw": 16,
"fp_dec": 6
}
net_builder = NetBuilder(net_dict)
snn = net_builder.build()
"""
trainer = Trainer(snn) #, readout_type="spk"
trainer.train(train_loader, test_loader, n_epochs=1, store=True)
"""
if os.path.exists("Trained/trained_state_dict.pt"):
state_dict = torch.load("Trained/trained_state_dict.pt")
snn.load_state_dict(state_dict)
else:
logging.info("權重檔案不存在,將從頭開始訓練。")
"""
optimizer = Optimizer(snn, net_dict, optim_config)
optimizer.optimize(test_loader)
"""
vhdl_generator = VhdlGenerator(snn, optim_params)
vhdl_snn = vhdl_generator.generate(functional=True, interface=False)
write_vhdl(vhdl_snn, output_dir="Spiker") #, rm=True
compile_vhdl(vhdl_snn, output_dir="Spiker") # 如需要,可編譯 VHDL
vhdl_sim = NetworkSimulator(vhdl_snn)
vhdl_sim.simulate(test_loader,sim_duration="200us")#,sim_duration="1000us"
if __name__ == "__main__":
main()
```