Al bucket alpinashop-catalogo hi ha 60 GB d'imatges. Unes 2.400 fitxes de producte, cadascuna amb entre cinc i deu fotos, organitzades a productos/<sku>/original/, productos/<sku>/web/ i productos/<sku>/thumb/.
Sobre aquestes imatges, AlpinaShop sap exactament una cosa: en quina carpeta són. Res més.
I això es tradueix en quatre problemes concrets que estan costant diners ara mateix:
El web no és accessible. Cap imatge no té atribut alt. Un lector de pantalla anuncia "imatge" i punt. A més de ser una barrera real per a clients amb discapacitat visual, és un problema de posicionament i una possible incidència de compliment normatiu.
El filtre per color no existeix. Màrqueting fa un any que demana que es pugui filtrar el catàleg per color. El camp color de la fitxa l'omple el proveïdor quan li ve de gust, amb valors com "blau", "blau marí", "navy", "azul" i en molts casos buit.
Ningú no modera les fotos dels clients. Des de fa uns mesos, les ressenyes admeten fotos. Es publiquen directament, sense revisió. És qüestió de temps que algú pugi alguna cosa inapropiada i aparegui en una fitxa de producte.
El catàleg és visualment irregular. Unes fotos tenen fons blanc, d'altres el terra del magatzem del proveïdor. Ningú no ha auditat 20.000 imatges.
Els quatre es resolen amb la mateixa eina i sense entrenar res. És el mateix principi de la lliçó anterior —no entrenis el que ja està entrenat— aplicat ara a píxels.
Contingut
- Què ofereix la Cloud Vision API
- Primera crida i lectura de la resposta
- Processament massiu amb
asyncBatchAnnotate - Dels JSON de sortida a
alpinashop_analitica - Cost per miler d'imatges i com controlar-lo
- Aplicació 1: el text alternatiu d'accessibilitat
- Aplicació 2: colors dominants i el filtre de la botiga
- Aplicació 3: moderar les fotos dels clients amb SafeSearch
- Aplicació 4: auditar l'estàndard visual del catàleg
- Arquitectura per esdeveniments amb
imagenes-subidas - OCR: text en imatges i documents
- Límits i biaixos: on falla amb material de muntanya
- Quan fer el salt a AutoML Vision
- Document AI per a factures i albarans
- Rostres, biometria i AI Act: l'advertiment seriós
- Què ofereix la Cloud Vision API
Una sola API amb diverses funcions que es demanen per separat. Cada funció sol·licitada factura de forma independent, així que convé saber quina serveix per a què.
| Funció | Què retorna | Ús a AlpinaShop |
|---|---|---|
Detecció d'etiquetes (LABEL_DETECTION) |
Conceptes presents a la imatge, amb confiança | Base del text alternatiu |
Detecció d'objectes (OBJECT_LOCALIZATION) |
Objectes amb el seu requadre i confiança | Comprovar enquadrament i detectar objectes intrusos |
OCR (TEXT_DETECTION) |
Text de la imatge amb la seva posició | Llegir talles, marques i codis a les fotos |
OCR de documents (DOCUMENT_TEXT_DETECTION) |
Text estructurat en pàgines, blocs i paràgrafs | Documents escanejats, no fotos de producte |
Logotips (LOGO_DETECTION) |
Marques comercials reconegudes | Verificar que la marca correspon a la fitxa |
Punts de referència (LANDMARK_DETECTION) |
Llocs famosos | Marginal: fotos d'ambient a muntanya |
Propietats d'imatge (IMAGE_PROPERTIES) |
Colors dominants en RGB amb fracció de píxels | El filtre per color |
SafeSearch (SAFE_SEARCH_DETECTION) |
Probabilitat de contingut adult, violent, mèdic, etc. | Moderació de fotos de clients |
Detecció web (WEB_DETECTION) |
Imatges similars al web i entitats associades | Detectar ús no autoritzat de fotos pròpies |
Retall suggerit (CROP_HINTS) |
Retalls recomanats per relació d'aspecte | Generar miniatures ben enquadrades |
Rostres (FACE_DETECTION) |
Posició i atributs de cares | Mira l'apartat 15 abans d'utilitzar-la |
Dues precisions importants des del principi.
Detecció d'etiquetes i detecció d'objectes no són el mateix. La primera diu quins conceptes hi ha ("motxilla", "muntanya", "blau", "equipament"). La segona diu quins objectes hi ha i on, amb coordenades. Per descriure una imatge n'hi ha prou amb la primera; per comprovar si el producte està centrat i ocupa l'enquadrament correcte, cal la segona.
La detecció web és la menys coneguda i té un ús molt concret per a una botiga: descobrir si les teves fotos de catàleg estan sent utilitzades per competidors. No és l'objectiu d'aquesta lliçó, però convé saber que existeix.
- Primera crida i lectura de la resposta
Després d'habilitar l'API (gcloud services enable vision.googleapis.com --project=alpinashop-datos):
from google.cloud import vision
client = vision.ImageAnnotatorClient()
imatge = vision.Image()
imatge.source.image_uri = "gs://alpinashop-catalogo/productos/MOC-4471/web/01.jpg"
resposta = client.annotate_image({
"image": imatge,
"features": [
{"type_": vision.Feature.Type.LABEL_DETECTION, "max_results": 10},
{"type_": vision.Feature.Type.OBJECT_LOCALIZATION, "max_results": 5},
{"type_": vision.Feature.Type.IMAGE_PROPERTIES},
{"type_": vision.Feature.Type.SAFE_SEARCH_DETECTION},
],
})
if resposta.error.message:
raise RuntimeError(resposta.error.message)Nota clau: la imatge es referencia pel seu URI de Cloud Storage, no es puja a la petició. L'API la llegeix directament del bucket. Això significa que no cal descarregar 60 GB enlloc, però també que el compte de servei necessita permís de lectura sobre el bucket. És la fallada de configuració número u d'aquesta lliçó.
La resposta, camp a camp:
# --- Etiquetes ---
for etiqueta in resposta.label_annotations:
print(f"{etiqueta.description:25} score={etiqueta.score:.3f} mid={etiqueta.mid}")
# Motxilla score=0.968 mid=/m/0n1cs
# Equipatge score=0.944
# Equipament score=0.911
# Blau score=0.887description: el concepte, en l'idioma que demanis. Es controla ambimage_context.language_hints=["ca"].score: confiança entre 0 i 1. Per sota de 0,6 les etiquetes solen ser soroll.mid: identificador estable del concepte al graf de coneixement de Google. És més fiable que el text per agrupar:/m/0n1csés sempre el mateix encara que la traducció canviï.
Els objectes arriben a resposta.localized_object_annotations, cadascun amb el seu name, el seu score i un bounding_poly.normalized_vertices —per exemple, Backpack 0.921 x[0.18-0.83] y[0.09-0.94]—. Les coordenades són normalitzades: van de 0 a 1 respecte de l'amplada i l'alçada de la imatge, no en píxels. Això les fa independents de la resolució, cosa molt còmoda perquè el mateix llindar serveix per a la foto original i per a la miniatura. Amb aquests quatre números es calcula quina fracció de l'enquadrament ocupa el producte i si està centrat, que és justament el que cal a l'apartat 9.
Els colors dominants arriben a resposta.image_properties_annotation.dominant_colors.colors, cadascun amb el seu RGB, el seu pixel_fraction i el seu score. Són dos camps que es confonen: pixel_fraction és la proporció de píxels d'aquell color i score la rellevància visual estimada. En una foto d'estudi, el primer resultat sol ser RGB(245,245,244) amb fracció 0,61 —el fons blanc— i el color real del producte ve al darrere. Aquest és exactament el problema de l'apartat 7.
I el SafeSearch es llegeix a resposta.safe_search_annotation, amb els camps adult, violence, racy, medical i spoof:
s = resposta.safe_search_annotation
print(s.adult.name, s.violence.name, s.racy.name, s.medical.name, s.spoof.name)
# VERY_UNLIKELY VERY_UNLIKELY UNLIKELY VERY_UNLIKELY VERY_UNLIKELYSafeSearch no retorna un booleà: retorna un de sis nivells (UNKNOWN, VERY_UNLIKELY, UNLIKELY, POSSIBLE, LIKELY, VERY_LIKELY) per a cadascuna de cinc categories. On es posa el tall és una decisió de política editorial, no tècnica —apartat 8.
- Processament massiu amb
asyncBatchAnnotate
asyncBatchAnnotateCridar 20.000 vegades annotate_image funciona, però és lent, gestiona malament les quotes i satura el procés local. Per a volum existeix asyncBatchAnnotate: un treball asíncron que processa un lot d'imatges i escriu els resultats directament a Cloud Storage, sense que el teu procés hagi d'esperar.
from google.cloud import vision
client = vision.ImageAnnotatorClient()
def peticio(uri):
return vision.AnnotateImageRequest(
image=vision.Image(source=vision.ImageSource(image_uri=uri)),
features=[
vision.Feature(type_=vision.Feature.Type.LABEL_DETECTION, max_results=10),
vision.Feature(type_=vision.Feature.Type.IMAGE_PROPERTIES),
vision.Feature(type_=vision.Feature.Type.OBJECT_LOCALIZATION, max_results=5),
],
image_context=vision.ImageContext(language_hints=["ca"]),
)
sortida = vision.OutputConfig(
gcs_destination=vision.GcsDestination(
uri="gs://alpinashop-datalake/vision/catalogo/"),
batch_size=100, # imatges per fitxer JSON de sortida
)
operacio = client.async_batch_annotate_images(
requests=[peticio(u) for u in uris_lot], # fins a 2000 per operacio
output_config=sortida,
)
resultat = operacio.result(timeout=3600)Els quatre punts que cal entendre:
És asíncron de debò. async_batch_annotate_images retorna una operació de llarga durada. Pots esperar-la amb operacio.result() o desar-te el nom de l'operació i consultar-la després. Per a 20.000 imatges, el sensat és el segon i deixar que un Workflow ho supervisi.
Els resultats van a Cloud Storage, no al teu procés. S'escriuen com a fitxers JSON al prefix indicat. La teva memòria no veu 20.000 respostes.
batch_size agrupa les respostes en fitxers. Amb 100, cada JSON conté 100 anotacions. Fitxers molt petits generen milers d'objectes que després cal llistar; molt grans són incòmodes de processar. Entre 50 i 200 és raonable.
Hi ha un topall de peticions per operació (de l'ordre de 2.000). Per a les 20.000 imatges d'AlpinaShop cal trossejar en diverses operacions, llançant-les de forma esglaonada.
I la preparació del llistat d'imatges es fa amb un gcloud storage ls --recursive "gs://alpinashop-catalogo/productos/**/web/*.jpg", aplicant dos filtres que estalvien diners:
Només web/. Les carpetes original/, web/ i thumb/ contenen la mateixa imatge en tres resolucions. Analitzar-les totes tres multiplica el cost per tres sense aportar absolutament res. Aquest únic filtre divideix la factura entre tres.
Només una o dues fotos per SKU per a l'anàlisi inicial. Si l'objectiu és descriure el producte i extreure'n el color, la foto principal basta. Les altres es processen només si calen per a l'auditoria d'enquadrament.
- Dels JSON de sortida a
alpinashop_analitica
alpinashop_analiticaEls fitxers JSON a Cloud Storage no serveixen de res mentre no siguin consultables. El pont és un bq load --source_format=NEWLINE_DELIMITED_JSON --autodetect sobre el prefix de sortida. A la pràctica, la sortida d'asyncBatchAnnotate ve imbricada com un array responses, així que sol caldre un pas d'aplanament previ: per a volums moderats, un script Python que llegeixi els JSON i escrigui files planes; per a volums grans, un treball de Dataflow (04-02).
La taula normalitzada de destinació:
CREATE OR REPLACE TABLE `alpinashop-datos.alpinashop_analitica.imagenes_vision`
PARTITION BY fecha_analisis
CLUSTER BY sku AS
SELECT
REGEXP_EXTRACT(uri, r'productos/([^/]+)/') AS sku,
uri,
ARRAY(SELECT AS STRUCT descripcion, score, mid
FROM UNNEST(etiquetas) WHERE score >= 0.60) AS etiquetas,
colores,
objetos,
safesearch_adult, safesearch_violence, safesearch_racy,
CURRENT_DATE() AS fecha_analisis,
'vision-api-v1' AS modelo_version
FROM `alpinashop-datos.alpinashop_analitica.raw_vision_catalogo`;El filtre score >= 0.60 dins de l'ARRAY és deliberat: les etiquetes per sota d'aquella confiança són gairebé sempre soroll genèric ("producte", "objecte", "fotografia") que embruta qualsevol anàlisi posterior.
I de nou modelo_version i fecha_analisis, per la mateixa raó de 05-04: les APIs preentrenades s'actualitzen soles, i sense aquestes columnes és impossible saber si un canvi en els resultats ve del model o de les imatges.
La comprovació immediata és un GROUP BY sobre UNNEST(etiquetas) comptant imatges per descripció. Aquell llistat respon d'un cop d'ull a "què veu la màquina al meu catàleg?" i sol deparar sorpreses: etiquetes genèriques dominant el rànquing, o conceptes inesperats que revelen fotos mal classificades.
- Cost per miler d'imatges i com controlar-lo
La Cloud Vision API factura per unitat, on una unitat és una imatge per cada funció sol·licitada. És la mateixa lògica de 05-04 i té la mateixa conseqüència: demanar quatre funcions sobre una imatge són quatre unitats.
Les tarifes s'estructuren per trams —hi ha un tram mensual gratuït i el preu per miler baixa en augmentar el volum— i canvien amb el temps: consulta sempre el tarificador oficial vigent. Com a ordre de magnitud, el cost per miler d'unitats es mou a l'entorn d'uns pocs euros per a les funcions habituals.
L'estimació per a AlpinaShop, feta abans de gastar:
| Escenari | Imatges | Funcions | Unitats |
|---|---|---|---|
| Tot el bucket, totes les funcions | 20.000 | 5 | 100.000 |
Només web/, totes les funcions |
~7.000 | 5 | 35.000 |
Només web/, foto principal, 3 funcions |
2.400 | 3 | 7.200 |
| Fotos noves al mes | ~200 | 3 | 600 |
La diferència entre la primera i la tercera fila és un factor de gairebé 14. I la tercera opció cobreix els quatre problemes de l'inici de la lliçó. Aquesta és la feina real d'aquesta secció: no negociar el preu, sinó no demanar el que no es necessita.
Les quatre mesures de control:
- Filtrar per carpeta. Només
web/. Divideix per tres. - Una foto per SKU per a etiquetes i color. Divideix per un altre tant.
- Demanar només les funcions necessàries. SafeSearch no té sentit sobre fotos de catàleg del proveïdor: es reserva per a les fotos que pugen els clients.
- No reprocessar. Desar el resultat i analitzar només el que és nou, amb la mateixa consulta incremental de 05-04.
La consulta incremental és la mateixa idea de 05-04: LEFT JOIN de l'inventari d'imatges contra imagenes_vision, filtrant v.uri IS NULL i carpeta = 'web', amb un LIMIT 2000 com a topall de seguretat.
I les etiquetes de facturació (centro-coste:analitica) amb la seva alerta de pressupost, igual que a tot el mòdul.
- Aplicació 1: el text alternatiu d'accessibilitat
El problema: 20.000 imatges sense alt. La solució amb etiquetes és directa, però cal fer-la bé.
CREATE OR REPLACE TABLE `alpinashop-datos.alpinashop_analitica.imagenes_alt` AS
WITH etiquetas_utiles AS (
SELECT
v.sku, v.uri,
ARRAY_AGG(e.descripcion ORDER BY e.score DESC LIMIT 3) AS conceptos
FROM `alpinashop-datos.alpinashop_analitica.imagenes_vision` v,
UNNEST(v.etiquetas) e
WHERE e.score >= 0.75
AND LOWER(e.descripcion) NOT IN
('producte','objecte','fotografia','imatge','fons','blanc')
GROUP BY v.sku, v.uri
)
SELECT
t.sku, t.uri,
CONCAT(p.nombre, '. ',
ARRAY_TO_STRING(t.conceptos, ', '),
'. Marca ', p.marca, '.') AS alt_propuesto,
ARRAY_LENGTH(t.conceptos) AS n_conceptos,
FALSE AS revisado
FROM etiquetas_utiles t
JOIN `alpinashop-datos.alpinashop_analitica.productos` p USING (sku);Les tres decisions d'aquesta consulta:
Començar pel nom del producte, no per les etiquetes. L'alt més útil per a un lector de pantalla és "Motxilla Trek 40L Blava. Motxilla, equipament de muntanya, blau. Marca Alpina." La informació que la botiga ja té és més precisa que la que endevina el model; l'API la complementa, no la substitueix.
Llista negra d'etiquetes genèriques. "Producte", "objecte" o "fotografia" no aporten res a ningú i apareixen constantment.
revisado = FALSE per defecte. És una proposta, no un text publicat. I aquí ve l'important.
Un
altincorrecte és pitjor que no teniralt. Una persona cega que sent "jaqueta, tèxtil, vermell" sobre una foto d'una motxilla blava rep informació falsa, sense cap manera de detectar l'error. L'absència d'altés una mancança; unalterroni és engany.
El flux correcte és esglaonat: publicar automàticament només les propostes d'alta confiança —aquelles on l'etiqueta principal coincideix amb la categoria de la fitxa— i enviar la resta a revisió humana. Amb 2.400 fitxes, si el 80 % coincideix, en queden unes 480 per revisar: una tarda de feina davant d'un projecte de setmanes, i el resultat és correcte.
I una nota d'abast: l'alt que genera aquesta tècnica és descriptiu i correcte, però pla. Redaccions més naturals i riques s'aconsegueixen combinant les etiquetes amb Gemini, que veu la imatge i escriu la frase. Això és 05-06.
- Aplicació 2: colors dominants i el filtre de la botiga
El camp color de les fitxes és un desastre de valors lliures. Els colors dominants de l'API són objectius i consistents, però tenen un problema evident: el fons sol ser el color dominant.
CREATE OR REPLACE TABLE `alpinashop-datos.alpinashop_analitica.productos_color` AS
WITH dominante AS (
SELECT
v.sku, c.red, c.green, c.blue, c.pixel_fraction,
ROW_NUMBER() OVER (PARTITION BY v.sku ORDER BY c.score DESC) AS pos
FROM `alpinashop-datos.alpinashop_analitica.imagenes_vision` v,
UNNEST(v.colores) c
WHERE NOT (c.red > 225 AND c.green > 225 AND c.blue > 225) -- fons blanc
AND NOT (c.red < 30 AND c.green < 30 AND c.blue < 30) -- fons negre
AND c.pixel_fraction >= 0.05
)
SELECT
sku, red, green, blue, ROUND(pixel_fraction, 3) AS fraccion,
CASE
WHEN red > 150 AND green < 90 AND blue < 90 THEN 'vermell'
WHEN blue > 130 AND red < 110 AND green < 130 THEN 'blau'
WHEN green > 120 AND red < 120 AND blue < 120 THEN 'verd'
WHEN red > 190 AND green > 140 AND blue < 90 THEN 'taronja'
WHEN red > 190 AND green > 190 AND blue < 120 THEN 'groc'
WHEN red < 80 AND green < 80 AND blue < 80 THEN 'negre'
WHEN red > 190 AND green > 190 AND blue > 190 THEN 'blanc'
WHEN ABS(red-green) < 28 AND ABS(green-blue) < 28 THEN 'gris'
ELSE 'altre'
END AS color_comercial
FROM dominante
WHERE pos = 1;Els dos filtres del principi són la clau de tot l'apartat. Sense ells, el 90 % dels productes del catàleg sortirien classificats com a "blanc", perquè el fons d'estudi ocupa més píxels que el producte. Descartar els extrems de lluminositat i exigir almenys un 5 % de píxels deixa els colors que realment pertanyen a l'objecte.
La traducció de RGB a nom comercial és aproximada i és una decisió de negoci. Els llindars de dalt són un punt de partida raonable; el color "blau marí" i el "blau elèctric" cauran tots dos en "blau", i això probablement està bé per a un filtre de botiga. Si calgués més finor, la conversió correcta passa per l'espai HSV, on el to se separa de la saturació i la lluminositat.
I la validació imprescindible, que costa deu minuts: un GROUP BY p.color, c.color_comercial unint productos_color amb productos sobre les fitxes que sí que tenen el color declarat. Comparar contra els colors que sí que estan declarats a la fitxa és la comprovació honesta: si "blau" a la fitxa es correspon amb "blau" detectat en la majoria dels casos, el mètode funciona. És exactament la mateixa idea que creuar el sentiment amb les estrelles a 05-04: utilitzar una etiqueta humana que ja existeix per validar la sortida automàtica.
- Aplicació 3: moderar les fotos dels clients amb SafeSearch
Aquest és el cas amb més risc dels quatre, i el que més s'agraeix tenir resolt abans que faci falta.
SafeSearch retorna cinc categories amb sis nivells cadascuna. La política de moderació d'AlpinaShop:
| Categoria | Què detecta | Llindar de bloqueig | Llindar de revisió |
|---|---|---|---|
adult |
Contingut sexual explícit | LIKELY |
POSSIBLE |
violence |
Contingut violent o sangonós | LIKELY |
POSSIBLE |
racy |
Contingut suggerent | VERY_LIKELY |
LIKELY |
medical |
Contingut mèdic o quirúrgic | — | LIKELY |
spoof |
Imatge manipulada o meme | — | LIKELY |
flowchart TD
A[Client puja foto en una ressenya] --> B[Emmagatzemar en quarantena]
B --> C[SafeSearch]
C -->|adult o violence LIKELY+| D[Rebuig automatic<br/>+ avis al client]
C -->|categoria en POSSIBLE| E[Cua de revisio humana]
C -->|tot VERY_UNLIKELY/UNLIKELY| F[Comprovar objecte de producte]
F -->|producte detectat| G[Publicar]
F -->|sense producte| E
E --> H[Decisio humana registrada]
Les cinc decisions de disseny d'aquesta arquitectura:
Quarantena per defecte. La foto s'emmagatzema en un bucket privat i no és accessible públicament fins que passa el filtre. Publicar primer i moderar després significa que el contingut problemàtic va estar visible.
Tres sortides, no dues. Rebuig automàtic, revisió humana i publicació. La franja intermèdia és on viu la major part de l'ambigüitat, i forçar una decisió binària garanteix errors en les dues direccions.
medical i spoof mai no bloquegen automàticament. En una botiga de muntanya, una foto d'una rascada de l'arnès o d'una butllofa és contingut legítim i útil en una ressenya sobre unes botes. Bloquejar-la seria absurd. Va a revisió.
Es comprova també que aparegui un producte. Una foto perfectament innòcua però que no mostra cap producte —un paisatge, una captura de pantalla, una foto accidental— tampoc no s'hauria de publicar en una fitxa. OBJECT_LOCALIZATION resol això i aporta més valor que SafeSearch en el dia a dia.
Tota decisió queda registrada. Amb la versió del model, els nivells retornats i, si hi va haver revisió, qui va decidir i quan. És el que s'ensenya si un client reclama que la seva foto va ser rebutjada injustament.
NIVELL = {"UNKNOWN": 0, "VERY_UNLIKELY": 1, "UNLIKELY": 2,
"POSSIBLE": 3, "LIKELY": 4, "VERY_LIKELY": 5}
def decidir(safesearch):
s = {k: NIVELL[getattr(safesearch, k).name]
for k in ("adult", "violence", "racy", "medical", "spoof")}
if s["adult"] >= 4 or s["violence"] >= 4 or s["racy"] >= 5:
return "REBUTJADA", s
if max(s.values()) >= 3:
return "REVISIO", s
return "APTA", sI l'advertiment final de l'apartat, que és de gestió i no tècnic: cap filtre automàtic no és infal·lible en cap de les dues direccions. Hi haurà contingut problemàtic que passi i contingut innocu que es bloquegi. Per això cal, a més del filtre, un canal de denúncia per als usuaris i un procediment de retirada ràpida. La moderació automàtica redueix el volum de feina humana; no l'elimina, i no trasllada la responsabilitat a la màquina.
- Aplicació 4: auditar l'estàndard visual del catàleg
L'estàndard d'AlpinaShop diu que la foto principal ha de tenir fons clar uniforme, el producte centrat ocupant entre el 50 % i el 85 % de l'enquadrament, i cap objecte aliè. Ningú no ho ha verificat mai.
Amb OBJECT_LOCALIZATION i IMAGE_PROPERTIES es comprova sol:
CREATE OR REPLACE VIEW `alpinashop-datos.alpinashop_analitica.v_auditoria_catalogo` AS
WITH metricas AS (
SELECT
v.sku, v.uri,
-- Area de l objecte principal (coordenades normalitzades 0-1)
(o.x_max - o.x_min) * (o.y_max - o.y_min) AS area_producto,
ABS(((o.x_min + o.x_max) / 2) - 0.5) AS desvio_h,
ABS(((o.y_min + o.y_max) / 2) - 0.5) AS desvio_v,
(SELECT MAX(c.pixel_fraction) FROM UNNEST(v.colores) c
WHERE c.red > 215 AND c.green > 215 AND c.blue > 215) AS fondo_claro,
ARRAY_LENGTH(v.objetos) AS n_objetos
FROM `alpinashop-datos.alpinashop_analitica.imagenes_vision` v,
UNNEST(v.objetos) o
WHERE o.principal
)
SELECT
sku, uri, ROUND(area_producto, 3) AS area, n_objetos,
ARRAY_TO_STRING(ARRAY(
SELECT problema FROM UNNEST([
IF(area_producto < 0.50, 'producte_petit', NULL),
IF(area_producto > 0.85, 'producte_retallat', NULL),
IF(desvio_h > 0.12 OR desvio_v > 0.12, 'descentrat', NULL),
IF(IFNULL(fondo_claro, 0) < 0.35, 'fons_no_estandard', NULL),
IF(n_objetos > 2, 'objectes_aliens', NULL)
]) AS problema WHERE problema IS NOT NULL), ', ') AS problemas
FROM metricas;La llista de treball surt d'unir aquella vista amb productos i amb les vendes dels últims dotze mesos, filtrant problemas != '' i ordenant per unitats venudes descendent.
I aquí hi ha el patró que es repeteix a tot el mòdul, ja per quarta vegada. El resultat no és un sistema que rebutja fotos automàticament. És una llista de treball prioritzada per impacte de negoci: cinquanta fitxes, les més venudes, amb el problema concret de cadascuna. Algú la mira, decideix i demana fotos noves al proveïdor on toqui.
Un sistema automàtic que rebutgés fotos seria més "intel·ligent" i molt pitjor: s'equivocaria en casos legítims, bloquejaria altes de producte i acabaria desactivat al cap de dues setmanes.
- Arquitectura per esdeveniments amb
imagenes-subidas
imagenes-subidasTot l'anterior és processament de l'històric. Per a les imatges noves cal un flux automàtic, i AlpinaShop ja té la peça central: el topic imagenes-subidas de Pub/Sub (04-04).
flowchart LR
A[Pujada a Cloud Storage] -->|notificacio| B[Topic imagenes-subidas]
B --> C[Subscripcio push]
C --> D[Cloud Function 06-03]
D --> E[Vision API]
E --> D
D --> F[BigQuery<br/>imagenes_vision]
D -->|foto de client| G{SafeSearch}
G -->|apta| H[Publicar]
G -->|dubtosa| I[Cua de revisio]
G -->|rebutjada| J[Marcar i avisar]
D -->|error| K[Dead letter topic]
La notificació de Cloud Storage a Pub/Sub es configura amb gcloud storage buckets notifications create gs://alpinashop-catalogo --topic=imagenes-subidas --event-types=OBJECT_FINALIZE --object-prefix=productos/. OBJECT_FINALIZE es dispara quan acaba la pujada d'un objecte, i el prefix evita generar esdeveniments per fitxers que no interessen.
Quatre coses que ja saps del mòdul 4 i que aquí s'apliquen tal qual:
Idempotència. El mateix esdeveniment es pot lliurar més d'una vegada. La clau de negoci és l'URI de l'objecte: si ja existeix una fila per a aquell URI amb la mateixa versió de model, no es reprocessa. Sense això, un reintent costa diners dues vegades.
Dead letter topic. Si el processament falla repetidament —imatge corrupta, format no admès—, el missatge acaba a la cua de missatges fallits en lloc de reintentar-se eternament. És exactament el patró de 04-04.
Autenticació OIDC a la subscripció push, perquè només Pub/Sub pugui invocar l'endpoint.
Desacoblament. La pujada de la imatge no espera l'anàlisi. El proveïdor puja la foto i acaba; l'anàlisi passa després.
La implementació de la funció arriba a 06-03, amb Cloud Functions. Aquí queda definida l'arquitectura i les garanties que ha de complir.
- OCR: text en imatges i documents
L'API distingeix dos modes de reconeixement de text:
| Mode | Pensat per a | Retorna |
|---|---|---|
TEXT_DETECTION |
Text dispers en fotos | Text complet + cada paraula amb la seva posició |
DOCUMENT_TEXT_DETECTION |
Documents densos escanejats | Estructura en pàgines, blocs, paràgrafs i paraules |
Per a les fotos de producte d'AlpinaShop, TEXT_DETECTION té tres usos concrets i útils:
- Verificar la marca impresa al producte contra la marca declarada a la fitxa. Detecta errors d'assignació de fotos.
- Llegir talles i capacitats visibles a la imatge ("40L", "XL", "8000 mm"), útils per comprovar que la foto correspon a la variant correcta.
- Detectar marques d'aigua o text promocional del proveïdor que no hauria d'aparèixer al catàleg ("50% OFF", el logotip del distribuïdor). És un problema real i difícil de trobar a mà.
Es demana amb features=[{"type_": vision.Feature.Type.TEXT_DETECTION}] i es llegeix a resposta.full_text_annotation.text. El primer element de text_annotations conté tot el text detectat; els següents, cada paraula amb el seu requadre. I language_hints=["ca","en"] importa: en material tècnic es barregen el català i l'anglès constantment.
- Límits i biaixos: on falla amb material de muntanya
Igual que a 05-04, toca la part honesta.
Les etiquetes són genèriques per disseny. El model es va entrenar amb un vocabulari ampli i general. Per a AlpinaShop, això produeix resultats com aquests:
| Producte real | Etiquetes típiques retornades | Problema |
|---|---|---|
| Piolet tècnic de cascada | "Destral", "Eina", "Metall" | Categoria equivocada |
| Grampons de 12 puntes | "Metall", "Calçat", "Eina" | No el reconeix |
| Jaqueta amb membrana | "Roba exterior", "Jaqueta", "Blau" | Correcte però superficial |
| Motxilla de 60 L | "Motxilla", "Equipatge", "Bossa" | Correcte |
| Arnès d'escalada | "Cinturó", "Corretja", "Corda" | Descripció parcial |
| Mosquetó HMS | "Metall", "Eina", "Anella" | Irreconeixible |
El patró és clar: com més comú és l'objecte a la vida quotidiana, millor l'identifica. Una motxilla, perfecte. Un mosquetó HMS, una "anella de metall". La raó és simple i no té remei dins de l'API preentrenada: el model va veure milions de motxilles durant el seu entrenament i molt pocs mosquetons.
Biaixos derivats de l'entrenament. El model reflecteix el que hi havia a les seves dades: és més precís amb marques i estils de producte habituals als mercats sobrerepresentats, i pot associar etiquetes de gènere a peces tècniques de forma discutible. Per a AlpinaShop, l'efecte és menor —parlem d'objectes, no de persones—, però mereix una comprovació: si el filtre de color o les etiquetes es comporten sistemàticament pitjor en alguna categoria, cal saber-ho abans que un client ho noti.
I el límit operatiu: les etiquetes no són estables en el temps. El model s'actualitza i les etiquetes poden canviar entre execucions. Per això modelo_version i fecha_analisis no són opcionals.
- Quan fer el salt a AutoML Vision
La taula de l'apartat 12 dibuixa la frontera amb precisió.
| Necessitat | Eina |
|---|---|
| Descriure una imatge en termes generals | Vision API |
| Extreure colors dominants | Vision API |
| Moderar contingut | Vision API (SafeSearch) |
| Llegir text | Vision API (OCR) |
| Comprovar enquadrament i composició | Vision API (objectes) |
| Classificar en la taxonomia pròpia de la botiga | AutoML Vision (05-02) |
| Distingir motxilla de travessa de motxilla d'atac | AutoML Vision |
| Detectar un defecte de fabricació concret | AutoML Vision (detecció d'objectes) |
| Descriure la imatge en llenguatge natural ric | Gemini (05-06) |
El criteri en una frase: si el teu vocabulari és el del món, fes servir Vision API; si el teu vocabulari és el teu, entrena amb AutoML Vision.
I les dues coses es complementen bé. A 05-02, AlpinaShop va entrenar AutoML Vision amb la seva taxonomia de vuit categories precisament perquè l'API genèrica no distingia els seus productes. L'API continua aportant el que AutoML no dóna: colors, text, moderació, enquadrament. No és que una substitueixi l'altra: es demanen funcions diferents sobre la mateixa imatge.
Un flux combinat raonable per a cada foto nova del catàleg: Vision API per a colors, OCR i enquadrament; AutoML Vision per a la categoria pròpia; i Gemini, quan arribi, per redactar la descripció.
- Document AI per a factures i albarans
Hi ha un problema d'AlpinaShop que no és d'imatges de producte: les factures i albarans de proveïdor arriben en PDF i algú els tecleja a mà a l'ERP.
DOCUMENT_TEXT_DETECTION extreu el text d'un PDF escanejat, però retorna text pla: cal escriure expressions regulars per trobar el número de factura, l'import o les línies de detall, i aquelles expressions es trenquen amb cada proveïdor i amb cada canvi de plantilla.
Document AI és un producte diferent i resol el problema d'una altra manera: fa servir processadors especialitzats per tipus de document que retornen camps estructurats.
| Aspecte | Vision OCR | Document AI |
|---|---|---|
| Sortida | Text pla amb posicions | Camps amb nom i valor |
| Factures | Requereix parseig propi | Processador de factures dedicat |
| Taules | Cal reconstruir-les | Extracció de taules nativa |
| Documents propis | No aplica | Processador personalitzat entrenable |
| Cost per pàgina | Menor | Major |
Per a AlpinaShop, el cas d'ús natural seria un processador de factures que extregui proveïdor, número, data, base imposable, IVA, total i línies de detall, i que aboqui el resultat a BigQuery per conciliar-lo amb les comandes de compra. Amb les mateixes cauteles de sempre: una factura conté dades personals i financeres, i l'extracció automàtica requereix validació humana abans de comptabilitzar res.
És un producte que mereix el seu propi projecte i queda fora de l'abast d'aquesta lliçó. El rellevant aquí és saber que existeix i no intentar fer amb OCR i expressions regulars el que té una eina específica.
- Rostres, biometria i AI Act: l'advertiment seriós
Aquest apartat és curt i és el més important de la lliçó.
La Cloud Vision API ofereix detecció de rostres (FACE_DETECTION). Retorna la posició de cada cara a la imatge, punts de referència facials i probabilitats d'expressió (alegria, sorpresa, enuig). No fa reconeixement facial: no identifica qui és la persona ni la compara amb una base de dades.
Aquella distinció és important tècnicament i no basta legalment. Convé entendre per què.
Detecció de rostres. Determinar que hi ha una cara en una imatge i on és. En si mateix no identifica ningú.
Reconeixement facial. Determinar qui és aquella persona. Implica tractament de dades biomètriques, que el RGPD classifica com a categoria especial de dades (article 9), amb un règim molt més estricte: prohibició general amb excepcions taxades, entre elles el consentiment explícit.
Reconeixement d'emocions. Inferir l'estat emocional a partir de la cara. L'AI Act europeu preveu restriccions específiques per als sistemes de reconeixement d'emocions, especialment en els àmbits laboral i educatiu, i estableix obligacions de transparència en altres contextos. Els atributs d'expressió que retorna la detecció de rostres cauen en un terreny que cal valorar amb criteri jurídic, no tècnic.
On apareix això a AlpinaShop, sense que ningú no ho hagi buscat:
- Fotos que pugen els clients a les ressenyes. Una persona es fotografia amb la motxilla posada. La seva cara és a la imatge.
- Fotos de catàleg amb models. Les peces es fotografien sobre persones.
- Fotos d'ambient d'escalada o travessa enviades pel proveïdor.
En cap dels tres casos AlpinaShop no necessita analitzar cares. I aquí hi ha la recomanació pràctica:
No sol·licitis
FACE_DETECTIONsi no tens una necessitat de negoci clara, documentada i validada jurídicament. No la demanis "per si de cas" ni perquè vingui inclosa en un exemple copiat. Cada funció que se sol·licita és una decisió sobre quines dades es tracten.
Si en algun moment calgués —per exemple, per difuminar cares abans de publicar una foto de client, que és un ús protector i raonable—, llavors caldria documentar la finalitat i la base legal, aplicar minimització tractant només l'imprescindible, no conservar les dades facials més enllà del processament, i informar els usuaris a la política de privadesa i al mateix formulari de pujada.
Recomanació expressa. Abans d'habilitar qualsevol funció d'anàlisi facial, un professional de compliment normatiu o el DPD ha de determinar: si el tractament implica dades biomètriques sota l'article 9 del RGPD; la base legal aplicable i si es requereix consentiment explícit; la classificació del sistema sota l'AI Act i les obligacions associades, amb atenció especial a les restriccions sobre reconeixement d'emocions; la necessitat d'una avaluació d'impacte (AIPD); i els requisits d'informació als interessats. Aquesta lliçó descriu controls tècnics i no constitueix assessorament jurídic. Totes les dades són fictícies.
I per a la resta de funcions —etiquetes, colors, OCR, SafeSearch sobre imatges de producte— les precaucions són les de sempre: la imatge s'envia a l'API, cal verificar a la documentació oficial la disponibilitat d'endpoints regionals si la residència de les dades a la UE és un requisit, i les fotos de clients han de tenir un termini de conservació definit i una via de supressió a petició de l'interessat.
Errors Habituals i Consells
Analitzar original/, web/ i thumb/. És la mateixa imatge tres vegades. Multiplica el cost per tres sense aportar res.
Oblidar el permís de lectura sobre el bucket. L'API llegeix la imatge des de Cloud Storage amb el compte de servei. Sense roles/storage.objectViewer, totes les crides fallen.
Confondre pixel_fraction amb rellevància. El fons sol guanyar en fracció de píxels. Sense filtrar els extrems de lluminositat, tot el catàleg surt "blanc".
Publicar l'alt generat sense revisió. Un text alternatiu incorrecte és pitjor que cap: enganya qui no el pot verificar.
Tractar SafeSearch com un booleà. Són cinc categories amb sis nivells. Forçar una decisió binària produeix falsos bloqueigs i falsos passis.
Bloquejar automàticament per medical. En una botiga de muntanya, una foto d'una rascada és contingut legítim en una ressenya.
Demanar totes les funcions sobre totes les imatges. Cada funció factura per separat. SafeSearch sobre fotos de catàleg del proveïdor són diners llençats.
Esperar precisió amb material tècnic. Un mosquetó HMS serà "una anella de metall". Si necessites la teva taxonomia, és AutoML Vision.
Demanar FACE_DETECTION sense necessitat. Mira l'apartat 15.
Consell: desa el mid de cada etiqueta, no només el text. És un identificador estable que no depèn de l'idioma ni de canvis de traducció.
Consell: valida sempre contra una dada humana que ja tinguis. El color declarat a la fitxa, la categoria del producte, la marca. És la comprovació més barata que existeix i la que detecta les fallades silencioses.
Exercicis
Exercici 1
La Marta calcula que analitzar les imatges del catàleg costarà uns 500 € i li sembla excessiu. El seu pla era: totes les imatges del bucket, amb les funcions d'etiquetes, objectes, propietats, SafeSearch, OCR i logotips. Redueix el cost almenys un 90 % sense perdre cap de les quatre aplicacions de la lliçó, i justifica cada retallada.
Exercici 2
Dissenya la comprovació automàtica que detecti fitxes de producte la foto de les quals no correspon al producte descrit. Indica quins senyals faries servir, com els combinaries, i què faries amb els resultats.
Exercici 3
Un client reclama que la seva foto va ser rebutjada injustament en una ressenya sobre unes botes. Descriu quina informació necessites per respondre-li, què s'havia d'haver registrat en el moment del rebuig, i què canviaries al sistema si resulta que el rebuig va ser incorrecte.
Solucions
Solució 1
El pla original: 20.000 imatges × 6 funcions = 120.000 unitats.
Les quatre retallades, per ordre d'impacte:
Retallada 1 — Només la carpeta web/. Les carpetes original/, web/ i thumb/ són la mateixa imatge en tres resolucions. Analitzar-les totes tres és literalment pagar tres vegades pel mateix resultat. De 20.000 a unes 7.000 imatges. Reducció: 65 %.
Retallada 2 — Només la foto principal de cada SKU per a etiquetes, color i OCR. Per descriure el producte, extreure'n el color i llegir-ne la marca, la foto principal és suficient: les altres són el mateix producte des d'un altre angle. De 7.000 a 2.400 imatges. Reducció acumulada: 88 %.
Retallada 3 — Eliminar funcions innecessàries.
| Funció | És necessària? | Justificació |
|---|---|---|
| Etiquetes | Sí | Base de l'alt (aplicació 1) |
| Propietats d'imatge | Sí | Filtre per color (aplicació 2) |
| Objectes | Sí | Auditoria d'enquadrament (aplicació 4) |
| SafeSearch | No aquí | Són fotos del proveïdor, no de clients. Es reserva per al flux de ressenyes |
| OCR | No massiu | Només sobre la mostra on se sospiti marca d'aigua |
| Logotips | No | La marca ja és a la fitxa; no aporta |
De 6 funcions a 3. Reducció acumulada: 94 %.
Retallada 4 — No reprocessar. Desar els resultats a imagenes_vision i analitzar només el que és nou amb la consulta incremental de l'apartat 5. El cost recurrent passa a ser d'unes 200 imatges al mes, és a dir, pràcticament res.
Resultat: 2.400 imatges × 3 funcions = 7.200 unitats davant de 120.000. Una reducció del 94 %, i les quatre aplicacions de la lliçó continuen cobertes:
| Aplicació | Coberta? | Amb què |
|---|---|---|
| Text alternatiu | Sí | Etiquetes sobre la foto principal |
| Filtre per color | Sí | Propietats d'imatge |
| Moderació de fotos de clients | Sí | SafeSearch al flux per esdeveniments, no a l'històric |
| Auditoria de catàleg | Sí | Objectes sobre la foto principal |
El que es perd i cal dir-ho: l'auditoria d'enquadrament només cobreix la foto principal, no totes les del carrusel. És una limitació acceptable —la principal és la que veu el 90 % dels clients—, i sempre es pot ampliar després a les fitxes més venudes.
La lliçó de fons: no s'ha negociat cap preu ni s'ha renunciat a cap funcionalitat. Simplement s'ha deixat de demanar el que no es necessitava. És el mateix raonament del SELECT de columnes concretes a BigQuery (04-01).
Solució 2
Senyals disponibles, i cap no és concloent per si sol:
| Senyal | Origen | Força |
|---|---|---|
| Categoria d'AutoML Vision vs. categoria de la fitxa | 05-02 | Alta |
| Etiqueta principal de Vision API vs. categoria | Vision API | Mitjana |
| Color detectat vs. color declarat | Vision API | Mitjana |
| Marca llegida per OCR vs. marca de la fitxa | Vision API | Alta quan hi ha text |
| Objecte detectat vs. tipus de producte | Vision API | Mitjana |
| Absència total d'objecte reconeixible | Vision API | Alta (foto inservible) |
La combinació: un sistema de punts, no una regla única.
CREATE OR REPLACE VIEW `alpinashop-datos.alpinashop_analitica.v_fotos_sospechosas` AS
WITH senyales AS (
SELECT
v.sku, v.uri, p.nombre, p.categoria, p.marca,
IF(a.categoria_predicha != p.categoria AND a.confianza > 0.85, 3, 0) AS p_automl,
IF(t.marca_detectada IS NOT NULL
AND UPPER(t.marca_detectada) != UPPER(p.marca), 3, 0) AS p_marca,
IF(ARRAY_LENGTH(v.objetos) = 0, 2, 0) AS p_sin_objeto,
IF(c.color_comercial != LOWER(p.color) AND p.color IS NOT NULL, 1, 0) AS p_color,
IF(NOT EXISTS(SELECT 1 FROM UNNEST(v.etiquetas) e WHERE e.score > 0.80
AND LOWER(e.descripcion) LIKE CONCAT('%', LOWER(p.categoria), '%')), 1, 0)
AS p_etiqueta
FROM `alpinashop-datos.alpinashop_analitica.imagenes_vision` v
JOIN `alpinashop-datos.alpinashop_analitica.productos` p USING (sku)
LEFT JOIN `alpinashop-datos.alpinashop_analitica.imagenes_clasificadas` a USING (uri)
LEFT JOIN `alpinashop-datos.alpinashop_analitica.productos_color` c USING (sku)
LEFT JOIN `alpinashop-datos.alpinashop_analitica.imagenes_texto` t USING (uri)
)
SELECT *, p_automl + p_marca + p_sin_objeto + p_color + p_etiqueta AS puntuacion
FROM senyales
WHERE p_automl + p_marca + p_sin_objeto + p_color + p_etiqueta >= 3;Per què els pesos són diferents. La discrepància d'AutoML (3 punts) i la de marca per OCR (3 punts) són senyals fortes: AutoML està entrenat amb la taxonomia pròpia i el text imprès al producte és difícil de malinterpretar. El color (1 punt) és el senyal més feble perquè un producte pot tenir diverses variants de color compartint fitxa, i perquè la traducció RGB a nom és aproximada. L'absència d'objecte (2 punts) indica una foto inservible més que una foto equivocada.
Un sol punt no dispara res. Un llindar de 3 exigeix o bé un senyal fort, o bé la coincidència de diversos de febles. Això redueix dràsticament els falsos positius, que és el que mata aquest tipus de sistemes: una llista amb 800 falses alarmes no la revisa ningú.
Què fer amb els resultats, en tres nivells:
- Puntuació ≥ 6 (diversos senyals forts): revisió prioritària, i si la fitxa està activa i es ven, avisar qui gestioni el catàleg.
- Puntuació 3-5: cua de revisió ordenada per vendes dels últims dotze mesos. L'impacte d'un error en un producte que ven 400 unitats no és el mateix que en un que en ven 2.
- Mai despublicar automàticament. Un fals positiu deixaria un producte sense foto a la botiga, cosa pitjor que el problema que s'intenta resoldre.
I una recomanació operativa: cada revisió humana genera una etiqueta. Al cap d'uns mesos hi ha un conjunt de casos confirmats i descartats que permet calibrar els pesos amb dades en lloc d'amb intuïció —o, si el volum ho justifica, entrenar un classificador propi.
Solució 3
Informació necessària per respondre al client:
- Identificador de la foto i moment exacte de la pujada.
- Resposta completa de SafeSearch: els cinc nivells retornats, no només la conclusió.
- Versió del model i data de l'anàlisi.
- Regla que es va aplicar: quina categoria i quin llindar van disparar el rebuig.
- Si hi va haver revisió humana: qui, quan i amb quin criteri.
- La imatge mateixa, per poder-la mirar.
El que s'havia d'haver registrat en el moment del rebuig:
CREATE TABLE IF NOT EXISTS `alpinashop-datos.alpinashop_analitica.moderacion_imagenes` (
imagen_id STRING NOT NULL, opinion_id STRING, uri_cuarentena STRING,
subida_en TIMESTAMP, analizada_en TIMESTAMP, modelo_version STRING,
nivel_adult STRING, nivel_violence STRING, nivel_racy STRING,
nivel_medical STRING, nivel_spoof STRING,
decision STRING, -- APTA | REVISIO | REBUTJADA
regla_aplicada STRING, -- p. ex. 'adult >= LIKELY'
revisor STRING, -- NULL si va ser automatica
revisado_en TIMESTAMP, motivo_revisor STRING,
reclamacion BOOL, resolucion STRING
)
PARTITION BY DATE(subida_en);Sense aquesta taula, la resposta al client és "el sistema ho va rebutjar i no sabem per què", cosa que és inacceptable i, si la decisió afecta els seus drets, jurídicament problemàtica.
Diagnòstic probable. En una botiga de muntanya, el sospitós habitual és medical o violence disparant-se davant d'una foto d'una butllofa, una rascada o un blau causat per les botes. És exactament el tipus de foto que un client puja per justificar una crítica sobre un calçat incòmode: contingut perfectament legítim, informatiu i rellevant. El segon sospitós és racy davant d'una foto on apareix pell —un peu descalç mostrant la rascada— sense cap connotació.
Què canviaria al sistema:
1. Mai rebuig automàtic per medical. Com ja preveu la política de l'apartat 8, aquesta categoria només ha d'enviar a revisió. Si el rebuig es va produir per aquí, hi ha un error d'implementació respecte de la política definida.
2. Pujar el llindar de racy i abaixar el de revisió. En aquest context, és preferible que passin a revisió humana més fotos de les estrictament necessàries que no pas rebutjar automàticament contingut legítim. El cost d'un fals positiu —un client enfadat i una ressenya perduda— és més gran que el d'uns minuts de revisió.
3. Context en la decisió. Combinar SafeSearch amb la detecció d'objectes: si a la imatge es detecta calçat, motxilla o material esportiu, és senyal que la foto és sobre el producte. Una foto amb medical en POSSIBLE i una bota detectada és gairebé amb seguretat una rascada legítima.
4. Canal de reclamació amb termini. El client ha de poder demanar revisió d'un rebuig, i aquella petició ha d'arribar a una persona en un termini definit. Que existeixi aquest exercici significa que el client va trobar la manera de reclamar, cosa que ja és bona.
5. Missatge de rebuig honest. "La teva foto està pendent de revisió" és correcte i no acusa ningú. "La teva foto ha estat rebutjada per contingut inapropiat" davant d'una butllofa és ofensiu, i a més probablement fals.
6. Revisió periòdica de la taxa de rebuig. Si el percentatge de fotos rebutjades automàticament supera un llindar petit, alguna cosa està mal calibrada. És la mateixa disciplina de les regles de qualitat de Dataplex a 04-07: mesurar el propi procés, no només el resultat.
I la conclusió de gestió: aquest exercici il·lustra per què la moderació automàtica no trasllada la responsabilitat a la màquina. AlpinaShop va respondre per aquella decisió, no el proveïdor de l'API. L'automatització redueix la feina humana; la responsabilitat es queda on era.
Conclusió
Seixanta gigabytes d'imatges que només tenien una ruta de carpeta són ara dades consultables a alpinashop_analitica, i han resolt els quatre problemes amb què començava la lliçó, sense entrenar res i per una xifra que cap al pressupost d'una tarda.
Coneixes les funcions de la Cloud Vision API i, més important, quina serveix per a què: etiquetes per descriure, objectes amb requadre per comprovar composició, OCR per llegir, propietats d'imatge per al color, SafeSearch per moderar, detecció web per vigilar l'ús de les teves fotos. I saps llegir la resposta camp a camp: el mid com a identificador estable davant del text traduïble, les coordenades normalitzades que funcionen igual en qualsevol resolució, i la diferència entre pixel_fraction i score als colors, que és el que separa un filtre de color útil d'un que classifica tot el catàleg com a blanc.
Has processat l'històric amb asyncBatchAnnotate, entenent que és asíncron de debò, que els resultats van a Cloud Storage i no a la teva memòria, i que cal trossejar pel topall de peticions. I els has portat a BigQuery particionats i agrupats, amb modelo_version i fecha_analisis, perquè les APIs preentrenades s'actualitzen soles i sense aquelles columnes no es pot distingir un canvi del model d'un canvi a les imatges.
Sobre el cost, has aplicat el raonament que val per a tot el mòdul: la factura no es negocia, es deixa de demanar el que no es necessita. Analitzar només web/, només la foto principal i només tres funcions redueix la despesa un 94 % sense perdre cap aplicació.
I les quatre aplicacions estan construïdes. El text alternatiu partint del nom del producte i completant-lo amb les etiquetes, amb publicació automàtica només on hi ha coincidència i revisió humana per a la resta, perquè un alt incorrecte enganya qui no el pot verificar. El filtre per color amb els dos filtres de lluminositat que impedeixen que el fons d'estudi s'endugui la classificació, validat contra el color que ja era a la fitxa. La moderació de fotos de clients amb quarantena per defecte, tres sortides en lloc de dues, medical i spoof sense bloqueig automàtic perquè una foto d'una rascada és contingut legítim en una botiga de muntanya, i registre complet de cada decisió. I l'auditoria de l'estàndard visual, que produeix el mateix que van produir AutoML i l'anàlisi de ressenyes: una llista de treball prioritzada per vendes, no un sistema que decideix sol.
Tens l'arquitectura per esdeveniments definida sobre el topic imagenes-subidas, amb idempotència per URI, dead letter topic, autenticació OIDC i desacoblament —les mateixes garanties de 04-04—, a punt perquè la implementació arribi amb Cloud Functions a 06-03.
I coneixes els límits sense adorns: l'API reconeix perfectament una motxilla i anomena "anella de metall" un mosquetó HMS, perquè va veure milions de les primeres i molt pocs dels segons. Aquí hi ha la frontera amb AutoML Vision: si el teu vocabulari és el del món, l'API; si és el teu, entrena. I no competeixen, es complementen sobre la mateixa imatge.
Finalment, l'advertiment que més importa. La detecció de rostres no és reconeixement facial, però aquella distinció tècnica no basta legalment: les dades biomètriques són categoria especial sota l'article 9 del RGPD, i l'AI Act restringeix específicament el reconeixement d'emocions. AlpinaShop té cares a les fotos de clients, a les de catàleg amb models i a les d'ambient, i no necessita analitzar-ne cap. La recomanació és literal: no demanis FACE_DETECTION sense una necessitat documentada i validada jurídicament. Cada funció que sol·licites és una decisió sobre quines dades tractes.
Queden dues preguntes del mòdul 4 sense respondre, i totes dues són de produir en lloc de classificar. Les 2.400 fitxes de producte que ningú no ha escrit, amb descripcions que avui són la fitxa tècnica del proveïdor copiada tal qual. I el cercador de la botiga, que només troba el que coincideix literalment: un client que escrigui "motxilla per a tres dies de travessa" no obté res, perquè cap fitxa no conté aquelles paraules exactes.
A 05-06, IA generativa a Vertex AI, canvia la naturalesa del problema: de classificar a produir. Treballaràs amb la família Gemini des del SDK, entenent què fan de debò temperature, top_p i les instruccions de sistema, i com forçar sortida en JSON. Generaràs les 2.400 fitxes per lots amb control de cost per token i revisió humana obligatòria abans de publicar. Resumiràs les ressenyes de cada producte en tres frases útils, comparant-ho amb el sentiment de 05-04. Entendràs què són les incrustacions de text i construiràs el cercador semàntic del catàleg amb Vector Search o amb VECTOR_SEARCH a BigQuery. Veuràs el patró RAG per a l'assistent d'atenció al client. I afrontaràs el que la IA generativa porta amb ella i les APIs anteriors no tenien: al·lucinacions, filtres de seguretat, grounding, avaluació, transparència del contingut generat, propietat intel·lectual i AI Act.
Curs de Google Cloud Platform (GCP)
Mòdul 1: Introducció a Google Cloud Platform
- Què és Google Cloud Platform?
- Configuració del teu compte de GCP
- Descripció general de la consola de GCP
- Projectes, jerarquia de recursos i facturació
- Regions, zones i model de responsabilitat compartida
- Cloud Shell i la CLI de gcloud
Mòdul 2: Serveis principals de GCP
- Compute Engine: màquines virtuals a Google Cloud
- Cloud Storage: emmagatzematge d'objectes
- Cloud SQL: bases de dades relacionals gestionades
- App Engine: plataforma com a servei
- Google Kubernetes Engine (GKE)
- Bases de dades NoSQL: Firestore, Bigtable i Spanner
- Com triar el servei de còmput adequat
Mòdul 3: Xarxes i seguretat
- Xarxes VPC
- Balanceig de càrrega al núvol
- Cloud CDN
- Gestió d'identitat i accés (IAM)
- Cloud Armor
- Secrets i xifratge: Secret Manager i Cloud KMS
- Cloud DNS, certificats TLS i publicació segura de serveis
Mòdul 4: Dades i anàlisi
- BigQuery: el magatzem de dades analític
- Cloud Dataflow: processament de dades per lots i en temps real
- Cloud Dataproc: Spark i Hadoop gestionats
- Cloud Pub/Sub: missatgeria asíncrona
- Cloud Data Fusion: integració de dades sense codi
- Orquestració de pipelines amb Cloud Composer i Workflows
- Govern de les dades i taulers amb Dataplex i Looker Studio
Mòdul 5: Aprenentatge automàtic i IA
- Vertex AI: la plataforma d'aprenentatge automàtic de GCP
- AutoML: models a mida sense escriure codi
- TensorFlow a GCP: entrenament i servei de models
- API de llenguatge natural
- API de visió
- IA generativa a Vertex AI: models Gemini i incrustacions
- MLOps: del model al producte amb Vertex AI Pipelines
Mòdul 6: DevOps i monitoratge
- Cloud Build: integració contínua a GCP
- Cloud Source Repositories i gestió del codi font
- Cloud Functions: funcions sense servidor
- Cloud Monitoring (abans Stackdriver): mètriques, taulers i alertes
- Cloud Deployment Manager i infraestructura com a codi nativa
- Cloud Logging i Cloud Trace: registres, traces i diagnòstic
- Terraform a GCP: infraestructura com a codi a la pràctica
Mòdul 7: Temes avançats de GCP
- Híbrid i multinúvol amb Anthos
- Computació sense servidor amb Cloud Run
- Xarxes avançades: VPC compartida, aparellament i connectivitat híbrida
- Bones pràctiques de seguretat
- Gestió i optimització de costos
- Fiabilitat: SLO, alta disponibilitat i recuperació de desastres
- Govern a escala: organització, polítiques i auditoria
