wsbuilder 1.2.13__tar.gz → 1.3.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.
- {wsbuilder-1.2.13/src/wsbuilder.egg-info → wsbuilder-1.3.0}/PKG-INFO +1 -1
- wsbuilder-1.3.0/docs/ai-predict.md +244 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/ia.md +15 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/pyproject.toml +1 -1
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/__init__.py +11 -1
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/ia.py +8 -0
- wsbuilder-1.3.0/src/wsbuilder/ia_audio.py +93 -0
- wsbuilder-1.3.0/src/wsbuilder/ia_text.py +100 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0/src/wsbuilder.egg-info}/PKG-INFO +1 -1
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder.egg-info/SOURCES.txt +3 -0
- wsbuilder-1.3.0/tests/test_ia_text_audio.py +165 -0
- wsbuilder-1.2.13/docs/ai-predict.md +0 -139
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/LICENSE +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/MANIFEST.in +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/README.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/404.html +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/CNAME +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/advanced.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/api-map.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/app-http.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/architecture.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/assets/wsbuilder-mark.svg +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/beginners.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/cache-security.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/cache.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/capabilities.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/data-orm.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/dns.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/full-project.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/getting-started.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/help/index.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/http-versions.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/http.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/index.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/install.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/intermediate.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/mdns.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/network-edge.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/observability-tasks.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/observability.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/persistence.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/proxy.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/reference/index.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/security.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/stylesheets/extra.css +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/tasks.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/tls-certificates.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/docs/websocket.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/examples/full-project/.gitignore +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/examples/full-project/README.md +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/examples/full-project/app.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/examples/full-project/pyproject.toml +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/examples/full-project/upstream.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/mkdocs.yml +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/setup.cfg +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/__main__.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/_coerce.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/_pragma.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/app.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/cache.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/caches.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/constants.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/cookies.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/db_replicas.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/dns.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/framework.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/headers.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/hpack.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/http.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/http1.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/http2.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/http3.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/http3_server.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/http3_websocket.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/logs.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/mdns.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/metrics.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/orm.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/pki.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/predicts.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/proxyi.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/qpack.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/quic/__init__.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/quic/address.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/quic/connection.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/quic/crypto.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/quic/frames.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/quic/packet.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/quic/recovery.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/quic/tls.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/quic/varint.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/security.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/server.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/tasks.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder/ws.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder.egg-info/dependency_links.txt +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder.egg-info/entry_points.txt +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder.egg-info/requires.txt +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/src/wsbuilder.egg-info/top_level.txt +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_app_threads.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_cache.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_caches.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_contributing_policy.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_demo_monitor.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_dns.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_docs.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_documentation_files.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_example_full_project.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_headers_cookies.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_hpack.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_http.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_http1.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_http2.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_http3.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_http3_server.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_http3_streaming.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_http3_streaming_udp.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_ia.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_logs.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_mdns.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_metrics.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_optional_extras.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_orm.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_orm_replicas.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_pki.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_pr_identity_policy.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_predicts.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_proxyi.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_proxyi_routing.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_quic_address.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_quic_crypto.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_quic_protocol.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_quic_recovery.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_quic_tls.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_security.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_server.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_server_errors.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_tasks.py +0 -0
- {wsbuilder-1.2.13 → wsbuilder-1.3.0}/tests/test_ws.py +0 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# IA y Prediccion
|
|
2
|
+
|
|
3
|
+
## `DataSet`
|
|
4
|
+
|
|
5
|
+
`DataSet` es el contenedor basico para pares `X` y `Y`.
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
from wsbuilder import DataSet
|
|
9
|
+
|
|
10
|
+
dataset = DataSet(
|
|
11
|
+
[[1, 2], [2, 3], [3, 4], [4, 5]],
|
|
12
|
+
[[0], [1], [1], [1]],
|
|
13
|
+
)
|
|
14
|
+
train, test = dataset.split(train_ratio=0.75, shuffle=False)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Metodos utiles:
|
|
18
|
+
|
|
19
|
+
- `sample_count`, `feature_count`, `target_count`
|
|
20
|
+
- `copy()`, `as_xy()`
|
|
21
|
+
- `shuffle()`, `split()`, `batches()`
|
|
22
|
+
- `describe_features()`, `describe_targets()`
|
|
23
|
+
- `standardize()`, `normalize_min_max()`
|
|
24
|
+
|
|
25
|
+
`split()` puede mezclar datos con `shuffle=True` y `seed` para resultados
|
|
26
|
+
repetibles. `batches()` permite entrenar por lotes.
|
|
27
|
+
|
|
28
|
+
## `NeuralNetwork`
|
|
29
|
+
|
|
30
|
+
La red neuronal es completamente Python puro.
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from wsbuilder import NeuralNetwork
|
|
34
|
+
|
|
35
|
+
net = NeuralNetwork(
|
|
36
|
+
seed=7,
|
|
37
|
+
learning_rate=0.3,
|
|
38
|
+
loss="binary_cross_entropy",
|
|
39
|
+
task="classification",
|
|
40
|
+
)
|
|
41
|
+
net.add_dense(6, input_size=2, activation="tanh")
|
|
42
|
+
net.add_dense(1, activation="sigmoid")
|
|
43
|
+
history = net.fit(
|
|
44
|
+
[[0, 0], [0, 1], [1, 0], [1, 1]],
|
|
45
|
+
[[0], [1], [1], [0]],
|
|
46
|
+
epochs=5000,
|
|
47
|
+
batch_size=4,
|
|
48
|
+
shuffle=False,
|
|
49
|
+
)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Capacidades mas visibles:
|
|
53
|
+
|
|
54
|
+
- `fit` para regresion.
|
|
55
|
+
- `fit_classification` para etiquetas simbolicas.
|
|
56
|
+
- `predict`, `predict_batch`, `predict_proba`, `predict_class`.
|
|
57
|
+
- `evaluate`, `accuracy`, `classification_metrics`.
|
|
58
|
+
- `predict_with_metrics` para reportes de error y confianza.
|
|
59
|
+
|
|
60
|
+
Activaciones soportadas por la red:
|
|
61
|
+
|
|
62
|
+
- `linear`
|
|
63
|
+
- `sigmoid`
|
|
64
|
+
- `tanh`
|
|
65
|
+
- `relu`
|
|
66
|
+
- `leaky_relu`
|
|
67
|
+
- `softmax`
|
|
68
|
+
|
|
69
|
+
Losses soportadas:
|
|
70
|
+
|
|
71
|
+
- `mse`
|
|
72
|
+
- `binary_cross_entropy`
|
|
73
|
+
- `categorical_cross_entropy`
|
|
74
|
+
|
|
75
|
+
## `DenseLayer`
|
|
76
|
+
|
|
77
|
+
`DenseLayer` permite definir capas manuales y se usa como bloque interno del
|
|
78
|
+
`NeuralNetwork`.
|
|
79
|
+
|
|
80
|
+
Tambien puedes crear capas con `net.add_dense(units, input_size=..., activation=...)`,
|
|
81
|
+
que es la forma mas directa para usuarios de la libreria.
|
|
82
|
+
|
|
83
|
+
## Estadistica y error
|
|
84
|
+
|
|
85
|
+
Helpers exportados:
|
|
86
|
+
|
|
87
|
+
- `describe_data(values)`
|
|
88
|
+
- `evaluate_errors(expected, predicted, permissible_error=...)`
|
|
89
|
+
|
|
90
|
+
Devuelven `DataSummary` y `ErrorSummary`, ambos con la propiedad `uncertainty`.
|
|
91
|
+
|
|
92
|
+
## Entrenamiento en background
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from wsbuilder import NeuralNetwork, TaskManager, submit_training_task
|
|
96
|
+
|
|
97
|
+
X = [[0, 0], [0, 1], [1, 0], [1, 1]]
|
|
98
|
+
labels = ["no", "yes", "yes", "no"]
|
|
99
|
+
|
|
100
|
+
net = NeuralNetwork(
|
|
101
|
+
seed=7,
|
|
102
|
+
learning_rate=0.3,
|
|
103
|
+
loss="binary_cross_entropy",
|
|
104
|
+
task="classification",
|
|
105
|
+
)
|
|
106
|
+
net.add_dense(6, input_size=2, activation="tanh")
|
|
107
|
+
net.add_dense(1, activation="sigmoid")
|
|
108
|
+
|
|
109
|
+
tasks = TaskManager(max_concurrent=1)
|
|
110
|
+
task = submit_training_task(
|
|
111
|
+
tasks,
|
|
112
|
+
net,
|
|
113
|
+
X,
|
|
114
|
+
labels,
|
|
115
|
+
classification=True,
|
|
116
|
+
epochs=1000,
|
|
117
|
+
batch_size=4,
|
|
118
|
+
shuffle=False,
|
|
119
|
+
name="ia-train",
|
|
120
|
+
)
|
|
121
|
+
history = task.get(timeout=5)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Texto Unicode y tokens
|
|
125
|
+
|
|
126
|
+
`ByteTokenizer` representa texto como IDs de bytes UTF-8 entre 0 y 255. No
|
|
127
|
+
necesita vocabulario entrenado y conserva exactamente el texto valido, incluidos
|
|
128
|
+
acentos, arabe, chino, emoji y codigo fuente. No aplica normalizacion. Estos IDs
|
|
129
|
+
no son los tokens de un modelo externo ni una estimacion de su facturacion.
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from wsbuilder import ByteTokenizer
|
|
133
|
+
|
|
134
|
+
tokenizer = ByteTokenizer(max_bytes=1_000_000)
|
|
135
|
+
text = "Hola, 世界! مرحبا 👋\nprint('hola')"
|
|
136
|
+
tokens = tokenizer.encode(text)
|
|
137
|
+
assert tokenizer.decode(tokens) == text
|
|
138
|
+
assert tokenizer.vocab_size == 256
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
`encode` rechaza texto que exceda `max_bytes`; `decode` rechaza secuencias
|
|
142
|
+
demasiado largas, IDs no enteros o fuera de rango y bytes que no formen UTF-8
|
|
143
|
+
valido. Un token individual puede ser solo parte de un caracter multibyte:
|
|
144
|
+
decodifica secuencias completas. No pases IDs de longitud variable directamente
|
|
145
|
+
a una capa densa de dimension fija.
|
|
146
|
+
|
|
147
|
+
### Vectores de texto para la red
|
|
148
|
+
|
|
149
|
+
`HashingTextVectorizer` convierte ngramas de caracteres Unicode en vectores de
|
|
150
|
+
dimension fija usando un hash estable. Funciona tambien con idiomas que no
|
|
151
|
+
separan palabras con espacios; no necesita `fit` ni un vocabulario mutable.
|
|
152
|
+
Dos ngramas pueden caer en la misma posicion (colisiones). Estos vectores
|
|
153
|
+
describen patrones de caracteres, no son embeddings semanticos preentrenados.
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
from wsbuilder import DataSet, HashingTextVectorizer, NeuralNetwork
|
|
157
|
+
|
|
158
|
+
vectorizer = HashingTextVectorizer(n_features=64, ngram_range=(1, 3))
|
|
159
|
+
texts = ["hola", "buen dia", "adios", "hasta luego"]
|
|
160
|
+
X = vectorizer.transform_batch(texts)
|
|
161
|
+
dataset = DataSet(X, [[1], [1], [0], [0]])
|
|
162
|
+
|
|
163
|
+
model = NeuralNetwork(
|
|
164
|
+
seed=7, learning_rate=0.3,
|
|
165
|
+
loss="binary_cross_entropy", task="classification",
|
|
166
|
+
)
|
|
167
|
+
model.add_dense(8, input_size=64, activation="tanh")
|
|
168
|
+
model.add_dense(1, activation="sigmoid")
|
|
169
|
+
model.fit_classification(
|
|
170
|
+
dataset.X, ["saludo", "saludo", "despedida", "despedida"],
|
|
171
|
+
epochs=300, batch_size=4, shuffle=False,
|
|
172
|
+
)
|
|
173
|
+
label = model.predict_class(vectorizer.transform("hola"))
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Este conjunto diminuto ilustra la API; no demuestra comprension del lenguaje ni
|
|
177
|
+
generalizacion a otros idiomas. Conserva la misma configuracion del vectorizador
|
|
178
|
+
en entrenamiento e inferencia. El texto vacio produce un vector de ceros.
|
|
179
|
+
`max_chars` limita la longitud por texto (100 000 por defecto).
|
|
180
|
+
Por defecto se aplica `casefold` (`lowercase=True`), normalizacion Unicode NFC
|
|
181
|
+
y norma euclidea unitaria (`normalize=True`). NFC se aplica incluso con
|
|
182
|
+
`lowercase=False`; el tokenizador reversible no realiza estas transformaciones.
|
|
183
|
+
`n_features` admite 1 a 65 536 y los extremos de `ngram_range`, 1 a 8.
|
|
184
|
+
`transform_batch` no limita el numero de documentos: procesa tus colecciones
|
|
185
|
+
en lotes acotados.
|
|
186
|
+
|
|
187
|
+
## Audio WAV y caracteristicas
|
|
188
|
+
|
|
189
|
+
`load_wav` lee una ruta o un archivo binario seekable con WAV PCM entero de
|
|
190
|
+
8, 16, 24 o 32 bits. Convierte los canales a mono mediante su promedio: canales
|
|
191
|
+
en contrafase pueden cancelarse. No remuestrea ni decodifica MP3, AAC o audio
|
|
192
|
+
comprimido. No cierra el archivo proporcionado por el llamador.
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
from wsbuilder import AudioData, NeuralNetwork, audio_features, load_wav
|
|
196
|
+
|
|
197
|
+
audio = load_wav("grabacion.wav", max_frames=1_000_000, max_channels=8)
|
|
198
|
+
features = audio_features(audio)
|
|
199
|
+
# Orden: duracion en segundos, media, RMS, pico absoluto, cruces por cero.
|
|
200
|
+
assert len(features) == 5
|
|
201
|
+
|
|
202
|
+
silence = AudioData(samples=(0.0,) * 8000, sample_rate=8000)
|
|
203
|
+
assert audio_features(silence) == [1.0, 0.0, 0.0, 0.0, 0.0]
|
|
204
|
+
|
|
205
|
+
audio_model = NeuralNetwork(seed=7)
|
|
206
|
+
audio_model.add_dense(1, input_size=5, activation="linear")
|
|
207
|
+
# Entrenar audio_model.fit(...) con ejemplos y objetivos propios antes de inferir.
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Los limites de frames y canales se verifican antes de leer las muestras; los
|
|
211
|
+
archivos truncados se rechazan. Un frame contiene una muestra por canal, por lo
|
|
212
|
+
que un millon de frames equivale aproximadamente a 20,8 segundos a 48 kHz.
|
|
213
|
+
`AudioData` contiene una tupla de muestras mono finitas entre -1 y 1 y una
|
|
214
|
+
frecuencia entera en Hz positiva. `duration` devuelve la duracion en segundos;
|
|
215
|
+
una tupla vacia produce cinco caracteristicas cero. Los cruces por cero son
|
|
216
|
+
la fraccion de pares adyacentes que cambian de signo, considerando cero positivo.
|
|
217
|
+
Las caracteristicas resumen amplitud y duracion: no transcriben voz, reconocen
|
|
218
|
+
idiomas ni generan audio. Usa la misma frecuencia de muestreo para comparar
|
|
219
|
+
tasas de cruces por cero; divide los datos de entrenamiento y evaluacion por
|
|
220
|
+
grabacion para evitar evaluar sobre fragmentos del mismo audio.
|
|
221
|
+
|
|
222
|
+
Texto y audio pueden combinarse concatenando sus vectores y configurando
|
|
223
|
+
`input_size` con la suma de dimensiones. Entrena con ejemplos emparejados y
|
|
224
|
+
escalas consistentes; concatenar vectores no aporta por si solo comprension
|
|
225
|
+
multimodal. Los adaptadores no cambian las condiciones de concurrencia de
|
|
226
|
+
`NeuralNetwork`: evita compartir una instancia entre entrenamientos e
|
|
227
|
+
inferencias simultaneos.
|
|
228
|
+
|
|
229
|
+
## `Predictor`
|
|
230
|
+
|
|
231
|
+
`Predictor` es una utilidad matematica aparte para regresion lineal simple.
|
|
232
|
+
|
|
233
|
+
```python
|
|
234
|
+
from wsbuilder import Predictor
|
|
235
|
+
|
|
236
|
+
predictor = Predictor()
|
|
237
|
+
predictor.fit([[1], [2], [3], [4]], [[2], [4], [6], [8]])
|
|
238
|
+
prediction, sigma, lower, upper = predictor.predict([5])
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Es util cuando quieres prediccion rapida sin configurar una red neuronal.
|
|
242
|
+
|
|
243
|
+
Despues de `fit`, la instancia expone informacion como `rank`, coeficientes y
|
|
244
|
+
residuos internos usados para calcular desviacion e intervalo estimado.
|
|
@@ -13,6 +13,21 @@ WSBuilder incluye herramientas sencillas de data science y redes neuronales hech
|
|
|
13
13
|
- `describe_data`
|
|
14
14
|
- `evaluate_errors`
|
|
15
15
|
- `submit_training_task`
|
|
16
|
+
- `ByteTokenizer`
|
|
17
|
+
- `HashingTextVectorizer`
|
|
18
|
+
- `AudioData`, `load_wav`, `audio_features`
|
|
19
|
+
|
|
20
|
+
## Texto, tokens y audio
|
|
21
|
+
|
|
22
|
+
La IA acepta texto Unicode, incluidos idiomas sin espacios, emoji y codigo,
|
|
23
|
+
mediante tokens UTF-8 reversibles y vectores numericos de caracteres. Tambien
|
|
24
|
+
convierte audio WAV PCM en muestras mono y caracteristicas numericas para
|
|
25
|
+
entrenar o ejecutar las redes existentes. Todo funciona con la biblioteca
|
|
26
|
+
estandar, sin descargar modelos ni enviar datos a servicios externos.
|
|
27
|
+
|
|
28
|
+
Consulta los [ejemplos de texto y audio](ai-predict.md#texto-unicode-y-tokens).
|
|
29
|
+
Estos adaptadores preparan datos: no incorporan un modelo de conversacion,
|
|
30
|
+
traduccion, reconocimiento de voz, generacion de audio, imagen o video.
|
|
16
31
|
|
|
17
32
|
## Datos
|
|
18
33
|
|
|
@@ -63,13 +63,18 @@ from .framework import (
|
|
|
63
63
|
)
|
|
64
64
|
from .headers import add_header, get_header, has_header, normalize_header_name, set_header
|
|
65
65
|
from .ia import (
|
|
66
|
+
AudioData,
|
|
67
|
+
ByteTokenizer,
|
|
66
68
|
DataSet,
|
|
67
69
|
DataSummary,
|
|
68
70
|
DenseLayer,
|
|
69
71
|
ErrorSummary,
|
|
72
|
+
HashingTextVectorizer,
|
|
70
73
|
NeuralNetwork,
|
|
74
|
+
audio_features,
|
|
71
75
|
describe_data,
|
|
72
76
|
evaluate_errors,
|
|
77
|
+
load_wav,
|
|
73
78
|
submit_training_task,
|
|
74
79
|
)
|
|
75
80
|
from .logs import NDJSONLog, install_logs
|
|
@@ -152,7 +157,7 @@ def __dir__():
|
|
|
152
157
|
names |= _PKI_EXPORTS
|
|
153
158
|
return sorted(names)
|
|
154
159
|
|
|
155
|
-
__version__ = "1.
|
|
160
|
+
__version__ = "1.3.0"
|
|
156
161
|
|
|
157
162
|
__all__ = [
|
|
158
163
|
"App",
|
|
@@ -250,6 +255,11 @@ __all__ = [
|
|
|
250
255
|
"OptimizedDatabase",
|
|
251
256
|
"SQLite3OptimizationConfig",
|
|
252
257
|
"DataSet",
|
|
258
|
+
"AudioData",
|
|
259
|
+
"ByteTokenizer",
|
|
260
|
+
"HashingTextVectorizer",
|
|
261
|
+
"audio_features",
|
|
262
|
+
"load_wav",
|
|
253
263
|
"DataSummary",
|
|
254
264
|
"ErrorSummary",
|
|
255
265
|
"DenseLayer",
|
|
@@ -20,7 +20,15 @@ import random
|
|
|
20
20
|
from dataclasses import dataclass
|
|
21
21
|
from statistics import NormalDist
|
|
22
22
|
|
|
23
|
+
from .ia_audio import AudioData, audio_features, load_wav
|
|
24
|
+
from .ia_text import ByteTokenizer, HashingTextVectorizer
|
|
25
|
+
|
|
23
26
|
__all__ = [
|
|
27
|
+
"AudioData",
|
|
28
|
+
"ByteTokenizer",
|
|
29
|
+
"HashingTextVectorizer",
|
|
30
|
+
"audio_features",
|
|
31
|
+
"load_wav",
|
|
24
32
|
"DataSet",
|
|
25
33
|
"DataSummary",
|
|
26
34
|
"DenseLayer",
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""Bounded PCM WAV decoding and numeric mono audio features (no speech model)."""
|
|
2
|
+
|
|
3
|
+
import math
|
|
4
|
+
import os
|
|
5
|
+
import wave
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
|
|
8
|
+
from .ia_text import _positive_integer
|
|
9
|
+
|
|
10
|
+
__all__ = ["AudioData", "load_wav", "audio_features"]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class AudioData:
|
|
15
|
+
"""Mono samples in [-1, 1] and a positive sample rate in Hz.
|
|
16
|
+
|
|
17
|
+
Direct construction accepts a tuple only, so it never consumes an unbounded
|
|
18
|
+
iterable. ``load_wav`` supplies bounded decoded samples.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
samples: tuple[float, ...]
|
|
22
|
+
sample_rate: int
|
|
23
|
+
|
|
24
|
+
def __post_init__(self):
|
|
25
|
+
_positive_integer(self.sample_rate, "sample_rate")
|
|
26
|
+
if not isinstance(self.samples, tuple):
|
|
27
|
+
raise TypeError("samples must be a tuple")
|
|
28
|
+
if any(isinstance(value, bool) or not isinstance(value, (int, float))
|
|
29
|
+
or not math.isfinite(value) or not -1 <= value <= 1
|
|
30
|
+
for value in self.samples):
|
|
31
|
+
raise ValueError("samples must contain finite numbers in [-1, 1]")
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def duration(self):
|
|
35
|
+
return len(self.samples) / self.sample_rate
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def load_wav(source, *, max_frames=1_000_000, max_channels=8):
|
|
39
|
+
"""Read integer PCM WAV (8/16/24/32 bit), averaging channels to mono.
|
|
40
|
+
|
|
41
|
+
Opposite-phase channels can cancel during averaging. Source is a path or
|
|
42
|
+
binary seekable file; caller-owned files remain open. Declared frame count
|
|
43
|
+
and channels are checked before sample allocation. Malformed/truncated PCM
|
|
44
|
+
is rejected. Compressed audio and float WAV are not supported.
|
|
45
|
+
"""
|
|
46
|
+
_positive_integer(max_frames, "max_frames")
|
|
47
|
+
_positive_integer(max_channels, "max_channels")
|
|
48
|
+
if isinstance(source, os.PathLike):
|
|
49
|
+
source = os.fspath(source)
|
|
50
|
+
with wave.open(source, "rb") as stream:
|
|
51
|
+
channels = stream.getnchannels()
|
|
52
|
+
width = stream.getsampwidth()
|
|
53
|
+
rate = stream.getframerate()
|
|
54
|
+
frames = stream.getnframes()
|
|
55
|
+
if not 1 <= channels <= max_channels:
|
|
56
|
+
raise ValueError("WAV channel count exceeds max_channels")
|
|
57
|
+
if frames > max_frames:
|
|
58
|
+
raise ValueError("WAV frame count exceeds max_frames")
|
|
59
|
+
if stream.getcomptype() != "NONE" or width not in (1, 2, 3, 4):
|
|
60
|
+
raise ValueError("WAV must use 8/16/24/32-bit integer PCM")
|
|
61
|
+
raw = stream.readframes(frames)
|
|
62
|
+
frame_size = width * channels
|
|
63
|
+
if len(raw) != frames * frame_size or stream.readframes(1):
|
|
64
|
+
raise ValueError("WAV contains truncated or incomplete PCM frames")
|
|
65
|
+
samples = []
|
|
66
|
+
scale = 1 << (width * 8 - 1)
|
|
67
|
+
for offset in range(0, len(raw), frame_size):
|
|
68
|
+
total = 0
|
|
69
|
+
for channel in range(channels):
|
|
70
|
+
start = offset + channel * width
|
|
71
|
+
value = int.from_bytes(raw[start:start + width], "little", signed=width != 1)
|
|
72
|
+
total += value - 128 if width == 1 else value
|
|
73
|
+
samples.append(total / (scale * channels))
|
|
74
|
+
return AudioData(tuple(samples), rate)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def audio_features(audio):
|
|
78
|
+
"""Return [duration seconds, mean, RMS, peak, zero-crossing fraction].
|
|
79
|
+
|
|
80
|
+
Zero crossings compare adjacent signs (zero is nonnegative). Empty audio
|
|
81
|
+
returns five zeros. These summaries discard temporal/spectral information;
|
|
82
|
+
they are suitable for basic signal classification, not transcription.
|
|
83
|
+
"""
|
|
84
|
+
if not isinstance(audio, AudioData):
|
|
85
|
+
raise TypeError("audio must be AudioData")
|
|
86
|
+
samples = audio.samples
|
|
87
|
+
if not samples:
|
|
88
|
+
return [0.0] * 5
|
|
89
|
+
crossings = sum((samples[i - 1] < 0) != (samples[i] < 0)
|
|
90
|
+
for i in range(1, len(samples)))
|
|
91
|
+
return [audio.duration, math.fsum(samples) / len(samples),
|
|
92
|
+
math.sqrt(math.fsum(value * value for value in samples) / len(samples)),
|
|
93
|
+
max(abs(value) for value in samples), crossings / max(1, len(samples) - 1)]
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""Bounded UTF-8 tokens and deterministic character features, using only stdlib."""
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
import math
|
|
5
|
+
import unicodedata
|
|
6
|
+
|
|
7
|
+
__all__ = ["ByteTokenizer", "HashingTextVectorizer"]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _positive_integer(value, name):
|
|
11
|
+
if type(value) is not int or value < 1:
|
|
12
|
+
raise ValueError(f"{name} must be a positive integer")
|
|
13
|
+
return value
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ByteTokenizer:
|
|
17
|
+
"""Reversible UTF-8 bytes (IDs 0..255), without text normalization.
|
|
18
|
+
|
|
19
|
+
These are byte tokens, not a provider/model's token accounting scheme.
|
|
20
|
+
Invalid UTF-8 and isolated Unicode surrogates are rejected.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
vocab_size = 256
|
|
24
|
+
|
|
25
|
+
def __init__(self, *, max_bytes=1_000_000):
|
|
26
|
+
self.max_bytes = _positive_integer(max_bytes, "max_bytes")
|
|
27
|
+
|
|
28
|
+
def encode(self, text):
|
|
29
|
+
if not isinstance(text, str):
|
|
30
|
+
raise TypeError("text must be a string")
|
|
31
|
+
if len(text) > self.max_bytes:
|
|
32
|
+
raise ValueError("text exceeds max_bytes")
|
|
33
|
+
encoded = text.encode("utf-8")
|
|
34
|
+
if len(encoded) > self.max_bytes:
|
|
35
|
+
raise ValueError("text exceeds max_bytes")
|
|
36
|
+
return list(encoded)
|
|
37
|
+
|
|
38
|
+
def decode(self, tokens):
|
|
39
|
+
encoded = bytearray()
|
|
40
|
+
for token in tokens:
|
|
41
|
+
if len(encoded) >= self.max_bytes:
|
|
42
|
+
raise ValueError("tokens exceed max_bytes")
|
|
43
|
+
if type(token) is not int or not 0 <= token < self.vocab_size:
|
|
44
|
+
raise ValueError("tokens must be integer byte IDs in 0..255")
|
|
45
|
+
encoded.append(token)
|
|
46
|
+
return encoded.decode("utf-8")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class HashingTextVectorizer:
|
|
50
|
+
"""Fixed-width counts of Unicode character n-grams, with stable hashing.
|
|
51
|
+
|
|
52
|
+
NFC normalization and optional casefolding are applied only to features.
|
|
53
|
+
Hash collisions are possible; these are lexical features, not semantic
|
|
54
|
+
embeddings. ``normalize=True`` scales each vector to unit Euclidean norm.
|
|
55
|
+
Empty text produces zeros. Configuration must match training and inference.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
def __init__(self, n_features=256, *, ngram_range=(1, 3), lowercase=True,
|
|
59
|
+
normalize=True, max_chars=100_000):
|
|
60
|
+
self.n_features = _positive_integer(n_features, "n_features")
|
|
61
|
+
if self.n_features > 65_536:
|
|
62
|
+
raise ValueError("n_features must not exceed 65536")
|
|
63
|
+
if not isinstance(ngram_range, (tuple, list)) or len(ngram_range) != 2:
|
|
64
|
+
raise ValueError("ngram_range must contain two integers")
|
|
65
|
+
low, high = ngram_range
|
|
66
|
+
if type(low) is not int or type(high) is not int or not 1 <= low <= high <= 8:
|
|
67
|
+
raise ValueError("ngram_range must satisfy 1 <= low <= high <= 8")
|
|
68
|
+
if type(lowercase) is not bool or type(normalize) is not bool:
|
|
69
|
+
raise TypeError("lowercase and normalize must be booleans")
|
|
70
|
+
self.ngram_range = (low, high)
|
|
71
|
+
self.lowercase = lowercase
|
|
72
|
+
self.normalize = normalize
|
|
73
|
+
self.max_chars = _positive_integer(max_chars, "max_chars")
|
|
74
|
+
|
|
75
|
+
def transform(self, text):
|
|
76
|
+
if not isinstance(text, str):
|
|
77
|
+
raise TypeError("text must be a string")
|
|
78
|
+
if len(text) > self.max_chars:
|
|
79
|
+
raise ValueError("text exceeds max_chars")
|
|
80
|
+
text = unicodedata.normalize("NFC", text.casefold() if self.lowercase else text)
|
|
81
|
+
if len(text) > self.max_chars:
|
|
82
|
+
raise ValueError("normalized text exceeds max_chars")
|
|
83
|
+
vector = [0.0] * self.n_features
|
|
84
|
+
for width in range(self.ngram_range[0], self.ngram_range[1] + 1):
|
|
85
|
+
for offset in range(len(text) - width + 1):
|
|
86
|
+
digest = hashlib.blake2b(text[offset:offset + width].encode("utf-8"),
|
|
87
|
+
digest_size=8).digest()
|
|
88
|
+
index = int.from_bytes(digest, "big") % self.n_features
|
|
89
|
+
vector[index] += 1.0
|
|
90
|
+
if self.normalize:
|
|
91
|
+
norm = math.sqrt(sum(value * value for value in vector))
|
|
92
|
+
if norm:
|
|
93
|
+
vector = [value / norm for value in vector]
|
|
94
|
+
return vector
|
|
95
|
+
|
|
96
|
+
def transform_batch(self, texts):
|
|
97
|
+
"""Transform a batch; the caller controls the number of documents."""
|
|
98
|
+
if isinstance(texts, str):
|
|
99
|
+
raise TypeError("texts must be an iterable of strings, not a string")
|
|
100
|
+
return [self.transform(text) for text in texts]
|
|
@@ -64,6 +64,8 @@ src/wsbuilder/http3.py
|
|
|
64
64
|
src/wsbuilder/http3_server.py
|
|
65
65
|
src/wsbuilder/http3_websocket.py
|
|
66
66
|
src/wsbuilder/ia.py
|
|
67
|
+
src/wsbuilder/ia_audio.py
|
|
68
|
+
src/wsbuilder/ia_text.py
|
|
67
69
|
src/wsbuilder/logs.py
|
|
68
70
|
src/wsbuilder/mdns.py
|
|
69
71
|
src/wsbuilder/metrics.py
|
|
@@ -110,6 +112,7 @@ tests/test_http3_server.py
|
|
|
110
112
|
tests/test_http3_streaming.py
|
|
111
113
|
tests/test_http3_streaming_udp.py
|
|
112
114
|
tests/test_ia.py
|
|
115
|
+
tests/test_ia_text_audio.py
|
|
113
116
|
tests/test_logs.py
|
|
114
117
|
tests/test_mdns.py
|
|
115
118
|
tests/test_metrics.py
|