8 min

Detección de texto IA gratis en local con Python y GPU NVIDIA

Ejecuta el mejor detector de texto IA gratis en tu PC con Python y una GPU NVIDIA: el modelo DeBERTa número uno del benchmark RAID, con análisis frase por frase y modo comparación.

Llevo tiempo curioso sobre la detección de contenido de IA. No sobre cómo engañarla, sino sobre cómo funciona por dentro. ¿Sabías que «el mejor» modelo del mundo es completamente gratuito, corre en cualquier PC y nadie parece saberlo?

Todo el mundo paga quince libras al mes por Originality.ai cuando podría ejecutar el detector mejor valorado en local en su PC en unos diez minutos. De eso va el post de hoy.


Por qué quería esto

Publico mucho. Parte lo escribo yo mismo, parte recibe ayuda de Claude, y quiero ver qué estaban captando los detectores. No una vaga etiqueta de «esto parece generado por IA» de una web. El desglose frase por frase.

¿Herramientas comerciales? Misma historia cada vez. Pegas texto, esperas, recibes un porcentaje. Originality.ai cobra unas quince libras al mes. GPTZero es parecido. Winston AI pide dieciocho. Pero las suscripciones dejan preguntas sin responder.

Como: ¿qué frases concretas están siendo marcadas? ¿A qué patrones reacciona el modelo? ¿Y puedo hacer un escaneo masivo de copy de IA para mi cliente con recomendaciones y referencias de página?

Respuesta a todo eso: sí. Simplemente, desde luego, no a través de una web.

El modelo

El que ejecuto es desklib/ai-text-detector-v1.01. Es un DeBERTa-v3-large fine-tuned que se sitúa el número uno en el benchmark RAID, el gran conjunto de pruebas independiente.

Diez millones de textos generados, doce LLM distintos, once dominios de escritura. ¿La métrica que importa? La tasa de verdaderos positivos al 1 % de falsos positivos. Básicamente: ¿cuántos textos de IA puede cazar marcando erróneamente solo a uno de cada cien humanos?

DeBERTa está arriba del todo.

Lo descarga de HuggingFace la primera vez que lo ejecutas. En tamaño son unos 1,5 GB, más o menos. Tras esa primera descarga se guarda en tu disco y funciona offline.

Cómo funciona la detección

Merece la pena dedicarle tiempo a esto, porque en cuanto ves cómo piensan estos modelos, toda la cuestión de «¿por qué mi reescritura no ayudó?» se responde sola.

Dos conceptos importan. Perplejidad y burstiness.

La perplejidad trata sobre la previsibilidad de tu prosa. Una IA como GPT-4 elige la palabra estadísticamente más probable la próxima vez, siempre. La salida se lee fluida pero de forma muy predecible. Los humanos estamos por todas partes al escribir y no somos fáciles de predecir: frases raras, pensamientos a medias, palabras que no son del todo correctas pero que de algún modo aciertan, errores gramaticales. Ese desorden se registra como alta perplejidad. Los detectores lo adoran como señal humana.

La burstiness trata sobre el ritmo. Lee algo que escribió una persona y notarás que las longitudes de las frases saltan. Unas cuantas cortas, luego una tan larga que debería editarse. Por fin algo que serpentea recogiendo tres digresiones antes de encontrar el camino de vuelta. La IA no hace eso. La IA produce frases de más o menos la misma longitud, misma estructura, misma cadencia pesada. Representa el coeficiente de variación y lo verás enseguida.

DeBERTa va más allá de la perplejidad básica. Ha sido alimentado con millones de muestras etiquetadas, así que ha captado señales más sutiles: distribuciones de tokens, entropía posicional, la forma en que los párrafos se repiten estructuralmente. Pero en realidad sigue haciendo una pregunta: ¿escribió un modelo de lenguaje esto?

¿Empezamos? Solo necesitas tener python instalado.

Lo que necesitarás

  • Python 3.11+ (yo uso 3.13 - python.org)
  • pip (viene con Python)
  • Unos 3 GB de espacio en disco para el modelo en la primera descarga
  • Una GPU NVIDIA si quieres velocidad (opcional: la CPU funciona, solo más lento)

Sin claves de API. Sin cuentas. Nada se envía a ningún sitio.

Instalar (primero en CPU)

Empieza aquí incluso si tienes GPU. Hazlo funcionar en CPU primero, preocúpate de CUDA después.

pip install torch transformers safetensors

Ese es el PyTorch solo para CPU. Windows, Mac, Linux, da igual.

Si tienes dos o tres versiones de Python llenando Windows:

C:\Python313\python.exe -m pip install torch transformers safetensors

El script de detección

Escribí un wrapper llamado detect.py que añade lo que yo quería: puntuación por frase, diagnóstico de patrones y un modo de comparación para pruebas de antes/después. Esto es lo que hace:

  1. Carga DeBERTa (descarga ~1,5 GB la primera vez)
  2. Divide tu texto en frases y clasifica cada una
  3. Te da un porcentaje de IA global más puntuaciones por frase

La carga del modelo es la parte interesante, porque esto es lo que cambia cuando añades una GPU:

def load_model():
    global _model, _tokenizer, _device
    if _model is None:
        _tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
        _device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
        dtype = torch.float32
        if _device.type == "cuda" and torch.cuda.is_bf16_supported():
            dtype = torch.bfloat16
        _model = DesklibAIDetectionModel.from_pretrained(MODEL_NAME, dtype=dtype)
        _model.to(_device).eval()

La detección de CUDA es automática. ¿Tienes GPU y el build correcto de PyTorch? La usará. ¿No tienes? CPU. Sin configuración con la que pelearse.

Primer escaneo

Guarda algo de texto en un archivo y apunta el script hacia él (nota aparte: si has configurado Claude con Desktop Commander puede hacer todo esto en tu flujo)

python detect.py --file article.txt

La salida se ve así:

Loading model...
Model loaded on cpu (desklib/ai-text-detector-v1.01)

--- Per-sentence results ---
[AI  0.9731] The NVIDIA RTX 3080 represents a significant advancement in GPU technology.
[AI  0.9845] This graphics card utilizes the Ampere architecture to deliver exceptional performance.
[HUM 0.2103] I bought mine off eBay for about 250 quid.
...

=== OVERALL ===
Model AI %: 73.2%
AI: 8 | Human: 3 | Total: 11

Salida de detección por frase: rojo para lo marcado como IA, verde para lo clasificado como humano. Las frases casuales pasan; las formales son cazadas siempre.

En CPU, un artículo de 1.500 palabras tarda unos cuatro a seis minutos. La mayor parte es la carga inicial del modelo.

También hay un modo de comparación:

python detect.py --compare original.txt rewritten.txt

Útil cuando iteras sobre un texto.

Añadir una GPU

Aquí es donde se pone interesante.

Lo corrí todo en CPU durante semanas. Bien para lo que era, solo lento. Luego metí una RTX 3080 en mi workstation. La conseguí en eBay por unas 250 libras: una pequeña apuesta comprar de segunda mano, pero ha sido impecable.

La GPU estaba dentro, los drivers instalados, nvidia-smi mostraba la tarjeta. Pero PyTorch no tenía ni idea de que estaba ahí. torch.cuda.is_available() seguía devolviendo False.

nvidia-smi confirmando que la RTX 3080 es reconocida y que CUDA 13.1 está disponible.

El gotcha es este: PyTorch entrega un build solo para CPU por defecto. Cuando haces pip install torch, obtienes una versión sin soporte CUDA. Mientras tanto, tu GPU se calienta sin ningún motivo. Tardé una hora entera en darme cuenta de qué pasaba, lo cual fue vergonzoso.

Comprueba tu build actual

import torch
print(f"torch {torch.__version__}")
print(f"CUDA available: {torch.cuda.is_available()}")
print(f"torch CUDA build: {torch.version.cuda}")

Si torch.version.cuda dice None, ese es tu problema.

Cambia al build de CUDA

Desinstala primero:

pip uninstall torch torchvision -y

Luego instala con el índice de CUDA:

pip install torch torchvision --index-url https://download.pytorch.org/whl/cu126

Ese cu126 significa CUDA 12.6. Comprueba qué soporta tu driver con nvidia-smi: la versión de CUDA está arriba a la derecha. Empareja con la más alta que soporte tu driver.

Aviso justo: unos 2,5 GB de descarga. Tiempo de poner el cacharro.

Verifica

import torch
print(f"torch {torch.__version__}")
print(f"CUDA available: {torch.cuda.is_available()}")
if torch.cuda.is_available():
    print(f"Device: {torch.cuda.get_device_name(0)}")
    print(f"VRAM: {torch.cuda.get_device_properties(0).total_mem / 1024**3:.1f} GB")

Verificación de CUDA: torch ve la GPU y reporta 10 GB de VRAM. El sufijo +cu130 en la cadena de versión confirma que este es el build de CUDA, no el de solo CPU.

El bug de dtype que sufrí

El modelo cargó bien en CUDA y luego crasheó en la primera frase:

RuntimeError: mat1 and mat2 must have the same dtype, but got Float and BFloat16

El clasificador del modelo cargó en bfloat16, pero el paso de mean pooling tenía .float() hardcoded. Dtypes mezclados. Arreglo de una línea en detect.py:

# Before:
input_mask_expanded = attention_mask.unsqueeze(-1).expand(last_hidden_state.size()).float()

# After:
input_mask_expanded = attention_mask.unsqueeze(-1).expand(last_hidden_state.size()).to(last_hidden_state.dtype)

Empareja la mask con el dtype en el que corre el modelo. La CPU acaba con float32, la GPU con bfloat16. Arreglado.

La diferencia de velocidad

Mismo comando, mismo artículo. Menos de un minuto en total. ¿Recuerdas aquel texto de 1.500 palabras? Seis minutos en CPU. ¿En la GPU? Cuarenta segundos. Cuarenta.

El día y la noche. A seis minutos por vez, solo escanearías una vez, justo al final. ¿Cuarenta segundos? Ahora lo ejecuto después de cada pasada de edición.

Integrarlo con Claude Code

No planifiqué esta parte. Resultó ser mi parte favorita de todo el montaje.

Uso Claude Code (el agente CLI de Anthropic) para la mayoría de mi flujo de escritura. Ejecuta comandos de shell, lee archivos, edita texto: todo. Así que lo apunté a mi script de detección y ahora escanea artículos como parte del proceso de edición.

El patrón: escribo algo (o Claude escribe un primer borrador), lo guardo en un archivo y Claude ejecuta detect.py contra él. Lee las puntuaciones por frase, identifica qué frases fueron marcadas como IA y reescribe solo esas: más cortas, más desordenadas, más contracciones, más fragmentos.

Luego escanea otra vez. Lava y repite.

Igual con Claude Desktop si le has dado acceso a archivos. Suelta tu artículo en una carpeta, dile que ejecute el detector e intentará arreglar las frases marcadas.

Lo más grande que he aprendido de mirar puntuaciones: al detector le da completamente igual el vocabulario. Cambiar palabras de sitio no hace casi nada. Lo que mueve la puntuación es la estructura. Variación de longitud de frase, fragmentos, contracciones, apuntes al final, detalles en primera persona. Los patrones que un humano usa sin pensar pero que un LLM casi nunca produce.

Qué mueve la puntuación

Lo probé yo mismo. Escribí tres versiones del mismo párrafo, cada una progresivamente más desordenada.

Versión 1 - Voz de IA pura (97 % IA):

Cada frase marcada. Longitud uniforme, verbos formales («utilizes», «provides»), sin contracciones, mismo patrón Sujeto-Verbo-Adjetivo-Objeto todo el rato.

Versión 2 - Primera reescritura (58 % IA):

Mejor. Primera persona, contracciones, frases coloquiales. Aún 58 %, eso sí.

Versión 3 - Segunda pasada (51 % IA):

Más fragmentos. Más variación. El «Cuarenta.» repetido por su cuenta es el tipo de cosa que cambia fiablemente una frase de IA a humano.

Patrones que funcionan

Qué hacerEjemplo
Fragmentos de frase«Sin rodeos.» / «Cuarenta.»
Contracciones por todas partes«it's» en vez de «it is», «doesn't» en vez de «does not»
Verbos coloquiales«lo metí» en vez de «lo instalé en»
Detalles en primera persona«me costó unas 250 libras» en vez de «con un precio de aproximadamente 300 $»
Apuntes al final«que pareció un chollazo»
Partir frases largasdos frases contundentes de 10 palabras en vez de una formal de 25

Lo que no funciona: cambiar palabras individuales, meter más adjetivos, reordenar cláusulas dentro de la misma forma de frase. ¿Vocabulario? Le da completamente igual el vocabulario. Está mirando cómo construyes las frases, no qué palabras eliges.

Cosas que he aprendido

La primera ejecución descarga el modelo. Unos 1,5 GB de HuggingFace. Necesita conexión a internet una vez, luego se cachea localmente. Funciona offline a partir de ahí.

Las frases cortas se saltan. Menos de cinco palabras no es contexto suficiente para clasificar. Fragmentos, frases de una palabra: ninguno se puntúa.

No persigas el cero. Cualquier cosa por debajo del 20 % está bien. He visto a escritores sufrir por las últimas frases y acabar con un copy que lee como escrito por un crío conmocionado. Simplemente... no te preocupes.

Se equivoca. Las frases cortas y factuales se marcan como IA incluso cuando las escribió un humano. Las frases largas y divagadoras llenas de apuntes y contracciones casi siempre pasan. Dice más sobre lo que mide DeBERTa que sobre tu escritura, siendo justos.

Las puntuaciones no derivan. Los pesos del modelo están congelados en tu disco, así que obtienes resultados idénticos cada vez. Escanea algo hoy, escanéalo en seis meses, mismo número. Prueba eso con Originality.ai: actualizan cuando les da la gana, así que seguir cualquier cosa a lo largo del tiempo es una pérdida de esfuerzo.

El modo de comparación es genial. --compare before.txt after.txt te muestra exactamente qué hizo tu reescritura a la puntuación de cada frase. Mucho mejor que ejecutar dos escaneos y entrecerrar los ojos con los números tú mismo.


Añadir la GPU barata hizo esto práctico: cuarenta segundos en vez de seis minutos es la diferencia entre algo que te molestarás en hacer y algo que no.

Modelo gratis, un poco de Python y tus palabras nunca salen de tu disco duro. No puedo creer que más gente no esté haciendo esto. ¡Disfrútalo!

Artículos relacionados