PyIntell 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.
pyintell/layers.py ADDED
@@ -0,0 +1,75 @@
1
+ """Neural-network layer and activation primitives."""
2
+
3
+ import numpy as np
4
+
5
+
6
+ def linear(x, weights, bias=None):
7
+ result = np.asarray(x) @ np.asarray(weights)
8
+ return result if bias is None else result + bias
9
+
10
+
11
+ def relu(x): return np.maximum(x, 0)
12
+ def leaky_relu(x, negative_slope=0.01):
13
+ x = np.asarray(x); return np.where(x >= 0, x, negative_slope * x)
14
+ def gelu(x):
15
+ x = np.asarray(x); return 0.5 * x * (1.0 + np.tanh(np.sqrt(2 / np.pi) * (x + 0.044715 * x**3)))
16
+ def sigmoid(x): return 1.0 / (1.0 + np.exp(-np.asarray(x)))
17
+ def tanh(x): return np.tanh(x)
18
+ def softplus(x): return np.log1p(np.exp(-np.abs(x))) + np.maximum(x, 0)
19
+ def silu(x): return np.asarray(x) * sigmoid(x)
20
+ def swish(x): return silu(x)
21
+ def mish(x): return np.asarray(x) * np.tanh(softplus(x))
22
+
23
+
24
+ def softmax(x, axis=-1):
25
+ x = np.asarray(x); shifted = x - np.max(x, axis=axis, keepdims=True)
26
+ values = np.exp(shifted); return values / np.sum(values, axis=axis, keepdims=True)
27
+
28
+
29
+ def log_softmax(x, axis=-1):
30
+ x = np.asarray(x); shifted = x - np.max(x, axis=axis, keepdims=True)
31
+ return shifted - np.log(np.sum(np.exp(shifted), axis=axis, keepdims=True))
32
+
33
+
34
+ def activation(x, name="gelu"):
35
+ functions = {"relu": relu, "gelu": gelu, "tanh": tanh, "sigmoid": sigmoid,
36
+ "leaky_relu": leaky_relu, "softplus": softplus, "silu": silu,
37
+ "swish": swish, "mish": mish}
38
+ if name not in functions: raise ValueError(f"unknown activation: {name}")
39
+ return functions[name](x)
40
+
41
+
42
+ def layer_norm(x, eps=1e-5):
43
+ x = np.asarray(x); mean = np.mean(x, axis=-1, keepdims=True); variance = np.var(x, axis=-1, keepdims=True)
44
+ return (x - mean) / np.sqrt(variance + eps)
45
+
46
+
47
+ def batch_norm(x, eps=1e-5):
48
+ x = np.asarray(x); mean = np.mean(x, axis=0, keepdims=True); variance = np.var(x, axis=0, keepdims=True)
49
+ return (x - mean) / np.sqrt(variance + eps)
50
+
51
+
52
+ def rms_norm(x, eps=1e-8):
53
+ x = np.asarray(x); return x / np.sqrt(np.mean(x * x, axis=-1, keepdims=True) + eps)
54
+
55
+
56
+ def dropout(x, probability=0.0, training=True):
57
+ if not training or probability <= 0: return x
58
+ if probability >= 1: raise ValueError("dropout probability must be less than 1")
59
+ mask = (np.random.random(np.asarray(x).shape) >= probability) / (1 - probability)
60
+ return np.asarray(x) * mask
61
+
62
+
63
+ def flatten_layer(x): return np.asarray(x).reshape(-1)
64
+
65
+
66
+ def feedforward(x, hidden_size=None, activation_name="gelu"):
67
+ width = x.shape[-1] if hidden_size is None else hidden_size; input_size = x.shape[-1]
68
+ w1 = np.random.randn(input_size, width).astype(np.float32) * 0.02
69
+ w2 = np.random.randn(width, input_size).astype(np.float32) * 0.02
70
+ return linear(activation(linear(x, w1), activation_name), w2)
71
+
72
+
73
+ def mlp(x, hidden_size=None, activation_name="gelu"): return feedforward(x, hidden_size, activation_name)
74
+ def residual(x, function): return np.asarray(x) + function(x)
75
+ def residual_block(x, function): return residual(x, function)
pyintell/loss.py ADDED
@@ -0,0 +1,39 @@
1
+ """Loss functions."""
2
+
3
+ import numpy as np
4
+
5
+
6
+ def cross_entropy(logits, targets):
7
+ logits = np.asarray(logits, dtype=np.float64); targets = np.asarray(targets, dtype=np.int64)
8
+ shifted = logits - np.max(logits, axis=-1, keepdims=True)
9
+ log_probs = shifted - np.log(np.sum(np.exp(shifted), axis=-1, keepdims=True))
10
+ flat = log_probs.reshape(-1, log_probs.shape[-1]); target_flat = targets.reshape(-1)
11
+ return float(-np.mean(flat[np.arange(target_flat.size), target_flat]))
12
+
13
+ def binary_cross_entropy(predictions, targets, eps=1e-12):
14
+ p = np.clip(np.asarray(predictions), eps, 1-eps); t = np.asarray(targets)
15
+ return float(-np.mean(t*np.log(p) + (1-t)*np.log(1-p)))
16
+
17
+ def mse(predictions, targets): return float(np.mean((np.asarray(predictions) - np.asarray(targets)) ** 2))
18
+ def mae(predictions, targets): return float(np.mean(np.abs(np.asarray(predictions) - np.asarray(targets))))
19
+ def huber_loss(predictions, targets, delta=1.0):
20
+ error = np.abs(np.asarray(predictions) - np.asarray(targets)); quadratic = np.minimum(error, delta)
21
+ linear = error - quadratic; return float(np.mean(0.5*quadratic**2 + delta*linear))
22
+
23
+ def kl_divergence(p, q, eps=1e-12):
24
+ p = np.clip(np.asarray(p), eps, None); q = np.clip(np.asarray(q), eps, None)
25
+ return float(np.sum(p * (np.log(p) - np.log(q))))
26
+
27
+ def contrastive_loss(a, b, target, margin=1.0):
28
+ distance = np.linalg.norm(np.asarray(a)-np.asarray(b), axis=-1)
29
+ target = np.asarray(target); return float(np.mean(target*distance**2 + (1-target)*np.maximum(0, margin-distance)**2))
30
+
31
+ def label_smoothing(targets, classes, smoothing=0.1):
32
+ result = np.full((len(targets), classes), smoothing/(classes-1), dtype=np.float32)
33
+ result[np.arange(len(targets)), np.asarray(targets)] = 1-smoothing
34
+ return result
35
+
36
+ def loss(predictions, targets, kind="cross_entropy"):
37
+ functions = {"cross_entropy": cross_entropy, "mse": mse, "mae": mae, "binary_cross_entropy": binary_cross_entropy, "huber": huber_loss}
38
+ if kind not in functions: raise ValueError(f"unknown loss: {kind}")
39
+ return functions[kind](predictions, targets)
pyintell/model.py ADDED
@@ -0,0 +1,224 @@
1
+ """Trainable language-model object returned by pyintell.build."""
2
+
3
+ import time
4
+ import numpy as np
5
+
6
+ from .embeddings import embedding, positional_embedding
7
+ from .transformer import transformer, init_block
8
+ from .tokenization import encode, decode
9
+ from .generation import sample
10
+ from .optim import Optimizer
11
+ from .focus import build_focus_config, normalize_focus
12
+
13
+
14
+ class Model:
15
+ """A compact, persistent NumPy Transformer language model.
16
+
17
+ Alpha note: this class provides the framework and introspection APIs. Its
18
+ training implementation is intentionally simple and is not production AI
19
+ training yet.
20
+ """
21
+ def __init__(self, vocab, reverse_vocab, dataset, parameters, focus, dtype, settings):
22
+ self.vocab = vocab
23
+ self.reverse_vocab = reverse_vocab
24
+ self.dataset = dataset
25
+ self.requested_parameters = int(parameters)
26
+ self.parameters = self.requested_parameters
27
+ self.focus = tuple(normalize_focus(focus))
28
+ self.requested_dtype = str(dtype)
29
+ storage_dtype = {"bfloat16": "float16", "int4": "uint8"}.get(self.requested_dtype, self.requested_dtype)
30
+ self.dtype = np.dtype(storage_dtype)
31
+ self.settings = dict(settings)
32
+ self.focus_config = dict(self.settings.get("focus_config", build_focus_config(self.focus)))
33
+ self.focus_priorities = dict(self.focus_config.get("priorities", {}))
34
+ self.model_name = str(self.settings.get("model_name", "pyintell_model"))
35
+ self.platform = str(self.settings.get("platform", "auto"))
36
+ self.device = self.settings.get("device")
37
+ self.layers = int(self.settings["layers"])
38
+ self.heads = int(self.settings["heads"])
39
+ self.embedding_size = int(self.settings["embedding_size"])
40
+ self.context_length = int(self.settings["context_length"])
41
+ self.hidden_size = int(self.settings["hidden_size"])
42
+ seed = self.settings.get("seed", None)
43
+ self.rng = np.random.default_rng(seed)
44
+ self.embedding_weights = embedding(np.arange(len(vocab)), dimensions=self.embedding_size,
45
+ vocab_size=len(vocab)).astype(self.dtype)
46
+ self.position_weights = positional_embedding(self.context_length, self.embedding_size).astype(self.dtype)
47
+ self.blocks = [init_block(self.embedding_size, self.heads, self.hidden_size, self.rng)
48
+ for _ in range(self.layers)]
49
+ self.output_weights = (self.rng.standard_normal((self.embedding_size, len(vocab))) *
50
+ (1.0 / np.sqrt(max(self.embedding_size, 1)))).astype(np.float32)
51
+ self.output_bias = np.zeros(len(vocab), dtype=np.float32)
52
+ self.training_steps = 0
53
+ self.training_history = []
54
+ self.optimizer = None
55
+ self.created_at = time.time()
56
+
57
+ def _named_parameters(self):
58
+ params = {"embedding": self.embedding_weights, "position": self.position_weights,
59
+ "output": self.output_weights, "output_bias": self.output_bias}
60
+ for i, block in enumerate(self.blocks):
61
+ for name, value in block.items():
62
+ params[f"blocks.{i}.{name}"] = value
63
+ return params
64
+
65
+ def named_parameters(self):
66
+ return self._named_parameters()
67
+
68
+ def parameter_count(self):
69
+ return int(sum(value.size for value in self._named_parameters().values()))
70
+
71
+ def parameters_info(self):
72
+ return {name: {"shape": tuple(value.shape), "dtype": str(value.dtype), "count": int(value.size)}
73
+ for name, value in self._named_parameters().items()}
74
+
75
+ def get_focus(self):
76
+ """Return the normalized capabilities selected for this model."""
77
+ return list(self.focus)
78
+
79
+ def set_focus(self, focus):
80
+ """Change focus metadata without pretending to retrain the model.
81
+
82
+ In 0.1.x this does not modify existing weights. Future training systems
83
+ can use the resulting configuration to implement specialization.
84
+ """
85
+ config = build_focus_config(focus)
86
+ self.focus = tuple(config["focuses"])
87
+ self.focus_config = config
88
+ self.focus_priorities = dict(config["priorities"])
89
+ self.settings["focus_config"] = config
90
+ return self.get_focus()
91
+
92
+ def focus_scores(self):
93
+ """Return normalized capability priorities produced by the builder."""
94
+ return dict(self.focus_priorities)
95
+
96
+ def focus_info(self):
97
+ """Return focus names and their current alpha priority metadata."""
98
+ ranked = sorted(self.focus_priorities.items(), key=lambda item: (-item[1], item[0]))
99
+ return {
100
+ "focuses": self.get_focus(),
101
+ "priorities": self.focus_scores(),
102
+ "top_capabilities": [name for name, _ in ranked[:5]],
103
+ }
104
+
105
+ def forward(self, token_ids):
106
+ ids = np.asarray(token_ids, dtype=np.int64)
107
+ if ids.ndim != 1:
108
+ raise ValueError("token_ids must be a one-dimensional sequence")
109
+ if len(ids) == 0:
110
+ raise ValueError("token_ids must not be empty")
111
+ if len(ids) > self.context_length:
112
+ raise ValueError("sequence exceeds model context length")
113
+ if ids.min() < 0 or ids.max() >= len(self.vocab):
114
+ raise ValueError("token ID outside vocabulary")
115
+ x = self.embedding_weights[ids].astype(np.float32) + self.position_weights[:len(ids)].astype(np.float32)
116
+ return transformer(x, layers=self.layers, heads=self.heads, hidden_size=self.hidden_size,
117
+ causal=True, weights=self.blocks)
118
+
119
+ def logits(self, token_ids):
120
+ return self.forward(token_ids) @ self.output_weights + self.output_bias
121
+
122
+ @staticmethod
123
+ def _softmax(logits):
124
+ shifted = logits - np.max(logits, axis=-1, keepdims=True)
125
+ probabilities = np.exp(np.clip(shifted, -80.0, 80.0))
126
+ return probabilities / np.maximum(np.sum(probabilities, axis=-1, keepdims=True), 1e-12)
127
+
128
+ def _examples(self, dataset):
129
+ for sequence in dataset:
130
+ ids = list(sequence)
131
+ if len(ids) < 2:
132
+ continue
133
+ for end in range(1, len(ids)):
134
+ start = max(0, end - self.context_length)
135
+ yield ids[start:end], ids[end]
136
+
137
+ def evaluate(self, dataset=None, **kwargs):
138
+ data = self.dataset if dataset is None else dataset
139
+ total_loss = 0.0; correct = 0; count = 0
140
+ for inputs, target in self._examples(data):
141
+ probabilities = self._softmax(self.logits(inputs)[-1])
142
+ target = int(target)
143
+ if target < 0 or target >= len(self.vocab):
144
+ raise ValueError("target token ID outside vocabulary")
145
+ total_loss -= float(np.log(np.clip(probabilities[target], 1e-12, 1.0)))
146
+ correct += int(np.argmax(probabilities) == target)
147
+ count += 1
148
+ if count == 0:
149
+ raise ValueError("dataset must contain sequences with at least two token IDs")
150
+ loss = total_loss / count
151
+ return {"loss": loss, "accuracy": correct / count, "samples": count,
152
+ "perplexity": float(np.exp(min(loss, 20.0)))}
153
+
154
+ def train(self, dataset=None, epochs=1, learning_rate=None, optimizer=None, **kwargs):
155
+ """Train the model's currently supported trainable output head."""
156
+ data = self.dataset if dataset is None else dataset
157
+ epochs = int(epochs if epochs is not None else self.settings.get("epochs", 1))
158
+ learning_rate = float(learning_rate if learning_rate is not None else self.settings.get("learning_rate", 3e-4))
159
+ optimizer = optimizer or self.settings.get("optimizer", "adamw")
160
+ if epochs < 1:
161
+ raise ValueError("epochs must be at least 1")
162
+ examples = list(self._examples(data))
163
+ if not examples:
164
+ raise ValueError("dataset must contain sequences with at least two token IDs")
165
+ trainable = {"output": self.output_weights, "output_bias": self.output_bias}
166
+ if self.optimizer is None or self.optimizer.kind != str(optimizer).lower() or self.optimizer.lr != learning_rate:
167
+ self.optimizer = Optimizer(trainable, kind=optimizer, learning_rate=learning_rate,
168
+ weight_decay=float(kwargs.get("weight_decay", self.settings.get("weight_decay", 0.0))),
169
+ clip_norm=kwargs.get("clip_norm", self.settings.get("gradient_clip")))
170
+ before = self.evaluate(data)
171
+ history = []
172
+ for _ in range(epochs):
173
+ gradients = {"output": np.zeros_like(self.output_weights), "output_bias": np.zeros_like(self.output_bias)}
174
+ total_loss = 0.0
175
+ for inputs, target in examples:
176
+ hidden = self.forward(inputs)[-1].astype(np.float32)
177
+ probabilities = self._softmax(hidden @ self.output_weights + self.output_bias)
178
+ target = int(target)
179
+ total_loss -= float(np.log(np.clip(probabilities[target], 1e-12, 1.0)))
180
+ probabilities[target] -= 1.0
181
+ gradients["output"] += np.outer(hidden, probabilities)
182
+ gradients["output_bias"] += probabilities
183
+ gradients = {k: v / len(examples) for k, v in gradients.items()}
184
+ self.optimizer.step(gradients)
185
+ self.training_steps += 1
186
+ history.append(total_loss / len(examples))
187
+ after = self.evaluate(data)
188
+ record = {"loss": history[-1], "loss_history": history, "epochs": epochs, "samples": len(examples),
189
+ "steps": self.training_steps, "before": before, "after": after,
190
+ "loss_decreased": after["loss"] < before["loss"],
191
+ "accuracy_improved": after["accuracy"] > before["accuracy"]}
192
+ self.training_history.append(record)
193
+ return record
194
+
195
+ def generate(self, prompt, max_tokens=50, temperature=1.0, top_k=None, **kwargs):
196
+ if isinstance(max_tokens, bool) or not isinstance(max_tokens, (int, np.integer)):
197
+ raise TypeError("max_tokens must be an integer")
198
+ max_tokens = int(max_tokens)
199
+ if max_tokens < 0:
200
+ raise ValueError("max_tokens must not be negative")
201
+ if isinstance(prompt, str):
202
+ unknown_token = "<unk>" if "<unk>" in self.vocab else None
203
+ ids = encode(prompt, self.vocab, unknown_token=unknown_token)
204
+ if any(token_id is None for token_id in ids):
205
+ raise ValueError("prompt contains unknown tokens and vocabulary has no '<unk>' token")
206
+ else:
207
+ ids = list(prompt)
208
+ if not ids:
209
+ raise ValueError("prompt must contain at least one known token")
210
+ for _ in range(max_tokens):
211
+ context = ids[-self.context_length:]
212
+ ids.append(sample(self.logits(context)[-1], temperature=temperature, top_k=top_k))
213
+ return decode(ids, self.reverse_vocab) if isinstance(prompt, str) else ids
214
+
215
+ def summary(self):
216
+ actual = self.parameter_count()
217
+ return {"model_name": self.model_name, "platform": self.platform, "device": self.device,
218
+ "parameters": actual, "requested_parameters": self.requested_parameters,
219
+ "focus": list(self.focus), "focus_scores": self.focus_scores(),
220
+ "dtype": self.requested_dtype, "layers": self.layers,
221
+ "heads": self.heads, "embedding_size": self.embedding_size, "hidden_size": self.hidden_size,
222
+ "context_length": self.context_length, "vocab_size": len(self.vocab),
223
+ "training_steps": self.training_steps, "training_history_length": len(self.training_history),
224
+ "settings": dict(self.settings)}
pyintell/optim.py ADDED
@@ -0,0 +1,94 @@
1
+ """Practical NumPy optimizers for pyintell parameters."""
2
+
3
+ import numpy as np
4
+
5
+
6
+ class Optimizer:
7
+ """Stateful optimizer supporting SGD, Adam, AdamW, RMSProp and Adagrad."""
8
+ def __init__(self, parameters, kind="adamw", learning_rate=3e-4, weight_decay=0.0,
9
+ beta1=0.9, beta2=0.999, eps=1e-8, momentum=0.0, clip_norm=None):
10
+ self.parameters = parameters
11
+ self.kind = str(kind).lower()
12
+ self.lr = float(learning_rate)
13
+ self.weight_decay = float(weight_decay)
14
+ self.beta1, self.beta2, self.eps = float(beta1), float(beta2), float(eps)
15
+ self.momentum, self.clip_norm = float(momentum), clip_norm
16
+ self.step_count = 0
17
+ self.m = {}
18
+ self.v = {}
19
+
20
+ def step(self, gradients):
21
+ """Apply one update to a dict of named NumPy arrays."""
22
+ self.step_count += 1
23
+ total = 0.0
24
+ for name, grad in gradients.items():
25
+ if name not in self.parameters:
26
+ continue
27
+ g = np.asarray(grad, dtype=np.float32)
28
+ if self.clip_norm is not None:
29
+ total += float(np.sum(g * g))
30
+ scale = 1.0
31
+ if self.clip_norm is not None:
32
+ norm = np.sqrt(total)
33
+ if norm > float(self.clip_norm):
34
+ scale = float(self.clip_norm) / max(norm, 1e-12)
35
+ for name, grad in gradients.items():
36
+ if name not in self.parameters:
37
+ continue
38
+ p = self.parameters[name]
39
+ g = np.asarray(grad, dtype=np.float32) * scale
40
+ if self.kind == "sgd":
41
+ if self.momentum:
42
+ self.m[name] = self.momentum * self.m.get(name, np.zeros_like(g)) + g
43
+ g = self.m[name]
44
+ update = g
45
+ elif self.kind in ("adam", "adamw"):
46
+ m = self.m[name] = self.beta1 * self.m.get(name, np.zeros_like(g)) + (1 - self.beta1) * g
47
+ v = self.v[name] = self.beta2 * self.v.get(name, np.zeros_like(g)) + (1 - self.beta2) * (g * g)
48
+ mh = m / (1 - self.beta1 ** self.step_count)
49
+ vh = v / (1 - self.beta2 ** self.step_count)
50
+ update = mh / (np.sqrt(vh) + self.eps)
51
+ if self.kind == "adamw" and self.weight_decay:
52
+ p *= (1.0 - self.lr * self.weight_decay)
53
+ elif self.kind == "rmsprop":
54
+ v = self.v[name] = 0.99 * self.v.get(name, np.zeros_like(g)) + 0.01 * (g * g)
55
+ update = g / (np.sqrt(v) + self.eps)
56
+ elif self.kind == "adagrad":
57
+ v = self.v[name] = self.v.get(name, np.zeros_like(g)) + g * g
58
+ update = g / (np.sqrt(v) + self.eps)
59
+ else:
60
+ raise ValueError(f"unknown optimizer: {self.kind}")
61
+ if self.kind != "adamw" and self.weight_decay:
62
+ update = update + self.weight_decay * p
63
+ p[...] = p - self.lr * update
64
+ return self
65
+
66
+ def zero_grad(self):
67
+ return {name: np.zeros_like(value, dtype=np.float32) for name, value in self.parameters.items()}
68
+
69
+ def state_dict(self):
70
+ return {"kind": self.kind, "learning_rate": self.lr, "weight_decay": self.weight_decay,
71
+ "step": self.step_count, "m": self.m, "v": self.v}
72
+
73
+
74
+ def optimizer(parameters=None, kind="adamw", learning_rate=3e-4, weight_decay=0.0, **kwargs):
75
+ return Optimizer(parameters or {}, kind, learning_rate, weight_decay, **kwargs)
76
+
77
+
78
+ def sgd(parameters=None, learning_rate=1e-2, **kwargs): return optimizer(parameters, "sgd", learning_rate, **kwargs)
79
+ def adam(parameters=None, learning_rate=3e-4, **kwargs): return optimizer(parameters, "adam", learning_rate, **kwargs)
80
+ def adamw(parameters=None, learning_rate=3e-4, **kwargs): return optimizer(parameters, "adamw", learning_rate, **kwargs)
81
+ def rmsprop(parameters=None, learning_rate=1e-3, **kwargs): return optimizer(parameters, "rmsprop", learning_rate, **kwargs)
82
+ def adagrad(parameters=None, learning_rate=1e-2, **kwargs): return optimizer(parameters, "adagrad", learning_rate, **kwargs)
83
+
84
+ def update_weights(weights, gradients, learning_rate=3e-4, weight_decay=0.0):
85
+ return np.asarray(weights) - learning_rate * (np.asarray(gradients) + weight_decay * np.asarray(weights))
86
+
87
+ def step(weights, gradients, learning_rate=3e-4, weight_decay=0.0):
88
+ return update_weights(weights, gradients, learning_rate, weight_decay)
89
+
90
+ def zero_grad(gradients):
91
+ return {k: np.zeros_like(v) for k, v in gradients.items()} if isinstance(gradients, dict) else np.zeros_like(gradients)
92
+
93
+ def learning_rate(optimizer_state):
94
+ return float(optimizer_state.lr if hasattr(optimizer_state, "lr") else optimizer_state.get("learning_rate", 0.0))
@@ -0,0 +1,25 @@
1
+ """Simple NumPy quantization and compression helpers."""
2
+
3
+ import numpy as np
4
+
5
+
6
+ def quantize(x, bits=8):
7
+ x = np.asarray(x, dtype=np.float32)
8
+ if bits == 8:
9
+ scale = max(float(np.max(np.abs(x))) / 127.0, 1e-12); return np.round(x / scale).astype(np.int8), scale
10
+ if bits == 4:
11
+ scale = max(float(np.max(np.abs(x))) / 7.0, 1e-12); return np.clip(np.round(x / scale), -8, 7).astype(np.int8), scale
12
+ raise ValueError("bits must be 8 or 4")
13
+
14
+ def dequantize(values, scale): return np.asarray(values, dtype=np.float32) * scale
15
+ def int8(x): return quantize(x, 8)
16
+ def int4(x): return quantize(x, 4)
17
+ def float16(x): return np.asarray(x, dtype=np.float16)
18
+ def bfloat16(x): return np.asarray(x, dtype=np.float32)
19
+ def prune(x, threshold=0.0): return np.where(np.abs(x) < threshold, 0, x)
20
+ def sparsify(x, threshold=0.0): return prune(x, threshold)
21
+ def compress(x): return np.asarray(x).tobytes()
22
+ def decompress(data, dtype=np.float32): return np.frombuffer(data, dtype=dtype)
23
+ def low_rank(x, rank):
24
+ u, s, vh = np.linalg.svd(np.asarray(x), full_matrices=False); return u[:, :rank], s[:rank], vh[:rank, :]
25
+ def factorize(x, rank): return low_rank(x, rank)
pyintell/scheduling.py ADDED
@@ -0,0 +1,16 @@
1
+ """Learning-rate and training scheduling helpers."""
2
+
3
+ import math
4
+
5
+
6
+ def learning_rate(initial, step=0): return float(initial)
7
+ def lr_scheduler(initial, kind="constant", total_steps=1, step=0):
8
+ if kind == "constant": return float(initial)
9
+ if kind == "linear": return linear_decay(initial, total_steps, step)
10
+ if kind == "cosine": return cosine_decay(initial, total_steps, step)
11
+ raise ValueError(f"unknown scheduler: {kind}")
12
+
13
+ def constant_lr(initial, step=0): return float(initial)
14
+ def linear_decay(initial, total_steps, step): return max(0.0, initial * (1.0 - min(step, total_steps) / max(total_steps, 1)))
15
+ def cosine_decay(initial, total_steps, step): return initial * 0.5 * (1 + math.cos(math.pi * min(step, total_steps) / max(total_steps, 1)))
16
+ def warmup(initial, warmup_steps, step): return initial * min(1.0, (step + 1) / max(warmup_steps, 1))