vectorphile 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.
Files changed (53) hide show
  1. vectorphile/__init__.py +154 -0
  2. vectorphile/amp/__init__.py +52 -0
  3. vectorphile/autograd/__init__.py +13 -0
  4. vectorphile/autograd/engine.py +208 -0
  5. vectorphile/core/__init__.py +21 -0
  6. vectorphile/core/tensor.py +696 -0
  7. vectorphile/data/__init__.py +23 -0
  8. vectorphile/data/frame.py +537 -0
  9. vectorphile/data/groupby.py +98 -0
  10. vectorphile/data/index.py +86 -0
  11. vectorphile/data/series.py +228 -0
  12. vectorphile/fft/__init__.py +34 -0
  13. vectorphile/linalg/__init__.py +63 -0
  14. vectorphile/ml/__init__.py +44 -0
  15. vectorphile/ml/boost/__init__.py +133 -0
  16. vectorphile/ml/cluster.py +218 -0
  17. vectorphile/ml/decomposition.py +161 -0
  18. vectorphile/ml/ensemble.py +209 -0
  19. vectorphile/ml/linear.py +250 -0
  20. vectorphile/ml/metrics.py +148 -0
  21. vectorphile/ml/model_selection.py +171 -0
  22. vectorphile/ml/naive_bayes.py +113 -0
  23. vectorphile/ml/neighbors.py +95 -0
  24. vectorphile/ml/preprocessing.py +202 -0
  25. vectorphile/ml/svm.py +165 -0
  26. vectorphile/ml/tree.py +155 -0
  27. vectorphile/nn/__init__.py +20 -0
  28. vectorphile/nn/activations.py +97 -0
  29. vectorphile/nn/layers.py +231 -0
  30. vectorphile/nn/losses.py +172 -0
  31. vectorphile/nn/module.py +97 -0
  32. vectorphile/nn/sequential.py +47 -0
  33. vectorphile/optim/__init__.py +12 -0
  34. vectorphile/optim/adagrad.py +31 -0
  35. vectorphile/optim/adam.py +71 -0
  36. vectorphile/optim/lr_scheduler.py +110 -0
  37. vectorphile/optim/optimizer.py +30 -0
  38. vectorphile/optim/rmsprop.py +34 -0
  39. vectorphile/optim/sgd.py +38 -0
  40. vectorphile/optimize/__init__.py +97 -0
  41. vectorphile/random/__init__.py +64 -0
  42. vectorphile/signal/__init__.py +67 -0
  43. vectorphile/sparse/__init__.py +129 -0
  44. vectorphile/stats/__init__.py +90 -0
  45. vectorphile/utils/__init__.py +3 -0
  46. vectorphile/utils/data/__init__.py +4 -0
  47. vectorphile/utils/data/dataloader.py +47 -0
  48. vectorphile/utils/data/dataset.py +29 -0
  49. vectorphile-0.1.0.dist-info/METADATA +208 -0
  50. vectorphile-0.1.0.dist-info/RECORD +53 -0
  51. vectorphile-0.1.0.dist-info/WHEEL +5 -0
  52. vectorphile-0.1.0.dist-info/licenses/LICENSE +201 -0
  53. vectorphile-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,97 @@
1
+ """Activation function modules."""
2
+ import numpy as np
3
+ from vectorphile.nn.module import Module
4
+ from vectorphile.core.tensor import Tensor
5
+
6
+
7
+ class ReLU(Module):
8
+ def __init__(self, inplace=False):
9
+ super().__init__()
10
+ def forward(self, x: Tensor) -> Tensor:
11
+ return x.relu()
12
+ def __repr__(self): return 'ReLU()'
13
+
14
+
15
+ class LeakyReLU(Module):
16
+ def __init__(self, negative_slope=0.01):
17
+ super().__init__()
18
+ self.negative_slope = negative_slope
19
+ def forward(self, x: Tensor) -> Tensor:
20
+ return Tensor(np.where(x._data >= 0, x._data, self.negative_slope * x._data), device=x.device)
21
+ def __repr__(self): return f'LeakyReLU(negative_slope={self.negative_slope})'
22
+
23
+
24
+ class ELU(Module):
25
+ def __init__(self, alpha=1.0):
26
+ super().__init__()
27
+ self.alpha = alpha
28
+ def forward(self, x: Tensor) -> Tensor:
29
+ return Tensor(np.where(x._data >= 0, x._data, self.alpha * (np.exp(x._data) - 1)), device=x.device)
30
+ def __repr__(self): return f'ELU(alpha={self.alpha})'
31
+
32
+
33
+ class GELU(Module):
34
+ def forward(self, x: Tensor) -> Tensor:
35
+ d = x._data
36
+ return Tensor(0.5 * d * (1 + np.tanh(np.sqrt(2/np.pi) * (d + 0.044715 * d**3))), device=x.device)
37
+ def __repr__(self): return 'GELU()'
38
+
39
+
40
+ class Sigmoid(Module):
41
+ def forward(self, x: Tensor) -> Tensor:
42
+ return x.sigmoid()
43
+ def __repr__(self): return 'Sigmoid()'
44
+
45
+
46
+ class Tanh(Module):
47
+ def forward(self, x: Tensor) -> Tensor:
48
+ return x.tanh()
49
+ def __repr__(self): return 'Tanh()'
50
+
51
+
52
+ class Softmax(Module):
53
+ def __init__(self, dim=-1):
54
+ super().__init__()
55
+ self.dim = dim
56
+ def forward(self, x: Tensor) -> Tensor:
57
+ from vectorphile.core.tensor import softmax
58
+ return softmax(x, self.dim)
59
+ def __repr__(self): return f'Softmax(dim={self.dim})'
60
+
61
+
62
+ class LogSoftmax(Module):
63
+ def __init__(self, dim=-1):
64
+ super().__init__()
65
+ self.dim = dim
66
+ def forward(self, x: Tensor) -> Tensor:
67
+ from vectorphile.core.tensor import log_softmax
68
+ return log_softmax(x, self.dim)
69
+ def __repr__(self): return f'LogSoftmax(dim={self.dim})'
70
+
71
+
72
+ class SELU(Module):
73
+ _alpha = 1.6732632423543772
74
+ _scale = 1.0507009873554805
75
+ def forward(self, x: Tensor) -> Tensor:
76
+ d = x._data
77
+ return Tensor(self._scale * np.where(d >= 0, d, self._alpha * (np.exp(d) - 1)), device=x.device)
78
+ def __repr__(self): return 'SELU()'
79
+
80
+
81
+ class SiLU(Module):
82
+ def forward(self, x: Tensor) -> Tensor:
83
+ return Tensor(x._data * (1 / (1 + np.exp(-x._data))), device=x.device)
84
+ def __repr__(self): return 'SiLU()'
85
+
86
+
87
+ class Mish(Module):
88
+ def forward(self, x: Tensor) -> Tensor:
89
+ return Tensor(x._data * np.tanh(np.log1p(np.exp(x._data))), device=x.device)
90
+ def __repr__(self): return 'Mish()'
91
+
92
+
93
+ class Hardswish(Module):
94
+ def forward(self, x: Tensor) -> Tensor:
95
+ d = x._data
96
+ return Tensor(d * np.clip(d + 3, 0, 6) / 6, device=x.device)
97
+ def __repr__(self): return 'Hardswish()'
@@ -0,0 +1,231 @@
1
+ """Neural network layers."""
2
+ from __future__ import annotations
3
+ import numpy as np
4
+ import math
5
+ from vectorphile.nn.module import Module
6
+ from vectorphile.core.tensor import Tensor
7
+ from vectorphile.autograd.engine import Function
8
+
9
+
10
+ class _LinearBackward(Function):
11
+ """Custom backward for y = x @ W.T (W is weight matrix, shape out×in)."""
12
+ def __init__(self, x, weight):
13
+ super().__init__(x, weight)
14
+ self.x = x
15
+ self.weight = weight
16
+
17
+ def backward(self, grad):
18
+ # grad shape: (N, out)
19
+ # dL/dx = grad @ W (N, in)
20
+ # dL/dW = grad.T @ x (out, in)
21
+ dx = Tensor(grad._data @ self.weight._data)
22
+ dw = Tensor(grad._data.T @ self.x._data)
23
+ return dx, dw
24
+
25
+
26
+ class Linear(Module):
27
+ def __init__(self, in_features: int, out_features: int, bias: bool = True):
28
+ super().__init__()
29
+ self.in_features = in_features
30
+ self.out_features = out_features
31
+ k = math.sqrt(1.0 / in_features)
32
+ self.weight = Tensor(
33
+ np.random.uniform(-k, k, (out_features, in_features)).astype(np.float32),
34
+ requires_grad=True
35
+ )
36
+ self.bias_param = None
37
+ if bias:
38
+ self.bias_param = Tensor(
39
+ np.zeros(out_features, dtype=np.float32), requires_grad=True
40
+ )
41
+ self._parameters['bias_param'] = self.bias_param
42
+ self._parameters['weight'] = self.weight
43
+
44
+ def forward(self, x: Tensor) -> Tensor:
45
+ # Manually compute matmul to keep weight in autograd graph
46
+ # x: (N, in), weight: (out, in) → out: (N, out)
47
+ from vectorphile.autograd.engine import MatMulBackward
48
+ import numpy as np
49
+ result_data = x._data @ self.weight._data.T
50
+ req_grad = x.requires_grad or self.weight.requires_grad
51
+ # Create a transposed view for the backward pass
52
+ weight_t = Tensor(self.weight._data.T, device=self.weight.device, requires_grad=False)
53
+ weight_t._data = self.weight._data.T # view, not copy
54
+ result = Tensor(result_data, device=x.device, requires_grad=req_grad)
55
+ if req_grad:
56
+ # Custom backward: dL/dx = grad @ W, dL/dW = grad.T @ x
57
+ result.grad_fn = _LinearBackward(x, self.weight)
58
+ result._is_leaf = False
59
+ if self.bias_param is not None:
60
+ result = result + self.bias_param
61
+ return result
62
+
63
+ def __repr__(self):
64
+ return f'Linear(in_features={self.in_features}, out_features={self.out_features}, bias={self.bias_param is not None})'
65
+
66
+
67
+ class Conv2d(Module):
68
+ def __init__(self, in_channels, out_channels, kernel_size, stride=1, padding=0, bias=True):
69
+ super().__init__()
70
+ self.in_channels = in_channels
71
+ self.out_channels = out_channels
72
+ self.kernel_size = kernel_size if isinstance(kernel_size, tuple) else (kernel_size, kernel_size)
73
+ self.stride = stride if isinstance(stride, tuple) else (stride, stride)
74
+ self.padding = padding if isinstance(padding, tuple) else (padding, padding)
75
+ kh, kw = self.kernel_size
76
+ k = math.sqrt(1.0 / (in_channels * kh * kw))
77
+ self.weight = Tensor(
78
+ np.random.uniform(-k, k, (out_channels, in_channels, kh, kw)).astype(np.float32),
79
+ requires_grad=True
80
+ )
81
+ self._parameters['weight'] = self.weight
82
+ self.bias_param = None
83
+ if bias:
84
+ self.bias_param = Tensor(np.zeros(out_channels, dtype=np.float32), requires_grad=True)
85
+ self._parameters['bias_param'] = self.bias_param
86
+
87
+ def forward(self, x: Tensor) -> Tensor:
88
+ N, C, H, W = x._data.shape
89
+ kh, kw = self.kernel_size
90
+ sh, sw = self.stride
91
+ ph, pw = self.padding
92
+ x_pad = np.pad(x._data, ((0,0),(0,0),(ph,ph),(pw,pw)), mode='constant')
93
+ Ho = (H + 2*ph - kh) // sh + 1
94
+ Wo = (W + 2*pw - kw) // sw + 1
95
+ out = np.zeros((N, self.out_channels, Ho, Wo), dtype=np.float32)
96
+ w = self.weight._data
97
+ for i in range(Ho):
98
+ for j in range(Wo):
99
+ patch = x_pad[:, :, i*sh:i*sh+kh, j*sw:j*sw+kw]
100
+ out[:, :, i, j] = np.einsum('nckl,ockl->no', patch, w)
101
+ if self.bias_param is not None:
102
+ out += self.bias_param._data[None, :, None, None]
103
+ return Tensor(out, device=x.device)
104
+
105
+ def __repr__(self):
106
+ return (f'Conv2d({self.in_channels}, {self.out_channels}, '
107
+ f'kernel_size={self.kernel_size}, stride={self.stride}, padding={self.padding})')
108
+
109
+
110
+ class BatchNorm1d(Module):
111
+ def __init__(self, num_features, eps=1e-5, momentum=0.1, affine=True):
112
+ super().__init__()
113
+ self.num_features = num_features
114
+ self.eps = eps
115
+ self.momentum = momentum
116
+ self.affine = affine
117
+ if affine:
118
+ self.weight = Tensor(np.ones(num_features, dtype=np.float32), requires_grad=True)
119
+ self.bias_param = Tensor(np.zeros(num_features, dtype=np.float32), requires_grad=True)
120
+ self._parameters['weight'] = self.weight
121
+ self._parameters['bias_param'] = self.bias_param
122
+ self.running_mean = np.zeros(num_features, dtype=np.float32)
123
+ self.running_var = np.ones(num_features, dtype=np.float32)
124
+
125
+ def forward(self, x: Tensor) -> Tensor:
126
+ data = x._data
127
+ if self.training:
128
+ mean = data.mean(axis=0)
129
+ var = data.var(axis=0)
130
+ self.running_mean = (1 - self.momentum) * self.running_mean + self.momentum * mean
131
+ self.running_var = (1 - self.momentum) * self.running_var + self.momentum * var
132
+ else:
133
+ mean, var = self.running_mean, self.running_var
134
+ x_norm = (data - mean) / np.sqrt(var + self.eps)
135
+ if self.affine:
136
+ x_norm = x_norm * self.weight._data + self.bias_param._data
137
+ return Tensor(x_norm.astype(np.float32), device=x.device)
138
+
139
+
140
+ class BatchNorm2d(Module):
141
+ def __init__(self, num_features, eps=1e-5, momentum=0.1, affine=True):
142
+ super().__init__()
143
+ self.num_features = num_features
144
+ self.eps = eps
145
+ self.momentum = momentum
146
+ self.affine = affine
147
+ if affine:
148
+ self.weight = Tensor(np.ones(num_features, dtype=np.float32), requires_grad=True)
149
+ self.bias_param = Tensor(np.zeros(num_features, dtype=np.float32), requires_grad=True)
150
+ self._parameters['weight'] = self.weight
151
+ self._parameters['bias_param'] = self.bias_param
152
+ self.running_mean = np.zeros(num_features, dtype=np.float32)
153
+ self.running_var = np.ones(num_features, dtype=np.float32)
154
+
155
+ def forward(self, x: Tensor) -> Tensor:
156
+ data = x._data # (N, C, H, W)
157
+ if self.training:
158
+ mean = data.mean(axis=(0, 2, 3))
159
+ var = data.var(axis=(0, 2, 3))
160
+ self.running_mean = (1 - self.momentum) * self.running_mean + self.momentum * mean
161
+ self.running_var = (1 - self.momentum) * self.running_var + self.momentum * var
162
+ else:
163
+ mean, var = self.running_mean, self.running_var
164
+ x_norm = (data - mean[None,:,None,None]) / np.sqrt(var[None,:,None,None] + self.eps)
165
+ if self.affine:
166
+ x_norm = x_norm * self.weight._data[None,:,None,None] + self.bias_param._data[None,:,None,None]
167
+ return Tensor(x_norm.astype(np.float32), device=x.device)
168
+
169
+
170
+ class LayerNorm(Module):
171
+ def __init__(self, normalized_shape, eps=1e-5, elementwise_affine=True):
172
+ super().__init__()
173
+ if isinstance(normalized_shape, int):
174
+ normalized_shape = (normalized_shape,)
175
+ self.normalized_shape = tuple(normalized_shape)
176
+ self.eps = eps
177
+ self.elementwise_affine = elementwise_affine
178
+ if elementwise_affine:
179
+ self.weight = Tensor(np.ones(normalized_shape, dtype=np.float32), requires_grad=True)
180
+ self.bias_param = Tensor(np.zeros(normalized_shape, dtype=np.float32), requires_grad=True)
181
+ self._parameters['weight'] = self.weight
182
+ self._parameters['bias_param'] = self.bias_param
183
+
184
+ def forward(self, x: Tensor) -> Tensor:
185
+ ndim = len(self.normalized_shape)
186
+ axes = tuple(range(-ndim, 0))
187
+ mean = x._data.mean(axis=axes, keepdims=True)
188
+ var = x._data.var(axis=axes, keepdims=True)
189
+ x_norm = (x._data - mean) / np.sqrt(var + self.eps)
190
+ if self.elementwise_affine:
191
+ x_norm = x_norm * self.weight._data + self.bias_param._data
192
+ return Tensor(x_norm.astype(np.float32), device=x.device)
193
+
194
+
195
+ class Dropout(Module):
196
+ def __init__(self, p=0.5):
197
+ super().__init__()
198
+ self.p = p
199
+
200
+ def forward(self, x: Tensor) -> Tensor:
201
+ if not self.training or self.p == 0:
202
+ return x
203
+ mask = (np.random.rand(*x._data.shape) > self.p).astype(np.float32)
204
+ return Tensor(x._data * mask / (1.0 - self.p), device=x.device, requires_grad=x.requires_grad)
205
+
206
+ def __repr__(self):
207
+ return f'Dropout(p={self.p})'
208
+
209
+
210
+ class Embedding(Module):
211
+ def __init__(self, num_embeddings, embedding_dim, padding_idx=None):
212
+ super().__init__()
213
+ self.num_embeddings = num_embeddings
214
+ self.embedding_dim = embedding_dim
215
+ self.padding_idx = padding_idx
216
+ self.weight = Tensor(
217
+ np.random.randn(num_embeddings, embedding_dim).astype(np.float32) * 0.02,
218
+ requires_grad=True
219
+ )
220
+ self._parameters['weight'] = self.weight
221
+
222
+ def forward(self, indices: Tensor) -> Tensor:
223
+ idx = indices._data.astype(int)
224
+ out = self.weight._data[idx]
225
+ if self.padding_idx is not None:
226
+ mask = (idx == self.padding_idx)[..., None]
227
+ out = np.where(mask, 0.0, out)
228
+ return Tensor(out.astype(np.float32), device=indices.device)
229
+
230
+ def __repr__(self):
231
+ return f'Embedding({self.num_embeddings}, {self.embedding_dim})'
@@ -0,0 +1,172 @@
1
+ """Loss functions."""
2
+ import numpy as np
3
+ from vectorphile.nn.module import Module
4
+ from vectorphile.core.tensor import Tensor
5
+
6
+
7
+ class MSELoss(Module):
8
+ def __init__(self, reduction='mean'):
9
+ super().__init__()
10
+ self.reduction = reduction
11
+
12
+ def forward(self, pred: Tensor, target: Tensor) -> Tensor:
13
+ diff = pred - target
14
+ sq = diff * diff
15
+ if self.reduction == 'mean':
16
+ return sq.mean()
17
+ elif self.reduction == 'sum':
18
+ return sq.sum()
19
+ return sq
20
+
21
+
22
+ class MAELoss(Module):
23
+ def __init__(self, reduction='mean'):
24
+ super().__init__()
25
+ self.reduction = reduction
26
+
27
+ def forward(self, pred: Tensor, target: Tensor) -> Tensor:
28
+ diff = np.abs(pred._data - target._data)
29
+ if self.reduction == 'mean':
30
+ return Tensor(diff.mean(), device=pred.device, requires_grad=True)
31
+ elif self.reduction == 'sum':
32
+ return Tensor(diff.sum(), device=pred.device, requires_grad=True)
33
+ return Tensor(diff, device=pred.device, requires_grad=True)
34
+
35
+
36
+ class CrossEntropyLoss(Module):
37
+ def __init__(self, reduction='mean', ignore_index=-100, label_smoothing=0.0):
38
+ super().__init__()
39
+ self.reduction = reduction
40
+ self.ignore_index = ignore_index
41
+ self.label_smoothing = label_smoothing
42
+
43
+ def forward(self, logits: Tensor, targets: Tensor) -> Tensor:
44
+ log_probs = logits._data - np.log(np.exp(logits._data).sum(axis=-1, keepdims=True) + 1e-12)
45
+ t = targets._data.astype(int)
46
+ n = log_probs.shape[0]
47
+ nll = -log_probs[np.arange(n), t]
48
+ if self.label_smoothing > 0:
49
+ smooth_loss = -log_probs.mean(axis=-1)
50
+ nll = (1 - self.label_smoothing) * nll + self.label_smoothing * smooth_loss
51
+ if self.reduction == 'mean':
52
+ return Tensor(nll.mean(), device=logits.device, requires_grad=True)
53
+ elif self.reduction == 'sum':
54
+ return Tensor(nll.sum(), device=logits.device, requires_grad=True)
55
+ return Tensor(nll, device=logits.device, requires_grad=True)
56
+
57
+
58
+ class _BCEBackward:
59
+ """Custom backward for BCE loss that keeps the computation in the autograd graph."""
60
+ def __init__(self, pred, target, reduction):
61
+ self.inputs = (pred,)
62
+ self.pred = pred
63
+ self.target = target
64
+ self.reduction = reduction
65
+
66
+ def backward(self, grad):
67
+ p = np.clip(self.pred._data, 1e-7, 1 - 1e-7)
68
+ t = self.target._data
69
+ n = p.size if self.reduction == 'mean' else 1
70
+ dp = (-(t / p) + (1 - t) / (1 - p)) / n
71
+ return (Tensor(grad._data * dp),)
72
+
73
+
74
+ class BCELoss(Module):
75
+ def __init__(self, reduction='mean'):
76
+ super().__init__()
77
+ self.reduction = reduction
78
+
79
+ def forward(self, pred: Tensor, target: Tensor) -> Tensor:
80
+ p = np.clip(pred._data, 1e-7, 1 - 1e-7)
81
+ t = target._data
82
+ loss_data = -(t * np.log(p) + (1 - t) * np.log(1 - p))
83
+ if self.reduction == 'mean':
84
+ val = loss_data.mean()
85
+ elif self.reduction == 'sum':
86
+ val = loss_data.sum()
87
+ else:
88
+ val = loss_data
89
+ result = Tensor(val, device=pred.device, requires_grad=pred.requires_grad)
90
+ if pred.requires_grad:
91
+ result.grad_fn = _BCEBackward(pred, target, self.reduction)
92
+ result._is_leaf = False
93
+ return result
94
+
95
+
96
+ class BCEWithLogitsLoss(Module):
97
+ def __init__(self, reduction='mean'):
98
+ super().__init__()
99
+ self.reduction = reduction
100
+
101
+ def forward(self, logits: Tensor, target: Tensor) -> Tensor:
102
+ d = logits._data
103
+ t = target._data
104
+ loss = np.maximum(d, 0) - d * t + np.log(1 + np.exp(-np.abs(d)))
105
+ if self.reduction == 'mean':
106
+ return Tensor(loss.mean(), device=logits.device, requires_grad=True)
107
+ elif self.reduction == 'sum':
108
+ return Tensor(loss.sum(), device=logits.device, requires_grad=True)
109
+ return Tensor(loss, device=logits.device, requires_grad=True)
110
+
111
+
112
+ class NLLLoss(Module):
113
+ def __init__(self, reduction='mean'):
114
+ super().__init__()
115
+ self.reduction = reduction
116
+
117
+ def forward(self, log_probs: Tensor, targets: Tensor) -> Tensor:
118
+ t = targets._data.astype(int)
119
+ n = log_probs._data.shape[0]
120
+ nll = -log_probs._data[np.arange(n), t]
121
+ if self.reduction == 'mean':
122
+ return Tensor(nll.mean(), device=log_probs.device, requires_grad=True)
123
+ elif self.reduction == 'sum':
124
+ return Tensor(nll.sum(), device=log_probs.device, requires_grad=True)
125
+ return Tensor(nll, device=log_probs.device, requires_grad=True)
126
+
127
+
128
+ class HuberLoss(Module):
129
+ def __init__(self, reduction='mean', delta=1.0):
130
+ super().__init__()
131
+ self.reduction = reduction
132
+ self.delta = delta
133
+
134
+ def forward(self, pred: Tensor, target: Tensor) -> Tensor:
135
+ diff = np.abs(pred._data - target._data)
136
+ loss = np.where(diff < self.delta, 0.5 * diff**2, self.delta * (diff - 0.5 * self.delta))
137
+ if self.reduction == 'mean':
138
+ return Tensor(loss.mean(), device=pred.device, requires_grad=True)
139
+ elif self.reduction == 'sum':
140
+ return Tensor(loss.sum(), device=pred.device, requires_grad=True)
141
+ return Tensor(loss, device=pred.device, requires_grad=True)
142
+
143
+
144
+ class KLDivLoss(Module):
145
+ def __init__(self, reduction='mean', log_target=False):
146
+ super().__init__()
147
+ self.reduction = reduction
148
+ self.log_target = log_target
149
+
150
+ def forward(self, input: Tensor, target: Tensor) -> Tensor:
151
+ if self.log_target:
152
+ loss = np.exp(target._data) * (target._data - input._data)
153
+ else:
154
+ loss = target._data * (np.log(target._data + 1e-12) - input._data)
155
+ if self.reduction == 'mean':
156
+ return Tensor(loss.mean(), device=input.device, requires_grad=True)
157
+ elif self.reduction == 'sum':
158
+ return Tensor(loss.sum(), device=input.device, requires_grad=True)
159
+ return Tensor(loss, device=input.device, requires_grad=True)
160
+
161
+
162
+ class CosineSimilarityLoss(Module):
163
+ def __init__(self, dim=1, eps=1e-8):
164
+ super().__init__()
165
+ self.dim = dim
166
+ self.eps = eps
167
+
168
+ def forward(self, x1: Tensor, x2: Tensor) -> Tensor:
169
+ n1 = np.linalg.norm(x1._data, axis=self.dim, keepdims=True)
170
+ n2 = np.linalg.norm(x2._data, axis=self.dim, keepdims=True)
171
+ cos_sim = (x1._data * x2._data).sum(axis=self.dim) / (n1.squeeze(self.dim) * n2.squeeze(self.dim) + self.eps)
172
+ return Tensor((1 - cos_sim).mean(), device=x1.device, requires_grad=True)
@@ -0,0 +1,97 @@
1
+ """Base Module class — mirrors PyTorch's nn.Module API."""
2
+ from __future__ import annotations
3
+ import numpy as np
4
+ from typing import Dict, Iterator, List, Optional, Tuple, Any
5
+ from collections import OrderedDict
6
+
7
+
8
+ class Module:
9
+ def __init__(self):
10
+ self._parameters: OrderedDict = OrderedDict()
11
+ self._modules: OrderedDict = OrderedDict()
12
+ self._buffers: OrderedDict = OrderedDict()
13
+ self.training: bool = True
14
+
15
+ def __setattr__(self, name: str, value):
16
+ from vectorphile.core.tensor import Tensor
17
+ if isinstance(value, Tensor) and value.requires_grad:
18
+ self._parameters[name] = value
19
+ elif isinstance(value, Module):
20
+ self._modules[name] = value
21
+ super().__setattr__(name, value)
22
+
23
+ def parameters(self) -> Iterator:
24
+ for p in self._parameters.values():
25
+ yield p
26
+ for module in self._modules.values():
27
+ yield from module.parameters()
28
+
29
+ def named_parameters(self, prefix='') -> Iterator[Tuple[str, Any]]:
30
+ for name, p in self._parameters.items():
31
+ yield (f'{prefix}.{name}' if prefix else name), p
32
+ for mname, module in self._modules.items():
33
+ full = f'{prefix}.{mname}' if prefix else mname
34
+ yield from module.named_parameters(prefix=full)
35
+
36
+ def modules(self) -> Iterator['Module']:
37
+ yield self
38
+ for m in self._modules.values():
39
+ yield from m.modules()
40
+
41
+ def named_modules(self, prefix='') -> Iterator[Tuple[str, 'Module']]:
42
+ yield prefix or '', self
43
+ for name, m in self._modules.items():
44
+ full = f'{prefix}.{name}' if prefix else name
45
+ yield from m.named_modules(prefix=full)
46
+
47
+ def state_dict(self) -> OrderedDict:
48
+ sd = OrderedDict()
49
+ for name, p in self.named_parameters():
50
+ sd[name] = p._data.copy()
51
+ return sd
52
+
53
+ def load_state_dict(self, state_dict: dict, strict=True):
54
+ from vectorphile.core.tensor import Tensor
55
+ own = dict(self.named_parameters())
56
+ for key, val in state_dict.items():
57
+ if key in own:
58
+ own[key]._data[:] = val
59
+ elif strict:
60
+ raise KeyError(f'Unexpected key "{key}" in state_dict')
61
+
62
+ def train(self, mode=True) -> 'Module':
63
+ self.training = mode
64
+ for m in self._modules.values():
65
+ m.train(mode)
66
+ return self
67
+
68
+ def eval(self) -> 'Module':
69
+ return self.train(False)
70
+
71
+ def zero_grad(self):
72
+ for p in self.parameters():
73
+ p.grad = None
74
+
75
+ def forward(self, *args, **kwargs):
76
+ raise NotImplementedError
77
+
78
+ def __call__(self, *args, **kwargs):
79
+ return self.forward(*args, **kwargs)
80
+
81
+ def __repr__(self):
82
+ lines = [self.__class__.__name__ + '(']
83
+ for name, m in self._modules.items():
84
+ mod_str = repr(m)
85
+ mod_str = ' ' + mod_str.replace('\n', '\n ')
86
+ lines.append(f' ({name}): {mod_str}')
87
+ lines.append(')')
88
+ return '\n'.join(lines)
89
+
90
+ def register_buffer(self, name: str, tensor):
91
+ self._buffers[name] = tensor
92
+ object.__setattr__(self, name, tensor)
93
+
94
+ def apply(self, fn) -> 'Module':
95
+ for m in self.modules():
96
+ fn(m)
97
+ return self
@@ -0,0 +1,47 @@
1
+ """Sequential container."""
2
+ from collections import OrderedDict
3
+ from vectorphile.nn.module import Module
4
+ from vectorphile.core.tensor import Tensor
5
+
6
+
7
+ class Sequential(Module):
8
+ def __init__(self, *args):
9
+ super().__init__()
10
+ if len(args) == 1 and isinstance(args[0], OrderedDict):
11
+ for key, module in args[0].items():
12
+ self._modules[key] = module
13
+ setattr(self, key, module)
14
+ else:
15
+ for i, module in enumerate(args):
16
+ self._modules[str(i)] = module
17
+ object.__setattr__(self, str(i), module)
18
+
19
+ def forward(self, x: Tensor) -> Tensor:
20
+ for module in self._modules.values():
21
+ x = module(x)
22
+ return x
23
+
24
+ def append(self, module: Module) -> 'Sequential':
25
+ idx = str(len(self._modules))
26
+ self._modules[idx] = module
27
+ object.__setattr__(self, idx, module)
28
+ return self
29
+
30
+ def __getitem__(self, idx):
31
+ if isinstance(idx, slice):
32
+ keys = list(self._modules.keys())[idx]
33
+ return Sequential(OrderedDict((k, self._modules[k]) for k in keys))
34
+ return list(self._modules.values())[idx]
35
+
36
+ def __len__(self):
37
+ return len(self._modules)
38
+
39
+ def __iter__(self):
40
+ return iter(self._modules.values())
41
+
42
+ def __repr__(self):
43
+ lines = ['Sequential(']
44
+ for i, (name, m) in enumerate(self._modules.items()):
45
+ lines.append(f' ({name}): {repr(m)}')
46
+ lines.append(')')
47
+ return '\n'.join(lines)
@@ -0,0 +1,12 @@
1
+ from vectorphile.optim.sgd import SGD
2
+ from vectorphile.optim.adam import Adam, AdamW
3
+ from vectorphile.optim.rmsprop import RMSprop
4
+ from vectorphile.optim.adagrad import Adagrad
5
+ from vectorphile.optim.lr_scheduler import (
6
+ StepLR, ExponentialLR, CosineAnnealingLR, LinearLR, ReduceLROnPlateau,
7
+ )
8
+
9
+ __all__ = [
10
+ 'SGD', 'Adam', 'AdamW', 'RMSprop', 'Adagrad',
11
+ 'StepLR', 'ExponentialLR', 'CosineAnnealingLR', 'LinearLR', 'ReduceLROnPlateau',
12
+ ]
@@ -0,0 +1,31 @@
1
+ """Adagrad optimizer."""
2
+ import numpy as np
3
+ from vectorphile.optim.optimizer import Optimizer
4
+
5
+
6
+ class Adagrad(Optimizer):
7
+ def __init__(self, params, lr=0.01, lr_decay=0.0, weight_decay=0.0, eps=1e-10):
8
+ defaults = dict(lr=lr, lr_decay=lr_decay, weight_decay=weight_decay, eps=eps)
9
+ super().__init__(params, defaults)
10
+ self.state = {}
11
+
12
+ def step(self):
13
+ for group in self.param_groups:
14
+ lr = group['lr']
15
+ lr_decay = group['lr_decay']
16
+ wd = group['weight_decay']
17
+ eps = group['eps']
18
+ for p in group['params']:
19
+ if p.grad is None:
20
+ continue
21
+ g = p.grad._data.copy()
22
+ if wd != 0:
23
+ g += wd * p._data
24
+ pid = id(p)
25
+ if pid not in self.state:
26
+ self.state[pid] = {'step': 0, 'sum': np.zeros_like(p._data)}
27
+ s = self.state[pid]
28
+ s['step'] += 1
29
+ clr = lr / (1 + (s['step'] - 1) * lr_decay)
30
+ s['sum'] += g**2
31
+ p._data -= clr * g / (np.sqrt(s['sum']) + eps)