Autor: LEONI
Materia: Inteligencia Artificial
Fecha: Noviembre 2025
Este proyecto implementa un sistema de clasificación automática de documentos utilizando técnicas de Procesamiento de Lenguaje Natural (NLP) y Machine Learning. El sistema es capaz de clasificar documentos escaneados (archivos .tif) en 16 categorías:
- Advertisement (publicidad)
- Budget (presupuestos)
- Email (correos electrónicos)
- File Folder (carpetas de archivos)
- Form (formularios)
- Handwritten (documentos manuscritos)
- Invoice (facturas)
- Letter (cartas)
- Memo (memorandos)
- News Article (artículos de noticias)
- Presentation (presentaciones)
- Questionnaire (cuestionarios)
- Resume (currículums vitae)
- Scientific Publication (publicaciones científicas)
- Scientific Report (reportes científicos)
- Specification (especificaciones técnicas)
El pipeline completo incluye:
- Extracción de texto mediante OCR (Tesseract) directamente desde archivos .tif
- Preprocesamiento avanzado de texto con NLP
- Análisis exploratorio exhaustivo de datos
- Entrenamiento y evaluación de múltiples modelos de ML
- Validación cruzada y detección de overfitting
- Deployment del mejor modelo
- Desarrollar un clasificador robusto de documentos basado en imágenes
- Implementar un pipeline completo de ML desde datos raw hasta deployment
- Aplicar técnicas avanzadas de NLP para feature engineering
- Realizar análisis comparativo de múltiples algoritmos de ML
- Validar la generalización del modelo mediante cross-validation
- ✅ Selección y justificación de representación/features: TF-IDF con n-gramas
- ✅ Selección y justificación de algoritmos: 5 modelos evaluados comparativamente
- ✅ Análisis exploratorio exhaustivo: EDA completo con visualizaciones
- ✅ Argumentación de decisiones: Cada paso está documentado y justificado
- ✅ Evaluación de PCA: Análisis de necesidad y decisión fundamentada
- ✅ Detección de overfitting: Cross-validation con 5-folds y métricas train/val
- PIL (Pillow): Carga y manipulación de imágenes TIF
- Tesseract OCR: Extracción de texto de imágenes (soporta .tif nativamente)
- NLTK: Tokenización, lemmatización, stopwords
- sklearn.feature_extraction.text: TF-IDF vectorization
- WordCloud: Visualización de vocabulario
- scikit-learn: Modelos, métricas, validación cruzada
- Logistic Regression
- Multinomial Naive Bayes
- Linear SVM (LinearSVC)
- Random Forest
- XGBoost: Gradient boosting
- LightGBM: Gradient boosting optimizado
- pandas: Manipulación de datos
- numpy: Operaciones numéricas
- matplotlib: Visualizaciones
- seaborn: Visualizaciones estadísticas avanzadas
Proyecto_AI/
│
├── main.ipynb # Notebook principal con todo el código
├── example.ipynb # Notebook de referencia/ejemplo
├── README.md # Este archivo
│
├── datasets/
│ └── document-classification-dataset-xl/ # Dataset con 16 clases
│ ├── advertisement/ # Publicidades
│ ├── budget/ # Presupuestos
│ ├── email/ # Emails
│ ├── file_folder/ # Carpetas
│ ├── form/ # Formularios
│ ├── handwritten/ # Manuscritos
│ ├── invoice/ # Facturas
│ ├── letter/ # Cartas
│ ├── memo/ # Memorandos
│ ├── news_article/ # Artículos de noticias
│ ├── presentation/ # Presentaciones
│ ├── questionnaire/ # Cuestionarios
│ ├── resume/ # CVs
│ ├── scientific_publication/ # Publicaciones científicas
│ ├── scientific_report/ # Reportes científicos
│ └── specification/ # Especificaciones técnicas
│
└── models/ # Modelos entrenados (generado)
├── model_latest.pkl # Mejor modelo entrenado
├── vectorizer_latest.pkl # Vectorizador TF-IDF
└── metadata_latest.json # Metadatos del modelo
- Python 3.8+
- Tesseract OCR instalado en el sistema
Windows:
# Descargar e instalar desde:
https://github.com/UB-Mannheim/tesseract/wiki
# Por defecto se instala en:
C:\Program Files\Tesseract-OCR\tesseract.exeLinux (Ubuntu/Debian):
sudo apt-get update
sudo apt-get install tesseract-ocrmacOS:
brew install tesseract# Crear entorno virtual (recomendado)
conda create -n proyecto_ai python=3.9
conda activate proyecto_ai
# Instalar librerías
pip install pandas numpy matplotlib seaborn
pip install nltk pytesseract pillow
pip install scikit-learn xgboost lightgbm
pip install wordcloud imbalanced-learnimport nltk
nltk.download('stopwords')
nltk.download('punkt')
nltk.download('wordnet')
nltk.download('omw-1.4')En el notebook main.ipynb, ajustar la ruta según tu instalación:
pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'Proceso:
- Cargar imagen TIF directamente con PIL (Tesseract soporta .tif nativamente)
- Aplicar Tesseract OCR para extraer texto
- Preprocesar texto con NLP
- Almacenar en DataFrame estructurado
Ventaja de usar archivos TIF directamente:
- Tesseract OCR puede procesar archivos .tif sin necesidad de conversión
- Ahorra tiempo de procesamiento y espacio en disco
- Mantiene la calidad original de la imagen
Preprocesamiento de Texto:
La función preprocess_data() realiza las siguientes transformaciones:
- Lowercase: Normaliza el texto
- Eliminación de saltos de línea y tabulaciones: Limpia formato OCR
- Normalización de espacios: Elimina espacios múltiples
- Eliminación de números: Reduce ruido (valores específicos no son relevantes)
- Eliminación de puntuación: Reduce dimensionalidad sin perder semántica
- Tokenización: Divide texto en palabras individuales
- Eliminación de stopwords: Elimina palabras comunes sin valor discriminativo
- Lemmatización: Reduce palabras a forma base (running → run)
Justificación de Lemmatización vs Stemming:
- Lemmatización preserva palabras reales (mejor interpretabilidad)
- Stemming es más agresivo pero puede generar tokens sin significado
- Para clasificación de documentos, preferimos interpretabilidad
Métricas calculadas:
- Conteo absoluto y porcentaje por clase
- Ratio de desbalance (clase_max / clase_min)
- Recomendaciones según nivel de desbalance
Criterios de balance:
- Balanceado: ratio < 1.2:1
- Ligeramente desbalanceado: 1.2:1 - 1.5:1
- Moderadamente desbalanceado: 1.5:1 - 2.0:1
- Severamente desbalanceado: > 2.0:1
Visualizaciones:
- Gráfico de barras (valores absolutos)
- Gráfico de pastel (proporciones)
Objetivo: Identificar si la longitud del texto es un feature discriminativo
Análisis realizado:
- Estadísticas descriptivas por clase (media, std, min, max)
- Boxplot para identificar outliers
- Histograma superpuesto para comparar distribuciones
- Violin plot para visualizar densidad
Insight esperado:
- Diferentes tipos de documentos tienen longitudes características
- Emails y memos tienden a ser más cortos
- Publicaciones científicas y reportes tienden a ser más largos
- Formularios y facturas tienen longitud variable
Funciones:
- Identificación de palabras más frecuentes por clase
- Generación de word clouds visuales
- Detección de vocabulario discriminativo
Utilidad:
- Validar que OCR funciona correctamente
- Identificar palabras clave características de cada clase (ej: "invoice" en facturas, "research" en papers)
- Detectar posibles problemas (palabras incorrectas por OCR deficiente)
División: 70% Train - 20% Validation - 10% Test
Justificación:
- 70% Entrenamiento: Suficiente datos para aprendizaje
- 20% Validación: Ajustar hiperparámetros sin contaminar test
- 10% Test: Evaluación final con datos nunca vistos
Estratificación:
- Mantiene la proporción de las 16 clases en cada conjunto
- Crítico para datasets con múltiples clases
- Asegura representatividad estadística
Proceso de división:
# Primero: separar test (10%)
X_temp, X_test, y_temp, y_test = train_test_split(
X, y, test_size=0.10, stratify=y
)
# Segundo: dividir resto en train (70%) y validation (20%)
X_train, X_val, y_train, y_val = train_test_split(
X_temp, y_temp, test_size=0.222, stratify=y_temp # 20% del total = 22.2% del 90%
)¿Qué es TF-IDF?
- TF (Term Frequency): Frecuencia de término en documento
- IDF (Inverse Document Frequency): Penaliza palabras comunes
- TF-IDF = TF × IDF: Resalta palabras importantes pero no comunes
Justificación de TF-IDF:
- Eficaz para clasificación de texto con múltiples clases
- Reduce peso de palabras comunes automáticamente
- Sparse pero eficiente en memoria
- Baseline sólido, estado del arte para muchos problemas NLP
Configuración utilizada:
TfidfVectorizer(
ngram_range=(1, 2), # Unigrams + Bigrams
max_features=5000, # Limitar dimensionalidad
min_df=2, # Ignorar términos muy raros
max_df=0.95, # Ignorar términos muy comunes
sublinear_tf=True # Escala logarítmica para TF
)Justificación de n-gramas (1,2):
- Unigrams: Capturan palabras individuales importantes
- Bigrams: Capturan frases y contexto local
- Ejemplo: "machine learning" como bigrama es más informativo que las palabras separadas
Alternativas consideradas:
- ❌ Bag of Words: Más simple pero ignora importancia relativa
- ❌ Word2Vec/GloVe: Requieren más datos y tiempo de entrenamiento
- ❌ BERT/Transformers: Computacionalmente costoso para este problema
Objetivo: Determinar si la reducción de dimensionalidad es beneficiosa
Criterios para aplicar PCA:
- Alta dimensionalidad (5000+ features)
- Features correlacionados
- Necesidad de reducir overfitting
- Visualización de datos
Desventajas de PCA para NLP con múltiples clases:
- Pérdida de interpretabilidad: Componentes principales no son palabras
- TF-IDF es sparse: PCA genera matrices densas (más memoria)
- Puede perder información: Features raros pero discriminativos importantes para distinguir entre 16 clases
Decisión tomada:
if pca.n_components_ < X_train_tfidf.shape[1] * 0.3:
# Usar PCA (reducción >70%)
else:
# No usar PCA (mantener TF-IDF sparse)Análisis realizado:
- Curva de varianza explicada acumulada
- Número de componentes necesarios para 95% varianza
- Comparación de eficiencia memoria: sparse vs dense
Modelos seleccionados y justificación:
Pros:
- Rápido y eficiente
- Interpretable (coeficientes = importancia de palabras)
- Funciona bien con features sparse
- Baseline excelente para text classification
Hiperparámetros:
C=1.0: Regularización L2 moderadaclass_weight='balanced': Maneja desbalance de clasessolver='liblinear': Eficiente para datasets medianos
Pros:
- Diseñado específicamente para datos de conteo (TF-IDF)
- Muy rápido, escala bien
- Funciona bien con poco datos
- Estado del arte clásico para text classification
Contras:
- Asume independencia de features (raramente cierto)
Hiperparámetros:
alpha=0.1: Suavizado de Laplace (previene probabilidades cero)
Pros:
- Encuentra hiperplano de máxima separación
- Robusto a overfitting con regularización adecuada
- Eficiente con datos high-dimensional sparse
- Excelente rendimiento en text classification
Hiperparámetros:
C=1.0: Balance entre margen y errordual=False: Más eficiente cuando n_samples > n_features
Pros:
- Maneja relaciones no lineales
- Robusto a overfitting (averaging de múltiples árboles)
- Proporciona importancia de features
- No requiere normalización
Contras:
- Más lento con muchos features
- No optimizado para sparse matrices
Hiperparámetros:
n_estimators=100: 100 árboles de decisiónmax_depth=None: Sin límite de profundidadmin_samples_split=5: Control de overfitting
Pros:
- Estado del arte para muchos problemas
- Rápido y eficiente en memoria
- Maneja relaciones no lineales complejas
- Excelente con features categóricos
Contras:
- Requiere tuning cuidadoso
- Mayor riesgo de overfitting sin regularización adecuada
Hiperparámetros:
n_estimators=100: Número de árbolesmax_depth=7: Profundidad máxima (control de overfitting)learning_rate=0.1: Tasa de aprendizaje
Configuración: 5-Fold Stratified Cross-Validation
¿Por qué Stratified con 16 clases?
- Mantiene las proporciones de las 16 clases en cada fold
- Crítico para problemas multi-clase
- Previene evaluación sesgada
Proceso:
- Dividir datos de entrenamiento en 5 partes (folds)
- Para cada fold:
- Entrenar con 4 folds
- Validar con 1 fold
- Promediar resultados de los 5 folds
Ventajas:
- Uso eficiente de datos (todos los datos se usan para train y validation)
- Estimación robusta del rendimiento
- Reduce varianza de la evaluación
- Detecta overfitting: Si CV score << train score → overfitting
Métricas calculadas:
- Cross-validation mean ± std
- Training accuracy
- Validation accuracy
- F1-score, Precision, Recall (weighted para 16 clases)
- Diferencia train-val (indicador de overfitting)
Detección de Overfitting:
Si (train_accuracy - val_accuracy) > 0.15:
→ OVERFITTING SEVERO
Si (train_accuracy - val_accuracy) > 0.05:
→ OVERFITTING LEVE
Else:
→ SIN OVERFITTING SIGNIFICATIVO
Métricas de evaluación para 16 clases:
- Definición: Porcentaje de predicciones correctas
- Fórmula: (TP + TN) / (TP + TN + FP + FN)
- Cuándo usar: Dataset balanceado
- Definición: De los predichos como clase X, cuántos son realmente X
- Fórmula: TP / (TP + FP)
- Interpretación: Mide "pureza" de predicciones positivas
- Importante cuando: Falsos positivos son costosos
- Definición: De todos los X reales, cuántos fueron detectados
- Fórmula: TP / (TP + FN)
- Interpretación: Mide "cobertura"
- Importante cuando: Falsos negativos son costosos
- Definición: Media armónica de Precision y Recall
- Fórmula: 2 × (Precision × Recall) / (Precision + Recall)
- Cuándo usar: Balance entre precision y recall, dataset desbalanceado
- Visualiza errores de clasificación
- Diagonal: Predicciones correctas
- Fuera de diagonal: Confusiones entre clases
- Utilidad: Identificar qué clases se confunden entre sí
Visualizaciones generadas:
- Matriz de confusión (valores absolutos)
- Matriz de confusión normalizada (porcentajes)
- Gráficos comparativos de métricas por modelo
Objetivo: Entender dónde y por qué falla el modelo
Análisis realizado:
- Identificación de casos mal clasificados
- Examen de texto original de errores
- Identificación de pares de clases más confundidos
- Análisis de patrones en errores
Utilidad:
- Identificar limitaciones del modelo
- Detectar problemas de calidad de datos (OCR errors)
- Decidir si necesitamos más datos de ciertas clases
- Guiar mejoras futuras del sistema
Archivos generados:
- model_latest.pkl: Mejor modelo entrenado (serializado)
- vectorizer_latest.pkl: Vectorizador TF-IDF (preserva vocabulario)
- metadata_latest.json: Metadatos (accuracy, fecha, configuración)
Función de predicción:
result = predict_document_class(
image_path="documento_nuevo.png",
model=best_model,
vectorizer=tfidf_vectorizer,
class_labels_dict=class_labels,
preprocess_func=preprocess_data
)
print(f"Clase predicha: {result['predicted_class']}")
print(f"Confianza: {result['confidence']*100:.2f}%")Pipeline de predicción:
- Cargar imagen
- OCR con Tesseract
- Preprocesar texto
- Vectorizar con TF-IDF
- Predecir con modelo
- Retornar clase + probabilidades
- Accuracy: > 85% en test set
- F1-Score: > 0.80 en todas las clases
- Overfitting: < 0.05 diferencia train-val
Modelo CV Accuracy Val Accuracy F1-Score Overfitting
────────────────────────────────────────────────────────────────────────────
Logistic Regression 0.8850±0.022 0.8900 0.8850 +0.0120
Linear SVM 0.8920±0.018 0.8980 0.8920 +0.0100
Naive Bayes 0.8650±0.031 0.8700 0.8600 +0.0180
Random Forest 0.8750±0.025 0.8650 0.8620 +0.0450
LightGBM 0.8980±0.020 0.8850 0.8800 +0.0380
(Nota: Valores reales dependerán del dataset específico)
# Definir clases
class_labels = {
'email': 0,
'resume': 1,
'scientific_publication': 2
}
# Cargar dataset
df = load_documents_from_images(
dataset_path=r"datasets\document-classification-dataset",
class_labels_dict=class_labels
)# Dividir datos
X_train, X_val, X_test, y_train, y_val, y_test = split_dataset_stratified(
df, train_size=0.7, val_size=0.2, test_size=0.1
)
# Crear features TF-IDF
X_train_tfidf, X_val_tfidf, X_test_tfidf, vectorizer = create_tfidf_features(
X_train, X_val, X_test
)
# Entrenar modelos
model_results, best_model_name = train_and_evaluate_models(
X_train_tfidf, X_val_tfidf, y_train, y_val
)
# Evaluar en test
best_model = model_results[best_model_name]['model']
test_results = evaluate_on_test_set(best_model, X_test_tfidf, y_test)save_model_artifacts(
model=best_model,
vectorizer=vectorizer,
class_labels_dict=class_labels,
model_name=best_model_name,
test_results=test_results
)# Cargar modelo guardado
import pickle
with open('models/model_latest.pkl', 'rb') as f:
model = pickle.load(f)
with open('models/vectorizer_latest.pkl', 'rb') as f:
vectorizer = pickle.load(f)
# Predecir documento nuevo
result = predict_document_class(
image_path="nuevo_documento.png",
model=model,
vectorizer=vectorizer,
class_labels_dict=class_labels,
preprocess_func=preprocess_data
)
print(f"Resultado: {result['predicted_class']}")
print(f"Confianza: {result['confidence']*100:.2f}%")- Distribución de clases (barras y pastel)
- Distribución de longitud de texto (boxplot, histograma, violin)
- Word clouds por clase
- Tabla de estadísticas por clase
- Comparación de accuracy por modelo
- Cross-validation con intervalos de confianza
- Comparación de F1-scores
- Análisis de overfitting por modelo
- Matriz de confusión (absoluta y normalizada)
- Métricas por clase
- Curva de varianza explicada (si se usa PCA)
Criterio: Justificar elección de representación
Respuesta:
- TF-IDF seleccionado por ser estado del arte para text classification
- N-gramas (1,2) para capturar contexto local y frases específicas
- Sparse matrices para eficiencia de memoria
- Alternativas evaluadas: BoW, Word2Vec, BERT (justificado por qué no se usaron)
Criterio: Justificar elección de algoritmos
Respuesta:
- 5 algoritmos evaluados: desde baseline simple (Naive Bayes) hasta ensemble avanzado (LightGBM)
- Justificación individual de cada modelo: pros, contras, cuándo usarlo
- Configuración de hiperparámetros explicada y justificada
- Comparación objetiva mediante cross-validation
Criterio: EDA completo y profundo
Respuesta:
- Balance de clases con análisis de desbalance y recomendaciones
- Distribución de longitud con múltiples visualizaciones
- Análisis de vocabulario con word clouds y frecuencias
- Detección de problemas de calidad de datos
- Visualizaciones múltiples para cada aspecto
Criterio: Justificar cada decisión importante
Respuesta:
- Preprocesamiento: Cada paso explicado (por qué lemmatization y no stemming)
- TF-IDF: Justificación de configuración (n-grams, max_features, etc.)
- PCA: Análisis completo de necesidad con criterios objetivos
- Modelos: Justificación de hiperparámetros
- División de datos: Justificación de 70-20-10
Criterio: Evaluar si PCA es necesario
Respuesta:
- Análisis completo de dimensionalidad actual
- Evaluación de trade-offs: interpretabilidad vs reducción
- Curva de varianza para decisión informada
- Decisión fundamentada: Usar PCA solo si reducción > 70%
- Justificación de no usar: TF-IDF sparse es más eficiente
Criterio: Cross-validation para detectar overfitting
Respuesta:
- 5-fold stratified CV en todos los modelos
- Métricas train vs val comparadas sistemáticamente
- Umbrales definidos: 0.05 (leve), 0.10 (severo)
- Visualización de overfitting por modelo
- Recomendaciones si se detecta overfitting
-
Mejorar calidad de OCR:
- Preprocesamiento de imágenes (binarización, deskew, denoising)
- Usar Tesseract 5.x con LSTM
- Detectar orientación automáticamente
-
Feature Engineering adicional:
- Features de layout (posición de texto, formato)
- Ratio de texto vs espacio en blanco
- Presencia de logos, firmas, imágenes
-
Aumentar dataset:
- Más ejemplos de cada clase
- Diversidad de layouts y formatos
- Documentos de diferentes fuentes
-
Modelos más avanzados:
- Transfer learning con BERT/RoBERTa
- Multimodal (texto + imagen)
- Ensemble stacking de múltiples modelos
-
Deployment en producción:
- API REST con Flask/FastAPI
- Dockerización del sistema
- Monitoreo de performance
-
Más clases:
- Facturas, contratos, formularios
- Dataset extendido con 15+ categorías
-
Procesamiento de documentos complejos:
- PDFs con múltiples páginas
- Documentos escaneados de baja calidad
- Idiomas múltiples
-
Análisis semántico profundo:
- Extracción de entidades (NER)
- Relaciones entre documentos
- Resumen automático
- Ramos, J. (2003). "Using TF-IDF to Determine Word Relevance in Document Queries"
- Sebastiani, F. (2002). "Machine Learning in Automated Text Categorization"
- Bird, S., Klein, E., & Loper, E. (2009). "Natural Language Processing with Python"
- Document Classification Dataset (Kaggle)
LEONI
Maestría en IoT y AI
Materia: Inteligencia Artificial
Noviembre 2025
Este proyecto es parte de un trabajo académico para la Maestría en IoT y AI.
Este es un proyecto académico, pero sugerencias y feedback son bienvenidos.
- Dependencia de calidad de OCR: Imágenes de baja calidad producen texto incorrecto
- Idioma: Sistema entrenado para inglés
- Layouts específicos: Mejor rendimiento con layouts estándar
- Tamaño de dataset: Rendimiento mejorará con más datos
- Mínimo: 8GB RAM, procesador dual-core
- Recomendado: 16GB RAM, procesador quad-core, SSD
- Para GPU: CUDA-compatible GPU (opcional, para modelos deep learning futuros)
- Carga de dataset (~150 imágenes): 5-10 minutos
- Training de todos los modelos: 10-20 minutos
- EDA completo: 5 minutos
- Total: ~30-40 minutos
Para preguntas o problemas:
- Revisar la documentación en este README
- Verificar que todas las dependencias estén instaladas
- Asegurarse de que Tesseract esté correctamente configurado
- Revisar los comentarios en el código del notebook
¡Gracias por usar este sistema de clasificación de documentos! 🎉