Agrupamiento difuso: el algoritmo fuzzy c-means y los puntos entre clusters
K-means y su asignación nítida, fuzzy c-means con pertenencias a todos los clusters, el fuzzificador m, el coeficiente de partición y el índice de Xie–Beni para elegir el número de clusters, y por qué los puntos atípicos reciben pertenencias altas.
Serie Fundamentos de IA Parte 36 de 52
- Mapa de la inteligencia artificial: qué es cada cosa y cómo encaja todo
- Breve historia de la inteligencia artificial: de Turing a los modelos de lenguaje
- Datos, modelo, entrenamiento, inferencia: los conceptos básicos en un solo ejemplo
- Cómo leer la notación matemática: símbolos, subíndices, sumatorios y cuantificadores
- Conjuntos y relaciones: las piezas con las que se construyen las definiciones en IA
- Funciones y aplicaciones: por qué todo modelo de IA es una función
- Lógica y demostraciones: las reglas con las que se razona en IA
- Grafos y árboles: la estructura sobre la que se apoyan la búsqueda y las redes neuronales
- Álgebra lineal para aprendizaje automático: vectores, matrices y por qué la IA sobre todo multiplica
- Derivadas, gradiente y regla de la cadena: cómo sabe un modelo en qué dirección aprender
- Descenso de gradiente: cómo aprende un modelo
- Qué es la probabilidad: del lanzamiento de un dado a los axiomas de Kolmogórov
- Probabilidad condicionada y teorema de Bayes: cómo cambiar una estimación cuando te enteras de algo nuevo
- Variable aleatoria y función de distribución: una sola función que lo dice todo sobre la distribución
- Función de densidad y función de probabilidad: por qué una variable continua tiene probabilidad cero en cada punto
- Esperanza, varianza y covarianza: dónde está una distribución, qué anchura tiene y qué se relaciona con qué
- Resumen de las distribuciones de probabilidad: Bernoulli, binomial, Poisson, uniforme, normal y exponencial
- Ley de los grandes números y teorema central del límite: por qué las medias se comportan de forma tan predecible
- Estadística y estimación de parámetros: máxima verosimilitud, MAP e intervalos de confianza
- Teoría de la información: entropía, entropía cruzada y divergencia KL, el origen de las funciones de pérdida
- Búsqueda en el espacio de estados: BFS, DFS, UCS y A* con animaciones
- Juegos y adversarios: minimax, poda alfa-beta y Monte Carlo tree search
- Satisfacción de restricciones: backtracking, propagación de restricciones y AC-3 con un sudoku y un mapa de regiones checas
- Lógica y sistemas basados en conocimiento: encadenamiento hacia delante y hacia atrás, resolución y sistemas expertos
- Planificación: STRIPS, el mundo de bloques, heurísticas a partir de la relajación y la planificación en los agentes de IA
- Búsqueda local y recocido simulado: un viajante por las capitales regionales checas
- Algoritmos genéticos: población, cruce, mutación y selección en una mochila para la excursión
- Estrategias evolutivas y programación genética: de la regla de un quinto a la ley de Kepler
- Optimización por colonia de hormigas: cómo un rastro de feromona encuentra un camino corto
- Optimización por enjambre de partículas: inercia, experiencia propia y experiencia del enjambre
- Neuroevolución: evolución de los pesos y la forma de las redes neuronales, y el algoritmo NEAT
- Conjuntos difusos y funciones de pertenencia: cómo escribir «algo cálido»
- Lógica difusa y probabilidad: por qué «algo cálido» no significa «probablemente cálido»
- Lógica difusa: t-normas, implicaciones y reglas SI–ENTONCES
- Control difuso: Mamdani y Sugeno, del ventilador al péndulo invertido sobre un carro
- Agrupamiento difuso: el algoritmo fuzzy c-means y los puntos entre clusters
- Sistemas neuro-difusos (ANFIS): reglas difusas que aprenden de los datos
- Cadenas de Markov: matriz de transición, distribución estacionaria y PageRank
- Modelos ocultos de Márkov: el algoritmo de Viterbi y el casino deshonesto
- Redes bayesianas: por qué está mojada la hierba y qué significa eso para el aspersor
- El filtro de Kalman: cómo estimar a partir de mediciones imprecisas dónde está un coche y a qué velocidad va
- Tipos de aprendizaje automático: supervisado, no supervisado, semisupervisado y más
- Prompts, contexto y RAG: cómo darle a un modelo la información correcta
- Autocodificadores variacionales (VAE): imágenes nuevas a partir de un espacio latente continuo
- Redes generativas adversarias (GAN): el falsificador contra el detective
- Modelos de difusión: cómo el ruido puro se convierte en una imagen
- Procesos de decisión de Markov: estados, acciones, recompensas y ecuaciones de Bellman
- Q-learning y DQN: aprender el valor de las acciones, de la tabla a la red neuronal
- Gradiente de la política y PPO: aprender directamente la política
- Cómo funciona un agente de IA: un modelo de lenguaje en un bucle con herramientas
- Programación con agentes de IA: encargo, pruebas y revisión
- Evaluación y seguridad de los sistemas de IA: cómo medir y cómo vigilar
Temario completo de la serie 52 de 83 temas publicados
- Cómo funciona k-means y qué calla sobre los puntos en la frontera entre clusters.
- Cómo calcula fuzzy c-means las pertenencias y los centros, y qué hace el fuzzificador m.
- Cómo elegir el número de clusters y dónde falla fuzzy c-means.
Conviene conocer antes: Conjuntos difusos y funciones de pertenencia: cómo escribir «algo cálido», Tipos de aprendizaje automático: supervisado, no supervisado, semisupervisado y más
El agrupamiento (clustering) busca en los datos grupos de los que no sabemos nada de antemano: clientes con hábitos de compra parecidos, zonas del mismo tejido en una imagen de resonancia magnética, documentos sobre temas similares. El algoritmo clásico k-means asigna cada punto a exactamente un grupo. Pero los datos reales suelen tener puntos en la frontera: el cliente que compra un poco como un estudiante y un poco como un padre de familia, el píxel en el que se encuentran la sustancia gris y la blanca del cerebro. El agrupamiento difuso permite que un punto pertenezca a varios grupos a la vez, a cada uno un poco.
Continúa las partes Conjuntos difusos y funciones de pertenencia y Tipos de aprendizaje automático, donde el agrupamiento apareció como tarea típica del aprendizaje no supervisado.
1. Agrupamiento nítido: k-means
Definición — el problema de k-means
Para los puntos x1, …, xn ∈ ℝd y un número de clusters c buscamos los centros v1, …, vc y una asignación de los puntos a los clusters que minimicen
J = ∑i=1n ‖xi − vshluk(i)‖2.
El algoritmo de Lloyd alterna dos pasos: asigna cada punto al centro más cercano; recalcula cada centro como la media de sus puntos. Repite mientras la asignación cambie.
Colocas unas cuantas «banderas» entre los datos. Cada punto se une a la más cercana. Luego cada bandera se muda al centro de sus puntos. Los puntos vuelven a unirse a la bandera más cercana, y así sucesivamente, hasta que nada cambia. Cada paso reduce J, así que el algoritmo termina seguro, aunque no necesariamente en la mejor partición posible.
Definición — distancia euclídea
Para los puntos x, v ∈ ℝd es ‖x − v‖ = √( ∑r=1d (xr − vr)2 ). En el plano es la longitud normal de un segmento, por ejemplo del punto M1 = (4,5; 2,5) al centro del cluster A (1,73; 2,31) tal como lo encuentra más abajo fuzzy c-means: √(2,772 + 0,192) ≈ 2,78. El agrupamiento trabaja con el cuadrado ‖·‖2: desaparece la raíz y los puntos lejanos se penalizan más.
def kmeans(X, c):
V = c puntos de X elegidos al azar # centros iniciales
while True:
shluk = [argmin_j ‖x − V[j]‖² for x in X] # paso de asignación
if shluk no ha cambiado: return V, shluk
for j in range(c): # paso de centros
V[j] = media de los puntos x con shluk(x) = j
Teorema — el algoritmo de Lloyd termina
Ningún paso aumenta J, y por eso el algoritmo termina tras un número finito de pasos.
Demostración: Asignación: cada término ‖xi − vshluk(i)‖2 se sustituye por el menor de todos los ‖xi − vj‖2, así que no aumenta. Centros: para los puntos S de un cluster con media x̄ se cumple la descomposición
∑i∈S ‖xi − v‖2 = ∑i∈S ‖xi − x̄‖2 + |S| · ‖x̄ − v‖2
(el término cruzado 2 ∑ (xi − x̄) · (x̄ − v) es nulo, porque ∑ (xi − x̄) = 0). La suma es, por tanto, mínima para v = x̄. Las particiones de n puntos en c clusters son finitas y J disminuye estrictamente en cada cambio de asignación (a igual distancia dejamos el punto en su cluster), así que ninguna partición se repite. ∎
El teorema no dice nada sobre si la partición encontrada es la de menor J; solo que el algoritmo no entrará en un ciclo.
Nuestros datos: tres grupos de 40 puntos —A abajo a la izquierda, B a la derecha y C arriba—, cuatro puntos en la frontera (M1–M4) y dos puntos atípicos lejos de todo (O1, O2). K-means se estabilizó tras cuatro iteraciones:
Encontró bien los clusters, pero tuvo que decidir sobre los puntos de la frontera: puso M1, M3 y M4 en B, y M2 en A. Y eso que M3 está casi exactamente entre B y C; k-means no dice nada de ello. Además, los dos puntos atípicos cayeron en el cluster C, como si le pertenecieran igual que su núcleo.
2. Fuzzy c-means
Definición — fuzzy c-means (Dunn 1973, Bezdek 1981)
En lugar de una asignación buscamos una matriz de pertenencias U = (uij), uij ∈ [0, 1], donde para cada punto ∑j uij = 1, y unos centros vj que minimicen
Jm = ∑i ∑j uijm ‖xi − vj‖2,
donde m > 1 es el fuzzificador. El algoritmo alterna dos pasos que se derivan de las condiciones de optimalidad:
vj = ∑i uijm xi / ∑i uijm, uij = 1 / ∑k (dij / dik)2/(m−1),
donde dij = ‖xi − vj‖.
Cada punto reparte su unidad entre todos los clusters, más para aquel al que está más cerca. El centro de un cluster es una media ponderada de todos los puntos: los que pertenecen al cluster con fuerza tiran de él más, los de otros clusters apenas un poco. La fórmula de la pertenencia compara distancias: si un punto está el doble de cerca de un centro que de otro, recibe (con m = 2) una pertenencia cuatro veces mayor.
Jm = ∑i=1n ∑j=1c uijm ‖xi − vj‖2, ∑j uij = 1
- Jm
- «coste» total de la partición; el algoritmo lo minimiza. Es pequeño cuando los puntos pertenecen sobre todo a centros que tienen cerca
- ∑i ∑j
- cada punto cuenta para cada cluster, a diferencia de k-means, donde se tomaba un solo cluster por punto
- uij
- pertenencia del punto i al cluster j: el peso con el que la distancia entra en el coste
- m
- fuzzificador: sin la potencia (m = 1) convendría dar toda la unidad al centro más cercano y volvería a salir k-means; con m > 1 compensa repartir: un punto a la misma distancia de dos centros paga, con m = 2 y un reparto a medias, solo (½2 + ½2) · d2 = ½d2 en lugar de d2
- ‖xi − vj‖2
- cuadrado de la distancia: lo que cuesta tener el punto «junto a» este centro; un centro lejano sale caro
- ∑j uij = 1
- restricción: sin ella el mínimo sería trivial, todos los uij = 0 y coste cero
uij = 1 / ∑k=1c ( dij / dik )2/(m−1)
- uij
- nueva pertenencia del punto i al cluster j con los centros fijos
- dij / dik
- cociente de distancias: cuántas veces está el centro j más lejos que el centro k; el cociente (no la diferencia) hace que el resultado no dependa de la escala de los datos
- 2/(m−1)
- con qué contundencia decide el cociente: para m = 2 es 2, así que un centro el doble de cerca recibe cuatro veces más; para m → 1 el exponente crece sin límite y todo se lo lleva el centro más cercano
- ∑k
- comparación con todos los centros, incluido el propio j (el término k = j vale 1); gracias a ello las pertenencias de un punto suman exactamente 1
Teorema — de dónde salen los dos pasos
Pertenencias con los centros fijos. Para un punto i minimizamos ∑j uijm dij2 con la restricción ∑j uij = 1. La función de Lagrange es L = ∑j uijm dij2 − λ (∑j uij − 1). De ∂L/∂uij = m uijm−1 dij2 − λ = 0 se obtiene uij = (λ/m)1/(m−1) · dij−2/(m−1). Al sustituir en la condición de la suma, la incógnita λ se cancela y queda
uij = dij−2/(m−1) / ∑k dik−2/(m−1) = 1 / ∑k (dij / dik)2/(m−1).
Salen números positivos, así que no hace falta vigilar aparte la condición uij ≥ 0. (Si un punto está justo en un centro, dij = 0, recibe pertenencia 1 a ese cluster; nuestra implementación lo resuelve haciendo que la distancia nunca baje de 10−12).
Centros con las pertenencias fijas. Jm es en vj una función cuadrática convexa y ∂Jm/∂vj = −2 ∑i uijm (xi − vj) = 0 da vj = ∑i uijm xi / ∑i uijm.
Cada paso minimiza, por tanto, Jm con el otro grupo de variables fijo y no puede aumentarlo: el mismo argumento que en k-means. Bezdek y colaboradores (1987, corrección del teorema original de Bezdek de 1980) demostraron que la sucesión tiene sus puntos de acumulación en puntos estacionarios de Jm (no necesariamente en el mínimo global).
import numpy as np
def fcm(X, c, m=2.0, iteraci=100, eps=1e-5):
U = np.random.dirichlet(np.ones(c), size=len(X)) # pertenencias aleatorias, fila = 1
for _ in range(iteraci):
W = U ** m
V = (W.T @ X) / W.sum(axis=0)[:, None] # centros como medias ponderadas
D = np.linalg.norm(X[:, None, :] - V[None, :, :], axis=2) + 1e-12
nove = 1 / ((D[:, :, None] / D[:, None, :]) ** (2 / (m - 1))).sum(axis=2)
if np.abs(nove - U).max() < eps:
return V, nove
U = nove
return V, U
3. Fuzzy c-means con nuestros datos
El color de cada punto es ahora una mezcla de los colores de los clusters según las pertenencias: un punto verde puro pertenece solo a A, un punto «entre» el verde y el naranja está en parte en A y en parte en B. Las líneas punteadas muestran cómo se desplazaron los centros; el algoritmo se estabilizó tras nueve iteraciones:
| Punto | k-means | A | B | C |
|---|---|---|---|---|
| M1 (4,5; 2,5) | B | 0,41 | 0,45 | 0,15 |
| M2 (3,3; 4,5) | A | 0,43 | 0,18 | 0,38 |
| M3 (5,8; 5) | B | 0,12 | 0,44 | 0,45 |
| M4 (4,6; 4) | B | 0,28 | 0,40 | 0,32 |
Ahora se ve lo que k-means callaba. M1 está casi a medias entre A y B; M3 está en realidad un pelo más en C que en B, aunque k-means lo puso en B; y M4, en medio del triángulo, pertenece un poco a los tres. Los puntos de los núcleos de los clusters tienen una pertenencia a su cluster cercana a uno. Los centros salieron prácticamente iguales que en k-means: fuzzy c-means no cambia dónde están los clusters, pero añade la información de con qué seguridad pertenece a ellos cada punto.
Ejemplo — pertenencias del punto M1 = (4,5; 2,5)
Centros tras nueve iteraciones: A ≈ (1,73; 2,31), B ≈ (7,12; 2,87), C ≈ (4,51; 7,11). Para m = 2 bastan los inversos de los cuadrados de las distancias:
| Cluster | d2 | 1/d2 | u = (1/d2) / 0,3190 |
|---|---|---|---|
| A | 2,772 + 0,192 ≈ 7,72 | 0,1296 | 0,41 |
| B | 2,622 + 0,372 ≈ 7,03 | 0,1423 | 0,45 |
| C | 0,012 + 4,612 ≈ 21,25 | 0,0471 | 0,15 |
| suma | 0,3190 | 1,00 |
M1 está casi a la misma distancia de los centros A y B (7,72 frente a 7,03), y por eso se reparte entre ellos casi a medias; C está tres veces más lejos (en cuadrado), así que solo recibe el resto.
4. El fuzzificador m
Definición — coeficiente de partición
El coeficiente de partición (partition coefficient) es PC = (1/n) ∑i ∑j uij2. Vale 1 para una partición nítida y 1/c cuando cada punto pertenece por igual a todos los clusters.
Un único número que dice lo «nítida» que es la partición. Para tres clusters está entre 1/3 (todo está difuminado) y 1 (cada punto tiene un hogar claro).
PC = (1/n) ∑i ∑j uij2
- PC
- nitidez de toda la partición, un número de [1/c; 1]
- (1/n) ∑i
- media sobre los puntos, para poder comparar conjuntos de datos de distinto tamaño
- ∑j uij2
- nitidez de un punto: el cuadrado «premia» la pertenencia concentrada: (1, 0, 0) da 1, (⅓, ⅓, ⅓) solo ⅓; con la suma fija en 1, la suma de cuadrados es mínima cuando todas las partes son iguales
Para m cercano a 1, fuzzy c-means se acerca a k-means: el coeficiente de partición es 0,98 y el punto M4 pertenece a B en 0,79. Para m = 2 el coeficiente es 0,84 y para m = 5 solo 0,42: los colores se funden e incluso un punto del núcleo del cluster A tiene una pertenencia a A de solo 0,45. Para m → ∞ todas las pertenencias tenderían a 1/3. Lo más habitual es usar m = 2: lo bastante suave para que se vean los puntos de la frontera y lo bastante nítido para que los clusters sigan siendo claros.
Ejemplo — un punto el doble de cerca de uno de dos centros
Un punto está a distancia 1 del centro 1 y a distancia 2 del centro 2. Según la fórmula, u1 = 1 / (1 + (1/2)2/(m−1)):
- m = 1,2: exponente 10, u1 = 1 / (1 + 1/1 024) ≈ 0,999, una asignación prácticamente nítida,
- m = 2: exponente 2, u1 = 1 / (1 + 1/4) = 0,8,
- m = 5: exponente 0,5, u1 = 1 / (1 + 0,707) ≈ 0,586,
- m → ∞: exponente → 0, (1/2)0 = 1 y u1 → 1/2.
En general: para m → 1+ el exponente 2/(m − 1) tiende a infinito, los cocientes dij/dik < 1 tienden a cero y los > 1 a infinito, así que el centro más cercano recibe 1 y los demás 0 (k-means). Para m → ∞ el exponente tiende a cero, los c términos de la suma tienden a 1 y uij → 1/c.
5. ¿Cuántos clusters?
El número de clusters hay que dárselo al algoritmo. Si no lo sabemos, lo ejecutamos para distintos valores de c y comparamos la calidad de las particiones. Para ello sirven el coeficiente de partición y el índice de Xie–Beni.
Definición — índice de Xie–Beni
XB = ∑i ∑j uijm ‖xi − vj‖2 / (n · minj ≠ k ‖vj − vk‖2); cuanto menor, mejor.
El numerador mide lo apretados que están los puntos junto a sus centros; el denominador, lo lejos que están entre sí los dos centros más cercanos. Una buena partición tiene clusters compactos y alejados unos de otros.
XB = ∑i ∑j uijm ‖xi − vj‖2 / ( n · minj ≠ k ‖vj − vk‖2 )
- XB
- cociente «dispersión dentro de los clusters / distancia entre ellos»; menor es mejor
- ∑ ∑ uijm ‖xi − vj‖2
- compacidad: exactamente el valor de Jm que minimizó el algoritmo
- n
- número de puntos: convierte el numerador en una media por punto, para que el índice no crezca con el tamaño de los datos
- minj ≠ k ‖vj − vk‖2
- separación: decide el par de centros más cercano; Jm por sí solo no hace más que bajar al añadir clusters, y este término penaliza la partición que parte un cluster real en dos cercanos
Ejemplo — índice de Xie–Beni para c = 3
Al terminar el algoritmo, J2 ≈ 166,73. Cuadrados de las distancias entre centros: A–B ≈ 29,4, A–C ≈ 30,8, B–C ≈ 24,79 (los más cercanos son B y C). Para n = 126 puntos:
XB = 166,73 / (126 · 24,79) = 166,73 / 3 124 ≈ 0,053.
| Número de clusters c | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|
| Coeficiente de partición (mayor es mejor) | 0,745 | 0,841 | 0,748 | 0,661 | 0,609 |
| Índice de Xie–Beni (menor es mejor) | 0,228 | 0,053 | 0,298 | 0,682 | 0,529 |
Ambos indicadores coinciden en los tres clusters que pusimos en los datos. Con datos reales esa coincidencia no está garantizada, y los indicadores son más una orientación que un veredicto.
6. Dónde falla fuzzy c-means
Fíjate en los puntos atípicos: O1, arriba a la derecha, tiene una pertenencia a C de 0,45 y a B de 0,39; O2, arriba a la izquierda, incluso 0,58 a C. Y sin embargo no pertenecen a ningún cluster. La culpa es de la restricción ∑j uij = 1: el punto tiene que repartir su unidad entre alguien, aunque esté lejos de todos. La pertenencia significa aquí más bien «de quién está más cerca» que «hasta qué punto pertenece aquí».
Ejemplo — por qué O2 = (0,2; 9,5) «pertenece» a C
Cuadrados de las distancias a los centros: A ≈ 54,07, B ≈ 91,88, C ≈ 24,29. Incluso del centro más cercano, C, está a 4,9 de distancia: más de seis veces la desviación típica de 0,8 con la que los puntos de los clusters están dispersos alrededor de los centros. Los inversos 0,0185, 0,0109 y 0,0412 suman 0,0706 y dan las pertenencias 0,26, 0,15 y 0,58. La fórmula solo ve los cocientes de las distancias, no su tamaño: cuanto más lejos estuviera el punto, más se acercarían los cocientes a 1 y las pertenencias a ⅓, pero nunca tenderían a cero.
Definición — c-means posibilístico (Krishnapuram y Keller, 1993)
Se elimina la restricción de la suma y se añade al coste un término que impide la solución trivial u = 0:
J = ∑i ∑j uijm dij2 + ∑j ηj ∑i (1 − uij)m, de donde uij = 1 / (1 + (dij2 / ηj)1/(m−1)).
ηj es el «radio» del cluster j: un punto a distancia d2 = ηj tiene pertenencia ½ y, para m = 2, un punto con d2 = 9ηj solo 0,1. La pertenencia depende ahora de la distancia a un único centro, no del cociente con los demás: un punto atípico recibe valores pequeños en todas partes.
- El c-means posibilístico (Krishnapuram y Keller, 1993) elimina la restricción de la suma: un punto atípico recibe entonces pertenencias pequeñas a todos los clusters.
- Forma de los clusters. La distancia euclídea produce clusters redondos. El algoritmo de Gustafson y Kessel (1979) da a cada cluster su propia matriz de distancia y encuentra también elipses alargadas.
- Posición inicial. Igual que k-means, puede acabar en un mínimo local peor; en la práctica se ejecuta varias veces desde distintos puntos de partida.
7. El agrupamiento difuso en la práctica
- Imágenes médicas. La segmentación de imágenes cerebrales de resonancia magnética en sustancia gris, sustancia blanca y líquido cefalorraquídeo es un uso clásico: un píxel en la frontera entre tejidos contiene de verdad ambos.
- Segmentación de clientes. Un cliente puede pertenecer en parte a varios grupos, y el marketing puede tenerlo en cuenta.
- Un pariente probabilístico. Las mezclas gaussianas aprendidas con el algoritmo EM también asignan los puntos a los clusters de forma «suave», pero con probabilidades, no con pertenencias. Los resultados suelen ser parecidos; la interpretación difiere justo como lo describió la parte sobre lógica difusa y probabilidad.
- Embeddings. Los textos convertidos en vectores por un modelo de lenguaje se pueden agrupar igual; la asignación suave muestra entonces los documentos que quedan entre temas.
8. Resumen
- K-means asigna cada punto a exactamente un cluster y no dice nada de los puntos de la frontera.
- Fuzzy c-means busca pertenencias uij que suman 1 y centros como medias ponderadas; alterna dos pasos sencillos.
- Con nuestros datos encontró los mismos centros que k-means y además mostró que los puntos M1–M4 pertenecen a dos o tres clusters a la vez.
- El fuzzificador m controla lo suave que es la partición; el coeficiente de partición y el índice de Xie–Beni ayudan a elegir el número de clusters.
- La restricción de suma 1 da a los puntos atípicos pertenencias altas; las variantes posibilísticas lo resuelven.
¿Quieres probarlo tú mismo? Pide a un agente de IA que ejecute fuzzy c-means sobre los colores de los píxeles de una foto tuya y la coloree según los clusters, y fíjate en qué píxeles quedan «entre» colores.
¿Lo ves de otra manera? Escribe a support@coden-app.com.