From e9c937a042c582a6920d94188c2196c54dd63392 Mon Sep 17 00:00:00 2001 From: sergio Date: Wed, 4 Feb 2026 20:43:50 +0100 Subject: [PATCH] Cross references --- README.md | 2 +- apply_content.py | 583 ++++---------------- claude.md | 11 +- content_handlers.py | 469 ++++++++++++++++ docs/00_resumen.md | 24 +- docs/01_introduccion.md | 4 +- docs/02_contexto_estado_arte.md | 24 +- docs/03_objetivos_metodologia.md | 10 +- docs/04_desarrollo_especifico.md | 28 +- docs/05_conclusiones_trabajo_futuro.md | 4 - docs/07_anexo_a.md | 62 +-- docs/compliance.md | 413 ++++++++------ markdown_utils.py | 153 +++++ thesis_output/figures/figura_12.png | Bin 63656 -> 59532 bytes thesis_output/figures/figura_17.png | Bin 97998 -> 0 bytes thesis_output/figures/figura_18.png | Bin 65068 -> 0 bytes thesis_output/figures/figures_manifest.json | 12 +- thesis_output/plantilla_individual.htm | 139 +++-- 18 files changed, 1118 insertions(+), 820 deletions(-) create mode 100644 content_handlers.py create mode 100644 markdown_utils.py delete mode 100644 thesis_output/figures/figura_17.png delete mode 100644 thesis_output/figures/figura_18.png diff --git a/README.md b/README.md index eeafb81..cb25250 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,7 @@ flowchart TB i2["plantilla_individual.htm"] end - scripts["apply_content.py
generate_mermaid_figures.py"] + scripts["apply_content.py
content_handlers.py
markdown_utils.py
generate_mermaid_figures.py"] config["claude.md
README.md"] end ``` diff --git a/apply_content.py b/apply_content.py index 0052b35..0a33254 100644 --- a/apply_content.py +++ b/apply_content.py @@ -1,136 +1,62 @@ #!/usr/bin/env python3 -"""Replace template content with thesis content from docs/ folder using BeautifulSoup.""" +"""Replace template content with thesis content from docs/ folder using BeautifulSoup. + +This module orchestrates the conversion of markdown documentation to UNIR's +Word template format. Content handling is delegated to: +- markdown_utils.py: Utility functions for markdown parsing +- content_handlers.py: Block-level content handlers (tables, figures, lists, etc.) +""" import re import os import shutil from bs4 import BeautifulSoup, NavigableString -from latex2mathml.converter import convert as latex_to_mathml -from PIL import Image + +from markdown_utils import ( + read_file, + write_file, + md_to_html_para, + convert_latex_formulas, + is_source_line, + is_leyenda_line, + split_into_paragraphs, + SOURCE_LINE_RE, +) +from content_handlers import ( + handle_mermaid_diagram, + handle_code_block, + handle_header, + handle_table, + handle_blockquote, + handle_bullet_list, + handle_numbered_list, +) BASE_DIR = os.path.dirname(os.path.abspath(__file__)) TEMPLATE_INPUT = os.path.join(BASE_DIR, 'instructions/plantilla_individual.htm') TEMPLATE_OUTPUT = os.path.join(BASE_DIR, 'thesis_output/plantilla_individual.htm') DOCS_DIR = os.path.join(BASE_DIR, 'docs') -# Accept Fuente/Source lines with or without markdown bold -SOURCE_LINE_RE = re.compile(r'^\s*(?:\*{1,2})?(Fuente|Source):(?:\*{1,2})?\s*(.*)$', re.IGNORECASE) -# Accept Leyenda lines with or without markdown bold -LEYENDA_LINE_RE = re.compile(r'^\s*(?:\*{1,2})?Leyenda:(?:\*{1,2})?\s*(.*)$', re.IGNORECASE) -# Global counters for tables and figures -table_counter = 0 -figure_counter = 0 -anexo_table_counter = 0 -anexo_figure_counter = 0 -# Global sequential counter for figure filenames (figura_1.png, figura_2.png, etc.) -global_figure_index = 0 +def parse_md_to_html_blocks(md_content, is_anexo=False, counters=None): + """Convert markdown content to HTML blocks with template styles. -def read_file(path): - try: - with open(path, 'r', encoding='utf-8') as f: - return f.read() - except UnicodeDecodeError: - with open(path, 'r', encoding='latin-1') as f: - return f.read() + Args: + md_content: Markdown content string + is_anexo: Boolean indicating if processing Anexo section + counters: Dict with table/figure counters. If None, creates new one. -def write_file(path, content): - with open(path, 'w', encoding='utf-8') as f: - f.write(content) - -def md_to_html_para(text): - """Convert markdown inline formatting to HTML.""" - # Bold - text = re.sub(r'\*\*([^*]+)\*\*', r'\1', text) - # Italic - text = re.sub(r'\*([^*]+)\*', r'\1', text) - # Inline code - text = re.sub(r'`([^`]+)`', r'\1', text) - # Links [text](url) -> text - text = re.sub(r'\[([^\]]+)\]\(([^)]+)\)', r'\1', text) - return text - -def convert_latex_formulas(text): - """Convert LaTeX formulas to MathML for Word compatibility.""" - # Block formulas $$...$$ - def convert_block(match): - latex = match.group(1) - try: - mathml = latex_to_mathml(latex, display="block") - return f'

{mathml}

' - except: - return match.group(0) # Keep original if conversion fails - - text = re.sub(r'\$\$([^$]+)\$\$', convert_block, text) - - # Inline formulas $...$ - def convert_inline(match): - latex = match.group(1) - try: - return latex_to_mathml(latex, display="inline") - except: - return match.group(0) - - text = re.sub(r'\$([^$]+)\$', convert_inline, text) - return text - -def extract_source_from_line(line): - """Return source text if line is a Fuente/Source line, otherwise None.""" - match = SOURCE_LINE_RE.match(line.strip()) - if not match: - return None - return match.group(2).strip() - -def is_source_line(line): - """Check whether a line starts with Fuente:/Source: (optionally bold).""" - return SOURCE_LINE_RE.match(line.strip()) is not None - -def extract_leyenda_from_line(line): - """Return leyenda text if line is a Leyenda line, otherwise None.""" - match = LEYENDA_LINE_RE.match(line.strip()) - if not match: - return None - return match.group(1).strip() - -def is_leyenda_line(line): - """Check whether a line starts with Leyenda: (optionally bold).""" - return LEYENDA_LINE_RE.match(line.strip()) is not None - -def extract_table_title(lines, current_index): - """Look for table title in preceding lines (e.g., **Tabla 1.** *Title*).""" - # Check previous non-empty lines for table title - for i in range(current_index - 1, max(0, current_index - 5), -1): - line = lines[i].strip() - if line.startswith('**Tabla') or line.startswith('*Tabla'): - return line - if line and not line.startswith('|'): - break - return None - -def extract_figure_title_from_mermaid(lines, current_index): - """Extract title from mermaid diagram or preceding text.""" - # Look for title in mermaid content - for i in range(current_index + 1, min(len(lines), current_index + 20)): - line = lines[i].strip() - if line.startswith('```'): - break - if 'title' in line.lower(): - # Extract title from: title "Some Title" - match = re.search(r'title\s+["\']([^"\']+)["\']', line) - if match: - return match.group(1) - - # Check preceding lines for figure reference - for i in range(current_index - 1, max(0, current_index - 3), -1): - line = lines[i].strip() - if line.startswith('**Figura') or 'Figura' in line: - return line - - return None - -def parse_md_to_html_blocks(md_content, is_anexo=False): - """Convert markdown content to HTML blocks with template styles.""" - global table_counter, figure_counter, anexo_table_counter, anexo_figure_counter, global_figure_index + Returns: + Tuple of (html_string, counters) where counters is the updated dict + """ + if counters is None: + counters = { + 'table': 0, + 'figure': 0, + 'anexo_table': 0, + 'anexo_figure': 0, + 'global_figure': 0, + } html_blocks = [] lines = md_content.split('\n') @@ -146,346 +72,49 @@ def parse_md_to_html_blocks(md_content, is_anexo=False): # Mermaid diagram - convert to figure with actual image if line.strip().startswith('```mermaid'): - # Always increment global index for sequential filenames - global_figure_index += 1 - - # Use Anexo-specific counter with "A" prefix for display, or global counter - if is_anexo: - anexo_figure_counter += 1 - fig_num = f"A{anexo_figure_counter}" # Display number: A1, A2, A3... - else: - figure_counter += 1 - fig_num = str(figure_counter) # Display number: 1, 2, 3... - - mermaid_lines = [] - i += 1 - while i < len(lines) and not lines[i].strip() == '```': - mermaid_lines.append(lines[i]) - i += 1 - - # Try to extract title from mermaid content (YAML format) - mermaid_content = '\n'.join(mermaid_lines) - # Match title with quotes: title: "Something" or title: 'Something' - title_match = re.search(r'title:\s*["\']([^"\']+)["\']', mermaid_content) - if not title_match: - # Match title without quotes: title: Something - title_match = re.search(r'title:\s*([^"\'\n]+)', mermaid_content) - if title_match: - fig_title = title_match.group(1).strip() - else: - fig_title = f"Diagrama {fig_num}" - - # Use global sequential index for filename (figura_1.png, figura_2.png, etc.) - fig_file = f'figures/figura_{global_figure_index}.png' - fig_path = os.path.join(BASE_DIR, 'thesis_output', fig_file) - - # Create figure with MsoCaption class and proper Word SEQ field for cross-reference - # Format: "Figura X." in bold, title in italic (per UNIR guidelines) - # Word TOC looks for text with Caption style - anchor must be outside main caption text - bookmark_id = f"_Ref_Fig{fig_num}" - # mso-pagination:keep-with-next ensures caption stays with figure image (correct MSO property) - # For Anexo figures, use static text (no SEQ field) to prevent Word from overwriting A1, A2... - # Add TC field so Anexo figures appear in Table of Figures index - # Use \f c to match the TOC field identifier in the template - if is_anexo: - tc_field = f'''''' - html_blocks.append(f'''{tc_field}

Figura {fig_num}. {fig_title}

''') - else: - html_blocks.append(f'''

Figura {fig_num}. {fig_title}

''') - - if os.path.exists(fig_path): - # Read actual image dimensions and scale to fit page width - img = Image.open(fig_path) - orig_w, orig_h = img.size - - # Scale to fit max width of 566px (15cm at 96dpi) while preserving aspect ratio - max_width = 566 - if orig_w > max_width: - scale = max_width / orig_w - new_w = max_width - new_h = int(orig_h * scale) - else: - new_w, new_h = orig_w, orig_h - - # Convert to pt (1px at 96dpi = 0.75pt) - w_pt = new_w * 0.75 - h_pt = new_h * 0.75 - - # mso-pagination:keep-with-next ensures image stays with source line - html_blocks.append(f'''

{fig_title}

''') - else: - # Fallback to placeholder - # mso-pagination:keep-with-next ensures placeholder stays with source line - html_blocks.append(f'''

[Insertar diagrama Mermaid aquí]

''') - - # Check if next non-empty line has custom Fuente - custom_source = None - fig_leyenda = None - lookahead = i + 1 - while lookahead < len(lines) and not lines[lookahead].strip(): - lookahead += 1 - if lookahead < len(lines): - next_line = lines[lookahead].strip() - if is_source_line(next_line): - # Extract custom source, removing markdown formatting - custom_source = extract_source_from_line(next_line) - # Ensure it ends with a period - if custom_source and not custom_source.endswith('.'): - custom_source += '.' - # Skip this line by advancing i past it - i = lookahead - # Check for Leyenda after source - leyenda_idx = i + 1 - while leyenda_idx < len(lines) and not lines[leyenda_idx].strip(): - leyenda_idx += 1 - if leyenda_idx < len(lines) and is_leyenda_line(lines[leyenda_idx]): - fig_leyenda = extract_leyenda_from_line(lines[leyenda_idx]) - i = leyenda_idx - - if custom_source: - source_html = md_to_html_para(custom_source) - html_blocks.append(f'''

Fuente: {source_html}

''') - else: - html_blocks.append(f'''

Fuente: Elaboración propia.

''') - - # Add leyenda if present (same style as Fuente, new line) - if fig_leyenda: - leyenda_html = md_to_html_para(fig_leyenda) - if not fig_leyenda.endswith('.'): - leyenda_html += '.' - html_blocks.append(f'''

Leyenda: {leyenda_html}

''') - - html_blocks.append('

 

') - i += 1 + blocks, i = handle_mermaid_diagram(lines, i, counters, is_anexo) + html_blocks.extend(blocks) continue # Code block (non-mermaid) if line.strip().startswith('```'): - code_lang = line.strip()[3:] - code_lines = [] - i += 1 - while i < len(lines) and not lines[i].strip().startswith('```'): - code_lines.append(lines[i]) - i += 1 - code = '\n'.join(code_lines) - # Escape HTML entities in code - code = code.replace('&', '&').replace('<', '<').replace('>', '>') - html_blocks.append(f'''
-
{code}
-
''') + blocks, i = handle_code_block(lines, i) + html_blocks.extend(blocks) + continue + + # Headers + if line.startswith('#'): + header_html = handle_header(line, is_anexo) + if header_html is not None: + html_blocks.append(header_html) i += 1 continue - # Headers - ## becomes h2, ### becomes h3 - if line.startswith('####'): - text = line.lstrip('#').strip() - # Apply consistent styling like h2/h3, disable numbering for h4 - html_blocks.append(f'

{text}

') - i += 1 - continue - elif line.startswith('###'): - text = line.lstrip('#').strip() - # Disable auto-numbering for Anexo content or A.x headings - if is_anexo or re.match(r'^A\.\d+', text): - # mso-list:none explicitly disables inherited list numbering from template CSS - html_blocks.append(f'

{text}

') - else: - html_blocks.append(f'

{text}

') - i += 1 - continue - elif line.startswith('##'): - text = line.lstrip('#').strip() - # Disable auto-numbering for Anexo content or A.x headings - if is_anexo or re.match(r'^A\.\d+', text): - # mso-list:none explicitly disables inherited list numbering from template CSS - html_blocks.append(f'

{text}

') - else: - html_blocks.append(f'

{text}

') - i += 1 - continue - elif line.startswith('#'): - # Skip h1 - we keep the original - i += 1 - continue - - # Table - check for table title pattern first + # Table if '|' in line and i + 1 < len(lines) and '---' in lines[i + 1]: - # Use Anexo-specific counter with "A" prefix, or global counter - if is_anexo: - anexo_table_counter += 1 - table_num = f"A{anexo_table_counter}" - else: - table_counter += 1 - table_num = str(table_counter) - - # Check if previous line has table title (e.g., **Tabla 1.** *Title*) - table_title = None - alt_title = None # Alternative title from **bold text:** pattern - table_source = "Elaboración propia" - - # Look back for table title - for j in range(i - 1, max(0, i - 5), -1): - prev_line = lines[j].strip() - if prev_line.startswith('**Tabla') or prev_line.startswith('*Tabla'): - # Extract title text - table_title = re.sub(r'\*+', '', prev_line).strip() - break - elif prev_line.startswith('**') and prev_line.endswith(':**'): - # Alternative: **Bold title:** pattern (for informal tables) - alt_title = re.sub(r'\*+', '', prev_line).rstrip(':').strip() - elif prev_line and not prev_line.startswith('|'): - break - - # Parse table - table_lines = [] - while i < len(lines) and '|' in lines[i]: - if '---' not in lines[i]: - table_lines.append(lines[i]) - i += 1 - - # Look ahead for source (skip blank lines first) - source_idx = i - table_leyenda = None - while source_idx < len(lines) and not lines[source_idx].strip(): - source_idx += 1 - if source_idx < len(lines) and is_source_line(lines[source_idx]): - table_source = extract_source_from_line(lines[source_idx]) - i = source_idx + 1 - # Check for Leyenda after source (skip blank lines) - leyenda_idx = i - while leyenda_idx < len(lines) and not lines[leyenda_idx].strip(): - leyenda_idx += 1 - if leyenda_idx < len(lines) and is_leyenda_line(lines[leyenda_idx]): - table_leyenda = extract_leyenda_from_line(lines[leyenda_idx]) - i = leyenda_idx + 1 - - # Add table title with MsoCaption class and proper Word SEQ field for cross-reference - # Format: "Tabla X." in bold, title in italic (per UNIR guidelines) - # Word TOC looks for text with Caption style - anchor must be outside main caption text - bookmark_id = f"_Ref_Tab{table_num}" - if table_title: - # Remove any "Tabla X." or "Tabla AX." pattern from the title - clean_title = re.sub(r'^Tabla\s+[A-Z]?\d+\.\s*', '', table_title).strip() - elif alt_title: - # Use alternative title from **bold text:** pattern - clean_title = alt_title - else: - clean_title = "Tabla de datos." - # mso-pagination:keep-with-next ensures caption stays with table (correct MSO property) - # For Anexo tables, use static text (no SEQ field) to prevent Word from overwriting A1, A2... - # Add TC field so Anexo tables appear in Table of Tables index - # Use \f t identifier - template TOC field will be modified to include this - if is_anexo: - tc_field = f'''''' - html_blocks.append(f'''{tc_field}

Tabla {table_num}. {clean_title}

''') - else: - html_blocks.append(f'''

Tabla {table_num}. {clean_title}

''') - - # Build table HTML with APA style (horizontal lines only, no vertical) - table_html = '
' - for j, tline in enumerate(table_lines): - cells = [c.strip() for c in tline.split('|')[1:-1]] - table_html += '' - for cell in cells: - if j == 0: - # Header row: top and bottom border, bold text - table_html += f'' - elif j == len(table_lines) - 1: - # Last row: bottom border only - table_html += f'' - else: - # Middle rows: no borders - table_html += f'' - table_html += '' - table_html += '

{md_to_html_para(cell)}

{md_to_html_para(cell)}

{md_to_html_para(cell)}

' - html_blocks.append(table_html) - - # Add source with proper template format (convert markdown links to HTML) - source_html = md_to_html_para(table_source) - if not table_source.endswith('.'): - source_html += '.' - html_blocks.append(f'

Fuente: {source_html}

') - - # Add leyenda if present (same style as Fuente, new line) - if table_leyenda: - leyenda_html = md_to_html_para(table_leyenda) - if not table_leyenda.endswith('.'): - leyenda_html += '.' - html_blocks.append(f'

Leyenda: {leyenda_html}

') - - html_blocks.append('

 

') + blocks, i = handle_table(lines, i, counters, is_anexo) + html_blocks.extend(blocks) continue # Blockquote if line.startswith('>'): - quote_text = line[1:].strip() - i += 1 - while i < len(lines) and lines[i].startswith('>'): - quote_text += ' ' + lines[i][1:].strip() - i += 1 - html_blocks.append(f'

{md_to_html_para(quote_text)}

') + blocks, i = handle_blockquote(lines, i) + html_blocks.extend(blocks) continue - # Bullet list (handle blank lines between items) + # Bullet list if re.match(r'^[\-\*\+]\s', line): - # Collect all bullet items first - bullet_items = [] - while i < len(lines): - # Skip blank lines - while i < len(lines) and not lines[i].strip(): - i += 1 - # Check if next non-blank line is a bullet item - if i < len(lines) and re.match(r'^[\-\*\+]\s', lines[i]): - item_text = lines[i][2:].strip() - item_text = convert_latex_formulas(item_text) - bullet_items.append(md_to_html_para(item_text)) - i += 1 - else: - break - # Output with proper First/Middle/Last classes - for idx, item in enumerate(bullet_items): - if len(bullet_items) == 1: - cls = 'MsoListParagraph' - elif idx == 0: - cls = 'MsoListParagraphCxSpFirst' - elif idx == len(bullet_items) - 1: - cls = 'MsoListParagraphCxSpLast' - else: - cls = 'MsoListParagraphCxSpMiddle' - html_blocks.append(f'

·     {item}

') + blocks, i = handle_bullet_list(lines, i) + html_blocks.extend(blocks) continue - # Numbered list (handle blank lines between items) + # Numbered list if re.match(r'^\d+\.\s', line): - # Collect all numbered items first - numbered_items = [] - while i < len(lines): - # Skip blank lines - while i < len(lines) and not lines[i].strip(): - i += 1 - # Check if next non-blank line is a numbered item - if i < len(lines) and re.match(r'^\d+\.\s', lines[i]): - item_text = re.sub(r'^\d+\.\s*', '', lines[i]).strip() - item_text = convert_latex_formulas(item_text) - numbered_items.append(md_to_html_para(item_text)) - i += 1 - else: - break - # Output with proper First/Middle/Last classes - for idx, item in enumerate(numbered_items): - num = idx + 1 - if len(numbered_items) == 1: - cls = 'MsoListParagraph' - elif idx == 0: - cls = 'MsoListParagraphCxSpFirst' - elif idx == len(numbered_items) - 1: - cls = 'MsoListParagraphCxSpLast' - else: - cls = 'MsoListParagraphCxSpMiddle' - html_blocks.append(f'

{num}.   {item}

') + blocks, i = handle_numbered_list(lines, i) + html_blocks.extend(blocks) continue - # Skip lines that are just table/figure titles (they'll be handled with the table/figure) + # Skip lines that are just table/figure titles if line.strip().startswith('**Tabla') or line.strip().startswith('*Tabla'): i += 1 continue @@ -514,12 +143,23 @@ def parse_md_to_html_blocks(md_content, is_anexo=False): else: html_blocks.append(f'

{md_to_html_para(para_text)}

') - return '\n\n'.join(html_blocks) + return '\n\n'.join(html_blocks), counters -def extract_section_content(md_content, is_anexo=False): - """Extract content from markdown, skipping the first # header.""" + +def extract_section_content(md_content, is_anexo=False, counters=None): + """Extract content from markdown, skipping the first # header. + + Args: + md_content: Markdown content string + is_anexo: Boolean indicating if processing Anexo section + counters: Dict with table/figure counters + + Returns: + Tuple of (html_string, counters) + """ md_content = re.sub(r'^#\s+[^\n]+\n+', '', md_content, count=1) - return parse_md_to_html_blocks(md_content, is_anexo=is_anexo) + return parse_md_to_html_blocks(md_content, is_anexo=is_anexo, counters=counters) + def find_section_element(soup, keyword): """Find element containing keyword (h1 or special paragraph classes).""" @@ -540,6 +180,7 @@ def find_section_element(soup, keyword): return p return None + def remove_elements_between(start_elem, end_elem): """Remove all elements between start and end (exclusive).""" current = start_elem.next_sibling @@ -553,6 +194,7 @@ def remove_elements_between(start_elem, end_elem): elif isinstance(elem, NavigableString): elem.extract() + def format_references(refs_content): """Format references with proper MsoBibliography style.""" refs_content = refs_content.replace('# Referencias bibliográficas {.unnumbered}', '').strip() @@ -566,20 +208,11 @@ def format_references(refs_content): # Apply markdown formatting formatted = md_to_html_para(line) - # Use MsoBibliography style with hanging indent (36pt indent, -36pt text-indent) + # Use MsoBibliography style with hanging indent refs_html += f'''

{formatted}

\n''' return refs_html -def split_into_paragraphs(text, lang='ES'): - """Split text by double newlines and wrap each paragraph in

tags.""" - paragraphs = [] - for para in text.split('\n\n'): - para = para.strip() - if para: - formatted = md_to_html_para(para) - paragraphs.append(f'

{formatted}

') - return '\n'.join(paragraphs) def extract_resumen_parts(resumen_content): """Extract Spanish resumen and English abstract from 00_resumen.md""" @@ -610,16 +243,21 @@ def extract_resumen_parts(resumen_content): return spanish_text, spanish_keywords, english_text, english_keywords + def main(): - global table_counter, figure_counter, anexo_table_counter, anexo_figure_counter + # Initialize counters dict (replaces global counters) + counters = { + 'table': 0, + 'figure': 0, + 'anexo_table': 0, + 'anexo_figure': 0, + 'global_figure': 0, + } print("Reading template...") html_content = read_file(TEMPLATE_INPUT) # Modify the Table of Tables TOC field to include TC entries with \f t identifier - # Original: TOC \h \z \t "Tablas;1" \c "Tabla" - # Modified: TOC \f t \h \z \t "Tablas;1" \c "Tabla" - # Use regex to handle whitespace/HTML variations in the TOC field html_content = re.sub( r'(TOC\s+)(\\h\s+\\z\s+\\t\s*\n?\s*"Tablas;1")', r'\1\\f t \2', @@ -652,7 +290,6 @@ def main(): print("Replacing Resumen...") resumen_title = soup.find('p', class_='Ttulondices', string=re.compile(r'Resumen')) if resumen_title: - # Find and replace content after Resumen title until Abstract current = resumen_title.find_next_sibling() elements_to_remove = [] while current: @@ -666,7 +303,6 @@ def main(): if hasattr(elem, 'decompose'): elem.decompose() - # Insert new resumen content (spanish_text already contains

tags) resumen_html = f'''{spanish_text}

 

Palabras clave: {spanish_kw}

@@ -681,11 +317,9 @@ def main(): print("Replacing Abstract...") abstract_title = soup.find('p', class_='Ttulondices', string=re.compile(r'Abstract')) if abstract_title: - # Find and replace content after Abstract title until next major section current = abstract_title.find_next_sibling() elements_to_remove = [] while current: - # Stop at page break or next title if current.name == 'span' and 'page-break' in str(current): break text = current.get_text() if hasattr(current, 'get_text') else str(current) @@ -698,7 +332,6 @@ def main(): if hasattr(elem, 'decompose'): elem.decompose() - # Insert new abstract content (english_text already contains

tags) abstract_html = f'''{english_text}

 

Keywords: {english_kw}

@@ -721,31 +354,24 @@ def main(): for elem in soup.find_all(string=re.compile(r'Ejemplo de nota al pie')): parent = elem.parent if parent: - # Find the footnote container and remove it while parent and parent.name != 'p': parent = parent.parent if parent: parent.decompose() print(" ✓ Removed footnote example") - # Clear old figure/table index entries (they need to be regenerated in Word) + # Clear old figure/table index entries print("Clearing old index entries...") - # Remove ALL content from MsoTof paragraphs that reference template examples - # The indices will be regenerated when user opens in Word and presses Ctrl+A, F9 for p in soup.find_all('p', class_='MsoTof'): text = p.get_text() - # Check for figure index entries with template examples if 'Figura' in text and 'Ejemplo' in text: - # Remove all tags (the actual index entry links) for a in p.find_all('a'): a.decompose() - # Also remove any remaining text content that shows the example for span in p.find_all('span', style=lambda x: x and 'mso-no-proof' in str(x)): if 'Ejemplo' in span.get_text(): span.decompose() print(" ✓ Cleared figure index example entry") - # Check for table index entries with template examples if 'Tabla' in text and 'Ejemplo' in text: for a in p.find_all('a'): a.decompose() @@ -754,24 +380,20 @@ def main(): span.decompose() print(" ✓ Cleared table index example entry") - # Remove old figure index entries that reference template examples for p in soup.find_all('p', class_='MsoToc3'): text = p.get_text() if 'Figura 1. Ejemplo' in text or 'Tabla 1. Ejemplo' in text: p.decompose() print(" ✓ Removed template index entry") - # Also clear the specific figure/table from template for p in soup.find_all('p', class_='Imagencentrada'): p.decompose() print(" ✓ Removed template figure placeholder") # Remove template table example for table in soup.find_all('table', class_='MsoTableGrid'): - # Check if this is the template example table text = table.get_text() if 'Celda 1' in text or 'Encabezado 1' in text: - # Also remove surrounding caption and source prev_sib = table.find_previous_sibling() next_sib = table.find_next_sibling() if prev_sib and 'Tabla 1. Ejemplo' in prev_sib.get_text(): @@ -782,7 +404,7 @@ def main(): print(" ✓ Removed template table example") break - # Define chapters with their keywords and next chapter keywords + # Define chapters chapters = [ ('Introducción', 'intro', 'Contexto'), ('Contexto', 'contexto', 'Objetivos'), @@ -795,16 +417,12 @@ def main(): for chapter_keyword, doc_key, next_keyword in chapters: print(f" Processing: {chapter_keyword}") - # Reset counters for consistent numbering per chapter (optional - remove if you want global numbering) - # table_counter = 0 - # figure_counter = 0 - start_elem = find_section_element(soup, chapter_keyword) end_elem = find_section_element(soup, next_keyword) if start_elem and end_elem: remove_elements_between(start_elem, end_elem) - new_content_html = extract_section_content(docs[doc_key]) + new_content_html, counters = extract_section_content(docs[doc_key], counters=counters) new_soup = BeautifulSoup(new_content_html, 'html.parser') insert_point = start_elem for new_elem in reversed(list(new_soup.children)): @@ -844,20 +462,20 @@ def main(): current.extract() current = next_elem - anexo_content = extract_section_content(docs['anexo'], is_anexo=True) + anexo_content, counters = extract_section_content(docs['anexo'], is_anexo=True, counters=counters) anexo_soup = BeautifulSoup(anexo_content, 'html.parser') insert_point = anexo_elem for new_elem in reversed(list(anexo_soup.children)): insert_point.insert_after(new_elem) print(f" ✓ Replaced content") - print(f"\nSummary: {table_counter} tables + {anexo_table_counter} Anexo tables, {figure_counter} figures + {anexo_figure_counter} Anexo figures processed") + print(f"\nSummary: {counters['table']} tables + {counters['anexo_table']} Anexo tables, {counters['figure']} figures + {counters['anexo_figure']} Anexo figures processed") print("Saving modified template...") output_html = str(soup) write_file(TEMPLATE_OUTPUT, output_html) - # Copy template support files (header.htm, images, etc.) + # Copy template support files support_files_src = os.path.join(BASE_DIR, 'instructions/plantilla_individual_files') support_files_dst = os.path.join(BASE_DIR, 'thesis_output/plantilla_individual_files') if os.path.exists(support_files_src): @@ -874,5 +492,6 @@ def main(): print(" - This will regenerate: Índice de contenidos, Índice de figuras, Índice de tablas") print("4. Save as .docx") + if __name__ == '__main__': main() diff --git a/claude.md b/claude.md index 2610244..e3dcde7 100644 --- a/claude.md +++ b/claude.md @@ -77,7 +77,9 @@ MastersThesis/ │ ├── instrucciones.pdf # TFE writing guidelines │ ├── plantilla_individual.pdf # Word template (PDF version) │ └── plantilla_individual.htm # Word template (HTML version, source) -├── apply_content.py # Generates TFM document from docs/ + template +├── apply_content.py # Main orchestrator: generates TFM from docs/ + template +├── content_handlers.py # Content block handlers (tables, figures, lists, etc.) +├── markdown_utils.py # Markdown utilities (cross-refs, latex, metadata extraction) ├── generate_mermaid_figures.py # Converts Mermaid diagrams to PNG ├── src/archived/ocr_benchmark_notebook.ipynb # Archived benchmark (do not cite) └── README.md @@ -162,6 +164,13 @@ python3 apply_content.py ``` **What `apply_content.py` does:** + +The script is organized into three modules for maintainability: +- `apply_content.py` - Main orchestrator (~300 lines) +- `content_handlers.py` - Block-level content handlers (~400 lines) +- `markdown_utils.py` - Utility functions (~150 lines) + +Functionality: - Replaces Resumen and Abstract with actual content + keywords - Replaces all 5 chapters with content from docs/ - Replaces Referencias with APA-formatted bibliography diff --git a/content_handlers.py b/content_handlers.py new file mode 100644 index 0000000..71aaa4f --- /dev/null +++ b/content_handlers.py @@ -0,0 +1,469 @@ +#!/usr/bin/env python3 +"""Content block handlers for markdown to HTML conversion.""" + +import os +import re +from PIL import Image + +from markdown_utils import ( + md_to_html_para, + convert_latex_formulas, + is_source_line, + extract_source_from_line, + is_leyenda_line, + extract_leyenda_from_line, +) + +# Base directory for resolving paths +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) + + +def handle_mermaid_diagram(lines, i, counters, is_anexo): + """Handle mermaid diagram block, converting to figure with image. + + Args: + lines: List of markdown lines + i: Current line index (pointing to ```mermaid) + counters: Dict with 'table', 'figure', 'anexo_table', 'anexo_figure', 'global_figure' + is_anexo: Boolean indicating if processing Anexo section + + Returns: + Tuple of (html_blocks, new_index) + """ + html_blocks = [] + + # Always increment global index for sequential filenames + counters['global_figure'] += 1 + + # Use Anexo-specific counter with "A" prefix for display, or global counter + if is_anexo: + counters['anexo_figure'] += 1 + fig_num = f"A{counters['anexo_figure']}" + else: + counters['figure'] += 1 + fig_num = str(counters['figure']) + + mermaid_lines = [] + i += 1 + while i < len(lines) and not lines[i].strip() == '```': + mermaid_lines.append(lines[i]) + i += 1 + + # Try to extract title from mermaid content (YAML format) + mermaid_content = '\n'.join(mermaid_lines) + # Match title with quotes: title: "Something" or title: 'Something' + title_match = re.search(r'title:\s*["\']([^"\']+)["\']', mermaid_content) + if not title_match: + # Match title without quotes: title: Something + title_match = re.search(r'title:\s*([^"\'\n]+)', mermaid_content) + if title_match: + fig_title = title_match.group(1).strip() + else: + fig_title = f"Diagrama {fig_num}" + + # Use global sequential index for filename + fig_file = f'figures/figura_{counters["global_figure"]}.png' + fig_path = os.path.join(BASE_DIR, 'thesis_output', fig_file) + + # Create figure with MsoCaption class and proper Word SEQ field + bookmark_id = f"_Ref_Fig{fig_num}" + + if is_anexo: + tc_field = f'''''' + html_blocks.append(f'''{tc_field}

Figura {fig_num}. {fig_title}

''') + else: + html_blocks.append(f'''

Figura {fig_num}. {fig_title}

''') + + if os.path.exists(fig_path): + # Read actual image dimensions and scale to fit page width + img = Image.open(fig_path) + orig_w, orig_h = img.size + + # Scale to fit max width of 566px (15cm at 96dpi) while preserving aspect ratio + max_width = 566 + if orig_w > max_width: + scale = max_width / orig_w + new_w = max_width + new_h = int(orig_h * scale) + else: + new_w, new_h = orig_w, orig_h + + # Convert to pt (1px at 96dpi = 0.75pt) + w_pt = new_w * 0.75 + h_pt = new_h * 0.75 + + html_blocks.append(f'''

{fig_title}

''') + else: + # Fallback to placeholder + html_blocks.append(f'''

[Insertar diagrama Mermaid aquí]

''') + + # Check if next non-empty line has custom Fuente + custom_source = None + fig_leyenda = None + lookahead = i + 1 + while lookahead < len(lines) and not lines[lookahead].strip(): + lookahead += 1 + if lookahead < len(lines): + next_line = lines[lookahead].strip() + if is_source_line(next_line): + custom_source = extract_source_from_line(next_line) + if custom_source and not custom_source.endswith('.'): + custom_source += '.' + i = lookahead + # Check for Leyenda after source + leyenda_idx = i + 1 + while leyenda_idx < len(lines) and not lines[leyenda_idx].strip(): + leyenda_idx += 1 + if leyenda_idx < len(lines) and is_leyenda_line(lines[leyenda_idx]): + fig_leyenda = extract_leyenda_from_line(lines[leyenda_idx]) + i = leyenda_idx + + if custom_source: + source_html = md_to_html_para(custom_source) + html_blocks.append(f'''

Fuente: {source_html}

''') + else: + html_blocks.append(f'''

Fuente: Elaboración propia.

''') + + if fig_leyenda: + leyenda_html = md_to_html_para(fig_leyenda) + if not fig_leyenda.endswith('.'): + leyenda_html += '.' + html_blocks.append(f'''

Leyenda: {leyenda_html}

''') + + html_blocks.append('

 

') + i += 1 + + return html_blocks, i + + +def handle_code_block(lines, i): + """Handle non-mermaid code block. + + Args: + lines: List of markdown lines + i: Current line index (pointing to ```) + + Returns: + Tuple of (html_blocks, new_index) + """ + html_blocks = [] + code_lang = lines[i].strip()[3:] + code_lines = [] + i += 1 + while i < len(lines) and not lines[i].strip().startswith('```'): + code_lines.append(lines[i]) + i += 1 + code = '\n'.join(code_lines) + # Escape HTML entities in code + code = code.replace('&', '&').replace('<', '<').replace('>', '>') + html_blocks.append(f'''
+
{code}
+
''') + i += 1 + return html_blocks, i + + +def handle_header(line, is_anexo): + """Handle header lines (##, ###, ####). + + Args: + line: The header line + is_anexo: Boolean indicating if processing Anexo section + + Returns: + HTML string for the header, or None if h1 (skip) + """ + if line.startswith('####'): + text = line.lstrip('#').strip() + return f'

{text}

' + elif line.startswith('###'): + text = line.lstrip('#').strip() + # Extract section number if present + sec_match = re.match(r'^([\d\.]+)\s+', text) + bookmark_html = '' + if sec_match: + sec_num = sec_match.group(1).rstrip('.') + bookmark_id = f"_Ref_Sec{sec_num.replace('.', '_')}" + bookmark_html = f'' + # Disable auto-numbering for Anexo content or A.x headings + if is_anexo or re.match(r'^A\.\d+', text): + return f'{bookmark_html}

{text}

' + else: + return f'{bookmark_html}

{text}

' + elif line.startswith('##'): + text = line.lstrip('#').strip() + # Extract section number if present + sec_match = re.match(r'^([\d\.]+)\s+', text) + bookmark_html = '' + if sec_match: + sec_num = sec_match.group(1).rstrip('.') + bookmark_id = f"_Ref_Sec{sec_num.replace('.', '_')}" + bookmark_html = f'' + # Disable auto-numbering for Anexo content or A.x headings + if is_anexo or re.match(r'^A\.\d+', text): + return f'{bookmark_html}

{text}

' + else: + return f'{bookmark_html}

{text}

' + elif line.startswith('#'): + # Skip h1 - we keep the original + return None + return None + + +def handle_table(lines, i, counters, is_anexo): + """Handle markdown table. + + Args: + lines: List of markdown lines + i: Current line index (pointing to first table row) + counters: Dict with table/figure counters + is_anexo: Boolean indicating if processing Anexo section + + Returns: + Tuple of (html_blocks, new_index) + """ + html_blocks = [] + + # Use Anexo-specific counter with "A" prefix, or global counter + if is_anexo: + counters['anexo_table'] += 1 + table_num = f"A{counters['anexo_table']}" + else: + counters['table'] += 1 + table_num = str(counters['table']) + + # Check if previous line has table title + table_title = None + alt_title = None + table_source = "Elaboración propia" + + # Look back for table title + for j in range(i - 1, max(0, i - 5), -1): + prev_line = lines[j].strip() + if prev_line.startswith('**Tabla') or prev_line.startswith('*Tabla'): + table_title = re.sub(r'\*+', '', prev_line).strip() + break + elif prev_line.startswith('**') and prev_line.endswith(':**'): + alt_title = re.sub(r'\*+', '', prev_line).rstrip(':').strip() + elif prev_line and not prev_line.startswith('|'): + break + + # Parse table + table_lines = [] + while i < len(lines) and '|' in lines[i]: + if '---' not in lines[i]: + table_lines.append(lines[i]) + i += 1 + + # Look ahead for source + source_idx = i + table_leyenda = None + while source_idx < len(lines) and not lines[source_idx].strip(): + source_idx += 1 + if source_idx < len(lines) and is_source_line(lines[source_idx]): + table_source = extract_source_from_line(lines[source_idx]) + i = source_idx + 1 + # Check for Leyenda after source + leyenda_idx = i + while leyenda_idx < len(lines) and not lines[leyenda_idx].strip(): + leyenda_idx += 1 + if leyenda_idx < len(lines) and is_leyenda_line(lines[leyenda_idx]): + table_leyenda = extract_leyenda_from_line(lines[leyenda_idx]) + i = leyenda_idx + 1 + + # Add table title with MsoCaption class + bookmark_id = f"_Ref_Tab{table_num}" + if table_title: + clean_title = re.sub(r'^Tabla\s+[A-Z]?\d+\.\s*', '', table_title).strip() + elif alt_title: + clean_title = alt_title + else: + clean_title = "Tabla de datos." + + if is_anexo: + tc_field = f'''''' + html_blocks.append(f'''{tc_field}

Tabla {table_num}. {clean_title}

''') + else: + html_blocks.append(f'''

Tabla {table_num}. {clean_title}

''') + + # Build table HTML with APA style + table_html = '
' + for j, tline in enumerate(table_lines): + cells = [c.strip() for c in tline.split('|')[1:-1]] + table_html += '' + for cell in cells: + if j == 0: + # Header row + table_html += f'' + elif j == len(table_lines) - 1: + # Last row + table_html += f'' + else: + # Middle rows + table_html += f'' + table_html += '' + table_html += '

{md_to_html_para(cell)}

{md_to_html_para(cell)}

{md_to_html_para(cell)}

' + html_blocks.append(table_html) + + # Add source + source_html = md_to_html_para(table_source) + if not table_source.endswith('.'): + source_html += '.' + html_blocks.append(f'

Fuente: {source_html}

') + + # Add leyenda if present + if table_leyenda: + leyenda_html = md_to_html_para(table_leyenda) + if not table_leyenda.endswith('.'): + leyenda_html += '.' + html_blocks.append(f'

Leyenda: {leyenda_html}

') + + html_blocks.append('

 

') + + return html_blocks, i + + +def handle_blockquote(lines, i): + """Handle blockquote (regular or Nota callout). + + Args: + lines: List of markdown lines + i: Current line index (pointing to > line) + + Returns: + Tuple of (html_blocks, new_index) + """ + html_blocks = [] + line = lines[i] + quote_text = line[1:].strip() + i += 1 + while i < len(lines) and lines[i].startswith('>'): + quote_text += ' ' + lines[i][1:].strip() + i += 1 + + # Check if this is a Nota/Note callout + if quote_text.startswith('**Nota:**') or quote_text.startswith('**Note:**'): + if quote_text.startswith('**Nota:**'): + label = 'Nota:' + content = quote_text[9:].strip() + else: + label = 'Note:' + content = quote_text[9:].strip() + + # UNIR callout box style + html_blocks.append(f'''
+

{label} {md_to_html_para(content)}

+
''') + else: + # Regular blockquote + html_blocks.append(f'

{md_to_html_para(quote_text)}

') + + return html_blocks, i + + +def handle_bullet_list(lines, i): + """Handle bullet list (-, *, +). + + Args: + lines: List of markdown lines + i: Current line index (pointing to first bullet) + + Returns: + Tuple of (html_blocks, new_index) + """ + html_blocks = [] + bullet_items = [] + + while i < len(lines): + # Skip blank lines + while i < len(lines) and not lines[i].strip(): + i += 1 + # Check if next non-blank line is a bullet item + if i < len(lines) and re.match(r'^[\-\*\+]\s', lines[i]): + item_text = lines[i][2:].strip() + item_text = convert_latex_formulas(item_text) + bullet_items.append(md_to_html_para(item_text)) + i += 1 + else: + break + + # Output with proper First/Middle/Last classes + for idx, item in enumerate(bullet_items): + if len(bullet_items) == 1: + cls = 'MsoListParagraph' + elif idx == 0: + cls = 'MsoListParagraphCxSpFirst' + elif idx == len(bullet_items) - 1: + cls = 'MsoListParagraphCxSpLast' + else: + cls = 'MsoListParagraphCxSpMiddle' + html_blocks.append(f'

·     {item}

') + + return html_blocks, i + + +def handle_numbered_list(lines, i): + """Handle numbered list (1., 2., etc). + + Args: + lines: List of markdown lines + i: Current line index (pointing to first numbered item) + + Returns: + Tuple of (html_blocks, new_index) + """ + html_blocks = [] + numbered_items = [] + + while i < len(lines): + # Skip blank lines + while i < len(lines) and not lines[i].strip(): + i += 1 + # Check if next non-blank line is a numbered item + if i < len(lines) and re.match(r'^\d+\.\s', lines[i]): + item_text = re.sub(r'^\d+\.\s*', '', lines[i]).strip() + i += 1 + # Collect any nested/indented content + nested_lines = [] + while i < len(lines): + current = lines[i] + # Stop conditions + if re.match(r'^\d+\.\s', current): + break + if current.startswith('#'): + break + if current.startswith('```'): + break + if current.startswith('**Tabla') or current.startswith('**Figura'): + break + if current.strip() and not current.startswith(' ') and not current.startswith('\t') and not current.startswith('-'): + if nested_lines or not current.strip(): + break + if current.strip(): + cleaned = current.strip() + if cleaned.startswith('- '): + cleaned = cleaned[2:] + nested_lines.append(cleaned) + i += 1 + # Combine item with nested content + if nested_lines: + item_text = item_text + '
' + '
'.join(nested_lines) + item_text = convert_latex_formulas(item_text) + numbered_items.append(md_to_html_para(item_text)) + else: + break + + # Output with proper First/Middle/Last classes + for idx, item in enumerate(numbered_items): + num = idx + 1 + if len(numbered_items) == 1: + cls = 'MsoListParagraph' + elif idx == 0: + cls = 'MsoListParagraphCxSpFirst' + elif idx == len(numbered_items) - 1: + cls = 'MsoListParagraphCxSpLast' + else: + cls = 'MsoListParagraphCxSpMiddle' + html_blocks.append(f'

{num}.   {item}

') + + return html_blocks, i diff --git a/docs/00_resumen.md b/docs/00_resumen.md index b3d37cd..9e7665b 100644 --- a/docs/00_resumen.md +++ b/docs/00_resumen.md @@ -1,29 +1,29 @@ # Resumen -El presente Trabajo Fin de Máster aborda la optimización de sistemas de Reconocimiento Óptico de Caracteres (OCR) basados en inteligencia artificial para documentos en español. El objetivo principal es identificar la configuración óptima de hiperparámetros que maximice la precisión del reconocimiento de texto sin requerir fine-tuning de los modelos base. +El presente Trabajo Fin de Máster aborda la optimización de sistemas de Reconocimiento Óptico de Caracteres (OCR) basados en inteligencia artificial para documentos en español. El objetivo principal es identificar una configuración de hiperparámetros que maximice la precisión del reconocimiento de texto sin requerir fine-tuning de los modelos base. -Se realizó un estudio comparativo de tres soluciones OCR de código abierto: EasyOCR, PaddleOCR (PP-OCRv5) y DocTR. Se evaluó su rendimiento mediante las métricas estándar CER (Character Error Rate) y WER (Word Error Rate) sobre un corpus de 45 páginas de documentos académicos en español. Tras identificar PaddleOCR como la solución más prometedora, se procedió a una optimización sistemática de hiperparámetros utilizando Ray Tune con el algoritmo de búsqueda Optuna, ejecutando 64 configuraciones diferentes con aceleración GPU (NVIDIA RTX 3060). +La metodología combina un benchmark comparativo de tres soluciones de código abierto (EasyOCR, PaddleOCR y DocTR) con un ajuste sistemático de hiperparámetros mediante Ray Tune y Optuna, evaluando 64 configuraciones con aceleración GPU sobre un corpus de 45 páginas. Las métricas de evaluación utilizadas fueron CER y WER. -Los resultados demuestran que la optimización de hiperparámetros logró mejoras significativas: el mejor trial individual alcanzó un CER de 0.79% (precisión del 99.21%), cumpliendo el objetivo de CER < 2%. Al validar la configuración optimizada sobre el dataset completo de 45 páginas, se obtuvo una mejora del 12.8% en CER (de 8.85% a 7.72%). El hallazgo más relevante fue que el parámetro `textline_orientation` (clasificación de orientación de línea de texto) tiene un impacto crítico en el rendimiento. Adicionalmente, se identificó que el umbral de detección (`text_det_thresh`) presenta una correlación positiva moderada (0.43) con el error, lo que indica que valores más bajos tienden a mejorar el rendimiento. +Los resultados muestran mejoras significativas en el mejor trial (CER 0.79%) y una mejora del 12.8% en CER en la validación sobre el dataset completo (de 8.85% a 7.72%). El parámetro `textline_orientation` destacó como factor crítico, mientras que `text_det_thresh` mostró correlación positiva moderada con el error. + +Se concluye que la optimización de hiperparámetros es una alternativa viable al fine-tuning en documentos académicos en español, aunque la generalización depende del tamaño del subconjunto de ajuste. En conclusión, la infraestructura dockerizada facilita la reproducibilidad y la evaluación sistemática de configuraciones OCR. **Fuente:** [`metrics_paddle.md`](https://seryus.ddns.net/unir/MastersThesis/src/branch/main/docs/metrics/metrics_paddle.md), [`paddle_correlations.csv`](https://seryus.ddns.net/unir/MastersThesis/src/branch/main/src/results/correlations/paddle_correlations.csv). -Este trabajo demuestra que la optimización de hiperparámetros es una alternativa viable al fine-tuning, especialmente útil cuando se dispone de modelos preentrenados para el idioma objetivo. La infraestructura dockerizada desarrollada permite reproducir los experimentos y facilita la evaluación sistemática de configuraciones OCR. - -**Palabras clave:** OCR, Reconocimiento Óptico de Caracteres, PaddleOCR, Optimización de Hiperparámetros, Ray Tune, Procesamiento de Documentos, Inteligencia Artificial +**Palabras clave:** OCR, PaddleOCR, Optimización de hiperparámetros, Ray Tune, Documentos académicos --- # Abstract -This Master's Thesis addresses the optimization of Artificial Intelligence-based Optical Character Recognition (OCR) systems for Spanish documents. The main objective is to identify the optimal hyperparameter configuration that maximizes text recognition accuracy without requiring fine-tuning of the base models. +This Master's Thesis addresses the optimization of AI-based Optical Character Recognition (OCR) systems for Spanish academic documents. The main objective is to identify a hyperparameter configuration that maximizes recognition accuracy without fine-tuning the base models. -A comparative study of three open-source OCR solutions was conducted with EasyOCR, PaddleOCR (PP-OCRv5), and DocTR. Their performance was evaluated using standard CER (Character Error Rate) and WER (Word Error Rate) metrics on a corpus of 45 pages of academic documents in Spanish. After identifying PaddleOCR as the most promising solution, systematic hyperparameter optimization was performed using Ray Tune with the Optuna search algorithm, executing 64 different configurations with GPU acceleration (NVIDIA RTX 3060). +The methodology combines a comparative benchmark of three open-source OCR engines (EasyOCR, PaddleOCR, and DocTR) with a systematic hyperparameter search using Ray Tune and Optuna. Sixty-four configurations were evaluated with GPU acceleration on a 45-page corpus, using CER and WER as evaluation metrics. -Results demonstrate that hyperparameter optimization achieved significant improvements. The best individual trial reached a CER of 0.79% (99.21% accuracy), meeting the CER < 2% objective. When validating the optimized configuration on the full 45-page dataset, a 12.8% CER improvement was obtained (from 8.85% to 7.72%). The most relevant finding was that the `textline_orientation` parameter (text line orientation classification) has a critical impact on performance. Additionally, the detection threshold (`text_det_thresh`) showed a moderate positive correlation (0.43) with error, indicating that lower values tend to improve performance. +Results show significant gains in the best trial (CER 0.79%) and a 12.8% CER improvement on the full dataset (from 8.85% to 7.72%). The `textline_orientation` parameter had the strongest impact, while `text_det_thresh` showed a moderate positive correlation with error. + +The study concludes that hyperparameter optimization is a viable alternative to fine-tuning for Spanish academic documents, although generalization depends on the size of the tuning subset. In conclusion, the dockerized infrastructure supports reproducibility and systematic evaluation of OCR configurations. Sources: [`metrics_paddle.md`](https://seryus.ddns.net/unir/MastersThesis/src/branch/main/docs/metrics/metrics_paddle.md), [`paddle_correlations.csv`](https://seryus.ddns.net/unir/MastersThesis/src/branch/main/src/results/correlations/paddle_correlations.csv). -This work demonstrates that hyperparameter optimization is a viable alternative to fine-tuning, especially useful when pre-trained models for the target language are available. The dockerized infrastructure developed enables experiment reproducibility and facilitates systematic evaluation of OCR configurations. - -**Keywords:** OCR, Optical Character Recognition, PaddleOCR, Hyperparameter Optimization, Ray Tune, Document Processing, Artificial Intelligence +**Keywords:** OCR, PaddleOCR, Hyperparameter optimization, Ray Tune, Academic documents diff --git a/docs/01_introduccion.md b/docs/01_introduccion.md index 92b9bb1..2092d72 100644 --- a/docs/01_introduccion.md +++ b/docs/01_introduccion.md @@ -1,6 +1,6 @@ # Introducción -¿Es posible mejorar significativamente un sistema OCR sin reentrenarlo? Esta pregunta, aparentemente simple, encierra un desafío práctico que afecta a investigadores, instituciones educativas y empresas que necesitan digitalizar documentos pero carecen de los recursos para realizar fine-tuning de modelos neuronales. A lo largo de este capítulo se desarrolla la motivación del trabajo, se identifica el problema a resolver y se plantean las preguntas de investigación que guiarán el desarrollo experimental. +¿Es posible mejorar significativamente un sistema OCR sin reentrenarlo? Esta pregunta, aparentemente simple, encierra un desafío práctico que afecta a investigadores, instituciones educativas y empresas que necesitan digitalizar documentos pero carecen de los recursos para realizar fine-tuning de modelos neuronales. ## Motivación @@ -56,7 +56,7 @@ La presente investigación surge de una necesidad práctica: optimizar un sistem La hipótesis central de este trabajo es que los modelos OCR preentrenados contienen capacidades latentes que pueden activarse mediante la configuración adecuada de sus hiperparámetros de inferencia. Parámetros como los umbrales de detección de texto, las opciones de preprocesamiento de imagen, y los filtros de confianza de reconocimiento pueden tener un impacto significativo en el rendimiento final. Su optimización sistemática puede aproximarse a los beneficios del fine-tuning sin sus costes asociados. -Esta oportunidad se ve reforzada por la disponibilidad de frameworks modernos de optimización de hiperparámetros como Ray Tune (Liaw et al., 2018) y algoritmos de búsqueda eficientes como Optuna (Akiba et al., 2019), que permiten explorar espacios de configuración de manera sistemática y eficiente. +Esta oportunidad se ve reforzada por la disponibilidad de frameworks modernos de optimización de hiperparámetros como Ray Tune y algoritmos de búsqueda eficientes como Optuna, que permiten explorar espacios de configuración de manera sistemática y eficiente. ## Planteamiento del trabajo diff --git a/docs/02_contexto_estado_arte.md b/docs/02_contexto_estado_arte.md index 48ed34e..54a2b8a 100644 --- a/docs/02_contexto_estado_arte.md +++ b/docs/02_contexto_estado_arte.md @@ -1,6 +1,6 @@ # Contexto y estado del arte -Para comprender el alcance y las decisiones tomadas en este trabajo, es necesario situarlo en su contexto tecnológico. El Reconocimiento Óptico de Caracteres ha recorrido un largo camino desde los primeros sistemas de plantillas de los años 50 hasta las sofisticadas arquitecturas de aprendizaje profundo actuales. A lo largo de este capítulo se revisan los fundamentos técnicos del OCR moderno. Se analizan las principales soluciones de código abierto y se identifican los vacíos en la literatura que motivan la contribución de este trabajo. +El Reconocimiento Óptico de Caracteres ha recorrido un largo camino desde los primeros sistemas de plantillas de los años 50 hasta las sofisticadas arquitecturas de aprendizaje profundo actuales. Motores clásicos como Tesseract marcaron un punto de inflexión en la adopción práctica de OCR en entornos reales (Smith, 2007). ## Contexto del problema @@ -137,6 +137,8 @@ Una vez detectadas las regiones de texto, la etapa de reconocimiento transcribe **CRNN (Convolutional Recurrent Neural Network)**: Propuesta por Shi et al. (2016), CRNN combina una CNN para extracción de características visuales con una RNN bidireccional (típicamente LSTM) para modelado de secuencias, entrenada con pérdida CTC. Esta arquitectura estableció el paradigma encoder-decoder que domina el campo. +En reconocimiento de texto en escenas, los modelos basados en secuencias convolucionales han mostrado mejoras relevantes en precisión y velocidad (He et al., 2016). + La arquitectura CRNN consta de tres componentes: 1. **Capas convolucionales**: Extraen características visuales de la imagen de entrada 2. **Capas recurrentes**: Modelan las dependencias secuenciales entre características @@ -201,6 +203,8 @@ El WER es generalmente mayor que el CER, ya que un solo error de carácter puede **BLEU Score**: Adaptado de traducción automática, mide la similitud entre el texto predicho y la referencia considerando n-gramas. +**Métricas derivadas de WER**: Variantes como MER y WIL complementan la evaluación de reconocimiento de secuencias (Morris et al., 2004). + ### Particularidades del OCR para el Idioma Español El español, como lengua romance, presenta características específicas que impactan el rendimiento de los sistemas OCR: @@ -240,7 +244,7 @@ En los últimos años han surgido varias soluciones OCR de código abierto que d #### EasyOCR -EasyOCR es una biblioteca de OCR desarrollada por Jaided AI (2020) con el objetivo de proporcionar una solución de fácil uso que soporte múltiples idiomas. Actualmente soporta más de 80 idiomas, incluyendo español. +EasyOCR es una librería de OCR desarrollada por JaidedAI (2020) con el objetivo de proporcionar una solución de fácil uso que soporte múltiples idiomas. Actualmente soporta más de 80 idiomas, incluyendo español. **Arquitectura técnica**: - **Detector**: CRAFT (Character Region Awareness for Text Detection) @@ -263,7 +267,7 @@ EasyOCR es una biblioteca de OCR desarrollada por Jaided AI (2020) con el objeti #### PaddleOCR -PaddleOCR es el sistema OCR desarrollado por Baidu como parte del ecosistema PaddlePaddle (2024). Representa una de las soluciones más completas y activamente mantenidas en el ecosistema de código abierto. La versión PP-OCRv5, utilizada en este trabajo, incorpora los últimos avances en el campo. +PaddleOCR es el sistema OCR desarrollado por Baidu como parte del ecosistema PaddlePaddle (2024). Representa una de las soluciones más completas y activamente mantenidas en el ecosistema de código abierto. Su evolución incluye PP-OCR (Du et al., 2020) y PP-OCRv4 (Du et al., 2023); la versión PP-OCRv5, utilizada en este trabajo, incorpora avances recientes en precisión y eficiencia. **Arquitectura técnica**: @@ -424,7 +428,7 @@ Desventajas: Propuesto por Bergstra & Bengio (2012), Random Search muestrea configuraciones aleatoriamente del espacio de búsqueda. Sorprendentemente, supera a Grid Search en muchos escenarios prácticos. -La intuición es que, cuando solo algunos hiperparámetros son importantes, Random Search explora más valores de estos parámetros críticos mientras Grid Search desperdicia evaluaciones variando parámetros irrelevantes. +La intuición es que, cuando solo algunos hiperparámetros son importantes, Random Search explora más valores de estos parámetros críticos mientras Grid Search desperdicia evaluaciones variando parámetros irrelevantes. En muchos escenarios, la búsqueda aleatoria ofrece un baseline competitivo (Bergstra & Bengio, 2012). **Optimización Bayesiana**: @@ -463,7 +467,7 @@ Configuraciones con alta probabilidad bajo $l$ y baja probabilidad bajo $g$ tien #### Ray Tune -Ray Tune (Liaw et al., 2018) es un framework de optimización de hiperparámetros escalable construido sobre Ray, un sistema de computación distribuida. Sus características principales incluyen: +Ray Tune (Liaw et al., 2018) es un framework de optimización de hiperparámetros escalable construido sobre Ray, un sistema de computación distribuida (Moritz et al., 2018). Sus características principales incluyen: **Escalabilidad**: - Ejecución paralela de múltiples trials @@ -487,6 +491,8 @@ La combinación de Ray Tune con OptunaSearch permite: 3. Beneficiarse de la infraestructura de Ray para distribución 4. Acceder a las visualizaciones de Optuna +Optuna se ha consolidado como una opción práctica y eficiente para optimización de hiperparámetros en problemas reales (Akiba et al., 2019). + ```mermaid --- title: "Ciclo de optimización con Ray Tune y Optuna" @@ -523,6 +529,10 @@ Breuel (2013) exploró la selección automática de arquitecturas de red para re Schulz & Kuhn (2017) optimizaron parámetros de modelos de lenguaje para corrección de errores OCR, incluyendo pesos de interpolación entre modelos de caracteres y palabras. +Además, la variabilidad del rendimiento puede analizarse mediante correlaciones lineales (Pearson, 1895), complementadas por criterios de magnitud del efecto (Cohen, 1988). + +Finalmente, líneas de AutoML como la búsqueda de arquitecturas (NAS) representan alternativas más costosas pero potencialmente automatizables para optimizar modelos (Zoph & Le, 2017). + **Vacío en la literatura**: A pesar de estos trabajos, existe un vacío significativo respecto a la optimización sistemática de hiperparámetros de inferencia en pipelines OCR modernos como PaddleOCR. La mayoría de trabajos se centran en: @@ -588,7 +598,9 @@ Los trabajos previos en OCR para español se han centrado principalmente en: La optimización de hiperparámetros para documentos académicos en español representa una contribución original de este trabajo, abordando un nicho no explorado en la literatura. -En síntesis, la revisión del estado del arte revela un panorama en el que las herramientas técnicas están maduras, pero su aplicación óptima para dominios específicos permanece poco explorada. Los sistemas OCR modernos, como PaddleOCR, EasyOCR y DocTR, ofrecen arquitecturas sofisticadas basadas en aprendizaje profundo que alcanzan resultados impresionantes en benchmarks estándar. Sin embargo, estos resultados no siempre se trasladan a documentos del mundo real, especialmente en idiomas con menos recursos como el español. +## Conclusiones + +La revisión del estado del arte revela un panorama en el que las herramientas técnicas están maduras, pero su aplicación óptima para dominios específicos permanece poco explorada. Los sistemas OCR modernos, como PaddleOCR, EasyOCR y DocTR, ofrecen arquitecturas sofisticadas basadas en aprendizaje profundo que alcanzan resultados impresionantes en benchmarks estándar. Sin embargo, estos resultados no siempre se trasladan a documentos del mundo real, especialmente en idiomas con menos recursos como el español. La evolución desde los sistemas de plantillas de los años 50 hasta los Transformers actuales ha sido espectacular, pero ha generado sistemas con decenas de hiperparámetros configurables cuyos valores por defecto representan compromisos generales, no configuraciones óptimas para dominios específicos. La literatura abunda en trabajos sobre entrenamiento y fine-tuning de modelos OCR, pero dedica poca atención a la optimización sistemática de los parámetros de inferencia, como umbrales de detección, opciones de preprocesamiento y filtros de confianza, que pueden marcar la diferencia entre un sistema usable y uno que requiere corrección manual extensiva. diff --git a/docs/03_objetivos_metodologia.md b/docs/03_objetivos_metodologia.md index 9694607..61f11a5 100644 --- a/docs/03_objetivos_metodologia.md +++ b/docs/03_objetivos_metodologia.md @@ -1,12 +1,10 @@ # Objetivos concretos y metodología de trabajo -La motivación presentada en el capítulo anterior se traduce ahora en objetivos concretos y medibles. Siguiendo la metodología SMART propuesta por Doran (1981), se define un objetivo general que guía el trabajo y cinco objetivos específicos que lo descomponen en metas alcanzables. La segunda parte del capítulo describe la metodología experimental diseñada para alcanzar estos objetivos. - ## Objetivo general > **Optimizar el rendimiento de PaddleOCR para documentos académicos en español mediante ajuste de hiperparámetros, alcanzando un CER inferior al 2% sin requerir fine-tuning del modelo.** -### Justificación SMART del Objetivo General +### Justificación SMART del Objetivo General (Doran, 1981) **Tabla 13.** *Justificación SMART del objetivo general.* @@ -60,7 +58,7 @@ flowchart LR **Descripción de las fases:** -- **Fase 1 - Preparación del Dataset**: Conversión PDF a imágenes (300 DPI), extracción de ground truth con PyMuPDF +- **Fase 1 - Preparación del Dataset**: Conversión PDF a imágenes (300 DPI), extracción de ground truth con PyMuPDF (PyMuPDF, 2024) - **Fase 2 - Benchmark Comparativo**: Evaluación de EasyOCR, PaddleOCR, DocTR con métricas CER/WER - **Fase 3 - Espacio de Búsqueda**: Identificación de hiperparámetros y configuración de Ray Tune + Optuna - **Fase 4 - Optimización**: Ejecución de 64 trials con paralelización (2 concurrentes) @@ -167,7 +165,7 @@ Se utilizó la biblioteca `jiwer` para calcular CER y WER comparando el texto de #### Configuración de Ray Tune -El espacio de búsqueda se definió utilizando `tune.choice()` para parámetros booleanos y `tune.uniform()` para parámetros continuos, con OptunaSearch como algoritmo de optimización configurado para minimizar CER en 64 trials. La implementación completa está disponible en [`src/raytune/raytune_ocr.py`](https://seryus.ddns.net/unir/MastersThesis/src/branch/main/src/raytune/raytune_ocr.py) (ver Anexo A). +El espacio de búsqueda se definió utilizando `tune.choice()` para parámetros booleanos y `tune.uniform()` para parámetros continuos, con OptunaSearch como algoritmo de optimización configurado para minimizar CER en 64 trials. La implementación completa está disponible en [`src/raytune/raytune_ocr.py`](https://seryus.ddns.net/unir/MastersThesis/src/branch/main/src/raytune/raytune_ocr.py) (ver Anexo A). Ray Tune se apoya en el ecosistema Ray para escalar la búsqueda (Moritz et al., 2018). ### Fase 4: Ejecución de Optimización @@ -286,7 +284,7 @@ Para un proyecto de investigación con múltiples iteraciones de ajuste de hiper 1. **Tamaño del dataset**: El dataset contiene 45 páginas de documentos académicos UNIR. Resultados pueden no generalizar a otros formatos. -2. **Subconjunto de optimización**: El ajuste de hiperparámetros se realizó sobre 5 páginas (páginas 5-10), lo que contribuyó al sobreajuste observado en la validación del dataset completo. +2. **Subconjunto de optimización**: El ajuste de hiperparámetros se realizó sobre 5 páginas (páginas 5-10), y su impacto se analiza en detalle en el capítulo de desarrollo específico. 3. **Texto de referencia imperfecto**: El texto de referencia extraído de PDF puede contener errores en documentos con diseños complejos. diff --git a/docs/04_desarrollo_especifico.md b/docs/04_desarrollo_especifico.md index 9bae2e3..08e8b61 100644 --- a/docs/04_desarrollo_especifico.md +++ b/docs/04_desarrollo_especifico.md @@ -1,11 +1,7 @@ # Desarrollo específico de la contribución -El presente capítulo constituye el núcleo técnico de este trabajo fin de máster. Siguiendo la estructura de "Comparativa de soluciones" establecida por las instrucciones de UNIR, se desarrollan tres fases interrelacionadas. Estas fases son tres: planteamiento y ejecución del benchmark comparativo, optimización de hiperparámetros mediante Ray Tune, y análisis e interpretación de los resultados. - ## Planteamiento de la comparativa -### Introducción - Antes de abordar la optimización de hiperparámetros, era necesario seleccionar el motor OCR que serviría como base para la experimentación. Para ello, se realizó un estudio comparativo entre tres soluciones de código abierto representativas del estado del arte: EasyOCR, PaddleOCR y DocTR. Los experimentos, documentados en los informes de métricas y en los CSV de resultados del repositorio, permitieron identificar el modelo más prometedor para la fase de optimización posterior. ### Identificación del Problema @@ -18,7 +14,7 @@ Los documentos académicos típicos incluyen texto corrido con índice, listas n Se seleccionaron tres soluciones OCR de código abierto representativas del estado del arte: -**Tabla 20.** *Soluciones OCR evaluadas en el benchmark comparativo.* +**Tabla 20.** *Soluciones OCR del benchmark.* | Solución | Desarrollador | Versión | Justificación de selección | |----------|---------------|---------|----------------------------| @@ -205,20 +201,16 @@ Esta riqueza de configuración permite explorar sistemáticamente el espacio de 2. **Único tipo de documento**: Documentos académicos de UNIR únicamente. Otros tipos de documentos (facturas, formularios, contratos) podrían presentar resultados diferentes. -3. **Ground truth automático**: El texto de referencia se extrajo programáticamente del PDF, lo cual puede introducir errores en el orden de lectura cuando hay secciones con encabezados y saltos de línea. - -4. **Referencia CPU separada**: Los tiempos en CPU se midieron en un experimento independiente y solo se usan como comparación de rendimiento frente a GPU. +3. **Referencia CPU separada**: Los tiempos en CPU se midieron en un experimento independiente y solo se usan como comparación de rendimiento frente a GPU. ### Síntesis del Benchmark -El benchmark comparativo ha permitido identificar PaddleOCR como la solución más prometedora para la fase de optimización, gracias a su combinación de rendimiento base competitivo, alta configurabilidad del pipeline y documentación técnica completa. Sin embargo, el análisis también reveló limitaciones importantes: el tamaño reducido del benchmark (5 páginas), la restricción a un único tipo de documento, y la extracción automática del ground truth que puede introducir errores en el orden de lectura cuando hay secciones con encabezados y saltos de línea. Estas limitaciones se tendrán en cuenta al interpretar los resultados de la fase de optimización. +El benchmark comparativo ha permitido identificar PaddleOCR como la solución más prometedora para la fase de optimización, gracias a su combinación de rendimiento base competitivo, alta configurabilidad del pipeline y documentación técnica completa. Estas limitaciones se tendrán en cuenta al interpretar los resultados de la fase de optimización. **Fuente:** [`docs/metrics/metrics.md`](https://seryus.ddns.net/unir/MastersThesis/src/branch/main/docs/metrics/metrics.md), [`src/results/*.csv`](https://seryus.ddns.net/unir/MastersThesis/src/branch/main/src/results/*.csv), documentación oficial de PaddleOCR. ## Desarrollo de la comparativa: Optimización de hiperparámetros -### Introducción - Una vez seleccionado PaddleOCR como motor base, el siguiente paso fue explorar sistemáticamente su espacio de configuración para identificar los hiperparámetros que maximizan el rendimiento en documentos académicos en español. Para ello se empleó Ray Tune con el algoritmo de búsqueda Optuna, una combinación que permite explorar eficientemente espacios de búsqueda mixtos (parámetros continuos y categóricos). Los experimentos se implementaron en [`src/run_tuning.py`](https://seryus.ddns.net/unir/MastersThesis/src/branch/main/src/run_tuning.py) con apoyo de la librería [`src/raytune_ocr.py`](https://seryus.ddns.net/unir/MastersThesis/src/branch/main/src/raytune_ocr.py), almacenándose los resultados en [`src/results`](https://seryus.ddns.net/unir/MastersThesis/src/branch/main/src/results). Esta aproximación ofrece ventajas significativas frente al fine-tuning tradicional: no requiere datasets de entrenamiento etiquetados, no modifica los pesos del modelo preentrenado, y puede ejecutarse con hardware de consumo cuando se dispone de aceleración GPU. ### Configuración del Experimento @@ -538,7 +530,7 @@ La clase `ImageTextDataset` gestiona la carga de pares imagen-texto desde la est El espacio de búsqueda se definió considerando los hiperparámetros más relevantes identificados en la documentación de PaddleOCR, utilizando `tune.choice()` para parámetros booleanos y `tune.uniform()` para umbrales continuos. La implementación está disponible en [`src/raytune/raytune_ocr.py`](https://seryus.ddns.net/unir/MastersThesis/src/branch/main/src/raytune/raytune_ocr.py) (ver Anexo A). -**Tabla 30.** *Descripción detallada del espacio de búsqueda.* +**Tabla 30.** *Espacio de búsqueda: parámetros.* | Parámetro | Tipo | Rango | Descripción | |-----------|------|-------|-------------| @@ -694,7 +686,7 @@ Configuración óptima: #### Análisis de Correlación -Se calculó la correlación de Pearson entre los parámetros de configuración (codificados como 0/1 en el caso de booleanos) y las métricas de error: +Se calculó la correlación de Pearson entre los parámetros de configuración (codificados como 0/1 en el caso de booleanos) y las métricas de error. Para interpretar la magnitud de las correlaciones se siguieron criterios habituales en investigación cuantitativa: **Tabla 36.** *Correlación de parámetros con CER.* @@ -831,7 +823,7 @@ La configuración óptima identificada se evaluó sobre el dataset completo de 4 **Fuente:** [`docs/metrics/metrics_paddle.md`](https://seryus.ddns.net/unir/MastersThesis/src/branch/main/docs/metrics/metrics_paddle.md). -> **Nota sobre generalización:** El mejor trial individual (5 páginas) alcanzó un CER de 0.79%, cumpliendo el objetivo de CER < 2%. Sin embargo, al aplicar la configuración al dataset completo de 45 páginas, el CER aumentó a 7.72%, evidenciando sobreajuste al subconjunto de entrenamiento. Esta diferencia es un hallazgo importante que se discute en la sección de análisis. +> **Nota sobre generalización:** El contraste entre el mejor trial y la validación en el dataset completo evidencia sobreajuste al subconjunto de entrenamiento. Esta diferencia se analiza en la sección de resultados consolidados. #### Métricas de Mejora @@ -848,11 +840,11 @@ La configuración óptima identificada se evaluó sobre el dataset completo de 4 **Fuente:** [`docs/metrics/metrics_paddle.md`](https://seryus.ddns.net/unir/MastersThesis/src/branch/main/docs/metrics/metrics_paddle.md). -**Figura 18.** *Reducción de errores: baseline vs optimizado (45 páginas).* +**Figura 18.** *Reducción de errores (baseline vs optimizado).* ```mermaid --- -title: "Reducción de errores: Baseline vs Optimizado (45 páginas)" +title: "Reducción de errores (baseline vs optimizado)" config: theme: base themeVariables: @@ -901,14 +893,12 @@ Observaciones: Los 64 trials ejecutados con Ray Tune y aceleración GPU revelaron patrones claros en el comportamiento de PaddleOCR. El hallazgo más significativo es que los parámetros estructurales, `textline_orientation` y `use_doc_orientation_classify`, tienen mayor impacto que los umbrales numéricos. Al activarlos se reduce el CER medio de 4.73% a 1.74%. En cuanto a umbrales, valores bajos de `text_det_thresh` (aprox. 0.05) benefician el rendimiento, mientras que `use_doc_unwarping` resulta innecesario para PDFs digitales. -El mejor trial alcanzó un CER de 0.79%, cumpliendo el objetivo de CER < 2%. No obstante, la validación sobre el dataset completo de 45 páginas arrojó un CER de 7.72%, evidenciando sobreajuste al subconjunto de optimización de 5 páginas. Aun así, esto representa una mejora del 12.8% respecto al baseline (8.85%), demostrando el valor de la optimización sistemática incluso cuando la generalización es imperfecta. +La optimización logró mejoras claras frente al baseline en el dataset completo, aunque la generalización quedó limitada por el tamaño del subconjunto de ajuste. **Fuente:** [`src/run_tuning.py`](https://seryus.ddns.net/unir/MastersThesis/src/branch/main/src/run_tuning.py), [`src/raytune_ocr.py`](https://seryus.ddns.net/unir/MastersThesis/src/branch/main/src/raytune_ocr.py), [`src/results/raytune_paddle_results_20260119_122609.csv`](https://seryus.ddns.net/unir/MastersThesis/src/branch/main/src/results/raytune_paddle_results_20260119_122609.csv). ## Discusión y análisis de resultados -### Introducción - Los resultados obtenidos en las secciones anteriores requieren un análisis que trascienda los números individuales para comprender su significado práctico. En esta sección se consolidan los hallazgos del benchmark comparativo y la optimización de hiperparámetros, evaluando hasta qué punto se han cumplido los objetivos planteados y qué limitaciones condicionan la generalización de las conclusiones. ### Resumen Consolidado de Resultados diff --git a/docs/05_conclusiones_trabajo_futuro.md b/docs/05_conclusiones_trabajo_futuro.md index db84e65..0d14f77 100644 --- a/docs/05_conclusiones_trabajo_futuro.md +++ b/docs/05_conclusiones_trabajo_futuro.md @@ -1,11 +1,7 @@ # Conclusiones y trabajo futuro -A lo largo de este trabajo se ha explorado la optimización de hiperparámetros como estrategia para mejorar el rendimiento de sistemas OCR sin necesidad de reentrenamiento. Las siguientes secciones evalúan el grado de cumplimiento de los objetivos planteados, sintetizan los hallazgos más relevantes y proponen direcciones para investigación futura. - ## Conclusiones -### Conclusiones Generales - Los resultados obtenidos confirman que la optimización sistemática de hiperparámetros constituye una alternativa viable al fine-tuning para mejorar sistemas OCR preentrenados. La infraestructura dockerizada con aceleración GPU desarrollada en este trabajo no solo facilita la experimentación reproducible, sino que reduce drásticamente los tiempos de ejecución, haciendo viable la exploración exhaustiva de espacios de configuración. El objetivo principal del trabajo era alcanzar un CER inferior al 2% en documentos académicos en español. Los resultados obtenidos se resumen a continuación: diff --git a/docs/07_anexo_a.md b/docs/07_anexo_a.md index 7b733a4..92c1bc0 100644 --- a/docs/07_anexo_a.md +++ b/docs/07_anexo_a.md @@ -206,7 +206,7 @@ Esta sección presenta los resultados completos de las evaluaciones comparativas ### Comparativa General de Servicios -**Tabla A4.** *Comparativa de servicios OCR en dataset de 45 páginas (GPU RTX 3060).* +**Tabla A4.** *Servicios OCR en 45 páginas (RTX 3060).* | Servicio | CER | WER | Tiempo/Página | Tiempo Total | VRAM | |----------|-----|-----|---------------|--------------|------| @@ -247,27 +247,7 @@ Se ejecutaron 64 trials por servicio utilizando Ray Tune con Optuna sobre las p **Fuente:** [`src/results/raytune_paddle_results_20260119_122609.csv`](https://seryus.ddns.net/unir/MastersThesis/src/branch/main/src/results/raytune_paddle_results_20260119_122609.csv). -**Figura A1.** *Distribución de trials por rango de CER (PaddleOCR).* - -```mermaid ---- -title: "Distribución de trials por rango de CER (PaddleOCR)" -config: - theme: base - themeVariables: - primaryColor: "#E6F4F9" - primaryTextColor: "#404040" - primaryBorderColor: "#0098CD" - lineColor: "#0098CD" ---- -pie showData - title Distribución de 64 trials - "CER < 2%" : 43 - "CER 2-5%" : 10 - "CER 5-10%" : 11 -``` - -**Fuente:** [`src/results/raytune_paddle_results_20260119_122609.csv`](https://seryus.ddns.net/unir/MastersThesis/src/branch/main/src/results/raytune_paddle_results_20260119_122609.csv). +> **Nota:** Ver [Figura 15](#figura-15) en el Capítulo 4 para la representación gráfica de esta distribución. ### Configuración Óptima PaddleOCR @@ -302,28 +282,7 @@ La siguiente configuración logró el mejor rendimiento en el ajuste de hiperpar **Fuente:** Datos de tiempo CPU de [`src/raytune_paddle_subproc_results_20251207_192320.csv`](https://seryus.ddns.net/unir/MastersThesis/src/branch/main/src/raytune_paddle_subproc_results_20251207_192320.csv) y tiempos de GPU en trials de ajuste. Elaboración propia. -**Figura A2.** *Tiempo de procesamiento: CPU vs GPU (segundos/página).* - -```mermaid ---- -title: "Tiempo de procesamiento: CPU vs GPU (segundos/página)" -config: - theme: base - themeVariables: - primaryColor: "#E6F4F9" - primaryTextColor: "#404040" - primaryBorderColor: "#0098CD" - lineColor: "#0098CD" - xyChart: - plotColorPalette: "#0098CD" ---- -xychart-beta - x-axis ["CPU", "GPU (RTX 3060)"] - y-axis "Segundos por página" 0 --> 75 - bar [69.4, 0.84] -``` - -**Fuente:** [`src/raytune_paddle_subproc_results_20251207_192320.csv`](https://seryus.ddns.net/unir/MastersThesis/src/branch/main/src/raytune_paddle_subproc_results_20251207_192320.csv) y [`src/results/raytune_paddle_results_20260119_122609.csv`](https://seryus.ddns.net/unir/MastersThesis/src/branch/main/src/results/raytune_paddle_results_20260119_122609.csv). Leyenda: Aceleración de **82x** con GPU. El procesamiento de una página pasa de 69.4s (CPU) a 0.84s (GPU). +> **Nota:** Ver [Figura 20](#figura-20) en el Capítulo 4 para la representación gráfica de esta comparación. ### Análisis de Errores por Servicio @@ -375,3 +334,18 @@ Requisitos extraídos de la documentación oficial de las dependencias usadas: ## A.10 Licencia El código se distribuye bajo licencia MIT. + +## A.11 Índice de acrónimos + +**Tabla A10.** *Acrónimos utilizados en el trabajo.* + +| Acrónimo | Significado | +|----------|-------------| +| OCR | Reconocimiento Óptico de Caracteres | +| CER | Character Error Rate (tasa de error de caracteres) | +| WER | Word Error Rate (tasa de error de palabras) | +| GPU | Graphics Processing Unit | +| CPU | Central Processing Unit | +| API | Application Programming Interface | + +**Fuente:** Elaboración propia. diff --git a/docs/compliance.md b/docs/compliance.md index 0f8af19..898f64e 100644 --- a/docs/compliance.md +++ b/docs/compliance.md @@ -1,157 +1,256 @@ -# UNIR Style Compliance Checklist - -This document lists the UNIR TFE style requirements to verify before final submission. - -## Page Layout - -| Requirement | Specification | Check | -|-------------|---------------|-------| -| Page size | A4 | ☐ | -| Left margin | 3.0 cm | ☐ | -| Right margin | 2.0 cm | ☐ | -| Top margin | 2.5 cm | ☐ | -| Bottom margin | 2.5 cm | ☐ | -| Header | Student name + TFE title | ☐ | -| Footer | Page number | ☐ | - -## Typography - -| Element | Specification | Check | -|---------|---------------|-------| -| Body text | Calibri 12pt, justified, 1.5 line spacing | ☐ | -| Título 1 (H1) | Calibri Light 18pt, blue, numbered (1., 2., ...) | ☐ | -| Título 2 (H2) | Calibri Light 14pt, blue, numbered (1.1, 1.2, ...) | ☐ | -| Título 3 (H3) | Calibri Light 12pt, numbered (1.1.1, 1.1.2, ...) | ☐ | -| Título 4 (H4) | Calibri 12pt, bold, unnumbered | ☐ | -| Footnotes | Calibri 10pt, justified, single spacing | ☐ | -| Code blocks | Consolas 10pt | ☐ | - -## Document Structure - -| Section | Requirements | Check | -|---------|--------------|-------| -| Portada | Title, Author, Type, Director, Date | ☐ | -| Resumen | 150-300 words in Spanish + Palabras clave (3-5) | ☐ | -| Abstract | 150-300 words in English + Keywords (3-5) | ☐ | -| Índice de contenidos | Auto-generated, new page | ☐ | -| Índice de figuras | Auto-generated, new page | ☐ | -| Índice de tablas | Auto-generated, new page | ☐ | -| Cap. 1 Introducción | 1.1 Motivación, 1.2 Planteamiento, 1.3 Estructura | ☐ | -| Cap. 2 Contexto | 2.1 Contexto, 2.2 Estado del arte, 2.3 Conclusiones | ☐ | -| Cap. 3 Objetivos | 3.1 Objetivo general, 3.2 Específicos, 3.3 Metodología | ☐ | -| Cap. 4 Desarrollo | Structure depends on work type | ☐ | -| Cap. 5 Conclusiones | 5.1 Conclusiones, 5.2 Trabajo futuro | ☐ | -| Referencias | APA format, alphabetical order | ☐ | -| Anexos | Code repository URL, supplementary data | ☐ | - -## Tables - -| Requirement | Specification | Check | -|-------------|---------------|-------| -| Title position | Above the table | ☐ | -| Title format | **Tabla N.** *Descriptive title in italics.* | ☐ | -| Numbering | Sequential (1, 2, 3...), Anexo uses A1, A2... | ☐ | -| Border style | APA: horizontal lines only (top, header bottom, table bottom) | ☐ | -| Source position | Below the table, centered | ☐ | -| Source format | Fuente: Author, Year. or Fuente: Elaboración propia. | ☐ | -| Leyenda (if needed) | Below Fuente, same style (Piedefoto-tabla) | ☐ | -| In TOT index | All tables appear in Índice de tablas | ☐ | - -## Figures - -| Requirement | Specification | Check | -|-------------|---------------|-------| -| Title position | Above the figure | ☐ | -| Title format | **Figura N.** *Descriptive title in italics.* | ☐ | -| Numbering | Sequential (1, 2, 3...), Anexo uses A1, A2... | ☐ | -| Alignment | Centered | ☐ | -| Source position | Below the figure, centered | ☐ | -| Source format | Fuente: Author, Year. or Fuente: Elaboración propia. | ☐ | -| Leyenda (if needed) | Below Fuente, same style (Piedefoto-tabla) | ☐ | -| In TOF index | All figures appear in Índice de figuras | ☐ | - -## Lists - -| Requirement | Specification | Check | -|-------------|---------------|-------| -| Bullet lists | Indented 36pt, bullet symbol (·) | ☐ | -| Numbered lists | Indented 36pt, sequential numbers (1, 2, 3...) | ☐ | -| Spacing | Proper First/Middle/Last paragraph spacing | ☐ | - -## Citations and References - -| Requirement | Specification | Check | -|-------------|---------------|-------| -| Citation format | APA 7th edition | ☐ | -| Single author | (Author, Year) or Author (Year) | ☐ | -| Two authors | (Author1 & Author2, Year) | ☐ | -| Three+ authors | (Author1 et al., Year) | ☐ | -| Reference list | Alphabetical by first author surname | ☐ | -| Hanging indent | 36pt left margin, -36pt text indent | ☐ | -| DOI/URL | Include when available | ☐ | -| No Wikipedia | Wikipedia citations not allowed | ☐ | -| Source variety | Books, journals, conferences (not just URLs) | ☐ | - -## SMART Objectives - -All objectives must be SMART: - -| Criterion | Requirement | Check | -|-----------|-------------|-------| -| **S**pecific | Clearly defined, unambiguous | ☐ | -| **M**easurable | Quantifiable success metric (e.g., CER < 2%) | ☐ | -| **A**ttainable | Feasible with available resources | ☐ | -| **R**elevant | Demonstrable impact | ☐ | -| **T**ime-bound | Achievable within timeframe | ☐ | - -## Writing Style - -| Requirement | Check | -|-------------|-------| -| Each chapter starts with introductory paragraph | ☐ | -| Each paragraph has at least 3 sentences | ☐ | -| No two consecutive headings without text between them | ☐ | -| No superfluous phrases or repetition | ☐ | -| All concepts defined with pertinent citations | ☐ | -| Spelling checked (Word corrector) | ☐ | -| Logical flow between paragraphs | ☐ | - -## Final Checks - -| Requirement | Check | -|-------------|-------| -| All cited references appear in reference list | ☐ | -| All references in list are cited in text | ☐ | -| All figures/tables have numbers and titles | ☐ | -| Update all indices (Ctrl+A, F9 in Word) | ☐ | -| Page count: 50-90 pages (excl. cover, indices, annexes) | ☐ | -| Final format: PDF for deposit | ☐ | - -## Automated Checks (apply_content.py) - -The following are automatically handled by the generation scripts: - -- ✓ Table/Figure sequential numbering -- ✓ Anexo items use A1, A2... prefix -- ✓ TC fields for Anexo items (appear in indices) -- ✓ Piedefoto-tabla style for Fuente/Leyenda -- ✓ MsoCaption style for titles -- ✓ APA table borders (horizontal only) -- ✓ MsoBibliography style for references -- ✓ MsoQuote style for blockquotes -- ✓ List paragraph classes (First/Middle/Last) -- ✓ Bold H4 headings (unnumbered) - -## Color Palette (UNIR Theme) - -| Color | Hex | Usage | -|-------|-----|-------| -| Primary Blue | `#0098CD` | Headings, diagram borders | -| Light Blue BG | `#E6F4F9` | Diagram backgrounds | -| Dark Gray | `#404040` | Body text | -| Accent Blue | `#5B9BD5` | Table headers | -| Light Accent | `#9CC2E5` | Table borders | - ---- - -**Reference:** UNIR TFE Guidelines (`instructions/instrucciones.pdf`, `instructions/plantilla_individual.pdf`) +# Lista de verificación de cumplimiento de estilo UNIR + +Este documento reúne los requisitos de estilo del TFE de UNIR a verificar antes de la entrega final. + +## Maquetación de página + +| Requisito | Especificación | Check | +|-----------|----------------|-------| +| Tamaño de página | A4 | ☐ | +| Margen izquierdo | 3.0 cm | ☐ | +| Margen derecho | 2.0 cm | ☐ | +| Margen superior | 2.5 cm | ☐ | +| Margen inferior | 2.5 cm | ☐ | +| Encabezado | Nombre del estudiante + título del TFE | ☐ | +| Pie de página | Número de página | ☐ | + +## Tipografía + +| Elemento | Especificación | Check | +|----------|----------------|-------| +| Texto normal | Calibri 12 pt, justificado, interlineado 1.5 | ☐ | +| Título 1 (H1) | Calibri Light 18 pt, azul, numerado (1., 2., ...) | ☐ | +| Título 2 (H2) | Calibri Light 14 pt, azul, numerado (1.1, 1.2, ...) | ☐ | +| Título 3 (H3) | Calibri Light 12 pt, numerado (1.1.1, 1.1.2, ...) | ☐ | +| Título 4 (H4) | Calibri 12 pt, negrita, sin numeración | ☐ | +| Notas al pie | Calibri 10 pt, justificado, interlineado simple | ☐ | +| Bloques de código | Consolas 10 pt | ☐ | + +## Estructura del documento + +| Sección | Requisitos | Check | +|---------|------------|-------| +| Portada | Título, autor, tipo, director, fecha | ☐ | +| Resumen | 150-300 palabras en español (plantilla) + Palabras clave (3-5); instrucciones mencionan ~150 palabras | ☐ | +| Resumen (contenido) | Debe incluir objetivo, metodología, resultados y conclusiones | ☐ | +| Abstract | 150-300 palabras en inglés + Keywords (3-5) | ☐ | +| Abstract (contenido) | Debe incluir objetivo, metodología, resultados y conclusiones | ☐ | +| Ubicación Resumen/Abstract | Al inicio del documento | ☐ | +| Índice de contenidos | Auto-generado, nueva página | ☐ | +| Índice de figuras | Auto-generado, nueva página | ☐ | +| Índice de tablas | Auto-generado, nueva página | ☐ | +| Índices separados | Contenidos/Figuras/Tablas en índices distintos | ☐ | +| Índices en nueva página | Cada índice comienza en nueva página | ☐ | +| Cap. 1 Introducción | 1.1 Motivación, 1.2 Planteamiento, 1.3 Estructura | ☐ | +| Cap. 2 Contexto | 2.1 Contexto, 2.2 Estado del arte, 2.3 Conclusiones | ☐ | +| Cap. 3 Objetivos | 3.1 Objetivo general, 3.2 Específicos, 3.3 Metodología | ☐ | +| Cap. 4 Desarrollo | Estructura según tipo de trabajo | ☐ | +| Cap. 5 Conclusiones | 5.1 Conclusiones, 5.2 Trabajo futuro | ☐ | +| Referencias | Formato APA, orden alfabético | ☐ | +| Anexos | URL del repositorio y datos complementarios | ☐ | +| Índice de acrónimos | Incluir si se usan acrónimos | ☐ | +| Inicio de capítulos | Cada capítulo comienza en nueva página | ☐ | + +## Tablas + +| Requisito | Especificación | Check | +|-----------|----------------|-------| +| Posición del título | Encima de la tabla | ☐ | +| Formato del título | **Tabla N.** *Título descriptivo en cursiva.* | ☐ | +| Numeración | Secuencial (1, 2, 3...), Anexos usan A1, A2... | ☐ | +| Bordes | APA: solo líneas horizontales (superior, cabecera, inferior) | ☐ | +| Posición de fuente | Debajo de la tabla, centrada | ☐ | +| Formato de fuente | Fuente: Autor, Año. o Fuente: Elaboración propia. | ☐ | +| Leyenda (si aplica) | Debajo de Fuente, mismo estilo (Piedefoto-tabla) | ☐ | +| En índice de tablas | Todas las tablas aparecen en el índice | ☐ | + +## Figuras + +| Requisito | Especificación | Check | +|-----------|----------------|-------| +| Posición del título | Encima de la figura | ☐ | +| Formato del título | **Figura N.** *Título descriptivo en cursiva.* | ☐ | +| Numeración | Secuencial (1, 2, 3...), Anexos usan A1, A2... | ☐ | +| Alineación | Centrada | ☐ | +| Posición de fuente | Debajo de la figura, centrada | ☐ | +| Formato de fuente | Fuente: Autor, Año. o Fuente: Elaboración propia. | ☐ | +| Leyenda (si aplica) | Debajo de Fuente, mismo estilo (Piedefoto-tabla) | ☐ | +| En índice de figuras | Todas las figuras aparecen en el índice | ☐ | + +## Listas + +| Requisito | Especificación | Check | +|-----------|----------------|-------| +| Viñetas | Sangría 36 pt, símbolo de viñeta (·) | ☐ | +| Numeradas | Sangría 36 pt, numeración secuencial (1, 2, 3...) | ☐ | +| Espaciado | Uso correcto de First/Middle/Last | ☐ | + +## Citas y referencias + +| Requisito | Especificación | Check | +|-----------|----------------|-------| +| Formato de citas | APA 7.ª edición | ☐ | +| Un autor | (Autor, Año) o Autor (Año) | ☐ | +| Dos autores | (Autor1 & Autor2, Año) | ☐ | +| Tres o más | (Autor1 et al., Año) | ☐ | +| Lista de referencias | Orden alfabético por apellido del primer autor | ☐ | +| Sangría francesa | 36 pt margen izquierdo, -36 pt sangría | ☐ | +| DOI/URL | Incluir cuando esté disponible | ☐ | +| Sin Wikipedia | No se permiten citas de Wikipedia | ☐ | +| Variedad de fuentes | Libros, revistas, congresos (no solo URLs) | ☐ | +| Referencias citadas | Toda referencia listada aparece en el texto | ☐ | +| Distribución de citas | La mayoría de citas en Cap. 2 (Estado del arte) | ☐ | + +## Objetivos SMART + +Todos los objetivos deben ser SMART: + +| Criterio | Requisito | Check | +|----------|-----------|-------| +| **S**pecific | Claramente definido, sin ambigüedad | ☐ | +| **M**easurable | Métrica cuantificable (p. ej., CER < 2%) | ☐ | +| **A**ttainable | Factible con los recursos disponibles | ☐ | +| **R**elevant | Impacto demostrable | ☐ | +| **T**ime-bound | Alcanzable en el plazo previsto | ☐ | + +## Estilo de redacción + +| Requisito | Check | +|-----------|-------| +| Cada capítulo inicia con un párrafo introductorio | ☐ | +| Cada párrafo tiene al menos 3 oraciones | ☐ | +| No hay dos encabezados consecutivos sin texto entre ellos | ☐ | +| Sin frases superfluas ni repetición | ☐ | +| Conceptos definidos con citas pertinentes | ☐ | +| Ortografía revisada (corrector de Word) | ☐ | +| Flujo lógico entre párrafos | ☐ | + +## Comprobaciones finales + +| Requisito | Check | +|-----------|-------| +| Todas las citas aparecen en la lista de referencias | ☐ | +| Todas las referencias listadas se citan en el texto | ☐ | +| Todas las figuras/tablas tienen número y título | ☐ | +| Actualizar índices (Ctrl+A, F9 en Word) | ☐ | +| Extensión: 50-90 páginas (sin portada, resumen/abstract, índices y anexos) | ☐ | +| Formato final: PDF para depósito | ☐ | + +## Comprobaciones automáticas (apply_content.py) + +Las siguientes tareas están automatizadas por los scripts de generación. + +**Arquitectura modular:** +- `apply_content.py` - Orquestador principal (~300 líneas) +- `content_handlers.py` - Manejadores de bloques de contenido (~400 líneas) +- `markdown_utils.py` - Utilidades de markdown (~150 líneas) + +**Funcionalidades automatizadas:** +- ✓ Numeración secuencial de tablas y figuras +- ✓ Anexos con prefijo A1, A2... +- ✓ Campos TC para anexos (aparecen en índices) +- ✓ Estilo Piedefoto-tabla para Fuente/Leyenda +- ✓ Estilo MsoCaption para títulos +- ✓ Bordes APA en tablas (solo horizontales) +- ✓ Estilo MsoBibliography para referencias +- ✓ Estilo MsoQuote para citas textuales +- ✓ Estilos de listas (First/Middle/Last) +- ✓ Encabezados H4 en negrita (sin numeración) + +## Paleta de color (tema UNIR) + +| Color | Hex | Uso | +|-------|-----|-----| +| Azul primario | `#0098CD` | Encabezados, bordes de diagramas | +| Azul claro (fondo) | `#E6F4F9` | Fondos de diagramas | +| Gris oscuro | `#404040` | Texto principal | +| Azul acento | `#5B9BD5` | Encabezados de tablas | +| Acento claro | `#9CC2E5` | Bordes de tablas | + +## Reglas de redundancia entre capítulos + +Al tratar el documento como un todo: + +| Tipo de repetición | Aceptable | Motivo | +|--------------------|-----------|--------| +| Métricas clave en Resumen y Conclusiones | ✓ Sí | El resumen sintetiza el trabajo | +| Resultados del Cap. 4 resumidos en Cap. 5 | ✓ Sí | Estructura académica estándar | +| Figuras idénticas en capítulo principal y anexo | ✗ No | Usar referencias cruzadas | +| Mismos datos de tablas en varios capítulos | ✗ No | Referenciar tabla previa | +| Párrafos de relleno que repiten encabezados | ✗ No | Eliminar redundancia | +| “Síntesis” intermedia en un capítulo | ⚠ Con cuidado | Solo si el capítulo es largo | + +**Antipatrones a evitar:** +- "En este capítulo se presenta..." seguido de "Las siguientes secciones describen..." (el encabezado ya lo indica) +- Diagramas Mermaid idénticos en varios capítulos (usar "Ver Figura N en Capítulo X") +- Repetir métricas exactas (CER 0.79%) más de 3 veces en capítulos principales + +## Documento como un todo + +Los archivos markdown (00-07) generan un único documento Word. Considerar: + +| Aspecto | Guía | +|---------|------| +| Auditoría global | Evaluar redundancia y coherencia tratando 00-07 como un solo documento | ☐ | +| Numeración de tablas/figuras | Secuencial en TODOS los capítulos (no reiniciar por capítulo) | +| Referencias cruzadas | "Ver Tabla X" o "como se describió en la Sección 2.3" | +| Contenido de anexos | Detalles complementarios, no duplicados del cuerpo principal | +| Datos repetidos | Referenciar ubicación, no copiar | + +## Reglas por capítulo + +### Capítulo 1: Introducción (3-5 páginas) + +| Sección | Debe incluir | +|---------|--------------| +| 1.1 Motivación | Identificación del problema, justificación e impacto, referencias previas | +| 1.2 Planteamiento | Enunciado breve del problema, solución propuesta, enfoque | +| 1.3 Estructura | Descripción breve de los capítulos siguientes | + +### Capítulo 2: Contexto y Estado del Arte (10-15 páginas) + +| Sección | Debe incluir | +|---------|--------------| +| 2.1 Contexto del problema | Estudio profundo del dominio de aplicación | +| 2.2 Estado del arte | Trabajos previos, estudios actuales, comparativas, autores clave | +| 2.3 Conclusiones | Síntesis que conecte con el trabajo a desarrollar | + +### Capítulo 3: Objetivos y Metodología + +| Sección | Debe incluir | +|---------|--------------| +| 3.1 Objetivo general | Objetivo SMART, efecto observable | +| 3.2 Objetivos específicos | 3-5 objetivos SMART, verbos en infinitivo | +| 3.3 Metodología | Pasos, instrumentos, métodos de análisis | + +### Capítulo 4: Desarrollo Específico + +Para trabajo híbrido tipo 1 (Experimental) + tipo 3 (Comparativo): + +| Sección | Debe incluir | +|---------|--------------| +| 4.1 Planteamiento | Problema, alternativas, criterios de éxito | +| 4.2 Desarrollo | Resultados, mediciones, gráficos, tablas | +| 4.3 Discusión | Significado de los resultados, ventajas/desventajas | + +### Capítulo 5: Conclusiones y Trabajo Futuro + +| Sección | Debe incluir | +|---------|--------------| +| 5.1 Conclusiones | Síntesis de aportes, **relación con objetivos**, grado de logro | +| 5.2 Líneas de trabajo futuro | Extensiones futuras con justificación | + +## Antipatrones de texto de relleno + +Eliminar texto introductorio redundante: + +| Patrón | Sustituir por | +|--------|---------------| +| "A lo largo de este capítulo se desarrolla..." | Contenido directo | +| "El presente capítulo constituye..." | Contenido directo | +| "Las siguientes secciones describen..." | Contenido directo | +| "Como se mencionó anteriormente..." | Referencia específica o eliminar | +| "### Introducción" bajo un apartado | Eliminar (el encabezado ya introduce) | + +Frases de apertura válidas: +- Ir directo al contenido diff --git a/markdown_utils.py b/markdown_utils.py new file mode 100644 index 0000000..426cfc6 --- /dev/null +++ b/markdown_utils.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python3 +"""Utility functions for markdown processing and conversion.""" + +import re +from latex2mathml.converter import convert as latex_to_mathml + +# Accept Fuente/Source lines with or without markdown bold +SOURCE_LINE_RE = re.compile(r'^\s*(?:\*{1,2})?(Fuente|Source):(?:\*{1,2})?\s*(.*)$', re.IGNORECASE) +# Accept Leyenda lines with or without markdown bold +LEYENDA_LINE_RE = re.compile(r'^\s*(?:\*{1,2})?Leyenda:(?:\*{1,2})?\s*(.*)$', re.IGNORECASE) + +# Cross-reference patterns using markdown links: +# [Figura 15](#figura-15) or [Tabla 20](#tabla-20) -> Word REF fields +# Also supports Anexo: [Figura A1](#figura-a1), [Tabla A2](#tabla-a2) +CROSS_REF_LINK_RE = re.compile(r'\[(Figura|Tabla)\s+([A-Za-z]?\d+)\]\(#(figura|tabla)-([a-z]?\d+)\)', re.IGNORECASE) +# Section/chapter cross-reference patterns: +# [Sección 4.1](#seccion-4-1) or [Capítulo 2](#capitulo-2) +SECTION_REF_LINK_RE = re.compile(r'\[(Sección|Seccion|Capítulo|Capitulo)\s+([\d\.]+)\]\(#(seccion|capitulo)-([0-9-]+)\)', re.IGNORECASE) + + +def read_file(path): + """Read file content with UTF-8 encoding, falling back to latin-1.""" + try: + with open(path, 'r', encoding='utf-8') as f: + return f.read() + except UnicodeDecodeError: + with open(path, 'r', encoding='latin-1') as f: + return f.read() + + +def write_file(path, content): + """Write content to file with UTF-8 encoding.""" + with open(path, 'w', encoding='utf-8') as f: + f.write(content) + + +def convert_cross_references(text): + """Convert markdown link cross-references to Word REF fields. + + Supported syntax (renders normally in markdown viewers): + - [Figura 15](#figura-15) -> clickable Word cross-reference to Figure 15 + - [Tabla 20](#tabla-20) -> clickable Word cross-reference to Table 20 + - [Figura A1](#figura-a1) -> links to Anexo figures + - [Sección 4.1](#seccion-4-1) -> clickable link to Section 4.1 + - [Capítulo 2](#capitulo-2) -> clickable link to Chapter 2 + """ + def replace_fig_tab_ref(match): + display_type = match.group(1) # "Figura" or "Tabla" + display_num = match.group(2) # "15" or "A1" + + if display_type.lower() == 'figura': + bookmark = f"_Ref_Fig{display_num}" + else: # Tabla + bookmark = f"_Ref_Tab{display_num}" + + display_text = f"{display_type} {display_num}" + + # Word REF field with \h for hyperlink + return f'''{display_text}''' + + def replace_section_ref(match): + display_type = match.group(1) # "Sección" or "Capítulo" + display_num = match.group(2) # "4.1" or "2" + anchor_num = match.group(4) # "4-1" or "2" + + # Create bookmark name from anchor (e.g., 4-1 -> _Ref_Sec4_1) + bookmark = f"_Ref_Sec{anchor_num.replace('-', '_')}" + display_text = f"{display_type} {display_num}" + + # Word REF field with \h for hyperlink + return f'''{display_text}''' + + # Apply cross-reference conversions + text = CROSS_REF_LINK_RE.sub(replace_fig_tab_ref, text) + text = SECTION_REF_LINK_RE.sub(replace_section_ref, text) + return text + + +def md_to_html_para(text): + """Convert markdown inline formatting to HTML.""" + # Cross-references (must be done before other conversions) + text = convert_cross_references(text) + # Bold + text = re.sub(r'\*\*([^*]+)\*\*', r'\1', text) + # Italic + text = re.sub(r'\*([^*]+)\*', r'\1', text) + # Inline code + text = re.sub(r'`([^`]+)`', r'\1', text) + # Links [text](url) -> text + text = re.sub(r'\[([^\]]+)\]\(([^)]+)\)', r'\1', text) + return text + + +def convert_latex_formulas(text): + """Convert LaTeX formulas to MathML for Word compatibility.""" + # Block formulas $$...$$ + def convert_block(match): + latex = match.group(1) + try: + mathml = latex_to_mathml(latex, display="block") + return f'

{mathml}

' + except: + return match.group(0) # Keep original if conversion fails + + text = re.sub(r'\$\$([^$]+)\$\$', convert_block, text) + + # Inline formulas $...$ + def convert_inline(match): + latex = match.group(1) + try: + return latex_to_mathml(latex, display="inline") + except: + return match.group(0) + + text = re.sub(r'\$([^$]+)\$', convert_inline, text) + return text + + +def extract_source_from_line(line): + """Return source text if line is a Fuente/Source line, otherwise None.""" + match = SOURCE_LINE_RE.match(line.strip()) + if not match: + return None + return match.group(2).strip() + + +def is_source_line(line): + """Check whether a line starts with Fuente:/Source: (optionally bold).""" + return SOURCE_LINE_RE.match(line.strip()) is not None + + +def extract_leyenda_from_line(line): + """Return leyenda text if line is a Leyenda line, otherwise None.""" + match = LEYENDA_LINE_RE.match(line.strip()) + if not match: + return None + return match.group(1).strip() + + +def is_leyenda_line(line): + """Check whether a line starts with Leyenda: (optionally bold).""" + return LEYENDA_LINE_RE.match(line.strip()) is not None + + +def split_into_paragraphs(text, lang='ES'): + """Split text by double newlines and wrap each paragraph in

tags.""" + paragraphs = [] + for para in text.split('\n\n'): + para = para.strip() + if para: + formatted = md_to_html_para(para) + paragraphs.append(f'

{formatted}

') + return '\n'.join(paragraphs) diff --git a/thesis_output/figures/figura_12.png b/thesis_output/figures/figura_12.png index 6f2afc6a36328ea176183b4d4c8412333cb0d0dc..baf102600b079f087b176f3ada39e5b1fde5adf7 100644 GIT binary patch literal 59532 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 literal 63656 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 Ao&W#< diff --git a/thesis_output/figures/figura_17.png b/thesis_output/figures/figura_17.png deleted file mode 100644 index fd6d5631f3d9e5fbafbc98bd8988275189a4684b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 97998 zcmeFZS5#D6)HR5hML|(f1Oz2XRzQ#-peU&bk~2j{au7izst6*IsY0@f1(I`;P$-H} zBngtE79vojBIjO*`+eR2Yd`c`GsYb<|ggj7{KXu~L2?`2|Q;G`r zG$<&JYEn=fmODlXe{-+#uptG-X$r-AceI`)k%o`@Xln2MSnN`?eIJW0i|y?Ea?P>7 zF)~+5_1wal1NYB8kh$Ogkm;3H5N|)_2g;&5B7%pauCV+zeL<>OOMkOBpUUH9uG1x= z*tNXmw@8@zI}{ViA=*?k>P?myS(_i#g|FQI5&fO}zrW7CBwqNR--k{$3H;Bm6%5hn z|M}HLlnwtszm7R6_#OD~_qyT8ZR82yC!p%e#^L{drSOy`Q~l4cb3y-aFT5VdAnIg< z_Zu#`&Bk_b|BE7JpA6ys(FZ63jt-RA7TdI6?pjrh;kYj+$G!hGZj=ph8w=9@+l#^d zedQ)KPrub7|094g5WYQNk6z5}qpgLEj7)Ey@%jBvh#iH8wEVTZvo$|bW8bY;VEX0m z%M)D0Yl$L`(YjqFJ6&qhD{E`IjTOyt0;%GU2_?7r`P-VCQ3qT;9z04tRQc$~rzlTm z3JRIIJMgUfD}NK6M{0csKP^^Hrp$C_#@r1T^jUZ5YRE`=nx%nLIuT#=bY=R^izD%6 z?FlzS>6ey0NPUGi?I}N){Ux18s!tTep#3%`(jue;9rt#(=3l&cf!wIufoL>~5I?$B z(v8??Snf1j?WK8DdR8NEq^Htz-o(Hl!SD1ZJl_Aog9l%pAD+;N4kr?c!Iz&lp80bW zo>jv2_czT9)h2(Y^XFF@7@W0o^wh7spk@*mcIYb@uJZKu@@hh@l8W1R*YJBQ3kwfi zPLX#L1g!|s?5I4W%BzV_;{~loeAk9wy*h@mVi0k-<+EO(ZeeQrZU|4wC{t$EAI*M9 z+IO?W2xBF-Ha44ld&`@bTb>&9WhKLe%q%~-zT!eYxW7X(ZspDeud(55C&2#SjvgrA@3 z$u-Q<;&N9({Xz`<$sWDvqD&x@myf`->YxyZ_Q6 zh+PdE83{x)_nDsAdHh~aUdnB6oiCIdcVVw8Nk{$}{%uVxxBHdV6vL@`_6Y1$UW4X= zM(SW1<`fY}1Gu$;a%a5vniJ+`Fg26l8{d^68fHH-QStELP?hKWp9pCY$2bSq@2}6F z;5UVx3o6W)yUSCIf7lj@gh5}Sg2D}WN*`oH^tV=KYz&yUd(r+(w_M)2 zeTGn)vOw!`wqwjl*-^dj|9-wll8eg?HoS~T%taJ=l;8OC+hkpW9Gs?>Z&@U_nI680 zXzI(751&frZ){Aao;RvXxM|-~mMY;nx4!s?n@TR6(YBlR(xpv^W$wDXU078Pl>~=@ zk|qt|Bi|PS83S)znk8HY*Us8x}?!!{Oe1;+@j_dU47TR-JMatEyuPbF^J=z2ETv* zjzyiIqAD&S5QI+JM9=CcQZ;Kmc~U_**MCnw?2=V?Ux}@z_ph-d!mxlo^EGQZ?#E7@ zf+zYUBweDGB5AvGMNFQ0Godk2H2>pLx|Mife!jCuYuiy+4!y6JxyKBw+mb5%_jaXx zHydw!oGs7N%It*OZOO~hEBru^dNkRFIcl95_&hr#A|fthnDitM4z9wN!;MshUQ5

VzW=i_%jQ%Zx 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

Io&md)Gs;l%g$ZeTb}vHl<$U8k5*fJ+ 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 DqLl1% diff --git a/thesis_output/figures/figura_18.png b/thesis_output/figures/figura_18.png deleted file mode 100644 index 943a64b8df799eed8a332b69cde3f4b2c78f10ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 65068 zcmeFZWms12_AQQqjfEJXpi(w)AjC@2Pvq(}(TrAUc_N-GUg(v5UI zz<(@!_wSr<|1alU=gVIEy7sHWv!1o?d&Zb!j(K}slarv>LA`^FjEq9^>Lo=ovTeL% zWaMqzHsW8@=YFN*KU;1}OI#vbBmNgv5bsAucAQM|(s^Zvu+er$7n!EDPirAp8D8$a z^xZ?F=cVF*!PEY(oH*8n!C++gtV2lIU928)D-phR)^Eiw^`Q+O<33b9O&&G#Do03vby^R zcm3xt_u_XJZ~xCoXdGY6Xpr zWaN|m|L#GT&D=yM*JBw6C#T)iyx$cTH<0Q7&nAXd^n80RVBW!g)uqQrz{A7is4pGa zwWAfn8$KxwH>ckkX-XB4H~y6~7JTN`(EvK)OIcq}>K+_x&l3<8Z5N4Ed8fLvG;6Kw zMt06}GxI&}uJQo3U+>M`X9g84EiE7IKDLE|y0`zUseGE6rlzK%qOYTr4>jM>fFopc zU1v5dDk>>GrxkVTF7Y~RKi=Edx7|(a^@WGHhgyPStU=YQ>v?u}e?~vlNqujV=tCo5 zU|?|Yz=7U@fkOw@uY(80+(^xOr~SEfJ1W+%y1F_C{x`V6JJQjKA2U z8((+5(8Y12F*%OT)x{-FE`0bCNnP#QHN6O}aJhl8F;T~<+icGb9yugZN9Wm%v2t?K zia4ZYUMG(lg?D_P>TsU8o@;e^%zUUlZ$3uSZ?o5#GiR*(D_J->`Bx9q&^XTyH_Ugs z>yA~Dk(Fl|kS;uX=cRzdlc<{&Ti*<3b`g6k)PY<712t0Ao)Bk4TgfF9XbaZs3(s0AO`35!J z7AM-^Jd59-<;7QKnsnXq{3Yc#w%q?cH->HN#4Vo}6|F^`zrQp;G11qjztVf)v{q?p zsq5%pG0k4tzNoHUi2wZFDzo65>Ny?Gfq5wJi9 z<2&;mm6owvVHD53+S=N%A7-6}?LUuL>fi}_zWPvB4&_L~Cn^Hi;`IV;gs&?q))uYK zrY0!Gudd8@mPZ^h;W@m^v?;}PAl$w>{H)rrUrbzFb?_N_F)^Lury*PZsZI7Hja8N{EXCvPd6^j*GU7{1OWtZ479Zl% zPEb=*n~=V(EYIVOLjnW)n@MDx_qsNB)z3H;^k(F9Hw}}MeaMNQJ9Fdo;hVQ^iLa5S z_t9U$)(Je?LuY66EUZ3OI;g)YG|12IR*j_7{CKjm+vXJPy6v#??3K5#KYafDnNd3M z&aa%R)u8?UPRG7^$H&}@jfrtt8j62TC(ggxjNgXm3h_(dvlg})e6jdkHk9ACRo`)H zYAWaMFN27wMlExdWX%SJ#q?g5JC6~R-bw|qes%AJ<5hn&&|W*mu*z;E%BVT>J{I}_m}jIdv~O^M&Js zLE+(dm(uYSh$^;vu%8_?(Ki6}@T54enSehzW0uN7}Kex-mpI3aQ z+^e>>Ht7-*wjsiOb#eDGVZ7)zw*WssN(2+q!X)DPp@RqQU?FvNO8r4OcUr6#{`_8D zT^%1E-$N%RBQIa`owz>0xnPnqrq$aJ_*^GT5A8h2{v!AOnWLZePP1!f4c5j=2ktg% zOwxd}oIH7Qb7(d~uXet}WLNQh582~7j(t>G)8{r&{>pdETzq#z)OjvRDgIec#wNj3 zdWmPj=008H;~fb~%v<}veSG|Q+w?pGp806-k&%eco<+Y09n4tvPp1<@1;Y1szJgsft$Sv=kK;W2FLM zT|e?2rxhb9$hvMi$egQH({&993SyRT(BU*R6%%mZ);hkE|%)6wdq)xZxyKAc}zW*@Z5-bV%=9OkUqADFKvzeS6nFmH~ z=UX&8GqYWA#|H!fyr=UTnF;`2(}y>L?Ee(@iK=am8RHOt?nNn|RP z17!N=@Rm9bzsV&AHj;J4;1%^c3tf>wx@Wq&y2y7PRDP!_TP?Y9(A+b zT-;aYW5@QAFCszH4zo^QAD9{&*=o@)2FT+_~TER z&;NU5#B@lX>)h@F9l9e&j3cJ%O+KiKtB|1#?mUR?3mkxzKf4`2RJ4U4h|wo z0*9$PefxnJt&wSfzdw8Wl-+Xt7jm=s@+ePFQF~ikAeS!f(W7@rlL)$6Id{CCKMx}5 zg$vsX+mDOf{=QdUUS8>@oLn_r5zFlB=T}O1#w=3MjpU%TQ&KMFDV{RR=w~!?@DG(R{x23bCLVn`wpjyT4TOb;t2@}UoQ`scjOz3W!7q+J9lo~sD>Gfie7(>6*#;L zpXf}xaq;HO=-v%qTcZF8?*7W*&@IdlQ0nXJJ0|9`fE=Zks>8v~o@JvB*!0h5@aCRMiAifhYB5aRIOOMOdFGOEq;5P1z!CLd*)zs1H5a@KFcTiD1euB7ufBI*WdC>|oa>w!ZoE3| z_pq3Y0}^Ml%fqe_{yWWSK%Q((SJyYS^eb_VH>abD>)6XKnc$0L3*2?L{bPeu2W%BV z{NDN6e)?&k=4PU^u_NFYgM8`Tf%oGk-|A-zt={QVq#-gX6odN)>iX+m%2RY z=hw@ELR4}2GquwD_U%*MtvXbz`_aZ24+T7&WA!whU(jwe`2BnPOP5M$%<~murKpO} zI669tuP*egt}HX=@oq$r{FR|!0k8yoE@IXDegA-f5KLH=mJ)@INnM;zv0gJg5S5U5 ztaWgB_{#D`5%DEopC0MImC%K$ z^%vG#v{qJ^C%Oqx1%RpIlggI;kT*PoJ3%-QAV=XZ!phrRim`X{~9q(vfv zEXFU;3{;2bVTTZXg7=puON7YFge!+$vpPhn5_$h7HSem5laz^SChoO>&F*JGrtv#+ zqAo7)wF!OWrIvMDygR=8?rOvptZskt_Ha2YtC$Dny<}um3t4y4?cA_m;NshS$pJYaXw}t}z?vj7C z*E_LLBkQw~sN5MD^?{V(e(ALXyppoA;b(4n>S?}9R4!oiF)zEiY676m->b-v6peb; zt1jGPWk=bOAnzpA*Xf0>%idda8Me+a#>U6vW~wf7b@{hqhszY7`1nvUqomeNn0{BH zzauyX&mGFe5M4J!T3L}MbtWhTD!clJ30WH;SvP)DOdjbh!sVVIp0W)a`|rMLWQGe_ zUpcL6R>Pw(Iqu2tqdt%yTL0lJ@Av@%&%Zz_u}0Y$Dz}lS!8{13wHZ<@i#13>2^cD1 zuJh3*fz$)I)c)av;G(XAvNA8Yfwk57B7XflD0ECFN^HA6{exP)e#v1R={8P2Qs-m) zf%kV>GF%p?YWq`eBWlsPExb;2XRIm@2<+9i{|EVYzXa9R-A7N!)kl%;V9kp?$OBcC zPrZzNpQ-Y31`&oZBSynxnhL=72uC~jBm36Y)!nU%_^Io@0^Fn9GPCc{+odcQ85!V3 z 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
-

Resumen

El presente Trabajo Fin de Máster aborda la optimización de sistemas de Reconocimiento Óptico de Caracteres (OCR) basados en inteligencia artificial para documentos en español. El objetivo principal es identificar la configuración óptima de hiperparámetros que maximice la precisión del reconocimiento de texto sin requerir fine-tuning de los modelos base.

-

Se realizó un estudio comparativo de tres soluciones OCR de código abierto: EasyOCR, PaddleOCR (PP-OCRv5) y DocTR. Se evaluó su rendimiento mediante las métricas estándar CER (Character Error Rate) y WER (Word Error Rate) sobre un corpus de 45 páginas de documentos académicos en español. Tras identificar PaddleOCR como la solución más prometedora, se procedió a una optimización sistemática de hiperparámetros utilizando Ray Tune con el algoritmo de búsqueda Optuna, ejecutando 64 configuraciones diferentes con aceleración GPU (NVIDIA RTX 3060).

-

Los resultados demuestran que la optimización de hiperparámetros logró mejoras significativas: el mejor trial individual alcanzó un CER de 0.79% (precisión del 99.21%), cumpliendo el objetivo de CER < 2%. Al validar la configuración optimizada sobre el dataset completo de 45 páginas, se obtuvo una mejora del 12.8% en CER (de 8.85% a 7.72%). El hallazgo más relevante fue que el parámetro textline_orientation (clasificación de orientación de línea de texto) tiene un impacto crítico en el rendimiento. Adicionalmente, se identificó que el umbral de detección (text_det_thresh) presenta una correlación positiva moderada (0.43) con el error, lo que indica que valores más bajos tienden a mejorar el rendimiento.

+

Resumen

El presente Trabajo Fin de Máster aborda la optimización de sistemas de Reconocimiento Óptico de Caracteres (OCR) basados en inteligencia artificial para documentos en español. El objetivo principal es identificar una configuración de hiperparámetros que maximice la precisión del reconocimiento de texto sin requerir fine-tuning de los modelos base.

+

La metodología combina un benchmark comparativo de tres soluciones de código abierto (EasyOCR, PaddleOCR y DocTR) con un ajuste sistemático de hiperparámetros mediante Ray Tune y Optuna, evaluando 64 configuraciones con aceleración GPU sobre un corpus de 45 páginas. Las métricas de evaluación utilizadas fueron CER y WER.

+

Los resultados muestran mejoras significativas en el mejor trial (CER 0.79%) y una mejora del 12.8% en CER en la validación sobre el dataset completo (de 8.85% a 7.72%). El parámetro textline_orientation destacó como factor crítico, mientras que text_det_thresh mostró correlación positiva moderada con el error.

+

Se concluye que la optimización de hiperparámetros es una alternativa viable al fine-tuning en documentos académicos en español, aunque la generalización depende del tamaño del subconjunto de ajuste. La infraestructura dockerizada facilita la reproducibilidad y la evaluación sistemática de configuraciones OCR.

Fuente: metrics_paddle.md, paddle_correlations.csv.

-

Este trabajo demuestra que la optimización de hiperparámetros es una alternativa viable al fine-tuning, especialmente útil cuando se dispone de modelos preentrenados para el idioma objetivo. La infraestructura dockerizada desarrollada permite reproducir los experimentos y facilita la evaluación sistemática de configuraciones OCR.

 

-

Palabras clave: OCR, Reconocimiento Óptico de Caracteres, PaddleOCR, Optimización de Hiperparámetros, Ray Tune, Procesamiento de Documentos, Inteligencia Artificial

+

Palabras clave: OCR, PaddleOCR, Optimización de hiperparámetros, Ray Tune, Documentos académicos

 

@@ -4167,13 +4167,13 @@ EN-US;mso-bidi-language:AR-SA'>
Abstract

This Master's Thesis addresses the optimization of Artificial Intelligence-based Optical Character Recognition (OCR) systems for Spanish documents. The main objective is to identify the optimal hyperparameter configuration that maximizes text recognition accuracy without requiring fine-tuning of the base models.

-

A comparative study of three open-source OCR solutions was conducted with EasyOCR, PaddleOCR (PP-OCRv5), and DocTR. Their performance was evaluated using standard CER (Character Error Rate) and WER (Word Error Rate) metrics on a corpus of 45 pages of academic documents in Spanish. After identifying PaddleOCR as the most promising solution, systematic hyperparameter optimization was performed using Ray Tune with the Optuna search algorithm, executing 64 different configurations with GPU acceleration (NVIDIA RTX 3060).

-

Results demonstrate that hyperparameter optimization achieved significant improvements. The best individual trial reached a CER of 0.79% (99.21% accuracy), meeting the CER < 2% objective. When validating the optimized configuration on the full 45-page dataset, a 12.8% CER improvement was obtained (from 8.85% to 7.72%). The most relevant finding was that the textline_orientation parameter (text line orientation classification) has a critical impact on performance. Additionally, the detection threshold (text_det_thresh) showed a moderate positive correlation (0.43) with error, indicating that lower values tend to improve performance.

+

Abstract

This Master's Thesis addresses the optimization of AI-based Optical Character Recognition (OCR) systems for Spanish academic documents. The main objective is to identify a hyperparameter configuration that maximizes recognition accuracy without fine-tuning the base models.

+

The methodology combines a comparative benchmark of three open-source OCR engines (EasyOCR, PaddleOCR, and DocTR) with a systematic hyperparameter search using Ray Tune and Optuna. Sixty-four configurations were evaluated with GPU acceleration on a 45-page corpus, using CER and WER as evaluation metrics.

+

Results show significant gains in the best trial (CER 0.79%) and a 12.8% CER improvement on the full dataset (from 8.85% to 7.72%). The textline_orientation parameter had the strongest impact, while text_det_thresh showed a moderate positive correlation with error.

+

The study concludes that hyperparameter optimization is a viable alternative to fine-tuning for Spanish academic documents, although generalization depends on the size of the tuning subset. The dockerized infrastructure supports reproducibility and systematic evaluation of OCR configurations.

Sources: metrics_paddle.md, paddle_correlations.csv.

-

This work demonstrates that hyperparameter optimization is a viable alternative to fine-tuning, especially useful when pre-trained models for the target language are available. The dockerized infrastructure developed enables experiment reproducibility and facilitates systematic evaluation of OCR configurations.

 

-

Keywords: OCR, Optical Character Recognition, PaddleOCR, Hyperparameter Optimization, Ray Tune, Document Processing, Artificial Intelligence

+

Keywords: OCR, PaddleOCR, Hyperparameter optimization, Ray Tune, Academic documents

 

@@ -4533,7 +4533,7 @@ mso-ansi-language:ES;mso-fareast-language:EN-US;mso-bidi-language:AR-SA'>

1.   -Introducción

¿Es posible mejorar significativamente un sistema OCR sin reentrenarlo? Esta pregunta, aparentemente simple, encierra un desafío práctico que afecta a investigadores, instituciones educativas y empresas que necesitan digitalizar documentos pero carecen de los recursos para realizar fine-tuning de modelos neuronales. A lo largo de este capítulo se desarrolla la motivación del trabajo, se identifica el problema a resolver y se plantean las preguntas de investigación que guiarán el desarrollo experimental.

+Introducción

¿Es posible mejorar significativamente un sistema OCR sin reentrenarlo? Esta pregunta, aparentemente simple, encierra un desafío práctico que afecta a investigadores, instituciones educativas y empresas que necesitan digitalizar documentos pero carecen de los recursos para realizar fine-tuning de modelos neuronales.

Motivación

El Reconocimiento Óptico de Caracteres (OCR) es una tecnología fundamental en la era de la digitalización documental. Su capacidad para convertir imágenes de texto en datos editables y procesables ha transformado sectores como la administración pública, el ámbito legal, la banca y la educación. La expansión de la transformación digital empresarial ha impulsado su adopción, aunque la implementación práctica de sistemas OCR de alta precisión sigue presentando desafíos considerables.

El contexto de la digitalización documental

@@ -4595,7 +4595,7 @@ major-latin;mso-bidi-font-family:"Calibri Light";mso-bidi-theme-font:major-latin text-transform:none'>2.   Contexto y estado del arte

Para comprender el alcance y las decisiones tomadas en este trabajo, es necesario situarlo en su contexto tecnológico. El Reconocimiento Óptico de Caracteres ha recorrido un largo camino desde los primeros sistemas de plantillas de los años 50 hasta las sofisticadas arquitecturas de aprendizaje profundo actuales. A lo largo de este capítulo se revisan los fundamentos técnicos del OCR moderno. Se analizan las principales soluciones de código abierto y se identifican los vacíos en la literatura que motivan la contribución de este trabajo.

+_Toc14106979">

El Reconocimiento Óptico de Caracteres ha recorrido un largo camino desde los primeros sistemas de plantillas de los años 50 hasta las sofisticadas arquitecturas de aprendizaje profundo actuales. Motores clásicos como Tesseract marcaron un punto de inflexión en la adopción práctica de OCR en entornos reales (Smith, 2007).

Contexto del problema

Definición y Evolución Histórica del OCR

El Reconocimiento Óptico de Caracteres (OCR) es el proceso de conversión de imágenes de texto manuscrito, mecanografiado o impreso en texto codificado digitalmente. Esta tecnología permite la digitalización masiva de documentos, facilitando su búsqueda, edición y almacenamiento electrónico. La tecnología OCR ha evolucionado significativamente desde sus orígenes en la década de 1950, atravesando cuatro generaciones claramente diferenciadas:

@@ -4657,11 +4657,11 @@ _Toc14106979">Etapa 2: Reconocimiento de Texto (Text Recognition)

Una vez detectadas las regiones de texto, la etapa de reconocimiento transcribe el contenido visual a texto digital. Las arquitecturas predominantes son:

CRNN (Convolutional Recurrent Neural Network): Propuesta por Shi et al. (2016), CRNN combina una CNN para extracción de características visuales con una RNN bidireccional (típicamente LSTM) para modelado de secuencias, entrenada con pérdida CTC. Esta arquitectura estableció el paradigma encoder-decoder que domina el campo.

+

En reconocimiento de texto en escenas, los modelos basados en secuencias convolucionales han mostrado mejoras relevantes en precisión y velocidad (He et al., 2016).

La arquitectura CRNN consta de tres componentes:

1.   Capas convolucionales: Extraen características visuales de la imagen de entrada

2.   Capas recurrentes: Modelan las dependencias secuenciales entre características

-

3.   Capa de transcripción: Convierte las predicciones de la RNN en secuencias de caracteres mediante CTC

-

SVTR (Scene-Text Visual Transformer Recognition): Desarrollado por Du et al. (2022), SVTR aplica la arquitectura Transformer al reconocimiento de texto, utilizando parches de imagen como tokens de entrada. Esta aproximación elimina la necesidad de RNN y permite capturar dependencias globales de manera más eficiente.

+

3.   Capa de transcripción: Convierte las predicciones de la RNN en secuencias de caracteres mediante CTC
SVTR (Scene-Text Visual Transformer Recognition): Desarrollado por Du et al. (2022), SVTR aplica la arquitectura Transformer al reconocimiento de texto, utilizando parches de imagen como tokens de entrada. Esta aproximación elimina la necesidad de RNN y permite capturar dependencias globales de manera más eficiente.

Arquitecturas con Atención: Los modelos encoder-decoder con mecanismos de atención (Bahdanau et al., 2015) permiten al decodificador "enfocarse" en diferentes partes de la imagen mientras genera cada carácter. Esto es especialmente útil para texto largo o con layouts complejos.

TrOCR (Transformer-based OCR): Propuesto por Li et al. (2023), TrOCR utiliza un Vision Transformer (ViT) como encoder y un Transformer de lenguaje como decoder, logrando resultados estado del arte en múltiples benchmarks.

Tabla 5. Comparativa de arquitecturas de reconocimiento de texto.

@@ -4691,6 +4691,7 @@ _Toc14106979">Precisión y Recall a nivel de palabra: Útiles cuando se evalúa la capacidad del sistema para detectar palabras específicas.

Bag-of-Words Accuracy: Mide la proporción de palabras correctamente reconocidas independientemente de su orden.

BLEU Score: Adaptado de traducción automática, mide la similitud entre el texto predicho y la referencia considerando n-gramas.

+

Métricas derivadas de WER: Variantes como MER y WIL complementan la evaluación de reconocimiento de secuencias (Morris et al., 2004).

Particularidades del OCR para el Idioma Español

El español, como lengua romance, presenta características específicas que impactan el rendimiento de los sistemas OCR:

Características Ortográficas

@@ -4714,7 +4715,7 @@ _Toc14106979">Soluciones OCR de Código Abierto

En los últimos años han surgido varias soluciones OCR de código abierto que democratizan el acceso a esta tecnología. A continuación se analizan en detalle las tres principales alternativas evaluadas en este trabajo.

EasyOCR

-

EasyOCR es una biblioteca de OCR desarrollada por Jaided AI (2020) con el objetivo de proporcionar una solución de fácil uso que soporte múltiples idiomas. Actualmente soporta más de 80 idiomas, incluyendo español.

+

EasyOCR es una biblioteca de OCR desarrollada por JaidedAI (2020) con el objetivo de proporcionar una solución de fácil uso que soporte múltiples idiomas. Actualmente soporta más de 80 idiomas, incluyendo español.

Arquitectura técnica:

·     Detector: CRAFT (Character Region Awareness for Text Detection)

·     Reconocedor: CRNN con backbone ResNet/VGG + BiLSTM + CTC

@@ -4731,15 +4732,12 @@ _Toc14106979">·     Documentación menos exhaustiva

Caso de uso ideal: Prototipado rápido, aplicaciones con restricciones de memoria, proyectos que requieren soporte multilingüe inmediato.

PaddleOCR

-

PaddleOCR es el sistema OCR desarrollado por Baidu como parte del ecosistema PaddlePaddle (2024). Representa una de las soluciones más completas y activamente mantenidas en el ecosistema de código abierto. La versión PP-OCRv5, utilizada en este trabajo, incorpora los últimos avances en el campo.

+

PaddleOCR es el sistema OCR desarrollado por Baidu como parte del ecosistema PaddlePaddle (2024). Representa una de las soluciones más completas y activamente mantenidas en el ecosistema de código abierto. Su evolución incluye PP-OCR (Du et al., 2020) y PP-OCRv4 (Du et al., 2023); la versión PP-OCRv5, utilizada en este trabajo, incorpora avances recientes en precisión y eficiencia.

Arquitectura técnica:

El pipeline de PaddleOCR consta de tres módulos principales:

-

1.   Detector de texto (DB - Differentiable Binarization):

-

- Backbone: ResNet18/ResNet50 - Neck: FPN (Feature Pyramid Network) - Head: Segmentación con binarización diferenciable - Salida: Polígonos que encierran regiones de texto

-

1.   Clasificador de orientación:

-

- Determina si el texto está rotado 0° o 180° - Permite corrección automática de texto invertido - Opcional pero recomendado para documentos escaneados

-

1.   Reconocedor de texto (SVTR):

-

- Encoder: Vision Transformer modificado - Decoder: CTC o Attention-based - Vocabulario: Configurable por idioma

+

1.   Detector de texto (DB - Differentiable Binarization):
Backbone: ResNet18/ResNet50
Neck: FPN (Feature Pyramid Network)
Head: Segmentación con binarización diferenciable
Salida: Polígonos que encierran regiones de texto

+

2.   Clasificador de orientación:
Determina si el texto está rotado 0° o 180°
Permite corrección automática de texto invertido
Opcional pero recomendado para documentos escaneados

+

3.   Reconocedor de texto (SVTR):
Encoder: Vision Transformer modificado
Decoder: CTC o Attention-based
Vocabulario: Configurable por idioma

Hiperparámetros configurables:

PaddleOCR expone numerosos hiperparámetros que permiten ajustar el comportamiento del sistema. Los más relevantes para este trabajo son:

Tabla 6. Hiperparámetros de detección de PaddleOCR.

@@ -4820,18 +4818,14 @@ El método más simple consiste en evaluar todas las combinaciones posibles de v

·     No aprovecha información de evaluaciones previas

Random Search (Búsqueda aleatoria):

Propuesto por Bergstra & Bengio (2012), Random Search muestrea configuraciones aleatoriamente del espacio de búsqueda. Sorprendentemente, supera a Grid Search en muchos escenarios prácticos.

-

La intuición es que, cuando solo algunos hiperparámetros son importantes, Random Search explora más valores de estos parámetros críticos mientras Grid Search desperdicia evaluaciones variando parámetros irrelevantes.

+

La intuición es que, cuando solo algunos hiperparámetros son importantes, Random Search explora más valores de estos parámetros críticos mientras Grid Search desperdicia evaluaciones variando parámetros irrelevantes. En muchos escenarios, la búsqueda aleatoria ofrece un baseline competitivo (Bergstra & Bengio, 2012).

Optimización Bayesiana:

La optimización bayesiana modela la función objetivo mediante un modelo probabilístico sustituto (surrogate model) y utiliza una función de adquisición para decidir qué configuración evaluar a continuación (Bergstra et al., 2011).

El proceso iterativo es:

1.   Ajustar el modelo sustituto a las observaciones actuales

2.   Optimizar la función de adquisición para seleccionar el siguiente punto

3.   Evaluar la función objetivo en el punto seleccionado

-

4.   Actualizar las observaciones y repetir

-

Los modelos sustitutos más comunes son:

-

·     Procesos Gaussianos (GP): Proporcionan incertidumbre bien calibrada pero escalan pobremente

-

·     Random Forests: Manejan bien espacios de alta dimensión y variables categóricas

-

·     Tree-structured Parzen Estimator (TPE): Modela densidades en lugar de la función objetivo

+

4.   Actualizar las observaciones y repetir
Los modelos sustitutos más comunes son:
Procesos Gaussianos (GP): Proporcionan incertidumbre bien calibrada pero escalan pobremente
Random Forests: Manejan bien espacios de alta dimensión y variables categóricas
Tree-structured Parzen Estimator (TPE): Modela densidades en lugar de la función objetivo

Tree-structured Parzen Estimator (TPE)

TPE, propuesto por Bergstra et al. (2011) e implementado en Optuna, es particularmente efectivo para HPO. En lugar de modelar p(y|λ) directamente, TPE modela: @@ -4851,7 +4845,7 @@ Configuraciones con alta probabilidad bajo ·     No requiere derivadas de la función objetivo

·     Implementación eficiente en Optuna

Ray Tune

-

Ray Tune (Liaw et al., 2018) es un framework de optimización de hiperparámetros escalable construido sobre Ray, un sistema de computación distribuida. Sus características principales incluyen:

+

Ray Tune (Liaw et al., 2018) es un framework de optimización de hiperparámetros escalable construido sobre Ray, un sistema de computación distribuida (Moritz et al., 2018). Sus características principales incluyen:

Escalabilidad:

·     Ejecución paralela de múltiples trials

·     Distribución automática en clusters

@@ -4915,16 +4909,16 @@ Configuraciones con alta probabilidad bajo Fuente: Elaboración propia.

 

La optimización de hiperparámetros para documentos académicos en español representa una contribución original de este trabajo, abordando un nicho no explorado en la literatura.

-

En síntesis, la revisión del estado del arte revela un panorama en el que las herramientas técnicas están maduras, pero su aplicación óptima para dominios específicos permanece poco explorada. Los sistemas OCR modernos, como PaddleOCR, EasyOCR y DocTR, ofrecen arquitecturas sofisticadas basadas en aprendizaje profundo que alcanzan resultados impresionantes en benchmarks estándar. Sin embargo, estos resultados no siempre se trasladan a documentos del mundo real, especialmente en idiomas con menos recursos como el español.

+

Conclusiones

+

La revisión del estado del arte revela un panorama en el que las herramientas técnicas están maduras, pero su aplicación óptima para dominios específicos permanece poco explorada. Los sistemas OCR modernos, como PaddleOCR, EasyOCR y DocTR, ofrecen arquitecturas sofisticadas basadas en aprendizaje profundo que alcanzan resultados impresionantes en benchmarks estándar. Sin embargo, estos resultados no siempre se trasladan a documentos del mundo real, especialmente en idiomas con menos recursos como el español.

La evolución desde los sistemas de plantillas de los años 50 hasta los Transformers actuales ha sido espectacular, pero ha generado sistemas con decenas de hiperparámetros configurables cuyos valores por defecto representan compromisos generales, no configuraciones óptimas para dominios específicos. La literatura abunda en trabajos sobre entrenamiento y fine-tuning de modelos OCR, pero dedica poca atención a la optimización sistemática de los parámetros de inferencia, como umbrales de detección, opciones de preprocesamiento y filtros de confianza, que pueden marcar la diferencia entre un sistema usable y uno que requiere corrección manual extensiva.

Este vacío, combinado con las particularidades del español (acentos, eñes, signos invertidos) y la escasez de recursos específicos para este idioma, define el espacio de contribución del presente trabajo. Frameworks como Ray Tune y Optuna proporcionan las herramientas para abordar esta optimización de manera sistemática; PaddleOCR, con su pipeline altamente configurable, ofrece el sustrato técnico adecuado. El siguiente capítulo traduce esta oportunidad en objetivos concretos y una metodología experimental rigurosa.

3.   Objetivos -concretos y metodología de trabajo

La motivación presentada en el capítulo anterior se traduce ahora en objetivos concretos y medibles. Siguiendo la metodología SMART propuesta por Doran (1981), se define un objetivo general que guía el trabajo y cinco objetivos específicos que lo descomponen en metas alcanzables. La segunda parte del capítulo describe la metodología experimental diseñada para alcanzar estos objetivos.

-

Objetivo general

+concretos y metodología de trabajo

Objetivo general

Optimizar el rendimiento de PaddleOCR para documentos académicos en español mediante ajuste de hiperparámetros, alcanzando un CER inferior al 2% sin requerir fine-tuning del modelo.

-

Justificación SMART del Objetivo General

+

Justificación SMART del Objetivo General (Doran, 1981)

Tabla 13. Justificación SMART del objetivo general.

Criterio

Cumplimiento

Específico (S)

Se define claramente qué se quiere lograr: optimizar PaddleOCR mediante ajuste de hiperparámetros para documentos en español

Medible (M)

Se establece una métrica cuantificable: CER < 2%

Alcanzable (A)

Es viable dado que: (1) PaddleOCR permite configuración de hiperparámetros, (2) Ray Tune posibilita búsqueda automatizada, (3) Aceleración GPU disponible para experimentación eficiente

Relevante (R)

El impacto es demostrable: mejora la extracción de texto en documentos académicos sin costes adicionales de infraestructura

Temporal (T)

El plazo es un cuatrimestre, correspondiente al TFM

Fuente: docs/metrics/metrics.md.

@@ -4948,7 +4942,7 @@ concretos y metodología de trabajo
Fuente: Elaboración propia.

 

Descripción de las fases:

-

·     Fase 1 - Preparación del Dataset: Conversión PDF a imágenes (300 DPI), extracción de ground truth con PyMuPDF

+

·     Fase 1 - Preparación del Dataset: Conversión PDF a imágenes (300 DPI), extracción de ground truth con PyMuPDF (PyMuPDF, 2024)

·     Fase 2 - Benchmark Comparativo: Evaluación de EasyOCR, PaddleOCR, DocTR con métricas CER/WER

·     Fase 3 - Espacio de Búsqueda: Identificación de hiperparámetros y configuración de Ray Tune + Optuna

·     Fase 4 - Optimización: Ejecución de 64 trials con paralelización (2 concurrentes)

@@ -4958,10 +4952,8 @@ concretos y metodología de trabajo
Se utilizaron documentos PDF académicos de UNIR (Universidad Internacional de La Rioja), específicamente las instrucciones para la elaboración del TFE del Máster en Inteligencia Artificial.

Proceso de Conversión

El script prepare_dataset.ipynb implementa:

-

1.   Conversión PDF a imágenes:

-

- Biblioteca: PyMuPDF (fitz) - Resolución: 300 DPI - Formato de salida: PNG

-

1.   Extracción de texto de referencia:

-

- Método: page.get_text("dict") de PyMuPDF - Preservación de estructura de líneas - Tratamiento de texto vertical/marginal - Normalización de espacios y saltos de línea

+

1.   Conversión PDF a imágenes:
Biblioteca: PyMuPDF (fitz)
Resolución: 300 DPI
Formato de salida: PNG

+

2.   Extracción de texto de referencia:
Método: page.get_text("dict") de PyMuPDF
Preservación de estructura de líneas
Tratamiento de texto vertical/marginal
Normalización de espacios y saltos de línea

Estructura del Dataset

Figura 4. Estructura del dataset de evaluación

Estructura del dataset de evaluación

@@ -4991,7 +4983,7 @@ concretos y metodología de trabajo
Fuente: Elaboración propia.

 

Configuración de Ray Tune

-

El espacio de búsqueda se definió utilizando tune.choice() para parámetros booleanos y tune.uniform() para parámetros continuos, con OptunaSearch como algoritmo de optimización configurado para minimizar CER en 64 trials. La implementación completa está disponible en src/raytune/raytune_ocr.py (ver Anexo A).

+

El espacio de búsqueda se definió utilizando tune.choice() para parámetros booleanos y tune.uniform() para parámetros continuos, con OptunaSearch como algoritmo de optimización configurado para minimizar CER en 64 trials. La implementación completa está disponible en src/raytune/raytune_ocr.py (ver Anexo A). Ray Tune se apoya en el ecosistema Ray para escalar la búsqueda (Moritz et al., 2018).

Fase 4: Ejecución de Optimización

Arquitectura de Ejecución

Se implementó una arquitectura basada en contenedores Docker para aislar los servicios OCR y facilitar la reproducibilidad (ver sección 4.2.3 para detalles de la arquitectura).

@@ -5052,11 +5044,10 @@ docker compose -f docker-compose.tuning.doctr.yml down

2.   Sin límites de tiempo: AWS y Colab imponen timeouts de sesión que interrumpen experimentos largos

3.   Acceso instantáneo: Sin tiempo de aprovisionamiento de instancias cloud

4.   Almacenamiento local: Dataset y resultados en disco sin costos de transferencia

-

5.   Iteración rápida: Reinicio inmediato de contenedores Docker para depuración

-

Para un proyecto de investigación con múltiples iteraciones de ajuste de hiperparámetros, la ejecución local reduce costos frente a servicios cloud. Este análisis se detalla en docs/metrics/metrics.md.)

+

5.   Iteración rápida: Reinicio inmediato de contenedores Docker para depuración
Para un proyecto de investigación con múltiples iteraciones de ajuste de hiperparámetros, la ejecución local reduce costos frente a servicios cloud. Este análisis se detalla en docs/metrics/metrics.md.)

Limitaciones Metodológicas

1.   Tamaño del dataset: El dataset contiene 45 páginas de documentos académicos UNIR. Resultados pueden no generalizar a otros formatos.

-

2.   Subconjunto de optimización: El ajuste de hiperparámetros se realizó sobre 5 páginas (páginas 5-10), lo que contribuyó al sobreajuste observado en la validación del dataset completo.

+

2.   Subconjunto de optimización: El ajuste de hiperparámetros se realizó sobre 5 páginas (páginas 5-10), y su impacto se analiza en detalle en el capítulo de desarrollo específico.

3.   Texto de referencia imperfecto: El texto de referencia extraído de PDF puede contener errores en documentos con diseños complejos.

4.   Parámetro fijo: text_det_unclip_ratio quedó fijado en 0.0 durante todo el experimento por decisión de diseño inicial.

Síntesis del capítulo

@@ -5083,16 +5074,14 @@ color:#0098CD;mso-font-kerning:16.0pt;mso-bidi-font-weight:bold'>arrollo específico de la contribución

El presente capítulo constituye el núcleo técnico de este trabajo fin de máster. Siguiendo la estructura de "Comparativa de soluciones" establecida por las instrucciones de UNIR, se desarrollan tres fases interrelacionadas. Estas fases son tres: planteamiento y ejecución del benchmark comparativo, optimización de hiperparámetros mediante Ray Tune, y análisis e interpretación de los resultados.

-

Planteamiento de la comparativa

-

Introducción

+color:#0098CD;mso-font-kerning:16.0pt;mso-bidi-font-weight:bold'>

Planteamiento de la comparativa

Antes de abordar la optimización de hiperparámetros, era necesario seleccionar el motor OCR que serviría como base para la experimentación. Para ello, se realizó un estudio comparativo entre tres soluciones de código abierto representativas del estado del arte: EasyOCR, PaddleOCR y DocTR. Los experimentos, documentados en los informes de métricas y en los CSV de resultados del repositorio, permitieron identificar el modelo más prometedor para la fase de optimización posterior.

Identificación del Problema

El reconocimiento óptico de caracteres en documentos académicos en español presenta desafíos específicos que la literatura no ha abordado en profundidad. A diferencia de los benchmarks estándar en inglés, los documentos académicos hispanohablantes combinan características ortográficas propias, como acentos, eñes, diéresis y signos de puntuación invertidos, con una estructura sencilla basada en índice y encabezados.

Los documentos académicos típicos incluyen texto corrido con índice, listas numeradas, encabezados multinivel y notas al pie, lo que complica la tarea de ordenación del texto reconocido. A esto se suma el uso de tipografía profesional con múltiples fuentes, tamaños y estilos (negrita, cursiva), que puede confundir a los modelos de reconocimiento. Aunque los PDFs digitales suelen tener alta calidad, pueden contener artefactos de compresión que degradan la legibilidad de caracteres pequeños o de bajo contraste.

Alternativas Evaluadas

Se seleccionaron tres soluciones OCR de código abierto representativas del estado del arte:

-

Tabla 20. Soluciones OCR evaluadas en el benchmark comparativo.

+

Tabla 20. Soluciones OCR del benchmark.

Solución

Desarrollador

Versión

Justificación de selección

EasyOCR

Jaided AI

Última estable

Popularidad, facilidad de uso

PaddleOCR

Baidu

PP-OCRv5

Estado del arte industrial

DocTR

Mindee

Última estable

Orientación académica

Fuente: docs/metrics/metrics.md.

 

@@ -5190,12 +5179,10 @@ color:#0098CD;mso-font-kerning:16.0pt;mso-bidi-font-weight:bold'>Limitaciones del Benchmark

1.   Tamaño reducido: Solo 5 páginas evaluadas en el benchmark comparativo inicial. Esto limita la generalización de las conclusiones.

2.   Único tipo de documento: Documentos académicos de UNIR únicamente. Otros tipos de documentos (facturas, formularios, contratos) podrían presentar resultados diferentes.

-

3.   Ground truth automático: El texto de referencia se extrajo programáticamente del PDF, lo cual puede introducir errores en el orden de lectura cuando hay secciones con encabezados y saltos de línea.

-

4.   Referencia CPU separada: Los tiempos en CPU se midieron en un experimento independiente y solo se usan como comparación de rendimiento frente a GPU.

+

3.   Referencia CPU separada: Los tiempos en CPU se midieron en un experimento independiente y solo se usan como comparación de rendimiento frente a GPU.

Síntesis del Benchmark

-

El benchmark comparativo ha permitido identificar PaddleOCR como la solución más prometedora para la fase de optimización, gracias a su combinación de rendimiento base competitivo, alta configurabilidad del pipeline y documentación técnica completa. Sin embargo, el análisis también reveló limitaciones importantes: el tamaño reducido del benchmark (5 páginas), la restricción a un único tipo de documento, y la extracción automática del ground truth que puede introducir errores en el orden de lectura cuando hay secciones con encabezados y saltos de línea. Estas limitaciones se tendrán en cuenta al interpretar los resultados de la fase de optimización.

+

El benchmark comparativo ha permitido identificar PaddleOCR como la solución más prometedora para la fase de optimización, gracias a su combinación de rendimiento base competitivo, alta configurabilidad del pipeline y documentación técnica completa. Estas limitaciones se tendrán en cuenta al interpretar los resultados de la fase de optimización.

Desarrollo de la comparativa: Optimización de hiperparámetros

-

Introducción

Una vez seleccionado PaddleOCR como motor base, el siguiente paso fue explorar sistemáticamente su espacio de configuración para identificar los hiperparámetros que maximizan el rendimiento en documentos académicos en español. Para ello se empleó Ray Tune con el algoritmo de búsqueda Optuna, una combinación que permite explorar eficientemente espacios de búsqueda mixtos (parámetros continuos y categóricos). Los experimentos se implementaron en src/run_tuning.py con apoyo de la librería src/raytune_ocr.py, almacenándose los resultados en src/results. Esta aproximación ofrece ventajas significativas frente al fine-tuning tradicional: no requiere datasets de entrenamiento etiquetados, no modifica los pesos del modelo preentrenado, y puede ejecutarse con hardware de consumo cuando se dispone de aceleración GPU.

Configuración del Experimento

Entorno de Ejecución

@@ -5266,7 +5253,9 @@ docker compose -f docker-compose.tuning.doctr.yml down

Archivo

Propósito

Servicios

src/docker-compose.tuning.yml

Optimización principal

RayTune + PaddleOCR + DocTR

src/docker-compose.tuning.easyocr.yml

Optimización EasyOCR

RayTune + EasyOCR

src/docker-compose.tuning.paddle.yml

Optimización PaddleOCR

RayTune + PaddleOCR

src/docker-compose.tuning.doctr.yml

Optimización DocTR

RayTune + DocTR

Fuente: src/docker-compose.tuning.yml, src/docker-compose.tuning.easyocr.yml, src/docker-compose.tuning.paddle.yml, src/docker-compose.tuning.doctr.yml.

 

-

Nota: EasyOCR y PaddleOCR utilizan el mismo puerto (8002). Debido a limitaciones de recursos GPU (VRAM insuficiente para ejecutar múltiples modelos OCR simultáneamente), solo se ejecuta un servicio a la vez durante los experimentos. Por esta razón, EasyOCR tiene su propio archivo Docker Compose separado.

+
+

Nota: EasyOCR y PaddleOCR utilizan el mismo puerto (8002). Debido a limitaciones de recursos GPU (VRAM insuficiente para ejecutar múltiples modelos OCR simultáneamente), solo se ejecuta un servicio a la vez durante los experimentos. Por esta razón, EasyOCR tiene su propio archivo Docker Compose separado.

+

Gestión de Volúmenes

Se utilizan volúmenes Docker nombrados para persistir los modelos descargados entre ejecuciones:

Tabla 28. Volúmenes Docker para caché de modelos.

@@ -5329,7 +5318,7 @@ docker compose -f docker-compose.tuning.paddle.yml down

La clase ImageTextDataset gestiona la carga de pares imagen-texto desde la estructura de carpetas pareadas. La implementación está disponible en src/paddle_ocr/dataset_manager.py, src/easyocr_service/dataset_manager.py y src/doctr_service/dataset_manager.py.

Espacio de Búsqueda

El espacio de búsqueda se definió considerando los hiperparámetros más relevantes identificados en la documentación de PaddleOCR, utilizando tune.choice() para parámetros booleanos y tune.uniform() para umbrales continuos. La implementación está disponible en src/raytune/raytune_ocr.py (ver Anexo A).

-

Tabla 30. Descripción detallada del espacio de búsqueda.

+

Tabla 30. Espacio de búsqueda: parámetros.

Parámetro

Tipo

Rango

Descripción

use_doc_orientation_classify

Booleano

{True, False}

Clasificación de orientación del documento completo

use_doc_unwarping

Booleano

{True, False}

Corrección de deformación/curvatura

textline_orientation

Booleano

{True, False}

Clasificación de orientación por línea de texto

text_det_thresh

Continuo

[0.0, 0.7]

Umbral de probabilidad para píxeles de texto

text_det_box_thresh

Continuo

[0.0, 0.7]

Umbral de confianza para cajas detectadas

text_det_unclip_ratio

Fijo

0.0

Coeficiente de expansión (no explorado)

text_rec_score_thresh

Continuo

[0.0, 0.7]

Umbral de confianza de reconocimiento

Fuente: Documentación de PaddleOCR.

 

@@ -5392,7 +5381,7 @@ Configuración óptima:

Fuente: src/results/raytune_paddle_results_20260119_122609.csv.

 

Análisis de Correlación

-

Se calculó la correlación de Pearson entre los parámetros de configuración (codificados como 0/1 en el caso de booleanos) y las métricas de error:

+

Se calculó la correlación de Pearson entre los parámetros de configuración (codificados como 0/1 en el caso de booleanos) y las métricas de error (Pearson, 1895). Para interpretar la magnitud de las correlaciones se siguieron criterios habituales en investigación cuantitativa (Cohen, 1988):

Tabla 36. Correlación de parámetros con CER.

Parámetro

Correlación con CER

Interpretación

use_doc_unwarping

+0.879

Correlación alta positiva

use_doc_orientation_classify

-0.712

Correlación alta negativa

textline_orientation

-0.535

Correlación moderada negativa

text_det_thresh

+0.428

Correlación moderada positiva

text_det_box_thresh

+0.311

Correlación moderada positiva

text_rec_score_thresh

-0.268

Correlación moderada negativa

text_det_unclip_ratio

NaN

Varianza cero (valor fijo)

Fuente: src/results/correlations/paddle_correlations.csv.

@@ -5437,14 +5426,14 @@ Configuración óptima:

Modelo

CER

Precisión Caracteres

WER

Precisión Palabras

PaddleOCR (Baseline)

8.85%

91.15%

13.05%

86.95%

PaddleOCR-HyperAdjust

7.72%

92.28%

11.40%

88.60%

Fuente: docs/metrics/metrics_paddle.md.

 

-

Nota sobre generalización: El mejor trial individual (5 páginas) alcanzó un CER de 0.79%, cumpliendo el objetivo de CER < 2%. Sin embargo, al aplicar la configuración al dataset completo de 45 páginas, el CER aumentó a 7.72%, evidenciando sobreajuste al subconjunto de entrenamiento. Esta diferencia es un hallazgo importante que se discute en la sección de análisis.

+

Nota sobre generalización: El contraste entre el mejor trial y la validación en el dataset completo evidencia sobreajuste al subconjunto de entrenamiento. Esta diferencia se analiza en la sección de resultados consolidados.

Métricas de Mejora

Tabla 41. Análisis cuantitativo de la mejora.

Forma de Medición

CER

WER

Valor baseline

8.85%

13.05%

Valor optimizado

7.72%

11.40%

Mejora absoluta

-1.13 pp

-1.65 pp

Reducción relativa del error

12.8%

12.6%

Factor de mejora

1.15x

1.14x

Mejor trial (5 páginas)

0.79%

7.78%

Fuente: docs/metrics/metrics_paddle.md.

 

-

Figura 12. Reducción de errores: Baseline vs Optimizado (45 páginas)

-

Reducción de errores: Baseline vs Optimizado (45 páginas)

+

Figura 12. Reducción de errores (baseline vs optimizado)

+

Reducción de errores (baseline vs optimizado)

Fuente: docs/metrics/metrics_paddle.md.

Leyenda: CER = Character Error Rate, WER = Word Error Rate. Baseline = configuración por defecto de PaddleOCR. Optimizado = configuración encontrada por Ray Tune. Los valores corresponden al dataset completo de 45 páginas.

 

@@ -5461,9 +5450,8 @@ Configuración óptima:

3.   En comparación, la ejecución en CPU requiere ~69 segundos/página (82x más lento), lo que justifica el uso de GPU para optimización y producción.

Síntesis de la Optimización

Los 64 trials ejecutados con Ray Tune y aceleración GPU revelaron patrones claros en el comportamiento de PaddleOCR. El hallazgo más significativo es que los parámetros estructurales, textline_orientation y use_doc_orientation_classify, tienen mayor impacto que los umbrales numéricos. Al activarlos se reduce el CER medio de 4.73% a 1.74%. En cuanto a umbrales, valores bajos de text_det_thresh (aprox. 0.05) benefician el rendimiento, mientras que use_doc_unwarping resulta innecesario para PDFs digitales.

-

El mejor trial alcanzó un CER de 0.79%, cumpliendo el objetivo de CER < 2%. No obstante, la validación sobre el dataset completo de 45 páginas arrojó un CER de 7.72%, evidenciando sobreajuste al subconjunto de optimización de 5 páginas. Aun así, esto representa una mejora del 12.8% respecto al baseline (8.85%), demostrando el valor de la optimización sistemática incluso cuando la generalización es imperfecta.

+

La optimización logró mejoras claras frente al baseline en el dataset completo, aunque la generalización quedó limitada por el tamaño del subconjunto de ajuste.

Discusión y análisis de resultados

-

Introducción

Los resultados obtenidos en las secciones anteriores requieren un análisis que trascienda los números individuales para comprender su significado práctico. En esta sección se consolidan los hallazgos del benchmark comparativo y la optimización de hiperparámetros, evaluando hasta qué punto se han cumplido los objetivos planteados y qué limitaciones condicionan la generalización de las conclusiones.

Resumen Consolidado de Resultados

Progresión del Rendimiento

@@ -5501,8 +5489,7 @@ Configuración óptima:

El clasificador de orientación de línea resuelve un problema fundamental en documentos con secciones y cambios de formato: determinar el orden correcto de lectura. Sin este clasificador:

1.   Las líneas del índice pueden mezclarse con el cuerpo del texto

2.   Los encabezados pueden insertarse en posiciones incorrectas

-

3.   Las listas numeradas pueden leerse en orden incorrecto

-

Para documentos académicos que típicamente incluyen índice, listas y encabezados multinivel, este clasificador es esencial.

+

3.   Las listas numeradas pueden leerse en orden incorrecto
Para documentos académicos que típicamente incluyen índice, listas y encabezados multinivel, este clasificador es esencial.

Recomendación: Siempre activar textline_orientation=True para documentos estructurados.

Análisis del Parámetro text_det_thresh

Comportamiento observado:

@@ -5618,16 +5605,16 @@ Configuración óptima:

La validación con aceleración GPU demuestra que la configuración optimizada mediante Ray Tune mejora la precisión (CER: 8.85% → 7.72% en dataset completo, 0.79% en mejor trial individual) y, combinada con la aceleración de 82x proporcionada por GPU, resulta prácticamente aplicable en escenarios de producción real. Las conclusiones derivadas de esta validación se presentan en el Capítulo 5.

5.   Conclusiones -y trabajo futuro

A lo largo de este trabajo se ha explorado la optimización de hiperparámetros como estrategia para mejorar el rendimiento de sistemas OCR sin necesidad de reentrenamiento. Las siguientes secciones evalúan el grado de cumplimiento de los objetivos planteados, sintetizan los hallazgos más relevantes y proponen direcciones para investigación futura.

-

Conclusiones

-

Conclusiones Generales

+y trabajo futuro

Conclusiones

Los resultados obtenidos confirman que la optimización sistemática de hiperparámetros constituye una alternativa viable al fine-tuning para mejorar sistemas OCR preentrenados. La infraestructura dockerizada con aceleración GPU desarrollada en este trabajo no solo facilita la experimentación reproducible, sino que reduce drásticamente los tiempos de ejecución, haciendo viable la exploración exhaustiva de espacios de configuración.

El objetivo principal del trabajo era alcanzar un CER inferior al 2% en documentos académicos en español. Los resultados obtenidos se resumen a continuación:

Tabla 53. Cumplimiento del objetivo de CER.

Métrica

Objetivo

Mejor Trial

Dataset Completo

Cumplimiento

CER

< 2%

0.79%

7.72%

✓ Parcial

Fuente: docs/metrics/metrics_paddle.md.

 

-

Nota: El objetivo de CER < 2% se cumple en el mejor trial individual (0.79%, 5 páginas). La validación sobre el conjunto de datos completo (45 páginas) muestra un CER de 7.72%, evidenciando sobreajuste al subconjunto de optimización. Esta diferencia se analiza en detalle en el Capítulo 4.

+
+

Nota: El objetivo de CER < 2% se cumple en el mejor trial individual (0.79%, 5 páginas). La validación sobre el conjunto de datos completo (45 páginas) muestra un CER de 7.72%, evidenciando sobreajuste al subconjunto de optimización. Esta diferencia se analiza en detalle en el Capítulo 4.

+

Cumplimiento de los Objetivos Específicos

La evaluación comparativa de soluciones OCR (OE1) reveló diferencias significativas entre las tres alternativas analizadas. De las tres soluciones de código abierto evaluadas, EasyOCR, PaddleOCR (PP-OCRv5) y DocTR, PaddleOCR demostró el mejor rendimiento base para documentos en español. Además, su arquitectura modular y la amplia configurabilidad de su pipeline lo convierten en el candidato idóneo para optimización mediante ajuste de hiperparámetros.

En cuanto a la preparación del conjunto de datos (OE2), se construyó un corpus estructurado con 45 páginas de documentos académicos de UNIR. La implementación de la clase ImageTextDataset permite cargar de forma eficiente pares imagen-texto. El texto de referencia se extrajo automáticamente del PDF original mediante PyMuPDF, garantizando así la consistencia entre las imágenes y sus transcripciones esperadas.

@@ -5743,7 +5730,9 @@ docker compose -f docker-compose.cpu-registry.yml up -d docker compose up -d

EasyOCR (Puerto 8002)

-

Nota: EasyOCR utiliza el mismo puerto (8002) que PaddleOCR. No se pueden ejecutar simultáneamente. Por esta razón, existe un archivo docker-compose separado para EasyOCR.

+
+

Nota: EasyOCR utiliza el mismo puerto (8002) que PaddleOCR. No se pueden ejecutar simultáneamente. Por esta razón, existe un archivo docker-compose separado para EasyOCR.

+

Imagen Docker: seryus.ddns.net/unir/easyocr-gpu

cd src/easyocr_service
@@ -5811,11 +5800,13 @@ analyze_results(results, prefix='raytune_paddle', config_keys=PADDLE_OCR_CONFIG_
 

Servicio

Puerto

Script de Ajuste

Nota

PaddleOCR

8002

paddle_ocr_payload

-

DocTR

8003

doctr_payload

-

EasyOCR

8002

easyocr_payload

Conflicto con PaddleOCR

Fuente: Elaboración propia.

 

-

Nota: Debido a limitaciones de recursos GPU (VRAM insuficiente para ejecutar múltiples modelos OCR simultáneamente), solo se ejecuta un servicio a la vez. PaddleOCR y EasyOCR comparten el puerto 8002. Para cambiar de servicio, detener el actual con docker compose down.

+
+

Nota: Debido a limitaciones de recursos GPU (VRAM insuficiente para ejecutar múltiples modelos OCR simultáneamente), solo se ejecuta un servicio a la vez. PaddleOCR y EasyOCR comparten el puerto 8002. Para cambiar de servicio, detener el actual con docker compose down.

+

A.7 Métricas de Rendimiento

Esta sección presenta los resultados completos de las evaluaciones comparativas y del ajuste de hiperparámetros realizado con Ray Tune sobre los tres servicios OCR evaluados.

Comparativa General de Servicios

-

Tabla A4. Comparativa de servicios OCR en dataset de 45 páginas (GPU RTX 3060).

+

Tabla A4. Servicios OCR en 45 páginas (RTX 3060).

Servicio

CER

WER

Tiempo/Página

Tiempo Total

VRAM

PaddleOCR (Mobile)

7.76%

11.62%

0.58s

32.0s

0.06 GB

EasyOCR

11.23%

36.36%

1.88s

88.5s

~2 GB

DocTR

12.06%

42.01%

0.50s

28.4s

~1 GB

Fuente: docs/metrics/metrics_paddle.md, docs/metrics/metrics_easyocr.md, docs/metrics/metrics_doctr.md.

 

@@ -5832,10 +5823,9 @@ analyze_results(results, prefix='raytune_paddle', config_keys=PADDLE_OCR_CONFIG_

Rango CER

Número de trials

Porcentaje

< 2%

43

67.2%

2% - 5%

10

15.6%

5% - 10%

11

17.2%

> 10%

0

0.0%

Fuente: src/results/raytune_paddle_results_20260119_122609.csv.

 

-

Figura A2. Distribución de trials por rango de CER (PaddleOCR)

-

Distribución de trials por rango de CER (PaddleOCR)

-

Fuente: src/results/raytune_paddle_results_20260119_122609.csv.

-

 

+
+

Nota: Ver Figura 15 en el Capítulo 4 para la representación gráfica de esta distribución.

+

Configuración Óptima PaddleOCR

La siguiente configuración logró el mejor rendimiento en el ajuste de hiperparámetros:

@@ -5859,10 +5849,9 @@ analyze_results(results, prefix='raytune_paddle', config_keys=PADDLE_OCR_CONFIG_

Métrica

CPU

GPU (RTX 3060)

Aceleración

Tiempo/Página

69.4s

0.84s

82x más rápido

45 páginas

~52 min

~38 seg

82x más rápido

Fuente: Datos de tiempo CPU de src/raytune_paddle_subproc_results_20251207_192320.csv y tiempos de GPU en trials de ajuste. Elaboración propia.

 

-

Figura A3. Tiempo de procesamiento: CPU vs GPU (segundos/página)

-

Tiempo de procesamiento: CPU vs GPU (segundos/página)

-

Fuente: src/raytune_paddle_subproc_results_20251207_192320.csv y src/results/raytune_paddle_results_20260119_122609.csv. Leyenda: Aceleración de 82x con GPU. El procesamiento de una página pasa de 69.4s (CPU) a 0.84s (GPU).

-

 

+
+

Nota: Ver Figura 20 en el Capítulo 4 para la representación gráfica de esta comparación.

+

Análisis de Errores por Servicio

Tabla A8. Tipos de errores identificados por servicio OCR.

Servicio

Fortalezas

Debilidades

¿Fine-tuning recomendado?

PaddleOCR

Preserva estructura, buen manejo de español

Errores menores de acentos

No (ya excelente)

DocTR

Más rápido

Pierde estructura, omite TODOS los diacríticos

Sí (para diacríticos)

EasyOCR

Modelo correcto para español

Caracteres espurios, confunde o/0

Sí (problemas del detector)