cogforge-engine 0.1.0__py3-none-any.whl

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/__init__.py ADDED
@@ -0,0 +1,44 @@
1
+ __version__ = "0.1.0"
2
+
3
+ from .app import (
4
+ Tensor,
5
+ Linear,
6
+ MLP,
7
+ Embedding,
8
+ RNNCell,
9
+ RNN,
10
+ StackedRNN,
11
+ SGD,
12
+ Adam,
13
+ Sequential,
14
+ BatchNorm1D,
15
+ Bridge,
16
+ Attention,
17
+ MultiHeadAttention,
18
+ LayerNorm,
19
+ FeedForward,
20
+ Transformer,
21
+ PositionalEncoding
22
+ )
23
+
24
+
25
+ __all__ = [
26
+ "Tensor",
27
+ "Linear",
28
+ "MLP",
29
+ "Embedding",
30
+ "RNNCell",
31
+ "RNN",
32
+ "StackedRNN",
33
+ "SGD",
34
+ "Adam",
35
+ "Sequential",
36
+ "BatchNorm1D",
37
+ "Bridge",
38
+ "Attention",
39
+ "MultiHeadAttention",
40
+ "LayerNorm",
41
+ "FeedForward",
42
+ "Transformer",
43
+ "PositionalEncoding"
44
+ ]
cogforge/app.py ADDED
@@ -0,0 +1,864 @@
1
+ import numpy as np
2
+
3
+ class Tensor:
4
+ def __init__(self, array,children=(),requires_grad = True):
5
+ self.data = np.asarray(array, dtype=float)
6
+ self.shape = self.data.shape
7
+ self.requires_grad = requires_grad
8
+ # if self.requires_grad:
9
+ self.grad = np.zeros(self.shape)
10
+ self._backwards = lambda:None
11
+ self._children = set(children)
12
+
13
+ def __getitem__(self, key):
14
+ out_data = self.data[key]
15
+
16
+ out = Tensor(out_data,children=(self,))
17
+
18
+ def _backward():
19
+ self.grad[key] += out.grad
20
+
21
+ out._backwards = _backward
22
+
23
+ return out
24
+
25
+
26
+ def __add__(self, other):
27
+ other = other if isinstance(other, Tensor) else Tensor(other)
28
+ out_data = self.data + other.data
29
+ out = Tensor(out_data, (self, other))
30
+
31
+ def _backward():
32
+ if self.shape != out_data.shape:
33
+ axis = tuple(range(out.grad.ndim - self.data.ndim))
34
+ self.grad += np.sum(out.grad, axis=axis).reshape(self.shape)
35
+ else:
36
+ self.grad += out.grad
37
+
38
+ if other.shape != out_data.shape:
39
+ axis = tuple(range(out.grad.ndim - other.data.ndim))
40
+ other.grad += np.sum(out.grad, axis=axis).reshape(other.shape)
41
+ else:
42
+ other.grad += out.grad
43
+
44
+ out._backwards = _backward
45
+ return out
46
+
47
+ def __radd__(self, other):
48
+ return self + other
49
+
50
+ def __neg__(self):
51
+ return self * -1
52
+
53
+ def __sub__(self, other):
54
+ other = other if isinstance(other, Tensor) else Tensor(other)
55
+ return self + (-other)
56
+
57
+ def __rsub__(self, other):
58
+ return (-self) + other
59
+
60
+ def __mul__(self, other):
61
+ other = other if isinstance(other, Tensor) else Tensor(other)
62
+ out_data = self.data * other.data
63
+
64
+ out = Tensor(out_data, children=(self, other))
65
+
66
+ def _backward():
67
+ grad_self = other.data * out.grad
68
+ if self.shape != out_data.shape:
69
+ axis = tuple(range(out.grad.ndim - self.data.ndim))
70
+ grad_self = np.sum(grad_self, axis=axis).reshape(self.shape)
71
+ self.grad += grad_self
72
+
73
+ # gradient w.r.t. other
74
+ grad_other = self.data * out.grad
75
+ if other.shape != out_data.shape:
76
+ axis = tuple(range(out.grad.ndim - other.data.ndim))
77
+ grad_other = np.sum(grad_other, axis=axis).reshape(other.shape)
78
+ other.grad += grad_other
79
+
80
+ out._backwards = _backward
81
+ return out
82
+
83
+ def __rmul__(self, other):
84
+ return self * other
85
+
86
+ # def __matmul__(self, other):
87
+ # out_data = self.data @ other.data
88
+
89
+ # out = Tensor(out_data, children=(self,other))
90
+
91
+ # # def _backward():
92
+ # # self.grad += out.grad @ np.transpose(other.data)
93
+ # # other.grad += np.transpose(self.data) @ out.grad
94
+
95
+ # def _backward():
96
+ # self.grad += out.grad @ np.swapaxes(other.data, -1, -2)
97
+ # other.grad += np.swapaxes(self.data, -1, -2) @ out.grad
98
+
99
+ # out._backwards = _backward
100
+ # return out
101
+
102
+ @staticmethod
103
+ def unbroadcast(grad, shape):
104
+ while grad.ndim > len(shape):
105
+ grad = grad.sum(axis=0)
106
+ for i, s in enumerate(shape):
107
+ if s == 1 and grad.shape[i] != 1:
108
+ grad = grad.sum(axis=i, keepdims=True)
109
+ return grad
110
+
111
+ def __matmul__(self, other):
112
+ out = Tensor(self.data @ other.data, children=(self, other))
113
+ def _backward():
114
+ gs = out.grad @ np.swapaxes(other.data, -1, -2)
115
+ go = np.swapaxes(self.data, -1, -2) @ out.grad
116
+ self.grad += Tensor.unbroadcast(gs, self.data.shape)
117
+ other.grad += Tensor.unbroadcast(go, other.data.shape)
118
+ out._backwards = _backward
119
+ return out
120
+
121
+ def relu(self):
122
+ out_data = np.maximum(0,self.data)
123
+
124
+ out = Tensor(out_data, children=(self,))
125
+
126
+ def _backward():
127
+ mask = out_data > 0
128
+ self.grad+=(mask*out.grad)
129
+
130
+ out._backwards = _backward
131
+ return out
132
+
133
+
134
+ def view(self,shape):
135
+ curr_shape = self.data.shape
136
+
137
+ if not self.data.flags['C_CONTIGUOUS']:
138
+ arr = np.ascontiguousarray(self.data)
139
+ else:
140
+ arr = self.data
141
+
142
+ out_data = arr.reshape(shape)
143
+ out = Tensor(out_data, children=(self,))
144
+
145
+ def _backward():
146
+ self.grad+=(out.grad.reshape(curr_shape))
147
+
148
+
149
+ out._backwards = _backward
150
+ return out
151
+
152
+
153
+
154
+
155
+ def sigmoid(self):
156
+ out_data = 1 / (1 + np.exp(-self.data))
157
+ out = Tensor(out_data, children=(self,))
158
+ def _backward():
159
+ local_derivative = out_data * (1 - out_data)
160
+
161
+ self.grad += local_derivative * out.grad
162
+
163
+ out._backwards = _backward
164
+ return out
165
+
166
+ def tanh(self):
167
+ out_data = np.tanh(self.data)
168
+ out = Tensor(out_data,children=(self,))
169
+ def _backward():
170
+ dervitive = 1-(out_data*out_data)
171
+ self.grad+=(out.grad*dervitive)
172
+
173
+ out._backwards = _backward
174
+ return out
175
+
176
+ def softmax(self,axis=-1):
177
+ maxVal = np.max(self.data,axis=axis,keepdims=True)
178
+ expVal = np.exp(self.data-maxVal)
179
+
180
+ out_data = expVal/np.sum(expVal,axis=axis,keepdims=True)
181
+ out=Tensor(out_data,(self,))
182
+
183
+ def _backward():
184
+ g = out.grad
185
+ self.grad += out_data * (g - np.sum(g * out_data, axis=axis, keepdims=True))
186
+
187
+ out._backwards = _backward
188
+ return out
189
+
190
+ #optimized for batches
191
+ @classmethod
192
+ def cross_entropy_loss(cls,predictions, targets):
193
+
194
+ probabilities = np.clip(predictions.data, 1e-15, 1 - 1e-15)
195
+ batch_size = predictions.shape[0]
196
+
197
+ loss_data = - np.sum(targets * np.log(probabilities)) / batch_size
198
+ loss = Tensor(loss_data, children=(predictions,))
199
+
200
+ def _backward():
201
+
202
+ # predictions.grad += (predictions.data - targets) / batch_size * loss.grad
203
+ predictions.grad += (-(targets / probabilities)) / batch_size * loss.grad
204
+
205
+
206
+ loss._backwards = _backward
207
+ return loss
208
+
209
+ #LEGACY
210
+ def backwards_recursive(self):
211
+ topo = []
212
+ visited = set()
213
+ def topoSort(v):
214
+ if v in visited:
215
+ return
216
+ visited.add(v)
217
+ for edges in v._children:
218
+ topoSort(edges)
219
+
220
+ topo.append(v)
221
+
222
+ topoSort(self)
223
+
224
+ self.grad = np.ones(self.shape)
225
+
226
+ for node in reversed(topo):
227
+ node._backwards()
228
+
229
+ def backwards(self):
230
+ topo = []
231
+ visited = set()
232
+ stack = [(self,False)]
233
+
234
+ while stack:
235
+ node,processed = stack.pop()
236
+ if processed:
237
+ topo.append(node)
238
+ continue
239
+ if node in visited:
240
+ continue
241
+ visited.add(node)
242
+
243
+ stack.append((node,True))
244
+ for child in node._children:
245
+ stack.append((child,False))
246
+
247
+ self.grad = np.ones(self.shape)
248
+
249
+ for node in reversed(topo):
250
+ node._backwards()
251
+
252
+
253
+ def flatten(self):
254
+ batch_size = self.shape[0]
255
+ out_data = self.data.reshape(batch_size, -1)
256
+
257
+ out = Tensor(out_data, children=(self,))
258
+ def _backward():
259
+ self.grad += out.grad.reshape(self.shape)
260
+
261
+ out._backwards = _backward
262
+ return out
263
+
264
+ def flatten_consective(self,num):
265
+ """THIS ONLY FLATTENS A 3 DIMENSIONAL TENSOR. CAREFULL.
266
+ EXPLECTS (BATCH, CONTEXT, DIMENSION) AS SHAPE"""
267
+ self.flatten_dim = num
268
+ if(len(self.shape)) != 3:
269
+ raise ValueError("THIS FUNCTION ONLY EXPECTS A 3 DIMENSIONAL TENSOR.")
270
+
271
+ B,T,C = self.shape
272
+
273
+ if T % num != 0:
274
+ raise ValueError(f"Time dimension {T} must be cleanly divisible by num {num}")
275
+
276
+ out_data = self.data.reshape(B,T//num,C*num)
277
+
278
+ out = Tensor(out_data, children=(self,))
279
+
280
+ def _backward():
281
+ self.grad += out.grad.reshape(self.shape)
282
+
283
+ out._backwards = _backward
284
+ return out
285
+
286
+ #TODO
287
+ @classmethod
288
+ def cross_entropy_loss_masked(cls, predictions, targets, mask):
289
+ """
290
+ predictions: (B, V) softmax probs
291
+ targets: (B, V) one-hot
292
+ mask: (B,) 1.0 for real positions, 0.0 for <PAD>
293
+ """
294
+ probs = np.clip(predictions.data, 1e-15, 1 - 1e-15)
295
+ mask = mask.reshape(-1, 1) # (B, 1)
296
+ n_real = mask.sum()
297
+ n_real = n_real if n_real > 0 else 1.0
298
+
299
+ # only real rows contribute to the loss value
300
+ loss_data = -np.sum(mask * targets * np.log(probs)) / n_real
301
+ loss = cls(loss_data, children=(predictions,))
302
+
303
+ def _backward():
304
+ # grad = (predictions.data - targets) / n_real
305
+ grad = -(targets / probs) / n_real
306
+ grad = grad * mask # <-- padded rows get exactly zero gradient
307
+ predictions.grad += grad * loss.grad
308
+
309
+ loss._backwards = _backward
310
+ return loss
311
+
312
+ #Legacy
313
+ @classmethod
314
+ def softmax_cross_entropy_old(cls, scores, targets):
315
+ z = scores.data
316
+ z = z - z.max(axis=1, keepdims=True) # stability
317
+ e = np.exp(z)
318
+ p = e / e.sum(axis=1, keepdims=True) # softmax, computed privately
319
+ B = scores.shape[0]
320
+
321
+ loss = cls(-np.sum(targets * np.log(np.clip(p, 1e-15, 1.0))) / B, children=(scores,))
322
+
323
+ def _backward():
324
+ scores.grad += (p - targets) / B * loss.grad
325
+ loss._backwards = _backward
326
+ return loss
327
+
328
+ @classmethod
329
+ def softmax_cross_entropy(cls, scores, targets):
330
+ z = scores.data
331
+ z = z - z.max(axis=-1, keepdims=True)
332
+ e = np.exp(z)
333
+ p = e / e.sum(axis=-1, keepdims=True)
334
+ N = np.prod(scores.shape[:-1]) # B for 2D, B*T for 3D
335
+ loss = cls(-np.sum(targets * np.log(np.clip(p, 1e-15, 1.0))) / N, children=(scores,))
336
+ def _backward():
337
+ scores.grad += (p - targets) / N * loss.grad
338
+ loss._backwards = _backward
339
+ return loss
340
+
341
+
342
+ @classmethod
343
+ def softmax_cross_entropy_masked(cls, scores, targets, mask):
344
+ z = scores.data
345
+ z = z - z.max(axis=1, keepdims=True)
346
+ e = np.exp(z)
347
+ p = e / e.sum(axis=1, keepdims=True)
348
+
349
+ mask = mask.reshape(-1, 1)
350
+ n_real = mask.sum()
351
+ n_real = n_real if n_real > 0 else 1.0
352
+
353
+ loss = cls(-np.sum(mask * targets * np.log(np.clip(p, 1e-15, 1.0))) / n_real, children=(scores,))
354
+
355
+ def _backward():
356
+ grad = (p - targets) / n_real
357
+ grad = grad * mask
358
+ scores.grad += grad * loss.grad
359
+ loss._backwards = _backward
360
+ return loss
361
+
362
+ def transpose(self,axes):
363
+ out = Tensor(np.transpose(self.data,axes=axes),children=(self,))
364
+ inv = np.argsort(axes)
365
+ def _backward():
366
+ self.grad+=np.transpose(out.grad,inv)
367
+
368
+ out._backwards = _backward
369
+ return out
370
+
371
+ def masked_fill(self, mask, value):
372
+ out_data = np.where(mask, value, self.data)
373
+ out = Tensor(out_data, children=(self,))
374
+ def _backward():
375
+ self.grad += np.where(mask, 0.0, 1.0) * out.grad
376
+ out._backwards = _backward
377
+ return out
378
+
379
+
380
+
381
+
382
+ class Linear:
383
+ def __init__(self, nin, nout):
384
+ self.W = Tensor(np.random.randn(nin, nout) * np.sqrt(2.0/nin))
385
+ self.v_W = np.zeros(self.W.shape)
386
+ self.b = Tensor(np.zeros((nout,)))
387
+ self.v_b = np.zeros(self.b.shape)
388
+
389
+ def __call__ (self,x):
390
+ if(x.shape[-1]!=self.W.shape[0]):
391
+ raise TypeError("Dimensions mismatched")
392
+ out = x@self.W + self.b
393
+ return out
394
+
395
+ def parameters(self):
396
+ return [self.W, self.b]
397
+
398
+
399
+ class MLP:
400
+
401
+
402
+ def __init__(self, layer_sizes):
403
+ self.layers = []
404
+ for i in range(len(layer_sizes)-1):
405
+ self.layers.append(Linear(layer_sizes[i], layer_sizes[i+1]))
406
+
407
+
408
+ def __call__(self, x):
409
+ for layer in self.layers[:-1]:
410
+ x = layer(x).relu()
411
+
412
+ out = self.layers[-1](x)
413
+ return out
414
+
415
+
416
+ def save(self, filename="best_model.npz"):
417
+ weights_dict = {}
418
+ for i, layer in enumerate(self.layers):
419
+ weights_dict[f'W_{i}'] = layer.W.data
420
+ weights_dict[f'b_{i}'] = layer.b.data
421
+
422
+ np.savez_compressed(filename, **weights_dict)
423
+
424
+ def load(self, filename="best_model.npz"):
425
+ with np.load(filename) as data:
426
+ for i, layer in enumerate(self.layers):
427
+ layer.W.data = data[f'W_{i}']
428
+ layer.b.data = data[f'b_{i}']
429
+ print(f"Model loaded successfully from {filename}!")
430
+
431
+
432
+ class Embedding:
433
+ def __init__(self, vocab_size, embedding_dim):
434
+ self.weights = Tensor(np.random.randn(vocab_size, embedding_dim))
435
+
436
+ def __call__(self, input_indices):
437
+ out_data = self.weights.data[input_indices]
438
+
439
+ out = Tensor(out_data, children=(self.weights,))
440
+
441
+ def _backward():
442
+ np.add.at(self.weights.grad,input_indices,out.grad)
443
+
444
+ out._backwards = _backward
445
+ return out
446
+
447
+ def parameters(self):
448
+ return [self.weights]
449
+
450
+
451
+ class RNNCell:
452
+ def __init__(self,input_dim, hidden_dim):
453
+ self.i2h = Linear(input_dim,hidden_dim)
454
+ self.h2h = Linear(hidden_dim,hidden_dim)
455
+
456
+ def __call__(self, x,h_prev):
457
+ input_transform = self.i2h(x)
458
+ hidden_transform = self.h2h(h_prev)
459
+
460
+ h_next = Tensor.tanh(input_transform + hidden_transform)
461
+
462
+ return h_next
463
+
464
+ def parameters(self):
465
+ return self.i2h.parameters() + self.h2h.parameters()
466
+
467
+
468
+ class RNN:
469
+ def __init__(self,input_dim, hidden_dim):
470
+ self.hidden_dim = hidden_dim
471
+ self.cell = RNNCell(input_dim=input_dim,hidden_dim=hidden_dim)
472
+
473
+ def __call__(self,xs,prev_hidden = None):
474
+ """
475
+ xs: A list of Tensors, each of shape (B, input_dim) — the same
476
+ timestep across all sequences in the batch.
477
+
478
+ Returns: A list of Tensors (hidden states) at every time step,
479
+ each of shape (B, hidden_dim).
480
+ """
481
+
482
+ if prev_hidden is not None:
483
+ h = prev_hidden
484
+ else:
485
+ B = xs[0].shape[0]
486
+ h = Tensor(np.zeros((B, self.hidden_dim)))
487
+
488
+
489
+ hidden_states = []
490
+
491
+ for x in xs:
492
+ h = self.cell(x,h)
493
+ hidden_states.append(h)
494
+
495
+ return hidden_states
496
+
497
+ def parameters(self):
498
+ return self.cell.parameters()
499
+
500
+
501
+ class StackedRNN:
502
+ def __init__(self,input_dim,hidden_dim,num_layers):
503
+ self.layers = []
504
+ self.layers.append(RNN(input_dim,hidden_dim))
505
+
506
+ for _ in range(1,num_layers):
507
+ self.layers.append(RNN(input_dim=hidden_dim, hidden_dim=hidden_dim))
508
+
509
+ def __call__(self,xs,prev_hidden = None):
510
+ """
511
+ xs: A list of Tensors (the initial word embeddings)
512
+
513
+ Returns: A list of Tensors representing the hidden states
514
+ from the very TOP layer.
515
+ """
516
+ if prev_hidden is not None:
517
+ assert len(prev_hidden) == len(self.layers), \
518
+ f"prev_hidden has {len(prev_hidden)} states but stack has {len(self.layers)} layers"
519
+
520
+
521
+ curr = xs
522
+ h_outs = []
523
+
524
+ for i,layer in enumerate(self.layers):
525
+ initial = prev_hidden[i] if prev_hidden is not None else None
526
+ curr = layer(curr,initial)
527
+
528
+ h_outs.append(curr[-1])
529
+
530
+ return curr,h_outs
531
+
532
+ def parameters(self):
533
+ return [p for layer in self.layers for p in layer.parameters()]
534
+
535
+
536
+ class SGD:
537
+ def __init__(self,parameters,learning_rate=0.01):
538
+ self.parameters = parameters
539
+ self.lr = learning_rate
540
+
541
+ def clip_grads(self, max_norm=5.0):
542
+ total = np.sqrt(sum(np.sum(p.grad ** 2) for p in self.parameters))
543
+ if total > max_norm:
544
+ scale = max_norm / (total + 1e-6)
545
+ for p in self.parameters:
546
+ p.grad *= scale
547
+
548
+ def step(self):
549
+ for p in self.parameters:
550
+ p.data-=self.lr*(p.grad)
551
+
552
+ def zero_grad(self):
553
+ for p in self.parameters:
554
+ p.grad = np.zeros_like(p.grad)
555
+
556
+ class Sequential:
557
+ def __init__(self,layers):
558
+ self.layers = layers
559
+
560
+ def __call__(self, x):
561
+ for layer in self.layers:
562
+ x = layer(x)
563
+ return x
564
+
565
+ def parameters(self):
566
+ params = []
567
+ for layer in self.layers:
568
+ if hasattr(layer, 'W'): params.append(layer.W)
569
+ if hasattr(layer, 'b'): params.append(layer.b)
570
+ if hasattr(layer, 'gamma'): params.append(layer.gamma)
571
+ if hasattr(layer, 'beta'): params.append(layer.beta)
572
+ #TODO: add rest of parameters
573
+ return params
574
+
575
+ def train(self):
576
+ for layer in self.layers:
577
+ if hasattr(layer, 'training'):
578
+ layer.training = True
579
+
580
+ def test(self):
581
+ for layer in self.layers:
582
+ if hasattr(layer, 'training'):
583
+ layer.training = False
584
+
585
+
586
+ class BatchNorm1D:
587
+ def __init__(self, dim, eps=1e-5, momentum=0.1):
588
+ self.eps = eps
589
+ self.momentum = momentum
590
+ self.training = True
591
+
592
+ self.gamma = Tensor(np.ones(dim))
593
+ self.beta = Tensor(np.zeros(dim))
594
+
595
+ self.running_mean = np.zeros(dim)
596
+ self.running_var = np.ones(dim)
597
+
598
+
599
+ def __call__(self,x:Tensor):
600
+ reduce_dims = (0,) if x.data.ndim==2 else (0,1)
601
+
602
+ if self.training:
603
+ mean = x.data.mean(axis=reduce_dims, keepdims=True)
604
+ var = x.data.var(axis=reduce_dims, keepdims=True)
605
+
606
+ self.x_centered = x.data - mean
607
+ self.std_inv = 1.0 / np.sqrt(var + self.eps)
608
+ self.x_hat = self.x_centered * self.std_inv
609
+
610
+ N = np.prod([x.data.shape[d] for d in reduce_dims])
611
+ unbiased_var = var.squeeze() * (N / (N - 1)) if N > 1 else var.squeeze()
612
+
613
+ self.running_mean = (1 - self.momentum) * self.running_mean + self.momentum * mean.squeeze()
614
+ self.running_var = (1 - self.momentum) * self.running_var + self.momentum * unbiased_var
615
+
616
+ else:
617
+ self.x_hat = (x.data-self.running_mean)/np.sqrt(self.running_var + self.eps)
618
+
619
+ out_data = self.gamma.data * self.x_hat + self.beta.data
620
+
621
+ out = Tensor(out_data, children=(x, self.gamma, self.beta))
622
+
623
+ def _backward():
624
+ if not self.training:
625
+ return
626
+
627
+ dout = out.grad
628
+ self.gamma.grad += np.sum(dout * self.x_hat, axis=reduce_dims)
629
+ self.beta.grad += np.sum(dout, axis=reduce_dims)
630
+
631
+ N = np.prod([x.data.shape[d] for d in reduce_dims])
632
+
633
+ dx_hat = dout * self.gamma.data
634
+ dx = (1.0 / N) * self.std_inv * (
635
+ N * dx_hat
636
+ - np.sum(dx_hat, axis=reduce_dims, keepdims=True)
637
+ - self.x_hat * np.sum(dx_hat * self.x_hat, axis=reduce_dims, keepdims=True)
638
+ )
639
+
640
+ x.grad += dx
641
+
642
+
643
+ out._backwards = _backward
644
+
645
+ return out
646
+
647
+
648
+
649
+ class Bridge:
650
+ """
651
+ Maps encoder final hidden states to decoder initial hidden states.
652
+ Handles asymmetry in:
653
+ - num_layers (encoder count != decoder count)
654
+ - hidden_dim (enc_hidden != dec_hidden)
655
+ Embedding-dim asymmetry needs no bridge: encoder/decoder have
656
+ independent Embeddings and independent first-layer i2h Linears.
657
+
658
+ mode:
659
+ "project" : one learned Linear(enc_H -> dec_H) per decoder layer (general, recommended)
660
+ "tie" : no params; requires enc_H == dec_H. Selects/repeats raw states.
661
+ """
662
+ def __init__(self, enc_hidden, dec_hidden, enc_layers, dec_layers, mode="project"):
663
+ self.enc_hidden = enc_hidden
664
+ self.dec_hidden = dec_hidden
665
+ self.enc_layers = enc_layers
666
+ self.dec_layers = dec_layers
667
+ self.mode = mode
668
+
669
+ if mode == "tie":
670
+ if enc_hidden != dec_hidden:
671
+ raise ValueError(
672
+ f"mode='tie' needs enc_hidden==dec_hidden, "
673
+ f"got {enc_hidden} vs {dec_hidden}. Use mode='project'."
674
+ )
675
+ self.projections = None
676
+ elif mode == "project":
677
+ # one projection per decoder layer
678
+ self.projections = [Linear(enc_hidden, dec_hidden) for _ in range(dec_layers)]
679
+ else:
680
+ raise ValueError(f"unknown bridge mode: {mode}")
681
+
682
+ def __select(self,enc_hidden):
683
+ """Pick/pad encoder states to exactly dec_layers, bottom→top aligned at the TOP."""
684
+ n = len(enc_hidden) #enc_hidden is the final layer of each level
685
+ if n == self.dec_layers:
686
+ return enc_hidden
687
+ elif n > self.dec_layers:
688
+ return enc_hidden[-self.dec_layers:]
689
+ else:
690
+ top = enc_hidden[-1]
691
+ return enc_hidden + [top] * (self.dec_layers - n)
692
+
693
+ def __call__(self,enc_hidden):
694
+ final_layers = self.__select(enc_hidden)
695
+ if self.mode=="tie":
696
+ return final_layers
697
+
698
+ return [proj(h) for proj,h in zip(self.projections,final_layers)]
699
+
700
+ def parameters(self):
701
+ if self.projections is None:
702
+ return []
703
+ return [p for proj in self.projections for p in proj.parameters()]
704
+
705
+
706
+ class Adam:
707
+ def __init__(self, parameters, lr=1e-3, beta1=0.9, beta2=0.999, eps=1e-8):
708
+ self.parameters = parameters
709
+ self.lr = lr; self.beta1 = beta1; self.beta2 = beta2; self.eps = eps
710
+ self.m = [np.zeros_like(p.data) for p in parameters]
711
+ self.v = [np.zeros_like(p.data) for p in parameters]
712
+ self.t = 0
713
+
714
+ def step(self):
715
+ self.t += 1
716
+ for i, p in enumerate(self.parameters):
717
+ self.m[i] = self.beta1*self.m[i] + (1-self.beta1)*p.grad
718
+ self.v[i] = self.beta2*self.v[i] + (1-self.beta2)*(p.grad**2)
719
+ m_hat = self.m[i] / (1 - self.beta1**self.t)
720
+ v_hat = self.v[i] / (1 - self.beta2**self.t)
721
+ p.data -= self.lr * m_hat / (np.sqrt(v_hat) + self.eps)
722
+
723
+ def zero_grad(self):
724
+ for p in self.parameters:
725
+ p.grad = np.zeros_like(p.grad)
726
+
727
+ def clip_grads(self, max_norm=5.0):
728
+ total = np.sqrt(sum(np.sum(p.grad**2) for p in self.parameters))
729
+ if total > max_norm:
730
+ scale = max_norm / (total + 1e-6)
731
+ for p in self.parameters:
732
+ p.grad *= scale
733
+
734
+
735
+ class Attention:
736
+ def __init__(self,dk):
737
+ self.scale = 1.0/np.sqrt(dk)
738
+
739
+ def __call__(self, Q:Tensor,K:Tensor,V:Tensor,mask=None):
740
+ axes = list(range(Q.data.ndim)); axes[-1], axes[-2] = axes[-2], axes[-1]
741
+ scores:Tensor = (Q @ K.transpose(tuple(axes)))*(self.scale)
742
+
743
+ if mask is not None:
744
+ scores = scores.masked_fill(mask,-1e9)
745
+
746
+ intermediate = scores.softmax(axis=-1)
747
+
748
+ return intermediate @ V
749
+
750
+ class MultiHeadAttention:
751
+ def __init__(self,dinp,dmodel, dout,n):
752
+ assert dmodel%n==0
753
+ self.h = n
754
+ self.dk = dmodel//n
755
+ self.q = Linear(dinp,dmodel) #dmodel = dk*h, we will slice it
756
+ self.k = Linear(dinp,dmodel)
757
+ self.v = Linear(dinp,dmodel)
758
+ self.o = Linear(dmodel,dout)
759
+ self.attention = Attention(self.dk)
760
+
761
+ def split(self,x:Tensor):
762
+ B,T,_ = x.data.shape
763
+ return x.view((B,T,self.h,self.dk)).transpose((0,2,1,3)) #(B, h, T, d_k)
764
+
765
+ def merge(self,x:Tensor):
766
+ B, h, T, d_k = x.data.shape
767
+ return x.transpose((0, 2, 1, 3)).view((B, T, h * d_k))
768
+
769
+ def __call__(self, query,key,value,mask=None):
770
+ Q = self.split(self.q(query))
771
+ K = self.split(self.k(key))
772
+ V = self.split(self.v(value))
773
+ out = self.attention(Q=Q,K=K,V=V,mask=mask)
774
+ return self.o(self.merge(out))
775
+
776
+ def parameters(self):
777
+ return (self.q.parameters() + self.k.parameters()
778
+ + self.v.parameters() + self.o.parameters())
779
+
780
+
781
+ class LayerNorm:
782
+ def __init__(self, dim, eps=1e-5):
783
+ self.eps = eps
784
+ self.gamma = Tensor(np.ones(dim))
785
+ self.beta = Tensor(np.zeros(dim))
786
+
787
+ def __call__(self, x):
788
+ mu = x.data.mean(axis=-1, keepdims=True)
789
+ var = x.data.var(axis=-1, keepdims=True)
790
+ std_inv = 1.0 / np.sqrt(var + self.eps)
791
+ x_hat = (x.data - mu) * std_inv
792
+ out_data = self.gamma.data * x_hat + self.beta.data
793
+ out = Tensor(out_data, children=(x, self.gamma, self.beta))
794
+
795
+ D = x.data.shape[-1]
796
+ def _backward():
797
+ dout = out.grad
798
+ axes = tuple(range(dout.ndim - 1))
799
+ self.gamma.grad += np.sum(dout * x_hat, axis=axes)
800
+ self.beta.grad += np.sum(dout, axis=axes)
801
+ dxhat = dout * self.gamma.data
802
+ dx = std_inv / D * (
803
+ D * dxhat
804
+ - np.sum(dxhat, axis=-1, keepdims=True)
805
+ - x_hat * np.sum(dxhat * x_hat, axis=-1, keepdims=True)
806
+ )
807
+ x.grad += dx
808
+ out._backwards = _backward
809
+ return out
810
+
811
+ def parameters(self):
812
+ return [self.gamma, self.beta]
813
+
814
+ class FeedForward:
815
+ def __init__(self,dmodel,dff=None):
816
+ dff = dff if dff is not None else 4 * dmodel
817
+ self.fc1 = Linear(dmodel,dff)
818
+ self.fc2 = Linear(dff,dmodel)
819
+
820
+ def __call__(self, x):
821
+ return self.fc2(self.fc1(x).relu())
822
+ def parameters(self):
823
+ return self.fc1.parameters() + self.fc2.parameters()
824
+
825
+ class Transformer:
826
+ """The transformer block has no bridge. So demb = dmodel"""
827
+
828
+ def __init__(self, dmodel, n, dff = None):
829
+ self.ln1 = LayerNorm(dmodel)
830
+ self.attn = MultiHeadAttention(dinp=dmodel,dmodel=dmodel,dout=dmodel,n=n)
831
+ self.ln2 = LayerNorm(dmodel)
832
+ self.ff = FeedForward(dmodel=dmodel,dff=dff)
833
+
834
+ def __call__(self,x,mask=None):
835
+ a = self.ln1(x)
836
+ x = x + self.attn(a,a,a,mask=mask)
837
+ f = self.ln2(x)
838
+ x = x+ self.ff(f)
839
+
840
+ return x
841
+
842
+ def parameters(self):
843
+ return (self.ln1.parameters() + self.attn.parameters()
844
+ + self.ln2.parameters() + self.ff.parameters())
845
+
846
+
847
+ class PositionalEncoding:
848
+ def __init__(self,max_len,dmodel):
849
+ pe = np.zeros((max_len,dmodel))
850
+ pos = np.arange(max_len).reshape(-1,1)
851
+ div = np.exp(np.arange(0, dmodel, 2) * (-np.log(10000.0) / dmodel))
852
+ pe[:,0::2] = np.sin(pos*div)
853
+ pe[:,1::2] = np.cos(pos*div)
854
+ self.pe = pe
855
+
856
+
857
+ def __call__(self, x):
858
+ T = x.shape[1]
859
+ return x + Tensor(self.pe[:T])
860
+
861
+ def parameters(self):
862
+ return []
863
+
864
+
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: cogforge-engine
3
+ Version: 0.1.0
4
+ Summary: A custom autograd engine and Transformer block built from scratch.
5
+ Project-URL: Homepage, https://github.com/avikmjd2/cogforge
6
+ Author-email: Avik Majumder <avikmjd2@gmail.com>
7
+ Requires-Python: >=3.8
8
+ Requires-Dist: numpy>=1.20.0
9
+ Description-Content-Type: text/markdown
10
+
11
+ Intial
@@ -0,0 +1,5 @@
1
+ cogforge/__init__.py,sha256=7HeqDsw0zZwevCy4mhX5s0EjPbb38a3vzHdUJfvQlrI,617
2
+ cogforge/app.py,sha256=iTL4ly4aokNjjy7KrHgspW8tlwM1ilUxrsPwEZ6jNw0,28196
3
+ cogforge_engine-0.1.0.dist-info/METADATA,sha256=Iin90tQDdhCz2IuiRGgDOd4otLXPHtBgXoat1xYV-eY,343
4
+ cogforge_engine-0.1.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
5
+ cogforge_engine-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any