end000's picture
Release directly programmed Russian draughts network and reproducible WSPForge experiments
6069338 verified
Raw History Blame Contribute Delete
3.41 kB
import hashlib
import json
from pathlib import Path
import numpy as np
from scipy.sparse import csr_matrix
class FixedNetwork:
def __init__(self, layers, metadata):
self.layers = layers
self.metadata = metadata
def forward(self, x, dense=False):
x = np.asarray(x, dtype=np.float32)
if x.ndim != 2 or x.shape[1] != 128:
raise ValueError("Нужен массив [число позиций, 128]")
for i, (w, b) in enumerate(self.layers):
x = x @ w.toarray().T + b if dense else (w @ x.T).T + b
if i + 1 < len(self.layers):
x = np.maximum(x, 0)
return x[:, 0]
def save(self, path):
path = Path(path)
path.mkdir(parents=True, exist_ok=False)
arrays = {}
for i, (w, b) in enumerate(self.layers):
for key, value in (("data", w.data), ("indices", w.indices), ("indptr", w.indptr), ("shape", w.shape), ("bias", b)):
arrays[f"l{i}_{key}"] = np.asarray(value)
np.savez(path / "weights.npz", **arrays)
digest = hashlib.sha256((path / "weights.npz").read_bytes()).hexdigest()
metadata = dict(self.metadata, sha256=digest, layers=len(self.layers), **self.stats())
(path / "model.json").write_text(json.dumps(metadata, ensure_ascii=False, indent=2) + "\n")
@classmethod
def load(cls, path):
path = Path(path)
meta = json.loads((path / "model.json").read_text())
if hashlib.sha256((path / "weights.npz").read_bytes()).hexdigest() != meta["sha256"]:
raise ValueError("Контрольная сумма весов не совпала")
layers = []
with np.load(path / "weights.npz", allow_pickle=False) as z:
for i in range(meta["layers"]):
w = csr_matrix((z[f"l{i}_data"], z[f"l{i}_indices"], z[f"l{i}_indptr"]), shape=tuple(z[f"l{i}_shape"]))
layers.append((w, z[f"l{i}_bias"]))
return cls(layers, meta)
def stats(self):
return {
"architecture": [128] + [w.shape[0] for w, b in self.layers],
"nonzero_weights": sum(w.nnz for w, b in self.layers),
"nonzero_biases": sum(int(np.count_nonzero(b)) for w, b in self.layers),
"dense_parameters_including_zeros": sum(w.shape[0] * w.shape[1] + len(b) for w, b in self.layers),
"neurons_excluding_input": sum(w.shape[0] for w, b in self.layers),
"sparse_array_bytes": sum(w.data.nbytes + w.indices.nbytes + w.indptr.nbytes + b.nbytes for w, b in self.layers),
"float_weight_and_bias_bytes": sum(w.data.nbytes + b.nbytes for w, b in self.layers),
}
class Layer:
def __init__(self, inputs):
self.inputs = inputs
self.rows, self.cols, self.values, self.bias = [], [], [], []
def node(self, terms, bias=0):
row = len(self.bias)
for col, value in terms:
if value:
self.rows.append(row)
self.cols.append(col)
self.values.append(value)
self.bias.append(bias)
return row
def finish(self):
w = csr_matrix((np.asarray(self.values, np.float32), (self.rows, self.cols)), shape=(len(self.bias), self.inputs), dtype=np.float32)
w.sum_duplicates()
w.eliminate_zeros()
return w, np.asarray(self.bias, np.float32)