shem-neural 0.1.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.
- shem_neural-0.1.0/PKG-INFO +37 -0
- shem_neural-0.1.0/README.md +12 -0
- shem_neural-0.1.0/pyproject.toml +81 -0
- shem_neural-0.1.0/pyproject.toml.orig +67 -0
- shem_neural-0.1.0/src/shem_neural/__init__.py +7 -0
- shem_neural-0.1.0/src/shem_neural/compile.py +117 -0
- shem_neural-0.1.0/src/shem_neural/cpu.py +364 -0
- shem_neural-0.1.0/src/shem_neural/export.py +103 -0
- shem_neural-0.1.0/src/shem_neural/gpu.py +89 -0
- shem_neural-0.1.0/src/shem_neural/isa.py +116 -0
- shem_neural-0.1.0/src/shem_neural/memory.py +256 -0
- shem_neural-0.1.0/src/shem_neural/model/__init__.py +6 -0
- shem_neural-0.1.0/src/shem_neural/model/blocks.py +223 -0
- shem_neural-0.1.0/src/shem_neural/model/generate.py +301 -0
- shem_neural-0.1.0/src/shem_neural/model/importers.py +242 -0
- shem_neural-0.1.0/src/shem_neural/model/manifest.py +58 -0
- shem_neural-0.1.0/src/shem_neural/model/tokenizer.py +75 -0
- shem_neural-0.1.0/src/shem_neural/model/tools.py +150 -0
- shem_neural-0.1.0/src/shem_neural/server.py +434 -0
- shem_neural-0.1.0/src/shem_neural/vcpu.py +140 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: shem-neural
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: shem's neural side: an RV32IM CPU compiled into network weights, and model images
|
|
5
|
+
Keywords: shem,containers,riscv,neural-network,language-models
|
|
6
|
+
Author: João Galego
|
|
7
|
+
Author-email: João Galego <jgalego1990@gmail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
13
|
+
Classifier: Topic :: System :: Emulators
|
|
14
|
+
Requires-Dist: huggingface-hub>=2.4.0
|
|
15
|
+
Requires-Dist: jinja2>=3.1.6
|
|
16
|
+
Requires-Dist: numpy>=2
|
|
17
|
+
Requires-Dist: safetensors>=0.4
|
|
18
|
+
Requires-Dist: tokenizers>=0.23.3
|
|
19
|
+
Requires-Dist: torch>=2.5
|
|
20
|
+
Requires-Python: >=3.12
|
|
21
|
+
Project-URL: Homepage, https://github.com/JGalego/shem
|
|
22
|
+
Project-URL: Documentation, https://github.com/JGalego/shem/tree/main/docs
|
|
23
|
+
Project-URL: Repository, https://github.com/JGalego/shem
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# shem-neural
|
|
27
|
+
|
|
28
|
+
The neural vCPU for [shem](https://github.com/JGalego/shem): an RV32IM CPU compiled into the weights of a network that runs one instruction per forward pass.
|
|
29
|
+
|
|
30
|
+

|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
uv run pytest # unit tests
|
|
34
|
+
uv run python -m shem_neural.server --socket /tmp/shem.sock # serve vCPUs to the host
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
See the [neural vCPU documentation](https://github.com/JGalego/shem/blob/main/docs/neural.md) for how the network works and how the host talks to it.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# shem-neural
|
|
2
|
+
|
|
3
|
+
The neural vCPU for [shem](https://github.com/JGalego/shem): an RV32IM CPU compiled into the weights of a network that runs one instruction per forward pass.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
uv run pytest # unit tests
|
|
9
|
+
uv run python -m shem_neural.server --socket /tmp/shem.sock # serve vCPUs to the host
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
See the [neural vCPU documentation](https://github.com/JGalego/shem/blob/main/docs/neural.md) for how the network works and how the host talks to it.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "shem-neural"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "shem's neural side: an RV32IM CPU compiled into network weights, and model images"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
keywords = [
|
|
8
|
+
"shem",
|
|
9
|
+
"containers",
|
|
10
|
+
"riscv",
|
|
11
|
+
"neural-network",
|
|
12
|
+
"language-models",
|
|
13
|
+
]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
19
|
+
"Topic :: System :: Emulators",
|
|
20
|
+
]
|
|
21
|
+
requires-python = ">=3.12"
|
|
22
|
+
dependencies = [
|
|
23
|
+
"huggingface-hub>=2.4.0",
|
|
24
|
+
"jinja2>=3.1.6",
|
|
25
|
+
"numpy>=2",
|
|
26
|
+
"safetensors>=0.4",
|
|
27
|
+
"tokenizers>=0.23.3",
|
|
28
|
+
"torch>=2.5",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[[project.authors]]
|
|
32
|
+
name = "João Galego"
|
|
33
|
+
email = "jgalego1990@gmail.com"
|
|
34
|
+
|
|
35
|
+
[project.urls]
|
|
36
|
+
Homepage = "https://github.com/JGalego/shem"
|
|
37
|
+
Documentation = "https://github.com/JGalego/shem/tree/main/docs"
|
|
38
|
+
Repository = "https://github.com/JGalego/shem"
|
|
39
|
+
|
|
40
|
+
[project.scripts]
|
|
41
|
+
shem-neural-server = "shem_neural.server:main"
|
|
42
|
+
shem-neural-generate = "shem_neural.model.generate:main"
|
|
43
|
+
shem-neural-import = "shem_neural.model.importers:main"
|
|
44
|
+
shem-neural-export = "shem_neural.export:main"
|
|
45
|
+
|
|
46
|
+
[dependency-groups]
|
|
47
|
+
dev = [
|
|
48
|
+
"pytest>=8",
|
|
49
|
+
"transformers>=5.22.2",
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
[tool.uv.sources.torch]
|
|
53
|
+
index = "pytorch-cpu"
|
|
54
|
+
|
|
55
|
+
[[tool.uv.index]]
|
|
56
|
+
name = "pytorch-cpu"
|
|
57
|
+
url = "https://download.pytorch.org/whl/cpu"
|
|
58
|
+
explicit = true
|
|
59
|
+
|
|
60
|
+
[tool.pytest.ini_options]
|
|
61
|
+
testpaths = ["tests"]
|
|
62
|
+
|
|
63
|
+
[tool.ruff]
|
|
64
|
+
line-length = 120
|
|
65
|
+
target-version = "py312"
|
|
66
|
+
|
|
67
|
+
[tool.ruff.lint]
|
|
68
|
+
select = [
|
|
69
|
+
"E",
|
|
70
|
+
"F",
|
|
71
|
+
"I",
|
|
72
|
+
"UP",
|
|
73
|
+
"B",
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
[tool.ruff.lint.per-file-ignores]
|
|
77
|
+
"src/shem_neural/export.py" = ["E501"]
|
|
78
|
+
|
|
79
|
+
[build-system]
|
|
80
|
+
requires = ["uv_build>=0.10.9,<0.11.0"]
|
|
81
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "shem-neural"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "shem's neural side: an RV32IM CPU compiled into network weights, and model images"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
authors = [{ name = "João Galego", email = "jgalego1990@gmail.com" }]
|
|
8
|
+
keywords = ["shem", "containers", "riscv", "neural-network", "language-models"]
|
|
9
|
+
classifiers = [
|
|
10
|
+
"Development Status :: 3 - Alpha",
|
|
11
|
+
"License :: OSI Approved :: MIT License",
|
|
12
|
+
"Programming Language :: Python :: 3",
|
|
13
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
14
|
+
"Topic :: System :: Emulators",
|
|
15
|
+
]
|
|
16
|
+
requires-python = ">=3.12"
|
|
17
|
+
dependencies = [
|
|
18
|
+
"huggingface-hub>=2.4.0",
|
|
19
|
+
"jinja2>=3.1.6",
|
|
20
|
+
"numpy>=2",
|
|
21
|
+
"safetensors>=0.4",
|
|
22
|
+
"tokenizers>=0.23.3",
|
|
23
|
+
"torch>=2.5",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
Homepage = "https://github.com/JGalego/shem"
|
|
28
|
+
Documentation = "https://github.com/JGalego/shem/tree/main/docs"
|
|
29
|
+
Repository = "https://github.com/JGalego/shem"
|
|
30
|
+
|
|
31
|
+
[project.scripts]
|
|
32
|
+
shem-neural-server = "shem_neural.server:main"
|
|
33
|
+
shem-neural-generate = "shem_neural.model.generate:main"
|
|
34
|
+
shem-neural-import = "shem_neural.model.importers:main"
|
|
35
|
+
shem-neural-export = "shem_neural.export:main"
|
|
36
|
+
|
|
37
|
+
[dependency-groups]
|
|
38
|
+
dev = [
|
|
39
|
+
"pytest>=8",
|
|
40
|
+
"transformers>=5.22.2",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[tool.uv.sources]
|
|
44
|
+
torch = { index = "pytorch-cpu" }
|
|
45
|
+
|
|
46
|
+
[[tool.uv.index]]
|
|
47
|
+
name = "pytorch-cpu"
|
|
48
|
+
url = "https://download.pytorch.org/whl/cpu"
|
|
49
|
+
explicit = true
|
|
50
|
+
|
|
51
|
+
[build-system]
|
|
52
|
+
requires = ["uv_build>=0.10.9,<0.11.0"]
|
|
53
|
+
build-backend = "uv_build"
|
|
54
|
+
|
|
55
|
+
[tool.pytest.ini_options]
|
|
56
|
+
testpaths = ["tests"]
|
|
57
|
+
|
|
58
|
+
[tool.ruff]
|
|
59
|
+
line-length = 120
|
|
60
|
+
target-version = "py312"
|
|
61
|
+
|
|
62
|
+
[tool.ruff.lint]
|
|
63
|
+
select = ["E", "F", "I", "UP", "B"]
|
|
64
|
+
|
|
65
|
+
[tool.ruff.lint.per-file-ignores]
|
|
66
|
+
# The model card is Markdown, one line per paragraph
|
|
67
|
+
"src/shem_neural/export.py" = ["E501"]
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""A neural vCPU for shem: an RV32IM CPU compiled into network weights.
|
|
2
|
+
|
|
3
|
+
`isa` describes the instruction set as data, `compile` turns that description
|
|
4
|
+
into the network's weight tensors, `cpu` is the network, one instruction per
|
|
5
|
+
forward pass, `vcpu` wraps it in the interface the shem host drives, and
|
|
6
|
+
`server` exposes vCPUs to the host over a Unix socket.
|
|
7
|
+
"""
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""Compile the RV32IM ISA table into lookup and routing weights.
|
|
2
|
+
|
|
3
|
+
Each tensor is derived from `isa.INSTRUCTIONS`; nothing is trained.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import torch
|
|
9
|
+
from safetensors.torch import load_file, save_file
|
|
10
|
+
|
|
11
|
+
from . import isa
|
|
12
|
+
|
|
13
|
+
DTYPE = torch.float64
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _matcher(patterns: list[tuple[int, int]], width: int) -> tuple[torch.Tensor, torch.Tensor]:
|
|
17
|
+
"""Weights and bias of a layer that counts mismatches: for bits `x`,
|
|
18
|
+
`x @ w.T + c` is how many masked bits differ from each pattern's match.
|
|
19
|
+
`relu(1 - that)` is then a one-hot over the patterns."""
|
|
20
|
+
w = torch.zeros(len(patterns), width, dtype=DTYPE)
|
|
21
|
+
c = torch.zeros(len(patterns), dtype=DTYPE)
|
|
22
|
+
for k, (mask, match) in enumerate(patterns):
|
|
23
|
+
for i in range(width):
|
|
24
|
+
if mask >> i & 1:
|
|
25
|
+
bit = match >> i & 1
|
|
26
|
+
w[k, i] = 1 - 2 * bit
|
|
27
|
+
c[k] += bit
|
|
28
|
+
return w, c
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _immediate(fmt: str) -> torch.Tensor:
|
|
32
|
+
"""A (32, 32) selection matrix from instruction bits to immediate bits,
|
|
33
|
+
with sign extension: row j says which instruction bit is immediate bit j."""
|
|
34
|
+
source: dict[int, int] = {}
|
|
35
|
+
if fmt == "I":
|
|
36
|
+
source = {j: 20 + j for j in range(11)}
|
|
37
|
+
sign = 11
|
|
38
|
+
elif fmt == "S":
|
|
39
|
+
source = {j: 7 + j for j in range(5)} | {j: 20 + j for j in range(5, 11)}
|
|
40
|
+
sign = 11
|
|
41
|
+
elif fmt == "B":
|
|
42
|
+
source = {j: 7 + j for j in range(1, 5)} | {j: 20 + j for j in range(5, 11)} | {11: 7}
|
|
43
|
+
sign = 12
|
|
44
|
+
elif fmt == "U":
|
|
45
|
+
source = {j: j for j in range(12, 32)}
|
|
46
|
+
sign = 32
|
|
47
|
+
elif fmt == "J":
|
|
48
|
+
source = {j: 20 + j for j in range(1, 11)} | {11: 20} | {j: j for j in range(12, 20)}
|
|
49
|
+
sign = 20
|
|
50
|
+
source |= {j: 31 for j in range(sign, 32)}
|
|
51
|
+
select = torch.zeros(32, 32, dtype=DTYPE)
|
|
52
|
+
for out, bit in source.items():
|
|
53
|
+
select[out, bit] = 1
|
|
54
|
+
return select
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _route(values: list[str | None], names: tuple[str, ...]) -> torch.Tensor:
|
|
58
|
+
"""One row per instruction, one-hot over `names` (all zeros for None)."""
|
|
59
|
+
route = torch.zeros(len(values), len(names), dtype=DTYPE)
|
|
60
|
+
for k, value in enumerate(values):
|
|
61
|
+
if value is not None:
|
|
62
|
+
route[k, names.index(value)] = 1
|
|
63
|
+
return route
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def compile_weights() -> dict[str, torch.Tensor]:
|
|
67
|
+
"""Build every tensor the network uses."""
|
|
68
|
+
ins = isa.INSTRUCTIONS
|
|
69
|
+
decode_w, decode_c = _matcher([(i.mask, i.match) for i in ins], 32)
|
|
70
|
+
index_w, index_c = _matcher([(0x1F, j) for j in range(32)], 5)
|
|
71
|
+
|
|
72
|
+
sll = torch.zeros(32, 32, 32, dtype=DTYPE) # [shift, out bit, in bit]
|
|
73
|
+
srl = torch.zeros(32, 32, 32, dtype=DTYPE)
|
|
74
|
+
sra_fill = torch.zeros(32, 32, dtype=DTYPE) # [shift, out bit]: copies of the sign bit
|
|
75
|
+
for s in range(32):
|
|
76
|
+
for i in range(32):
|
|
77
|
+
if i - s >= 0:
|
|
78
|
+
sll[s, i, i - s] = 1
|
|
79
|
+
if i + s < 32:
|
|
80
|
+
srl[s, i, i + s] = 1
|
|
81
|
+
else:
|
|
82
|
+
sra_fill[s, i] = 1
|
|
83
|
+
|
|
84
|
+
byte_bits = torch.tensor([[v >> j & 1 for j in range(8)] for v in range(256)], dtype=DTYPE)
|
|
85
|
+
return {
|
|
86
|
+
"decode_w": decode_w,
|
|
87
|
+
"decode_c": decode_c,
|
|
88
|
+
"index_w": index_w,
|
|
89
|
+
"index_c": index_c,
|
|
90
|
+
"imm_bits": torch.stack([_immediate(f) for f in isa.FORMATS]),
|
|
91
|
+
"format_route": _route([i.format for i in ins], isa.FORMATS),
|
|
92
|
+
"uses_imm": torch.tensor([float(i.uses_imm) for i in ins], dtype=DTYPE),
|
|
93
|
+
"result_route": _route([i.result for i in ins], isa.RESULTS),
|
|
94
|
+
"next_pc_route": _route([i.next_pc for i in ins], isa.NEXT_PC),
|
|
95
|
+
"branch_route": _route([i.branch for i in ins], isa.BRANCHES),
|
|
96
|
+
"exit_route": _route([i.exit for i in ins], isa.EXITS),
|
|
97
|
+
"load_size": torch.tensor([float(i.load) for i in ins], dtype=DTYPE),
|
|
98
|
+
"store_size": torch.tensor([float(i.store) for i in ins], dtype=DTYPE),
|
|
99
|
+
"sll": sll,
|
|
100
|
+
"srl": srl,
|
|
101
|
+
"sra_fill": sra_fill,
|
|
102
|
+
"byte_bits": byte_bits,
|
|
103
|
+
"powers": torch.tensor([2.0**i for i in range(32)], dtype=DTYPE),
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def to(weights: dict[str, torch.Tensor], device: torch.device | str) -> dict[str, torch.Tensor]:
|
|
108
|
+
"""The weights on `device`, where the network will run."""
|
|
109
|
+
return {name: tensor.to(device) for name, tensor in weights.items()}
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def save(weights: dict[str, torch.Tensor], path: Path) -> None:
|
|
113
|
+
save_file({name: t.contiguous() for name, t in weights.items()}, str(path))
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def load(path: Path) -> dict[str, torch.Tensor]:
|
|
117
|
+
return load_file(str(path))
|