Haziel1357 commited on
Commit
1efb433
·
verified ·
1 Parent(s): a294091

Agregar guia de peso y estimar_foto

Browse files
entrenamiento/GUIA_PESO.md ADDED
@@ -0,0 +1,299 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Guía del modelo de peso (Revalora IA)
2
+
3
+ Última actualización: 2026-11-10
4
+
5
+ Léela completa antes de entrenar nada. Resume qué se hizo, con qué datos, qué resultados dio y qué falta.
6
+
7
+ ---
8
+
9
+ ## 1. Objetivo
10
+
11
+ Estimar el **peso en gramos** de un objeto. El modelo de peso (LightGBM) recibe **números**, no fotos:
12
+ material, largo, ancho, espesor y número de piezas. El flujo final que se busca es:
13
+
14
+ foto → material + piezas + medidas → LightGBM → peso
15
+
16
+ Hoy solo está terminada la última parte (características → peso). Ver sección 10.
17
+
18
+ ---
19
+
20
+ ## 2. Entorno
21
+
22
+ - Windows, PowerShell, VS Code, `uv`.
23
+ - Proyecto: `EagleDev-Innovacion\revalora-ia-service`. Todos los comandos se corren desde esa carpeta.
24
+ - Entorno virtual: `.venv` (activar con `.venv\Scripts\Activate.ps1`).
25
+ - Modelos y datos compartidos: repo de Hugging Face `EagleDev-Innovation/Modelos-EagleDev-Innovation`.
26
+ - Para subir necesitas ser miembro de la organización `EagleDev-Innovation` y un token con permiso **Write**:
27
+
28
+ ```powershell
29
+ uv pip install huggingface_hub
30
+ hf auth login
31
+ hf auth whoami
32
+ ```
33
+
34
+ ---
35
+
36
+ ## 3. Estado actual
37
+
38
+ | Pieza | Estado |
39
+ |---|---|
40
+ | LightGBM de peso (`modelos_entrenados/peso/lgbm_peso.joblib`) | Entrenado y subido a Hugging Face |
41
+ | Datos propios de peso | Solo 12 filas, todas barras de acero de 12 mm (`metal`) |
42
+ | Dataset externo EMMa (Stanford) | Descargado (6.7 GB), convertido a `emma_mat.csv` |
43
+ | Búsqueda por similitud con CLIP (foto → productos parecidos) | Scripts listos, índice **sin terminar** (muy lento en CPU) |
44
+ | Foto → medidas en cm | **No existe** |
45
+ | Integración en el backend | Pendiente |
46
+ | Modelos `conteo` y `material_estado` | Ya existen en el repo de Hugging Face (no se documentan en esta guía) |
47
+
48
+ ---
49
+
50
+ ## 4. Archivos (carpeta `entrenamiento\`)
51
+
52
+ | Archivo | Para qué sirve |
53
+ |---|---|
54
+ | `hf_sync.py` | `bajar` / `subir "mensaje"` de la carpeta `modelos_entrenados` |
55
+ | `hf_sync_datos.py` | `bajar` / `subir "mensaje"` de scripts y CSV (ver sección 11) |
56
+ | `importar_emma_mat.py` | Lee el `.fdd` de EMMa y genera `datos/externos/emma_mat.csv` |
57
+ | `entrenar_peso.py` | Entrena y valida el LightGBM de peso |
58
+ | `probar_peso.py` | Predice peso con valores a mano |
59
+ | `probar_foto.py` | Igual que el anterior, pero recibe la ruta de una foto (solo comprueba que exista, el peso sale de las medidas que se escriben) |
60
+ | `indexar_emma.py` | Crea el índice CLIP de títulos de EMMa |
61
+ | `estimar_foto.py` | Busca en el índice los productos parecidos a una foto y estima el peso |
62
+ | `ver_emma.py`, `splits_emma.py` | Scripts de exploración del `.fdd` (se pueden ignorar) |
63
+ | `importar_emma.py`, `limpiar_emma.py` | Versión anterior (sin material ni espesor). **Reemplazados** por `importar_emma_mat.py` |
64
+ | `importar_externos.py` | Script anterior de datos externos. Ya no lo usa `entrenar_peso.py` |
65
+
66
+ Otros archivos de la carpeta (`afinar_completo.py`, `buscar_roboflow.py`, `colab_entrenamiento.ipynb`, `entrenar.py`, `evaluar_conteo.py`, etc.) son de los modelos de conteo y material, y no se tocaron en este trabajo.
67
+
68
+ ---
69
+
70
+ ## 5. Datos
71
+
72
+ ### 5.1 Datos propios: `datos/peso/caracteristicas.csv`
73
+
74
+ Columnas: `foto, sesion, tipo_objeto, material, n_piezas, area_cm2_total, largo_cm_medio, ancho_cm_medio, largo_cm_max, espesor_cm, peso_real_g`.
75
+
76
+ - 12 filas, todas barras de acero de 12 mm (`material = metal`). Ejemplo: 1 barra = 888 g, 2 barras = 1780 g, 3 barras = 2650 g.
77
+ - `area_cm2_total` es el área **de todas las piezas juntas** (120, 240, 360...). El entrenamiento la divide entre `n_piezas`.
78
+ - `largo_cm_max` es ~1 % mayor que `largo_cm_medio` (101 contra 100).
79
+ - `espesor_cm` se agregó a mano (1.2).
80
+ - Esta es la parte débil del proyecto: con esto no se puede validar nada fuera del acero.
81
+
82
+ ### 5.2 Dataset externo: EMMa v1.1 (Stanford)
83
+
84
+ - Archivo: `datos/externos/EMMa_v1.1.fdd` (6,692,678,168 bytes ≈ 6.7 GB), con título, descripción, atributos (masa, dimensiones, categoría, materiales como vector).
85
+ - Descarga (reanuda si se corta):
86
+
87
+ ```powershell
88
+ mkdir datos\externos -Force
89
+ curl.exe -L -C - -o datos\externos\EMMa_v1.1.fdd https://downloads.cs.stanford.edu/downloads/emma/EMMa_v1.1.fdd
90
+ ```
91
+
92
+ - **No** se descargó el archivo de imágenes (185 GB), no hace falta.
93
+ - **No** subir el `.fdd` a Hugging Face: cada quien lo descarga de Stanford.
94
+
95
+ ### 5.3 Cómo se lee el `.fdd` (importante)
96
+
97
+ El paquete `freeze-dried-data` 2.6.4 **no sirve** para este archivo (`RFDD` da `Split not found` y no existe `FDD`). Se lee a mano:
98
+
99
+ - Los últimos 8 bytes son el tamaño del índice.
100
+ - El índice es un pickle `asin → (inicio, largo)`.
101
+ - Cada registro es un pickle (a veces comprimido con zlib) con: `materials, category, price, mass, ratings, dimensions, quantity, custom, image_hashes, title, features, description`.
102
+ - 2,883,694 registros.
103
+ - Unidades verificadas con medianas razonables: **masa en kg, dimensiones en pulgadas**. Son 3 valores por objeto; se ordenan de mayor a menor (largo, ancho, espesor).
104
+
105
+ ### 5.4 `datos/externos/emma_mat.csv` (lo que se usa para entrenar)
106
+
107
+ Lo genera `importar_emma_mat.py` con estos filtros:
108
+
109
+ - Un solo producto (`quantity == 1`) con una sola medida de dimensiones.
110
+ - Masa entre 5 y 30,000 g. Largo 1–150 cm, ancho 0.5–150 cm, espesor ≥ 0.1 cm.
111
+ - **Material** asignado por palabras clave del título y las características (en inglés). Solo se conservan los productos que coinciden con **un único** material: `metal, plastico, madera, vidrio, ceramica, carton, tela`.
112
+ - Columnas: `material, n_piezas, area_cm2_total, largo_cm_medio, ancho_cm_medio, largo_cm_max, espesor_cm, peso_real_g`.
113
+ - Quedan cientos de miles de filas (en el entrenamiento se muestrean 300,000).
114
+
115
+ Limitación: el material por palabras clave es aproximado, y EMMa tiene muchos productos **huecos** (envases, cajas), por eso el modelo tiende a subestimar objetos macizos.
116
+
117
+ ---
118
+
119
+ ## 6. Modelo de peso
120
+
121
+ Archivo: `entrenar_peso.py`. Configuración actual:
122
+
123
+ - **Features:** `area_pieza` (área total ÷ n_piezas), `largo_cm_medio`, `ancho_cm_medio`, `largo_cm_max`, `relacion` (largo ÷ ancho), `espesor_cm`, `volumen_cm3` (area_pieza × espesor) y `material` en uno-caliente.
124
+ - **Objetivo:** peso **por pieza** en escala logarítmica (`TransformedTargetRegressor` con `log1p` / `expm1`). Al predecir sale en gramos por pieza; se multiplica por `n_piezas`.
125
+ - **LightGBM:** 600 árboles, `learning_rate=0.05`, `num_leaves=63`, `min_child_samples=30`, `subsample=0.8`.
126
+ - **Pesos de muestra:** datos propios = 20, EMMa = 1 (`W_PROPIO = 20`).
127
+ - **Muestreo:** `MAX_EXT = 300000` filas de EMMa.
128
+ - Se guarda en `modelos_entrenados/peso/lgbm_peso.joblib` con las claves `modelo`, `columnas` y `cat`.
129
+
130
+ ### Validación
131
+
132
+ Se hacen dos: (1) 20 % de EMMa reservado, (2) tus datos propios por sesión (GroupKFold), comparando contra un modelo lineal solo con datos propios.
133
+
134
+ ### Resultados (2026-11-10)
135
+
136
+ | Prueba | Resultado |
137
+ |---|---|
138
+ | EMMa reservado | MAE 1263 g, error mediano **47.9 %** |
139
+ | Propio, lineal | MAE 8.5 g (0.4 %) |
140
+ | Propio, LightGBM | MAE **11.5 g** (0.5 %) |
141
+
142
+ ### Decisiones y por qué
143
+
144
+ 1. **Se agregó el espesor.** Sin él (solo largo y ancho) el error mediano en EMMa era 56 % y en las barras 111.9 g de MAE.
145
+ 2. **Se quitó `tipo_objeto`** de las features: los tipos propios no existen en EMMa.
146
+ 3. **`min_child_samples=30` y no 5.** Con 5 el modelo memoriza las 12 filas propias y la validación empeora (67.7 g).
147
+ 4. **Peso en logaritmo**, porque los pesos van de 5 g a 30 kg.
148
+ 5. El error de ~48 % en EMMa es el techo con estas features: dos productos del mismo tamaño y material pueden pesar muy distinto (macizo contra hueco).
149
+
150
+ ---
151
+
152
+ ## 7. Cómo reproducir
153
+
154
+ Desde `revalora-ia-service` con el venv activo:
155
+
156
+ ```powershell
157
+ # 1. Datos (descarga 1 vez, ~1 hora)
158
+ mkdir datos\externos -Force
159
+ curl.exe -L -C - -o datos\externos\EMMa_v1.1.fdd https://downloads.cs.stanford.edu/downloads/emma/EMMa_v1.1.fdd
160
+
161
+ # 2. Convertir a CSV (~3 min)
162
+ python entrenamiento\importar_emma_mat.py
163
+
164
+ # 3. Entrenar y validar
165
+ python entrenamiento\entrenar_peso.py
166
+
167
+ # 4. Probar
168
+ python entrenamiento\probar_peso.py
169
+ python entrenamiento\probar_peso.py plastico 20 10 5
170
+ python entrenamiento\probar_peso.py madera 25 10 2 4 # 4 piezas
171
+
172
+ # 5. Subir (solo si los resultados no empeoraron)
173
+ python entrenamiento\hf_sync.py subir "descripción del cambio"
174
+ ```
175
+
176
+ Si ya están `emma_mat.csv` y `caracteristicas.csv` en Hugging Face, se saltan los pasos 1 y 2 con `python entrenamiento\hf_sync_datos.py bajar`.
177
+
178
+ Orden de `probar_peso.py`: material, largo, ancho, espesor (cm) y, opcional, número de piezas. Materiales válidos: `metal`, `plastico`, `madera`, `vidrio`, `ceramica`, `carton`, `tela`. En PowerShell no escribas los `<...>` de los ejemplos: son marcadores.
179
+
180
+ ---
181
+
182
+ ## 8. Pruebas hechas
183
+
184
+ | Objeto | Predicho | Real |
185
+ |---|---|---|
186
+ | Barra de acero 100×1.2×1.2 cm | 873 g | 888 g |
187
+ | 3 barras de acero | 2619 g | 2650 g |
188
+ | Madera 30×20×2 cm | ~800 g | sin pesar |
189
+ | Plástico 20×10×5 cm | ~240 g | sin pesar |
190
+ | Cartón 40×30×20 cm | ~3000 g | sin pesar |
191
+ | Vidrio 20×8×8 cm | ~690 g | sin pesar |
192
+
193
+ Solo el acero está validado con peso real. Pendiente: probar un bidón de agua de ~20 L vacío (usar plástico, ~50 × 27 × 27 cm, y pesarlo vacío; el modelo predice el objeto, no el contenido) y una hoja de papel (A4 ≈ 5 g, probablemente fuera de lo que vio el modelo).
194
+
195
+ ---
196
+
197
+ ## 9. Búsqueda por similitud con CLIP
198
+
199
+ Idea: dada una foto, buscar los productos de EMMa más parecidos (CLIP compara la foto con los **títulos**) y estimar el peso con los vecinos.
200
+
201
+ - `indexar_emma.py` toma 150,000 productos válidos de EMMa y guarda `datos/externos/emma_clip.npy` y `emma_clip.csv`.
202
+ - `estimar_foto.py` recibe la ruta de una foto (y opcionalmente una pista en inglés, ej. `plastic water jug`), muestra los 15 vecinos más parecidos y da la mediana ponderada de su peso.
203
+ - **Estado:** el 2026-11-10 se probó en CPU: ~19 s por lote, ~3 horas en total, sin terminar. En una GPU de Colab tarda unos minutos. Hay que correrlo en Colab (ver abajo) o bajar `N` en el script.
204
+ - Todavía no se evaluó si las coincidencias son buenas. Revisar siempre la lista de vecinos impresa: si no son del mismo tipo de objeto, el estimado no sirve.
205
+
206
+ Uso en Colab (GPU):
207
+
208
+ ```python
209
+ !pip install -q sentence-transformers pillow huggingface_hub pandas lightgbm scikit-learn joblib
210
+ from huggingface_hub import login
211
+ from google.colab import userdata
212
+ login(userdata.get("HF_TOKEN"))
213
+ ```
214
+ Luego descargar los scripts (`hf_sync_datos.py bajar`), bajar el `.fdd` con `!curl -L -C - -o datos/externos/EMMa_v1.1.fdd <URL>`, correr `!python entrenamiento/indexar_emma.py` y subir con `hf_sync_datos.py subir`.
215
+
216
+ ---
217
+
218
+ ## 10. Qué falta (por prioridad)
219
+
220
+ 1. **Más datos propios variados.** Es lo que más mejora el modelo. Pesar 5–10 objetos de cada material (plástico, madera, cartón, vidrio, cerámica, tela, metal distinto al acero), medir largo, ancho y espesor, y agregarlos a `datos/peso/caracteristicas.csv` con `espesor_cm` y una `sesion` distinta por lote. Después reentrenar. Con esto la validación `[propio]` empieza a significar algo.
221
+ 2. **Foto → medidas en cm.** Una foto no trae escala. Opciones: objeto de referencia en la foto (hoja A4, tarjeta de 8.5×5.4 cm) para calcular cm por píxel, o que el usuario escriba las medidas. Decidir cuál.
222
+ 3. **Espesor.** También hay que pedirlo o asignarlo por tipo de objeto (hoja de papel ≈ 0.01 cm).
223
+ 4. **Conectar** `material_estado` (material) y `conteo` (piezas) con el LightGBM en un endpoint (`POST /peso` que reciba la imagen).
224
+ 5. **Terminar y evaluar el índice CLIP** y decidir si se combina con el LightGBM (el LightGBM con medidas reales debería ser más preciso).
225
+ 6. **Papel y objetos muy delgados:** EMMa se filtró con espesor ≥ 1 mm, así que el modelo no vio hojas sueltas. Necesitan datos propios.
226
+ 7. **Objetos de obra** (barras, tablones, cemento): EMMa son productos de Amazon, con pocas coincidencias buenas.
227
+
228
+ ---
229
+
230
+ ## 11. Integración con el backend
231
+
232
+ Para predecir, el backend debe armar exactamente estas columnas (la lista oficial está en la clave `columnas` del `.joblib`):
233
+
234
+ - `area_pieza` = área total ÷ n_piezas
235
+ - `largo_cm_medio`, `ancho_cm_medio`
236
+ - `largo_cm_max` = longitud máxima medida de la pieza (en las barras es ~1.01 × el largo medio; no copiar el largo medio, las predicciones salen más bajas)
237
+ - `relacion` = largo ÷ ancho
238
+ - `espesor_cm`
239
+ - `volumen_cm3` = area_pieza × espesor
240
+ - `material_carton, material_ceramica, material_madera, material_metal, material_plastico, material_tela, material_vidrio` (uno en 1, el resto en 0)
241
+
242
+ La predicción sale en **gramos por pieza**; hay que multiplicar por `n_piezas`. Ver `probar_peso.py` como ejemplo exacto.
243
+
244
+ ---
245
+
246
+ ## 12. Compartir trabajo (Hugging Face)
247
+
248
+ `hf_sync.py` maneja los modelos (`modelos_entrenados/`). `hf_sync_datos.py` maneja scripts y CSV:
249
+
250
+ ```python
251
+ import sys
252
+ from huggingface_hub import snapshot_download, upload_folder
253
+
254
+ REPO = "EagleDev-Innovation/Modelos-EagleDev-Innovation"
255
+ PATRONES = [
256
+ "entrenamiento/*.py", "entrenamiento/*.md", "entrenamiento/*.ipynb",
257
+ "datos/peso/*.csv",
258
+ "datos/externos/emma_mat.csv",
259
+ "datos/externos/emma_clip.*",
260
+ ]
261
+
262
+ def bajar():
263
+ snapshot_download(repo_id=REPO, repo_type="model",
264
+ allow_patterns=PATRONES, local_dir=".")
265
+ print("Código y datos descargados")
266
+
267
+ def subir(mensaje):
268
+ upload_folder(repo_id=REPO, repo_type="model", folder_path=".",
269
+ allow_patterns=PATRONES,
270
+ ignore_patterns=[".venv/**", "**/__pycache__/**"],
271
+ commit_message=mensaje)
272
+ print("Código y datos subidos")
273
+
274
+ if __name__ == "__main__":
275
+ if sys.argv[1] == "bajar":
276
+ bajar()
277
+ elif sys.argv[1] == "subir":
278
+ subir(sys.argv[2] if len(sys.argv) > 2 else "Actualizar código y datos")
279
+ ```
280
+
281
+ Reglas de equipo:
282
+
283
+ - **`bajar` sobrescribe archivos locales.** Antes de bajar, sube o guarda lo tuyo; si no, pierdes cambios.
284
+ - Hugging Face guarda historial: si alguien sube algo peor, se puede volver al commit anterior desde la pestaña *Files and versions*.
285
+ - Suban un modelo solo si `[propio]` y `[EMMA holdout]` no empeoraron. Pongan el resultado en el mensaje del commit.
286
+ - Avísense quién entrena para no pisarse en la misma carpeta.
287
+ - No suban tokens, ni el `.fdd`, ni el `.venv`.
288
+
289
+ ---
290
+
291
+ ## 13. Errores que ya pasaron
292
+
293
+ - `RFDD` con `Split not found` o `cannot import name 'FDD'`: leer el `.fdd` a mano (sección 5.3).
294
+ - En PowerShell, `<material>` da error: no escribir los `<>`.
295
+ - `.Replace(...)` sobre un script **no avisa si no encontró el texto**: el archivo queda igual. Verificar con `Select-String -Path archivo -Pattern 'texto'`.
296
+ - `largo_cm_max` en la prueba debe ser ~1.01 × el largo (como en los datos propios), o las barras salen ~30 % más bajas.
297
+ - `hf_sync.py subir` puede imprimir "No files have been modified" aunque haya subido: comprobar la fecha del archivo en Hugging Face.
298
+ - Los avisos de symlinks de Hugging Face en Windows son inofensivos.
299
+ - CLIP en CPU es muy lento (ver sección 9).
entrenamiento/estimar_foto.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import numpy as np
3
+ import pandas as pd
4
+ from PIL import Image
5
+ from sentence_transformers import SentenceTransformer
6
+
7
+ K = 15
8
+ emb = np.load("datos/externos/emma_clip.npy").astype("float32")
9
+ df = pd.read_csv("datos/externos/emma_clip.csv")
10
+ model = SentenceTransformer("clip-ViT-B-32")
11
+
12
+ img = Image.open(sys.argv[1]).convert("RGB")
13
+ q = model.encode(img, normalize_embeddings=True)
14
+ if len(sys.argv) > 2: # pista opcional en inglés, ej: "plastic water jug"
15
+ t = model.encode(" ".join(sys.argv[2:]), normalize_embeddings=True)
16
+ q = q + t
17
+ q = q / np.linalg.norm(q)
18
+
19
+ sim = emb @ q
20
+ top = np.argsort(-sim)[:K]
21
+ sel = df.iloc[top].copy()
22
+ sel["sim"] = sim[top]
23
+
24
+ print("\nCoincidencias más parecidas:")
25
+ for _, r in sel.iterrows():
26
+ print(f"{r.sim:.3f} | {r.peso_g:7.0f} g | {str(r.dims_cm):22s} | {r.title[:60]}")
27
+
28
+ o = np.argsort(sel.peso_g.to_numpy())
29
+ p, w = sel.peso_g.to_numpy()[o], sel.sim.to_numpy()[o]
30
+ mediana = p[np.searchsorted(np.cumsum(w) / w.sum(), 0.5)]
31
+ print(f"\nPeso estimado: {mediana:.0f} g (rango típico {np.percentile(p, 25):.0f} - {np.percentile(p, 75):.0f} g)")