1 puntos por GN⁺ 2024-11-30 | 1 comentarios | Compartir por WhatsApp
  • Proyecto que creó un modelo de TensorFlow.js para resolver automáticamente el CAPTCHA de 4Chan dentro del navegador, logrando la meta mínima de 80% y el objetivo preferido de más de 90% de precisión
  • La recolección de CAPTCHA fue difícil de resolver con scraping masivo simple debido a la API de solicitudes, ttl, cd, Cloudflare Turnstile y los tiempos de espera crecientes
  • Los servicios comerciales de resolución con humanos y el etiquetado manual se toparon con errores y throttling, así que se generaron unas 50,000 imágenes de datos sintéticos con alrededor de 2,500 fondos reales y 50 a 150 imágenes por carácter para obtener suficiente escala de entrenamiento
  • El modelo usó una arquitectura CNN+LSTM y codificación CTC, y tras entrenarse con Keras/TensorFlow se convirtió a TensorFlow.js pasando por Python 3.10, Keras 2 y el formato .h5
  • En el navegador, la primera carga tardó cerca de 1 segundo y luego la ejecución se sintió instantánea, mostrando una tasa de éxito superior al 90% en cientos de CAPTCHA reales

Objetivo y código publicado

  • El objetivo era aprender machine learning y TensorFlow mientras se construía un modelo capaz de resolver de forma confiable el CAPTCHA de 4Chan en el navegador
  • La referencia era un mínimo de 80% de precisión y, de ser posible, más de 90%, algo que finalmente se logró
  • El código relacionado está publicado en GitHub en 4chan-captcha-playground

Cómo funciona el CAPTCHA de 4Chan

  • 4Chan exige ingresar un CAPTCHA antes de publicar un post o una respuesta
  • El CAPTCHA normal es una imagen con 5 o 6 caracteres alfanuméricos y el usuario debe introducir todos correctamente
  • El CAPTCHA con deslizador funciona alineando una imagen de fondo que parece tener fragmentos aleatorios de caracteres con una imagen de primer plano que tiene agujeros transparentes, para así revelar el texto del CAPTCHA

Límites encontrados al recolectar CAPTCHA

  • Al observar una nueva solicitud de CAPTCHA, el navegador envía una petición a https://sys.4chan.org/captcha?framed=1&board={board}
  • Si se elimina framed=1, en lugar de postMessage() dentro de HTML se devuelve JSON crudo
    • El JSON incluye challenge, ttl, cd, img, img_width, img_height, bg, bg_width y otros campos
    • ttl parece ser el tiempo tras el cual el CAPTCHA expira, aproximadamente 2 minutos después
    • cd se interpreta como el valor de cooldown que hay que esperar antes de solicitar el siguiente CAPTCHA
  • Al enviar solicitudes seguidas, cd va aumentando progresivamente
    • En las primeras veces se puede pedir uno cada 5 segundos
    • Luego sube a 8 segundos y después sigue creciendo aproximadamente al doble
    • Finalmente alcanza un tope de 280 segundos
  • Después de llegar al temporizador de 280 segundos, los CAPTCHA se vuelven más difíciles
    • Aparecen imágenes con múltiples líneas horizontales y elementos ovalados de distracción
    • La calidad de los datos baja, aunque todavía podían usarse
  • Antes de solicitar un CAPTCHA hay que pasar Cloudflare Turnstile
    • No era realista usar muchos proxys ni scripts simples
    • El script de recolección copiaba las cookies de Cloudflare desde el navegador y las reemplazaba manualmente cuando expiraban
  • Con este método se recolectaron varios cientos de CAPTCHA, pero no era suficiente para entrenar ni se tenían etiquetas con las respuestas correctas

Limitaciones del etiquetado con humanos

  • La alineación del CAPTCHA con deslizador logró 100% de éxito usando un script heurístico en trainer/captcha_aligner.py
  • Se escribió trainer/labeler.py para enviar los CAPTCHA a un servicio comercial de resolución, donde personas reales introducían la respuesta
  • En las primeras decenas de CAPTCHA enviadas, la mayoría se resolvían con al menos un carácter incorrecto
  • Se usó la función de “100% Recognition” del servicio para recibir resultados solo cuando varias personas coincidieran en la misma respuesta
    • La configuración fue n = 2, x = 2, y = 3
    • Primero se enviaba a 2 personas y, si no coincidían, se reenviaba a hasta 3 más hasta que dos respuestas fueran iguales
  • Con esta configuración se resolvió alrededor del 80% de los CAPTCHA y, de esos, cerca del 90% eran correctos, pero aproximadamente el 10% seguían teniendo errores
    • En algunos casos, varias personas cometían exactamente el mismo error
  • También se usó un user script para resolver CAPTCHA manualmente, con ayuda propia o de conocidos, y guardar la imagen junto con la respuesta
    • Así se obtuvieron varios cientos de imágenes adicionales para el set de entrenamiento
    • Este enfoque se abandonó por el throttling de solicitudes repetidas y el aumento de dificultad del CAPTCHA

Generación de datos sintéticos

  • Como 4Chan y el código de su CAPTCHA no son open source, no era posible ejecutar localmente el mismo código
  • En su lugar, se generaron CAPTCHA sintéticos que aproximaban la estructura de los CAPTCHA reales
  • El CAPTCHA se trató como dos partes: fondo y caracteres
    • El fondo se obtuvo encontrando grandes contour en imágenes reales y eliminando el área de los caracteres
    • Después de quitar los caracteres, solo quedaba el fondo con ruido
  • Los caracteres individuales se obtuvieron mediante etiquetado manual
    • Se usó VoTT para etiquetar caracteres
    • Un script simple extrajo los caracteres y aplicó posprocesamiento
    • Se consiguieron entre 50 y 150 imágenes aisladas por cada carácter
  • El CAPTCHA de 4Chan solo contiene 0, 2, 4, A, D, G, H, J, K, M, N, P, R, S, T, W, X, Y
    • Probablemente se eligieron para evitar ambigüedades
  • Combinando los caracteres extraídos con los fondos y siguiendo los patrones observados en la colocación de caracteres, se generaron imágenes sintéticas
  • Como los caracteres de entrada ya estaban etiquetados, las respuestas correctas de los CAPTCHA sintéticos también podían generarse automáticamente

Arquitectura del modelo y preprocesamiento

  • Los datos de entrenamiento mezclaban CAPTCHA con deslizador previamente alineados, CAPTCHA normales y CAPTCHA sintéticos
  • El script de entrenamiento ajustaba todas las imágenes a 300x80 píxeles y las convertía a blanco y negro puro
  • El modelo seguía una arquitectura LSTM CNN armada a partir de varios textos sobre resolución de CAPTCHA
    • Se usaron 3 capas de convolution/max-pooling
    • Se usaron 2 capas LSTM
    • También se probó una cuarta capa convolution, pero no mejoró el rendimiento
  • Como la longitud de salida variaba entre 5 y 6 caracteres, se utilizó codificación CTC
  • La implementación usó Keras y TensorFlow

Problema con el orden de argumentos de tf.image.resize()

  • Algunos CAPTCHA antiguos con deslizador ya alineados no coincidían con la resolución 300x80 ni con esa relación de aspecto
  • El script de entrenamiento usaba tf.image.resize() para manejar entradas de distintos tamaños
  • Al principio se asumió que el argumento de tamaño era una tupla (width, height), pero en realidad tf.image.resize() exige el orden (height, width)
  • Por este error, las imágenes terminaban estiradas verticalmente como 80x300 y se volvían ilegibles
    • Incluso tras entrenar más de 32 epoch, el rendimiento sobre imágenes ya vistas era casi nulo
    • En CAPTCHA nuevos hacía predicciones casi aleatorias
  • El problema se detectó al visualizar las imágenes de entrada procesadas, y tras corregirlo el rendimiento del entrenamiento mejoró mucho

Escala de entrenamiento y resultados

  • El dataset final estuvo compuesto por unas 500 imágenes resueltas manualmente y unas 50,000 imágenes sintéticas
  • Las imágenes sintéticas se generaron muestreando aleatoriamente a partir de alrededor de 2,500 imágenes de fondo y de 50 a 150 imágenes por carácter
  • El dataset se mezcló aleatoriamente y luego se dividió en proporción 90/10 entre entrenamiento y evaluación
  • En una NVIDIA RTX A4000 Laptop GPU, el tiempo de entrenamiento por epoch fue de unos 45 segundos
  • Al terminar el primer epoch, el loss rondaba 19 y las predicciones casi no acertaban nada
  • Al terminar el cuarto epoch, el loss había caído hasta 0.55 y acertó 5 de 5 predicciones en una prueba aleatoria
  • Entre 8 y 16 epoch fue un buen equilibrio entre tiempo y rendimiento final
    • Hacia el epoch 8 el loss ya se había estabilizado
    • Después de 16 epoch la mejora adicional se reducía bastante
  • La inferencia se probó en Python con trainer/infer.py y los resultados parecían prometedores incluso en imágenes no vistas

Conversión a TensorFlow.js y ejecución en el navegador

  • El user script fue escrito con TensorFlow.js y TypeScript
  • Se reimplementaron el algoritmo de alineación de CAPTCHA y el código de preprocesamiento de imágenes que existían en Python
  • El código relacionado está en el directorio user-scripts/ del repositorio
  • El formato del modelo de Python TensorFlow/Keras no es compatible con el formato que espera TensorFlow.js
  • Fue necesario usar el script oficial de conversión, pero hubo dos problemas
    • El convertidor oficial de TensorFlow a TFJS no funcionaba en Python 3.12 y el mensaje de error tampoco era claro
    • Al usar Python 3.10 con PyEnv, la conversión sí funcionó
  • El script de conversión podía transformar modelos de Keras 3 al formato de TensorFlow.js, pero TensorFlow.js en realidad no podía leer ese modelo convertido
  • La solución fue usar Keras 2
    • Se instaló el paquete legado tf_keras
    • Se configuró la variable de entorno TF_USE_LEGACY_KERAS=1 para entrenar
    • El modelo se exportó en el formato legado .h5 y se indicó el formato de entrada al script de conversión
    • El cambio en el código solo requirió una modificación simple de una línea

Rendimiento en CAPTCHA reales de 4Chan

  • El modelo también funcionó bien sobre CAPTCHA reales de 4Chan
  • La primera carga del modelo tardaba alrededor de 1 segundo
  • Después de eso, la ejecución se sentía prácticamente instantánea
  • Según la experiencia al resolver cientos de CAPTCHA reales en el navegador, la tasa de éxito fue superior al 90%
  • Era raro que se equivocara en los caracteres mismos; cuando fallaba, normalmente omitía un carácter completo
  • Aún había margen de mejora entrenando con más datos reales o ajustando el layout del generador de datos sintéticos
  • La precisión de este modelo fue mucho mayor que la de los servicios comerciales de resolución de CAPTCHA con humanos

CAPTCHA de 4 caracteres y cierre

  • Después de terminar el proyecto, mientras se escribía y editaba el texto, 4Chan empezó ocasionalmente a ofrecer CAPTCHA de 4 caracteres
  • Aunque el modelo solo se entrenó con CAPTCHA de 5 y 6 caracteres, mostró un nivel de rendimiento similar también en los de 4 caracteres
  • Durante el proyecto se aprendió mucho sobre machine learning y visión por computadora, y se logró completar el objetivo inicial de crear un modelo de resolución de CAPTCHA basado en navegador

1 comentarios

 
GN⁺ 2024-11-30
Opiniones de Hacker News
  • La parte en la que la integración entre Keras y TensorFlow.js es un desastre suena al TensorFlow típico
    Al usar TensorFlow, siempre se sentía menos como un producto integrado y pulido, y más como un conjunto de herramientas que parecían más o menos relacionadas, agrupadas bajo el mismo paraguas
    De hecho, se podría decir que todas las bibliotecas o herramientas open source de Google dan esa misma impresión

    • En relación con eso, hace 15 días hubo un contexto parecido en una publicación sobre François Chollet dejando Google: https://news.ycombinator.com/item?id=42130881
      La respuesta a “¿por qué se decidió fusionar Keras con TensorFlow en 2019?” fue: “No fue una decisión mía. Fue una decisión tomada por los líderes de TF en 2018; en ese momento yo era un contribuyente individual L5 y esa fue una decisión L8”
    • Me recuerda a la ley de Conway
  • Necesitaba un CAPTCHA para evitar spam en el formulario de comentarios de mi sitio[0], así que reutilicé un método interesante que había visto antes
    No es ni de lejos perfecto ni difícil, pero me encantó el proceso de crearlo
    [0] https://www.hybridlogic.co.uk/contact

  • Hay una razón por la que la gente se alejó de los CAPTCHA basados en texto distorsionado
    Ya estamos casi en el punto en que las computadoras los resuelven mejor que las personas
    https://www.usenix.org/system/files/conference/woot14/woot14... es un paper sobre este tema y me parece bastante interesante
    Aun así, una cantidad sorprendentemente grande de CAPTCHA basados en texto se puede resolver con un script de shell de unas pocas líneas que hace conversión a escala de grises con ImageMagick, aplica dilatación y erosión, y luego lo pasa a Tesseract
    Pero también existen sitios como https://2captcha.net, así que al final un CAPTCHA se parece más a un mecanismo que exige un pequeño esfuerzo mínimo

    • Que se pueda romper técnicamente no significa que sea inútil
      La solución de este artículo también requirió bastante tiempo, habilidad y esfuerzo, y el resultado no se generaliza bien, así que para otro tipo de CAPTCHA habría que empezar desde cero
      La mayoría de los spammers no podrá reproducir esto, y quienes sí puedan probablemente ganen dinero de forma legítima o apunten a objetivos más rentables
      Este tipo de CAPTCHA todavía funciona bien para elevar el costo del spam exitoso por encima de sus ingresos esperados
    • Me pregunto qué vendrá después
      ¿Se podría crear un foro en el que todos los miembros tengan que hacer una entrevista por video de 15 minutos con un moderador? Sé que “no escala”, pero como mecanismo tipo broma parece posible
    • Veo el CAPTCHA simplemente como otra línea de defensa que aumenta la dificultad para los actores que abusan del sistema
      No es una solución; es como una pequeña fortaleza que se va quedando obsoleta poco a poco
    • No diría que sea pequeña
      Según el enlace, reCAPTCHA v3 toma entre 10 y 15 segundos y cuesta 1.3 dólares por cada 1000 CAPTCHA
      En muchas tareas en las que se quiere evadir CAPTCHA, como scrapear sitios web grandes a gran escala, ese costo en realidad se vuelve considerable y difícil de asumir
    • A ese nivel, un CAPTCHA de prueba de trabajo quizá sea la mejor opción
      mCaptcha.org es una de esas opciones, y hay otras implementaciones
      Los CAPTCHA tradicionales, si son aunque sea un poco efectivos, suelen convertirse en una pesadilla de accesibilidad
  • Si te interesa este tema, también está mi análisis del CAPTCHA de Silk Road que preparé en 2014: https://github.com/mieko/sr-captcha

  • La respuesta de 4chan parece adecuada
    De todos modos se puede resolver fácilmente con redes neuronales, así que simplifican el trabajo que le dan a la persona
    Hoy, aunque diseñes un CAPTCHA muy difícil, es poco probable que sea difícil para las máquinas, y es mucho más probable que solo moleste más a las personas

    • Entonces también podrían bloquear por completo la publicación para usuarios gratuitos y hacer que todos compren un 4chan Pass de 20 dólares al año para poder publicar
      https://4chan.org/pass
      Ya se ofrece como opción para publicar sin CAPTCHA
      Si el CAPTCHA es completamente inútil, la conclusión que se desprende es eliminar el CAPTCHA y la publicación gratuita, y exigir que cualquiera que quiera publicar compre un 4chan Pass
    • Creo que llevamos atrapados en ese punto al menos 5 años, quizá hasta 10
    • La próxima vez, simplemente pueden usar el escaneo de retina de Worldcoin
    • A 4chan no le importa mucho si las personas se molestan
      Recientemente introdujeron un retraso de 15 minutos para publicar y es realmente irritante
      Tuve que agregar 4chan a la lista de permitidos en Cookie AutoDelete
  • Me pregunto si no sería mejor fingir que hay un CAPTCHA, pero en realidad analizar el timing y el comportamiento del usuario.
    Para ser sincero, también siento que eso probablemente ya está pasando.
    Si nos vamos completamente a lo meta, también podríamos entrenar una IA para que determine si el actor del otro lado es una persona o no.
    Es decir, estaríamos inventando una prueba de Turing inversa, donde si la IA no puede distinguir la respuesta de la de una persona normal, se la considera persona.
    La diferencia es que no se trata de distinguirla de una respuesta humana usada para marketing.
    Ya solo pensar en esto me da algo de náusea, así que voy a acostarme.

    • Los grandes proveedores de CAPTCHA en general ya hacen eso.
      Incluso antes de mostrar un CAPTCHA, primero identifican la huella TLS, la IP, HTTP/2, la solicitud, el entorno JavaScript, la capacidad de renderizar fuentes e imágenes y el propio navegador.
      Con esa información calculan una puntuación de confianza y deciden si mostrar o no un CAPTCHA desde el principio.
      Solo después tiene sentido analizar la entrada del CAPTCHA, pero para entonces ya atraparon al 90% de los bots.
      La cantidad de información que un navegador puede comunicarle al servidor sin que nos demos cuenta es absurdamente grande, al punto de que es muy posible que la huella digital de cada uno de nosotros sea más única que una huella dactilar real.
    • Eso es lo que hace reCAPTCHA.
  • Sigo pensando que el caso casi original de romper el CAPTCHA de 4chan es el de Yannick Kilcher, que ajustó finamente GPT-J con el dataset “Raiders of the Lost Kek”.
    Quizás sea uno de los usos más geniales de un modelo de lenguaje grande que se hayan mostrado en video: https://youtu.be/efPrtcLdcdM?si=errY0PrEhnX9ylDw

    • Casi un minuto entero son solo descargos y advertencias sobre 4chan.
      Para la historia.
  • Hace unos años casi abandoné la idea de probar un poco de aprendizaje automático por cosas como “el conversor oficial de modelos de TensorFlow a TFJS no funciona en Python 3.12 y tampoco está bien documentado” y “TensorFlow.js no soporta Keras 3”.
    Demasiadas veces los tutoriales recientes ya estaban obsoletos, había muchas trampas aleatorias y era sorprendente la cantidad de guías de “primeros pasos” que asumían que uno ya era experto.

    • Como alguien que lleva años haciendo aprendizaje automático, recomendaría evitar lo que está de moda.
      Conviene aprender las bases con un viejo libro de texto de estadística bayesiana y luego pasar a un framework principal como PyTorch.
      Al principio es mejor escribir uno mismo cada parte de las arquitecturas CNN, RNN y Transformer, y del pipeline de entrenamiento.
      Incluido el data loader, aunque se pueden omitir los kernels de matrices CUDA.
      Es mejor mantenerse lejos de wrappers sobre wrappers ajenos, como LangChain.
      Muchas veces la documentación no solo está obsoleta, sino que directamente se equivoca sobre los fundamentos.
      Hugging Face es excelente si conoces las bases y puedes arreglar las cosas cuando se rompe el wrapper estándar.
  • Esto se parece a pasar varias horas aprendiendo cómo abrir la tapa de una fosa séptica.

    • Curiosamente, la mayor parte de 4chan se siente menos pudrecerebros que Twitter antes de Musk.
    • No hay que subestimar lo que se puede aprender estudiando sistemas sépticos.
  • Si sigues los enlaces a servicios de resolución de CAPTCHA, puedes leer los perfiles de la gente que hace ese trabajo.
    Se promociona como algo más ético que trabajar en una fábrica peligrosa.