Files
MastersThesis/paddle_ocr_fine_tune_unir_raytune.ipynb

1320 lines
108 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"id": "be3c1872",
"metadata": {},
"source": [
"# AI-based OCR Benchmark Notebook\n",
"\n",
"This notebook benchmarks **AI-based OCR models** on scanned PDF documents/images in Spanish.\n",
"It excludes traditional OCR engines like Tesseract that require external installations."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6a1e98fe",
"metadata": {},
"outputs": [],
"source": [
"%pip install --upgrade pip\n",
"%pip install --upgrade jupyter\n",
"%pip install --upgrade ipywidgets\n",
"%pip install --upgrade ipykernel\n",
"\n",
"# Install necessary packages\n",
"%pip install transformers torch pdf2image pillow jiwer paddleocr hf_xet paddlepaddle\n",
"# pdf reading\n",
"%pip install PyMuPDF\n",
"\n",
"# Data analysis and visualization\n",
"%pip install pandas\n",
"%pip install matplotlib\n",
"%pip install seaborn"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "ae33632a",
"metadata": {},
"outputs": [],
"source": [
"# Imports\n",
"import os, json\n",
"import numpy as np\n",
"import pandas as pd\n",
"import matplotlib.pyplot as plt\n",
"from pdf2image import convert_from_path\n",
"from PIL import Image, ImageOps\n",
"import torch\n",
"from jiwer import wer, cer\n",
"from paddleocr import PaddleOCR\n",
"import fitz # PyMuPDF\n",
"import re\n",
"from datetime import datetime"
]
},
{
"cell_type": "markdown",
"id": "0e00f1b0",
"metadata": {},
"source": [
"## 1 Configuration"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"PDF_FOLDER = './instructions' # Folder containing PDF files\n",
"OUTPUT_FOLDER = 'results'\n",
"os.makedirs(OUTPUT_FOLDER, exist_ok=True)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "8bd4ca23",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"c:\\Users\\sji\\Desktop\\MastersThesis\\instructions\n",
"c:\\Users\\sji\\Desktop\\MastersThesis\\paddle_ocr_tuning.py\n",
"c:\\Users\\sji\\Desktop\\MastersThesis\n"
]
}
],
"source": [
"PDF_FOLDER_ABS = os.path.abspath(PDF_FOLDER) # ./instructions -> C:\\...\\instructions\n",
"SCRIPT_ABS = os.path.abspath(\"paddle_ocr_tuning.py\") # paddle_ocr_tuning.py -> C:\\...\\paddle_ocr_tuning.py\n",
"SCRIPT_DIR = os.path.dirname(SCRIPT_ABS)\n",
"\n",
"print(PDF_FOLDER_ABS)\n",
"print(SCRIPT_ABS)\n",
"print(SCRIPT_DIR)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "243849b9",
"metadata": {},
"outputs": [],
"source": [
"# 3. PaddleOCR \n",
"# https://www.paddleocr.ai/v3.0.0/en/version3.x/pipeline_usage/OCR.html?utm_source=chatgpt.com#21-command-line\n",
"from paddleocr import PaddleOCR\n",
"\n",
"# Initialize with better settings for Spanish/Latin text\n",
"# https://www.paddleocr.ai/main/en/version3.x/algorithm/PP-OCRv5/PP-OCRv5_multi_languages.html?utm_source=chatgpt.com#5-models-and-their-supported-languages\n",
"paddleocr_model = PaddleOCR(\n",
" text_detection_model_name=\"PP-OCRv5_server_det\",\n",
" text_recognition_model_name=\"PP-OCRv5_server_rec\"\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "329da34a",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">3.3</span>.<span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">1</span>\n",
"</pre>\n"
],
"text/plain": [
"\u001b[1;36m3.3\u001b[0m.\u001b[1;36m1\u001b[0m\n"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import paddleocr\n",
"\n",
"print(paddleocr.__version__)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "b1541bb6",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">c:\\Users\\sji\\Desktop\\MastersThesis\\.venv\\Lib\\site-packages\\paddleocr\n",
"</pre>\n"
],
"text/plain": [
"c:\\Users\\sji\\Desktop\\MastersThesis\\.venv\\Lib\\site-packages\\paddleocr\n"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# 1) Locate the installed PaddleOCR package\n",
"pkg_dir = os.path.dirname(paddleocr.__file__)\n",
"print(pkg_dir)"
]
},
{
"cell_type": "markdown",
"id": "84c999e2",
"metadata": {},
"source": [
"## 2 Helper Functions"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9596c7df",
"metadata": {},
"outputs": [],
"source": [
"from typing import List, Optional\n",
"from paddle_ocr_tuning import pdf_to_images, pdf_extract_text, evaluate_text, assemble_from_paddle_result\n",
"\n",
"def show_page(img: Image.Image, text: str, scale: float = 1):\n",
" \"\"\"\n",
" Displays a smaller version of the image with text as a footer.\n",
" \"\"\"\n",
" # Compute plot size based on image dimensions (but without resizing the image)\n",
" w, h = img.size\n",
" figsize = (w * scale / 100, h * scale / 100) # convert pixels to inches approx\n",
"\n",
" fig, ax = plt.subplots(figsize=figsize)\n",
" ax.imshow(img)\n",
" ax.axis(\"off\")\n",
"\n",
"\n",
" # Add OCR text below the image (footer)\n",
" # plt.figtext(0.5, 0.02, text.strip(), wrap=True, ha='center', va='bottom', fontsize=10)\n",
" plt.tight_layout()\n",
" plt.show()"
]
},
{
"cell_type": "markdown",
"id": "e42cae29",
"metadata": {},
"source": [
"## Run AI OCR Benchmark"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9b55c154",
"metadata": {},
"outputs": [],
"source": [
"results = []\n",
"\n",
"for pdf_file in os.listdir(PDF_FOLDER):\n",
" if not pdf_file.lower().endswith('.pdf'):\n",
" continue\n",
" pdf_path = os.path.join(PDF_FOLDER, pdf_file)\n",
" page_range = range(5, 10)\n",
" \n",
" images = pdf_to_images(pdf_path, 300, page_range)\n",
" \n",
" for i, img in enumerate(images):\n",
" # img = preprocess_for_ocr(img)\n",
" page_num = page_range[i]\n",
" ref = pdf_extract_text(pdf_path, page_num=page_num)\n",
" show_page(img, f\"page: {page_num}\", 0.15)\n",
" print(f\"ref: \\n{ref}\")\n",
" \n",
" # Convert PIL image to numpy array\n",
" image_array = np.array(img)\n",
" out = paddleocr_model.predict(\n",
" image_array,\n",
" use_doc_orientation_classify=False,\n",
" use_doc_unwarping=False,\n",
" use_textline_orientation=True\n",
" )\n",
" # PaddleOCR\n",
" paddle_text = assemble_from_paddle_result(out)\n",
" print(f\"paddle_text: \\n{paddle_text}\")\n",
" results.append({'PDF': pdf_file, 'Page': page_num, 'Model': 'PaddleOCR', 'Prediction': paddle_text, **evaluate_text(ref, paddle_text)})\n",
" "
]
},
{
"cell_type": "markdown",
"id": "0db6dc74",
"metadata": {},
"source": [
"## 5 Save and Analyze Results"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "da3155e3",
"metadata": {},
"outputs": [],
"source": [
"df_results = pd.DataFrame(results)\n",
"\n",
"# Generate a unique filename with timestamp\n",
"timestamp = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n",
"filename = f\"ai_ocr_benchmark_finetune_results_{timestamp}.csv\"\n",
"filepath = os.path.join(OUTPUT_FOLDER, filename)\n",
"\n",
"df_results.to_csv(filepath, index=False)\n",
"print(f\"Benchmark results saved as {filename}\")\n",
"\n",
"# Summary by model\n",
"summary = df_results.groupby('Model')[['WER', 'CER']].mean()\n",
"print(summary)\n",
"\n",
"# Plot\n",
"summary.plot(kind='bar', figsize=(8,5), title='AI OCR Benchmark (WER & CER)')\n",
"plt.ylabel('Error Rate')\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"id": "3e0f00c0",
"metadata": {},
"source": [
"### How to read this chart:\n",
"- CER (Character Error Rate) focus on raw transcription quality\n",
"- WER (Word Error Rate) penalizes incorrect tokenization or missing spaces\n",
"- CER and WER are error metrics, which means:\n",
" - Higher values = worse performance\n",
" - Lower values = better accuracy"
]
},
{
"cell_type": "markdown",
"id": "830b0e25",
"metadata": {},
"source": [
"# Busqueda de hyperparametros\n",
"https://docs.ray.io/en/latest/tune/index.html"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3a4bd700",
"metadata": {},
"outputs": [],
"source": [
"!python --version\n",
"!pip --version"
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "b0cf4bcf",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Looking in indexes: https://pypi.org/simple, https://pypi.ngc.nvidia.com\n",
"Collecting rich\n",
" Downloading rich-14.2.0-py3-none-any.whl.metadata (18 kB)\n",
"Requirement already satisfied: ray[tune] in c:\\users\\sji\\desktop\\mastersthesis\\.venv\\lib\\site-packages (2.51.1)\n",
"Requirement already satisfied: click!=8.3.0,>=7.0 in c:\\users\\sji\\desktop\\mastersthesis\\.venv\\lib\\site-packages (from ray[tune]) (8.2.1)\n",
"Requirement already satisfied: filelock in c:\\users\\sji\\desktop\\mastersthesis\\.venv\\lib\\site-packages (from ray[tune]) (3.20.0)\n",
"Requirement already satisfied: jsonschema in c:\\users\\sji\\desktop\\mastersthesis\\.venv\\lib\\site-packages (from ray[tune]) (4.25.1)\n",
"Requirement already satisfied: msgpack<2.0.0,>=1.0.0 in c:\\users\\sji\\desktop\\mastersthesis\\.venv\\lib\\site-packages (from ray[tune]) (1.1.2)\n",
"Requirement already satisfied: packaging in c:\\users\\sji\\desktop\\mastersthesis\\.venv\\lib\\site-packages (from ray[tune]) (25.0)\n",
"Requirement already satisfied: protobuf>=3.20.3 in c:\\users\\sji\\desktop\\mastersthesis\\.venv\\lib\\site-packages (from ray[tune]) (6.33.0)\n",
"Requirement already satisfied: pyyaml in c:\\users\\sji\\desktop\\mastersthesis\\.venv\\lib\\site-packages (from ray[tune]) (6.0.2)\n",
"Requirement already satisfied: requests in c:\\users\\sji\\desktop\\mastersthesis\\.venv\\lib\\site-packages (from ray[tune]) (2.32.5)\n",
"Requirement already satisfied: pandas in c:\\users\\sji\\desktop\\mastersthesis\\.venv\\lib\\site-packages (from ray[tune]) (2.3.3)\n",
"Requirement already satisfied: tensorboardX>=1.9 in c:\\users\\sji\\desktop\\mastersthesis\\.venv\\lib\\site-packages (from ray[tune]) (2.6.4)\n",
"Requirement already satisfied: pyarrow>=9.0.0 in c:\\users\\sji\\desktop\\mastersthesis\\.venv\\lib\\site-packages (from ray[tune]) (22.0.0)\n",
"Requirement already satisfied: fsspec in c:\\users\\sji\\desktop\\mastersthesis\\.venv\\lib\\site-packages (from ray[tune]) (2025.10.0)\n",
"Collecting markdown-it-py>=2.2.0 (from rich)\n",
" Downloading markdown_it_py-4.0.0-py3-none-any.whl.metadata (7.3 kB)\n",
"Requirement already satisfied: pygments<3.0.0,>=2.13.0 in c:\\users\\sji\\desktop\\mastersthesis\\.venv\\lib\\site-packages (from rich) (2.19.2)\n",
"Requirement already satisfied: colorama in c:\\users\\sji\\desktop\\mastersthesis\\.venv\\lib\\site-packages (from click!=8.3.0,>=7.0->ray[tune]) (0.4.6)\n",
"Collecting mdurl~=0.1 (from markdown-it-py>=2.2.0->rich)\n",
" Downloading mdurl-0.1.2-py3-none-any.whl.metadata (1.6 kB)\n",
"Requirement already satisfied: numpy in c:\\users\\sji\\desktop\\mastersthesis\\.venv\\lib\\site-packages (from tensorboardX>=1.9->ray[tune]) (2.3.4)\n",
"Requirement already satisfied: attrs>=22.2.0 in c:\\users\\sji\\desktop\\mastersthesis\\.venv\\lib\\site-packages (from jsonschema->ray[tune]) (25.4.0)\n",
"Requirement already satisfied: jsonschema-specifications>=2023.03.6 in c:\\users\\sji\\desktop\\mastersthesis\\.venv\\lib\\site-packages (from jsonschema->ray[tune]) (2025.9.1)\n",
"Requirement already satisfied: referencing>=0.28.4 in c:\\users\\sji\\desktop\\mastersthesis\\.venv\\lib\\site-packages (from jsonschema->ray[tune]) (0.37.0)\n",
"Requirement already satisfied: rpds-py>=0.7.1 in c:\\users\\sji\\desktop\\mastersthesis\\.venv\\lib\\site-packages (from jsonschema->ray[tune]) (0.28.0)\n",
"Requirement already satisfied: typing-extensions>=4.4.0 in c:\\users\\sji\\desktop\\mastersthesis\\.venv\\lib\\site-packages (from referencing>=0.28.4->jsonschema->ray[tune]) (4.15.0)\n",
"Requirement already satisfied: python-dateutil>=2.8.2 in c:\\users\\sji\\desktop\\mastersthesis\\.venv\\lib\\site-packages (from pandas->ray[tune]) (2.9.0.post0)\n",
"Requirement already satisfied: pytz>=2020.1 in c:\\users\\sji\\desktop\\mastersthesis\\.venv\\lib\\site-packages (from pandas->ray[tune]) (2025.2)\n",
"Requirement already satisfied: tzdata>=2022.7 in c:\\users\\sji\\desktop\\mastersthesis\\.venv\\lib\\site-packages (from pandas->ray[tune]) (2025.2)\n",
"Requirement already satisfied: six>=1.5 in c:\\users\\sji\\desktop\\mastersthesis\\.venv\\lib\\site-packages (from python-dateutil>=2.8.2->pandas->ray[tune]) (1.17.0)\n",
"Requirement already satisfied: charset_normalizer<4,>=2 in c:\\users\\sji\\desktop\\mastersthesis\\.venv\\lib\\site-packages (from requests->ray[tune]) (3.4.4)\n",
"Requirement already satisfied: idna<4,>=2.5 in c:\\users\\sji\\desktop\\mastersthesis\\.venv\\lib\\site-packages (from requests->ray[tune]) (3.11)\n",
"Requirement already satisfied: urllib3<3,>=1.21.1 in c:\\users\\sji\\desktop\\mastersthesis\\.venv\\lib\\site-packages (from requests->ray[tune]) (2.5.0)\n",
"Requirement already satisfied: certifi>=2017.4.17 in c:\\users\\sji\\desktop\\mastersthesis\\.venv\\lib\\site-packages (from requests->ray[tune]) (2025.10.5)\n",
"Downloading rich-14.2.0-py3-none-any.whl (243 kB)\n",
"Downloading markdown_it_py-4.0.0-py3-none-any.whl (87 kB)\n",
"Downloading mdurl-0.1.2-py3-none-any.whl (10.0 kB)\n",
"Installing collected packages: mdurl, markdown-it-py, rich\n",
"\n",
" ---------------------------------------- 0/3 [mdurl]\n",
" ---------------------------------------- 0/3 [mdurl]\n",
" ---------------------------------------- 0/3 [mdurl]\n",
" ---------------------------------------- 0/3 [mdurl]\n",
" ---------------------------------------- 0/3 [mdurl]\n",
" ---------------------------------------- 0/3 [mdurl]\n",
" ---------------------------------------- 0/3 [mdurl]\n",
" ---------------------------------------- 0/3 [mdurl]\n",
" ---------------------------------------- 0/3 [mdurl]\n",
" ---------------------------------------- 0/3 [mdurl]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" ------------- -------------------------- 1/3 [markdown-it-py]\n",
" -------------------------- ------------- 2/3 [rich]\n",
" -------------------------- ------------- 2/3 [rich]\n",
" -------------------------- ------------- 2/3 [rich]\n",
" -------------------------- ------------- 2/3 [rich]\n",
" -------------------------- ------------- 2/3 [rich]\n",
" -------------------------- ------------- 2/3 [rich]\n",
" -------------------------- ------------- 2/3 [rich]\n",
" -------------------------- ------------- 2/3 [rich]\n",
" -------------------------- ------------- 2/3 [rich]\n",
" -------------------------- ------------- 2/3 [rich]\n",
" -------------------------- ------------- 2/3 [rich]\n",
" -------------------------- ------------- 2/3 [rich]\n",
" -------------------------- ------------- 2/3 [rich]\n",
" -------------------------- ------------- 2/3 [rich]\n",
" -------------------------- ------------- 2/3 [rich]\n",
" -------------------------- ------------- 2/3 [rich]\n",
" -------------------------- ------------- 2/3 [rich]\n",
" -------------------------- ------------- 2/3 [rich]\n",
" ---------------------------------------- 3/3 [rich]\n",
"\n",
"Successfully installed markdown-it-py-4.0.0 mdurl-0.1.2 rich-14.2.0\n",
"Note: you may need to restart the kernel to use updated packages.\n"
]
}
],
"source": [
"# Instalación de Ray y Ray Tune\n",
"%pip install -U \"ray[tune]\" rich"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "f3ca0b9b",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"2025-11-12 22:30:42,267\tINFO worker.py:1850 -- Calling ray.init() again after it has already been called.\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Ray Tune listo (versión: 2.51.1 )\n"
]
}
],
"source": [
"import ray\n",
"from ray import tune\n",
"from ray.tune.schedulers import ASHAScheduler\n",
"\n",
"ray.init(ignore_reinit_error=True)\n",
"print(\"Ray Tune listo (versión:\", ray.__version__, \")\")"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "ae5a10c4",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"2025-11-12 22:30:48,318\tINFO worker.py:1850 -- Calling ray.init() again after it has already been called.\n"
]
}
],
"source": [
"# ===============================================================\n",
"# 🔍 RAY TUNE: OPTIMIZACIÓN AUTOMÁTICA DE HIPERPARÁMETROS OCR\n",
"# ===============================================================\n",
"\n",
"from ray import tune, air\n",
"from ray.tune.schedulers import ASHAScheduler\n",
"import pandas as pd\n",
"import time\n",
"import colorama\n",
"from rich import print\n",
"import sys, subprocess \n",
"from rich.console import Console\n",
"\n",
"colorama.just_fix_windows_console()\n",
"ray.init(ignore_reinit_error=True)\n",
"\n",
"# Tell Ray Tune to use a Jupyter-compatible console\n",
"console = Console(force_jupyter=True)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "96c320e8",
"metadata": {},
"outputs": [],
"source": [
"\n",
"\n",
"# --- Configuración base del experimento ---\n",
"search_space = {\n",
" \"dpi\": tune.choice([240, 300, 360]),\n",
" \"textline_orientation\": tune.choice([True, False]),\n",
" \"text_det_box_thresh\": tune.uniform(0.4, 0.7),\n",
" \"text_det_unclip_ratio\": tune.uniform(1.2, 2.0),\n",
" \"text_rec_score_thresh\": tune.choice([0.0, 0.2, 0.4]),\n",
" \"line_tolerance\": tune.choice([0.5, 0.6, 0.7]),\n",
" \"min_box_score\": tune.choice([0, 0.5, 0.6])\n",
"}\n",
"KEYMAP = {\n",
" \"dpi\": \"dpi\",\n",
" \"textline_orientation\": \"textline-orientation\",\n",
" \"text_det_box_thresh\": \"text-det-box-thresh\",\n",
" \"text_det_unclip_ratio\": \"text-det-unclip-ratio\",\n",
" \"text_rec_score_thresh\": \"text-rec-score-thresh\",\n",
" \"line_tolerance\": \"line-tolerance\",\n",
" \"pages_per_pdf\": \"pages-per-pdf\",\n",
" \"min_box_score\": \"min-box-score\",\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "accb4e9d",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">Notebook Python: c:\\Users\\sji\\Desktop\\MastersThesis\\.venv\\Scripts\\python.exe\n",
"</pre>\n"
],
"text/plain": [
"Notebook Python: c:\\Users\\sji\\Desktop\\MastersThesis\\.venv\\Scripts\\python.exe\n"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\">{</span><span style=\"color: #008000; text-decoration-color: #008000\">'CER'</span>: <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">0.019801980198019802</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'WER'</span>: <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">0.09090909090909091</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'TIME'</span>: <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">38.859522104263306</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'PAGES'</span>: <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">1</span><span style=\"font-weight: bold\">}</span>\n",
"</pre>\n"
],
"text/plain": [
"\u001b[1m{\u001b[0m\u001b[32m'CER'\u001b[0m: \u001b[1;36m0.019801980198019802\u001b[0m, \u001b[32m'WER'\u001b[0m: \u001b[1;36m0.09090909090909091\u001b[0m, \u001b[32m'TIME'\u001b[0m: \u001b[1;36m38.859522104263306\u001b[0m, \u001b[32m'PAGES'\u001b[0m: \u001b[1;36m1\u001b[0m\u001b[1m}\u001b[0m\n"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">return code: <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">0</span>\n",
"</pre>\n"
],
"text/plain": [
"return code: \u001b[1;36m0\u001b[0m\n"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">args: <span style=\"font-weight: bold\">[</span><span style=\"color: #008000; text-decoration-color: #008000\">'c:\\\\Users\\\\sji\\\\Desktop\\\\MastersThesis\\\\.venv\\\\Scripts\\\\python.exe'</span>, \n",
"<span style=\"color: #008000; text-decoration-color: #008000\">'c:\\\\Users\\\\sji\\\\Desktop\\\\MastersThesis\\\\paddle_ocr_tuning.py'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'--pdf-folder'</span>, \n",
"<span style=\"color: #008000; text-decoration-color: #008000\">'c:\\\\Users\\\\sji\\\\Desktop\\\\MastersThesis\\\\instructions'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'--pages-per-pdf'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'1'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'--dpi'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'360'</span>, \n",
"<span style=\"color: #008000; text-decoration-color: #008000\">'--textline-orientation'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'True'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'--text-det-box-thresh'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'0.46611732611383844'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'--text-det-unclip-ratio'</span>, \n",
"<span style=\"color: #008000; text-decoration-color: #008000\">'1.3598680409827462'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'--text-rec-score-thresh'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'0.0'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'--line-tolerance'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'0.5'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'--min-box-score'</span>, <span style=\"color: #008000; text-decoration-color: #008000\">'0.6'</span><span style=\"font-weight: bold\">]</span>\n",
"</pre>\n"
],
"text/plain": [
"args: \u001b[1m[\u001b[0m\u001b[32m'c:\\\\Users\\\\sji\\\\Desktop\\\\MastersThesis\\\\.venv\\\\Scripts\\\\python.exe'\u001b[0m, \n",
"\u001b[32m'c:\\\\Users\\\\sji\\\\Desktop\\\\MastersThesis\\\\paddle_ocr_tuning.py'\u001b[0m, \u001b[32m'--pdf-folder'\u001b[0m, \n",
"\u001b[32m'c:\\\\Users\\\\sji\\\\Desktop\\\\MastersThesis\\\\instructions'\u001b[0m, \u001b[32m'--pages-per-pdf'\u001b[0m, \u001b[32m'1'\u001b[0m, \u001b[32m'--dpi'\u001b[0m, \u001b[32m'360'\u001b[0m, \n",
"\u001b[32m'--textline-orientation'\u001b[0m, \u001b[32m'True'\u001b[0m, \u001b[32m'--text-det-box-thresh'\u001b[0m, \u001b[32m'0.46611732611383844'\u001b[0m, \u001b[32m'--text-det-unclip-ratio'\u001b[0m, \n",
"\u001b[32m'1.3598680409827462'\u001b[0m, \u001b[32m'--text-rec-score-thresh'\u001b[0m, \u001b[32m'0.0'\u001b[0m, \u001b[32m'--line-tolerance'\u001b[0m, \u001b[32m'0.5'\u001b[0m, \u001b[32m'--min-box-score'\u001b[0m, \u001b[32m'0.6'\u001b[0m\u001b[1m]\u001b[0m\n"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import sys, subprocess\n",
"print(\"Notebook Python:\", sys.executable)\n",
"# test paddle ocr run with params\n",
"args = [sys.executable, \n",
" SCRIPT_ABS, \n",
" \"--pdf-folder\", PDF_FOLDER_ABS, \n",
" \"--pages-per-pdf\", \"1\",\n",
" \"--dpi\",\"360\" ,\n",
" \"--textline-orientation\",\"True\",\n",
" \"--text-det-box-thresh\",\"0.46611732611383844\",\n",
" \"--text-det-unclip-ratio\",\"1.3598680409827462\",\n",
" \"--text-rec-score-thresh\",\"0.0\",\n",
" \"--line-tolerance\", \"0.5\",\n",
" \"--min-box-score\",\"0.6\"]\n",
"test_proc = subprocess.run(args, capture_output=True, text=True, cwd=SCRIPT_DIR)\n",
"if test_proc.returncode != 0:\n",
" print(test_proc.stderr)\n",
"last = test_proc.stdout.strip().splitlines()[-1]\n",
"\n",
"metrics = json.loads(last)\n",
"print(metrics)\n",
"\n",
"print(f\"return code: {test_proc.returncode}\")\n",
"print(f\"args: {args}\")"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "8df28468",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"c:\\Users\\sji\\Desktop\\MastersThesis\\.venv\\Lib\\site-packages\\ray\\tune\\impl\\tuner_internal.py:144: RayDeprecationWarning: The `RunConfig` class should be imported from `ray.tune` when passing it to the Tuner. Please update your imports. See this issue for more context and migration options: https://github.com/ray-project/ray/issues/49454. Disable these warnings by setting the environment variable: RAY_TRAIN_ENABLE_V2_MIGRATION_WARNINGS=0\n",
" _log_deprecation_warning(\n",
"2025-11-12 22:31:01,166\tINFO tune.py:616 -- [output] This uses the legacy output and progress reporter, as Jupyter notebooks are not supported by the new engine, yet. For more information, please see https://github.com/ray-project/ray/issues/36949\n"
]
},
{
"data": {
"text/html": [
"<div class=\"tuneStatus\">\n",
" <div style=\"display: flex;flex-direction: row\">\n",
" <div style=\"display: flex;flex-direction: column;\">\n",
" <h3>Tune Status</h3>\n",
" <table>\n",
"<tbody>\n",
"<tr><td>Current time:</td><td>2025-11-12 22:39:26</td></tr>\n",
"<tr><td>Running for: </td><td>00:08:25.78 </td></tr>\n",
"<tr><td>Memory: </td><td>9.9/31.8 GiB </td></tr>\n",
"</tbody>\n",
"</table>\n",
" </div>\n",
" <div class=\"vDivider\"></div>\n",
" <div class=\"systemInfo\">\n",
" <h3>System Info</h3>\n",
" Using AsyncHyperBand: num_stopped=1<br>Bracket: Iter 64.000: None | Iter 32.000: None | Iter 16.000: None | Iter 8.000: None | Iter 4.000: None | Iter 2.000: None | Iter 1.000: -0.062382927481937384<br>Logical resource usage: 1.0/12 CPUs, 0/1 GPUs (0.0/1.0 accelerator_type:G)\n",
" </div>\n",
" \n",
" </div>\n",
" <div class=\"hDivider\"></div>\n",
" <div class=\"trialStatus\">\n",
" <h3>Trial Status</h3>\n",
" <table>\n",
"<thead>\n",
"<tr><th>Trial name </th><th>status </th><th>loc </th><th style=\"text-align: right;\"> dpi</th><th style=\"text-align: right;\"> line_tolerance</th><th style=\"text-align: right;\"> min_box_score</th><th style=\"text-align: right;\"> text_det_box_thresh</th><th style=\"text-align: right;\"> text_det_unclip_rati\n",
"o</th><th style=\"text-align: right;\"> text_rec_score_thres\n",
"h</th><th>textline_orientation </th><th style=\"text-align: right;\"> iter</th><th style=\"text-align: right;\"> total time (s)</th><th style=\"text-align: right;\"> CER</th><th style=\"text-align: right;\"> WER</th><th style=\"text-align: right;\"> TIME</th></tr>\n",
"</thead>\n",
"<tbody>\n",
"<tr><td>trainable_paddle_ocr_3632f_00000</td><td>TERMINATED</td><td>127.0.0.1:22388</td><td style=\"text-align: right;\"> 360</td><td style=\"text-align: right;\"> 0.6</td><td style=\"text-align: right;\"> 0.6</td><td style=\"text-align: right;\"> 0.598139</td><td style=\"text-align: right;\">1.595 </td><td style=\"text-align: right;\">0.2</td><td>True </td><td style=\"text-align: right;\"> 1</td><td style=\"text-align: right;\"> 500.4 </td><td style=\"text-align: right;\">0.0684595</td><td style=\"text-align: right;\">0.414935</td><td style=\"text-align: right;\">473.74 </td></tr>\n",
"<tr><td>trainable_paddle_ocr_3632f_00001</td><td>TERMINATED</td><td>127.0.0.1:10796</td><td style=\"text-align: right;\"> 300</td><td style=\"text-align: right;\"> 0.6</td><td style=\"text-align: right;\"> 0.5</td><td style=\"text-align: right;\"> 0.418069</td><td style=\"text-align: right;\">1.61857</td><td style=\"text-align: right;\">0.2</td><td>True </td><td style=\"text-align: right;\"> 1</td><td style=\"text-align: right;\"> 465.474</td><td style=\"text-align: right;\">0.0563063</td><td style=\"text-align: right;\">0.285714</td><td style=\"text-align: right;\">438.892</td></tr>\n",
"</tbody>\n",
"</table>\n",
" </div>\n",
"</div>\n",
"<style>\n",
".tuneStatus {\n",
" color: var(--jp-ui-font-color1);\n",
"}\n",
".tuneStatus .systemInfo {\n",
" display: flex;\n",
" flex-direction: column;\n",
"}\n",
".tuneStatus td {\n",
" white-space: nowrap;\n",
"}\n",
".tuneStatus .trialStatus {\n",
" display: flex;\n",
" flex-direction: column;\n",
"}\n",
".tuneStatus h3 {\n",
" font-weight: bold;\n",
"}\n",
".tuneStatus .hDivider {\n",
" border-bottom-width: var(--jp-border-width);\n",
" border-bottom-color: var(--jp-border-color0);\n",
" border-bottom-style: solid;\n",
"}\n",
".tuneStatus .vDivider {\n",
" border-left-width: var(--jp-border-width);\n",
" border-left-color: var(--jp-border-color0);\n",
" border-left-style: solid;\n",
" margin: 0.5em 1em 0.5em 1em;\n",
"}\n",
"</style>\n"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"2025-11-12 22:31:01,216\tWARNING trial.py:647 -- The path to the trial log directory is too long (max length: 260. Consider using `trial_dirname_creator` to shorten the path. Path: C:\\Users\\sji\\AppData\\Local\\Temp\\ray\\session_2025-11-12_22-29-00_496141_15712\\artifacts\\2025-11-12_22-31-01\\trainable_paddle_ocr_2025-11-12_22-31-01\\driver_artifacts\\trainable_paddle_ocr_3632f_00000_0_dpi=360,line_tolerance=0.6000,min_box_score=0.6000,text_det_box_thresh=0.5981,text_det_unclip_r_2025-11-12_22-31-01\n",
"2025-11-12 22:31:01,216\tWARNING trial.py:647 -- The path to the trial log directory is too long (max length: 260. Consider using `trial_dirname_creator` to shorten the path. Path: C:\\Users\\sji\\AppData\\Local\\Temp\\ray\\session_2025-11-12_22-29-00_496141_15712\\artifacts\\2025-11-12_22-31-01\\trainable_paddle_ocr_2025-11-12_22-31-01\\driver_artifacts\\trainable_paddle_ocr_3632f_00000_0_dpi=360,line_tolerance=0.6000,min_box_score=0.6000,text_det_box_thresh=0.5981,text_det_unclip_r_2025-11-12_22-31-01\n",
"2025-11-12 22:31:01,265\tWARNING trial.py:647 -- The path to the trial log directory is too long (max length: 260. Consider using `trial_dirname_creator` to shorten the path. Path: C:\\Users\\sji\\AppData\\Local\\Temp\\ray\\session_2025-11-12_22-29-00_496141_15712\\artifacts\\2025-11-12_22-31-01\\trainable_paddle_ocr_2025-11-12_22-31-01\\driver_artifacts\\trainable_paddle_ocr_3632f_00001_1_dpi=300,line_tolerance=0.6000,min_box_score=0.5000,text_det_box_thresh=0.4181,text_det_unclip_r_2025-11-12_22-31-01\n",
"2025-11-12 22:31:01,265\tWARNING trial.py:647 -- The path to the trial log directory is too long (max length: 260. Consider using `trial_dirname_creator` to shorten the path. Path: C:\\Users\\sji\\AppData\\Local\\Temp\\ray\\session_2025-11-12_22-29-00_496141_15712\\artifacts\\2025-11-12_22-31-01\\trainable_paddle_ocr_2025-11-12_22-31-01\\driver_artifacts\\trainable_paddle_ocr_3632f_00001_1_dpi=300,line_tolerance=0.6000,min_box_score=0.5000,text_det_box_thresh=0.4181,text_det_unclip_r_2025-11-12_22-31-01\n",
"2025-11-12 22:31:06,561\tWARNING trial.py:647 -- The path to the trial log directory is too long (max length: 260. Consider using `trial_dirname_creator` to shorten the path. Path: C:\\Users\\sji\\AppData\\Local\\Temp\\ray\\session_2025-11-12_22-29-00_496141_15712\\artifacts\\2025-11-12_22-31-01\\trainable_paddle_ocr_2025-11-12_22-31-01\\driver_artifacts\\trainable_paddle_ocr_3632f_00000_0_dpi=360,line_tolerance=0.6000,min_box_score=0.6000,text_det_box_thresh=0.5981,text_det_unclip_r_2025-11-12_22-31-01\n",
"2025-11-12 22:31:06,563\tWARNING trial.py:647 -- The path to the trial log directory is too long (max length: 260. Consider using `trial_dirname_creator` to shorten the path. Path: C:\\Users\\sji\\AppData\\Local\\Temp\\ray\\session_2025-11-12_22-29-00_496141_15712\\artifacts\\2025-11-12_22-31-01\\trainable_paddle_ocr_2025-11-12_22-31-01\\driver_artifacts\\trainable_paddle_ocr_3632f_00000_0_dpi=360,line_tolerance=0.6000,min_box_score=0.6000,text_det_box_thresh=0.5981,text_det_unclip_r_2025-11-12_22-31-01\n",
"2025-11-12 22:31:06,605\tWARNING trial.py:647 -- The path to the trial log directory is too long (max length: 260. Consider using `trial_dirname_creator` to shorten the path. Path: C:\\Users\\sji\\AppData\\Local\\Temp\\ray\\session_2025-11-12_22-29-00_496141_15712\\artifacts\\2025-11-12_22-31-01\\trainable_paddle_ocr_2025-11-12_22-31-01\\driver_artifacts\\trainable_paddle_ocr_3632f_00001_1_dpi=300,line_tolerance=0.6000,min_box_score=0.5000,text_det_box_thresh=0.4181,text_det_unclip_r_2025-11-12_22-31-01\n",
"2025-11-12 22:31:06,605\tWARNING trial.py:647 -- The path to the trial log directory is too long (max length: 260. Consider using `trial_dirname_creator` to shorten the path. Path: C:\\Users\\sji\\AppData\\Local\\Temp\\ray\\session_2025-11-12_22-29-00_496141_15712\\artifacts\\2025-11-12_22-31-01\\trainable_paddle_ocr_2025-11-12_22-31-01\\driver_artifacts\\trainable_paddle_ocr_3632f_00001_1_dpi=300,line_tolerance=0.6000,min_box_score=0.5000,text_det_box_thresh=0.4181,text_det_unclip_r_2025-11-12_22-31-01\n"
]
},
{
"data": {
"text/html": [
"<div class=\"trialProgress\">\n",
" <h3>Trial Progress</h3>\n",
" <table>\n",
"<thead>\n",
"<tr><th>Trial name </th><th style=\"text-align: right;\"> CER</th><th style=\"text-align: right;\"> PAGES</th><th style=\"text-align: right;\"> TIME</th><th style=\"text-align: right;\"> TIME_PER_PAGE</th><th style=\"text-align: right;\"> WER</th></tr>\n",
"</thead>\n",
"<tbody>\n",
"<tr><td>trainable_paddle_ocr_3632f_00000</td><td style=\"text-align: right;\">0.0684595</td><td style=\"text-align: right;\"> 2</td><td style=\"text-align: right;\">473.74 </td><td style=\"text-align: right;\"> 236.768</td><td style=\"text-align: right;\">0.414935</td></tr>\n",
"<tr><td>trainable_paddle_ocr_3632f_00001</td><td style=\"text-align: right;\">0.0563063</td><td style=\"text-align: right;\"> 2</td><td style=\"text-align: right;\">438.892</td><td style=\"text-align: right;\"> 219.372</td><td style=\"text-align: right;\">0.285714</td></tr>\n",
"</tbody>\n",
"</table>\n",
"</div>\n",
"<style>\n",
".trialProgress {\n",
" display: flex;\n",
" flex-direction: column;\n",
" color: var(--jp-ui-font-color1);\n",
"}\n",
".trialProgress h3 {\n",
" font-weight: bold;\n",
"}\n",
".trialProgress td {\n",
" white-space: nowrap;\n",
"}\n",
"</style>\n"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"2025-11-12 22:38:52,093\tWARNING trial.py:647 -- The path to the trial log directory is too long (max length: 260. Consider using `trial_dirname_creator` to shorten the path. Path: C:\\Users\\sji\\AppData\\Local\\Temp\\ray\\session_2025-11-12_22-29-00_496141_15712\\artifacts\\2025-11-12_22-31-01\\trainable_paddle_ocr_2025-11-12_22-31-01\\driver_artifacts\\trainable_paddle_ocr_3632f_00001_1_dpi=300,line_tolerance=0.6000,min_box_score=0.5000,text_det_box_thresh=0.4181,text_det_unclip_r_2025-11-12_22-31-01\n",
"2025-11-12 22:39:26,972\tWARNING trial.py:647 -- The path to the trial log directory is too long (max length: 260. Consider using `trial_dirname_creator` to shorten the path. Path: C:\\Users\\sji\\AppData\\Local\\Temp\\ray\\session_2025-11-12_22-29-00_496141_15712\\artifacts\\2025-11-12_22-31-01\\trainable_paddle_ocr_2025-11-12_22-31-01\\driver_artifacts\\trainable_paddle_ocr_3632f_00000_0_dpi=360,line_tolerance=0.6000,min_box_score=0.6000,text_det_box_thresh=0.5981,text_det_unclip_r_2025-11-12_22-31-01\n",
"2025-11-12 22:39:26,988\tINFO tune.py:1009 -- Wrote the latest version of all result files and experiment state to 'C:/Users/sji/ray_results/trainable_paddle_ocr_2025-11-12_22-31-01' in 0.0087s.\n",
"2025-11-12 22:39:26,994\tINFO tune.py:1041 -- Total run time: 505.83 seconds (505.77 seconds for the tuning loop).\n"
]
}
],
"source": [
"def trainable_paddle_ocr(config):\n",
" args = [sys.executable, SCRIPT_ABS, \"--pdf-folder\", PDF_FOLDER_ABS, \"--pages-per-pdf\", \"2\"]\n",
" for k, v in config.items():\n",
" args += [f\"--{KEYMAP[k]}\", str(v)]\n",
" proc = subprocess.run(args, capture_output=True, text=True, cwd=SCRIPT_DIR)\n",
"\n",
" if proc.returncode != 0:\n",
" tune.report(CER=1.0, WER=1.0, TIME=0.0, ERROR=proc.stderr[:500])\n",
" return\n",
" # última línea = JSON con métricas\n",
" last = proc.stdout.strip().splitlines()[-1]\n",
" \n",
" metrics = json.loads(last)\n",
" tune.report(metrics=metrics)\n",
"\n",
"scheduler = ASHAScheduler(grace_period=1, reduction_factor=2)\n",
"\n",
"tuner = tune.Tuner(\n",
" trainable_paddle_ocr,\n",
" tune_config=tune.TuneConfig(metric=\"CER\", \n",
" mode=\"min\", \n",
" scheduler=scheduler, \n",
" num_samples=2, \n",
" max_concurrent_trials=4),\n",
" run_config=air.RunConfig(verbose=2, log_to_file=False),\n",
" param_space=search_space\n",
")\n",
"\n",
"results = tuner.fit()\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "710a67ce",
"metadata": {},
"outputs": [],
"source": [
"df = results.get_dataframe().sort_values(\"CER\", ascending=True)"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "1ab345a3",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">Guardado: raytune_paddle_subproc_results_20251112_223927.csv\n",
"</pre>\n"
],
"text/plain": [
"Guardado: raytune_paddle_subproc_results_20251112_223927.csv\n"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Generate a unique filename with timestamp\n",
"timestamp = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n",
"filename = f\"raytune_paddle_subproc_results_{timestamp}.csv\"\n",
"filepath = os.path.join(OUTPUT_FOLDER, filename)\n",
"\n",
"\n",
"df.to_csv(filename, index=False)\n",
"print(f\"Guardado: {filename}\")"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "3e3a34e4",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>CER</th>\n",
" <th>WER</th>\n",
" <th>TIME</th>\n",
" <th>PAGES</th>\n",
" <th>TIME_PER_PAGE</th>\n",
" <th>timestamp</th>\n",
" <th>training_iteration</th>\n",
" <th>time_this_iter_s</th>\n",
" <th>time_total_s</th>\n",
" <th>pid</th>\n",
" <th>time_since_restore</th>\n",
" <th>iterations_since_restore</th>\n",
" <th>config/dpi</th>\n",
" <th>config/text_det_box_thresh</th>\n",
" <th>config/text_det_unclip_ratio</th>\n",
" <th>config/text_rec_score_thresh</th>\n",
" <th>config/line_tolerance</th>\n",
" <th>config/min_box_score</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>count</th>\n",
" <td>2.000000</td>\n",
" <td>2.000000</td>\n",
" <td>2.000000</td>\n",
" <td>2.0</td>\n",
" <td>2.000000</td>\n",
" <td>2.000000e+00</td>\n",
" <td>2.0</td>\n",
" <td>2.000000</td>\n",
" <td>2.000000</td>\n",
" <td>2.000000</td>\n",
" <td>2.000000</td>\n",
" <td>2.0</td>\n",
" <td>2.000000</td>\n",
" <td>2.000000</td>\n",
" <td>2.000000</td>\n",
" <td>2.0</td>\n",
" <td>2.0</td>\n",
" <td>2.000000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>mean</th>\n",
" <td>0.062383</td>\n",
" <td>0.350325</td>\n",
" <td>456.315870</td>\n",
" <td>2.0</td>\n",
" <td>228.070288</td>\n",
" <td>1.762958e+09</td>\n",
" <td>1.0</td>\n",
" <td>482.937319</td>\n",
" <td>482.937319</td>\n",
" <td>16592.000000</td>\n",
" <td>482.937319</td>\n",
" <td>1.0</td>\n",
" <td>330.000000</td>\n",
" <td>0.508104</td>\n",
" <td>1.606787</td>\n",
" <td>0.2</td>\n",
" <td>0.6</td>\n",
" <td>0.550000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>std</th>\n",
" <td>0.008594</td>\n",
" <td>0.091373</td>\n",
" <td>24.641709</td>\n",
" <td>0.0</td>\n",
" <td>12.300573</td>\n",
" <td>2.404163e+01</td>\n",
" <td>0.0</td>\n",
" <td>24.696451</td>\n",
" <td>24.696451</td>\n",
" <td>8196.781808</td>\n",
" <td>24.696451</td>\n",
" <td>0.0</td>\n",
" <td>42.426407</td>\n",
" <td>0.127329</td>\n",
" <td>0.016666</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.070711</td>\n",
" </tr>\n",
" <tr>\n",
" <th>min</th>\n",
" <td>0.056306</td>\n",
" <td>0.285714</td>\n",
" <td>438.891550</td>\n",
" <td>2.0</td>\n",
" <td>219.372469</td>\n",
" <td>1.762958e+09</td>\n",
" <td>1.0</td>\n",
" <td>465.474291</td>\n",
" <td>465.474291</td>\n",
" <td>10796.000000</td>\n",
" <td>465.474291</td>\n",
" <td>1.0</td>\n",
" <td>300.000000</td>\n",
" <td>0.418069</td>\n",
" <td>1.595003</td>\n",
" <td>0.2</td>\n",
" <td>0.6</td>\n",
" <td>0.500000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>25%</th>\n",
" <td>0.059345</td>\n",
" <td>0.318019</td>\n",
" <td>447.603710</td>\n",
" <td>2.0</td>\n",
" <td>223.721378</td>\n",
" <td>1.762958e+09</td>\n",
" <td>1.0</td>\n",
" <td>474.205805</td>\n",
" <td>474.205805</td>\n",
" <td>13694.000000</td>\n",
" <td>474.205805</td>\n",
" <td>1.0</td>\n",
" <td>315.000000</td>\n",
" <td>0.463086</td>\n",
" <td>1.600895</td>\n",
" <td>0.2</td>\n",
" <td>0.6</td>\n",
" <td>0.525000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>50%</th>\n",
" <td>0.062383</td>\n",
" <td>0.350325</td>\n",
" <td>456.315870</td>\n",
" <td>2.0</td>\n",
" <td>228.070288</td>\n",
" <td>1.762958e+09</td>\n",
" <td>1.0</td>\n",
" <td>482.937319</td>\n",
" <td>482.937319</td>\n",
" <td>16592.000000</td>\n",
" <td>482.937319</td>\n",
" <td>1.0</td>\n",
" <td>330.000000</td>\n",
" <td>0.508104</td>\n",
" <td>1.606787</td>\n",
" <td>0.2</td>\n",
" <td>0.6</td>\n",
" <td>0.550000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>75%</th>\n",
" <td>0.065421</td>\n",
" <td>0.382630</td>\n",
" <td>465.028030</td>\n",
" <td>2.0</td>\n",
" <td>232.419197</td>\n",
" <td>1.762958e+09</td>\n",
" <td>1.0</td>\n",
" <td>491.668833</td>\n",
" <td>491.668833</td>\n",
" <td>19490.000000</td>\n",
" <td>491.668833</td>\n",
" <td>1.0</td>\n",
" <td>345.000000</td>\n",
" <td>0.553121</td>\n",
" <td>1.612680</td>\n",
" <td>0.2</td>\n",
" <td>0.6</td>\n",
" <td>0.575000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>max</th>\n",
" <td>0.068460</td>\n",
" <td>0.414935</td>\n",
" <td>473.740190</td>\n",
" <td>2.0</td>\n",
" <td>236.768107</td>\n",
" <td>1.762958e+09</td>\n",
" <td>1.0</td>\n",
" <td>500.400347</td>\n",
" <td>500.400347</td>\n",
" <td>22388.000000</td>\n",
" <td>500.400347</td>\n",
" <td>1.0</td>\n",
" <td>360.000000</td>\n",
" <td>0.598139</td>\n",
" <td>1.618572</td>\n",
" <td>0.2</td>\n",
" <td>0.6</td>\n",
" <td>0.600000</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" CER WER TIME PAGES TIME_PER_PAGE timestamp \\\n",
"count 2.000000 2.000000 2.000000 2.0 2.000000 2.000000e+00 \n",
"mean 0.062383 0.350325 456.315870 2.0 228.070288 1.762958e+09 \n",
"std 0.008594 0.091373 24.641709 0.0 12.300573 2.404163e+01 \n",
"min 0.056306 0.285714 438.891550 2.0 219.372469 1.762958e+09 \n",
"25% 0.059345 0.318019 447.603710 2.0 223.721378 1.762958e+09 \n",
"50% 0.062383 0.350325 456.315870 2.0 228.070288 1.762958e+09 \n",
"75% 0.065421 0.382630 465.028030 2.0 232.419197 1.762958e+09 \n",
"max 0.068460 0.414935 473.740190 2.0 236.768107 1.762958e+09 \n",
"\n",
" training_iteration time_this_iter_s time_total_s pid \\\n",
"count 2.0 2.000000 2.000000 2.000000 \n",
"mean 1.0 482.937319 482.937319 16592.000000 \n",
"std 0.0 24.696451 24.696451 8196.781808 \n",
"min 1.0 465.474291 465.474291 10796.000000 \n",
"25% 1.0 474.205805 474.205805 13694.000000 \n",
"50% 1.0 482.937319 482.937319 16592.000000 \n",
"75% 1.0 491.668833 491.668833 19490.000000 \n",
"max 1.0 500.400347 500.400347 22388.000000 \n",
"\n",
" time_since_restore iterations_since_restore config/dpi \\\n",
"count 2.000000 2.0 2.000000 \n",
"mean 482.937319 1.0 330.000000 \n",
"std 24.696451 0.0 42.426407 \n",
"min 465.474291 1.0 300.000000 \n",
"25% 474.205805 1.0 315.000000 \n",
"50% 482.937319 1.0 330.000000 \n",
"75% 491.668833 1.0 345.000000 \n",
"max 500.400347 1.0 360.000000 \n",
"\n",
" config/text_det_box_thresh config/text_det_unclip_ratio \\\n",
"count 2.000000 2.000000 \n",
"mean 0.508104 1.606787 \n",
"std 0.127329 0.016666 \n",
"min 0.418069 1.595003 \n",
"25% 0.463086 1.600895 \n",
"50% 0.508104 1.606787 \n",
"75% 0.553121 1.612680 \n",
"max 0.598139 1.618572 \n",
"\n",
" config/text_rec_score_thresh config/line_tolerance \\\n",
"count 2.0 2.0 \n",
"mean 0.2 0.6 \n",
"std 0.0 0.0 \n",
"min 0.2 0.6 \n",
"25% 0.2 0.6 \n",
"50% 0.2 0.6 \n",
"75% 0.2 0.6 \n",
"max 0.2 0.6 \n",
"\n",
" config/min_box_score \n",
"count 2.000000 \n",
"mean 0.550000 \n",
"std 0.070711 \n",
"min 0.500000 \n",
"25% 0.525000 \n",
"50% 0.550000 \n",
"75% 0.575000 \n",
"max 0.600000 "
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.describe()"
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "4ce5eb6a",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">Correlación con CER:\n",
" config/min_box_score <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">1.0</span>\n",
"CER <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">1.0</span>\n",
"config/text_det_box_thresh <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">1.0</span>\n",
"config/dpi <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">1.0</span>\n",
"config/text_det_unclip_ratio <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">-1.0</span>\n",
"config/text_rec_score_thresh NaN\n",
"config/line_tolerance NaN\n",
"Name: CER, dtype: float64\n",
"</pre>\n"
],
"text/plain": [
"Correlación con CER:\n",
" config/min_box_score \u001b[1;36m1.0\u001b[0m\n",
"CER \u001b[1;36m1.0\u001b[0m\n",
"config/text_det_box_thresh \u001b[1;36m1.0\u001b[0m\n",
"config/dpi \u001b[1;36m1.0\u001b[0m\n",
"config/text_det_unclip_ratio \u001b[1;36m-1.0\u001b[0m\n",
"config/text_rec_score_thresh NaN\n",
"config/line_tolerance NaN\n",
"Name: CER, dtype: float64\n"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">Correlación con WER:\n",
" config/min_box_score <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">1.0</span>\n",
"config/dpi <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">1.0</span>\n",
"config/text_det_box_thresh <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">1.0</span>\n",
"WER <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">1.0</span>\n",
"config/text_det_unclip_ratio <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">-1.0</span>\n",
"config/text_rec_score_thresh NaN\n",
"config/line_tolerance NaN\n",
"Name: WER, dtype: float64\n",
"</pre>\n"
],
"text/plain": [
"Correlación con WER:\n",
" config/min_box_score \u001b[1;36m1.0\u001b[0m\n",
"config/dpi \u001b[1;36m1.0\u001b[0m\n",
"config/text_det_box_thresh \u001b[1;36m1.0\u001b[0m\n",
"WER \u001b[1;36m1.0\u001b[0m\n",
"config/text_det_unclip_ratio \u001b[1;36m-1.0\u001b[0m\n",
"config/text_rec_score_thresh NaN\n",
"config/line_tolerance NaN\n",
"Name: WER, dtype: float64\n"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"param_cols = [\n",
" \"config/dpi\",\n",
" \"config/text_det_box_thresh\",\n",
" \"config/text_det_unclip_ratio\",\n",
" \"config/text_rec_score_thresh\",\n",
" \"config/line_tolerance\",\n",
" \"config/min_box_score\",\n",
"]\n",
"# Correlación de Pearson con CER y WER\n",
"corr_cer = df[param_cols + [\"CER\"]].corr()[\"CER\"].sort_values(ascending=False)\n",
"corr_wer = df[param_cols + [\"WER\"]].corr()[\"WER\"].sort_values(ascending=False)\n",
"\n",
"print(\"Correlación con CER:\\n\", corr_cer)\n",
"print(\"Correlación con WER:\\n\", corr_wer)"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "02fc0a87",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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fXc81KWC/n7yySfTvd2pPR+jbf+KFSvc+aPp1OWBmngrTWpvs7NmzXLN/LWtF198sXfjjTd63377bZJ11nYmlpblyGuvveaahGs5uXPn9kqXLu317t3bW758eSiNf66tW7cuYlr/uOl/3/vvv+81b97cna9lypRx180777yTJF3QsUjLPGTVqlWumwj/uNerV8974oknIq6xwYMHu2tL94DE+yY113pa7jkpdQ8Qfs0G7deU9k9qzk2/ewDdX1Irue184YUXIp4v6vJC3UWoSwKth+6pH3/8cZJnUEbs/6wkh/6JdbAGAACQFVFHCQAAIACBEgAAQAACJQAAgAAESgAAAAEIlAAAAAIQKAEAAASgw8l0UieBemO5OvpKz1vfAQBA5lOvSOqwVp35Jn7RcDQESumkIEnvCwMAAPHnm2++cb37p4RAKZ38LuO1o9WVOwAAyPqOHDniMjpS++oXAqV08ovbFCQRKAEAEF9SW22GytwAAAABCJQAAAACECgBAAAEIFACAAAIQKAEAAAQgEAJAAAgAIESAABAAAIlAACAAARKAAAAAeiZGwAAZBlnznq2dtdB23f0pJUolGBNK11suXLG7uXzBEoAACBLWLTlB7t/3mf2w+GToWGliyTYuGtrWcc6pWOyThS9AQCALBEk3f7KxoggSfYcPumGa3wsECgBAICYF7fdP+8z86KM84dpvNJlNgIlAAAQU2t3HUySkxRO4ZHGK11mI1ACAAAxte/oyQxNl5EIlAAAQEyVKJSQoekyEoESAACIqaaVLnat24I6AdBwjVe6zEagBAAAYipXzhyuCwBJHCz53zU+Fv0pESgBAICY61intD31h8usVJHI4jV91/BY9aNEh5MAACBL6FintF1dqxQ9cwMAAESj4rUWlxa1rIKiNwAAgKwaKE2dOtUqVqxoCQkJ1qxZM1u7dm2y6WfPnm01atRw6evWrWsLFy5Mkmbr1q3WtWtXK1KkiBUoUMCaNGliu3fvDo3fs2eP3XTTTVaqVCk3/rLLLrM5c+ack+0DAADxK6aB0qxZs2z48OE2btw427hxo9WvX986dOhg+/bti5p+9erV1rdvXxs4cKBt2rTJunfv7j5btmwJpdm5c6e1atXKBVPLly+3zZs327333usCK1+/fv1s+/bt9tZbb9knn3xiPXv2tBtuuMHNEwAAwJfD87zMf3HK/6ccJOX2TJkyxX0/e/aslStXzgYPHmwjR45Mkr537952/Phxmz9/fmhY8+bNrUGDBjZt2jT3vU+fPpYnTx57+eWXA5dbsGBBe+qpp1yukq9o0aL20EMP2R//+MdUrfuRI0dcjtXhw4etcOHCadpuAAAQG2l9fscsR+n06dO2YcMGa9eu3X9XJmdO933NmjVRp9Hw8PSiHCg/vQKtBQsWWLVq1dzwEiVKuGBs7ty5EdO0bNnS5WYdPHjQTTNz5kw7efKkXXHFFedkWwEAQHyKWaB04MABO3PmjJUsWTJiuL6rDlE0Gp5cehXZHTt2zCZOnGgdO3a0xYsXW48ePVzR2ooVK0LT/Pvf/7ZffvnF5SLly5fPbr31VnvjjTesSpUqget76tQpF4WGfwAAQPaWrfpRUu6QdOvWzYYNG+b+VrGc6japaK5NmzZumOosHTp0yN59910rVqyYy3FSHaWVK1e6CuLRTJgwwe6///5M3BoAAHDe5igpQMmVK5ft3bs3Yri+qzVaNBqeXHrNM3fu3Far1v91g+6rWbNmqNWbKnurTtTzzz9vV111latArsrkjRs3di3wgowaNcqVZ/qfb775Jt3bDgAA4kPMAqW8efNao0aNbOnSpRE5QvreokWLqNNoeHh6WbJkSSi95qnK4WrRFm7Hjh1WoUIF9/eJEydC9aHCKWjzc6SiURGdKn2FfwAAQPYW06I3dQ3Qv39/l5vTtGlTmzx5smvVNmDAgFAz/rJly7piLxk6dKgrPps0aZJ17tzZVcJev369TZ8+PTTPESNGuNZxrVu3trZt29qiRYts3rx5rqsAUbcBqoukekmPPvqoq6ekojcFXOGt6QAAAMyLsSeeeMIrX768lzdvXq9p06beBx98EBrXpk0br3///hHp//3vf3vVqlVz6WvXru0tWLAgyTyfe+45r0qVKl5CQoJXv359b+7cuRHjd+zY4fXs2dMrUaKEd8EFF3j16tXzXnrppTSt9+HDh9WtgvsfAADEh7Q+v2Paj1I8ox8lAADiT9z0owQAAJDVESgBAAAEIFACAAAIQKAEAAAQgEAJAAAgAIESAABAAAIlAACAAARKAAAAAQiUAAAAAhAoAQAABCBQAgAACECgBAAAEIBACQAAIACBEgAAQAACJQAAgAAESgAAAAEIlAAAAAIQKAEAAAQgUAIAAAhAoAQAABCAQAkAACAAgRIAAEAAAiUAAIAABEoAAAABCJQAAAACECgBAAAEIFACAAAIQKAEAAAQgEAJAAAgAIESAABAAAIlAACAAARKAAAAAQiUAAAAAhAoAQAABCBQAgAACECgBAAAEIBACQAAIACBEgAAQAACJQAAgAAESgAAAAEIlAAAAAIQKAEAAAQgUAIAAAhAoAQAABCAQAkAACCrBkpTp061ihUrWkJCgjVr1szWrl2bbPrZs2dbjRo1XPq6devawoULk6TZunWrde3a1YoUKWIFChSwJk2a2O7duyPSrFmzxq688ko3vnDhwta6dWv7+eefM3z7AABA/IppoDRr1iwbPny4jRs3zjZu3Gj169e3Dh062L59+6KmX716tfXt29cGDhxomzZtsu7du7vPli1bQml27txprVq1csHU8uXLbfPmzXbvvfe6wCo8SOrYsaO1b9/eBWbr1q2zO++803LmjHncCAAAspAcnud5sVq4cpCU2zNlyhT3/ezZs1auXDkbPHiwjRw5Mkn63r172/Hjx23+/PmhYc2bN7cGDRrYtGnT3Pc+ffpYnjx57OWXXw5crqa5+uqrbfz48ele9yNHjrgcq8OHD7scKQAAkPWl9fkdsyyU06dP24YNG6xdu3b/XZmcOd135fhEo+Hh6UU5UH56BVoLFiywatWqueElSpRwwdjcuXND6ZVb9eGHH7pxLVu2tJIlS1qbNm1s1apVya7vqVOn3M4N/wAAgOwtZoHSgQMH7MyZMy5QCafve/bsiTqNhieXXkHQsWPHbOLEia5obfHixdajRw/r2bOnrVixwqX58ssv3f/33Xef3XLLLbZo0SK77LLL7KqrrrLPP/88cH0nTJjgIlD/o5wvAACQvWWrSjnKUZJu3brZsGHDXJGcivC6dOkSKprz09x66602YMAAa9iwoT3++ONWvXp1e/755wPnPWrUKJdN53+++eabTNoqAAAQK7ljteBixYpZrly5bO/evRHD9b1UqVJRp9Hw5NJrnrlz57ZatWpFpKlZs2aoaK106dLu/2hpEreMC5cvXz73AQAA54+Y5SjlzZvXGjVqZEuXLg0NU26Pvrdo0SLqNBoenl6WLFkSSq95qnL49u3bI9Ls2LHDKlSo4P5WVwRlypRJNg0AAEBMc5REXQP079/fGjdubE2bNrXJkye7Vm0qEpN+/fpZ2bJl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UaNc8Zz/+eabb1K9nQAA4DwIlFTEpVZnderUcYGDirT0GT16tBu2atWqJMViycmbN681atTIli5dGpEjpO8tWrSIOo2Gh6eXJUuWhNJrnqocvn379og0O3bscJXRw3OSVPFc6d96661Qi7gg+fLlc3WYwj8AACB7S3PNZQUb6rfop59+si+++MLUaK5q1ap20UUXpWsF1DVA//79rXHjxta0aVPXBYFang0YMMCN79evn6sbpaIvGTp0qLVp08YmTZrkisxmzpxp69evt+nTp4fmOWLECNc6rnXr1ta2bVvXUm/evHmuq4DwIOnEiRP2yiuvRNQ5Kl68uMvlAgAAUKATc0888YRXvnx5L2/evF7Tpk29Dz74IDSuTZs2Xv/+/SPS//vf//aqVavm0teuXdtbsGBBknk+99xzXpUqVbyEhASvfv363ty5c0Pj3nvvPXWJEPWza9euVK3z4cOHXXr9DwAA4kNan99p6kcJ/0U/SgAAxJ9z2o8SAADA+YRACQAAIACBEgAAQAACJQAAgAAESgAAAAEIlAAAAAIQKAEAAAQgUAIAAAhAoAQAABCAQAkAACAAgRIAAEAAAiUAAIAABEoAAAABCJQAAAACECgBAAAEIFACAAAIQKAEAAAQgEAJAAAgAIESAABAAAIlAACAAARKAAAAAQiUAAAAAhAoAQAABCBQAgAACECgBAAAEIBACQAAIACBEgAAQAACJQAAgAAESgAAAAEIlAAAAAIQKAEAAAQgUAIAAAhAoAQAABCAQAkAACAAgRIAAEAAAiUAAIAABEoAAAABCJQAAAACECgBAAAEIFACAAAIQKAEAAAQgEAJAAAgAIESAABAAAIlAACAAARKAAAAWTlQmjp1qlWsWNESEhKsWbNmtnbt2mTTz54922rUqOHS161b1xYuXJgkzdatW61r165WpEgRK1CggDVp0sR2794dGn/y5EkbNGiQFS1a1AoWLGi9evWyvXv3npPtAwAA8SnmgdKsWbNs+PDhNm7cONu4caPVr1/fOnToYPv27YuafvXq1da3b18bOHCgbdq0ybp37+4+W7ZsCaXZuXOntWrVygVTy5cvt82bN9u9997rAivfsGHDbN68eS7oWrFihX3//ffWs2fPTNlmAAAQH3J4nufFcgWUg6TcnilTprjvZ8+etXLlytngwYNt5MiRSdL37t3bjh8/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sKGVLl3aOnXqlGyO0qlTp9zODf8AAIDsLaaB0oEDB+zMmTNWsmTJiOH6vmfPnqjTaHhK6VXs9tJLL9nSpUvtoYceshUrVrhASMuSL7/80v1/33332ZgxY1zulOooXXHFFXbwYPSOHVXUpwjU/yjXCwAAZG9ZojJ3RuvTp4917drVVfRW/SUFQuvWrXO5TH7xntxzzz3Wq1cva9SokavPpErfqigezahRo1w2nf/55ptvMnWbAADAeRYoFStWzHLlymV79+6NGK7vpUqVijqNhqclvVSuXNkt64svvnDfVdQmtWr9X7N8v7K30u3evTvqPDReZZnhHwAAkL3FNFDKmzevy81REZlPuT363qJFi6jTaHh4elmyZElgevn2229dHSU/QNIyFfioZZ3vl19+sa+++soqVKiQAVsGAACyg5h3D6CuAfr372+NGze2pk2buu4B1KptwIABbny/fv2sbNmyro6QDB061Nq0aWOTJk2yzp0728yZM239+vU2ffp0N/7YsWOuNZuK1JTLtHPnTrvrrrusSpUqrtK3KDfotttus3Hjxrm6RgqOHnnkETfu+uuvj9m+AAAAWUvMAyU199+/f7+NHTvWVchWM/9FixaFKmyrKEyt03wtW7Z0fSepEvbo0aOtatWqrhsAvw8lFeVt3rzZXnzxRddFQJkyZax9+/Y2fvz4iL6UFBjlzp3bbrrpJvv5559dNwXLli1zlboBAACyRD9K8Yp+lAAAiD9x1Y8SAABAVhbzord45WfE0fEkAADxw39up7ZAjUApnY4ePer+p+NJAADi8zmuIriUUEcpndSNwffff2+FChVyHVVmxYhZQZw6xjyf+nw6X7db2Pbz77hzzDnm55MjGXR/V06SgiQ19gpvLBaEHKV00s695JJLLKs7XzvHPF+3W9j28++4c8w55ueTwhlwf09NTpKPytwAAAABCJQAAAACEChlU+pcUz2Ph3eyeT44X7db2Pbz77hzzDnm55N8Mbq/U5kbAAAgADlKAAAAAQiUAAAAAhAoAQAABCBQAgAACECglEVNnTrVKlasaAkJCdasWTNbu3ZtqqabOXOm6ym8e/fuoWG//PKL3X333Va3bl0rUKCA6420X79+rmfxcFqepg3/TJw40eJ52+Xmm29Osl0dO3aMSHPw4EG78cYbXSdmF154oQ0cONCOHTtm8bzdibfZ/zzyyCNxfcxfeOGFJOus6RL3vDt27FgrXbq05c+f39q1a2eff/55XB/zlLY7O1/nqTnm8XKdn4ttj5drfWoa73GHDh2yQYMGuetYLd2qVatmCxcuTNM8T5486eZRtGhRK1iwoPXq1cv27t2bthX3kOXMnDnTy5s3r/f88897n376qXfLLbd4F154obd3795kp9u1a5dXtmxZ73e/+53XrVu30PBDhw557dq182bNmuVt27bNW7Nmjde0aVOvUaNGEdNXqFDBe+CBB7wffvgh9Dl27JgXz9su/fv39zp27BixXQcPHoxIo/H169f3PvjgA2/lypVelSpVvL59+3rxvN3h26uP5p0jRw5v586dcX3MZ8yY4RUuXDhinffs2RORZuLEiV6RIkW8uXPneh9//LHXtWtXr1KlSt7PP/8ct8c8pe3Oztd5ao55PFzn52rb4+Fan5nG7T516pTXuHFj75prrvFWrVrl7nXLly/3PvroozTN87bbbvPKlSvnLV261Fu/fr3XvHlzr2XLlmladwKlLEg3t0GDBoW+nzlzxitTpow3YcKEwGl+/fVXd/CfffZZd8NI/NBMbO3atXptsvf1119HXEiPP/64l922PaX98dlnn7l9sW7dutCwt99+291ovvvuOy+7HHONv/LKKyOGxeMx14NDQVCQs2fPeqVKlfIeeeSRiCAiX7583r/+9a+4PeYpbXd2vs5Ts+3xcJ1n1nHPitd60zRu91NPPeVVrlzZO336dLrnqes+T5483uzZs0Nptm7d6s4D/ZBILYrespjTp0/bhg0bXFFB+Hvl9H3NmjWB0z3wwANWokQJl5WcGocPH3ZZr8p+DqesWGVRNmzY0GXb/vrrr5Ydtn358uUuTfXq1e3222+3H3/8MTRO89Z+aNy4cWiYlqllf/jhh5YdjrmymhcsWBA1bTwecxWXVKhQwb0gs1u3bvbpp5+Gxu3atcv27NkTMU+910nZ8v484/WYJ7fd2f06T822Z+XrPLOOe1a81k+nY7vfeusta9GihSs2K1mypNWpU8cefPBBO3PmTKrnqfEqkg5PU6NGDStfvnyy+zsxXoqbxRw4cMCdCDoxwun7tm3bok6zatUqe+655+yjjz5K1TJUZqu6DH379o14seCQIUPssssus4svvthWr15to0aNsh9++MEee+wxi+dtVz2Fnj17WqVK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lSSQnZGjBGRzy5Yts08++cR69erlvquS9549eyx37twuWAj/qPK1qBJ14hyD1Eyn3ImlS5cGrku0+SZWs2ZNe//99yOG6bsqBmc0BTvy888/h5atysz/Vxr232UrELrkkkvcd+XKPPTQQ67SunJwVCE8FrQ/FBDt3r07yfFQro5PFbn79+9vr7zyiuuaYfr06aleRtCxqFatWmjfJU6vloQKIH0ffPBBurcRyArIUQKykVOnTrlgRsHI3r17bdGiRa5VlLoH6Nevn0vTrl07VxzSvXt3e/jhh91DT83llTOhVlcq3lHrJhWpqZhKAYIChdRMN27cOJdroSKpPn36uNZiys24++673bI1X+XGaJwe8n6AFW7EiBF2ww03WMOGDd0yFZCoGb2a1f9WKqrS/hHtn/Hjx7vWWSp+81uwKZgYPHiwC4C2b9/utmn48OGWM2dOV0R300032ZAhQ1xujfaNWvhde+21dt1111lm0jH561//6loRKgeoVatWdvjwYRfIqNhOwdHYsWNdKzm1WtO5MX/+fBfMpNZf/vIXt33aT+rvSkHklClT7Mknn4yaXsdL54WW/cgjj9iRI0fsnnvuycCtBmIg1pWkAGRcZW5d0vqoQm3x4sVdK7Xnn38+VBHZd+TIEW/w4MFemTJlXAXncuXKuUrYu3fvDlUU7tWrl3fhhRe6+fmtnFKaTubMmeNaWuXNm9e1GOvZs2do3Jo1a1zLLbXM8m8/iStzy5NPPulVrlzZLUMVrl966aWI8dEqB2se4a2xolXm9vePPhdddJEbFt6aTtRiTK3atP6lSpXy7r777lArMFWMrlu3bkSFdrXKu/jii12Lud9SmTu57fErc//0008RaVTpfPLkya4CuvaVjnmHDh1cazMZP368q8SvlmpaR1X2//LLLwPXR/PXMC3P99prr7nK25p/+fLlvUceeSTZCvpqHdeqVSu3/3Ts1CKPytyIZzn0TywCNAAAgKyOOkoAAAABCJQAAAACECgBAAAEIFACAAAIQKAEAAAQgEAJAAAgAIESAABAAAIlAACAAARKAAAAAQiUAAAAAhAoAQAABCBQAgAAsOj+H/J0DCCfyHwoAAAAAElFTkSuQmCC",
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAkAAAAHHCAYAAABXx+fLAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjcsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvTLEjVAAAAAlwSFlzAAAPYQAAD2EBqD+naQAAQJZJREFUeJzt3Ql4VOXd/vFfCEvYEZBVdlREFGQVF0ALokUELRX9W4m4oEWwFjcQAREsdSlFWRWLVGlf6IugFBGsgBQURKEIIgSKKIsCocoqm3D+1/2810xnQhImIckkeb6f6xplzpw5c84zJzP3PNtJCIIgMAAAAI8UifcOAAAA5DUCEAAA8A4BCAAAeIcABAAAvEMAAgAA3iEAAQAA7xCAAACAdwhAAADAOwQgAADgHQIQctWhQ4fs3nvvtWrVqllCQoI9/PDDbvnu3butR48eVqlSJbd8zJgxBf6Ysuuuu+6yunXrWkH19NNPu3IAOBdQkBCAkGVTp051X3gZ3VasWBFe93e/+51b/9e//rW9+eabduedd7rlv/3tb23BggU2aNAgt/z666/P8XdCr/3222/nynbTO6b0KNjceOONlt8odGX2HoZuWg/5S9++fa1IkSL2/fffRy3XfS0vUaKEHT16NOqxr776yr2fTz75pOX38y8pKcnyK/09R+5r6dKlrXXr1vbGG29ke5vz5s1zwRF5r2gcXhOFxDPPPGP16tU7bXnDhg3D/160aJFdfvnlNmzYsKh1tLxbt2726KOP5tr+Kaiolql79+45ut2Mjim7Jk+ebKdOnbK8dP/991vHjh3D97du3WpDhw61Pn362NVXXx1e3qBBgzzdL5zZVVddZRMnTrSPPvrIunbtGl7+8ccfuwB04sQJ++yzz9x6IVo39Nz8QkHttddeO215YmKi5WfNmjWzRx55xP37u+++c8eQnJxsx44ds/vuuy9bAWj8+PGEoDggACHbbrjhBmvZsmWm6+zZs8caN26c7vIKFSoUyNLP6Jiyq1ixYpbX2rZt624h+sJUANKyX/3qVxZPP/74o5UqVSqu+5CfhULMsmXLogKQQs6ll15qR44ccY9Fhh3dVzi64oorzuq1f/rpJxfWixcvbmeraNGi2TrXDh8+7GpecuPcieX4atasGbXfqs2qX7++/fGPf8xWAEL80ASGXPHhhx+6KmLVLLz77rvhKuNQ81kQBO5XT2h5yL59+1yfmlq1arlfiKpNeu65506rIdH9l156yS655BJXZX7uuee6ZjR9kYu2qQ/KP//5zzE35yjY3HPPPVa1alW3zaZNm7rnn+mYvv766xztA6Ttabsvvviivfrqq64WRmXRqlUr+/TTT097/saNG11NV8WKFd1+K5TOmTPHcsL//u//WosWLaxkyZJWuXJl98G/c+fOmJ47bdq08HO1b7fddptt3749ap0OHTpYkyZNbNWqVdauXTv35RVqpnnnnXesS5cuVqNGDXf8KocRI0bYyZMn093Gl19+addcc43bhr6knn/++dP2SU1Dam644IILXFlVr17dbrnlFtuyZUvUuaU+aRdffLFbR+eDasx++OGHmGsIVYumL2mFfNV0btiwId2+Mv/+97/d+6/1ypcvb71793Zf4pmpXbu2+/sI1eqE6P6VV17pQk56j+l4Qj86znSupz0PVR6h81DlHApVOif1fD32yiuvWE4LfV4sWbLENf1VqVLFzjvvvDOeOzlxfLHSZ0+jRo2iziFZunSp/fKXv3Tvl7ar90xN/wqoIXrv9TkokU1rOXUuInPUACHb9u/fb3v37o1apj9edWy+6KKLXP8Y/cHrAytUZXzZZZeF+8106tTJevXqFX6uPvjbt2/vvmD1R64PDlXrq5+QqpojO0rrw00fjqqFUodk/XLTB476HykA6DW0XO3zatY5U3OOPpT0gaovpH79+rmmPX356wNKoew3v/lNhsekD8Dc8Ne//tUOHjzoykLlqi90fVmrP0eo1mj9+vXuS09f+AMHDnRfun/7299cs99bb71lN998c7ZfX+WrL2R9yY0aNcp1XFfo1Jfpv/71r0xr8J599lkbMmSI3Xrrre59SE1NtbFjx7ovqrTP/c9//uPeRwUkBSx9yIdev0yZMjZgwAD3fwUL1VIdOHDAXnjhhajX0xeCArDKR685c+ZMe+KJJ1xA1rZFwUn9sRYuXOheS++pyvcf//iHffHFF+HzQ+UdOvaHHnrIBd5x48a5/daxZ1Zj98EHH7jXU42AQo7OKx233qPVq1ef1tld+6pzTeWrx9Wcoi95hf7MqHZn1qxZrtlFX67Hjx934Vj90vR39Pjjj7sfGTpvVDb6Un/ggQdiPtcjvf766y446u9Ir6Uwu27dOrvuuuvcua/j1N+fmoRD712s0n5+iGpfypUrF7VM4UevpfdfP2wyO3dy4viyQse+Y8cOO+ecc6KW6zX1Xug90WfiypUr3bmgdfVY6Fz79ttv3Tmoz5a0zuZcRAwCIItef/31QKdOercSJUpErVunTp2gS5cup21D6z744INRy0aMGBGULl062LRpU9TygQMHBomJicG2bdvc/UWLFrnnP/TQQ6dt99SpU+F/a1vJyckxHdOYMWPcNqdNmxZedvz48aBt27ZBmTJlggMHDpzxmNITy7raR60XsnXrVrcvlSpVCr7//vvw8nfeecct//vf/x5e9rOf/Sy45JJLgqNHj0aVwRVXXBGcf/75Qaw+/fRTt229t6Fjr1KlStCkSZPgyJEj4fXmzp3r1hs6dGh42bBhw9yykK+//tq9X88++2zUa6xbty4oWrRo1PL27du7506aNOm0ffrxxx9PW3b//fcHpUqVijre0DbeeOON8LJjx44F1apVC37xi1+El02ZMsWtN3r06AzPm6VLl7p1/vKXv0Q9Pn/+/HSXp9WsWTNXbv/5z3/Cyz7//POgSJEiQa9evU4rs7vvvjvq+TfffLN7389k/Pjx7vnaX1m+fLm7/8033wRffvml+/f69euj3rPQvsd6rofOw3LlygV79uyJev3u3bsHSUlJ7vVC9Lp632P5WtE5n9FnSOfOnU/7rLnqqquCn376KWobGZ07OXF8GdHf6XXXXRekpqa6m87pO++8M93Ps/TO31GjRgUJCQlR5abnpVdmZ3su4sxoAkO2qepWv1wib++99162t6dfRWo60C8p/TIM3dRZV7/e//nPf7r1VLOhX7bpdULO7nBsdUTUsPbbb789vEy/rvSrS8PeVQWf13r27Bn1qzLUOVk1QKFRP6oVUS2CajJC5aVfxZ07d7bNmzfH3FyVlpoS1YygX96Ro3LUJKXqfjUBZkQ1E6q6135Fvo8q3/PPP98WL14ctb5+desXblpqOgsJHZ/KQL+q1ewXSTVEkf0yVIug2r9QWYXOGzXj9e/fP8PzRuegmqJUOxm572rK02uk3fdIqqVcs2aNq2mIrEVQvxxtT+dYWqFamRAdn94/1XLF2g9IVBugWkDVmur90euHmsHSdoDO6rn+i1/8IqqWU3+LGsGpWka9XohqSHXexUrnVdrPD91+//vfn7au+tak1zk6vXPnbI/vTN5//323vm6qYVTN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16+3go4+QAAAFO7v72wFoBDVAumSGO+995698sordujQIWaCBgAAhacTdCjwrFixwnWIVs3PJ598YrVq1bJ27drZuHHjrH379me77wAAALku5gCkvj4KPOoIraBz//3321//+lerXr167u4hAABAvALQ0qVLXdhREOrQoYMLQZUqVcrp/QEAAMg/w+D37dtnr776qpUqVcqee+45d+X3Sy65xPr162czZ8601NTU3N1TAACAHJLtTtC69MWyZcvC/YE+//xzO//88+2LL76wgo5RYAAAFO7v72xfDLV06dJWsWJFdzvnnHOsaNGitmHDhuxuDgAAIP/1ATp16pR99tlnrrZHtT4a/n748GGrWbOmmwRx/Pjx7v8AAACFJgBVqFDBBZ5q1aq5oPPHP/7RdYbWJTIAAAAKZQB64YUXXPC54IILcnePAAAA8ksA0rw/AAAAhUG2O0EDAAAUVAQgAADgHQIQAADwDgEIAAB4J18EIM0hVLduXUtKSrI2bdrYypUrM1x31qxZ1rJlSzcsX5MxNmvWzN58880M13/ggQcsISHBxowZk0t7DwAACpq4B6AZM2bYgAEDbNiwYbZ69Wpr2rSpde7c2fbs2ZPu+pp5evDgwbZ8+XJbu3at9e7d290WLFhw2rqzZ8+2FStWuOuWAQAA5JsANHr0aLvvvvtciGncuLFNmjTJXXB1ypQp6a6vyRdvvvlmu+iii9wkjL/5zW/s0ksvddcli7Rz507r37+//eUvf7FixYrl0dEAAICCIK4B6Pjx47Zq1Srr2LHjf3eoSBF3XzU8Z6LruC5cuNBSUlKsXbt2UZftuPPOO+2xxx6ziy++ONf2HwAAFPKJEHPD3r177eTJk1a1atWo5bq/cePGDJ+nq7zqGmTHjh2zxMREmzBhgnXq1Cn8+HPPPecuzvrQQw/FtB/ajm6RV5MFAACFV1wDUHaVLVvW1qxZY4cOHXI1QOpDVL9+fdc8phqll156yfUnUufnWIwaNcqGDx+e6/sNAADyh7g2gVWuXNnV4OzevTtque7roqsZUTNZw4YN3QiwRx55xHr06OFCjCxdutR1oK5du7arBdLtm2++cetppFl6Bg0a5GqVQrft27fn8JECAID8JK4BqHjx4taiRQtXixPZf0f327ZtG/N29JxQE5b6/mh0mGqIQjeNAlN/oPRGikmJEiWsXLly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C1kZ3fQ4AJwJV4MHkKNy+4rps2bNcleMlu3bt1vr1q3tgw8+sIsvvtgty+2LdBbmq6ADPqEGCECuNYF9/fXX7r5CyzXXXGOlSpWypk2b2vLly6Oes2zZMrv66qutZMmSVqtWLXvooYfs8OHD6W6/YsWKLmTppivbS6VKlcLLFi9e7MJQiRIlXHPXH/7wh0z3d9++fXbvvfe6bZUrV86uvfZa+/zzz8OPP/3009asWTN77bXX3NW7k5KS3PL58+fbVVddZRUqVHCvf+ONN9qWLVvCz4v12D/66CNXa6XHzznnHOvcubP98MMP4atfjxo1yr2uykbPnzlzZhbfEQDpIQAByHWDBw+2Rx991NasWWMXXHCB3X777fbTTz+5xxQarr/+evvFL35ha9eutRkzZrhA1K9fvyy/zqpVq+zWW2+12267zdatW+fCy5AhQ2zq1KkZPueXv/yl7dmzx9577z33/ObNm9vPfvYz+/7778Pr/Pvf/7a33nrLhRkdgyigDRgwwD777DNbuHChFSlSxG6++WYXWmI9di3TazVu3NgFIx13165d7eTJk+5xhZ833njDJk2aZOvXr7ff/va39qtf/cqWLFmS5bIBkEaOXloVQKGnqzp369Ytw8f1sTJ79mz3761bt7r7r732Wvjx9evXu2UbNmxw9++5556gT58+UdtYunRpUKRIkeDIkSOZ7kto+//617/c/f/3//5f0KlTp6h1HnvssaBx48bh+7rC9B//+Mfw65QrVy44evRo1HMaNGgQvPLKK+7fw4YNC4oVKxbs2bMn031JTU11+7Ju3bqYj/32228PrrzyynS3p30qVaqUu2p2JJWXngfg7FADBCDXXXrppeF/V69e3f1ftS6i5ibV0JQpUyZ8UzOQalK2bt2apdfZsGGDXXnllVHLdH/z5s3hWpVIeu1Dhw65JqzI19frRjZn1alTJ9zcFqJtqjanfv36rulMzW2ybdu2mI89VAOUHtU6/fjjj9apU6eofVONUOS+AcgeOkEDyHXFihUL/1v9YiTUVKQAcv/997t+P2nVrl07V/dLr61Q8uGHH572mPr2hJQuXfq0x9VUpWA0efJkq1GjhjueJk2ahDtox3Ls6teT2b7Ju+++azVr1ox6TP2bAJwdAhCAuFKfmy+//NIaNmx41tu66KKLXKfiSLqvvjeJiYnpvvauXbusaNGi4RqcWPznP/+xlJQUF37UeVvUfyerVDuk/kPDhw8/7TH1C1LQUY1S+/bts7xtAJkjAAHIsv3794c7A4eoGUkjuLLqiSeesMsvv9x1etZoLNW2KBD94x//sHHjxmVpW4888oi1atXKRowYYT179nQdi7WNCRMmpLt+x44drW3btm5eo+eff94FpW+//dbVuqhDc8uWLdN9nkZr6XhfffVVV4OkkDJw4MAsH/ugQYPskksusb59+9oDDzzghtdrFJs6ZleuXNl1nlbHZ9UYacSZyl2BTk1uycnJWX49AP9FAAKQZWoyuuyyy6KW3XPPPW6oeHZqQTSqSaOlVJuiftQNGjRwASarVKPzt7/9zYYOHepCkMLJM8884yZvTI+apObNm+deu3fv3paamuqG0rdr186qVq2a4etoxNf06dNds52avS688EJ7+eWXszwJowLX+++/b08++aSbz0hNYm3atHF9i0THoL5HGg321VdfuWY5HaPWB3B2EtQT+iy3AQAAUKAwCgwAAHiHAAQAALxDAAIAAN4hAAEAAO8QgAAAgHcIQAAAwDsEIAAA4B0CEAAA8A4BCAAAeIcABAAAvEMAAgAA3iEAAQAA7/x/AMWQdXKJnMMAAAAASUVORK5CYII=",
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import matplotlib.pyplot as plt\n",
"\n",
"plt.scatter(df[\"config/text_det_box_thresh\"], df[\"CER\"])\n",
"plt.xlabel(\"Detection Box Threshold\")\n",
"plt.ylabel(\"CER\")\n",
"plt.title(\"Effect of Detection Threshold on Character Error Rate\")\n",
"plt.show()\n",
"\n",
"plt.scatter(df[\"config/line_tolerance\"], df[\"WER\"])\n",
"plt.xlabel(\"Line Tolerance\")\n",
"plt.ylabel(\"WER\")\n",
"plt.title(\"Effect of Line Tolerance on Word Error Rate\")\n",
"plt.show()\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv (3.11.9)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.9"
}
},
"nbformat": 4,
"nbformat_minor": 5
}