potnn 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- potnn-1.0.0/LICENSE +72 -0
- potnn-1.0.0/PKG-INFO +260 -0
- potnn-1.0.0/README.md +225 -0
- potnn-1.0.0/potnn/__init__.py +86 -0
- potnn-1.0.0/potnn/codegen/__init__.py +20 -0
- potnn-1.0.0/potnn/codegen/bit2.py +263 -0
- potnn-1.0.0/potnn/codegen/fp130.py +269 -0
- potnn-1.0.0/potnn/codegen/header.py +460 -0
- potnn-1.0.0/potnn/codegen/level5.py +393 -0
- potnn-1.0.0/potnn/codegen/scale.py +184 -0
- potnn-1.0.0/potnn/codegen/ternary.py +354 -0
- potnn-1.0.0/potnn/codegen/unroll.py +616 -0
- potnn-1.0.0/potnn/config.py +112 -0
- potnn-1.0.0/potnn/export.py +2196 -0
- potnn-1.0.0/potnn/fuse.py +167 -0
- potnn-1.0.0/potnn/modules/__init__.py +11 -0
- potnn-1.0.0/potnn/modules/add.py +114 -0
- potnn-1.0.0/potnn/modules/avgpool.py +173 -0
- potnn-1.0.0/potnn/modules/base.py +225 -0
- potnn-1.0.0/potnn/modules/conv.py +203 -0
- potnn-1.0.0/potnn/modules/conv1d.py +317 -0
- potnn-1.0.0/potnn/modules/depthwise.py +216 -0
- potnn-1.0.0/potnn/modules/linear.py +199 -0
- potnn-1.0.0/potnn/quantize/__init__.py +35 -0
- potnn-1.0.0/potnn/quantize/calibration.py +233 -0
- potnn-1.0.0/potnn/quantize/integer_ops.py +207 -0
- potnn-1.0.0/potnn/quantize/integer_sim.py +225 -0
- potnn-1.0.0/potnn/quantize/pot.py +455 -0
- potnn-1.0.0/potnn/quantize/qat.py +356 -0
- potnn-1.0.0/potnn/utils/__init__.py +13 -0
- potnn-1.0.0/potnn/utils/allocation.py +240 -0
- potnn-1.0.0/potnn/utils/memory.py +158 -0
- potnn-1.0.0/potnn/wrapper.py +304 -0
- potnn-1.0.0/potnn.egg-info/PKG-INFO +260 -0
- potnn-1.0.0/potnn.egg-info/SOURCES.txt +38 -0
- potnn-1.0.0/potnn.egg-info/dependency_links.txt +1 -0
- potnn-1.0.0/potnn.egg-info/requires.txt +2 -0
- potnn-1.0.0/potnn.egg-info/top_level.txt +1 -0
- potnn-1.0.0/setup.cfg +4 -0
- potnn-1.0.0/setup.py +31 -0
potnn-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
PoT-NN Dual License
|
|
2
|
+
====================
|
|
3
|
+
|
|
4
|
+
This software is available under a dual license model:
|
|
5
|
+
|
|
6
|
+
1. GNU General Public License v3.0 (GPL-3.0) - For open source use
|
|
7
|
+
2. Commercial License - For proprietary/closed-source use
|
|
8
|
+
|
|
9
|
+
--------------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
OPTION 1: GPL-3.0 (Open Source)
|
|
12
|
+
--------------------------------
|
|
13
|
+
|
|
14
|
+
Copyright (c) 2026 PoT-NN Developers
|
|
15
|
+
|
|
16
|
+
This program is free software: you can redistribute it and/or modify
|
|
17
|
+
it under the terms of the GNU General Public License as published by
|
|
18
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
19
|
+
(at your option) any later version.
|
|
20
|
+
|
|
21
|
+
This program is distributed in the hope that it will be useful,
|
|
22
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
23
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
24
|
+
GNU General Public License for more details.
|
|
25
|
+
|
|
26
|
+
You should have received a copy of the GNU General Public License
|
|
27
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
28
|
+
|
|
29
|
+
**Summary of GPL-3.0 Requirements:**
|
|
30
|
+
- You must disclose your source code if you distribute the software
|
|
31
|
+
- Derivative works must also be licensed under GPL-3.0
|
|
32
|
+
- You must include the original copyright notice
|
|
33
|
+
- Changes to the code must be documented
|
|
34
|
+
|
|
35
|
+
--------------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
OPTION 2: Commercial License
|
|
38
|
+
-----------------------------
|
|
39
|
+
|
|
40
|
+
For companies and individuals who wish to use PoT-NN in proprietary products
|
|
41
|
+
without the obligations of the GPL license, a commercial license is available.
|
|
42
|
+
|
|
43
|
+
**Commercial License Benefits:**
|
|
44
|
+
- Use in closed-source products
|
|
45
|
+
- No requirement to disclose your source code
|
|
46
|
+
- No copyleft obligations
|
|
47
|
+
- Priority support available
|
|
48
|
+
|
|
49
|
+
**To obtain a commercial license, please contact:**
|
|
50
|
+
- Email: [YOUR_EMAIL@example.com]
|
|
51
|
+
- Website: [YOUR_WEBSITE]
|
|
52
|
+
|
|
53
|
+
--------------------------------------------------------------------------------
|
|
54
|
+
|
|
55
|
+
Which License Applies to You?
|
|
56
|
+
------------------------------
|
|
57
|
+
|
|
58
|
+
- **Open Source Projects**: If your project is open source and will be
|
|
59
|
+
distributed under GPL-3.0 or a compatible license, you can use PoT-NN
|
|
60
|
+
for free under the GPL-3.0 license.
|
|
61
|
+
|
|
62
|
+
- **Commercial/Proprietary Use**: If you want to use PoT-NN in a proprietary
|
|
63
|
+
product without releasing your source code, you need a commercial license.
|
|
64
|
+
|
|
65
|
+
- **Internal Use**: If you're only using PoT-NN internally and not distributing
|
|
66
|
+
it, GPL-3.0 does not require you to release your source code. However, if
|
|
67
|
+
you want dedicated support or prefer clear legal terms, consider a
|
|
68
|
+
commercial license.
|
|
69
|
+
|
|
70
|
+
--------------------------------------------------------------------------------
|
|
71
|
+
|
|
72
|
+
Full GPL-3.0 License Text: https://www.gnu.org/licenses/gpl-3.0.txt
|
potnn-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: potnn
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Multiplication-free neural networks for ultra-low-power MCUs
|
|
5
|
+
Home-page: https://github.com/scienthoon/potnn
|
|
6
|
+
Author: Scienthoon
|
|
7
|
+
Author-email: scienthoon@gmail.com
|
|
8
|
+
License: GPL-3.0
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
18
|
+
Classifier: Topic :: Software Development :: Embedded Systems
|
|
19
|
+
Requires-Python: >=3.8
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: torch>=2.0.0
|
|
23
|
+
Requires-Dist: numpy
|
|
24
|
+
Dynamic: author
|
|
25
|
+
Dynamic: author-email
|
|
26
|
+
Dynamic: classifier
|
|
27
|
+
Dynamic: description
|
|
28
|
+
Dynamic: description-content-type
|
|
29
|
+
Dynamic: home-page
|
|
30
|
+
Dynamic: license
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
Dynamic: requires-dist
|
|
33
|
+
Dynamic: requires-python
|
|
34
|
+
Dynamic: summary
|
|
35
|
+
|
|
36
|
+
# PoT-NN: Multiplication-Free Neural Networks for Ultra-Low-Power MCUs
|
|
37
|
+
|
|
38
|
+
[](https://opensource.org/licenses/MIT)
|
|
39
|
+
[](https://www.python.org/downloads/)
|
|
40
|
+
[](https://pytorch.org/)
|
|
41
|
+
|
|
42
|
+
**PoT-NN** is a quantization framework that enables **deep learning inference without multiplication**.
|
|
43
|
+
Run neural networks on ultra-low-cost MCUs without hardware multipliers (CH32V003, PY32F003, etc.).
|
|
44
|
+
|
|
45
|
+
> ๐ฐ๐ท [ํ๊ตญ์ด ๋ฌธ์](README_ko.md)
|
|
46
|
+
|
|
47
|
+
## ๐ฏ Key Features
|
|
48
|
+
|
|
49
|
+
| Feature | Description |
|
|
50
|
+
|---------|-------------|
|
|
51
|
+
| **Multiplication-Free** | All weights quantized to powers-of-two, using only `<<`, `>>`, `+` operations |
|
|
52
|
+
| **Integer-Only Inference** | No floating-point operations, only `int8`/`int32` arithmetic |
|
|
53
|
+
| **5 Encoding Modes** | Choose between accuracy vs. memory tradeoff |
|
|
54
|
+
| **Auto C Export** | Generates standalone C header files with zero dependencies |
|
|
55
|
+
| **Bit-Exact Matching** | Guaranteed 100% match between Python simulation and C code |
|
|
56
|
+
|
|
57
|
+
## ๐ฆ Installation
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
git clone https://github.com/YOUR_USERNAME/potnn.git
|
|
61
|
+
cd potnn
|
|
62
|
+
pip install -e .
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## ๐ Quick Start
|
|
66
|
+
|
|
67
|
+
### Method 1: One-Line Training (Recommended)
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
import torch
|
|
71
|
+
import torch.nn as nn
|
|
72
|
+
import potnn
|
|
73
|
+
from potnn import PoTConv2d, PoTLinear
|
|
74
|
+
|
|
75
|
+
# 1. Define model using PoT layers
|
|
76
|
+
class TinyNet(nn.Module):
|
|
77
|
+
def __init__(self):
|
|
78
|
+
super().__init__()
|
|
79
|
+
self.conv1 = PoTConv2d(1, 8, kernel_size=3, padding=1)
|
|
80
|
+
self.conv2 = PoTConv2d(8, 16, kernel_size=3, padding=1)
|
|
81
|
+
self.pool = nn.AdaptiveAvgPool2d(1) # Auto-replaced with PoTGlobalAvgPool
|
|
82
|
+
self.fc = PoTLinear(16, 10)
|
|
83
|
+
|
|
84
|
+
def forward(self, x):
|
|
85
|
+
x = torch.relu(self.conv1(x))
|
|
86
|
+
x = nn.functional.max_pool2d(x, 2)
|
|
87
|
+
x = torch.relu(self.conv2(x))
|
|
88
|
+
x = self.pool(x).view(x.size(0), -1)
|
|
89
|
+
return self.fc(x)
|
|
90
|
+
|
|
91
|
+
model = TinyNet()
|
|
92
|
+
|
|
93
|
+
# 2. Configure
|
|
94
|
+
config = potnn.Config(
|
|
95
|
+
flash=16384, # Target MCU Flash (bytes)
|
|
96
|
+
ram=2048, # Target MCU RAM (bytes)
|
|
97
|
+
mean=0.1307, # Dataset mean
|
|
98
|
+
std=0.3081, # Dataset std
|
|
99
|
+
input_h=16, input_w=16, input_channels=1,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# 3. Train (Float โ Calibrate โ QAT โ Integer Sim)
|
|
103
|
+
model = potnn.train(model, train_loader, test_loader, config,
|
|
104
|
+
float_epochs=15, qat_epochs=50)
|
|
105
|
+
|
|
106
|
+
# 4. Export to C
|
|
107
|
+
potnn.export(model, "model.h", config)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Method 2: Manual Pipeline
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
import potnn
|
|
114
|
+
|
|
115
|
+
# Step 1: Train float model (standard PyTorch training)
|
|
116
|
+
train_float(model, train_loader, epochs=15)
|
|
117
|
+
|
|
118
|
+
# Step 2: Fuse BatchNorm into Conv (if any)
|
|
119
|
+
potnn.fuse_batchnorm(model)
|
|
120
|
+
|
|
121
|
+
# Step 3: Calibrate activation scales
|
|
122
|
+
potnn.calibrate(model, train_loader, config)
|
|
123
|
+
|
|
124
|
+
# Step 4: Prepare for QAT
|
|
125
|
+
potnn.prepare_qat(model, config)
|
|
126
|
+
|
|
127
|
+
# Step 5: QAT training
|
|
128
|
+
train_qat(model, train_loader, epochs=50)
|
|
129
|
+
|
|
130
|
+
# Step 6: Enable integer simulation (C-compatible)
|
|
131
|
+
potnn.enable_integer_sim(model, input_std=config.std, input_mean=config.mean)
|
|
132
|
+
|
|
133
|
+
# Step 7: Export
|
|
134
|
+
potnn.export(model, "model.h", config)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## ๐ Encoding Modes
|
|
138
|
+
|
|
139
|
+
Choose encoding based on accuracy vs. memory tradeoff:
|
|
140
|
+
|
|
141
|
+
| Encoding | Levels | Values | Bits/Weight | Best For |
|
|
142
|
+
|----------|--------|--------|-------------|----------|
|
|
143
|
+
| `unroll` | 17 | 0, ยฑ1, ยฑ2, ยฑ4, ..., ยฑ128 | Code-unrolled | Highest accuracy |
|
|
144
|
+
| `fp130` | 16 | ยฑ1, ยฑ2, ยฑ4, ..., ยฑ128 | 4-bit | Dense layers |
|
|
145
|
+
| `5level` | 5 | -8, -1, 0, +1, +8 | 4-bit (skip) | Balanced |
|
|
146
|
+
| `2bit` | 4 | -2, -1, +1, +2 | 2-bit | Smallest memory |
|
|
147
|
+
| `ternary` | 3 | -1, 0, +1 | 2-bit (RLE) | Sparse models |
|
|
148
|
+
|
|
149
|
+
### Per-Layer Encoding
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
config = potnn.Config(
|
|
153
|
+
flash=16384, ram=2048,
|
|
154
|
+
layer_encodings={
|
|
155
|
+
'conv1': 'unroll', # First layer: max accuracy
|
|
156
|
+
'conv2': '5level', # Middle layer
|
|
157
|
+
'fc': 'unroll', # Last layer: max accuracy
|
|
158
|
+
},
|
|
159
|
+
default_encoding='5level'
|
|
160
|
+
)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Encoding Details
|
|
164
|
+
|
|
165
|
+
#### `unroll` (Default)
|
|
166
|
+
- Weights embedded directly as shift-add operations
|
|
167
|
+
- Zero weights omitted entirely (sparse-friendly)
|
|
168
|
+
- Largest code size, highest accuracy
|
|
169
|
+
|
|
170
|
+
#### `fp130` (FP1.3.0 Format)
|
|
171
|
+
- 4-bit packing: `[sign(1)][exp(3)]`
|
|
172
|
+
- No zero (zeros replaced with ยฑ1)
|
|
173
|
+
- Good for dense layers
|
|
174
|
+
|
|
175
|
+
#### `5level` (Skip Encoding)
|
|
176
|
+
- 4-bit packing: `[skip(2)][sign(1)][mag(1)]`
|
|
177
|
+
- Skip field compresses consecutive zeros (0-3)
|
|
178
|
+
- **Constraint**: Max 3 consecutive zeros (4th+ replaced with +1)
|
|
179
|
+
|
|
180
|
+
#### `2bit`
|
|
181
|
+
- 2-bit packing: `[sign(1)][shift(1)]`
|
|
182
|
+
- Smallest memory (16 weights per uint32)
|
|
183
|
+
- No zero (zeros replaced with ยฑ1)
|
|
184
|
+
|
|
185
|
+
#### `ternary` (Triple-Run)
|
|
186
|
+
- 2-bit codes with run-length encoding
|
|
187
|
+
- `11` code = repeat previous value 2 more times
|
|
188
|
+
- Best for very sparse models
|
|
189
|
+
|
|
190
|
+
## ๐ Supported Layers
|
|
191
|
+
|
|
192
|
+
| Layer | Class | Notes |
|
|
193
|
+
|-------|-------|-------|
|
|
194
|
+
| Conv2D | `PoTConv2d` | All standard parameters supported |
|
|
195
|
+
| Conv1D | `PoTConv1d` | For time series |
|
|
196
|
+
| Depthwise | `PoTDepthwiseConv2d` | MobileNet-style |
|
|
197
|
+
| Linear | `PoTLinear` | Fully connected |
|
|
198
|
+
| GAP | Auto-replaced | `nn.AdaptiveAvgPool2d(1)` โ `PoTGlobalAvgPool` |
|
|
199
|
+
| Add | `PoTAdd` | For residual connections |
|
|
200
|
+
| BatchNorm | Auto-fused | Merged into preceding Conv/Linear |
|
|
201
|
+
|
|
202
|
+
## โ๏ธ API Reference
|
|
203
|
+
|
|
204
|
+
### `potnn.Config`
|
|
205
|
+
|
|
206
|
+
| Parameter | Type | Required | Description |
|
|
207
|
+
|-----------|------|----------|-------------|
|
|
208
|
+
| `flash` | int | โ
| Flash memory budget (bytes) |
|
|
209
|
+
| `ram` | int | โ
| RAM budget (bytes) |
|
|
210
|
+
| `mean` | float/list | โ | Dataset mean (single or per-channel) |
|
|
211
|
+
| `std` | float/list | โ | Dataset std |
|
|
212
|
+
| `input_h`, `input_w` | int | โ | Input dimensions (default: 16ร16) |
|
|
213
|
+
| `input_channels` | int | โ | Input channels (default: 1) |
|
|
214
|
+
| `layer_encodings` | dict | โ | Per-layer encoding override |
|
|
215
|
+
| `default_encoding` | str | โ | Default encoding (default: 'unroll') |
|
|
216
|
+
|
|
217
|
+
### Key Functions
|
|
218
|
+
|
|
219
|
+
```python
|
|
220
|
+
potnn.train(model, train_loader, test_loader, config, ...) # Full pipeline
|
|
221
|
+
potnn.calibrate(model, data_loader, config) # Calibrate scales
|
|
222
|
+
potnn.prepare_qat(model, config) # Enable QAT mode
|
|
223
|
+
potnn.enable_integer_sim(model, input_std, input_mean) # C-compatible mode
|
|
224
|
+
potnn.export(model, output_path, config) # Generate C code
|
|
225
|
+
potnn.fuse_batchnorm(model) # Fuse BN layers
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## ๐งช Verified Results
|
|
229
|
+
|
|
230
|
+
- **Bit-Exact Matching**: Python integer simulation matches C output 100%
|
|
231
|
+
- **MNIST**: 97%+ accuracy with 12KB binary
|
|
232
|
+
- **100-Model Stress Test**: Verified across random architectures
|
|
233
|
+
|
|
234
|
+
## ๐ License
|
|
235
|
+
|
|
236
|
+
**Dual License**: GPL-3.0 + Commercial
|
|
237
|
+
|
|
238
|
+
| Use Case | License |
|
|
239
|
+
|----------|---------|
|
|
240
|
+
| Open Source Projects | GPL-3.0 (Free) |
|
|
241
|
+
| Proprietary/Commercial | Commercial License (Contact us) |
|
|
242
|
+
|
|
243
|
+
See [LICENSE](LICENSE) for details.
|
|
244
|
+
|
|
245
|
+
## ๐ Contributing
|
|
246
|
+
|
|
247
|
+
This project was created by a solo developer without formal CS education.
|
|
248
|
+
There may be bugs, inefficiencies, or areas for improvement.
|
|
249
|
+
|
|
250
|
+
**Any contributions are greatly appreciated!**
|
|
251
|
+
- ๐ Bug reports
|
|
252
|
+
- ๐ก Feature suggestions
|
|
253
|
+
- ๐ง Pull requests
|
|
254
|
+
- ๐ Documentation improvements
|
|
255
|
+
|
|
256
|
+
If you find issues or have ideas, please open an issue or PR. Thank you!
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
**Made with โค๏ธ for ultra-low-power AI**
|
potnn-1.0.0/README.md
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# PoT-NN: Multiplication-Free Neural Networks for Ultra-Low-Power MCUs
|
|
2
|
+
|
|
3
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
|
+
[](https://www.python.org/downloads/)
|
|
5
|
+
[](https://pytorch.org/)
|
|
6
|
+
|
|
7
|
+
**PoT-NN** is a quantization framework that enables **deep learning inference without multiplication**.
|
|
8
|
+
Run neural networks on ultra-low-cost MCUs without hardware multipliers (CH32V003, PY32F003, etc.).
|
|
9
|
+
|
|
10
|
+
> ๐ฐ๐ท [ํ๊ตญ์ด ๋ฌธ์](README_ko.md)
|
|
11
|
+
|
|
12
|
+
## ๐ฏ Key Features
|
|
13
|
+
|
|
14
|
+
| Feature | Description |
|
|
15
|
+
|---------|-------------|
|
|
16
|
+
| **Multiplication-Free** | All weights quantized to powers-of-two, using only `<<`, `>>`, `+` operations |
|
|
17
|
+
| **Integer-Only Inference** | No floating-point operations, only `int8`/`int32` arithmetic |
|
|
18
|
+
| **5 Encoding Modes** | Choose between accuracy vs. memory tradeoff |
|
|
19
|
+
| **Auto C Export** | Generates standalone C header files with zero dependencies |
|
|
20
|
+
| **Bit-Exact Matching** | Guaranteed 100% match between Python simulation and C code |
|
|
21
|
+
|
|
22
|
+
## ๐ฆ Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
git clone https://github.com/YOUR_USERNAME/potnn.git
|
|
26
|
+
cd potnn
|
|
27
|
+
pip install -e .
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## ๐ Quick Start
|
|
31
|
+
|
|
32
|
+
### Method 1: One-Line Training (Recommended)
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
import torch
|
|
36
|
+
import torch.nn as nn
|
|
37
|
+
import potnn
|
|
38
|
+
from potnn import PoTConv2d, PoTLinear
|
|
39
|
+
|
|
40
|
+
# 1. Define model using PoT layers
|
|
41
|
+
class TinyNet(nn.Module):
|
|
42
|
+
def __init__(self):
|
|
43
|
+
super().__init__()
|
|
44
|
+
self.conv1 = PoTConv2d(1, 8, kernel_size=3, padding=1)
|
|
45
|
+
self.conv2 = PoTConv2d(8, 16, kernel_size=3, padding=1)
|
|
46
|
+
self.pool = nn.AdaptiveAvgPool2d(1) # Auto-replaced with PoTGlobalAvgPool
|
|
47
|
+
self.fc = PoTLinear(16, 10)
|
|
48
|
+
|
|
49
|
+
def forward(self, x):
|
|
50
|
+
x = torch.relu(self.conv1(x))
|
|
51
|
+
x = nn.functional.max_pool2d(x, 2)
|
|
52
|
+
x = torch.relu(self.conv2(x))
|
|
53
|
+
x = self.pool(x).view(x.size(0), -1)
|
|
54
|
+
return self.fc(x)
|
|
55
|
+
|
|
56
|
+
model = TinyNet()
|
|
57
|
+
|
|
58
|
+
# 2. Configure
|
|
59
|
+
config = potnn.Config(
|
|
60
|
+
flash=16384, # Target MCU Flash (bytes)
|
|
61
|
+
ram=2048, # Target MCU RAM (bytes)
|
|
62
|
+
mean=0.1307, # Dataset mean
|
|
63
|
+
std=0.3081, # Dataset std
|
|
64
|
+
input_h=16, input_w=16, input_channels=1,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# 3. Train (Float โ Calibrate โ QAT โ Integer Sim)
|
|
68
|
+
model = potnn.train(model, train_loader, test_loader, config,
|
|
69
|
+
float_epochs=15, qat_epochs=50)
|
|
70
|
+
|
|
71
|
+
# 4. Export to C
|
|
72
|
+
potnn.export(model, "model.h", config)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Method 2: Manual Pipeline
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
import potnn
|
|
79
|
+
|
|
80
|
+
# Step 1: Train float model (standard PyTorch training)
|
|
81
|
+
train_float(model, train_loader, epochs=15)
|
|
82
|
+
|
|
83
|
+
# Step 2: Fuse BatchNorm into Conv (if any)
|
|
84
|
+
potnn.fuse_batchnorm(model)
|
|
85
|
+
|
|
86
|
+
# Step 3: Calibrate activation scales
|
|
87
|
+
potnn.calibrate(model, train_loader, config)
|
|
88
|
+
|
|
89
|
+
# Step 4: Prepare for QAT
|
|
90
|
+
potnn.prepare_qat(model, config)
|
|
91
|
+
|
|
92
|
+
# Step 5: QAT training
|
|
93
|
+
train_qat(model, train_loader, epochs=50)
|
|
94
|
+
|
|
95
|
+
# Step 6: Enable integer simulation (C-compatible)
|
|
96
|
+
potnn.enable_integer_sim(model, input_std=config.std, input_mean=config.mean)
|
|
97
|
+
|
|
98
|
+
# Step 7: Export
|
|
99
|
+
potnn.export(model, "model.h", config)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## ๐ Encoding Modes
|
|
103
|
+
|
|
104
|
+
Choose encoding based on accuracy vs. memory tradeoff:
|
|
105
|
+
|
|
106
|
+
| Encoding | Levels | Values | Bits/Weight | Best For |
|
|
107
|
+
|----------|--------|--------|-------------|----------|
|
|
108
|
+
| `unroll` | 17 | 0, ยฑ1, ยฑ2, ยฑ4, ..., ยฑ128 | Code-unrolled | Highest accuracy |
|
|
109
|
+
| `fp130` | 16 | ยฑ1, ยฑ2, ยฑ4, ..., ยฑ128 | 4-bit | Dense layers |
|
|
110
|
+
| `5level` | 5 | -8, -1, 0, +1, +8 | 4-bit (skip) | Balanced |
|
|
111
|
+
| `2bit` | 4 | -2, -1, +1, +2 | 2-bit | Smallest memory |
|
|
112
|
+
| `ternary` | 3 | -1, 0, +1 | 2-bit (RLE) | Sparse models |
|
|
113
|
+
|
|
114
|
+
### Per-Layer Encoding
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
config = potnn.Config(
|
|
118
|
+
flash=16384, ram=2048,
|
|
119
|
+
layer_encodings={
|
|
120
|
+
'conv1': 'unroll', # First layer: max accuracy
|
|
121
|
+
'conv2': '5level', # Middle layer
|
|
122
|
+
'fc': 'unroll', # Last layer: max accuracy
|
|
123
|
+
},
|
|
124
|
+
default_encoding='5level'
|
|
125
|
+
)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Encoding Details
|
|
129
|
+
|
|
130
|
+
#### `unroll` (Default)
|
|
131
|
+
- Weights embedded directly as shift-add operations
|
|
132
|
+
- Zero weights omitted entirely (sparse-friendly)
|
|
133
|
+
- Largest code size, highest accuracy
|
|
134
|
+
|
|
135
|
+
#### `fp130` (FP1.3.0 Format)
|
|
136
|
+
- 4-bit packing: `[sign(1)][exp(3)]`
|
|
137
|
+
- No zero (zeros replaced with ยฑ1)
|
|
138
|
+
- Good for dense layers
|
|
139
|
+
|
|
140
|
+
#### `5level` (Skip Encoding)
|
|
141
|
+
- 4-bit packing: `[skip(2)][sign(1)][mag(1)]`
|
|
142
|
+
- Skip field compresses consecutive zeros (0-3)
|
|
143
|
+
- **Constraint**: Max 3 consecutive zeros (4th+ replaced with +1)
|
|
144
|
+
|
|
145
|
+
#### `2bit`
|
|
146
|
+
- 2-bit packing: `[sign(1)][shift(1)]`
|
|
147
|
+
- Smallest memory (16 weights per uint32)
|
|
148
|
+
- No zero (zeros replaced with ยฑ1)
|
|
149
|
+
|
|
150
|
+
#### `ternary` (Triple-Run)
|
|
151
|
+
- 2-bit codes with run-length encoding
|
|
152
|
+
- `11` code = repeat previous value 2 more times
|
|
153
|
+
- Best for very sparse models
|
|
154
|
+
|
|
155
|
+
## ๐ Supported Layers
|
|
156
|
+
|
|
157
|
+
| Layer | Class | Notes |
|
|
158
|
+
|-------|-------|-------|
|
|
159
|
+
| Conv2D | `PoTConv2d` | All standard parameters supported |
|
|
160
|
+
| Conv1D | `PoTConv1d` | For time series |
|
|
161
|
+
| Depthwise | `PoTDepthwiseConv2d` | MobileNet-style |
|
|
162
|
+
| Linear | `PoTLinear` | Fully connected |
|
|
163
|
+
| GAP | Auto-replaced | `nn.AdaptiveAvgPool2d(1)` โ `PoTGlobalAvgPool` |
|
|
164
|
+
| Add | `PoTAdd` | For residual connections |
|
|
165
|
+
| BatchNorm | Auto-fused | Merged into preceding Conv/Linear |
|
|
166
|
+
|
|
167
|
+
## โ๏ธ API Reference
|
|
168
|
+
|
|
169
|
+
### `potnn.Config`
|
|
170
|
+
|
|
171
|
+
| Parameter | Type | Required | Description |
|
|
172
|
+
|-----------|------|----------|-------------|
|
|
173
|
+
| `flash` | int | โ
| Flash memory budget (bytes) |
|
|
174
|
+
| `ram` | int | โ
| RAM budget (bytes) |
|
|
175
|
+
| `mean` | float/list | โ | Dataset mean (single or per-channel) |
|
|
176
|
+
| `std` | float/list | โ | Dataset std |
|
|
177
|
+
| `input_h`, `input_w` | int | โ | Input dimensions (default: 16ร16) |
|
|
178
|
+
| `input_channels` | int | โ | Input channels (default: 1) |
|
|
179
|
+
| `layer_encodings` | dict | โ | Per-layer encoding override |
|
|
180
|
+
| `default_encoding` | str | โ | Default encoding (default: 'unroll') |
|
|
181
|
+
|
|
182
|
+
### Key Functions
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
potnn.train(model, train_loader, test_loader, config, ...) # Full pipeline
|
|
186
|
+
potnn.calibrate(model, data_loader, config) # Calibrate scales
|
|
187
|
+
potnn.prepare_qat(model, config) # Enable QAT mode
|
|
188
|
+
potnn.enable_integer_sim(model, input_std, input_mean) # C-compatible mode
|
|
189
|
+
potnn.export(model, output_path, config) # Generate C code
|
|
190
|
+
potnn.fuse_batchnorm(model) # Fuse BN layers
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## ๐งช Verified Results
|
|
194
|
+
|
|
195
|
+
- **Bit-Exact Matching**: Python integer simulation matches C output 100%
|
|
196
|
+
- **MNIST**: 97%+ accuracy with 12KB binary
|
|
197
|
+
- **100-Model Stress Test**: Verified across random architectures
|
|
198
|
+
|
|
199
|
+
## ๐ License
|
|
200
|
+
|
|
201
|
+
**Dual License**: GPL-3.0 + Commercial
|
|
202
|
+
|
|
203
|
+
| Use Case | License |
|
|
204
|
+
|----------|---------|
|
|
205
|
+
| Open Source Projects | GPL-3.0 (Free) |
|
|
206
|
+
| Proprietary/Commercial | Commercial License (Contact us) |
|
|
207
|
+
|
|
208
|
+
See [LICENSE](LICENSE) for details.
|
|
209
|
+
|
|
210
|
+
## ๐ Contributing
|
|
211
|
+
|
|
212
|
+
This project was created by a solo developer without formal CS education.
|
|
213
|
+
There may be bugs, inefficiencies, or areas for improvement.
|
|
214
|
+
|
|
215
|
+
**Any contributions are greatly appreciated!**
|
|
216
|
+
- ๐ Bug reports
|
|
217
|
+
- ๐ก Feature suggestions
|
|
218
|
+
- ๐ง Pull requests
|
|
219
|
+
- ๐ Documentation improvements
|
|
220
|
+
|
|
221
|
+
If you find issues or have ideas, please open an issue or PR. Thank you!
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
**Made with โค๏ธ for ultra-low-power AI**
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""potnn: Power-of-Two Neural Network Compiler for Ultra-Low-Cost MCUs
|
|
2
|
+
|
|
3
|
+
A PyTorch-based library for training and deploying neural networks
|
|
4
|
+
on MCUs without multiplication instructions, using only shifts and adds.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
__version__ = "0.4.8"
|
|
8
|
+
|
|
9
|
+
# Import core modules
|
|
10
|
+
from .modules.conv import PoTConv2d
|
|
11
|
+
from .modules.conv1d import PoTConv1d
|
|
12
|
+
from .modules.depthwise import PoTDepthwiseConv2d
|
|
13
|
+
from .modules.linear import PoTLinear
|
|
14
|
+
from .modules.add import PoTAdd
|
|
15
|
+
from .modules.avgpool import PoTGlobalAvgPool
|
|
16
|
+
from .config import Config
|
|
17
|
+
from .export import export
|
|
18
|
+
from .quantize.calibration import calibrate_model
|
|
19
|
+
from .quantize.qat import prepare_qat, enable_integer_sim, disable_integer_sim
|
|
20
|
+
from .wrapper import train
|
|
21
|
+
from .fuse import fuse_batchnorm, check_bn_fused
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def calibrate(model, data_loader, config=None, num_batches=10, mean=None, std=None):
|
|
25
|
+
"""Calibrate model activation scales.
|
|
26
|
+
|
|
27
|
+
Two calling conventions supported:
|
|
28
|
+
|
|
29
|
+
1. With config (recommended):
|
|
30
|
+
calibrate(model, loader, config, num_batches=10)
|
|
31
|
+
|
|
32
|
+
2. Direct parameters:
|
|
33
|
+
calibrate(model, loader, num_batches=10, mean=[0.1307], std=[0.3081])
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
model: Model with PoT layers
|
|
37
|
+
data_loader: Calibration data loader
|
|
38
|
+
config: potnn.Config object (optional, extracts mean/std from it)
|
|
39
|
+
num_batches: Number of batches for calibration (default: 10)
|
|
40
|
+
mean: Dataset mean (list or float), used if config not provided
|
|
41
|
+
std: Dataset std (list or float), used if config not provided
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
Dictionary of activation max values per layer
|
|
45
|
+
"""
|
|
46
|
+
# Handle config object
|
|
47
|
+
if config is not None and isinstance(config, Config):
|
|
48
|
+
mean = config.mean if config.mean is not None else [0.0]
|
|
49
|
+
std = config.std if config.std is not None else [1.0]
|
|
50
|
+
|
|
51
|
+
# Handle num_batches passed as config (common mistake)
|
|
52
|
+
if isinstance(config, int):
|
|
53
|
+
num_batches = config
|
|
54
|
+
config = None
|
|
55
|
+
|
|
56
|
+
# Default values
|
|
57
|
+
if mean is None:
|
|
58
|
+
mean = 0.0
|
|
59
|
+
if std is None:
|
|
60
|
+
std = 1.0
|
|
61
|
+
|
|
62
|
+
return calibrate_model(model, data_loader, num_batches=num_batches, mean=mean, std=std)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
__all__ = [
|
|
66
|
+
'PoTConv2d',
|
|
67
|
+
'PoTConv1d',
|
|
68
|
+
'PoTDepthwiseConv2d',
|
|
69
|
+
'PoTLinear',
|
|
70
|
+
'PoTAdd',
|
|
71
|
+
'PoTGlobalAvgPool',
|
|
72
|
+
'Config',
|
|
73
|
+
'export',
|
|
74
|
+
'calibrate_model',
|
|
75
|
+
'calibrate',
|
|
76
|
+
'prepare_qat',
|
|
77
|
+
'enable_integer_sim',
|
|
78
|
+
'disable_integer_sim',
|
|
79
|
+
'train',
|
|
80
|
+
'fuse_batchnorm',
|
|
81
|
+
'check_bn_fused',
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
# Package metadata
|
|
85
|
+
__author__ = "potnn developers"
|
|
86
|
+
__license__ = "MIT"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""C code generation module for potnn."""
|
|
2
|
+
|
|
3
|
+
from .header import generate_c_header
|
|
4
|
+
from .unroll import generate_unrolled_layer
|
|
5
|
+
from .scale import decompose_scale_to_shifts, generate_scale_func
|
|
6
|
+
from .fp130 import generate_fp130_layer
|
|
7
|
+
from .bit2 import generate_2bit_layer
|
|
8
|
+
from .level5 import generate_5level_layer
|
|
9
|
+
from .ternary import generate_ternary_layer
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
'generate_c_header',
|
|
13
|
+
'generate_unrolled_layer',
|
|
14
|
+
'decompose_scale_to_shifts',
|
|
15
|
+
'generate_scale_func',
|
|
16
|
+
'generate_fp130_layer',
|
|
17
|
+
'generate_2bit_layer',
|
|
18
|
+
'generate_5level_layer',
|
|
19
|
+
'generate_ternary_layer',
|
|
20
|
+
]
|