Cómo convertir tus PDF en un dataset para entrenar un modelo, sin subirlos a ningún sitio
De PDF a JSONL: extraer el texto, anonimizar, quitar duplicados, repartir por documento y exportar en el formato que espera tu entrenador. En tu equipo.
Por Brayan Pedraza9 min de lectura
En corto
Suelta los PDF en una herramienta que corra en tu navegador o en tu terminal, extrae el texto a Markdown, sustituye los datos personales por marcadores, quita los registros duplicados, reparte los documentos (nunca los trozos) en entrenamiento, validación y prueba, y exporta a JSONL con los nombres de campo que pida tu entrenador. Ninguno de esos cinco pasos necesita un servidor ni una llamada a un modelo, así que los documentos de clientes nunca salen de tu equipo.
El problema que nadie te cuenta hasta que lo sufres
Afinar un modelo con tus propios documentos suena a una tarde de trabajo. En la práctica, hoy solo hay dos caminos y los dos tienen un peaje.
El primero es subir los PDF a un servicio de extracción en la nube. Rápido, bueno, y completamente inviable en cuanto los documentos llevan datos de clientes: historias clínicas, contratos, nóminas, expedientes. No es un problema de confianza en el proveedor, es que tú sigues siendo el responsable del tratamiento y acabas de enviar el dato a un tercero.
El segundo es montar un pipeline de Python pensado para corpus web de terabytes. Funciona, pero está diseñado para otro problema: limpiar Common Crawl, no convertir cuarenta contratos en un conjunto de entrenamiento coherente.
Falta el camino del medio, y es el que describe este artículo: la cadena completa, corriendo donde ya están los archivos.
Los cinco pasos
1. De PDF a texto estructurado
Un PDF no es texto: es una lista de instrucciones para dibujar glifos en una página. Extraerlo bien significa recuperar la estructura —qué era un encabezado, qué era una lista, dónde empieza un párrafo— porque esa estructura es lo que luego te permite trocear por secciones en vez de por número de caracteres.
Si el PDF es un escaneo, no hay capa de texto y hace falta OCR. Conviene que el OCR también corra en local: un escaneo de una historia clínica es exactamente el tipo de archivo que no quieres subir.
Señal de alarma: si el texto extraído tiene los encabezados mezclados con los párrafos, el troceado posterior va a partir las secciones por la mitad y cada fragmento perderá el contexto que lo hacía útil.
2. Anonimizar, y poder volver atrás
Un modelo repite lo que ve. Carlini y otros (arXiv:2012.07805) extrajeron de GPT-2 cientos de secuencias literales del corpus, entre ellas «names, phone numbers, and email addresses», de secuencias que aparecían en un solo documento. Y midieron que los modelos grandes son más vulnerables que los pequeños.
Lo que hacen los pipelines públicos es menos de lo que la gente supone. Dolma se limita a tres tipos detectables con alta precisión: correos, IP y teléfonos. FineWeb anonimiza correos e IP públicas. Llama 3 elimina dominios enteros con mucha PII, no la PII en sí.
La lección honesta viene de StarCoder (arXiv:2305.06161), que entrenó un detector con 22.950 entidades anotadas a mano: 99 % de cobertura en correos, 89 % en nombres y 53 % en identificadores genéricos, tan malo que lo excluyeron. Nadie detecta identificadores genéricos de forma fiable, ni con personas anotando.
Dos consecuencias prácticas:
- Revisa. La anonimización automática es un primer filtro, no una garantía.
- Que sea reversible. Sustituir por un marcador consistente —el mismo valor siempre por el mismo marcador— conserva la coherencia del texto y te deja recuperar el original con el mapa. Sustituir por un token fijo e irreversible, como hacen Dolma o datatrove, destruye información que quizá necesites después.
3. Quitar duplicados
Lee y otros, Deduplicating Training Data Makes Language Models Better (ACL 2022, arXiv:2107.06499):
«over 1% of the unprompted output of language models trained on these datasets is copied verbatim from the training data […] Deduplication allows us to train models that emit memorized text ten times less frequently and require fewer train steps to achieve the same or better accuracy.»
En C4 encontraron una frase de 61 palabras repetida más de 60.000 veces. CCNet midió que los párrafos duplicados eran el 70 % del texto de su corpus. Una carpeta de contratos de empresa es un caso peor, no mejor: la misma plantilla, el mismo membrete, el mismo pie de «este documento es confidencial».
El método estándar es MinHash sobre shingles de 5-gramas de palabras, con LSH para no comparar todos los pares. Los parámetros varían: The Pile usó 10 hashes y umbral 0,5; FineWeb 112 hashes y 0,75; RefinedWeb critica explícitamente los 10 de The Pile por dar «lower deduplication rates and worse model performance».
Detalle que cuesta tiempo: bajar el umbral no basta si el bandeado LSH no acompaña. Si el bandeado está fijo, los pares ni se comparan y bajar el umbral no encuentra más duplicados.
4. Repartir por documento, jamás por trozo
Este es el error que más se comete y menos se nota, porque el resultado es una métrica mejor.
Si troceas los documentos primero y repartes los trozos después, dos fragmentos del mismo contrato acaban uno en entrenamiento y otro en prueba. El modelo ya vio la plantilla, el vocabulario y el formato. La documentación de scikit-learn lo dice en una frase:
«we need to ensure that all the samples in the validation fold come from groups that are not represented at all in the paired training fold»
Kapoor y Narayanan (arXiv:2207.07048) catalogan esto como fuga L3.2 y contabilizan 329 artículos afectados en 17 campos. Un trabajo sobre ventanas deslizantes cuantifica el efecto: el reparto ingenuo «can inflate classification accuracy from a genuine ~20–60 % to an artefactual 99.9 %», con los mismos datos y el mismo modelo.
Además conviene que el reparto sea estable: que la misma semilla dé siempre el mismo resultado y que añadir un documento nuevo no mueva de partición a los que ya estaban. Si no, cada vez que regeneras el conjunto invalidas la evaluación anterior sin darte cuenta.
5. Exportar con los nombres de campo correctos
Cada entrenador espera un esquema distinto y el error se ve tarde, en la validación del archivo. Las cuatro formas que cubren casi todo:
| Forma | Campos | Quién la come |
|---|---|---|
| Texto suelto | text | Preentrenamiento continuado |
| Conversación | messages[{role, content}] | TRL, Unsloth, afinado de OpenAI |
| Instrucción | prompt, completion | TRL |
| Alpaca | instruction, input, output | Axolotl, LLaMA-Factory, Unsloth |
Si además vas a repartir, exporta train.jsonl, validation.jsonl y test.jsonl por separado, y guarda junto al conjunto de dónde salió cada registro. La procedencia es lo primero que te van a preguntar cuando el modelo diga algo raro.
Lo que este camino no puede hacer
Conviene decirlo antes de que lo descubras a mitad del proyecto.
- No genera preguntas y respuestas «inteligentes» a partir de prosa. Eso exige llamar a un modelo con el contenido de tus documentos, que es justo lo que este enfoque evita.
- No construye pares de preferencia para DPO, por lo mismo: hacen falta dos respuestas generadas.
- No lee escritura a mano ni convierte gráficos en datos. Eso necesita un modelo de visión.
Lo que sí se puede hacer sin modelo es más de lo que parece: de una tabla salen pares de pregunta y respuesta con verificación por código —cuánto costó tal cosa, cuántas filas hay, cuál es el máximo de una columna— que es exactamente el tipo de dato que usa el aprendizaje por refuerzo con recompensa verificable. DeepSeek-R1 (arXiv:2501.12948) explica por qué prefirieron la recompensa comprobable por código a un modelo de recompensa: «the neural reward model may suffer from reward hacking in the large-scale reinforcement learning process».
Cómo hacerlo hoy
En el navegador, sin instalar nada: suelta la carpeta de PDF en PDF a datos de IA y descarga el JSONL.
En tu terminal, para miles de documentos:
npx sirdas dataset docs/*.pdf --format chat --split --hf -d dataset/
npx sirdas dedupe docs/*.pdf --threshold 0.8 --json
npx sirdas contamination train/*.pdf --against eval/*.pdf --n 13
Los dos caminos usan el mismo motor y dan el mismo resultado. Ninguno abre una conexión de red con tu contenido.