ZeroPoint / zerocg /nn.py
Holmes2011's picture
ZeroPoint v0 research prototype: new from-scratch NumPy framework, memory-slot architecture, audited Transformer comparison
5c6c847 verified
Raw History Blame Contribute Delete
4.88 kB
"""神经网络层。Module 会自动收集属性里的 Parameter / 子 Module。"""
import numpy as np
from . import functional as F
from .tensor import Tensor, get_default_dtype
class Parameter(Tensor):
def __init__(self, data):
super().__init__(np.asarray(data, dtype=get_default_dtype()), requires_grad=True)
class Module:
training = True
def forward(self, *args, **kwargs):
raise NotImplementedError
def __call__(self, *args, **kwargs):
return self.forward(*args, **kwargs)
def named_parameters(self, prefix=""):
for name, v in vars(self).items():
items = [(name, v)] if not isinstance(v, (list, tuple)) else [(f"{name}.{i}", x) for i, x in enumerate(v)]
for n, x in items:
if isinstance(x, Parameter):
yield prefix + n, x
elif isinstance(x, Module):
yield from x.named_parameters(prefix + n + ".")
def parameters(self):
return [p for _, p in self.named_parameters()]
def modules(self):
yield self
for v in vars(self).values():
for x in (v if isinstance(v, (list, tuple)) else [v]):
if isinstance(x, Module):
yield from x.modules()
def train(self, mode=True):
for m in self.modules():
m.training = mode
return self
def eval(self):
return self.train(False)
def zero_grad(self):
for p in self.parameters():
p.grad = None
def num_parameters(self):
return sum(p.data.size for p in self.parameters())
# ---------- 保存 / 加载 ----------
def state_dict(self):
return {n: p.data.copy() for n, p in self.named_parameters()}
def load_state_dict(self, state):
for n, p in self.named_parameters():
assert state[n].shape == p.shape, f"{n} 形状不匹配: {state[n].shape} vs {p.shape}"
p.data = state[n].astype(p.dtype)
def save(self, path):
np.savez(path, **self.state_dict())
def load(self, path):
with np.load(path) as f:
self.load_state_dict(dict(f))
return self
class Linear(Module):
def __init__(self, in_features, out_features, bias=True):
bound = 1 / np.sqrt(in_features)
self.weight = Parameter(np.random.uniform(-bound, bound, (in_features, out_features)))
self.bias = Parameter(np.zeros(out_features)) if bias else None
def forward(self, x):
y = x @ self.weight
return y + self.bias if self.bias is not None else y
class Embedding(Module):
def __init__(self, num_embeddings, dim):
self.weight = Parameter(np.random.randn(num_embeddings, dim) * 0.02)
def forward(self, idx):
return self.weight[idx.data if isinstance(idx, Tensor) else np.asarray(idx)]
class LayerNorm(Module):
def __init__(self, dim, eps=1e-5):
self.eps = eps
self.weight = Parameter(np.ones(dim))
self.bias = Parameter(np.zeros(dim))
def forward(self, x):
mu = x.mean(axis=-1, keepdims=True)
xc = x - mu
var = (xc * xc).mean(axis=-1, keepdims=True)
return xc / (var + self.eps).sqrt() * self.weight + self.bias
class Dropout(Module):
def __init__(self, p=0.1):
self.p = p
def forward(self, x):
return F.dropout(x, self.p, self.training)
class ReLU(Module):
def forward(self, x):
return x.relu()
class Tanh(Module):
def forward(self, x):
return x.tanh()
class GELU(Module):
def forward(self, x):
return F.gelu(x)
class Sequential(Module):
def __init__(self, *layers):
self.layers = list(layers)
def forward(self, x):
for layer in self.layers:
x = layer(x)
return x
def __getitem__(self, i):
return self.layers[i]
class CausalSelfAttention(Module):
"""多头因果自注意力:位置 t 只能看到 <= t 的位置。输入/输出形状 (B, T, C)。"""
def __init__(self, n_embd, n_head, dropout=0.0):
assert n_embd % n_head == 0
self.n_head = n_head
self.qkv = Linear(n_embd, 3 * n_embd)
self.proj = Linear(n_embd, n_embd)
self.attn_drop = Dropout(dropout)
self.resid_drop = Dropout(dropout)
def forward(self, x):
B, T, C = x.shape
H, D = self.n_head, C // self.n_head
qkv = self.qkv(x).reshape(B, T, 3, H, D).transpose(2, 0, 3, 1, 4) # (3, B, H, T, D)
q, k, v = qkv[0], qkv[1], qkv[2]
att = (q @ k.transpose(0, 1, 3, 2)) * (1.0 / np.sqrt(D)) # (B, H, T, T)
att = att.masked_fill(np.triu(np.ones((T, T), dtype=bool), 1), -1e9)
att = self.attn_drop(F.softmax(att, axis=-1))
y = (att @ v).transpose(0, 2, 1, 3).reshape(B, T, C)
return self.resid_drop(self.proj(y))