File size: 4,882 Bytes
5c6c847
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
"""神经网络层。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))