cogforge-engine 0.2.1__tar.gz → 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.
- cogforge_engine-1.0.0/LICENSE +201 -0
- {cogforge_engine-0.2.1 → cogforge_engine-1.0.0}/PKG-INFO +4 -1
- {cogforge_engine-0.2.1 → cogforge_engine-1.0.0}/cogforge/app.py +208 -91
- cogforge_engine-1.0.0/cogforge/backend.py +27 -0
- cogforge_engine-1.0.0/cogforge/models.py +88 -0
- {cogforge_engine-0.2.1 → cogforge_engine-1.0.0}/pyproject.toml +7 -1
- cogforge_engine-0.2.1/LICENSE +0 -0
- cogforge_engine-0.2.1/cogforge/models.py +0 -44
- {cogforge_engine-0.2.1 → cogforge_engine-1.0.0}/.github/workflows/release.yml +0 -0
- {cogforge_engine-0.2.1 → cogforge_engine-1.0.0}/.gitignore +0 -0
- {cogforge_engine-0.2.1 → cogforge_engine-1.0.0}/README.md +0 -0
- {cogforge_engine-0.2.1 → cogforge_engine-1.0.0}/cogforge/__init__.py +0 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cogforge-engine
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 1.0.0
|
|
4
4
|
Summary: A custom autograd engine and Transformer block built from scratch.
|
|
5
5
|
Project-URL: Homepage, https://github.com/avikmjd2/cogforge
|
|
6
6
|
Author-email: Avik Majumder <avikmjd2@gmail.com>
|
|
7
7
|
License-File: LICENSE
|
|
8
8
|
Requires-Python: >=3.8
|
|
9
|
+
Requires-Dist: numexpr>=2.8.0
|
|
9
10
|
Requires-Dist: numpy>=1.20.0
|
|
11
|
+
Provides-Extra: cuda
|
|
12
|
+
Requires-Dist: cupy>=12.0.0; extra == 'cuda'
|
|
10
13
|
Description-Content-Type: text/markdown
|
|
11
14
|
|
|
12
15
|
# cogforge
|
|
@@ -1,12 +1,55 @@
|
|
|
1
|
-
import numpy
|
|
1
|
+
import numpy
|
|
2
|
+
import backend
|
|
3
|
+
# from backend import np,USE_GPU,NO_GRAD,_NUMEXPR_AVAILABLE,USE_NUMEXPR,_ne,_cp
|
|
4
|
+
|
|
5
|
+
def to_cpu(a):
|
|
6
|
+
"""Return a numpy array regardless of backend."""
|
|
7
|
+
return backend._cp.asnumpy(a) if (backend.USE_GPU and backend._cp is not None) else numpy.asarray(a)
|
|
8
|
+
|
|
9
|
+
def scatter_add(target, indices, values):
|
|
10
|
+
"""target[indices] += values."""
|
|
11
|
+
if backend.USE_GPU:
|
|
12
|
+
import cupyx
|
|
13
|
+
cupyx.scatter_add(target, indices, values)
|
|
14
|
+
else:
|
|
15
|
+
numpy.add.at(target, indices, values)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def set_numexpr(enabled, threads=None):
|
|
19
|
+
"""User switch: set_numexpr(True, threads=8) or set_numexpr(False)."""
|
|
20
|
+
# global USE_NUMEXPR
|
|
21
|
+
if enabled and not backend._NUMEXPR_AVAILABLE:
|
|
22
|
+
raise RuntimeError("numexpr not installed — run: pip install numexpr")
|
|
23
|
+
backend.USE_NUMEXPR = bool(enabled)
|
|
24
|
+
if backend.USE_NUMEXPR and threads:
|
|
25
|
+
backend._ne.set_num_threads(threads)
|
|
26
|
+
return backend.USE_NUMEXPR
|
|
27
|
+
|
|
28
|
+
def _stable_softmax(sd):
|
|
29
|
+
mx = sd.max(axis=-1, keepdims=True) # reduction stays numpy
|
|
30
|
+
if backend.USE_NUMEXPR and not backend.USE_GPU:
|
|
31
|
+
e = backend._ne.evaluate("exp(sd - mx)")
|
|
32
|
+
s = e.sum(axis=-1, keepdims=True)
|
|
33
|
+
return backend._ne.evaluate("e / s")
|
|
34
|
+
e = backend.np.exp(sd - mx)
|
|
35
|
+
return e / e.sum(axis=-1, keepdims=True)
|
|
36
|
+
|
|
37
|
+
def _iadd_scaled(dst, src, coef): # dst += src * coef, in place
|
|
38
|
+
if backend.USE_NUMEXPR and not backend.USE_GPU:
|
|
39
|
+
backend._ne.evaluate("dst + src * coef", out=dst)
|
|
40
|
+
else:
|
|
41
|
+
dst += src * coef
|
|
42
|
+
|
|
43
|
+
def needGradientHence(state:bool):
|
|
44
|
+
backend.NO_GRAD = not state
|
|
2
45
|
|
|
3
46
|
class Tensor:
|
|
4
47
|
def __init__(self, array,children=(),requires_grad = True,typed="compressed"):
|
|
5
|
-
self.data = np.asarray(array, dtype=np.float32 if typed == "compressed" else np.float64)
|
|
48
|
+
self.data = backend.np.asarray(array, dtype=backend.np.float32 if typed == "compressed" else backend.np.float64)
|
|
6
49
|
self.shape = self.data.shape
|
|
7
50
|
self.requires_grad = requires_grad
|
|
8
51
|
# if self.requires_grad:
|
|
9
|
-
self.grad = np.zeros(self.shape,dtype=np.float32 if typed == "compressed" else np.float64)
|
|
52
|
+
self.grad = None if backend.NO_GRAD else backend.np.zeros(self.shape,dtype=backend.np.float32 if typed == "compressed" else backend.np.float64)
|
|
10
53
|
self._backwards = lambda:None
|
|
11
54
|
self._children = set(children)
|
|
12
55
|
self.typed = typed
|
|
@@ -18,12 +61,36 @@ class Tensor:
|
|
|
18
61
|
|
|
19
62
|
def _backward():
|
|
20
63
|
self.grad[key] += out.grad
|
|
64
|
+
# backend.np.add.at(self.grad, key, out.grad)
|
|
65
|
+
|
|
66
|
+
out._backwards = _backward
|
|
67
|
+
|
|
68
|
+
return out
|
|
69
|
+
|
|
70
|
+
@classmethod
|
|
71
|
+
def cat(cls,tensors:tuple,axis=-1):
|
|
72
|
+
"""
|
|
73
|
+
Concatenates a sequence of Tensors along a specified axis.
|
|
74
|
+
"""
|
|
75
|
+
arrays = tuple(t.data for t in tensors)
|
|
76
|
+
out = Tensor(backend.np.concatenate(arrays,axis=axis),children=tensors)
|
|
77
|
+
sizes = [a.shape[axis] for a in arrays[:-1]]
|
|
78
|
+
split_indices = backend.np.cumsum(sizes)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _backward():
|
|
82
|
+
split_grads = backend.np.split(out.grad, split_indices, axis=axis)
|
|
83
|
+
|
|
84
|
+
for t,g in zip(tensors,split_grads):
|
|
85
|
+
if t.requires_grad:
|
|
86
|
+
t.grad += g
|
|
21
87
|
|
|
22
88
|
out._backwards = _backward
|
|
23
89
|
|
|
24
90
|
return out
|
|
25
91
|
|
|
26
92
|
|
|
93
|
+
|
|
27
94
|
def __add__(self, other):
|
|
28
95
|
other = other if isinstance(other, Tensor) else Tensor(other)
|
|
29
96
|
out_data = self.data + other.data
|
|
@@ -32,13 +99,13 @@ class Tensor:
|
|
|
32
99
|
def _backward():
|
|
33
100
|
if self.shape != out_data.shape:
|
|
34
101
|
axis = tuple(range(out.grad.ndim - self.data.ndim))
|
|
35
|
-
self.grad += np.sum(out.grad, axis=axis).reshape(self.shape)
|
|
102
|
+
self.grad += backend.np.sum(out.grad, axis=axis).reshape(self.shape)
|
|
36
103
|
else:
|
|
37
104
|
self.grad += out.grad
|
|
38
105
|
|
|
39
106
|
if other.shape != out_data.shape:
|
|
40
107
|
axis = tuple(range(out.grad.ndim - other.data.ndim))
|
|
41
|
-
other.grad += np.sum(out.grad, axis=axis).reshape(other.shape)
|
|
108
|
+
other.grad += backend.np.sum(out.grad, axis=axis).reshape(other.shape)
|
|
42
109
|
else:
|
|
43
110
|
other.grad += out.grad
|
|
44
111
|
|
|
@@ -68,14 +135,14 @@ class Tensor:
|
|
|
68
135
|
grad_self = other.data * out.grad
|
|
69
136
|
if self.shape != out_data.shape:
|
|
70
137
|
axis = tuple(range(out.grad.ndim - self.data.ndim))
|
|
71
|
-
grad_self = np.sum(grad_self, axis=axis).reshape(self.shape)
|
|
138
|
+
grad_self = backend.np.sum(grad_self, axis=axis).reshape(self.shape)
|
|
72
139
|
self.grad += grad_self
|
|
73
140
|
|
|
74
141
|
# gradient w.r.t. other
|
|
75
142
|
grad_other = self.data * out.grad
|
|
76
143
|
if other.shape != out_data.shape:
|
|
77
144
|
axis = tuple(range(out.grad.ndim - other.data.ndim))
|
|
78
|
-
grad_other = np.sum(grad_other, axis=axis).reshape(other.shape)
|
|
145
|
+
grad_other = backend.np.sum(grad_other, axis=axis).reshape(other.shape)
|
|
79
146
|
other.grad += grad_other
|
|
80
147
|
|
|
81
148
|
out._backwards = _backward
|
|
@@ -90,12 +157,12 @@ class Tensor:
|
|
|
90
157
|
# out = Tensor(out_data, children=(self,other))
|
|
91
158
|
|
|
92
159
|
# # def _backward():
|
|
93
|
-
# # self.grad += out.grad @ np.transpose(other.data)
|
|
94
|
-
# # other.grad += np.transpose(self.data) @ out.grad
|
|
160
|
+
# # self.grad += out.grad @ backend.np.transpose(other.data)
|
|
161
|
+
# # other.grad += backend.np.transpose(self.data) @ out.grad
|
|
95
162
|
|
|
96
163
|
# def _backward():
|
|
97
|
-
# self.grad += out.grad @ np.swapaxes(other.data, -1, -2)
|
|
98
|
-
# other.grad += np.swapaxes(self.data, -1, -2) @ out.grad
|
|
164
|
+
# self.grad += out.grad @ backend.np.swapaxes(other.data, -1, -2)
|
|
165
|
+
# other.grad += backend.np.swapaxes(self.data, -1, -2) @ out.grad
|
|
99
166
|
|
|
100
167
|
# out._backwards = _backward
|
|
101
168
|
# return out
|
|
@@ -112,15 +179,15 @@ class Tensor:
|
|
|
112
179
|
def __matmul__(self, other):
|
|
113
180
|
out = Tensor(self.data @ other.data, children=(self, other))
|
|
114
181
|
def _backward():
|
|
115
|
-
gs = out.grad @ np.swapaxes(other.data, -1, -2)
|
|
116
|
-
go = np.swapaxes(self.data, -1, -2) @ out.grad
|
|
182
|
+
gs = out.grad @ backend.np.swapaxes(other.data, -1, -2)
|
|
183
|
+
go = backend.np.swapaxes(self.data, -1, -2) @ out.grad
|
|
117
184
|
self.grad += Tensor.unbroadcast(gs, self.data.shape)
|
|
118
185
|
other.grad += Tensor.unbroadcast(go, other.data.shape)
|
|
119
186
|
out._backwards = _backward
|
|
120
187
|
return out
|
|
121
188
|
|
|
122
189
|
def relu(self):
|
|
123
|
-
out_data = np.maximum(0,self.data)
|
|
190
|
+
out_data = backend.np.maximum(0,self.data)
|
|
124
191
|
|
|
125
192
|
out = Tensor(out_data, children=(self,))
|
|
126
193
|
|
|
@@ -136,7 +203,7 @@ class Tensor:
|
|
|
136
203
|
curr_shape = self.data.shape
|
|
137
204
|
|
|
138
205
|
if not self.data.flags['C_CONTIGUOUS']:
|
|
139
|
-
arr = np.ascontiguousarray(self.data)
|
|
206
|
+
arr = backend.np.ascontiguousarray(self.data)
|
|
140
207
|
else:
|
|
141
208
|
arr = self.data
|
|
142
209
|
|
|
@@ -154,7 +221,7 @@ class Tensor:
|
|
|
154
221
|
|
|
155
222
|
|
|
156
223
|
def sigmoid(self):
|
|
157
|
-
out_data = 1 / (1 + np.exp(-self.data))
|
|
224
|
+
out_data = 1 / (1 + backend.np.exp(-self.data))
|
|
158
225
|
out = Tensor(out_data, children=(self,))
|
|
159
226
|
def _backward():
|
|
160
227
|
local_derivative = out_data * (1 - out_data)
|
|
@@ -165,7 +232,7 @@ class Tensor:
|
|
|
165
232
|
return out
|
|
166
233
|
|
|
167
234
|
def tanh(self):
|
|
168
|
-
out_data = np.tanh(self.data)
|
|
235
|
+
out_data = backend.np.tanh(self.data)
|
|
169
236
|
out = Tensor(out_data,children=(self,))
|
|
170
237
|
def _backward():
|
|
171
238
|
dervitive = 1-(out_data*out_data)
|
|
@@ -175,15 +242,15 @@ class Tensor:
|
|
|
175
242
|
return out
|
|
176
243
|
|
|
177
244
|
def softmax(self,axis=-1):
|
|
178
|
-
maxVal = np.max(self.data,axis=axis,keepdims=True)
|
|
179
|
-
expVal = np.exp(self.data-maxVal)
|
|
245
|
+
maxVal = backend.np.max(self.data,axis=axis,keepdims=True)
|
|
246
|
+
expVal = backend.np.exp(self.data-maxVal)
|
|
180
247
|
|
|
181
|
-
out_data = expVal/np.sum(expVal,axis=axis,keepdims=True)
|
|
248
|
+
out_data = expVal/backend.np.sum(expVal,axis=axis,keepdims=True)
|
|
182
249
|
out=Tensor(out_data,(self,))
|
|
183
250
|
|
|
184
251
|
def _backward():
|
|
185
252
|
g = out.grad
|
|
186
|
-
self.grad += out_data * (g - np.sum(g * out_data, axis=axis, keepdims=True))
|
|
253
|
+
self.grad += out_data * (g - backend.np.sum(g * out_data, axis=axis, keepdims=True))
|
|
187
254
|
|
|
188
255
|
out._backwards = _backward
|
|
189
256
|
return out
|
|
@@ -192,10 +259,10 @@ class Tensor:
|
|
|
192
259
|
@classmethod
|
|
193
260
|
def cross_entropy_loss(cls,predictions, targets):
|
|
194
261
|
|
|
195
|
-
probabilities = np.clip(predictions.data, 1e-15, 1 - 1e-15)
|
|
262
|
+
probabilities = backend.np.clip(predictions.data, 1e-15, 1 - 1e-15)
|
|
196
263
|
batch_size = predictions.shape[0]
|
|
197
264
|
|
|
198
|
-
loss_data = - np.sum(targets * np.log(probabilities)) / batch_size
|
|
265
|
+
loss_data = - backend.np.sum(targets * backend.np.log(probabilities)) / batch_size
|
|
199
266
|
loss = Tensor(loss_data, children=(predictions,))
|
|
200
267
|
|
|
201
268
|
def _backward():
|
|
@@ -222,7 +289,7 @@ class Tensor:
|
|
|
222
289
|
|
|
223
290
|
topoSort(self)
|
|
224
291
|
|
|
225
|
-
self.grad = np.ones(self.shape,dtype=np.float32 if self.typed == "compressed" else np.float64)
|
|
292
|
+
self.grad = backend.np.ones(self.shape,dtype=backend.np.float32 if self.typed == "compressed" else backend.np.float64)
|
|
226
293
|
|
|
227
294
|
for node in reversed(topo):
|
|
228
295
|
node._backwards()
|
|
@@ -245,7 +312,7 @@ class Tensor:
|
|
|
245
312
|
for child in node._children:
|
|
246
313
|
stack.append((child,False))
|
|
247
314
|
|
|
248
|
-
self.grad = np.ones(self.shape,dtype=np.float32 if self.typed == "compressed" else np.float64)
|
|
315
|
+
self.grad = backend.np.ones(self.shape,dtype=backend.np.float32 if self.typed == "compressed" else backend.np.float64)
|
|
249
316
|
|
|
250
317
|
for node in reversed(topo):
|
|
251
318
|
node._backwards()
|
|
@@ -292,13 +359,13 @@ class Tensor:
|
|
|
292
359
|
targets: (B, V) one-hot
|
|
293
360
|
mask: (B,) 1.0 for real positions, 0.0 for <PAD>
|
|
294
361
|
"""
|
|
295
|
-
probs = np.clip(predictions.data, 1e-15, 1 - 1e-15)
|
|
362
|
+
probs = backend.np.clip(predictions.data, 1e-15, 1 - 1e-15)
|
|
296
363
|
mask = mask.reshape(-1, 1) # (B, 1)
|
|
297
364
|
n_real = mask.sum()
|
|
298
365
|
n_real = n_real if n_real > 0 else 1.0
|
|
299
366
|
|
|
300
367
|
# only real rows contribute to the loss value
|
|
301
|
-
loss_data = -np.sum(mask * targets * np.log(probs)) / n_real
|
|
368
|
+
loss_data = -backend.np.sum(mask * targets * backend.np.log(probs)) / n_real
|
|
302
369
|
loss = cls(loss_data, children=(predictions,))
|
|
303
370
|
|
|
304
371
|
def _backward():
|
|
@@ -311,31 +378,45 @@ class Tensor:
|
|
|
311
378
|
return loss
|
|
312
379
|
|
|
313
380
|
@classmethod
|
|
314
|
-
def
|
|
381
|
+
def sparse_softmax_cross_entropy_legacy(cls, scores, target_ids):
|
|
315
382
|
""" scores: (B, T, V) logits ; target_ids: (B, T) int """
|
|
316
383
|
z = scores.data - scores.data.max(axis=-1, keepdims=True)
|
|
317
|
-
e = np.exp(z)
|
|
384
|
+
e = backend.np.exp(z)
|
|
318
385
|
p = e / e.sum(axis=-1, keepdims=True)
|
|
319
|
-
N = int(np.prod(scores.shape[:-1]))
|
|
320
|
-
flat, idx, rows = p.reshape(N, -1), target_ids.reshape(N), np.arange(N)
|
|
321
|
-
loss = cls(-np.log(np.clip(flat[rows, idx], 1e-15, 1.0)).sum() / N, children=(scores,))
|
|
386
|
+
N = int(backend.np.prod(scores.shape[:-1]))
|
|
387
|
+
flat, idx, rows = p.reshape(N, -1), target_ids.reshape(N), backend.np.arange(N)
|
|
388
|
+
loss = cls(-backend.np.log(backend.np.clip(flat[rows, idx], 1e-15, 1.0)).sum() / N, children=(scores,))
|
|
322
389
|
def _backward():
|
|
323
|
-
g = p.reshape(N, -1).copy()
|
|
390
|
+
# g = p.reshape(N, -1).copy()
|
|
391
|
+
g = p.reshape(N, -1)
|
|
324
392
|
g[rows, idx] -= 1.0 # (softmax - onehot)
|
|
325
393
|
scores.grad += (g.reshape(scores.shape) / N) * loss.grad
|
|
326
394
|
loss._backwards = _backward
|
|
327
395
|
return loss
|
|
328
396
|
|
|
397
|
+
@classmethod
|
|
398
|
+
def sparse_softmax_cross_entropy(cls, scores, target_ids):
|
|
399
|
+
p = _stable_softmax(scores.data)
|
|
400
|
+
N = int(backend.np.prod(scores.shape[:-1]))
|
|
401
|
+
flat, idx, rows = p.reshape(N, -1), target_ids.reshape(N), backend.np.arange(N)
|
|
402
|
+
loss = cls(-backend.np.log(backend.np.clip(flat[rows, idx], 1e-15, 1.0)).sum() / N, children=(scores,))
|
|
403
|
+
def _backward():
|
|
404
|
+
g = p.reshape(N, -1)
|
|
405
|
+
g[rows, idx] -= 1.0
|
|
406
|
+
_iadd_scaled(scores.grad, g.reshape(scores.shape), backend.np.float32(loss.grad / N))
|
|
407
|
+
loss._backwards = _backward
|
|
408
|
+
return loss
|
|
409
|
+
|
|
329
410
|
#Legacy
|
|
330
411
|
@classmethod
|
|
331
412
|
def softmax_cross_entropy_old(cls, scores, targets):
|
|
332
413
|
z = scores.data
|
|
333
414
|
z = z - z.max(axis=1, keepdims=True) # stability
|
|
334
|
-
e = np.exp(z)
|
|
415
|
+
e = backend.np.exp(z)
|
|
335
416
|
p = e / e.sum(axis=1, keepdims=True) # softmax, computed privately
|
|
336
417
|
B = scores.shape[0]
|
|
337
418
|
|
|
338
|
-
loss = cls(-np.sum(targets * np.log(np.clip(p, 1e-15, 1.0))) / B, children=(scores,))
|
|
419
|
+
loss = cls(-backend.np.sum(targets * backend.np.log(backend.np.clip(p, 1e-15, 1.0))) / B, children=(scores,))
|
|
339
420
|
|
|
340
421
|
def _backward():
|
|
341
422
|
scores.grad += (p - targets) / B * loss.grad
|
|
@@ -346,10 +427,10 @@ class Tensor:
|
|
|
346
427
|
def softmax_cross_entropy(cls, scores, targets):
|
|
347
428
|
z = scores.data
|
|
348
429
|
z = z - z.max(axis=-1, keepdims=True)
|
|
349
|
-
e = np.exp(z)
|
|
430
|
+
e = backend.np.exp(z)
|
|
350
431
|
p = e / e.sum(axis=-1, keepdims=True)
|
|
351
|
-
N = np.prod(scores.shape[:-1]) # B for 2D, B*T for 3D
|
|
352
|
-
loss = cls(-np.sum(targets * np.log(np.clip(p, 1e-15, 1.0))) / N, children=(scores,))
|
|
432
|
+
N = backend.np.prod(scores.shape[:-1]) # B for 2D, B*T for 3D
|
|
433
|
+
loss = cls(-backend.np.sum(targets * backend.np.log(backend.np.clip(p, 1e-15, 1.0))) / N, children=(scores,))
|
|
353
434
|
def _backward():
|
|
354
435
|
scores.grad += (p - targets) / N * loss.grad
|
|
355
436
|
loss._backwards = _backward
|
|
@@ -360,14 +441,14 @@ class Tensor:
|
|
|
360
441
|
def softmax_cross_entropy_masked(cls, scores, targets, mask):
|
|
361
442
|
z = scores.data
|
|
362
443
|
z = z - z.max(axis=1, keepdims=True)
|
|
363
|
-
e = np.exp(z)
|
|
444
|
+
e = backend.np.exp(z)
|
|
364
445
|
p = e / e.sum(axis=1, keepdims=True)
|
|
365
446
|
|
|
366
447
|
mask = mask.reshape(-1, 1)
|
|
367
448
|
n_real = mask.sum()
|
|
368
449
|
n_real = n_real if n_real > 0 else 1.0
|
|
369
450
|
|
|
370
|
-
loss = cls(-np.sum(mask * targets * np.log(np.clip(p, 1e-15, 1.0))) / n_real, children=(scores,))
|
|
451
|
+
loss = cls(-backend.np.sum(mask * targets * backend.np.log(backend.np.clip(p, 1e-15, 1.0))) / n_real, children=(scores,))
|
|
371
452
|
|
|
372
453
|
def _backward():
|
|
373
454
|
grad = (p - targets) / n_real
|
|
@@ -377,19 +458,19 @@ class Tensor:
|
|
|
377
458
|
return loss
|
|
378
459
|
|
|
379
460
|
def transpose(self,axes):
|
|
380
|
-
out = Tensor(np.transpose(self.data,axes=axes),children=(self,))
|
|
381
|
-
inv = np.argsort(axes)
|
|
461
|
+
out = Tensor(backend.np.transpose(self.data,axes=axes),children=(self,))
|
|
462
|
+
inv = backend.np.argsort(axes)
|
|
382
463
|
def _backward():
|
|
383
|
-
self.grad+=np.transpose(out.grad,inv)
|
|
464
|
+
self.grad+=backend.np.transpose(out.grad,inv)
|
|
384
465
|
|
|
385
466
|
out._backwards = _backward
|
|
386
467
|
return out
|
|
387
468
|
|
|
388
469
|
def masked_fill(self, mask, value):
|
|
389
|
-
out_data = np.where(mask, value, self.data)
|
|
470
|
+
out_data = backend.np.where(mask, value, self.data)
|
|
390
471
|
out = Tensor(out_data, children=(self,))
|
|
391
472
|
def _backward():
|
|
392
|
-
self.grad += np.where(mask, 0.0, 1.0) * out.grad
|
|
473
|
+
self.grad += backend.np.where(mask, 0.0, 1.0) * out.grad
|
|
393
474
|
out._backwards = _backward
|
|
394
475
|
return out
|
|
395
476
|
|
|
@@ -398,10 +479,10 @@ class Tensor:
|
|
|
398
479
|
|
|
399
480
|
class Linear:
|
|
400
481
|
def __init__(self, nin, nout):
|
|
401
|
-
self.W = Tensor(np.random.randn(nin, nout) * np.sqrt(2.0/nin))
|
|
402
|
-
self.v_W = np.zeros(self.W.shape)
|
|
403
|
-
self.b = Tensor(np.zeros((nout,)))
|
|
404
|
-
self.v_b = np.zeros(self.b.shape)
|
|
482
|
+
self.W = Tensor(backend.np.random.randn(nin, nout) * backend.np.sqrt(2.0/nin))
|
|
483
|
+
self.v_W = backend.np.zeros(self.W.shape)
|
|
484
|
+
self.b = Tensor(backend.np.zeros((nout,)))
|
|
485
|
+
self.v_b = backend.np.zeros(self.b.shape)
|
|
405
486
|
|
|
406
487
|
def __call__ (self,x):
|
|
407
488
|
if(x.shape[-1]!=self.W.shape[0]):
|
|
@@ -436,10 +517,10 @@ class MLP:
|
|
|
436
517
|
weights_dict[f'W_{i}'] = layer.W.data
|
|
437
518
|
weights_dict[f'b_{i}'] = layer.b.data
|
|
438
519
|
|
|
439
|
-
np.savez_compressed(filename, **weights_dict)
|
|
520
|
+
backend.np.savez_compressed(filename, **weights_dict)
|
|
440
521
|
|
|
441
522
|
def load(self, filename="best_model.npz"):
|
|
442
|
-
with np.load(filename) as data:
|
|
523
|
+
with backend.np.load(filename) as data:
|
|
443
524
|
for i, layer in enumerate(self.layers):
|
|
444
525
|
layer.W.data = data[f'W_{i}']
|
|
445
526
|
layer.b.data = data[f'b_{i}']
|
|
@@ -448,15 +529,17 @@ class MLP:
|
|
|
448
529
|
|
|
449
530
|
class Embedding:
|
|
450
531
|
def __init__(self, vocab_size, embedding_dim):
|
|
451
|
-
self.weights = Tensor(np.random.randn(vocab_size, embedding_dim))
|
|
532
|
+
self.weights = Tensor(backend.np.random.randn(vocab_size, embedding_dim))
|
|
452
533
|
|
|
453
534
|
def __call__(self, input_indices):
|
|
454
|
-
|
|
535
|
+
idx = backend.np.asarray(input_indices)
|
|
536
|
+
out_data = self.weights.data[idx]
|
|
455
537
|
|
|
456
538
|
out = Tensor(out_data, children=(self.weights,))
|
|
457
539
|
|
|
458
540
|
def _backward():
|
|
459
|
-
np.add.at(self.weights.grad,input_indices,out.grad)
|
|
541
|
+
# backend.np.add.at(self.weights.grad,input_indices,out.grad)
|
|
542
|
+
scatter_add(self.weights.grad, idx, out.grad)
|
|
460
543
|
|
|
461
544
|
out._backwards = _backward
|
|
462
545
|
return out
|
|
@@ -500,7 +583,7 @@ class RNN:
|
|
|
500
583
|
h = prev_hidden
|
|
501
584
|
else:
|
|
502
585
|
B = xs[0].shape[0]
|
|
503
|
-
h = Tensor(np.zeros((B, self.hidden_dim)))
|
|
586
|
+
h = Tensor(backend.np.zeros((B, self.hidden_dim)))
|
|
504
587
|
|
|
505
588
|
|
|
506
589
|
hidden_states = []
|
|
@@ -556,7 +639,7 @@ class SGD:
|
|
|
556
639
|
self.lr = learning_rate
|
|
557
640
|
|
|
558
641
|
def clip_grads(self, max_norm=5.0):
|
|
559
|
-
total = np.sqrt(sum(np.sum(p.grad ** 2) for p in self.parameters))
|
|
642
|
+
total = backend.np.sqrt(sum(backend.np.sum(p.grad ** 2) for p in self.parameters))
|
|
560
643
|
if total > max_norm:
|
|
561
644
|
scale = max_norm / (total + 1e-6)
|
|
562
645
|
for p in self.parameters:
|
|
@@ -568,7 +651,7 @@ class SGD:
|
|
|
568
651
|
|
|
569
652
|
def zero_grad(self):
|
|
570
653
|
for p in self.parameters:
|
|
571
|
-
p.grad = np.zeros_like(p.grad)
|
|
654
|
+
p.grad = backend.np.zeros_like(p.grad)
|
|
572
655
|
|
|
573
656
|
class Sequential:
|
|
574
657
|
def __init__(self,layers):
|
|
@@ -606,11 +689,11 @@ class BatchNorm1D:
|
|
|
606
689
|
self.momentum = momentum
|
|
607
690
|
self.training = True
|
|
608
691
|
|
|
609
|
-
self.gamma = Tensor(np.ones(dim))
|
|
610
|
-
self.beta = Tensor(np.zeros(dim))
|
|
692
|
+
self.gamma = Tensor(backend.np.ones(dim))
|
|
693
|
+
self.beta = Tensor(backend.np.zeros(dim))
|
|
611
694
|
|
|
612
|
-
self.running_mean = np.zeros(dim)
|
|
613
|
-
self.running_var = np.ones(dim)
|
|
695
|
+
self.running_mean = backend.np.zeros(dim)
|
|
696
|
+
self.running_var = backend.np.ones(dim)
|
|
614
697
|
|
|
615
698
|
|
|
616
699
|
def __call__(self,x:Tensor):
|
|
@@ -621,17 +704,17 @@ class BatchNorm1D:
|
|
|
621
704
|
var = x.data.var(axis=reduce_dims, keepdims=True)
|
|
622
705
|
|
|
623
706
|
self.x_centered = x.data - mean
|
|
624
|
-
self.std_inv = 1.0 / np.sqrt(var + self.eps)
|
|
707
|
+
self.std_inv = 1.0 / backend.np.sqrt(var + self.eps)
|
|
625
708
|
self.x_hat = self.x_centered * self.std_inv
|
|
626
709
|
|
|
627
|
-
N = np.prod([x.data.shape[d] for d in reduce_dims])
|
|
710
|
+
N = backend.np.prod([x.data.shape[d] for d in reduce_dims])
|
|
628
711
|
unbiased_var = var.squeeze() * (N / (N - 1)) if N > 1 else var.squeeze()
|
|
629
712
|
|
|
630
713
|
self.running_mean = (1 - self.momentum) * self.running_mean + self.momentum * mean.squeeze()
|
|
631
714
|
self.running_var = (1 - self.momentum) * self.running_var + self.momentum * unbiased_var
|
|
632
715
|
|
|
633
716
|
else:
|
|
634
|
-
self.x_hat = (x.data-self.running_mean)/np.sqrt(self.running_var + self.eps)
|
|
717
|
+
self.x_hat = (x.data-self.running_mean)/backend.np.sqrt(self.running_var + self.eps)
|
|
635
718
|
|
|
636
719
|
out_data = self.gamma.data * self.x_hat + self.beta.data
|
|
637
720
|
|
|
@@ -642,16 +725,16 @@ class BatchNorm1D:
|
|
|
642
725
|
return
|
|
643
726
|
|
|
644
727
|
dout = out.grad
|
|
645
|
-
self.gamma.grad += np.sum(dout * self.x_hat, axis=reduce_dims)
|
|
646
|
-
self.beta.grad += np.sum(dout, axis=reduce_dims)
|
|
728
|
+
self.gamma.grad += backend.np.sum(dout * self.x_hat, axis=reduce_dims)
|
|
729
|
+
self.beta.grad += backend.np.sum(dout, axis=reduce_dims)
|
|
647
730
|
|
|
648
|
-
N = np.prod([x.data.shape[d] for d in reduce_dims])
|
|
731
|
+
N = backend.np.prod([x.data.shape[d] for d in reduce_dims])
|
|
649
732
|
|
|
650
733
|
dx_hat = dout * self.gamma.data
|
|
651
734
|
dx = (1.0 / N) * self.std_inv * (
|
|
652
735
|
N * dx_hat
|
|
653
|
-
- np.sum(dx_hat, axis=reduce_dims, keepdims=True)
|
|
654
|
-
- self.x_hat * np.sum(dx_hat * self.x_hat, axis=reduce_dims, keepdims=True)
|
|
736
|
+
- backend.np.sum(dx_hat, axis=reduce_dims, keepdims=True)
|
|
737
|
+
- self.x_hat * backend.np.sum(dx_hat * self.x_hat, axis=reduce_dims, keepdims=True)
|
|
655
738
|
)
|
|
656
739
|
|
|
657
740
|
x.grad += dx
|
|
@@ -724,8 +807,8 @@ class Adam:
|
|
|
724
807
|
def __init__(self, parameters, lr=1e-3, beta1=0.9, beta2=0.999, eps=1e-8):
|
|
725
808
|
self.parameters = parameters
|
|
726
809
|
self.lr = lr; self.beta1 = beta1; self.beta2 = beta2; self.eps = eps
|
|
727
|
-
self.m = [np.zeros_like(p.data) for p in parameters]
|
|
728
|
-
self.v = [np.zeros_like(p.data) for p in parameters]
|
|
810
|
+
self.m = [backend.np.zeros_like(p.data) for p in parameters]
|
|
811
|
+
self.v = [backend.np.zeros_like(p.data) for p in parameters]
|
|
729
812
|
self.t = 0
|
|
730
813
|
|
|
731
814
|
def step(self):
|
|
@@ -735,14 +818,14 @@ class Adam:
|
|
|
735
818
|
self.v[i] = self.beta2*self.v[i] + (1-self.beta2)*(p.grad**2)
|
|
736
819
|
m_hat = self.m[i] / (1 - self.beta1**self.t)
|
|
737
820
|
v_hat = self.v[i] / (1 - self.beta2**self.t)
|
|
738
|
-
p.data -= self.lr * m_hat / (np.sqrt(v_hat) + self.eps)
|
|
821
|
+
p.data -= self.lr * m_hat / (backend.np.sqrt(v_hat) + self.eps)
|
|
739
822
|
|
|
740
823
|
def zero_grad(self):
|
|
741
824
|
for p in self.parameters:
|
|
742
|
-
p.grad = np.zeros_like(p.grad)
|
|
825
|
+
p.grad = backend.np.zeros_like(p.grad)
|
|
743
826
|
|
|
744
827
|
def clip_grads(self, max_norm=5.0):
|
|
745
|
-
total = np.sqrt(sum(np.sum(p.grad**2) for p in self.parameters))
|
|
828
|
+
total = backend.np.sqrt(sum(backend.np.sum(p.grad**2) for p in self.parameters))
|
|
746
829
|
if total > max_norm:
|
|
747
830
|
scale = max_norm / (total + 1e-6)
|
|
748
831
|
for p in self.parameters:
|
|
@@ -751,7 +834,7 @@ class Adam:
|
|
|
751
834
|
|
|
752
835
|
class Attention:
|
|
753
836
|
def __init__(self,dk):
|
|
754
|
-
self.scale = 1.0/np.sqrt(dk)
|
|
837
|
+
self.scale = 1.0/backend.np.sqrt(dk)
|
|
755
838
|
|
|
756
839
|
def __call__(self, Q:Tensor,K:Tensor,V:Tensor,mask=None):
|
|
757
840
|
axes = list(range(Q.data.ndim)); axes[-1], axes[-2] = axes[-2], axes[-1]
|
|
@@ -765,12 +848,13 @@ class Attention:
|
|
|
765
848
|
return intermediate @ V
|
|
766
849
|
|
|
767
850
|
class MultiHeadAttention:
|
|
768
|
-
def __init__(self,dinp,dmodel, dout,n):
|
|
851
|
+
def __init__(self,dinp,dmodel, dout,n,rope=None):
|
|
769
852
|
assert dmodel%n==0
|
|
770
853
|
self.h = n
|
|
771
854
|
self.dk = dmodel//n
|
|
772
855
|
self.q = Linear(dinp,dmodel) #dmodel = dk*h, we will slice it
|
|
773
856
|
self.k = Linear(dinp,dmodel)
|
|
857
|
+
self.rope = rope
|
|
774
858
|
self.v = Linear(dinp,dmodel)
|
|
775
859
|
self.o = Linear(dmodel,dout)
|
|
776
860
|
self.attention = Attention(self.dk)
|
|
@@ -787,6 +871,9 @@ class MultiHeadAttention:
|
|
|
787
871
|
Q = self.split(self.q(query))
|
|
788
872
|
K = self.split(self.k(key))
|
|
789
873
|
V = self.split(self.v(value))
|
|
874
|
+
if self.rope is not None:
|
|
875
|
+
Q = self.rope(Q)
|
|
876
|
+
K = self.rope(K)
|
|
790
877
|
out = self.attention(Q=Q,K=K,V=V,mask=mask)
|
|
791
878
|
return self.o(self.merge(out))
|
|
792
879
|
|
|
@@ -798,13 +885,13 @@ class MultiHeadAttention:
|
|
|
798
885
|
class LayerNorm:
|
|
799
886
|
def __init__(self, dim, eps=1e-5):
|
|
800
887
|
self.eps = eps
|
|
801
|
-
self.gamma = Tensor(np.ones(dim))
|
|
802
|
-
self.beta = Tensor(np.zeros(dim))
|
|
888
|
+
self.gamma = Tensor(backend.np.ones(dim))
|
|
889
|
+
self.beta = Tensor(backend.np.zeros(dim))
|
|
803
890
|
|
|
804
891
|
def __call__(self, x):
|
|
805
892
|
mu = x.data.mean(axis=-1, keepdims=True)
|
|
806
893
|
var = x.data.var(axis=-1, keepdims=True)
|
|
807
|
-
std_inv = 1.0 / np.sqrt(var + self.eps)
|
|
894
|
+
std_inv = 1.0 / backend.np.sqrt(var + self.eps)
|
|
808
895
|
x_hat = (x.data - mu) * std_inv
|
|
809
896
|
out_data = self.gamma.data * x_hat + self.beta.data
|
|
810
897
|
out = Tensor(out_data, children=(x, self.gamma, self.beta))
|
|
@@ -813,13 +900,13 @@ class LayerNorm:
|
|
|
813
900
|
def _backward():
|
|
814
901
|
dout = out.grad
|
|
815
902
|
axes = tuple(range(dout.ndim - 1))
|
|
816
|
-
self.gamma.grad += np.sum(dout * x_hat, axis=axes)
|
|
817
|
-
self.beta.grad += np.sum(dout, axis=axes)
|
|
903
|
+
self.gamma.grad += backend.np.sum(dout * x_hat, axis=axes)
|
|
904
|
+
self.beta.grad += backend.np.sum(dout, axis=axes)
|
|
818
905
|
dxhat = dout * self.gamma.data
|
|
819
906
|
dx = std_inv / D * (
|
|
820
907
|
D * dxhat
|
|
821
|
-
- np.sum(dxhat, axis=-1, keepdims=True)
|
|
822
|
-
- x_hat * np.sum(dxhat * x_hat, axis=-1, keepdims=True)
|
|
908
|
+
- backend.np.sum(dxhat, axis=-1, keepdims=True)
|
|
909
|
+
- x_hat * backend.np.sum(dxhat * x_hat, axis=-1, keepdims=True)
|
|
823
910
|
)
|
|
824
911
|
x.grad += dx
|
|
825
912
|
out._backwards = _backward
|
|
@@ -842,9 +929,9 @@ class FeedForward:
|
|
|
842
929
|
class Transformer:
|
|
843
930
|
"""The transformer block has no bridge. So demb = dmodel"""
|
|
844
931
|
|
|
845
|
-
def __init__(self, dmodel, n, dff = None):
|
|
932
|
+
def __init__(self, dmodel, n, dff = None,rope=None):
|
|
846
933
|
self.ln1 = LayerNorm(dmodel)
|
|
847
|
-
self.attn = MultiHeadAttention(dinp=dmodel,dmodel=dmodel,dout=dmodel,n=n)
|
|
934
|
+
self.attn = MultiHeadAttention(dinp=dmodel,dmodel=dmodel,dout=dmodel,n=n,rope=rope)
|
|
848
935
|
self.ln2 = LayerNorm(dmodel)
|
|
849
936
|
self.ff = FeedForward(dmodel=dmodel,dff=dff)
|
|
850
937
|
|
|
@@ -863,11 +950,11 @@ class Transformer:
|
|
|
863
950
|
|
|
864
951
|
class PositionalEncoding:
|
|
865
952
|
def __init__(self,max_len,dmodel):
|
|
866
|
-
pe = np.zeros((max_len,dmodel))
|
|
867
|
-
pos = np.arange(max_len).reshape(-1,1)
|
|
868
|
-
div = np.exp(np.arange(0, dmodel, 2) * (-np.log(10000.0) / dmodel))
|
|
869
|
-
pe[:,0::2] = np.sin(pos*div)
|
|
870
|
-
pe[:,1::2] = np.cos(pos*div)
|
|
953
|
+
pe = backend.np.zeros((max_len,dmodel))
|
|
954
|
+
pos = backend.np.arange(max_len).reshape(-1,1)
|
|
955
|
+
div = backend.np.exp(backend.np.arange(0, dmodel, 2) * (-backend.np.log(10000.0) / dmodel))
|
|
956
|
+
pe[:,0::2] = backend.np.sin(pos*div)
|
|
957
|
+
pe[:,1::2] = backend.np.cos(pos*div)
|
|
871
958
|
self.pe = pe
|
|
872
959
|
|
|
873
960
|
|
|
@@ -877,4 +964,34 @@ class PositionalEncoding:
|
|
|
877
964
|
|
|
878
965
|
def parameters(self):
|
|
879
966
|
return []
|
|
880
|
-
|
|
967
|
+
|
|
968
|
+
class RotatoryPositionalEncoding:
|
|
969
|
+
def __init__(self,max_len, dim, base=10000.0):
|
|
970
|
+
assert dim % 2 == 0, "RoPE dim must be even"
|
|
971
|
+
theta = 1.0/(base**(backend.np.arange(0,dim,2)/float(dim)))
|
|
972
|
+
m = backend.np.arange(max_len)
|
|
973
|
+
|
|
974
|
+
freq = backend.np.outer(m,theta)
|
|
975
|
+
cos = backend.np.cos(freq)
|
|
976
|
+
sin = backend.np.sin(freq)
|
|
977
|
+
|
|
978
|
+
self.cos = backend.np.concatenate([cos, cos], axis=-1)
|
|
979
|
+
self.sin = backend.np.concatenate([sin, sin], axis=-1)
|
|
980
|
+
|
|
981
|
+
def xbar(self,x):
|
|
982
|
+
d = x.shape[-1]
|
|
983
|
+
half = d // 2
|
|
984
|
+
|
|
985
|
+
x1 = x[...,:half]
|
|
986
|
+
x2 = x[...,half:]
|
|
987
|
+
|
|
988
|
+
return Tensor.cat((-x2,x1),axis=-1)
|
|
989
|
+
|
|
990
|
+
def __call__(self,x:Tensor):
|
|
991
|
+
T = x.shape[-2]
|
|
992
|
+
cos = self.cos[:T]
|
|
993
|
+
sin = self.sin[:T]
|
|
994
|
+
return x*cos + self.xbar(x)*sin
|
|
995
|
+
|
|
996
|
+
def parameters(self):
|
|
997
|
+
return []
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import numpy
|
|
2
|
+
try:
|
|
3
|
+
import numexpr as _ne
|
|
4
|
+
_NUMEXPR_AVAILABLE = True
|
|
5
|
+
except ImportError:
|
|
6
|
+
_NUMEXPR_AVAILABLE = False
|
|
7
|
+
|
|
8
|
+
USE_NUMEXPR = False
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
import cupy as _cp
|
|
12
|
+
_CUPY_AVAILABLE = True
|
|
13
|
+
except ImportError:
|
|
14
|
+
_cp = None
|
|
15
|
+
_CUPY_AVAILABLE = False
|
|
16
|
+
|
|
17
|
+
np = numpy
|
|
18
|
+
USE_GPU = False
|
|
19
|
+
NO_GRAD = False
|
|
20
|
+
|
|
21
|
+
def use_gpu(enabled = True):
|
|
22
|
+
global np, USE_GPU
|
|
23
|
+
if enabled and not _CUPY_AVAILABLE:
|
|
24
|
+
raise RuntimeError("cupy not installed")
|
|
25
|
+
np = _cp if enabled else numpy
|
|
26
|
+
USE_GPU = bool(enabled)
|
|
27
|
+
return USE_GPU
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import backend
|
|
2
|
+
from app import Embedding, PositionalEncoding,Transformer,LayerNorm,Linear,RotatoryPositionalEncoding
|
|
3
|
+
|
|
4
|
+
class GPTV1:
|
|
5
|
+
def __init__(self, vocab, d_model, n_heads, n_layers, max_len, d_ff=None):
|
|
6
|
+
self.tok = Embedding(vocab, d_model)
|
|
7
|
+
self.pos = PositionalEncoding(max_len, d_model)
|
|
8
|
+
self.blocks = [Transformer(dmodel=d_model,n=n_heads,dff=d_ff) for _ in range(n_layers)]
|
|
9
|
+
self.ln_f = LayerNorm(d_model)
|
|
10
|
+
self.head = Linear(d_model, vocab)
|
|
11
|
+
self.max_len = max_len
|
|
12
|
+
|
|
13
|
+
def __call__(self, idx):
|
|
14
|
+
T = idx.shape[1]
|
|
15
|
+
casual = backend.np.triu((backend.np.ones((T,T))),k=1).astype(bool)
|
|
16
|
+
x = self.pos(self.tok(idx))
|
|
17
|
+
for block in self.blocks:
|
|
18
|
+
x = block(x,mask=casual)
|
|
19
|
+
|
|
20
|
+
x = self.ln_f(x)
|
|
21
|
+
return self.head(x)
|
|
22
|
+
|
|
23
|
+
def parameters(self):
|
|
24
|
+
ps = self.tok.parameters() + self.ln_f.parameters() + self.head.parameters()
|
|
25
|
+
for b in self.blocks: ps += b.parameters()
|
|
26
|
+
return ps
|
|
27
|
+
|
|
28
|
+
def generate(self, idx, n_new, temperature=1.0, top_k=None):
|
|
29
|
+
"""
|
|
30
|
+
idx: (B, T) int array — the prompt (B is usually 1)
|
|
31
|
+
returns: (B, T + n_new) int array
|
|
32
|
+
"""
|
|
33
|
+
for _ in range(n_new):
|
|
34
|
+
cond = idx[:, -self.max_len:] # crop to context window
|
|
35
|
+
logits = self(cond).data[:, -1, :] / temperature # (B, V) — last position only
|
|
36
|
+
if top_k is not None:
|
|
37
|
+
kth = backend.np.sort(logits, axis=-1)[:, -top_k][:, None]
|
|
38
|
+
logits = backend.np.where(logits < kth, -1e9, logits) # keep only top-k choices
|
|
39
|
+
z = logits - logits.max(-1, keepdims=True)
|
|
40
|
+
p = backend.np.exp(z).astype(backend.np.float64); p /= p.sum(-1, keepdims=True) # softmax → probabilities
|
|
41
|
+
nxt = backend.np.array([[backend.np.random.choice(len(pr), p=pr)] for pr in p]) # sample
|
|
42
|
+
idx = backend.np.concatenate([idx, nxt], axis=1) # append, feed back in
|
|
43
|
+
return idx # <-- was a bare `return`
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class GPT2:
|
|
49
|
+
def __init__(self, vocab, d_model, n_heads, n_layers, max_len, d_ff=None, base=10000.0):
|
|
50
|
+
assert d_model % n_heads == 0
|
|
51
|
+
self.tok = Embedding(vocab, d_model)
|
|
52
|
+
self.rope = RotatoryPositionalEncoding(max_len, d_model // n_heads, base) # dim = d_k
|
|
53
|
+
self.blocks = [Transformer(d_model, n_heads, dff=d_ff, rope=self.rope)
|
|
54
|
+
for _ in range(n_layers)]
|
|
55
|
+
self.ln_f = LayerNorm(d_model)
|
|
56
|
+
self.head = Linear(d_model, vocab)
|
|
57
|
+
self.max_len = max_len
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def __call__(self, idx):
|
|
61
|
+
T = idx.shape[1]
|
|
62
|
+
causal = backend.np.triu(backend.np.ones((T, T)), k=1).astype(bool)
|
|
63
|
+
x = self.tok(idx) # straight from token embedding
|
|
64
|
+
for block in self.blocks:
|
|
65
|
+
x = block(x, mask=causal)
|
|
66
|
+
return self.head(self.ln_f(x))
|
|
67
|
+
|
|
68
|
+
def parameters(self):
|
|
69
|
+
ps = self.tok.parameters() + self.ln_f.parameters() + self.head.parameters()
|
|
70
|
+
for b in self.blocks: ps += b.parameters()
|
|
71
|
+
return ps
|
|
72
|
+
|
|
73
|
+
def generate(self, idx, n_new, temperature=1.0, top_k=None):
|
|
74
|
+
"""
|
|
75
|
+
idx: (B, T) int array — the prompt (B is usually 1)
|
|
76
|
+
returns: (B, T + n_new) int array
|
|
77
|
+
"""
|
|
78
|
+
for _ in range(n_new):
|
|
79
|
+
cond = idx[:, -self.max_len:] # crop to context window
|
|
80
|
+
logits = self(cond).data[:, -1, :] / temperature # (B, V) — last position only
|
|
81
|
+
if top_k is not None:
|
|
82
|
+
kth = backend.np.sort(logits, axis=-1)[:, -top_k][:, None]
|
|
83
|
+
logits = backend.np.where(logits < kth, -1e9, logits) # keep only top-k choices
|
|
84
|
+
z = logits - logits.max(-1, keepdims=True)
|
|
85
|
+
p = backend.np.exp(z).astype(backend.np.float64); p /= p.sum(-1, keepdims=True) # softmax → probabilities
|
|
86
|
+
nxt = backend.np.array([[backend.np.random.choice(len(pr), p=pr)] for pr in p]) # sample
|
|
87
|
+
idx = backend.np.concatenate([idx, nxt], axis=1) # append, feed back in
|
|
88
|
+
return idx
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "cogforge-engine"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "1.0.0"
|
|
8
8
|
authors = [
|
|
9
9
|
{ name="Avik Majumder", email="avikmjd2@gmail.com" },
|
|
10
10
|
]
|
|
@@ -13,6 +13,12 @@ readme = "README.md"
|
|
|
13
13
|
requires-python = ">=3.8"
|
|
14
14
|
dependencies = [
|
|
15
15
|
"numpy>=1.20.0",
|
|
16
|
+
"numexpr>=2.8.0",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[project.optional-dependencies]
|
|
20
|
+
cuda = [
|
|
21
|
+
"cupy>=12.0.0", # Optional dependency
|
|
16
22
|
]
|
|
17
23
|
|
|
18
24
|
[project.urls]
|
cogforge_engine-0.2.1/LICENSE
DELETED
|
File without changes
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import numpy as np
|
|
2
|
-
from app import Embedding, PositionalEncoding,Transformer,LayerNorm,Linear
|
|
3
|
-
|
|
4
|
-
class GPTV1:
|
|
5
|
-
def __init__(self, vocab, d_model, n_heads, n_layers, max_len, d_ff=None):
|
|
6
|
-
self.tok = Embedding(vocab, d_model)
|
|
7
|
-
self.pos = PositionalEncoding(max_len, d_model)
|
|
8
|
-
self.blocks = [Transformer(dmodel=d_model,n=n_heads,dff=d_ff) for _ in range(n_layers)]
|
|
9
|
-
self.ln_f = LayerNorm(d_model)
|
|
10
|
-
self.head = Linear(d_model, vocab)
|
|
11
|
-
self.max_len = max_len
|
|
12
|
-
|
|
13
|
-
def __call__(self, idx):
|
|
14
|
-
T = idx.shape[1]
|
|
15
|
-
casual = np.triu((np.ones((T,T))),k=1).astype(bool)
|
|
16
|
-
x = self.pos(self.tok(idx))
|
|
17
|
-
for block in self.blocks:
|
|
18
|
-
x = block(x,mask=casual)
|
|
19
|
-
|
|
20
|
-
x = self.ln_f(x)
|
|
21
|
-
return self.head(x)
|
|
22
|
-
|
|
23
|
-
def parameters(self):
|
|
24
|
-
ps = self.tok.parameters() + self.ln_f.parameters() + self.head.parameters()
|
|
25
|
-
for b in self.blocks: ps += b.parameters()
|
|
26
|
-
return ps
|
|
27
|
-
|
|
28
|
-
def generate(self, idx, n_new, temperature=1.0, top_k=None):
|
|
29
|
-
"""
|
|
30
|
-
idx: (B, T) int array — the prompt (B is usually 1)
|
|
31
|
-
returns: (B, T + n_new) int array
|
|
32
|
-
"""
|
|
33
|
-
for _ in range(n_new):
|
|
34
|
-
cond = idx[:, -self.max_len:] # crop to context window
|
|
35
|
-
logits = self(cond).data[:, -1, :] / temperature # (B, V) — last position only
|
|
36
|
-
if top_k is not None:
|
|
37
|
-
kth = np.sort(logits, axis=-1)[:, -top_k][:, None]
|
|
38
|
-
logits = np.where(logits < kth, -1e9, logits) # keep only top-k choices
|
|
39
|
-
z = logits - logits.max(-1, keepdims=True)
|
|
40
|
-
p = np.exp(z).astype(np.float64); p /= p.sum(-1, keepdims=True) # softmax → probabilities
|
|
41
|
-
nxt = np.array([[np.random.choice(len(pr), p=pr)] for pr in p]) # sample
|
|
42
|
-
idx = np.concatenate([idx, nxt], axis=1) # append, feed back in
|
|
43
|
-
return idx # <-- was a bare `return`
|
|
44
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|