<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="es"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://dev-jcgi.github.io/blog/feed.xml" rel="self" type="application/atom+xml" /><link href="https://dev-jcgi.github.io/blog/" rel="alternate" type="text/html" hreflang="es" /><updated>2026-01-29T22:28:54-06:00</updated><id>https://dev-jcgi.github.io/blog/feed.xml</id><title type="html">AI Tech Blog</title><subtitle>Explorando el Futuro de la Inteligencia Artificial.  Noticias, tutoriales y análisis sobre IA, Machine Learning, Deep Learning y tecnologías emergentes.</subtitle><author><name>AI Tech Team</name></author><entry><title type="html">YOLO v8: Object Detection en Tiempo Real</title><link href="https://dev-jcgi.github.io/blog/blog/2026/01/29/yolo-object-detection/" rel="alternate" type="text/html" title="YOLO v8: Object Detection en Tiempo Real" /><published>2026-01-29T09:00:00-06:00</published><updated>2026-01-29T09:00:00-06:00</updated><id>https://dev-jcgi.github.io/blog/blog/2026/01/29/yolo-object-detection</id><content type="html" xml:base="https://dev-jcgi.github.io/blog/blog/2026/01/29/yolo-object-detection/"><![CDATA[<p><strong>YOLO</strong> (You Only Look Once) revolucionó Computer Vision al hacer object detection en <strong>un solo forward pass</strong>: 3ms de latency vs 2 segundos de R-CNN.</p>

<h2 id="yolo-v8-el-estado-del-arte">YOLO v8: El Estado del Arte</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">ultralytics</span> <span class="kn">import</span> <span class="n">YOLO</span>

<span class="c1"># Cargar modelo pre-entrenado
</span><span class="n">model</span> <span class="o">=</span> <span class="n">YOLO</span><span class="p">(</span><span class="s">'yolov8n.pt'</span><span class="p">)</span>  <span class="c1"># nano (más rápido)
# model = YOLO('yolov8s.pt')  # small
# model = YOLO('yolov8m.pt')  # medium
# model = YOLO('yolov8l.pt')  # large
# model = YOLO('yolov8x.pt')  # xlarge (más preciso)
</span>
<span class="c1"># Detectar objetos
</span><span class="n">results</span> <span class="o">=</span> <span class="n">model</span><span class="p">(</span><span class="s">'image.jpg'</span><span class="p">)</span>

<span class="c1"># Visualizar
</span><span class="n">results</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">show</span><span class="p">()</span>

<span class="c1"># Acceder a detecciones
</span><span class="k">for</span> <span class="n">box</span> <span class="ow">in</span> <span class="n">results</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">boxes</span><span class="p">:</span>
    <span class="n">class_id</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">box</span><span class="p">.</span><span class="n">cls</span><span class="p">)</span>
    <span class="n">confidence</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">box</span><span class="p">.</span><span class="n">conf</span><span class="p">)</span>
    <span class="n">bbox</span> <span class="o">=</span> <span class="n">box</span><span class="p">.</span><span class="n">xyxy</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">tolist</span><span class="p">()</span>  <span class="c1"># [x1, y1, x2, y2]
</span>    
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Detected: </span><span class="si">{</span><span class="n">model</span><span class="p">.</span><span class="n">names</span><span class="p">[</span><span class="n">class_id</span><span class="p">]</span><span class="si">}</span><span class="s"> (</span><span class="si">{</span><span class="n">confidence</span><span class="si">:</span><span class="p">.</span><span class="mi">2</span><span class="n">f</span><span class="si">}</span><span class="s">)"</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"BBox: </span><span class="si">{</span><span class="n">bbox</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="comparación-de-modelos">Comparación de Modelos</h3>

<table>
  <thead>
    <tr>
      <th>Model</th>
      <th>Size (MB)</th>
      <th>mAP@50-95</th>
      <th>Speed (ms)</th>
      <th>Params (M)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>YOLOv8n</td>
      <td>6</td>
      <td>37.3</td>
      <td>1.5</td>
      <td>3.2</td>
    </tr>
    <tr>
      <td>YOLOv8s</td>
      <td>22</td>
      <td>44.9</td>
      <td>2.8</td>
      <td>11.2</td>
    </tr>
    <tr>
      <td>YOLOv8m</td>
      <td>52</td>
      <td>50.2</td>
      <td>5.9</td>
      <td>25.9</td>
    </tr>
    <tr>
      <td>YOLOv8l</td>
      <td>87</td>
      <td>52.9</td>
      <td>9.1</td>
      <td>43.7</td>
    </tr>
    <tr>
      <td>YOLOv8x</td>
      <td>136</td>
      <td>53.9</td>
      <td>12.4</td>
      <td>68.2</td>
    </tr>
  </tbody>
</table>

<p><strong>Trade-off:</strong> nano para tiempo real (30+ FPS), xlarge para máxima precisión.</p>

<h2 id="arquitectura-yolo-v8">Arquitectura YOLO v8</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Input (640×640)
    ↓
[Backbone: CSPDarknet]
    ↓ (1× feature maps)
    ↓ (2× feature maps)
    ↓ (4× feature maps)
    ↓
[Neck: PANet + FPN]
    ↓ (multi-scale features)
    ↓
[Head: Decoupled]
    ├─→ [Classification] (80 clases COCO)
    └─→ [Regression] (bboxes)
    ↓
Output: boxes, classes, confidences
</code></pre></div></div>

<p><strong>Mejoras vs YOLOv5:</strong></p>
<ul>
  <li>❌ Anchor-free: no necesita anchor boxes predefinidos</li>
  <li>✅ Decoupled head: separate classification y regression</li>
  <li>✅ TaskAlignedAssigner: mejor loss function</li>
  <li>✅ C2f modules: más eficiente que C3</li>
</ul>

<h2 id="detección-básica">Detección Básica</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">ultralytics</span> <span class="kn">import</span> <span class="n">YOLO</span>
<span class="kn">import</span> <span class="nn">cv2</span>

<span class="n">model</span> <span class="o">=</span> <span class="n">YOLO</span><span class="p">(</span><span class="s">'yolov8n.pt'</span><span class="p">)</span>

<span class="c1"># 1. Imagen única
</span><span class="n">results</span> <span class="o">=</span> <span class="n">model</span><span class="p">(</span><span class="s">'path/to/image.jpg'</span><span class="p">)</span>

<span class="c1"># 2. Múltiples imágenes
</span><span class="n">results</span> <span class="o">=</span> <span class="n">model</span><span class="p">([</span><span class="s">'image1.jpg'</span><span class="p">,</span> <span class="s">'image2.jpg'</span><span class="p">])</span>

<span class="c1"># 3. Video
</span><span class="n">results</span> <span class="o">=</span> <span class="n">model</span><span class="p">(</span><span class="s">'video.mp4'</span><span class="p">,</span> <span class="n">stream</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="k">for</span> <span class="n">r</span> <span class="ow">in</span> <span class="n">results</span><span class="p">:</span>
    <span class="n">r</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>  <span class="c1"># Mostrar frame por frame
</span>
<span class="c1"># 4. Webcam en tiempo real
</span><span class="n">results</span> <span class="o">=</span> <span class="n">model</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">stream</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>  <span class="c1"># 0 = default webcam
</span><span class="k">for</span> <span class="n">r</span> <span class="ow">in</span> <span class="n">results</span><span class="p">:</span>
    <span class="n">frame</span> <span class="o">=</span> <span class="n">r</span><span class="p">.</span><span class="n">plot</span><span class="p">()</span>  <span class="c1"># Dibujar detecciones
</span>    <span class="n">cv2</span><span class="p">.</span><span class="n">imshow</span><span class="p">(</span><span class="s">'YOLO'</span><span class="p">,</span> <span class="n">frame</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">cv2</span><span class="p">.</span><span class="n">waitKey</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0xFF</span> <span class="o">==</span> <span class="nb">ord</span><span class="p">(</span><span class="s">'q'</span><span class="p">):</span>
        <span class="k">break</span>

<span class="c1"># 5. YouTube stream
</span><span class="n">results</span> <span class="o">=</span> <span class="n">model</span><span class="p">(</span><span class="s">'https://www.youtube.com/watch?v=dQw4w9WgXcQ'</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="procesamiento-de-detecciones">Procesamiento de Detecciones</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">results</span> <span class="o">=</span> <span class="n">model</span><span class="p">(</span><span class="s">'image.jpg'</span><span class="p">)</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">results</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>

<span class="c1"># Bounding boxes
</span><span class="n">boxes</span> <span class="o">=</span> <span class="n">result</span><span class="p">.</span><span class="n">boxes</span><span class="p">.</span><span class="n">xyxy</span><span class="p">.</span><span class="n">cpu</span><span class="p">().</span><span class="n">numpy</span><span class="p">()</span>  <span class="c1"># [[x1, y1, x2, y2], ...]
</span><span class="n">confidences</span> <span class="o">=</span> <span class="n">result</span><span class="p">.</span><span class="n">boxes</span><span class="p">.</span><span class="n">conf</span><span class="p">.</span><span class="n">cpu</span><span class="p">().</span><span class="n">numpy</span><span class="p">()</span>  <span class="c1"># [0.95, 0.87, ...]
</span><span class="n">classes</span> <span class="o">=</span> <span class="n">result</span><span class="p">.</span><span class="n">boxes</span><span class="p">.</span><span class="n">cls</span><span class="p">.</span><span class="n">cpu</span><span class="p">().</span><span class="n">numpy</span><span class="p">()</span>  <span class="c1"># [0, 5, 2, ...]  (class IDs)
</span>
<span class="c1"># Filtrar por confidence
</span><span class="n">high_conf</span> <span class="o">=</span> <span class="n">boxes</span><span class="p">[</span><span class="n">confidences</span> <span class="o">&gt;</span> <span class="mf">0.7</span><span class="p">]</span>

<span class="c1"># Filtrar por clase (ej: solo personas = clase 0)
</span><span class="n">person_boxes</span> <span class="o">=</span> <span class="n">boxes</span><span class="p">[</span><span class="n">classes</span> <span class="o">==</span> <span class="mi">0</span><span class="p">]</span>

<span class="c1"># Guardar imagen con detecciones
</span><span class="n">annotated</span> <span class="o">=</span> <span class="n">result</span><span class="p">.</span><span class="n">plot</span><span class="p">()</span>
<span class="n">cv2</span><span class="p">.</span><span class="n">imwrite</span><span class="p">(</span><span class="s">'output.jpg'</span><span class="p">,</span> <span class="n">annotated</span><span class="p">)</span>

<span class="c1"># Guardar boxes como JSON
</span><span class="kn">import</span> <span class="nn">json</span>
<span class="n">detections</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">box</span><span class="p">,</span> <span class="n">conf</span><span class="p">,</span> <span class="n">cls</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">boxes</span><span class="p">,</span> <span class="n">confidences</span><span class="p">,</span> <span class="n">classes</span><span class="p">):</span>
    <span class="n">detections</span><span class="p">.</span><span class="n">append</span><span class="p">({</span>
        <span class="s">"class"</span><span class="p">:</span> <span class="n">model</span><span class="p">.</span><span class="n">names</span><span class="p">[</span><span class="nb">int</span><span class="p">(</span><span class="n">cls</span><span class="p">)],</span>
        <span class="s">"confidence"</span><span class="p">:</span> <span class="nb">float</span><span class="p">(</span><span class="n">conf</span><span class="p">),</span>
        <span class="s">"bbox"</span><span class="p">:</span> <span class="n">box</span><span class="p">.</span><span class="n">tolist</span><span class="p">()</span>
    <span class="p">})</span>
<span class="n">json</span><span class="p">.</span><span class="n">dump</span><span class="p">(</span><span class="n">detections</span><span class="p">,</span> <span class="nb">open</span><span class="p">(</span><span class="s">'detections.json'</span><span class="p">,</span> <span class="s">'w'</span><span class="p">))</span>
</code></pre></div></div>

<h2 id="entrenar-modelo-custom">Entrenar Modelo Custom</h2>

<h3 id="1-preparar-dataset">1. Preparar Dataset</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dataset/
├── images/
│   ├── train/
│   │   ├── img001.jpg
│   │   ├── img002.jpg
│   ├── val/
│   │   ├── img050.jpg
│   │   ├── img051.jpg
├── labels/
│   ├── train/
│   │   ├── img001.txt
│   │   ├── img002.txt
│   ├── val/
│       ├── img050.txt
│       ├── img051.txt
</code></pre></div></div>

<p><strong>Formato YOLO de labels</strong> (<code class="language-plaintext highlighter-rouge">img001.txt</code>):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># class x_center y_center width height (normalized 0-1)
0 0.716797 0.395833 0.216406 0.147222
1 0.357031 0.422222 0.089844 0.177778
</code></pre></div></div>

<h3 id="2-dataset-yaml">2. Dataset YAML</h3>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># dataset.yaml</span>
<span class="na">path</span><span class="pi">:</span> <span class="s">/path/to/dataset</span>  <span class="c1"># Root del dataset</span>
<span class="na">train</span><span class="pi">:</span> <span class="s">images/train</span>
<span class="na">val</span><span class="pi">:</span> <span class="s">images/val</span>
<span class="na">test</span><span class="pi">:</span> <span class="s">images/test</span>  <span class="c1"># opcional</span>

<span class="c1"># Classes</span>
<span class="na">names</span><span class="pi">:</span>
  <span class="na">0</span><span class="pi">:</span> <span class="s">defect_scratch</span>
  <span class="na">1</span><span class="pi">:</span> <span class="s">defect_dent</span>
  <span class="na">2</span><span class="pi">:</span> <span class="s">defect_crack</span>
  <span class="na">3</span><span class="pi">:</span> <span class="s">OK</span>

<span class="na">nc</span><span class="pi">:</span> <span class="m">4</span>  <span class="c1"># Número de clases</span>
</code></pre></div></div>

<h3 id="3-entrenar">3. Entrenar</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">ultralytics</span> <span class="kn">import</span> <span class="n">YOLO</span>

<span class="c1"># Cargar modelo pre-entrenado (transfer learning)
</span><span class="n">model</span> <span class="o">=</span> <span class="n">YOLO</span><span class="p">(</span><span class="s">'yolov8n.pt'</span><span class="p">)</span>

<span class="c1"># Entrenar
</span><span class="n">results</span> <span class="o">=</span> <span class="n">model</span><span class="p">.</span><span class="n">train</span><span class="p">(</span>
    <span class="n">data</span><span class="o">=</span><span class="s">'dataset.yaml'</span><span class="p">,</span>
    <span class="n">epochs</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span>
    <span class="n">imgsz</span><span class="o">=</span><span class="mi">640</span><span class="p">,</span>
    <span class="n">batch</span><span class="o">=</span><span class="mi">16</span><span class="p">,</span>
    <span class="n">device</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span>  <span class="c1"># GPU 0 (o 'cpu')
</span>    <span class="n">workers</span><span class="o">=</span><span class="mi">8</span><span class="p">,</span>
    <span class="n">patience</span><span class="o">=</span><span class="mi">20</span><span class="p">,</span>  <span class="c1"># Early stopping
</span>    <span class="n">save</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
    <span class="n">project</span><span class="o">=</span><span class="s">'defect_detection'</span><span class="p">,</span>
    <span class="n">name</span><span class="o">=</span><span class="s">'exp1'</span>
<span class="p">)</span>

<span class="c1"># Resultados guardados en: defect_detection/exp1/
# - weights/best.pt
# - weights/last.pt
# - results.png (gráficas)
# - confusion_matrix.png
</span></code></pre></div></div>

<h3 id="hiperparámetros">Hiperparámetros</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">model</span><span class="p">.</span><span class="n">train</span><span class="p">(</span>
    <span class="n">data</span><span class="o">=</span><span class="s">'dataset.yaml'</span><span class="p">,</span>
    <span class="n">epochs</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span>
    <span class="n">imgsz</span><span class="o">=</span><span class="mi">640</span><span class="p">,</span>
    <span class="n">batch</span><span class="o">=</span><span class="mi">16</span><span class="p">,</span>
    
    <span class="c1"># Learning rate
</span>    <span class="n">lr0</span><span class="o">=</span><span class="mf">0.01</span><span class="p">,</span>        <span class="c1"># Initial LR
</span>    <span class="n">lrf</span><span class="o">=</span><span class="mf">0.01</span><span class="p">,</span>        <span class="c1"># Final LR (lr0 * lrf)
</span>    <span class="n">momentum</span><span class="o">=</span><span class="mf">0.937</span><span class="p">,</span>
    <span class="n">weight_decay</span><span class="o">=</span><span class="mf">0.0005</span><span class="p">,</span>
    
    <span class="c1"># Augmentation
</span>    <span class="n">hsv_h</span><span class="o">=</span><span class="mf">0.015</span><span class="p">,</span>     <span class="c1"># Hue
</span>    <span class="n">hsv_s</span><span class="o">=</span><span class="mf">0.7</span><span class="p">,</span>       <span class="c1"># Saturation
</span>    <span class="n">hsv_v</span><span class="o">=</span><span class="mf">0.4</span><span class="p">,</span>       <span class="c1"># Value
</span>    <span class="n">degrees</span><span class="o">=</span><span class="mf">10.0</span><span class="p">,</span>    <span class="c1"># Rotation (±deg)
</span>    <span class="n">translate</span><span class="o">=</span><span class="mf">0.1</span><span class="p">,</span>   <span class="c1"># Translation (±%)
</span>    <span class="n">scale</span><span class="o">=</span><span class="mf">0.5</span><span class="p">,</span>       <span class="c1"># Scaling (±%)
</span>    <span class="n">flipud</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>      <span class="c1"># Vertical flip
</span>    <span class="n">fliplr</span><span class="o">=</span><span class="mf">0.5</span><span class="p">,</span>      <span class="c1"># Horizontal flip
</span>    <span class="n">mosaic</span><span class="o">=</span><span class="mf">1.0</span><span class="p">,</span>      <span class="c1"># Mosaic augmentation
</span>    
    <span class="c1"># Regularization
</span>    <span class="n">dropout</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>
    <span class="n">label_smoothing</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span>
    
    <span class="c1"># Device
</span>    <span class="n">device</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span>        <span class="c1"># GPU
</span>    <span class="n">workers</span><span class="o">=</span><span class="mi">8</span><span class="p">,</span>
    <span class="n">cache</span><span class="o">=</span><span class="bp">True</span>       <span class="c1"># Cache images in RAM
</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="4-evaluar">4. Evaluar</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Cargar best model
</span><span class="n">model</span> <span class="o">=</span> <span class="n">YOLO</span><span class="p">(</span><span class="s">'defect_detection/exp1/weights/best.pt'</span><span class="p">)</span>

<span class="c1"># Evaluar en validation set
</span><span class="n">metrics</span> <span class="o">=</span> <span class="n">model</span><span class="p">.</span><span class="n">val</span><span class="p">()</span>

<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"mAP@50: </span><span class="si">{</span><span class="n">metrics</span><span class="p">.</span><span class="n">box</span><span class="p">.</span><span class="n">map50</span><span class="si">:</span><span class="p">.</span><span class="mi">3</span><span class="n">f</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"mAP@50-95: </span><span class="si">{</span><span class="n">metrics</span><span class="p">.</span><span class="n">box</span><span class="p">.</span><span class="nb">map</span><span class="si">:</span><span class="p">.</span><span class="mi">3</span><span class="n">f</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Precision: </span><span class="si">{</span><span class="n">metrics</span><span class="p">.</span><span class="n">box</span><span class="p">.</span><span class="n">p</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Recall: </span><span class="si">{</span><span class="n">metrics</span><span class="p">.</span><span class="n">box</span><span class="p">.</span><span class="n">r</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>

<span class="c1"># Test en imágenes específicas
</span><span class="n">results</span> <span class="o">=</span> <span class="n">model</span><span class="p">.</span><span class="n">predict</span><span class="p">(</span><span class="s">'test_images/'</span><span class="p">,</span> <span class="n">save</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="técnicas-avanzadas">Técnicas Avanzadas</h2>

<h3 id="1-data-augmentation">1. Data Augmentation</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">albumentations</span> <span class="kn">import</span> <span class="n">Compose</span><span class="p">,</span> <span class="n">HorizontalFlip</span><span class="p">,</span> <span class="n">RandomBrightnessContrast</span><span class="p">,</span> <span class="n">CLAHE</span>

<span class="n">augment</span> <span class="o">=</span> <span class="n">Compose</span><span class="p">([</span>
    <span class="n">HorizontalFlip</span><span class="p">(</span><span class="n">p</span><span class="o">=</span><span class="mf">0.5</span><span class="p">),</span>
    <span class="n">RandomBrightnessContrast</span><span class="p">(</span><span class="n">p</span><span class="o">=</span><span class="mf">0.2</span><span class="p">),</span>
    <span class="n">CLAHE</span><span class="p">(</span><span class="n">p</span><span class="o">=</span><span class="mf">0.3</span><span class="p">)</span>
<span class="p">])</span>

<span class="c1"># Aplicar antes de training
</span><span class="n">augmented</span> <span class="o">=</span> <span class="n">augment</span><span class="p">(</span><span class="n">image</span><span class="o">=</span><span class="n">image</span><span class="p">,</span> <span class="n">bboxes</span><span class="o">=</span><span class="n">bboxes</span><span class="p">,</span> <span class="n">class_labels</span><span class="o">=</span><span class="n">labels</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="2-ensemble-de-modelos">2. Ensemble de Modelos</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">models</span> <span class="o">=</span> <span class="p">[</span>
    <span class="n">YOLO</span><span class="p">(</span><span class="s">'yolov8n.pt'</span><span class="p">),</span>
    <span class="n">YOLO</span><span class="p">(</span><span class="s">'yolov8s.pt'</span><span class="p">),</span>
    <span class="n">YOLO</span><span class="p">(</span><span class="s">'yolov8m.pt'</span><span class="p">)</span>
<span class="p">]</span>

<span class="c1"># Combinar predicciones
</span><span class="n">all_boxes</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">all_scores</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">model</span> <span class="ow">in</span> <span class="n">models</span><span class="p">:</span>
    <span class="n">results</span> <span class="o">=</span> <span class="n">model</span><span class="p">(</span><span class="s">'image.jpg'</span><span class="p">)</span>
    <span class="n">all_boxes</span><span class="p">.</span><span class="n">extend</span><span class="p">(</span><span class="n">results</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">boxes</span><span class="p">.</span><span class="n">xyxy</span><span class="p">)</span>
    <span class="n">all_scores</span><span class="p">.</span><span class="n">extend</span><span class="p">(</span><span class="n">results</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">boxes</span><span class="p">.</span><span class="n">conf</span><span class="p">)</span>

<span class="c1"># Non-Maximum Suppression (NMS) para eliminar duplicados
</span><span class="kn">from</span> <span class="nn">torchvision.ops</span> <span class="kn">import</span> <span class="n">nms</span>
<span class="n">keep</span> <span class="o">=</span> <span class="n">nms</span><span class="p">(</span><span class="n">all_boxes</span><span class="p">,</span> <span class="n">all_scores</span><span class="p">,</span> <span class="n">iou_threshold</span><span class="o">=</span><span class="mf">0.5</span><span class="p">)</span>
<span class="n">final_boxes</span> <span class="o">=</span> <span class="n">all_boxes</span><span class="p">[</span><span class="n">keep</span><span class="p">]</span>
</code></pre></div></div>

<h3 id="3-test-time-augmentation-tta">3. Test-Time Augmentation (TTA)</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Predecir con múltiples augmentaciones y promediar
</span><span class="n">results</span> <span class="o">=</span> <span class="n">model</span><span class="p">(</span><span class="s">'image.jpg'</span><span class="p">,</span> <span class="n">augment</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="c1"># Internamente hace: original + flipud + fliplr y promedia
</span></code></pre></div></div>

<h3 id="4-tracking-reid">4. Tracking (Reid)</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">ultralytics</span> <span class="kn">import</span> <span class="n">YOLO</span>

<span class="n">model</span> <span class="o">=</span> <span class="n">YOLO</span><span class="p">(</span><span class="s">'yolov8n.pt'</span><span class="p">)</span>

<span class="c1"># Object tracking con ByteTrack
</span><span class="n">results</span> <span class="o">=</span> <span class="n">model</span><span class="p">.</span><span class="n">track</span><span class="p">(</span>
    <span class="s">'video.mp4'</span><span class="p">,</span>
    <span class="n">stream</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
    <span class="n">tracker</span><span class="o">=</span><span class="s">'bytetrack.yaml'</span>  <span class="c1"># o 'botsort.yaml'
</span><span class="p">)</span>

<span class="k">for</span> <span class="n">r</span> <span class="ow">in</span> <span class="n">results</span><span class="p">:</span>
    <span class="k">for</span> <span class="n">box</span> <span class="ow">in</span> <span class="n">r</span><span class="p">.</span><span class="n">boxes</span><span class="p">:</span>
        <span class="n">track_id</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">box</span><span class="p">.</span><span class="nb">id</span><span class="p">)</span>  <span class="c1"># ID único del objeto
</span>        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Object </span><span class="si">{</span><span class="n">track_id</span><span class="si">}</span><span class="s"> at </span><span class="si">{</span><span class="n">box</span><span class="p">.</span><span class="n">xyxy</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="optimización-para-producción">Optimización para Producción</h2>

<h3 id="1-export-a-formatos-optimizados">1. Export a Formatos Optimizados</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">model</span> <span class="o">=</span> <span class="n">YOLO</span><span class="p">(</span><span class="s">'yolov8n.pt'</span><span class="p">)</span>

<span class="c1"># ONNX (universal)
</span><span class="n">model</span><span class="p">.</span><span class="n">export</span><span class="p">(</span><span class="nb">format</span><span class="o">=</span><span class="s">'onnx'</span><span class="p">)</span>

<span class="c1"># TensorRT (NVIDIA GPUs - 3× más rápido)
</span><span class="n">model</span><span class="p">.</span><span class="n">export</span><span class="p">(</span><span class="nb">format</span><span class="o">=</span><span class="s">'engine'</span><span class="p">,</span> <span class="n">device</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>

<span class="c1"># CoreML (iOS/macOS)
</span><span class="n">model</span><span class="p">.</span><span class="n">export</span><span class="p">(</span><span class="nb">format</span><span class="o">=</span><span class="s">'coreml'</span><span class="p">)</span>

<span class="c1"># TFLite (Android/Edge devices)
</span><span class="n">model</span><span class="p">.</span><span class="n">export</span><span class="p">(</span><span class="nb">format</span><span class="o">=</span><span class="s">'tflite'</span><span class="p">)</span>

<span class="c1"># OpenVINO (Intel CPUs)
</span><span class="n">model</span><span class="p">.</span><span class="n">export</span><span class="p">(</span><span class="nb">format</span><span class="o">=</span><span class="s">'openvino'</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="2-inference-con-tensorrt">2. Inference con TensorRT</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Modelo exportado: yolov8n.engine
</span><span class="n">model</span> <span class="o">=</span> <span class="n">YOLO</span><span class="p">(</span><span class="s">'yolov8n.engine'</span><span class="p">)</span>

<span class="c1"># Inference 3× más rápido (1.5ms → 0.5ms)
</span><span class="n">results</span> <span class="o">=</span> <span class="n">model</span><span class="p">(</span><span class="s">'image.jpg'</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="3-batch-inference">3. Batch Inference</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Procesar múltiples imágenes a la vez
</span><span class="n">images</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s">'image</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s">.jpg'</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">100</span><span class="p">)]</span>
<span class="n">results</span> <span class="o">=</span> <span class="n">model</span><span class="p">(</span><span class="n">images</span><span class="p">,</span> <span class="n">batch</span><span class="o">=</span><span class="mi">32</span><span class="p">)</span>  <span class="c1"># 32 imágenes por batch
</span></code></pre></div></div>

<h3 id="4-half-precision-fp16">4. Half Precision (FP16)</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">model</span> <span class="o">=</span> <span class="n">YOLO</span><span class="p">(</span><span class="s">'yolov8n.pt'</span><span class="p">)</span>
<span class="n">model</span><span class="p">.</span><span class="n">to</span><span class="p">(</span><span class="s">'cuda'</span><span class="p">)</span>
<span class="n">model</span><span class="p">.</span><span class="n">half</span><span class="p">()</span>  <span class="c1"># FP32 → FP16 (2× más rápido, misma precisión)
</span>
<span class="n">results</span> <span class="o">=</span> <span class="n">model</span><span class="p">(</span><span class="s">'image.jpg'</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="casos-de-uso-reales">Casos de Uso Reales</h2>

<h3 id="1-safety-helmet-detection">1. Safety Helmet Detection</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Dataset: 5000 imágenes de trabajadores
# Classes: helmet, no_helmet, person
</span>
<span class="n">model</span> <span class="o">=</span> <span class="n">YOLO</span><span class="p">(</span><span class="s">'yolov8s.pt'</span><span class="p">)</span>
<span class="n">model</span><span class="p">.</span><span class="n">train</span><span class="p">(</span>
    <span class="n">data</span><span class="o">=</span><span class="s">'helmet_dataset.yaml'</span><span class="p">,</span>
    <span class="n">epochs</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span>
    <span class="n">imgsz</span><span class="o">=</span><span class="mi">640</span><span class="p">,</span>
    <span class="n">batch</span><span class="o">=</span><span class="mi">16</span>
<span class="p">)</span>

<span class="c1"># Deployment: Raspberry Pi 4 + webcam
# FPS: 8-10 con yolov8n.pt
# Alerta si detect 'no_helmet'
</span></code></pre></div></div>

<h3 id="2-product-quality-inspection">2. Product Quality Inspection</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Dataset: 10k imágenes de piezas manufacturadas
# Classes: scratch, dent, crack, ok
</span>
<span class="c1"># Training
</span><span class="n">model</span> <span class="o">=</span> <span class="n">YOLO</span><span class="p">(</span><span class="s">'yolov8m.pt'</span><span class="p">)</span>
<span class="n">model</span><span class="p">.</span><span class="n">train</span><span class="p">(</span><span class="n">data</span><span class="o">=</span><span class="s">'defects.yaml'</span><span class="p">,</span> <span class="n">epochs</span><span class="o">=</span><span class="mi">150</span><span class="p">)</span>

<span class="c1"># Accuracy: 96.3% mAP@50
# Deployment: Conveyor belt @ 30 FPS
# ROI: 85% reduction in manual inspection
</span></code></pre></div></div>

<h3 id="3-traffic-monitoring">3. Traffic Monitoring</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Detect vehicles, count traffic, detect violations
</span><span class="n">model</span> <span class="o">=</span> <span class="n">YOLO</span><span class="p">(</span><span class="s">'yolov8l.pt'</span><span class="p">)</span>

<span class="c1"># Classes: car, truck, bus, motorcycle, bicycle, person
</span><span class="n">results</span> <span class="o">=</span> <span class="n">model</span><span class="p">.</span><span class="n">track</span><span class="p">(</span><span class="s">'traffic_cam.mp4'</span><span class="p">,</span> <span class="n">tracker</span><span class="o">=</span><span class="s">'bytetrack.yaml'</span><span class="p">)</span>

<span class="c1"># Count vehicles crossing line
</span><span class="n">line_y</span> <span class="o">=</span> <span class="mi">500</span>
<span class="n">vehicle_count</span> <span class="o">=</span> <span class="mi">0</span>

<span class="k">for</span> <span class="n">r</span> <span class="ow">in</span> <span class="n">results</span><span class="p">:</span>
    <span class="k">for</span> <span class="n">box</span> <span class="ow">in</span> <span class="n">r</span><span class="p">.</span><span class="n">boxes</span><span class="p">:</span>
        <span class="n">y_center</span> <span class="o">=</span> <span class="p">(</span><span class="n">box</span><span class="p">.</span><span class="n">xyxy</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">box</span><span class="p">.</span><span class="n">xyxy</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">3</span><span class="p">])</span> <span class="o">/</span> <span class="mi">2</span>
        <span class="k">if</span> <span class="n">y_center</span> <span class="o">&gt;</span> <span class="n">line_y</span><span class="p">:</span>
            <span class="n">vehicle_count</span> <span class="o">+=</span> <span class="mi">1</span>
</code></pre></div></div>

<h2 id="troubleshooting">Troubleshooting</h2>

<h3 id="problema-1-overfitting">Problema 1: Overfitting</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Síntomas: train loss baja, val loss alta
# Soluciones:
</span>
<span class="c1"># 1. Más data augmentation
</span><span class="n">model</span><span class="p">.</span><span class="n">train</span><span class="p">(</span>
    <span class="n">data</span><span class="o">=</span><span class="s">'dataset.yaml'</span><span class="p">,</span>
    <span class="n">mosaic</span><span class="o">=</span><span class="mf">1.0</span><span class="p">,</span>
    <span class="n">degrees</span><span class="o">=</span><span class="mi">15</span><span class="p">,</span>
    <span class="n">scale</span><span class="o">=</span><span class="mf">0.7</span><span class="p">,</span>
    <span class="n">mixup</span><span class="o">=</span><span class="mf">0.1</span>
<span class="p">)</span>

<span class="c1"># 2. Early stopping
</span><span class="n">model</span><span class="p">.</span><span class="n">train</span><span class="p">(</span><span class="n">patience</span><span class="o">=</span><span class="mi">30</span><span class="p">)</span>

<span class="c1"># 3. Regularization
</span><span class="n">model</span><span class="p">.</span><span class="n">train</span><span class="p">(</span><span class="n">dropout</span><span class="o">=</span><span class="mf">0.2</span><span class="p">,</span> <span class="n">label_smoothing</span><span class="o">=</span><span class="mf">0.1</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="problema-2-clases-desbalanceadas">Problema 2: Clases Desbalanceadas</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Dataset: 90% OK, 5% defect_A, 5% defect_B
</span>
<span class="c1"># Solución: Class weights en loss function
# (YOLOv8 hace esto automáticamente con class frequencies)
</span>
<span class="c1"># O balancear dataset manualmente
</span><span class="kn">from</span> <span class="nn">imblearn.over_sampling</span> <span class="kn">import</span> <span class="n">RandomOverSampler</span>

<span class="c1"># Aumentar muestras de clases minoritarias
</span></code></pre></div></div>

<h3 id="problema-3-detecciones-pequeñas">Problema 3: Detecciones Pequeñas</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Objetos &lt; 32×32 píxeles difíciles de detectar
</span>
<span class="c1"># Soluciones:
</span>
<span class="c1"># 1. Aumentar resolución
</span><span class="n">model</span><span class="p">.</span><span class="n">train</span><span class="p">(</span><span class="n">imgsz</span><span class="o">=</span><span class="mi">1280</span><span class="p">)</span>  <span class="c1"># Default: 640
</span>
<span class="c1"># 2. Tile-based inference
# Dividir imagen grande en tiles, detectar en cada tile
</span>
<span class="c1"># 3. Usar YOLOv8-P6 (diseñado para objetos pequeños)
</span><span class="n">model</span> <span class="o">=</span> <span class="n">YOLO</span><span class="p">(</span><span class="s">'yolov8n-p6.pt'</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="comparación-con-otros-detectores">Comparación con Otros Detectores</h2>

<table>
  <thead>
    <tr>
      <th>Model</th>
      <th>mAP@50-95</th>
      <th>FPS (V100)</th>
      <th>Params</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Faster R-CNN</td>
      <td>42.0</td>
      <td>5</td>
      <td>137M</td>
    </tr>
    <tr>
      <td>SSD</td>
      <td>25.1</td>
      <td>22</td>
      <td>26M</td>
    </tr>
    <tr>
      <td>RetinaNet</td>
      <td>40.8</td>
      <td>11</td>
      <td>36M</td>
    </tr>
    <tr>
      <td>EfficientDet-D1</td>
      <td>40.5</td>
      <td>19</td>
      <td>6.6M</td>
    </tr>
    <tr>
      <td><strong>YOLOv8s</strong></td>
      <td><strong>44.9</strong></td>
      <td><strong>320</strong></td>
      <td><strong>11M</strong></td>
    </tr>
    <tr>
      <td>YOLOv7</td>
      <td>51.2</td>
      <td>161</td>
      <td>37M</td>
    </tr>
    <tr>
      <td>DINO</td>
      <td>58.5</td>
      <td>9</td>
      <td>218M</td>
    </tr>
  </tbody>
</table>

<p><strong>YOLO v8 gana en:</strong></p>
<ul>
  <li>✅ Speed (10-30× más rápido que R-CNN)</li>
  <li>✅ Efficiency (menos params, menos VRAM)</li>
  <li>✅ Ease of use (3 líneas de código)</li>
</ul>

<p><strong>YOLO v8 pierde en:</strong></p>
<ul>
  <li>❌ Small object detection (&lt; 32×32 px)</li>
  <li>❌ Máxima precisión (DINO/DETR son mejores)</li>
</ul>

<h2 id="recursos">Recursos</h2>

<ul>
  <li><strong>Ultralytics Docs</strong>: <a href="https://docs.ultralytics.com">docs.ultralytics.com</a></li>
  <li><strong>YOLO Paper</strong>: “You Only Look Once” (Redmon et al., 2016)</li>
  <li><strong>Dataset Labeling</strong>: <a href="https://roboflow.com">Roboflow</a>, <a href="https://labelstud.io">Label Studio</a></li>
  <li><strong>Pre-trained Models</strong>: <a href="https://hub.ultralytics.com">Ultralytics Hub</a></li>
</ul>

<hr />

<p><strong>Próximo:</strong> NLP con spaCy para procesamiento de texto y Named Entity Recognition.</p>]]></content><author><name>AI Tech Team</name></author><category term="Computer Vision" /><category term="yolo" /><category term="object-detection" /><category term="computer-vision" /><category term="deep-learning" /><category term="ultralytics" /><summary type="html"><![CDATA[Guía completa de YOLO v8: desde detección básica hasta entrenar modelos custom en tus propias imágenes. Inference en 3ms @ 640px.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://images.unsplash.com/photo-1558591710-4b4a1ae0f04d?w=800" /><media:content medium="image" url="https://images.unsplash.com/photo-1558591710-4b4a1ae0f04d?w=800" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">MLOps: De Notebooks a Producción</title><link href="https://dev-jcgi.github.io/blog/blog/2026/01/28/mlops-best-practices/" rel="alternate" type="text/html" title="MLOps: De Notebooks a Producción" /><published>2026-01-28T10:30:00-06:00</published><updated>2026-01-28T10:30:00-06:00</updated><id>https://dev-jcgi.github.io/blog/blog/2026/01/28/mlops-best-practices</id><content type="html" xml:base="https://dev-jcgi.github.io/blog/blog/2026/01/28/mlops-best-practices/"><![CDATA[<p><strong>87% de los modelos ML nunca llegan a producción.</strong> MLOps resuelve el gap entre experimentos en notebooks y sistemas escalables en producción.</p>

<h2 id="el-problema-del-notebook-hell">El Problema del “Notebook Hell”</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># notebook_final_v3_REAL_ultima_version.ipynb
</span>
<span class="c1"># ❌ Problemas:
# - Datos sin versionar (¿qué data entrenó este modelo?)
# - Experimentos perdidos (¿qué hiperparams funcionaron?)
# - Código no reproducible (funciona en mi máquina™)
# - Deploy manual (copiar .pkl a servidor)
# - Sin monitoring (modelo degrada silently)
</span></code></pre></div></div>

<h2 id="pipeline-mlops-completo">Pipeline MLOps Completo</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Data] → [Version] → [Train] → [Track] → [Test] → [Deploy] → [Monitor]
   ↓         DVC        ↓       MLflow     CI/CD    Docker    Prometheus
  S3                  Code                                    Grafana
</code></pre></div></div>

<h2 id="1-versionado-de-datos-con-dvc">1. Versionado de Datos con DVC</h2>

<p><strong>DVC</strong> (Data Version Control) es Git para datasets:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Instalar</span>
pip <span class="nb">install </span>dvc dvc-s3

<span class="c"># Inicializar</span>
dvc init
git commit <span class="nt">-m</span> <span class="s2">"Initialize DVC"</span>

<span class="c"># Trackear datasets</span>
dvc add data/train.csv
dvc add data/test.csv

<span class="c"># DVC crea .dvc files (50 KB) en lugar de guardar CSV (5 GB) en Git</span>
git add data/train.csv.dvc data/test.csv.dvc
git commit <span class="nt">-m</span> <span class="s2">"Add training data v1"</span>

<span class="c"># Configurar remote storage (S3/Azure/GCS)</span>
dvc remote add <span class="nt">-d</span> storage s3://my-ml-bucket/data
dvc push  <span class="c"># Sube data a S3</span>
</code></pre></div></div>

<h3 id="pipeline-dvc">Pipeline DVC</h3>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># dvc.yaml - Define pipeline completo</span>
<span class="na">stages</span><span class="pi">:</span>
  <span class="na">preprocess</span><span class="pi">:</span>
    <span class="na">cmd</span><span class="pi">:</span> <span class="s">python src/preprocess.py</span>
    <span class="na">deps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">data/raw.csv</span>
      <span class="pi">-</span> <span class="s">src/preprocess.py</span>
    <span class="na">outs</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">data/train.csv</span>
      <span class="pi">-</span> <span class="s">data/test.csv</span>
    
  <span class="na">train</span><span class="pi">:</span>
    <span class="na">cmd</span><span class="pi">:</span> <span class="s">python src/train.py</span>
    <span class="na">deps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">data/train.csv</span>
      <span class="pi">-</span> <span class="s">src/train.py</span>
    <span class="na">params</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">train.learning_rate</span>
      <span class="pi">-</span> <span class="s">train.epochs</span>
    <span class="na">outs</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">models/model.pkl</span>
    <span class="na">metrics</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">metrics.json</span><span class="pi">:</span>
          <span class="na">cache</span><span class="pi">:</span> <span class="no">false</span>
          
  <span class="na">evaluate</span><span class="pi">:</span>
    <span class="na">cmd</span><span class="pi">:</span> <span class="s">python src/evaluate.py</span>
    <span class="na">deps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">models/model.pkl</span>
      <span class="pi">-</span> <span class="s">data/test.csv</span>
    <span class="na">metrics</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">scores.json</span><span class="pi">:</span>
          <span class="na">cache</span><span class="pi">:</span> <span class="no">false</span>
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># src/train.py
</span><span class="kn">import</span> <span class="nn">yaml</span>
<span class="kn">import</span> <span class="nn">joblib</span>
<span class="kn">from</span> <span class="nn">sklearn.ensemble</span> <span class="kn">import</span> <span class="n">RandomForestClassifier</span>

<span class="c1"># Leer params
</span><span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"params.yaml"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
    <span class="n">params</span> <span class="o">=</span> <span class="n">yaml</span><span class="p">.</span><span class="n">safe_load</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>

<span class="c1"># Entrenar
</span><span class="n">model</span> <span class="o">=</span> <span class="n">RandomForestClassifier</span><span class="p">(</span>
    <span class="n">n_estimators</span><span class="o">=</span><span class="n">params</span><span class="p">[</span><span class="s">"train"</span><span class="p">][</span><span class="s">"n_estimators"</span><span class="p">],</span>
    <span class="n">max_depth</span><span class="o">=</span><span class="n">params</span><span class="p">[</span><span class="s">"train"</span><span class="p">][</span><span class="s">"max_depth"</span><span class="p">]</span>
<span class="p">)</span>
<span class="n">model</span><span class="p">.</span><span class="n">fit</span><span class="p">(</span><span class="n">X_train</span><span class="p">,</span> <span class="n">y_train</span><span class="p">)</span>

<span class="c1"># Guardar
</span><span class="n">joblib</span><span class="p">.</span><span class="n">dump</span><span class="p">(</span><span class="n">model</span><span class="p">,</span> <span class="s">"models/model.pkl"</span><span class="p">)</span>

<span class="c1"># Guardar métricas
</span><span class="n">metrics</span> <span class="o">=</span> <span class="p">{</span><span class="s">"accuracy"</span><span class="p">:</span> <span class="mf">0.92</span><span class="p">,</span> <span class="s">"f1"</span><span class="p">:</span> <span class="mf">0.89</span><span class="p">}</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"metrics.json"</span><span class="p">,</span> <span class="s">"w"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
    <span class="n">json</span><span class="p">.</span><span class="n">dump</span><span class="p">(</span><span class="n">metrics</span><span class="p">,</span> <span class="n">f</span><span class="p">)</span>
</code></pre></div></div>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Ejecutar pipeline</span>
dvc repro

<span class="c"># Ver métricas de diferentes versiones</span>
dvc metrics show <span class="nt">--all-branches</span>

<span class="c"># Cambiar a versión anterior del dataset</span>
git checkout v1.0
dvc checkout  <span class="c"># Descarga data de esa versión</span>
</code></pre></div></div>

<h2 id="2-experiment-tracking-con-mlflow">2. Experiment Tracking con MLflow</h2>

<p><strong>MLflow</strong> trackea experimentos, modelos y deployments:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">mlflow</span>
<span class="kn">import</span> <span class="nn">mlflow.sklearn</span>
<span class="kn">from</span> <span class="nn">sklearn.ensemble</span> <span class="kn">import</span> <span class="n">RandomForestClassifier</span>
<span class="kn">from</span> <span class="nn">sklearn.metrics</span> <span class="kn">import</span> <span class="n">accuracy_score</span><span class="p">,</span> <span class="n">f1_score</span>

<span class="c1"># Configurar tracking server
</span><span class="n">mlflow</span><span class="p">.</span><span class="n">set_tracking_uri</span><span class="p">(</span><span class="s">"http://localhost:5000"</span><span class="p">)</span>
<span class="n">mlflow</span><span class="p">.</span><span class="n">set_experiment</span><span class="p">(</span><span class="s">"credit-card-fraud"</span><span class="p">)</span>

<span class="c1"># Entrenar con tracking
</span><span class="k">with</span> <span class="n">mlflow</span><span class="p">.</span><span class="n">start_run</span><span class="p">(</span><span class="n">run_name</span><span class="o">=</span><span class="s">"rf_baseline"</span><span class="p">):</span>
    
    <span class="c1"># Log params
</span>    <span class="n">params</span> <span class="o">=</span> <span class="p">{</span>
        <span class="s">"n_estimators"</span><span class="p">:</span> <span class="mi">100</span><span class="p">,</span>
        <span class="s">"max_depth"</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span>
        <span class="s">"min_samples_split"</span><span class="p">:</span> <span class="mi">5</span>
    <span class="p">}</span>
    <span class="n">mlflow</span><span class="p">.</span><span class="n">log_params</span><span class="p">(</span><span class="n">params</span><span class="p">)</span>
    
    <span class="c1"># Entrenar modelo
</span>    <span class="n">model</span> <span class="o">=</span> <span class="n">RandomForestClassifier</span><span class="p">(</span><span class="o">**</span><span class="n">params</span><span class="p">)</span>
    <span class="n">model</span><span class="p">.</span><span class="n">fit</span><span class="p">(</span><span class="n">X_train</span><span class="p">,</span> <span class="n">y_train</span><span class="p">)</span>
    
    <span class="c1"># Evaluar
</span>    <span class="n">y_pred</span> <span class="o">=</span> <span class="n">model</span><span class="p">.</span><span class="n">predict</span><span class="p">(</span><span class="n">X_test</span><span class="p">)</span>
    <span class="n">accuracy</span> <span class="o">=</span> <span class="n">accuracy_score</span><span class="p">(</span><span class="n">y_test</span><span class="p">,</span> <span class="n">y_pred</span><span class="p">)</span>
    <span class="n">f1</span> <span class="o">=</span> <span class="n">f1_score</span><span class="p">(</span><span class="n">y_test</span><span class="p">,</span> <span class="n">y_pred</span><span class="p">)</span>
    
    <span class="c1"># Log metrics
</span>    <span class="n">mlflow</span><span class="p">.</span><span class="n">log_metrics</span><span class="p">({</span>
        <span class="s">"accuracy"</span><span class="p">:</span> <span class="n">accuracy</span><span class="p">,</span>
        <span class="s">"f1_score"</span><span class="p">:</span> <span class="n">f1</span><span class="p">,</span>
        <span class="s">"precision"</span><span class="p">:</span> <span class="n">precision_score</span><span class="p">(</span><span class="n">y_test</span><span class="p">,</span> <span class="n">y_pred</span><span class="p">)</span>
    <span class="p">})</span>
    
    <span class="c1"># Log artifacts
</span>    <span class="n">fig</span> <span class="o">=</span> <span class="n">plot_confusion_matrix</span><span class="p">(</span><span class="n">y_test</span><span class="p">,</span> <span class="n">y_pred</span><span class="p">)</span>
    <span class="n">mlflow</span><span class="p">.</span><span class="n">log_figure</span><span class="p">(</span><span class="n">fig</span><span class="p">,</span> <span class="s">"confusion_matrix.png"</span><span class="p">)</span>
    
    <span class="c1"># Log model
</span>    <span class="n">mlflow</span><span class="p">.</span><span class="n">sklearn</span><span class="p">.</span><span class="n">log_model</span><span class="p">(</span>
        <span class="n">model</span><span class="p">,</span>
        <span class="s">"model"</span><span class="p">,</span>
        <span class="n">registered_model_name</span><span class="o">=</span><span class="s">"FraudDetector"</span>
    <span class="p">)</span>
    
    <span class="c1"># Log dataset info
</span>    <span class="n">mlflow</span><span class="p">.</span><span class="n">log_param</span><span class="p">(</span><span class="s">"train_size"</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">X_train</span><span class="p">))</span>
    <span class="n">mlflow</span><span class="p">.</span><span class="n">log_param</span><span class="p">(</span><span class="s">"test_size"</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">X_test</span><span class="p">))</span>
</code></pre></div></div>

<h3 id="mlflow-ui">MLflow UI</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Levantar tracking server</span>
mlflow server <span class="nt">--host</span> 0.0.0.0 <span class="nt">--port</span> 5000

<span class="c"># Acceder a http://localhost:5000</span>
<span class="c"># Ver: experimentos, métricas, modelos, artifacts</span>
</code></pre></div></div>

<h3 id="model-registry">Model Registry</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">mlflow.tracking</span> <span class="kn">import</span> <span class="n">MlflowClient</span>

<span class="n">client</span> <span class="o">=</span> <span class="n">MlflowClient</span><span class="p">()</span>

<span class="c1"># Registrar modelo
</span><span class="n">result</span> <span class="o">=</span> <span class="n">mlflow</span><span class="p">.</span><span class="n">register_model</span><span class="p">(</span>
    <span class="s">"runs:/abc123/model"</span><span class="p">,</span>
    <span class="s">"FraudDetector"</span>
<span class="p">)</span>

<span class="c1"># Promover a staging
</span><span class="n">client</span><span class="p">.</span><span class="n">transition_model_version_stage</span><span class="p">(</span>
    <span class="n">name</span><span class="o">=</span><span class="s">"FraudDetector"</span><span class="p">,</span>
    <span class="n">version</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span>
    <span class="n">stage</span><span class="o">=</span><span class="s">"Staging"</span>
<span class="p">)</span>

<span class="c1"># Después de validar, promover a production
</span><span class="n">client</span><span class="p">.</span><span class="n">transition_model_version_stage</span><span class="p">(</span>
    <span class="n">name</span><span class="o">=</span><span class="s">"FraudDetector"</span><span class="p">,</span>
    <span class="n">version</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span>
    <span class="n">stage</span><span class="o">=</span><span class="s">"Production"</span>
<span class="p">)</span>

<span class="c1"># Cargar modelo de producción
</span><span class="n">model_uri</span> <span class="o">=</span> <span class="s">"models:/FraudDetector/Production"</span>
<span class="n">model</span> <span class="o">=</span> <span class="n">mlflow</span><span class="p">.</span><span class="n">sklearn</span><span class="p">.</span><span class="n">load_model</span><span class="p">(</span><span class="n">model_uri</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="3-cicd-para-ml">3. CI/CD para ML</h2>

<h3 id="github-actions-pipeline">GitHub Actions Pipeline</h3>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># .github/workflows/ml-pipeline.yml</span>
<span class="na">name</span><span class="pi">:</span> <span class="s">ML Training Pipeline</span>

<span class="na">on</span><span class="pi">:</span>
  <span class="na">push</span><span class="pi">:</span>
    <span class="na">branches</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">main</span><span class="pi">]</span>
  <span class="na">pull_request</span><span class="pi">:</span>
    <span class="na">branches</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">main</span><span class="pi">]</span>

<span class="na">jobs</span><span class="pi">:</span>
  <span class="na">train</span><span class="pi">:</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    
    <span class="na">steps</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v3</span>
    
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Set up Python</span>
      <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/setup-python@v4</span>
      <span class="na">with</span><span class="pi">:</span>
        <span class="na">python-version</span><span class="pi">:</span> <span class="s1">'</span><span class="s">3.10'</span>
    
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install dependencies</span>
      <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
        <span class="s">pip install -r requirements.txt</span>
        <span class="s">pip install dvc[s3] mlflow</span>
    
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Configure DVC</span>
      <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
        <span class="s">dvc remote modify storage access_key_id $</span>
        <span class="s">dvc remote modify storage secret_access_key $</span>
    
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Pull data</span>
      <span class="na">run</span><span class="pi">:</span> <span class="s">dvc pull</span>
    
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Run pipeline</span>
      <span class="na">run</span><span class="pi">:</span> <span class="s">dvc repro</span>
    
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Evaluate model</span>
      <span class="na">run</span><span class="pi">:</span> <span class="s">python src/evaluate.py</span>
    
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Check performance threshold</span>
      <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
        <span class="s">accuracy=$(jq '.accuracy' metrics.json)</span>
        <span class="s">if (( $(echo "$accuracy &lt; 0.85" | bc -l) )); then</span>
          <span class="s">echo "❌ Accuracy $accuracy below threshold 0.85"</span>
          <span class="s">exit 1</span>
        <span class="s">fi</span>
        <span class="s">echo "✅ Accuracy $accuracy passed"</span>
    
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Upload model to MLflow</span>
      <span class="na">if</span><span class="pi">:</span> <span class="s">github.ref == 'refs/heads/main'</span>
      <span class="na">env</span><span class="pi">:</span>
        <span class="na">MLFLOW_TRACKING_URI</span><span class="pi">:</span> <span class="s">$</span>
      <span class="na">run</span><span class="pi">:</span> <span class="s">python src/register_model.py</span>
</code></pre></div></div>

<h3 id="testing-ml-code">Testing ML Code</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># tests/test_model.py
</span><span class="kn">import</span> <span class="nn">pytest</span>
<span class="kn">import</span> <span class="nn">joblib</span>
<span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="n">pd</span>
<span class="kn">from</span> <span class="nn">src.train</span> <span class="kn">import</span> <span class="n">preprocess</span><span class="p">,</span> <span class="n">train_model</span>

<span class="k">def</span> <span class="nf">test_preprocess</span><span class="p">():</span>
    <span class="s">"""Test data preprocessing"""</span>
    <span class="n">df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">DataFrame</span><span class="p">({</span>
        <span class="s">'amount'</span><span class="p">:</span> <span class="p">[</span><span class="mi">100</span><span class="p">,</span> <span class="mi">200</span><span class="p">,</span> <span class="mi">50</span><span class="p">],</span>
        <span class="s">'time'</span><span class="p">:</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">200</span><span class="p">],</span>
        <span class="s">'class'</span><span class="p">:</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">]</span>
    <span class="p">})</span>
    <span class="n">X</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="n">preprocess</span><span class="p">(</span><span class="n">df</span><span class="p">)</span>
    <span class="k">assert</span> <span class="n">X</span><span class="p">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="mi">3</span>
    <span class="k">assert</span> <span class="nb">len</span><span class="p">(</span><span class="n">y</span><span class="p">)</span> <span class="o">==</span> <span class="mi">3</span>
    <span class="k">assert</span> <span class="ow">not</span> <span class="n">X</span><span class="p">.</span><span class="n">isnull</span><span class="p">().</span><span class="nb">any</span><span class="p">().</span><span class="nb">any</span><span class="p">()</span>

<span class="k">def</span> <span class="nf">test_model_predictions</span><span class="p">():</span>
    <span class="s">"""Test model makes valid predictions"""</span>
    <span class="n">model</span> <span class="o">=</span> <span class="n">joblib</span><span class="p">.</span><span class="n">load</span><span class="p">(</span><span class="s">"models/model.pkl"</span><span class="p">)</span>
    <span class="n">X_test</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">read_csv</span><span class="p">(</span><span class="s">"data/test.csv"</span><span class="p">)</span>
    
    <span class="n">predictions</span> <span class="o">=</span> <span class="n">model</span><span class="p">.</span><span class="n">predict</span><span class="p">(</span><span class="n">X_test</span><span class="p">)</span>
    
    <span class="c1"># Verificar output shape
</span>    <span class="k">assert</span> <span class="nb">len</span><span class="p">(</span><span class="n">predictions</span><span class="p">)</span> <span class="o">==</span> <span class="nb">len</span><span class="p">(</span><span class="n">X_test</span><span class="p">)</span>
    
    <span class="c1"># Verificar valores válidos (0 o 1)
</span>    <span class="k">assert</span> <span class="nb">all</span><span class="p">(</span><span class="n">p</span> <span class="ow">in</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">predictions</span><span class="p">)</span>
    
    <span class="c1"># Verificar performance mínima
</span>    <span class="n">accuracy</span> <span class="o">=</span> <span class="n">accuracy_score</span><span class="p">(</span><span class="n">y_test</span><span class="p">,</span> <span class="n">predictions</span><span class="p">)</span>
    <span class="k">assert</span> <span class="n">accuracy</span> <span class="o">&gt;=</span> <span class="mf">0.85</span><span class="p">,</span> <span class="sa">f</span><span class="s">"Accuracy </span><span class="si">{</span><span class="n">accuracy</span><span class="si">}</span><span class="s"> below threshold"</span>

<span class="k">def</span> <span class="nf">test_model_latency</span><span class="p">():</span>
    <span class="s">"""Test inference speed"""</span>
    <span class="n">model</span> <span class="o">=</span> <span class="n">joblib</span><span class="p">.</span><span class="n">load</span><span class="p">(</span><span class="s">"models/model.pkl"</span><span class="p">)</span>
    <span class="n">X_sample</span> <span class="o">=</span> <span class="n">X_test</span><span class="p">.</span><span class="n">iloc</span><span class="p">[:</span><span class="mi">100</span><span class="p">]</span>
    
    <span class="kn">import</span> <span class="nn">time</span>
    <span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span>
    <span class="n">model</span><span class="p">.</span><span class="n">predict</span><span class="p">(</span><span class="n">X_sample</span><span class="p">)</span>
    <span class="n">latency</span> <span class="o">=</span> <span class="p">(</span><span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">start</span><span class="p">)</span> <span class="o">/</span> <span class="mi">100</span>
    
    <span class="k">assert</span> <span class="n">latency</span> <span class="o">&lt;</span> <span class="mf">0.01</span><span class="p">,</span> <span class="sa">f</span><span class="s">"Latency </span><span class="si">{</span><span class="n">latency</span><span class="si">}</span><span class="s">s exceeds 10ms"</span>

<span class="o">@</span><span class="n">pytest</span><span class="p">.</span><span class="n">mark</span><span class="p">.</span><span class="n">parametrize</span><span class="p">(</span><span class="s">"input_data,expected"</span><span class="p">,</span> <span class="p">[</span>
    <span class="p">({</span><span class="s">"amount"</span><span class="p">:</span> <span class="mi">100</span><span class="p">,</span> <span class="s">"time"</span><span class="p">:</span> <span class="mi">0</span><span class="p">},</span> <span class="mi">0</span><span class="p">),</span>
    <span class="p">({</span><span class="s">"amount"</span><span class="p">:</span> <span class="mi">10000</span><span class="p">,</span> <span class="s">"time"</span><span class="p">:</span> <span class="mi">50</span><span class="p">},</span> <span class="mi">1</span><span class="p">),</span>
<span class="p">])</span>
<span class="k">def</span> <span class="nf">test_edge_cases</span><span class="p">(</span><span class="n">input_data</span><span class="p">,</span> <span class="n">expected</span><span class="p">):</span>
    <span class="s">"""Test edge cases"""</span>
    <span class="n">model</span> <span class="o">=</span> <span class="n">joblib</span><span class="p">.</span><span class="n">load</span><span class="p">(</span><span class="s">"models/model.pkl"</span><span class="p">)</span>
    <span class="n">X</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">DataFrame</span><span class="p">([</span><span class="n">input_data</span><span class="p">])</span>
    <span class="n">pred</span> <span class="o">=</span> <span class="n">model</span><span class="p">.</span><span class="n">predict</span><span class="p">(</span><span class="n">X</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
    <span class="k">assert</span> <span class="n">pred</span> <span class="o">==</span> <span class="n">expected</span>
</code></pre></div></div>

<h2 id="4-containerización-con-docker">4. Containerización con Docker</h2>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Dockerfile</span>
<span class="k">FROM</span><span class="s"> python:3.10-slim</span>

<span class="k">WORKDIR</span><span class="s"> /app</span>

<span class="c"># Instalar dependencias</span>
<span class="k">COPY</span><span class="s"> requirements.txt .</span>
<span class="k">RUN </span>pip <span class="nb">install</span> <span class="nt">--no-cache-dir</span> <span class="nt">-r</span> requirements.txt

<span class="c"># Copiar código</span>
<span class="k">COPY</span><span class="s"> src/ ./src/</span>
<span class="k">COPY</span><span class="s"> models/ ./models/</span>

<span class="c"># Exponer puerto</span>
<span class="k">EXPOSE</span><span class="s"> 8000</span>

<span class="c"># Comando para servir modelo</span>
<span class="k">CMD</span><span class="s"> ["uvicorn", "src.api:app", "--host", "0.0.0.0", "--port", "8000"]</span>
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># src/api.py - FastAPI serving
</span><span class="kn">from</span> <span class="nn">fastapi</span> <span class="kn">import</span> <span class="n">FastAPI</span>
<span class="kn">from</span> <span class="nn">pydantic</span> <span class="kn">import</span> <span class="n">BaseModel</span>
<span class="kn">import</span> <span class="nn">joblib</span>
<span class="kn">import</span> <span class="nn">mlflow.sklearn</span>

<span class="n">app</span> <span class="o">=</span> <span class="n">FastAPI</span><span class="p">(</span><span class="n">title</span><span class="o">=</span><span class="s">"Fraud Detection API"</span><span class="p">)</span>

<span class="c1"># Cargar modelo al startup
</span><span class="o">@</span><span class="n">app</span><span class="p">.</span><span class="n">on_event</span><span class="p">(</span><span class="s">"startup"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">load_model</span><span class="p">():</span>
    <span class="k">global</span> <span class="n">model</span>
    <span class="c1"># Opción 1: Desde archivo
</span>    <span class="n">model</span> <span class="o">=</span> <span class="n">joblib</span><span class="p">.</span><span class="n">load</span><span class="p">(</span><span class="s">"models/model.pkl"</span><span class="p">)</span>
    
    <span class="c1"># Opción 2: Desde MLflow
</span>    <span class="c1"># model = mlflow.sklearn.load_model("models:/FraudDetector/Production")
</span>
<span class="k">class</span> <span class="nc">Transaction</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
    <span class="n">amount</span><span class="p">:</span> <span class="nb">float</span>
    <span class="n">time</span><span class="p">:</span> <span class="nb">int</span>
    <span class="n">v1</span><span class="p">:</span> <span class="nb">float</span>
    <span class="n">v2</span><span class="p">:</span> <span class="nb">float</span>
    <span class="c1"># ... más features
</span>
<span class="o">@</span><span class="n">app</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="s">"/predict"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">predict</span><span class="p">(</span><span class="n">transaction</span><span class="p">:</span> <span class="n">Transaction</span><span class="p">):</span>
    <span class="s">"""Predict fraud probability"""</span>
    <span class="n">features</span> <span class="o">=</span> <span class="p">[[</span>
        <span class="n">transaction</span><span class="p">.</span><span class="n">amount</span><span class="p">,</span>
        <span class="n">transaction</span><span class="p">.</span><span class="n">time</span><span class="p">,</span>
        <span class="n">transaction</span><span class="p">.</span><span class="n">v1</span><span class="p">,</span>
        <span class="n">transaction</span><span class="p">.</span><span class="n">v2</span>
    <span class="p">]]</span>
    
    <span class="n">prediction</span> <span class="o">=</span> <span class="n">model</span><span class="p">.</span><span class="n">predict</span><span class="p">(</span><span class="n">features</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
    <span class="n">probability</span> <span class="o">=</span> <span class="n">model</span><span class="p">.</span><span class="n">predict_proba</span><span class="p">(</span><span class="n">features</span><span class="p">)[</span><span class="mi">0</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span>
    
    <span class="k">return</span> <span class="p">{</span>
        <span class="s">"is_fraud"</span><span class="p">:</span> <span class="nb">bool</span><span class="p">(</span><span class="n">prediction</span><span class="p">),</span>
        <span class="s">"fraud_probability"</span><span class="p">:</span> <span class="nb">float</span><span class="p">(</span><span class="n">probability</span><span class="p">)</span>
    <span class="p">}</span>

<span class="o">@</span><span class="n">app</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"/health"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">health</span><span class="p">():</span>
    <span class="k">return</span> <span class="p">{</span><span class="s">"status"</span><span class="p">:</span> <span class="s">"healthy"</span><span class="p">,</span> <span class="s">"model_loaded"</span><span class="p">:</span> <span class="n">model</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">}</span>
</code></pre></div></div>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Build y deploy</span>
docker build <span class="nt">-t</span> fraud-detector:v1 <span class="nb">.</span>
docker run <span class="nt">-p</span> 8000:8000 fraud-detector:v1

<span class="c"># Test API</span>
curl <span class="nt">-X</span> POST http://localhost:8000/predict <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"Content-Type: application/json"</span> <span class="se">\</span>
  <span class="nt">-d</span> <span class="s1">'{"amount": 100.5, "time": 42, "v1": 0.5, "v2": -1.2}'</span>
</code></pre></div></div>

<h2 id="5-monitoring-en-producción">5. Monitoring en Producción</h2>

<h3 id="data-drift-detection">Data Drift Detection</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">evidently</span> <span class="kn">import</span> <span class="n">ColumnMapping</span>
<span class="kn">from</span> <span class="nn">evidently.report</span> <span class="kn">import</span> <span class="n">Report</span>
<span class="kn">from</span> <span class="nn">evidently.metrics</span> <span class="kn">import</span> <span class="n">DataDriftTable</span><span class="p">,</span> <span class="n">DatasetDriftMetric</span>

<span class="k">def</span> <span class="nf">detect_drift</span><span class="p">(</span><span class="n">reference_data</span><span class="p">,</span> <span class="n">current_data</span><span class="p">):</span>
    <span class="s">"""Detectar drift en features"""</span>
    
    <span class="n">report</span> <span class="o">=</span> <span class="n">Report</span><span class="p">(</span><span class="n">metrics</span><span class="o">=</span><span class="p">[</span>
        <span class="n">DataDriftTable</span><span class="p">(),</span>
        <span class="n">DatasetDriftMetric</span><span class="p">()</span>
    <span class="p">])</span>
    
    <span class="n">report</span><span class="p">.</span><span class="n">run</span><span class="p">(</span>
        <span class="n">reference_data</span><span class="o">=</span><span class="n">reference_data</span><span class="p">,</span>
        <span class="n">current_data</span><span class="o">=</span><span class="n">current_data</span>
    <span class="p">)</span>
    
    <span class="n">drift_report</span> <span class="o">=</span> <span class="n">report</span><span class="p">.</span><span class="n">as_dict</span><span class="p">()</span>
    
    <span class="k">if</span> <span class="n">drift_report</span><span class="p">[</span><span class="s">'metrics'</span><span class="p">][</span><span class="mi">1</span><span class="p">][</span><span class="s">'result'</span><span class="p">][</span><span class="s">'dataset_drift'</span><span class="p">]:</span>
        <span class="k">print</span><span class="p">(</span><span class="s">"⚠️ Data drift detectado!"</span><span class="p">)</span>
        <span class="n">drifted_features</span> <span class="o">=</span> <span class="p">[</span>
            <span class="n">col</span> <span class="k">for</span> <span class="n">col</span><span class="p">,</span> <span class="n">metrics</span> <span class="ow">in</span> <span class="n">drift_report</span><span class="p">[</span><span class="s">'metrics'</span><span class="p">][</span><span class="mi">0</span><span class="p">][</span><span class="s">'result'</span><span class="p">][</span><span class="s">'drift_by_columns'</span><span class="p">].</span><span class="n">items</span><span class="p">()</span>
            <span class="k">if</span> <span class="n">metrics</span><span class="p">[</span><span class="s">'drift_detected'</span><span class="p">]</span>
        <span class="p">]</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Features con drift: </span><span class="si">{</span><span class="n">drifted_features</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
        
        <span class="c1"># Trigger retraining
</span>        <span class="n">trigger_retraining_pipeline</span><span class="p">()</span>
    
    <span class="k">return</span> <span class="n">drift_report</span>
</code></pre></div></div>

<h3 id="model-performance-monitoring">Model Performance Monitoring</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">prometheus_client</span> <span class="kn">import</span> <span class="n">Counter</span><span class="p">,</span> <span class="n">Histogram</span><span class="p">,</span> <span class="n">Gauge</span>
<span class="kn">import</span> <span class="nn">time</span>

<span class="c1"># Métricas Prometheus
</span><span class="n">prediction_counter</span> <span class="o">=</span> <span class="n">Counter</span><span class="p">(</span><span class="s">'predictions_total'</span><span class="p">,</span> <span class="s">'Total predictions made'</span><span class="p">)</span>
<span class="n">prediction_latency</span> <span class="o">=</span> <span class="n">Histogram</span><span class="p">(</span><span class="s">'prediction_latency_seconds'</span><span class="p">,</span> <span class="s">'Prediction latency'</span><span class="p">)</span>
<span class="n">fraud_rate</span> <span class="o">=</span> <span class="n">Gauge</span><span class="p">(</span><span class="s">'fraud_rate'</span><span class="p">,</span> <span class="s">'Current fraud detection rate'</span><span class="p">)</span>

<span class="o">@</span><span class="n">app</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="s">"/predict"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">predict</span><span class="p">(</span><span class="n">transaction</span><span class="p">:</span> <span class="n">Transaction</span><span class="p">):</span>
    <span class="c1"># Medir latency
</span>    <span class="n">start_time</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span>
    
    <span class="c1"># Hacer predicción
</span>    <span class="n">prediction</span> <span class="o">=</span> <span class="n">model</span><span class="p">.</span><span class="n">predict</span><span class="p">([</span><span class="n">transaction</span><span class="p">.</span><span class="nb">dict</span><span class="p">().</span><span class="n">values</span><span class="p">()])[</span><span class="mi">0</span><span class="p">]</span>
    
    <span class="c1"># Actualizar métricas
</span>    <span class="n">prediction_counter</span><span class="p">.</span><span class="n">inc</span><span class="p">()</span>
    <span class="n">prediction_latency</span><span class="p">.</span><span class="n">observe</span><span class="p">(</span><span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">start_time</span><span class="p">)</span>
    
    <span class="k">if</span> <span class="n">prediction</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
        <span class="n">fraud_rate</span><span class="p">.</span><span class="n">inc</span><span class="p">()</span>
    
    <span class="k">return</span> <span class="p">{</span><span class="s">"is_fraud"</span><span class="p">:</span> <span class="nb">bool</span><span class="p">(</span><span class="n">prediction</span><span class="p">)}</span>
</code></pre></div></div>

<h3 id="logging-estructurado">Logging Estructurado</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">logging</span>
<span class="kn">import</span> <span class="nn">json</span>
<span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span>

<span class="k">class</span> <span class="nc">JSONFormatter</span><span class="p">(</span><span class="n">logging</span><span class="p">.</span><span class="n">Formatter</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">format</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">record</span><span class="p">):</span>
        <span class="n">log_data</span> <span class="o">=</span> <span class="p">{</span>
            <span class="s">"timestamp"</span><span class="p">:</span> <span class="n">datetime</span><span class="p">.</span><span class="n">utcnow</span><span class="p">().</span><span class="n">isoformat</span><span class="p">(),</span>
            <span class="s">"level"</span><span class="p">:</span> <span class="n">record</span><span class="p">.</span><span class="n">levelname</span><span class="p">,</span>
            <span class="s">"message"</span><span class="p">:</span> <span class="n">record</span><span class="p">.</span><span class="n">getMessage</span><span class="p">(),</span>
            <span class="s">"module"</span><span class="p">:</span> <span class="n">record</span><span class="p">.</span><span class="n">module</span><span class="p">,</span>
            <span class="s">"function"</span><span class="p">:</span> <span class="n">record</span><span class="p">.</span><span class="n">funcName</span>
        <span class="p">}</span>
        <span class="k">return</span> <span class="n">json</span><span class="p">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">log_data</span><span class="p">)</span>

<span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="p">.</span><span class="n">getLogger</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
<span class="n">handler</span> <span class="o">=</span> <span class="n">logging</span><span class="p">.</span><span class="n">StreamHandler</span><span class="p">()</span>
<span class="n">handler</span><span class="p">.</span><span class="n">setFormatter</span><span class="p">(</span><span class="n">JSONFormatter</span><span class="p">())</span>
<span class="n">logger</span><span class="p">.</span><span class="n">addHandler</span><span class="p">(</span><span class="n">handler</span><span class="p">)</span>

<span class="o">@</span><span class="n">app</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="s">"/predict"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">predict</span><span class="p">(</span><span class="n">transaction</span><span class="p">:</span> <span class="n">Transaction</span><span class="p">):</span>
    <span class="n">logger</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"Prediction request"</span><span class="p">,</span> <span class="n">extra</span><span class="o">=</span><span class="p">{</span>
        <span class="s">"transaction_id"</span><span class="p">:</span> <span class="n">transaction</span><span class="p">.</span><span class="nb">id</span><span class="p">,</span>
        <span class="s">"amount"</span><span class="p">:</span> <span class="n">transaction</span><span class="p">.</span><span class="n">amount</span>
    <span class="p">})</span>
    
    <span class="n">prediction</span> <span class="o">=</span> <span class="n">model</span><span class="p">.</span><span class="n">predict</span><span class="p">(...)</span>
    
    <span class="n">logger</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"Prediction made"</span><span class="p">,</span> <span class="n">extra</span><span class="o">=</span><span class="p">{</span>
        <span class="s">"transaction_id"</span><span class="p">:</span> <span class="n">transaction</span><span class="p">.</span><span class="nb">id</span><span class="p">,</span>
        <span class="s">"prediction"</span><span class="p">:</span> <span class="n">prediction</span><span class="p">,</span>
        <span class="s">"latency_ms"</span><span class="p">:</span> <span class="n">latency</span>
    <span class="p">})</span>
    
    <span class="k">return</span> <span class="p">{</span><span class="s">"prediction"</span><span class="p">:</span> <span class="n">prediction</span><span class="p">}</span>
</code></pre></div></div>

<h2 id="6-feature-store">6. Feature Store</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">feast</span> <span class="kn">import</span> <span class="n">FeatureStore</span><span class="p">,</span> <span class="n">Entity</span><span class="p">,</span> <span class="n">FeatureView</span><span class="p">,</span> <span class="n">Field</span>
<span class="kn">from</span> <span class="nn">feast.types</span> <span class="kn">import</span> <span class="n">Int64</span><span class="p">,</span> <span class="n">Float32</span>
<span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">timedelta</span>

<span class="c1"># Definir entity
</span><span class="n">user</span> <span class="o">=</span> <span class="n">Entity</span><span class="p">(</span>
    <span class="n">name</span><span class="o">=</span><span class="s">"user"</span><span class="p">,</span>
    <span class="n">join_keys</span><span class="o">=</span><span class="p">[</span><span class="s">"user_id"</span><span class="p">]</span>
<span class="p">)</span>

<span class="c1"># Definir feature view
</span><span class="n">user_features</span> <span class="o">=</span> <span class="n">FeatureView</span><span class="p">(</span>
    <span class="n">name</span><span class="o">=</span><span class="s">"user_transaction_features"</span><span class="p">,</span>
    <span class="n">entities</span><span class="o">=</span><span class="p">[</span><span class="n">user</span><span class="p">],</span>
    <span class="n">ttl</span><span class="o">=</span><span class="n">timedelta</span><span class="p">(</span><span class="n">days</span><span class="o">=</span><span class="mi">1</span><span class="p">),</span>
    <span class="n">schema</span><span class="o">=</span><span class="p">[</span>
        <span class="n">Field</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s">"avg_transaction_amount"</span><span class="p">,</span> <span class="n">dtype</span><span class="o">=</span><span class="n">Float32</span><span class="p">),</span>
        <span class="n">Field</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s">"transaction_count_7d"</span><span class="p">,</span> <span class="n">dtype</span><span class="o">=</span><span class="n">Int64</span><span class="p">),</span>
        <span class="n">Field</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s">"max_transaction_amount"</span><span class="p">,</span> <span class="n">dtype</span><span class="o">=</span><span class="n">Float32</span><span class="p">)</span>
    <span class="p">],</span>
    <span class="n">source</span><span class="o">=</span><span class="n">BigQuerySource</span><span class="p">(</span>
        <span class="n">table</span><span class="o">=</span><span class="s">"project.dataset.user_features"</span>
    <span class="p">)</span>
<span class="p">)</span>

<span class="c1"># Usar en training
</span><span class="n">fs</span> <span class="o">=</span> <span class="n">FeatureStore</span><span class="p">(</span><span class="s">"."</span><span class="p">)</span>
<span class="n">features</span> <span class="o">=</span> <span class="n">fs</span><span class="p">.</span><span class="n">get_historical_features</span><span class="p">(</span>
    <span class="n">entity_df</span><span class="o">=</span><span class="n">training_data</span><span class="p">,</span>
    <span class="n">features</span><span class="o">=</span><span class="p">[</span>
        <span class="s">"user_transaction_features:avg_transaction_amount"</span><span class="p">,</span>
        <span class="s">"user_transaction_features:transaction_count_7d"</span>
    <span class="p">]</span>
<span class="p">).</span><span class="n">to_df</span><span class="p">()</span>

<span class="c1"># Usar en inference (online serving)
</span><span class="n">features</span> <span class="o">=</span> <span class="n">fs</span><span class="p">.</span><span class="n">get_online_features</span><span class="p">(</span>
    <span class="n">features</span><span class="o">=</span><span class="p">[</span>
        <span class="s">"user_transaction_features:avg_transaction_amount"</span>
    <span class="p">],</span>
    <span class="n">entity_rows</span><span class="o">=</span><span class="p">[{</span><span class="s">"user_id"</span><span class="p">:</span> <span class="mi">12345</span><span class="p">}]</span>
<span class="p">).</span><span class="n">to_dict</span><span class="p">()</span>
</code></pre></div></div>

<h2 id="best-practices">Best Practices</h2>

<h3 id="1-reproducibilidad">1. Reproducibilidad</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># requirements.txt con versiones fijas
</span><span class="n">numpy</span><span class="o">==</span><span class="mf">1.24</span><span class="p">.</span><span class="mi">3</span>
<span class="n">scikit</span><span class="o">-</span><span class="n">learn</span><span class="o">==</span><span class="mf">1.3</span><span class="p">.</span><span class="mi">0</span>
<span class="n">pandas</span><span class="o">==</span><span class="mf">2.0</span><span class="p">.</span><span class="mi">3</span>

<span class="c1"># Seed para reproducibilidad
</span><span class="n">np</span><span class="p">.</span><span class="n">random</span><span class="p">.</span><span class="n">seed</span><span class="p">(</span><span class="mi">42</span><span class="p">)</span>
<span class="n">random</span><span class="p">.</span><span class="n">seed</span><span class="p">(</span><span class="mi">42</span><span class="p">)</span>

<span class="c1"># Guardar environment info
</span><span class="kn">import</span> <span class="nn">platform</span>
<span class="n">info</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">"python_version"</span><span class="p">:</span> <span class="n">platform</span><span class="p">.</span><span class="n">python_version</span><span class="p">(),</span>
    <span class="s">"packages"</span><span class="p">:</span> <span class="p">{</span><span class="n">pkg</span><span class="p">.</span><span class="n">key</span><span class="p">:</span> <span class="n">pkg</span><span class="p">.</span><span class="n">version</span> <span class="k">for</span> <span class="n">pkg</span> <span class="ow">in</span> <span class="n">pkg_resources</span><span class="p">.</span><span class="n">working_set</span><span class="p">}</span>
<span class="p">}</span>
<span class="n">mlflow</span><span class="p">.</span><span class="n">log_dict</span><span class="p">(</span><span class="n">info</span><span class="p">,</span> <span class="s">"environment.json"</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="2-model-versioning">2. Model Versioning</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>models/
├── v1.0.0/
│   ├── model.pkl
│   ├── scaler.pkl
│   └── metadata.json
├── v1.1.0/
│   ├── model.pkl
│   └── metadata.json
└── current -&gt; v1.1.0/  # Symlink
</code></pre></div></div>

<h3 id="3-ab-testing">3. A/B Testing</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">random</span>

<span class="k">def</span> <span class="nf">get_model_version</span><span class="p">(</span><span class="n">user_id</span><span class="p">):</span>
    <span class="s">"""Route 10% traffic to new model"""</span>
    <span class="k">if</span> <span class="nb">hash</span><span class="p">(</span><span class="n">user_id</span><span class="p">)</span> <span class="o">%</span> <span class="mi">100</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="p">:</span>
        <span class="k">return</span> <span class="s">"v2-challenger"</span>
    <span class="k">return</span> <span class="s">"v1-champion"</span>

<span class="o">@</span><span class="n">app</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="s">"/predict"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">predict</span><span class="p">(</span><span class="n">transaction</span><span class="p">:</span> <span class="n">Transaction</span><span class="p">):</span>
    <span class="n">model_version</span> <span class="o">=</span> <span class="n">get_model_version</span><span class="p">(</span><span class="n">transaction</span><span class="p">.</span><span class="n">user_id</span><span class="p">)</span>
    <span class="n">model</span> <span class="o">=</span> <span class="n">models</span><span class="p">[</span><span class="n">model_version</span><span class="p">]</span>
    
    <span class="n">prediction</span> <span class="o">=</span> <span class="n">model</span><span class="p">.</span><span class="n">predict</span><span class="p">(...)</span>
    
    <span class="c1"># Log for analysis
</span>    <span class="n">mlflow</span><span class="p">.</span><span class="n">log_metric</span><span class="p">(</span><span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">model_version</span><span class="si">}</span><span class="s">_prediction"</span><span class="p">,</span> <span class="n">prediction</span><span class="p">)</span>
    
    <span class="k">return</span> <span class="p">{</span><span class="s">"prediction"</span><span class="p">:</span> <span class="n">prediction</span><span class="p">}</span>
</code></pre></div></div>

<h2 id="herramientas-del-ecosistema">Herramientas del Ecosistema</h2>

<table>
  <thead>
    <tr>
      <th>Categoría</th>
      <th>Herramientas</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Data Versioning</strong></td>
      <td>DVC, Pachyderm, lakeFS</td>
    </tr>
    <tr>
      <td><strong>Experiment Tracking</strong></td>
      <td>MLflow, Weights &amp; Biases, Neptune</td>
    </tr>
    <tr>
      <td><strong>Feature Store</strong></td>
      <td>Feast, Tecton, Hopsworks</td>
    </tr>
    <tr>
      <td><strong>Model Serving</strong></td>
      <td>MLflow, BentoML, Seldon, KServe</td>
    </tr>
    <tr>
      <td><strong>Monitoring</strong></td>
      <td>Evidently, WhyLabs, Arize</td>
    </tr>
    <tr>
      <td><strong>Orchestration</strong></td>
      <td>Airflow, Prefect, Kubeflow</td>
    </tr>
    <tr>
      <td><strong>CI/CD</strong></td>
      <td>GitHub Actions, GitLab CI, Jenkins</td>
    </tr>
  </tbody>
</table>

<h2 id="conclusión">Conclusión</h2>

<p>MLOps convierte experimentos en productos:</p>

<ul>
  <li>✅ <strong>Reproducibilidad</strong>: DVC versiona data, Git versiona código</li>
  <li>✅ <strong>Trazabilidad</strong>: MLflow trackea experimentos y modelos</li>
  <li>✅ <strong>Automation</strong>: CI/CD entrena y deploya automáticamente</li>
  <li>✅ <strong>Monitoring</strong>: Detecta drift y degradación</li>
  <li>✅ <strong>Escalabilidad</strong>: Docker + Kubernetes para production</li>
</ul>

<p><strong>Next Steps:</strong></p>
<ol>
  <li>Implementar DVC para versionar tu data</li>
  <li>Trackear experimentos con MLflow</li>
  <li>Automatizar training con GitHub Actions</li>
  <li>Deployar modelo con Docker + FastAPI</li>
  <li>Monitorear performance en producción</li>
</ol>

<hr />

<p><strong>Recursos:</strong></p>
<ul>
  <li><a href="https://mlflow.org/docs/latest/index.html">MLflow Docs</a></li>
  <li><a href="https://dvc.org/doc/start">DVC Tutorial</a></li>
  <li><a href="https://docs.evidentlyai.com/">Evidently AI</a></li>
</ul>]]></content><author><name>AI Tech Team</name></author><category term="MLOps" /><category term="mlops" /><category term="mlflow" /><category term="dvc" /><category term="ci-cd" /><category term="monitoring" /><category term="deployment" /><summary type="html"><![CDATA[Pipeline completo de MLOps: versionado de datos con DVC, tracking con MLflow, CI/CD con GitHub Actions, monitoring en producción.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://images.unsplash.com/photo-1518432031352-d6fc5c10da5a?w=800" /><media:content medium="image" url="https://images.unsplash.com/photo-1518432031352-d6fc5c10da5a?w=800" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Ejemplo de Post con Todas las Características</title><link href="https://dev-jcgi.github.io/blog/blog/2026/01/28/ejemplo-completo/" rel="alternate" type="text/html" title="Ejemplo de Post con Todas las Características" /><published>2026-01-28T00:00:00-06:00</published><updated>2026-01-28T00:00:00-06:00</updated><id>https://dev-jcgi.github.io/blog/blog/2026/01/28/ejemplo-completo</id><content type="html" xml:base="https://dev-jcgi.github.io/blog/blog/2026/01/28/ejemplo-completo/"><![CDATA[<h2 id="introducción">Introducción</h2>

<p>Este post de ejemplo demuestra todas las increíbles características que has implementado en tu blog Jekyll. Desde búsqueda avanzada hasta PWA, este blog tiene todo lo que necesitas para una experiencia moderna.</p>

<h2 id="características-destacadas">Características Destacadas</h2>

<h3 id="1-tabla-de-contenidos-automática">1. Tabla de Contenidos Automática</h3>

<p>Esta tabla de contenidos se genera automáticamente basándose en los encabezados H2, H3 y H4 de tu contenido. Puedes navegar fácilmente por secciones largas.</p>

<h3 id="2-modo-clarooscuro">2. Modo Claro/Oscuro</h3>

<p>Usa el toggle en el header para cambiar entre temas claro y oscuro. Tu preferencia se guarda automáticamente.</p>

<h3 id="3-búsqueda-potente">3. Búsqueda Potente</h3>

<p>Ve a <code class="language-plaintext highlighter-rouge">/buscar/</code> para probar la búsqueda con Lunr.js. Busca por:</p>
<ul>
  <li>Títulos de posts</li>
  <li>Contenido</li>
  <li>Categorías</li>
  <li>Tags</li>
</ul>

<h2 id="sintaxis-y-formato">Sintaxis y Formato</h2>

<h3 id="código-inline">Código Inline</h3>

<p>Puedes usar <code class="language-plaintext highlighter-rouge">código inline</code> así: <code class="language-plaintext highlighter-rouge">console.log('Hola Mundo')</code>.</p>

<h3 id="bloques-de-código">Bloques de Código</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">saludo</span><span class="p">(</span><span class="n">nombre</span><span class="p">):</span>
    <span class="s">"""Función de ejemplo en Python"""</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"¡Hola, </span><span class="si">{</span><span class="n">nombre</span><span class="si">}</span><span class="s">!"</span><span class="p">)</span>
    
<span class="n">saludo</span><span class="p">(</span><span class="s">"Mundo"</span><span class="p">)</span>
</code></pre></div></div>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Código JavaScript</span>
<span class="kd">function</span> <span class="nx">calcular</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">a</span> <span class="o">+</span> <span class="nx">b</span><span class="p">;</span>
<span class="p">}</span>

<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">calcular</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">3</span><span class="p">));</span>
</code></pre></div></div>

<h3 id="listas">Listas</h3>

<p><strong>Lista ordenada:</strong></p>
<ol>
  <li>Primer elemento</li>
  <li>Segundo elemento</li>
  <li>Tercer elemento</li>
</ol>

<p><strong>Lista desordenada:</strong></p>
<ul>
  <li>Item uno</li>
  <li>Item dos</li>
  <li>Item tres
    <ul>
      <li>Subitem</li>
      <li>Otro subitem</li>
    </ul>
  </li>
</ul>

<h3 id="citas">Citas</h3>

<blockquote>
  <p>“La inteligencia artificial es el nuevo motor de la transformación digital. No se trata solo de automatizar tareas, sino de reimaginar completamente cómo trabajamos y creamos valor.”</p>

  <p>— Experto en IA</p>
</blockquote>

<h3 id="enlaces">Enlaces</h3>

<p>Visita <a href="https://jekyllrb.com/">la documentación de Jekyll</a> para aprender más sobre cómo personalizar tu blog.</p>

<h3 id="imágenes">Imágenes</h3>

<p><img src="https://images.unsplash.com/photo-1498050108023-c5249f4df085?w=600" alt="Ejemplo de imagen" /></p>

<p><em>Imagen de ejemplo con texto alternativo</em></p>

<h2 id="compartir-es-importante">Compartir es Importante</h2>

<p>Al final de este post encontrarás botones mejorados para compartir en:</p>
<ul>
  <li>Twitter</li>
  <li>LinkedIn</li>
  <li>Facebook</li>
  <li>WhatsApp</li>
  <li>Telegram</li>
  <li>Copiar enlace</li>
</ul>

<h2 id="posts-relacionados">Posts Relacionados</h2>

<p>El sistema mostrará automáticamente hasta 3 posts relacionados basándose en:</p>
<ul>
  <li>Misma categoría</li>
  <li>Tags en común</li>
</ul>

<h2 id="comentarios">Comentarios</h2>

<p>Si has configurado Utterances o Disqus, verás la sección de comentarios al final de este post.</p>

<h2 id="pwa---progressive-web-app">PWA - Progressive Web App</h2>

<p>Este blog funciona como una aplicación:</p>
<ul>
  <li><strong>Instalable</strong>: Puedes instalarlo en tu dispositivo</li>
  <li><strong>Offline</strong>: Funciona sin conexión</li>
  <li><strong>Rápido</strong>: Caché inteligente para cargas instantáneas</li>
</ul>

<h2 id="analytics">Analytics</h2>

<p>El blog tiene soporte para:</p>
<ul>
  <li>Google Analytics</li>
  <li>Plausible Analytics (alternativa privacy-focused)</li>
</ul>

<h2 id="más-características">Más Características</h2>

<h3 id="barra-de-progreso">Barra de Progreso</h3>

<p>Nota la barra de progreso en la parte superior que muestra cuánto has leído del artículo.</p>

<h3 id="navegación-entre-posts">Navegación entre Posts</h3>

<p>Al final del post hay botones para navegar al post anterior y siguiente.</p>

<h3 id="archivo-por-fecha">Archivo por Fecha</h3>

<p>Visita <code class="language-plaintext highlighter-rouge">/archivo/</code> para ver todos los posts organizados por año y mes en un timeline visual.</p>

<h3 id="categorías-y-tags">Categorías y Tags</h3>

<ul>
  <li><code class="language-plaintext highlighter-rouge">/categorias/</code> - Explora posts por categoría</li>
  <li><code class="language-plaintext highlighter-rouge">/tags/</code> - Nube de tags interactiva</li>
</ul>

<h2 id="conclusión">Conclusión</h2>

<p>Tu blog Jekyll ahora tiene todas las características modernas que los usuarios esperan:</p>

<p>✅ Paginación inteligente
✅ Búsqueda avanzada
✅ Modo claro/oscuro
✅ Compartir en redes sociales
✅ Sistema de comentarios
✅ Analytics integrado
✅ PWA para experiencia offline
✅ Posts relacionados
✅ Tabla de contenidos automática
✅ Archivo temporal
✅ Organización por categorías y tags</p>

<p>¡Disfruta tu nuevo blog y que tengas éxito con tus contenidos!</p>

<h2 id="recursos-adicionales">Recursos Adicionales</h2>

<p>Para más información sobre cómo usar estas características, consulta el archivo <a href="../FEATURES.md">FEATURES.md</a> en la raíz del proyecto.</p>

<h3 id="tips-de-uso">Tips de Uso</h3>

<ol>
  <li><strong>Escribe con Markdown</strong>: Todo el contenido se escribe en Markdown, un formato simple y potente</li>
  <li><strong>Usa Front Matter</strong>: Configura cada post con los metadatos correctos</li>
  <li><strong>Optimiza imágenes</strong>: Usa URLs de CDN o imágenes optimizadas</li>
  <li><strong>Escribe buenos títulos</strong>: Son importantes para SEO y compartir en redes</li>
  <li><strong>Usa tags relevantes</strong>: Ayudan a los lectores a encontrar contenido relacionado</li>
</ol>

<hr />

<p><em>Este es un post de ejemplo. Puedes editarlo o eliminarlo según tus necesidades.</em></p>]]></content><author><name>AI Tech Team</name></author><category term="Tutorial" /><category term="Jekyll" /><category term="Blog" /><category term="Ejemplo" /><summary type="html"><![CDATA[Este es un post de ejemplo que demuestra todas las características implementadas en el blog.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://images.unsplash.com/photo-1555066931-4365d14bab8c?w=800" /><media:content medium="image" url="https://images.unsplash.com/photo-1555066931-4365d14bab8c?w=800" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Fine-tuning LLMs con LoRA y PEFT</title><link href="https://dev-jcgi.github.io/blog/blog/2026/01/27/fine-tuning-lora-peft/" rel="alternate" type="text/html" title="Fine-tuning LLMs con LoRA y PEFT" /><published>2026-01-27T11:00:00-06:00</published><updated>2026-01-27T11:00:00-06:00</updated><id>https://dev-jcgi.github.io/blog/blog/2026/01/27/fine-tuning-lora-peft</id><content type="html" xml:base="https://dev-jcgi.github.io/blog/blog/2026/01/27/fine-tuning-lora-peft/"><![CDATA[<p>Fine-tuning completo de LLMs requiere <strong>cientos de GB de VRAM</strong> y días de entrenamiento. <strong>LoRA</strong> (Low-Rank Adaptation) resuelve esto ajustando solo un 0.1% de losparámetros con resultados casi idénticos.</p>

<h2 id="el-problema-del-fine-tuning-tradicional">El Problema del Fine-tuning Tradicional</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Full fine-tuning de LLaMA-7B
</span><span class="n">model</span> <span class="o">=</span> <span class="n">AutoModelForCausalLM</span><span class="p">.</span><span class="n">from_pretrained</span><span class="p">(</span><span class="s">"meta-llama/Llama-2-7b-hf"</span><span class="p">)</span>

<span class="c1"># Problema:
# - 7B parámetros × 4 bytes = 28 GB solo del modelo
# - + Gradientes (28 GB) + Optimizer states (56 GB) = 112 GB VRAM total
# - Training time: ~40 horas en 8x A100
# - Costo: ~$500-1000
</span></code></pre></div></div>

<p><strong>¿La solución?</strong> Parameter-Efficient Fine-Tuning (PEFT)</p>

<h2 id="qué-es-lora">¿Qué es LoRA?</h2>

<p>LoRA congela el modelo pre-entrenado y agrega matrices de <strong>bajo rango</strong> (low-rank) que se entrenan:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>W = W₀ + ΔW
ΔW = BA

Donde:
- W₀: pesos originales (frozen)
- B: matriz d × r (trainable)
- A: matriz r × k (trainable)
- r &lt;&lt; min(d, k) (rango bajo, típicamente 8-64)
</code></pre></div></div>

<h3 id="ventajas-de-lora">Ventajas de LoRA</h3>

<table>
  <thead>
    <tr>
      <th>Métrica</th>
      <th>Full Fine-tuning</th>
      <th>LoRA (r=16)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Parámetros entrenables</strong></td>
      <td>7B (100%)</td>
      <td>8.4M (0.12%)</td>
    </tr>
    <tr>
      <td><strong>VRAM requerida</strong></td>
      <td>112 GB</td>
      <td>12 GB</td>
    </tr>
    <tr>
      <td><strong>Training time</strong></td>
      <td>40h</td>
      <td>6h</td>
    </tr>
    <tr>
      <td><strong>Costo</strong></td>
      <td>$800</td>
      <td>$60</td>
    </tr>
    <tr>
      <td><strong>Performance</strong></td>
      <td>100%</td>
      <td>97-99%</td>
    </tr>
  </tbody>
</table>

<h2 id="implementación-con-peft">Implementación con PEFT</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">transformers</span> <span class="kn">import</span> <span class="n">AutoModelForCausalLM</span><span class="p">,</span> <span class="n">AutoTokenizer</span><span class="p">,</span> <span class="n">TrainingArguments</span>
<span class="kn">from</span> <span class="nn">peft</span> <span class="kn">import</span> <span class="n">LoraConfig</span><span class="p">,</span> <span class="n">get_peft_model</span><span class="p">,</span> <span class="n">prepare_model_for_kbit_training</span>
<span class="kn">from</span> <span class="nn">datasets</span> <span class="kn">import</span> <span class="n">load_dataset</span>
<span class="kn">import</span> <span class="nn">torch</span>

<span class="c1"># 1. Cargar modelo base
</span><span class="n">model_name</span> <span class="o">=</span> <span class="s">"meta-llama/Llama-2-7b-hf"</span>
<span class="n">model</span> <span class="o">=</span> <span class="n">AutoModelForCausalLM</span><span class="p">.</span><span class="n">from_pretrained</span><span class="p">(</span>
    <span class="n">model_name</span><span class="p">,</span>
    <span class="n">load_in_8bit</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>  <span class="c1"># Cuantización para ahorrar memoria
</span>    <span class="n">device_map</span><span class="o">=</span><span class="s">"auto"</span><span class="p">,</span>
    <span class="n">torch_dtype</span><span class="o">=</span><span class="n">torch</span><span class="p">.</span><span class="n">float16</span>
<span class="p">)</span>
<span class="n">tokenizer</span> <span class="o">=</span> <span class="n">AutoTokenizer</span><span class="p">.</span><span class="n">from_pretrained</span><span class="p">(</span><span class="n">model_name</span><span class="p">)</span>

<span class="c1"># 2. Configurar LoRA
</span><span class="n">lora_config</span> <span class="o">=</span> <span class="n">LoraConfig</span><span class="p">(</span>
    <span class="n">r</span><span class="o">=</span><span class="mi">16</span><span class="p">,</span>                    <span class="c1"># Rank (dimensión del low-rank)
</span>    <span class="n">lora_alpha</span><span class="o">=</span><span class="mi">32</span><span class="p">,</span>           <span class="c1"># Scaling factor (típ. 2×r)
</span>    <span class="n">target_modules</span><span class="o">=</span><span class="p">[</span>         <span class="c1"># Qué layers modificar
</span>        <span class="s">"q_proj"</span><span class="p">,</span>            <span class="c1"># Query projection
</span>        <span class="s">"k_proj"</span><span class="p">,</span>            <span class="c1"># Key projection
</span>        <span class="s">"v_proj"</span><span class="p">,</span>            <span class="c1"># Value projection
</span>        <span class="s">"o_proj"</span>             <span class="c1"># Output projection
</span>    <span class="p">],</span>
    <span class="n">lora_dropout</span><span class="o">=</span><span class="mf">0.05</span><span class="p">,</span>       <span class="c1"># Dropout para regularización
</span>    <span class="n">bias</span><span class="o">=</span><span class="s">"none"</span><span class="p">,</span>             <span class="c1"># No entrenar bias
</span>    <span class="n">task_type</span><span class="o">=</span><span class="s">"CAUSAL_LM"</span>    <span class="c1"># Tipo de tarea
</span><span class="p">)</span>

<span class="c1"># 3. Preparar modelo para training
</span><span class="n">model</span> <span class="o">=</span> <span class="n">prepare_model_for_kbit_training</span><span class="p">(</span><span class="n">model</span><span class="p">)</span>
<span class="n">model</span> <span class="o">=</span> <span class="n">get_peft_model</span><span class="p">(</span><span class="n">model</span><span class="p">,</span> <span class="n">lora_config</span><span class="p">)</span>

<span class="c1"># Verificar parámetros entrenables
</span><span class="n">model</span><span class="p">.</span><span class="n">print_trainable_parameters</span><span class="p">()</span>
<span class="c1"># Output: trainable params: 8.4M || all params: 6.74B || trainable%: 0.124%
</span>
<span class="c1"># 4. Cargar dataset
</span><span class="n">dataset</span> <span class="o">=</span> <span class="n">load_dataset</span><span class="p">(</span><span class="s">"json"</span><span class="p">,</span> <span class="n">data_files</span><span class="o">=</span><span class="s">"training_data.json"</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">format_instruction</span><span class="p">(</span><span class="n">example</span><span class="p">):</span>
    <span class="s">"""Formato tipo Alpaca"""</span>
    <span class="k">return</span> <span class="sa">f</span><span class="s">"""### Instruction:
</span><span class="si">{</span><span class="n">example</span><span class="p">[</span><span class="s">'instruction'</span><span class="p">]</span><span class="si">}</span><span class="s">

### Input:
</span><span class="si">{</span><span class="n">example</span><span class="p">[</span><span class="s">'input'</span><span class="p">]</span><span class="si">}</span><span class="s">

### Response:
</span><span class="si">{</span><span class="n">example</span><span class="p">[</span><span class="s">'output'</span><span class="p">]</span><span class="si">}</span><span class="s">"""</span>

<span class="k">def</span> <span class="nf">tokenize_function</span><span class="p">(</span><span class="n">examples</span><span class="p">):</span>
    <span class="n">texts</span> <span class="o">=</span> <span class="p">[</span><span class="n">format_instruction</span><span class="p">(</span><span class="n">ex</span><span class="p">)</span> <span class="k">for</span> <span class="n">ex</span> <span class="ow">in</span> <span class="n">examples</span><span class="p">]</span>
    <span class="k">return</span> <span class="n">tokenizer</span><span class="p">(</span>
        <span class="n">texts</span><span class="p">,</span>
        <span class="n">truncation</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
        <span class="n">max_length</span><span class="o">=</span><span class="mi">2048</span><span class="p">,</span>
        <span class="n">padding</span><span class="o">=</span><span class="s">"max_length"</span>
    <span class="p">)</span>

<span class="n">tokenized_dataset</span> <span class="o">=</span> <span class="n">dataset</span><span class="p">.</span><span class="nb">map</span><span class="p">(</span><span class="n">tokenize_function</span><span class="p">,</span> <span class="n">batched</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>

<span class="c1"># 5. Training arguments
</span><span class="n">training_args</span> <span class="o">=</span> <span class="n">TrainingArguments</span><span class="p">(</span>
    <span class="n">output_dir</span><span class="o">=</span><span class="s">"./lora_results"</span><span class="p">,</span>
    <span class="n">num_train_epochs</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span>
    <span class="n">per_device_train_batch_size</span><span class="o">=</span><span class="mi">4</span><span class="p">,</span>
    <span class="n">gradient_accumulation_steps</span><span class="o">=</span><span class="mi">4</span><span class="p">,</span>
    <span class="n">learning_rate</span><span class="o">=</span><span class="mf">2e-4</span><span class="p">,</span>
    <span class="n">fp16</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
    <span class="n">logging_steps</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span>
    <span class="n">save_steps</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span>
    <span class="n">evaluation_strategy</span><span class="o">=</span><span class="s">"steps"</span><span class="p">,</span>
    <span class="n">eval_steps</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span>
    <span class="n">warmup_steps</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span>
    <span class="n">optim</span><span class="o">=</span><span class="s">"adamw_torch"</span>
<span class="p">)</span>

<span class="c1"># 6. Entrenar
</span><span class="kn">from</span> <span class="nn">transformers</span> <span class="kn">import</span> <span class="n">Trainer</span>

<span class="n">trainer</span> <span class="o">=</span> <span class="n">Trainer</span><span class="p">(</span>
    <span class="n">model</span><span class="o">=</span><span class="n">model</span><span class="p">,</span>
    <span class="n">args</span><span class="o">=</span><span class="n">training_args</span><span class="p">,</span>
    <span class="n">train_dataset</span><span class="o">=</span><span class="n">tokenized_dataset</span><span class="p">[</span><span class="s">"train"</span><span class="p">],</span>
    <span class="n">eval_dataset</span><span class="o">=</span><span class="n">tokenized_dataset</span><span class="p">[</span><span class="s">"validation"</span><span class="p">]</span>
<span class="p">)</span>

<span class="n">trainer</span><span class="p">.</span><span class="n">train</span><span class="p">()</span>

<span class="c1"># 7. Guardar adaptadores LoRA (solo 16-32 MB!)
</span><span class="n">model</span><span class="p">.</span><span class="n">save_pretrained</span><span class="p">(</span><span class="s">"./lora_adapter"</span><span class="p">)</span>
<span class="n">tokenizer</span><span class="p">.</span><span class="n">save_pretrained</span><span class="p">(</span><span class="s">"./lora_adapter"</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="qlora-lora--cuantización">QLoRA: LoRA + Cuantización</h2>

<p><strong>QLoRA</strong> combina LoRA con cuantización de 4 bits:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">transformers</span> <span class="kn">import</span> <span class="n">BitsAndBytesConfig</span>

<span class="c1"># Configurar cuantización 4-bit
</span><span class="n">bnb_config</span> <span class="o">=</span> <span class="n">BitsAndBytesConfig</span><span class="p">(</span>
    <span class="n">load_in_4bit</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
    <span class="n">bnb_4bit_quant_type</span><span class="o">=</span><span class="s">"nf4"</span><span class="p">,</span>           <span class="c1"># Normal Float 4
</span>    <span class="n">bnb_4bit_use_double_quant</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>      <span class="c1"># Double quantization
</span>    <span class="n">bnb_4bit_compute_dtype</span><span class="o">=</span><span class="n">torch</span><span class="p">.</span><span class="n">bfloat16</span>
<span class="p">)</span>

<span class="c1"># Cargar modelo cuantizado
</span><span class="n">model</span> <span class="o">=</span> <span class="n">AutoModelForCausalLM</span><span class="p">.</span><span class="n">from_pretrained</span><span class="p">(</span>
    <span class="s">"meta-llama/Llama-2-7b-hf"</span><span class="p">,</span>
    <span class="n">quantization_config</span><span class="o">=</span><span class="n">bnb_config</span><span class="p">,</span>
    <span class="n">device_map</span><span class="o">=</span><span class="s">"auto"</span>
<span class="p">)</span>

<span class="c1"># Resultado:
# - LLaMA-7B en 4-bit: ~4.5 GB VRAM
# - + LoRA adapters: ~500 MB
# - Total: ~5 GB VRAM (puedes fine-tunear en una RTX 3090!)
</span></code></pre></div></div>

<h3 id="comparación-lora-vs-qlora">Comparación: LoRA vs QLoRA</h3>

<table>
  <thead>
    <tr>
      <th>Configuración</th>
      <th>VRAM</th>
      <th>Training Time</th>
      <th>Performance</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Full FP32</td>
      <td>112 GB</td>
      <td>40h</td>
      <td>100%</td>
    </tr>
    <tr>
      <td>LoRA FP16</td>
      <td>12 GB</td>
      <td>6h</td>
      <td>98%</td>
    </tr>
    <tr>
      <td>QLoRA 8-bit</td>
      <td>8 GB</td>
      <td>7h</td>
      <td>97%</td>
    </tr>
    <tr>
      <td>QLoRA 4-bit</td>
      <td>5 GB</td>
      <td>8h</td>
      <td>95%</td>
    </tr>
  </tbody>
</table>

<h2 id="hiperparámetros-clave">Hiperparámetros Clave</h2>

<h3 id="1-rango-r">1. Rango (r)</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># r = rank de las matrices low-rank
# Trade-off: performance vs memory
</span>
<span class="n">configs</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">"tiny"</span><span class="p">:</span> <span class="n">LoraConfig</span><span class="p">(</span><span class="n">r</span><span class="o">=</span><span class="mi">8</span><span class="p">),</span>      <span class="c1"># ~4M params, menos expresivo
</span>    <span class="s">"small"</span><span class="p">:</span> <span class="n">LoraConfig</span><span class="p">(</span><span class="n">r</span><span class="o">=</span><span class="mi">16</span><span class="p">),</span>    <span class="c1"># ~8M params, balance óptimo
</span>    <span class="s">"medium"</span><span class="p">:</span> <span class="n">LoraConfig</span><span class="p">(</span><span class="n">r</span><span class="o">=</span><span class="mi">32</span><span class="p">),</span>   <span class="c1"># ~16M params, más expresivo
</span>    <span class="s">"large"</span><span class="p">:</span> <span class="n">LoraConfig</span><span class="p">(</span><span class="n">r</span><span class="o">=</span><span class="mi">64</span><span class="p">)</span>     <span class="c1"># ~32M params, casi full fine-tuning
</span><span class="p">}</span>

<span class="c1"># Recomendación: empezar con r=16, subir si no converge
</span></code></pre></div></div>

<h3 id="2-alpha-α">2. Alpha (α)</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Scaling factor: ΔW × (α/r)
# Controla la "fuerza" de los adapters
</span>
<span class="n">lora_config</span> <span class="o">=</span> <span class="n">LoraConfig</span><span class="p">(</span>
    <span class="n">r</span><span class="o">=</span><span class="mi">16</span><span class="p">,</span>
    <span class="n">lora_alpha</span><span class="o">=</span><span class="mi">32</span>  <span class="c1"># Típicamente α = 2×r (estándar)
</span><span class="p">)</span>

<span class="c1"># α &lt; 2r: adapters más sutiles (menos overfitting)
# α = 2r: balance recomendado
# α &gt; 2r: adapters más agresivos (útil para datasets pequeños)
</span></code></pre></div></div>

<h3 id="3-target-modules">3. Target Modules</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># ¿Qué layers modificar con LoRA?
</span>
<span class="c1"># Opción 1: Solo attention (más eficiente)
</span><span class="n">target_modules</span> <span class="o">=</span> <span class="p">[</span><span class="s">"q_proj"</span><span class="p">,</span> <span class="s">"v_proj"</span><span class="p">]</span>

<span class="c1"># Opción 2: Todas las proyecciones de attention (recomendado)
</span><span class="n">target_modules</span> <span class="o">=</span> <span class="p">[</span><span class="s">"q_proj"</span><span class="p">,</span> <span class="s">"k_proj"</span><span class="p">,</span> <span class="s">"v_proj"</span><span class="p">,</span> <span class="s">"o_proj"</span><span class="p">]</span>

<span class="c1"># Opción 3: Attention + FFN (máximo performance)
</span><span class="n">target_modules</span> <span class="o">=</span> <span class="p">[</span>
    <span class="s">"q_proj"</span><span class="p">,</span> <span class="s">"k_proj"</span><span class="p">,</span> <span class="s">"v_proj"</span><span class="p">,</span> <span class="s">"o_proj"</span><span class="p">,</span>
    <span class="s">"gate_proj"</span><span class="p">,</span> <span class="s">"up_proj"</span><span class="p">,</span> <span class="s">"down_proj"</span>
<span class="p">]</span>

<span class="c1"># Trade-off: más modules = mejor performance pero más VRAM
</span></code></pre></div></div>

<h2 id="inference-con-lora">Inference con LoRA</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">peft</span> <span class="kn">import</span> <span class="n">PeftModel</span>

<span class="c1"># 1. Cargar modelo base
</span><span class="n">base_model</span> <span class="o">=</span> <span class="n">AutoModelForCausalLM</span><span class="p">.</span><span class="n">from_pretrained</span><span class="p">(</span>
    <span class="s">"meta-llama/Llama-2-7b-hf"</span><span class="p">,</span>
    <span class="n">load_in_8bit</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
    <span class="n">device_map</span><span class="o">=</span><span class="s">"auto"</span>
<span class="p">)</span>

<span class="c1"># 2. Cargar adaptadores LoRA
</span><span class="n">model</span> <span class="o">=</span> <span class="n">PeftModel</span><span class="p">.</span><span class="n">from_pretrained</span><span class="p">(</span><span class="n">base_model</span><span class="p">,</span> <span class="s">"./lora_adapter"</span><span class="p">)</span>

<span class="c1"># 3. Generar texto
</span><span class="n">tokenizer</span> <span class="o">=</span> <span class="n">AutoTokenizer</span><span class="p">.</span><span class="n">from_pretrained</span><span class="p">(</span><span class="s">"./lora_adapter"</span><span class="p">)</span>
<span class="n">inputs</span> <span class="o">=</span> <span class="n">tokenizer</span><span class="p">(</span><span class="s">"### Instruction: Explica qué es LoRA</span><span class="se">\n\n</span><span class="s">### Response:"</span><span class="p">,</span> <span class="n">return_tensors</span><span class="o">=</span><span class="s">"pt"</span><span class="p">)</span>
<span class="n">outputs</span> <span class="o">=</span> <span class="n">model</span><span class="p">.</span><span class="n">generate</span><span class="p">(</span><span class="o">**</span><span class="n">inputs</span><span class="p">,</span> <span class="n">max_length</span><span class="o">=</span><span class="mi">512</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">tokenizer</span><span class="p">.</span><span class="n">decode</span><span class="p">(</span><span class="n">outputs</span><span class="p">[</span><span class="mi">0</span><span class="p">]))</span>

<span class="c1"># 4. (Opcional) Merge adapters con modelo base para deployment
</span><span class="n">model</span> <span class="o">=</span> <span class="n">model</span><span class="p">.</span><span class="n">merge_and_unload</span><span class="p">()</span>
<span class="n">model</span><span class="p">.</span><span class="n">save_pretrained</span><span class="p">(</span><span class="s">"./merged_model"</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="otras-técnicas-peft">Otras Técnicas PEFT</h2>

<h3 id="1-prefix-tuning">1. Prefix Tuning</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">peft</span> <span class="kn">import</span> <span class="n">PrefixTuningConfig</span>

<span class="n">config</span> <span class="o">=</span> <span class="n">PrefixTuningConfig</span><span class="p">(</span>
    <span class="n">task_type</span><span class="o">=</span><span class="s">"CAUSAL_LM"</span><span class="p">,</span>
    <span class="n">num_virtual_tokens</span><span class="o">=</span><span class="mi">20</span><span class="p">,</span>  <span class="c1"># Tokens "virtuales" prepended
</span>    <span class="n">encoder_hidden_size</span><span class="o">=</span><span class="mi">4096</span>
<span class="p">)</span>

<span class="c1"># Entrena embeddings virtuales en lugar de pesos del modelo
</span></code></pre></div></div>

<h3 id="2-prompt-tuning">2. Prompt Tuning</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">peft</span> <span class="kn">import</span> <span class="n">PromptTuningConfig</span>

<span class="n">config</span> <span class="o">=</span> <span class="n">PromptTuningConfig</span><span class="p">(</span>
    <span class="n">task_type</span><span class="o">=</span><span class="s">"CAUSAL_LM"</span><span class="p">,</span>
    <span class="n">num_virtual_tokens</span><span class="o">=</span><span class="mi">8</span><span class="p">,</span>
    <span class="n">prompt_tuning_init</span><span class="o">=</span><span class="s">"TEXT"</span><span class="p">,</span>
    <span class="n">prompt_tuning_init_text</span><span class="o">=</span><span class="s">"Classify if the sentiment is positive or negative:"</span>
<span class="p">)</span>

<span class="c1"># Similar a prefix tuning pero más simple
</span></code></pre></div></div>

<h3 id="3-adalora-adaptive-lora">3. AdaLoRA (Adaptive LoRA)</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">peft</span> <span class="kn">import</span> <span class="n">AdaLoraConfig</span>

<span class="n">config</span> <span class="o">=</span> <span class="n">AdaLoraConfig</span><span class="p">(</span>
    <span class="n">r</span><span class="o">=</span><span class="mi">8</span><span class="p">,</span>
    <span class="n">target_r</span><span class="o">=</span><span class="mi">4</span><span class="p">,</span>  <span class="c1"># Rank objetivo dinámico
</span>    <span class="n">init_r</span><span class="o">=</span><span class="mi">12</span><span class="p">,</span>   <span class="c1"># Rank inicial
</span>    <span class="n">tinit</span><span class="o">=</span><span class="mi">200</span><span class="p">,</span>   <span class="c1"># Steps para adaptar rank
</span>    <span class="n">tfinal</span><span class="o">=</span><span class="mi">1000</span>
<span class="p">)</span>

<span class="c1"># Ajusta automáticamente el rank durante training
</span></code></pre></div></div>

<h2 id="dataset-preparation">Dataset Preparation</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">prepare_instruction_dataset</span><span class="p">(</span><span class="n">examples</span><span class="p">):</span>
    <span class="s">"""
    Formato Alpaca para instruction following
    """</span>
    <span class="n">formatted</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">ex</span> <span class="ow">in</span> <span class="n">examples</span><span class="p">:</span>
        <span class="n">text</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"""Below is an instruction that describes a task. Write a response that appropriately completes the request.

### Instruction:
</span><span class="si">{</span><span class="n">ex</span><span class="p">[</span><span class="s">'instruction'</span><span class="p">]</span><span class="si">}</span><span class="s">

### Input:
</span><span class="si">{</span><span class="n">ex</span><span class="p">[</span><span class="s">'input'</span><span class="p">]</span> <span class="k">if</span> <span class="s">'input'</span> <span class="ow">in</span> <span class="n">ex</span> <span class="k">else</span> <span class="s">''</span><span class="si">}</span><span class="s">

### Response:
</span><span class="si">{</span><span class="n">ex</span><span class="p">[</span><span class="s">'output'</span><span class="p">]</span><span class="si">}</span><span class="s">"""</span>
        <span class="n">formatted</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">text</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">formatted</span>

<span class="c1"># Ejemplo de dataset JSON
</span><span class="n">training_data</span> <span class="o">=</span> <span class="p">[</span>
    <span class="p">{</span>
        <span class="s">"instruction"</span><span class="p">:</span> <span class="s">"Explica qué es machine learning"</span><span class="p">,</span>
        <span class="s">"input"</span><span class="p">:</span> <span class="s">""</span><span class="p">,</span>
        <span class="s">"output"</span><span class="p">:</span> <span class="s">"Machine learning es una rama de la IA que..."</span>
    <span class="p">},</span>
    <span class="p">{</span>
        <span class="s">"instruction"</span><span class="p">:</span> <span class="s">"Traduce al español"</span><span class="p">,</span>
        <span class="s">"input"</span><span class="p">:</span> <span class="s">"Hello world"</span><span class="p">,</span>
        <span class="s">"output"</span><span class="p">:</span> <span class="s">"Hola mundo"</span>
    <span class="p">}</span>
<span class="p">]</span>
</code></pre></div></div>

<h2 id="monitoreo-y-evaluación">Monitoreo y Evaluación</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">transformers</span> <span class="kn">import</span> <span class="n">TrainerCallback</span>
<span class="kn">import</span> <span class="nn">wandb</span>

<span class="k">class</span> <span class="nc">LoRAMetricsCallback</span><span class="p">(</span><span class="n">TrainerCallback</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">on_log</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">args</span><span class="p">,</span> <span class="n">state</span><span class="p">,</span> <span class="n">control</span><span class="p">,</span> <span class="n">logs</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">logs</span><span class="p">:</span>
            <span class="c1"># Log a Weights &amp; Biases
</span>            <span class="n">wandb</span><span class="p">.</span><span class="n">log</span><span class="p">({</span>
                <span class="s">"train_loss"</span><span class="p">:</span> <span class="n">logs</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"loss"</span><span class="p">),</span>
                <span class="s">"learning_rate"</span><span class="p">:</span> <span class="n">logs</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"learning_rate"</span><span class="p">),</span>
                <span class="s">"epoch"</span><span class="p">:</span> <span class="n">logs</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"epoch"</span><span class="p">)</span>
            <span class="p">})</span>
            
            <span class="c1"># Verificar overfitting
</span>            <span class="k">if</span> <span class="s">"eval_loss"</span> <span class="ow">in</span> <span class="n">logs</span> <span class="ow">and</span> <span class="s">"loss"</span> <span class="ow">in</span> <span class="n">logs</span><span class="p">:</span>
                <span class="n">gap</span> <span class="o">=</span> <span class="n">logs</span><span class="p">[</span><span class="s">"loss"</span><span class="p">]</span> <span class="o">-</span> <span class="n">logs</span><span class="p">[</span><span class="s">"eval_loss"</span><span class="p">]</span>
                <span class="k">if</span> <span class="n">gap</span> <span class="o">&gt;</span> <span class="mf">0.5</span><span class="p">:</span>
                    <span class="k">print</span><span class="p">(</span><span class="s">"⚠️ Posible overfitting detectado"</span><span class="p">)</span>

<span class="c1"># Agregar al trainer
</span><span class="n">training_args</span><span class="p">.</span><span class="n">callbacks</span> <span class="o">=</span> <span class="p">[</span><span class="n">LoRAMetricsCallback</span><span class="p">()]</span>
</code></pre></div></div>

<h2 id="best-practices">Best Practices</h2>

<h3 id="1-learning-rate">1. Learning Rate</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Full fine-tuning: 5e-6 típico
# LoRA: 10-100× más alto
</span>
<span class="n">learning_rates</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">"conservative"</span><span class="p">:</span> <span class="mf">1e-4</span><span class="p">,</span>  <span class="c1"># Datasets grandes
</span>    <span class="s">"standard"</span><span class="p">:</span> <span class="mf">2e-4</span><span class="p">,</span>      <span class="c1"># Recomendado
</span>    <span class="s">"aggressive"</span><span class="p">:</span> <span class="mf">5e-4</span>     <span class="c1"># Datasets pequeños
</span><span class="p">}</span>
</code></pre></div></div>

<h3 id="2-batch-size-y-gradient-accumulation">2. Batch Size y Gradient Accumulation</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Effective batch size = per_device × accumulation × num_gpus
</span>
<span class="n">training_args</span> <span class="o">=</span> <span class="n">TrainingArguments</span><span class="p">(</span>
    <span class="n">per_device_train_batch_size</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span>  <span class="c1"># Lo que cabe en VRAM
</span>    <span class="n">gradient_accumulation_steps</span><span class="o">=</span><span class="mi">8</span><span class="p">,</span>   <span class="c1"># Accumular gradientes
</span>    <span class="c1"># Effective batch size = 2 × 8 × 1 = 16
</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="3-warmup">3. Warmup</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Warmup evita inestabilidad inicial
</span>
<span class="n">training_args</span> <span class="o">=</span> <span class="n">TrainingArguments</span><span class="p">(</span>
    <span class="n">warmup_steps</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span>  <span class="c1"># O warmup_ratio=0.1
</span>    <span class="c1"># LR gradualmente de 0 → max_lr en primeros 100 steps
</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="caso-de-uso-real">Caso de Uso Real</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Fine-tune LLaMA-7B para responder preguntas médicas
</span>
<span class="c1"># 1. Dataset
</span><span class="n">medical_data</span> <span class="o">=</span> <span class="n">load_dataset</span><span class="p">(</span><span class="s">"medmcqa"</span><span class="p">)</span>

<span class="c1"># 2. Configuración QLoRA
</span><span class="n">model</span> <span class="o">=</span> <span class="n">AutoModelForCausalLM</span><span class="p">.</span><span class="n">from_pretrained</span><span class="p">(</span>
    <span class="s">"meta-llama/Llama-2-7b-hf"</span><span class="p">,</span>
    <span class="n">load_in_4bit</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
    <span class="n">device_map</span><span class="o">=</span><span class="s">"auto"</span>
<span class="p">)</span>

<span class="n">lora_config</span> <span class="o">=</span> <span class="n">LoraConfig</span><span class="p">(</span>
    <span class="n">r</span><span class="o">=</span><span class="mi">32</span><span class="p">,</span>  <span class="c1"># Rank más alto para dominio específico
</span>    <span class="n">lora_alpha</span><span class="o">=</span><span class="mi">64</span><span class="p">,</span>
    <span class="n">target_modules</span><span class="o">=</span><span class="p">[</span><span class="s">"q_proj"</span><span class="p">,</span> <span class="s">"k_proj"</span><span class="p">,</span> <span class="s">"v_proj"</span><span class="p">,</span> <span class="s">"o_proj"</span><span class="p">],</span>
    <span class="n">lora_dropout</span><span class="o">=</span><span class="mf">0.1</span>
<span class="p">)</span>

<span class="c1"># 3. Entrenar 3 epochs
</span><span class="n">trainer</span><span class="p">.</span><span class="n">train</span><span class="p">()</span>

<span class="c1"># Resultado:
# - Accuracy: 68% → 84% (+16%)
# - Training time: 4 horas en 1× RTX 4090
# - Costo: ~$5
</span></code></pre></div></div>

<h2 id="conclusión">Conclusión</h2>

<p>LoRA y PEFT democratizan el fine-tuning de LLMs:</p>

<ul>
  <li>✅ <strong>100× menos memoria</strong>: fine-tunea LLaMA-7B en una RTX 3090</li>
  <li>✅ <strong>10× más rápido</strong>: 6h vs 40h</li>
  <li>✅ <strong>20× más barato</strong>: $60 vs $800</li>
  <li>✅ <strong>97-99% de performance</strong> vs full fine-tuning</li>
</ul>

<p><strong>Cuándo usar LoRA:</strong></p>
<ul>
  <li>Adaptar LLM a dominio específico (medicina, legal, código)</li>
  <li>Instruction following personalizado</li>
  <li>Multi-task learning (múltiples adapters)</li>
  <li>Rapid prototyping</li>
</ul>

<p><strong>Recursos:</strong></p>
<ul>
  <li><a href="https://arxiv.org/abs/2106.09685">Paper: LoRA</a></li>
  <li><a href="https://arxiv.org/abs/2305.14314">Paper: QLoRA</a></li>
  <li><a href="https://github.com/huggingface/peft">PEFT Library</a></li>
  <li><a href="https://colab.research.google.com/drive/finetune-llama-lora">Colab Demo</a></li>
</ul>

<hr />

<p><strong>Próximo:</strong> MLOps pipelines para entrenar, versionar y deployar modelos en producción.</p>]]></content><author><name>AI Tech Team</name></author><category term="Machine Learning" /><category term="fine-tuning" /><category term="lora" /><category term="peft" /><category term="llm" /><category term="parameter-efficient" /><category term="quantization" /><summary type="html"><![CDATA[Aprende a fine-tunear modelos grandes de manera eficiente con LoRA, QLoRA y PEFT. Reduce memory footprint de 40GB a 8GB manteniendo performance.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://images.unsplash.com/photo-1579547621113-e4bb2a19bdd6?w=800" /><media:content medium="image" url="https://images.unsplash.com/photo-1579547621113-e4bb2a19bdd6?w=800" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Introducción al Machine Learning</title><link href="https://dev-jcgi.github.io/blog/blog/2026/01/27/introduccion-machine-learning/" rel="alternate" type="text/html" title="Introducción al Machine Learning" /><published>2026-01-27T00:00:00-06:00</published><updated>2026-01-27T00:00:00-06:00</updated><id>https://dev-jcgi.github.io/blog/blog/2026/01/27/introduccion-machine-learning</id><content type="html" xml:base="https://dev-jcgi.github.io/blog/blog/2026/01/27/introduccion-machine-learning/"><![CDATA[<p>El <strong>Machine Learning</strong> (Aprendizaje Automático) es una rama de la inteligencia artificial que permite a las computadoras aprender de los datos sin ser programadas explícitamente.</p>

<h2 id="qué-es-machine-learning">¿Qué es Machine Learning?</h2>

<p>Machine Learning es el estudio de algoritmos que mejoran automáticamente a través de la experiencia y el uso de datos. Es una tecnología que está detrás de muchas aplicaciones actuales:</p>

<ul>
  <li>Recomendaciones de Netflix y Spotify</li>
  <li>Reconocimiento facial en smartphones</li>
  <li>Detección de spam en correo electrónico</li>
  <li>Vehículos autónomos</li>
</ul>

<h2 id="tipos-de-machine-learning">Tipos de Machine Learning</h2>

<h3 id="aprendizaje-supervisado">Aprendizaje Supervisado</h3>

<p>Se entrena el modelo con datos etiquetados. El algoritmo aprende la relación entre entradas y salidas. Algunos ejemplos incluyen:</p>

<ul>
  <li>Clasificación de imágenes</li>
  <li>Predicción de precios</li>
  <li>Diagnóstico médico</li>
  <li>Detección de fraude</li>
</ul>

<h3 id="aprendizaje-no-supervisado">Aprendizaje No Supervisado</h3>

<p>El modelo encuentra patrones en datos sin etiquetar. Clustering y reducción de dimensionalidad son ejemplos comunes:</p>

<ul>
  <li>Segmentación de clientes</li>
  <li>Detección de anomalías</li>
  <li>Sistemas de recomendación</li>
  <li>Análisis de sentimientos</li>
</ul>

<h3 id="aprendizaje-por-refuerzo">Aprendizaje por Refuerzo</h3>

<p>El agente aprende a través de prueba y error, recibiendo recompensas o penalizaciones. Aplicaciones incluyen:</p>

<ul>
  <li>Juegos (AlphaGo, OpenAI Five)</li>
  <li>Robótica</li>
  <li>Trading automático</li>
  <li>Control de sistemas</li>
</ul>

<h2 id="aplicaciones-prácticas">Aplicaciones Prácticas</h2>

<p>El Machine Learning está revolucionando industrias:</p>

<ol>
  <li><strong>Salud</strong>: Diagnóstico médico y descubrimiento de fármacos</li>
  <li><strong>Finanzas</strong>: Detección de fraudes y trading algorítmico</li>
  <li><strong>Retail</strong>: Personalización y optimización de inventario</li>
  <li><strong>Manufactura</strong>: Mantenimiento predictivo y control de calidad</li>
</ol>

<h2 id="herramientas-populares">Herramientas Populares</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Ejemplo simple con scikit-learn
</span><span class="kn">from</span> <span class="nn">sklearn.linear_model</span> <span class="kn">import</span> <span class="n">LinearRegression</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>

<span class="c1"># Datos de entrenamiento
</span><span class="n">X</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">([[</span><span class="mi">1</span><span class="p">],</span> <span class="p">[</span><span class="mi">2</span><span class="p">],</span> <span class="p">[</span><span class="mi">3</span><span class="p">],</span> <span class="p">[</span><span class="mi">4</span><span class="p">],</span> <span class="p">[</span><span class="mi">5</span><span class="p">]])</span>
<span class="n">y</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">([</span><span class="mi">2</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">10</span><span class="p">])</span>

<span class="c1"># Crear y entrenar el modelo
</span><span class="n">model</span> <span class="o">=</span> <span class="n">LinearRegression</span><span class="p">()</span>
<span class="n">model</span><span class="p">.</span><span class="n">fit</span><span class="p">(</span><span class="n">X</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>

<span class="c1"># Hacer predicciones
</span><span class="n">prediction</span> <span class="o">=</span> <span class="n">model</span><span class="p">.</span><span class="n">predict</span><span class="p">([[</span><span class="mi">6</span><span class="p">]])</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Predicción: </span><span class="si">{</span><span class="n">prediction</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="desafíos-y-consideraciones">Desafíos y Consideraciones</h2>

<p>Es importante tener en cuenta:</p>

<ul>
  <li><strong>Calidad de los datos</strong>: Basura entra, basura sale</li>
  <li><strong>Overfitting</strong>: Cuando el modelo memoriza en lugar de aprender</li>
  <li><strong>Interpretabilidad</strong>: Entender cómo toma decisiones el modelo</li>
  <li><strong>Sesgos</strong>: Los modelos pueden perpetuar sesgos presentes en los datos</li>
</ul>

<h2 id="conclusión">Conclusión</h2>

<p>El Machine Learning es una herramienta poderosa que está transformando cómo interactuamos con la tecnología. Con las herramientas y recursos disponibles hoy en día, nunca ha sido más fácil comenzar a aprender y aplicar estas técnicas.</p>

<p>¡El futuro del ML es emocionante y lleno de posibilidades!</p>]]></content><author><name>AI Tech Blog</name></author><category term="Machine Learning" /><category term="ML" /><category term="IA" /><category term="Tutorial" /><summary type="html"><![CDATA[Descubre los conceptos fundamentales del aprendizaje automático y cómo está transformando la tecnología moderna.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://images.unsplash.com/photo-1555255707-c07966088b7b?w=800" /><media:content medium="image" url="https://images.unsplash.com/photo-1555255707-c07966088b7b?w=800" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">RAG Systems: Combining Retrieval with Generation</title><link href="https://dev-jcgi.github.io/blog/blog/2026/01/26/rag-systems-implementacion/" rel="alternate" type="text/html" title="RAG Systems: Combining Retrieval with Generation" /><published>2026-01-26T09:30:00-06:00</published><updated>2026-01-26T09:30:00-06:00</updated><id>https://dev-jcgi.github.io/blog/blog/2026/01/26/rag-systems-implementacion</id><content type="html" xml:base="https://dev-jcgi.github.io/blog/blog/2026/01/26/rag-systems-implementacion/"><![CDATA[<p>Los <strong>RAG (Retrieval-Augmented Generation)</strong> systems han revolucionado cómo los LLMs interactúan con información externa. En lugar de depender solo del conocimiento embebido en sus pesos, RAG permite que los modelos accedan a documentos actualizados, bases de conocimiento privadas y contexto específico.</p>

<h2 id="qué-es-rag">¿Qué es RAG?</h2>

<p>RAG combina dos componentes:</p>

<ol>
  <li><strong>Retrieval</strong>: Búsqueda de documentos relevantes en una base de conocimiento</li>
  <li><strong>Generation</strong>: Generación de respuestas usando un LLM con el contexto recuperado</li>
</ol>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>User Query → Retrieve Relevant Docs → Augment Prompt → LLM Generation → Answer
</code></pre></div></div>

<h3 id="por-qué-rag">¿Por Qué RAG?</h3>

<p><strong>Problemas que resuelve:</strong></p>
<ul>
  <li>❌ <strong>Hallucinations</strong>: LLMs inventan información</li>
  <li>❌ <strong>Knowledge cutoff</strong>: entrenados hasta cierta fecha</li>
  <li>❌ <strong>Domain-specific knowledge</strong>: no tienen datos privados</li>
  <li>❌ <strong>Cost</strong>: fine-tuning es caro</li>
</ul>

<p><strong>Ventajas de RAG:</strong></p>
<ul>
  <li>✅ Información actualizada en tiempo real</li>
  <li>✅ Cita fuentes (trazabilidad)</li>
  <li>✅ Escalable y económico</li>
  <li>✅ No requiere reentrenamiento</li>
</ul>

<h2 id="arquitectura-rag-completa">Arquitectura RAG Completa</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">langchain.embeddings</span> <span class="kn">import</span> <span class="n">OpenAIEmbeddings</span>
<span class="kn">from</span> <span class="nn">langchain.vectorstores</span> <span class="kn">import</span> <span class="n">Chroma</span>
<span class="kn">from</span> <span class="nn">langchain.text_splitter</span> <span class="kn">import</span> <span class="n">RecursiveCharacterTextSplitter</span>
<span class="kn">from</span> <span class="nn">langchain.llms</span> <span class="kn">import</span> <span class="n">OpenAI</span>
<span class="kn">from</span> <span class="nn">langchain.chains</span> <span class="kn">import</span> <span class="n">RetrievalQA</span>
<span class="kn">from</span> <span class="nn">langchain.document_loaders</span> <span class="kn">import</span> <span class="n">DirectoryLoader</span>

<span class="k">class</span> <span class="nc">RAGSystem</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">docs_path</span><span class="p">,</span> <span class="n">model_name</span><span class="o">=</span><span class="s">"gpt-3.5-turbo"</span><span class="p">):</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">embeddings</span> <span class="o">=</span> <span class="n">OpenAIEmbeddings</span><span class="p">()</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">llm</span> <span class="o">=</span> <span class="n">OpenAI</span><span class="p">(</span><span class="n">model_name</span><span class="o">=</span><span class="n">model_name</span><span class="p">,</span> <span class="n">temperature</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">vectorstore</span> <span class="o">=</span> <span class="bp">None</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">qa_chain</span> <span class="o">=</span> <span class="bp">None</span>
        
        <span class="c1"># Cargar y procesar documentos
</span>        <span class="bp">self</span><span class="p">.</span><span class="n">load_documents</span><span class="p">(</span><span class="n">docs_path</span><span class="p">)</span>
        
    <span class="k">def</span> <span class="nf">load_documents</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">docs_path</span><span class="p">):</span>
        <span class="c1"># 1. Cargar documentos
</span>        <span class="n">loader</span> <span class="o">=</span> <span class="n">DirectoryLoader</span><span class="p">(</span><span class="n">docs_path</span><span class="p">,</span> <span class="n">glob</span><span class="o">=</span><span class="s">"**/*.txt"</span><span class="p">)</span>
        <span class="n">documents</span> <span class="o">=</span> <span class="n">loader</span><span class="p">.</span><span class="n">load</span><span class="p">()</span>
        
        <span class="c1"># 2. Split en chunks (chunking strategy)
</span>        <span class="n">text_splitter</span> <span class="o">=</span> <span class="n">RecursiveCharacterTextSplitter</span><span class="p">(</span>
            <span class="n">chunk_size</span><span class="o">=</span><span class="mi">1000</span><span class="p">,</span>
            <span class="n">chunk_overlap</span><span class="o">=</span><span class="mi">200</span><span class="p">,</span>
            <span class="n">length_function</span><span class="o">=</span><span class="nb">len</span>
        <span class="p">)</span>
        <span class="n">chunks</span> <span class="o">=</span> <span class="n">text_splitter</span><span class="p">.</span><span class="n">split_documents</span><span class="p">(</span><span class="n">documents</span><span class="p">)</span>
        
        <span class="c1"># 3. Crear embeddings y almacenar en vector DB
</span>        <span class="bp">self</span><span class="p">.</span><span class="n">vectorstore</span> <span class="o">=</span> <span class="n">Chroma</span><span class="p">.</span><span class="n">from_documents</span><span class="p">(</span>
            <span class="n">documents</span><span class="o">=</span><span class="n">chunks</span><span class="p">,</span>
            <span class="n">embedding</span><span class="o">=</span><span class="bp">self</span><span class="p">.</span><span class="n">embeddings</span><span class="p">,</span>
            <span class="n">persist_directory</span><span class="o">=</span><span class="s">"./chroma_db"</span>
        <span class="p">)</span>
        
        <span class="c1"># 4. Crear cadena de Q&amp;A
</span>        <span class="bp">self</span><span class="p">.</span><span class="n">qa_chain</span> <span class="o">=</span> <span class="n">RetrievalQA</span><span class="p">.</span><span class="n">from_chain_type</span><span class="p">(</span>
            <span class="n">llm</span><span class="o">=</span><span class="bp">self</span><span class="p">.</span><span class="n">llm</span><span class="p">,</span>
            <span class="n">chain_type</span><span class="o">=</span><span class="s">"stuff"</span><span class="p">,</span>
            <span class="n">retriever</span><span class="o">=</span><span class="bp">self</span><span class="p">.</span><span class="n">vectorstore</span><span class="p">.</span><span class="n">as_retriever</span><span class="p">(</span>
                <span class="n">search_kwargs</span><span class="o">=</span><span class="p">{</span><span class="s">"k"</span><span class="p">:</span> <span class="mi">4</span><span class="p">}</span>  <span class="c1"># Top 4 documentos
</span>            <span class="p">),</span>
            <span class="n">return_source_documents</span><span class="o">=</span><span class="bp">True</span>
        <span class="p">)</span>
        
    <span class="k">def</span> <span class="nf">query</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">question</span><span class="p">):</span>
        <span class="n">result</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">qa_chain</span><span class="p">({</span><span class="s">"query"</span><span class="p">:</span> <span class="n">question</span><span class="p">})</span>
        <span class="k">return</span> <span class="p">{</span>
            <span class="s">"answer"</span><span class="p">:</span> <span class="n">result</span><span class="p">[</span><span class="s">"result"</span><span class="p">],</span>
            <span class="s">"sources"</span><span class="p">:</span> <span class="n">result</span><span class="p">[</span><span class="s">"source_documents"</span><span class="p">]</span>
        <span class="p">}</span>

<span class="c1"># Uso
</span><span class="n">rag</span> <span class="o">=</span> <span class="n">RAGSystem</span><span class="p">(</span><span class="s">"./knowledge_base"</span><span class="p">)</span>
<span class="n">response</span> <span class="o">=</span> <span class="n">rag</span><span class="p">.</span><span class="n">query</span><span class="p">(</span><span class="s">"¿Cómo funcionan los transformers?"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">response</span><span class="p">[</span><span class="s">"answer"</span><span class="p">])</span>
</code></pre></div></div>

<h2 id="componentes-clave">Componentes Clave</h2>

<h3 id="1-document-loading">1. Document Loading</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">langchain.document_loaders</span> <span class="kn">import</span> <span class="p">(</span>
    <span class="n">TextLoader</span><span class="p">,</span>
    <span class="n">PDFLoader</span><span class="p">,</span>
    <span class="n">UnstructuredMarkdownLoader</span><span class="p">,</span>
    <span class="n">CSVLoader</span>
<span class="p">)</span>

<span class="c1"># Diferentes tipos de documentos
</span><span class="n">loaders</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">"txt"</span><span class="p">:</span> <span class="n">TextLoader</span><span class="p">(</span><span class="s">"docs/file.txt"</span><span class="p">),</span>
    <span class="s">"pdf"</span><span class="p">:</span> <span class="n">PDFLoader</span><span class="p">(</span><span class="s">"docs/file.pdf"</span><span class="p">),</span>
    <span class="s">"md"</span><span class="p">:</span> <span class="n">UnstructuredMarkdownLoader</span><span class="p">(</span><span class="s">"docs/file.md"</span><span class="p">),</span>
    <span class="s">"csv"</span><span class="p">:</span> <span class="n">CSVLoader</span><span class="p">(</span><span class="s">"data/file.csv"</span><span class="p">)</span>
<span class="p">}</span>

<span class="c1"># Cargar todos
</span><span class="n">documents</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">loader</span> <span class="ow">in</span> <span class="n">loaders</span><span class="p">.</span><span class="n">values</span><span class="p">():</span>
    <span class="n">documents</span><span class="p">.</span><span class="n">extend</span><span class="p">(</span><span class="n">loader</span><span class="p">.</span><span class="n">load</span><span class="p">())</span>
</code></pre></div></div>

<h3 id="2-text-chunking">2. Text Chunking</h3>

<p><strong>Estrategias de chunking:</strong></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">langchain.text_splitter</span> <span class="kn">import</span> <span class="p">(</span>
    <span class="n">RecursiveCharacterTextSplitter</span><span class="p">,</span>
    <span class="n">TokenTextSplitter</span><span class="p">,</span>
    <span class="n">CharacterTextSplitter</span>
<span class="p">)</span>

<span class="c1"># Estrategia 1: Por caracteres con overlap
</span><span class="n">char_splitter</span> <span class="o">=</span> <span class="n">RecursiveCharacterTextSplitter</span><span class="p">(</span>
    <span class="n">chunk_size</span><span class="o">=</span><span class="mi">1000</span><span class="p">,</span>
    <span class="n">chunk_overlap</span><span class="o">=</span><span class="mi">200</span><span class="p">,</span>
    <span class="n">separators</span><span class="o">=</span><span class="p">[</span><span class="s">"</span><span class="se">\n\n</span><span class="s">"</span><span class="p">,</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="s">" "</span><span class="p">,</span> <span class="s">""</span><span class="p">]</span>
<span class="p">)</span>

<span class="c1"># Estrategia 2: Por tokens (mejor para LLMs)
</span><span class="n">token_splitter</span> <span class="o">=</span> <span class="n">TokenTextSplitter</span><span class="p">(</span>
    <span class="n">chunk_size</span><span class="o">=</span><span class="mi">256</span><span class="p">,</span>
    <span class="n">chunk_overlap</span><span class="o">=</span><span class="mi">50</span>
<span class="p">)</span>

<span class="c1"># Estrategia 3: Semantic chunking (por significado)
</span><span class="kn">from</span> <span class="nn">langchain.text_splitter</span> <span class="kn">import</span> <span class="n">SemanticChunker</span>

<span class="n">semantic_splitter</span> <span class="o">=</span> <span class="n">SemanticChunker</span><span class="p">(</span>
    <span class="n">embeddings</span><span class="o">=</span><span class="n">OpenAIEmbeddings</span><span class="p">(),</span>
    <span class="n">breakpoint_threshold_type</span><span class="o">=</span><span class="s">"percentile"</span>  <span class="c1"># O "standard_deviation"
</span><span class="p">)</span>

<span class="n">chunks</span> <span class="o">=</span> <span class="n">semantic_splitter</span><span class="p">.</span><span class="n">split_documents</span><span class="p">(</span><span class="n">documents</span><span class="p">)</span>
</code></pre></div></div>

<p><strong>Recomendaciones:</strong></p>
<ul>
  <li><strong>Chunk size</strong>: 512-1024 tokens (balance context vs precisión)</li>
  <li><strong>Overlap</strong>: 10-20% del chunk size (mantiene contexto)</li>
  <li><strong>Separators</strong>: priorizar párrafos &gt; oraciones &gt; palabras</li>
</ul>

<h3 id="3-embeddings">3. Embeddings</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">sentence_transformers</span> <span class="kn">import</span> <span class="n">SentenceTransformer</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>

<span class="k">class</span> <span class="nc">EmbeddingModel</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">model_name</span><span class="o">=</span><span class="s">"all-MiniLM-L6-v2"</span><span class="p">):</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">model</span> <span class="o">=</span> <span class="n">SentenceTransformer</span><span class="p">(</span><span class="n">model_name</span><span class="p">)</span>
        
    <span class="k">def</span> <span class="nf">embed_documents</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">texts</span><span class="p">):</span>
        <span class="s">"""Convierte textos en vectores densos"""</span>
        <span class="n">embeddings</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">model</span><span class="p">.</span><span class="n">encode</span><span class="p">(</span><span class="n">texts</span><span class="p">,</span> <span class="n">convert_to_numpy</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">embeddings</span>
    
    <span class="k">def</span> <span class="nf">embed_query</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">query</span><span class="p">):</span>
        <span class="s">"""Embedding para la query"""</span>
        <span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">model</span><span class="p">.</span><span class="n">encode</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">convert_to_numpy</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
    
    <span class="k">def</span> <span class="nf">cosine_similarity</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">vec1</span><span class="p">,</span> <span class="n">vec2</span><span class="p">):</span>
        <span class="s">"""Similitud coseno entre vectores"""</span>
        <span class="k">return</span> <span class="n">np</span><span class="p">.</span><span class="n">dot</span><span class="p">(</span><span class="n">vec1</span><span class="p">,</span> <span class="n">vec2</span><span class="p">)</span> <span class="o">/</span> <span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">linalg</span><span class="p">.</span><span class="n">norm</span><span class="p">(</span><span class="n">vec1</span><span class="p">)</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="n">linalg</span><span class="p">.</span><span class="n">norm</span><span class="p">(</span><span class="n">vec2</span><span class="p">))</span>

<span class="c1"># Ejemplo
</span><span class="n">embedder</span> <span class="o">=</span> <span class="n">EmbeddingModel</span><span class="p">()</span>

<span class="c1"># Embeddings de documentos
</span><span class="n">docs</span> <span class="o">=</span> <span class="p">[</span><span class="s">"Transformers usan self-attention"</span><span class="p">,</span> <span class="s">"BERT es un encoder"</span><span class="p">]</span>
<span class="n">doc_embeddings</span> <span class="o">=</span> <span class="n">embedder</span><span class="p">.</span><span class="n">embed_documents</span><span class="p">(</span><span class="n">docs</span><span class="p">)</span>

<span class="c1"># Embedding de query
</span><span class="n">query</span> <span class="o">=</span> <span class="s">"¿Qué es self-attention?"</span>
<span class="n">query_embedding</span> <span class="o">=</span> <span class="n">embedder</span><span class="p">.</span><span class="n">embed_query</span><span class="p">(</span><span class="n">query</span><span class="p">)</span>

<span class="c1"># Calcular similitud
</span><span class="n">similarities</span> <span class="o">=</span> <span class="p">[</span>
    <span class="n">embedder</span><span class="p">.</span><span class="n">cosine_similarity</span><span class="p">(</span><span class="n">query_embedding</span><span class="p">,</span> <span class="n">doc_emb</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">doc_emb</span> <span class="ow">in</span> <span class="n">doc_embeddings</span>
<span class="p">]</span>
<span class="k">print</span><span class="p">(</span><span class="n">similarities</span><span class="p">)</span>  <span class="c1"># [0.78, 0.42]
</span></code></pre></div></div>

<p><strong>Modelos de Embeddings Populares:</strong></p>

<table>
  <thead>
    <tr>
      <th>Modelo</th>
      <th>Dimensiones</th>
      <th>Rendimiento</th>
      <th>Velocidad</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>OpenAI text-embedding-3-small</td>
      <td>1536</td>
      <td>⭐⭐⭐⭐⭐</td>
      <td>⚡⚡⚡</td>
    </tr>
    <tr>
      <td>sentence-transformers/all-MiniLM-L6-v2</td>
      <td>384</td>
      <td>⭐⭐⭐⭐</td>
      <td>⚡⚡⚡⚡⚡</td>
    </tr>
    <tr>
      <td>sentence-transformers/all-mpnet-base-v2</td>
      <td>768</td>
      <td>⭐⭐⭐⭐⭐</td>
      <td>⚡⚡⚡</td>
    </tr>
    <tr>
      <td>Cohere embed-multilingual-v3.0</td>
      <td>1024</td>
      <td>⭐⭐⭐⭐⭐</td>
      <td>⚡⚡⚡⚡</td>
    </tr>
  </tbody>
</table>

<h3 id="4-vector-databases">4. Vector Databases</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">chromadb</span>
<span class="kn">from</span> <span class="nn">chromadb.config</span> <span class="kn">import</span> <span class="n">Settings</span>

<span class="k">class</span> <span class="nc">VectorStore</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">collection_name</span><span class="o">=</span><span class="s">"knowledge_base"</span><span class="p">):</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">client</span> <span class="o">=</span> <span class="n">chromadb</span><span class="p">.</span><span class="n">Client</span><span class="p">(</span><span class="n">Settings</span><span class="p">(</span>
            <span class="n">chroma_db_impl</span><span class="o">=</span><span class="s">"duckdb+parquet"</span><span class="p">,</span>
            <span class="n">persist_directory</span><span class="o">=</span><span class="s">"./chroma_storage"</span>
        <span class="p">))</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">collection</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">client</span><span class="p">.</span><span class="n">get_or_create_collection</span><span class="p">(</span>
            <span class="n">name</span><span class="o">=</span><span class="n">collection_name</span><span class="p">,</span>
            <span class="n">metadata</span><span class="o">=</span><span class="p">{</span><span class="s">"hnsw:space"</span><span class="p">:</span> <span class="s">"cosine"</span><span class="p">}</span>  <span class="c1"># Métrica de distancia
</span>        <span class="p">)</span>
        
    <span class="k">def</span> <span class="nf">add_documents</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">texts</span><span class="p">,</span> <span class="n">metadatas</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">ids</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
        <span class="s">"""Agregar documentos al vector store"""</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">collection</span><span class="p">.</span><span class="n">add</span><span class="p">(</span>
            <span class="n">documents</span><span class="o">=</span><span class="n">texts</span><span class="p">,</span>
            <span class="n">metadatas</span><span class="o">=</span><span class="n">metadatas</span><span class="p">,</span>
            <span class="n">ids</span><span class="o">=</span><span class="n">ids</span> <span class="k">if</span> <span class="n">ids</span> <span class="k">else</span> <span class="p">[</span><span class="sa">f</span><span class="s">"doc_</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s">"</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">texts</span><span class="p">))]</span>
        <span class="p">)</span>
        
    <span class="k">def</span> <span class="nf">search</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">query</span><span class="p">,</span> <span class="n">k</span><span class="o">=</span><span class="mi">5</span><span class="p">):</span>
        <span class="s">"""Búsqueda por similitud"""</span>
        <span class="n">results</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">collection</span><span class="p">.</span><span class="n">query</span><span class="p">(</span>
            <span class="n">query_texts</span><span class="o">=</span><span class="p">[</span><span class="n">query</span><span class="p">],</span>
            <span class="n">n_results</span><span class="o">=</span><span class="n">k</span><span class="p">,</span>
            <span class="n">include</span><span class="o">=</span><span class="p">[</span><span class="s">"documents"</span><span class="p">,</span> <span class="s">"metadatas"</span><span class="p">,</span> <span class="s">"distances"</span><span class="p">]</span>
        <span class="p">)</span>
        <span class="k">return</span> <span class="n">results</span>
    
    <span class="k">def</span> <span class="nf">search_with_filter</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">query</span><span class="p">,</span> <span class="n">filter_dict</span><span class="p">,</span> <span class="n">k</span><span class="o">=</span><span class="mi">5</span><span class="p">):</span>
        <span class="s">"""Búsqueda con filtros de metadata"""</span>
        <span class="n">results</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">collection</span><span class="p">.</span><span class="n">query</span><span class="p">(</span>
            <span class="n">query_texts</span><span class="o">=</span><span class="p">[</span><span class="n">query</span><span class="p">],</span>
            <span class="n">n_results</span><span class="o">=</span><span class="n">k</span><span class="p">,</span>
            <span class="n">where</span><span class="o">=</span><span class="n">filter_dict</span>  <span class="c1"># e.g., {"category": "deep-learning"}
</span>        <span class="p">)</span>
        <span class="k">return</span> <span class="n">results</span>

<span class="c1"># Uso
</span><span class="n">vectorstore</span> <span class="o">=</span> <span class="n">VectorStore</span><span class="p">()</span>
<span class="n">vectorstore</span><span class="p">.</span><span class="n">add_documents</span><span class="p">(</span>
    <span class="n">texts</span><span class="o">=</span><span class="p">[</span><span class="s">"Transformers revolucionaron NLP"</span><span class="p">,</span> <span class="s">"GPT-3 tiene 175B parámetros"</span><span class="p">],</span>
    <span class="n">metadatas</span><span class="o">=</span><span class="p">[{</span><span class="s">"category"</span><span class="p">:</span> <span class="s">"nlp"</span><span class="p">},</span> <span class="p">{</span><span class="s">"category"</span><span class="p">:</span> <span class="s">"llm"</span><span class="p">}],</span>
    <span class="n">ids</span><span class="o">=</span><span class="p">[</span><span class="s">"doc1"</span><span class="p">,</span> <span class="s">"doc2"</span><span class="p">]</span>
<span class="p">)</span>

<span class="n">results</span> <span class="o">=</span> <span class="n">vectorstore</span><span class="p">.</span><span class="n">search</span><span class="p">(</span><span class="s">"¿Qué es GPT-3?"</span><span class="p">,</span> <span class="n">k</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">results</span><span class="p">)</span>
</code></pre></div></div>

<p><strong>Vector Databases Populares:</strong></p>

<ul>
  <li><strong>Chroma</strong>: Open-source, fácil de usar, persistencia local</li>
  <li><strong>Pinecone</strong>: Managed, alta escala, serverless</li>
  <li><strong>Weaviate</strong>: Open-source, multimodal, GraphQL</li>
  <li><strong>Qdrant</strong>: Rust, performance, filtros avanzados</li>
  <li><strong>Milvus</strong>: Distributed, billions de vectores</li>
</ul>

<h3 id="5-retrieval-strategies">5. Retrieval Strategies</h3>

<h4 id="a-dense-retrieval-vector-search">a) <strong>Dense Retrieval (Vector Search)</strong></h4>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Búsqueda por similitud de embeddings
</span><span class="n">results</span> <span class="o">=</span> <span class="n">vectorstore</span><span class="p">.</span><span class="n">similarity_search</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">k</span><span class="o">=</span><span class="mi">4</span><span class="p">)</span>
</code></pre></div></div>

<h4 id="b-sparse-retrieval-bm25">b) <strong>Sparse Retrieval (BM25)</strong></h4>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">rank_bm25</span> <span class="kn">import</span> <span class="n">BM25Okapi</span>

<span class="k">class</span> <span class="nc">BM25Retriever</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">documents</span><span class="p">):</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">documents</span> <span class="o">=</span> <span class="n">documents</span>
        <span class="n">tokenized_docs</span> <span class="o">=</span> <span class="p">[</span><span class="n">doc</span><span class="p">.</span><span class="n">split</span><span class="p">()</span> <span class="k">for</span> <span class="n">doc</span> <span class="ow">in</span> <span class="n">documents</span><span class="p">]</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">bm25</span> <span class="o">=</span> <span class="n">BM25Okapi</span><span class="p">(</span><span class="n">tokenized_docs</span><span class="p">)</span>
        
    <span class="k">def</span> <span class="nf">retrieve</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">query</span><span class="p">,</span> <span class="n">k</span><span class="o">=</span><span class="mi">5</span><span class="p">):</span>
        <span class="n">tokenized_query</span> <span class="o">=</span> <span class="n">query</span><span class="p">.</span><span class="n">split</span><span class="p">()</span>
        <span class="n">scores</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">bm25</span><span class="p">.</span><span class="n">get_scores</span><span class="p">(</span><span class="n">tokenized_query</span><span class="p">)</span>
        <span class="n">top_k_indices</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">argsort</span><span class="p">(</span><span class="n">scores</span><span class="p">)[::</span><span class="o">-</span><span class="mi">1</span><span class="p">][:</span><span class="n">k</span><span class="p">]</span>
        <span class="k">return</span> <span class="p">[</span><span class="bp">self</span><span class="p">.</span><span class="n">documents</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">top_k_indices</span><span class="p">]</span>
</code></pre></div></div>

<h4 id="c-hybrid-search-dense--sparse">c) <strong>Hybrid Search (Dense + Sparse)</strong></h4>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">hybrid_search</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">alpha</span><span class="o">=</span><span class="mf">0.5</span><span class="p">):</span>
    <span class="s">"""
    Combina vector search y BM25
    alpha: peso de vector search (0-1)
    """</span>
    <span class="c1"># Dense retrieval
</span>    <span class="n">dense_results</span> <span class="o">=</span> <span class="n">vectorstore</span><span class="p">.</span><span class="n">search</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">k</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span>
    <span class="n">dense_scores</span> <span class="o">=</span> <span class="p">{</span><span class="n">doc</span><span class="p">.</span><span class="nb">id</span><span class="p">:</span> <span class="mi">1</span> <span class="o">-</span> <span class="n">dist</span> <span class="k">for</span> <span class="n">doc</span><span class="p">,</span> <span class="n">dist</span> <span class="ow">in</span> <span class="n">dense_results</span><span class="p">}</span>
    
    <span class="c1"># Sparse retrieval (BM25)
</span>    <span class="n">sparse_results</span> <span class="o">=</span> <span class="n">bm25_retriever</span><span class="p">.</span><span class="n">retrieve</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">k</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span>
    <span class="n">sparse_scores</span> <span class="o">=</span> <span class="p">{</span><span class="n">doc</span><span class="p">.</span><span class="nb">id</span><span class="p">:</span> <span class="n">score</span> <span class="k">for</span> <span class="n">doc</span><span class="p">,</span> <span class="n">score</span> <span class="ow">in</span> <span class="n">sparse_results</span><span class="p">}</span>
    
    <span class="c1"># Combinar scores
</span>    <span class="n">all_doc_ids</span> <span class="o">=</span> <span class="nb">set</span><span class="p">(</span><span class="n">dense_scores</span><span class="p">.</span><span class="n">keys</span><span class="p">())</span> <span class="o">|</span> <span class="nb">set</span><span class="p">(</span><span class="n">sparse_scores</span><span class="p">.</span><span class="n">keys</span><span class="p">())</span>
    <span class="n">hybrid_scores</span> <span class="o">=</span> <span class="p">{}</span>
    
    <span class="k">for</span> <span class="n">doc_id</span> <span class="ow">in</span> <span class="n">all_doc_ids</span><span class="p">:</span>
        <span class="n">dense_score</span> <span class="o">=</span> <span class="n">dense_scores</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">doc_id</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
        <span class="n">sparse_score</span> <span class="o">=</span> <span class="n">sparse_scores</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">doc_id</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
        <span class="n">hybrid_scores</span><span class="p">[</span><span class="n">doc_id</span><span class="p">]</span> <span class="o">=</span> <span class="n">alpha</span> <span class="o">*</span> <span class="n">dense_score</span> <span class="o">+</span> <span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">alpha</span><span class="p">)</span> <span class="o">*</span> <span class="n">sparse_score</span>
    
    <span class="c1"># Ordenar por score
</span>    <span class="n">sorted_docs</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">hybrid_scores</span><span class="p">.</span><span class="n">items</span><span class="p">(),</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">reverse</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">sorted_docs</span><span class="p">[:</span><span class="mi">5</span><span class="p">]</span>
</code></pre></div></div>

<h3 id="6-reranking">6. Reranking</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">sentence_transformers</span> <span class="kn">import</span> <span class="n">CrossEncoder</span>

<span class="k">class</span> <span class="nc">Reranker</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">model_name</span><span class="o">=</span><span class="s">"cross-encoder/ms-marco-MiniLM-L-6-v2"</span><span class="p">):</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">model</span> <span class="o">=</span> <span class="n">CrossEncoder</span><span class="p">(</span><span class="n">model_name</span><span class="p">)</span>
        
    <span class="k">def</span> <span class="nf">rerank</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">query</span><span class="p">,</span> <span class="n">documents</span><span class="p">,</span> <span class="n">top_k</span><span class="o">=</span><span class="mi">3</span><span class="p">):</span>
        <span class="s">"""
        Reranquear documentos usando un modelo de cross-encoder
        Más preciso que cosine similarity pero más lento
        """</span>
        <span class="c1"># Crear pares (query, doc)
</span>        <span class="n">pairs</span> <span class="o">=</span> <span class="p">[[</span><span class="n">query</span><span class="p">,</span> <span class="n">doc</span><span class="p">]</span> <span class="k">for</span> <span class="n">doc</span> <span class="ow">in</span> <span class="n">documents</span><span class="p">]</span>
        
        <span class="c1"># Calcular scores de relevancia
</span>        <span class="n">scores</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">model</span><span class="p">.</span><span class="n">predict</span><span class="p">(</span><span class="n">pairs</span><span class="p">)</span>
        
        <span class="c1"># Ordenar por score
</span>        <span class="n">ranked_indices</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">argsort</span><span class="p">(</span><span class="n">scores</span><span class="p">)[::</span><span class="o">-</span><span class="mi">1</span><span class="p">][:</span><span class="n">top_k</span><span class="p">]</span>
        <span class="k">return</span> <span class="p">[</span><span class="n">documents</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">ranked_indices</span><span class="p">]</span>

<span class="c1"># Pipeline completo
</span><span class="k">def</span> <span class="nf">retrieve_and_rerank</span><span class="p">(</span><span class="n">query</span><span class="p">):</span>
    <span class="c1"># 1. Initial retrieval (k=20)
</span>    <span class="n">candidates</span> <span class="o">=</span> <span class="n">vectorstore</span><span class="p">.</span><span class="n">search</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">k</span><span class="o">=</span><span class="mi">20</span><span class="p">)</span>
    
    <span class="c1"># 2. Rerank top 20 → top 3
</span>    <span class="n">reranker</span> <span class="o">=</span> <span class="n">Reranker</span><span class="p">()</span>
    <span class="n">final_docs</span> <span class="o">=</span> <span class="n">reranker</span><span class="p">.</span><span class="n">rerank</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">candidates</span><span class="p">,</span> <span class="n">top_k</span><span class="o">=</span><span class="mi">3</span><span class="p">)</span>
    
    <span class="k">return</span> <span class="n">final_docs</span>
</code></pre></div></div>

<h2 id="prompt-engineering-para-rag">Prompt Engineering para RAG</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">create_rag_prompt</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">context_docs</span><span class="p">):</span>
    <span class="s">"""
    Crear prompt optimizado para RAG
    """</span>
    <span class="n">context</span> <span class="o">=</span> <span class="s">"</span><span class="se">\n\n</span><span class="s">"</span><span class="p">.</span><span class="n">join</span><span class="p">([</span>
        <span class="sa">f</span><span class="s">"Documento </span><span class="si">{</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="si">}</span><span class="s">:</span><span class="se">\n</span><span class="si">{</span><span class="n">doc</span><span class="si">}</span><span class="s">"</span>
        <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">doc</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">context_docs</span><span class="p">)</span>
    <span class="p">])</span>
    
    <span class="n">prompt</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"""Eres un asistente que responde preguntas basándote SOLO en el contexto proporcionado.

CONTEXTO:
</span><span class="si">{</span><span class="n">context</span><span class="si">}</span><span class="s">

PREGUNTA: </span><span class="si">{</span><span class="n">query</span><span class="si">}</span><span class="s">

INSTRUCCIONES:
1. Responde SOLO usando información del contexto
2. Si la respuesta no está en el contexto, di "No tengo información suficiente"
3. Cita el número de documento que usaste
4. Sé conciso y preciso

RESPUESTA:"""</span>
    
    <span class="k">return</span> <span class="n">prompt</span>

<span class="c1"># Uso
</span><span class="n">query</span> <span class="o">=</span> <span class="s">"¿Cómo funciona self-attention?"</span>
<span class="n">docs</span> <span class="o">=</span> <span class="n">retrieve_and_rerank</span><span class="p">(</span><span class="n">query</span><span class="p">)</span>
<span class="n">prompt</span> <span class="o">=</span> <span class="n">create_rag_prompt</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">docs</span><span class="p">)</span>
<span class="n">answer</span> <span class="o">=</span> <span class="n">llm</span><span class="p">.</span><span class="n">generate</span><span class="p">(</span><span class="n">prompt</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="advanced-rag-techniques">Advanced RAG Techniques</h2>

<h3 id="1-query-transformation">1. <strong>Query Transformation</strong></h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">transform_query</span><span class="p">(</span><span class="n">original_query</span><span class="p">):</span>
    <span class="s">"""
    Transforma query para mejorar retrieval
    """</span>
    <span class="n">transformations</span> <span class="o">=</span> <span class="p">{</span>
        <span class="s">"expansion"</span><span class="p">:</span> <span class="sa">f</span><span class="s">"Genera 3 variaciones de esta pregunta: </span><span class="si">{</span><span class="n">original_query</span><span class="si">}</span><span class="s">"</span><span class="p">,</span>
        <span class="s">"decomposition"</span><span class="p">:</span> <span class="sa">f</span><span class="s">"Descompón esta pregunta compleja en sub-preguntas: </span><span class="si">{</span><span class="n">original_query</span><span class="si">}</span><span class="s">"</span><span class="p">,</span>
        <span class="s">"abstraction"</span><span class="p">:</span> <span class="sa">f</span><span class="s">"Reformula esta pregunta de manera más general: </span><span class="si">{</span><span class="n">original_query</span><span class="si">}</span><span class="s">"</span>
    <span class="p">}</span>
    
    <span class="c1"># Usar LLM para transformar
</span>    <span class="n">expanded_queries</span> <span class="o">=</span> <span class="n">llm</span><span class="p">.</span><span class="n">generate</span><span class="p">(</span><span class="n">transformations</span><span class="p">[</span><span class="s">"expansion"</span><span class="p">])</span>
    
    <span class="c1"># Buscar con múltiples queries
</span>    <span class="n">all_results</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">query</span> <span class="ow">in</span> <span class="n">expanded_queries</span><span class="p">:</span>
        <span class="n">results</span> <span class="o">=</span> <span class="n">vectorstore</span><span class="p">.</span><span class="n">search</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">k</span><span class="o">=</span><span class="mi">3</span><span class="p">)</span>
        <span class="n">all_results</span><span class="p">.</span><span class="n">extend</span><span class="p">(</span><span class="n">results</span><span class="p">)</span>
    
    <span class="c1"># Deduplicate y rankear
</span>    <span class="n">unique_results</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">all_results</span><span class="p">))</span>
    <span class="k">return</span> <span class="n">unique_results</span>
</code></pre></div></div>

<h3 id="2-parent-document-retrieval">2. <strong>Parent Document Retrieval</strong></h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">ParentDocumentRetriever</span><span class="p">:</span>
    <span class="s">"""
    Guarda chunks pequeños para búsqueda, pero devuelve chunks padres más grandes
    """</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">parent_splitter</span><span class="p">,</span> <span class="n">child_splitter</span><span class="p">):</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">parent_chunks</span> <span class="o">=</span> <span class="p">{}</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">child_to_parent</span> <span class="o">=</span> <span class="p">{}</span>
        
    <span class="k">def</span> <span class="nf">index_documents</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">documents</span><span class="p">):</span>
        <span class="c1"># Split en chunks grandes (parents)
</span>        <span class="n">parent_docs</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">parent_splitter</span><span class="p">.</span><span class="n">split_documents</span><span class="p">(</span><span class="n">documents</span><span class="p">)</span>
        
        <span class="c1"># Split cada parent en chunks pequeños (children)
</span>        <span class="k">for</span> <span class="n">parent_id</span><span class="p">,</span> <span class="n">parent_doc</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">parent_docs</span><span class="p">):</span>
            <span class="bp">self</span><span class="p">.</span><span class="n">parent_chunks</span><span class="p">[</span><span class="n">parent_id</span><span class="p">]</span> <span class="o">=</span> <span class="n">parent_doc</span>
            
            <span class="n">child_docs</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">child_splitter</span><span class="p">.</span><span class="n">split_documents</span><span class="p">([</span><span class="n">parent_doc</span><span class="p">])</span>
            <span class="k">for</span> <span class="n">child_doc</span> <span class="ow">in</span> <span class="n">child_docs</span><span class="p">:</span>
                <span class="bp">self</span><span class="p">.</span><span class="n">child_to_parent</span><span class="p">[</span><span class="n">child_doc</span><span class="p">.</span><span class="nb">id</span><span class="p">]</span> <span class="o">=</span> <span class="n">parent_id</span>
                <span class="n">vectorstore</span><span class="p">.</span><span class="n">add_documents</span><span class="p">([</span><span class="n">child_doc</span><span class="p">])</span>
    
    <span class="k">def</span> <span class="nf">retrieve</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">query</span><span class="p">,</span> <span class="n">k</span><span class="o">=</span><span class="mi">3</span><span class="p">):</span>
        <span class="c1"># Buscar en child chunks
</span>        <span class="n">child_results</span> <span class="o">=</span> <span class="n">vectorstore</span><span class="p">.</span><span class="n">search</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">k</span><span class="o">=</span><span class="n">k</span><span class="o">*</span><span class="mi">2</span><span class="p">)</span>
        
        <span class="c1"># Devolver parent chunks únicos
</span>        <span class="n">parent_ids</span> <span class="o">=</span> <span class="nb">set</span><span class="p">([</span><span class="bp">self</span><span class="p">.</span><span class="n">child_to_parent</span><span class="p">[</span><span class="n">child</span><span class="p">.</span><span class="nb">id</span><span class="p">]</span> <span class="k">for</span> <span class="n">child</span> <span class="ow">in</span> <span class="n">child_results</span><span class="p">])</span>
        <span class="k">return</span> <span class="p">[</span><span class="bp">self</span><span class="p">.</span><span class="n">parent_chunks</span><span class="p">[</span><span class="n">pid</span><span class="p">]</span> <span class="k">for</span> <span class="n">pid</span> <span class="ow">in</span> <span class="n">parent_ids</span><span class="p">][:</span><span class="n">k</span><span class="p">]</span>
</code></pre></div></div>

<h3 id="3-self-rag-reflexión">3. <strong>Self-RAG (Reflexión)</strong></h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">self_rag</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">max_iterations</span><span class="o">=</span><span class="mi">3</span><span class="p">):</span>
    <span class="s">"""
    RAG con auto-reflexión para mejorar respuestas
    """</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">max_iterations</span><span class="p">):</span>
        <span class="c1"># 1. Retrieve
</span>        <span class="n">docs</span> <span class="o">=</span> <span class="n">vectorstore</span><span class="p">.</span><span class="n">search</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">k</span><span class="o">=</span><span class="mi">3</span><span class="p">)</span>
        
        <span class="c1"># 2. Generate
</span>        <span class="n">answer</span> <span class="o">=</span> <span class="n">generate_answer</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">docs</span><span class="p">)</span>
        
        <span class="c1"># 3. Critique (auto-evaluación)
</span>        <span class="n">critique_prompt</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"""
        Query: </span><span class="si">{</span><span class="n">query</span><span class="si">}</span><span class="s">
        Answer: </span><span class="si">{</span><span class="n">answer</span><span class="si">}</span><span class="s">
        
        ¿Esta respuesta es completa y precisa? ¿Qué falta?
        Responde: [COMPLETA] o [INCOMPLETA: razón]
        """</span>
        <span class="n">critique</span> <span class="o">=</span> <span class="n">llm</span><span class="p">.</span><span class="n">generate</span><span class="p">(</span><span class="n">critique_prompt</span><span class="p">)</span>
        
        <span class="c1"># 4. Si completa, terminar
</span>        <span class="k">if</span> <span class="s">"[COMPLETA]"</span> <span class="ow">in</span> <span class="n">critique</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">answer</span>
        
        <span class="c1"># 5. Si no, refinar query
</span>        <span class="n">refine_prompt</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"Reformula esta query para obtener mejor información: </span><span class="si">{</span><span class="n">query</span><span class="si">}</span><span class="se">\n</span><span class="s">Problema: </span><span class="si">{</span><span class="n">critique</span><span class="si">}</span><span class="s">"</span>
        <span class="n">query</span> <span class="o">=</span> <span class="n">llm</span><span class="p">.</span><span class="n">generate</span><span class="p">(</span><span class="n">refine_prompt</span><span class="p">)</span>
    
    <span class="k">return</span> <span class="n">answer</span>
</code></pre></div></div>

<h2 id="evaluación-de-rag">Evaluación de RAG</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">ragas</span> <span class="kn">import</span> <span class="n">evaluate</span>
<span class="kn">from</span> <span class="nn">ragas.metrics</span> <span class="kn">import</span> <span class="p">(</span>
    <span class="n">faithfulness</span><span class="p">,</span>
    <span class="n">answer_relevancy</span><span class="p">,</span>
    <span class="n">context_relevancy</span><span class="p">,</span>
    <span class="n">context_recall</span>
<span class="p">)</span>

<span class="c1"># Crear dataset de evaluación
</span><span class="n">eval_dataset</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">"question"</span><span class="p">:</span> <span class="p">[</span><span class="s">"¿Qué es RAG?"</span><span class="p">,</span> <span class="s">"¿Cómo funciona retrieval?"</span><span class="p">],</span>
    <span class="s">"answer"</span><span class="p">:</span> <span class="p">[</span><span class="n">answer1</span><span class="p">,</span> <span class="n">answer2</span><span class="p">],</span>
    <span class="s">"contexts"</span><span class="p">:</span> <span class="p">[</span><span class="n">contexts1</span><span class="p">,</span> <span class="n">contexts2</span><span class="p">],</span>
    <span class="s">"ground_truth"</span><span class="p">:</span> <span class="p">[</span><span class="n">gt1</span><span class="p">,</span> <span class="n">gt2</span><span class="p">]</span>
<span class="p">}</span>

<span class="c1"># Evaluar
</span><span class="n">results</span> <span class="o">=</span> <span class="n">evaluate</span><span class="p">(</span>
    <span class="n">dataset</span><span class="o">=</span><span class="n">eval_dataset</span><span class="p">,</span>
    <span class="n">metrics</span><span class="o">=</span><span class="p">[</span>
        <span class="n">faithfulness</span><span class="p">,</span>        <span class="c1"># ¿Respuesta fiel al contexto?
</span>        <span class="n">answer_relevancy</span><span class="p">,</span>    <span class="c1"># ¿Respuesta relevante a la pregunta?
</span>        <span class="n">context_relevancy</span><span class="p">,</span>   <span class="c1"># ¿Contexto relevante?
</span>        <span class="n">context_recall</span>       <span class="c1"># ¿Recuperó todo el contexto necesario?
</span>    <span class="p">]</span>
<span class="p">)</span>

<span class="k">print</span><span class="p">(</span><span class="n">results</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="optimización-y-best-practices">Optimización y Best Practices</h2>

<h3 id="1-caché-de-embeddings">1. <strong>Caché de Embeddings</strong></h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">hashlib</span>
<span class="kn">import</span> <span class="nn">pickle</span>

<span class="k">class</span> <span class="nc">EmbeddingCache</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">cache_file</span><span class="o">=</span><span class="s">"embeddings_cache.pkl"</span><span class="p">):</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">cache_file</span> <span class="o">=</span> <span class="n">cache_file</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">cache</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">load_cache</span><span class="p">()</span>
        
    <span class="k">def</span> <span class="nf">load_cache</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">cache_file</span><span class="p">,</span> <span class="s">'rb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
                <span class="k">return</span> <span class="n">pickle</span><span class="p">.</span><span class="n">load</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
        <span class="k">except</span> <span class="nb">FileNotFoundError</span><span class="p">:</span>
            <span class="k">return</span> <span class="p">{}</span>
    
    <span class="k">def</span> <span class="nf">save_cache</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">cache_file</span><span class="p">,</span> <span class="s">'wb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
            <span class="n">pickle</span><span class="p">.</span><span class="n">dump</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">cache</span><span class="p">,</span> <span class="n">f</span><span class="p">)</span>
    
    <span class="k">def</span> <span class="nf">get_embedding</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">text</span><span class="p">,</span> <span class="n">embedder</span><span class="p">):</span>
        <span class="c1"># Hash del texto
</span>        <span class="n">text_hash</span> <span class="o">=</span> <span class="n">hashlib</span><span class="p">.</span><span class="n">md5</span><span class="p">(</span><span class="n">text</span><span class="p">.</span><span class="n">encode</span><span class="p">()).</span><span class="n">hexdigest</span><span class="p">()</span>
        
        <span class="k">if</span> <span class="n">text_hash</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">cache</span><span class="p">:</span>
            <span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">cache</span><span class="p">[</span><span class="n">text_hash</span><span class="p">]</span>
        
        <span class="c1"># Calcular embedding
</span>        <span class="n">embedding</span> <span class="o">=</span> <span class="n">embedder</span><span class="p">.</span><span class="n">embed_query</span><span class="p">(</span><span class="n">text</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">cache</span><span class="p">[</span><span class="n">text_hash</span><span class="p">]</span> <span class="o">=</span> <span class="n">embedding</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">save_cache</span><span class="p">()</span>
        
        <span class="k">return</span> <span class="n">embedding</span>
</code></pre></div></div>

<h3 id="2-chunking-inteligente">2. <strong>Chunking Inteligente</strong></h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">smart_chunking</span><span class="p">(</span><span class="n">document</span><span class="p">,</span> <span class="n">max_chunk_size</span><span class="o">=</span><span class="mi">1000</span><span class="p">):</span>
    <span class="s">"""
    Chunking que respeta estructura del documento
    """</span>
    <span class="c1"># Detectar secciones
</span>    <span class="n">sections</span> <span class="o">=</span> <span class="n">document</span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">##"</span><span class="p">)</span>  <span class="c1"># Headers de markdown
</span>    
    <span class="n">chunks</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">section</span> <span class="ow">in</span> <span class="n">sections</span><span class="p">:</span>
        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">section</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="n">max_chunk_size</span><span class="p">:</span>
            <span class="n">chunks</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">section</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="c1"># Split subsections
</span>            <span class="n">subsections</span> <span class="o">=</span> <span class="n">section</span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">###"</span><span class="p">)</span>
            <span class="k">for</span> <span class="n">subsection</span> <span class="ow">in</span> <span class="n">subsections</span><span class="p">:</span>
                <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">subsection</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="n">max_chunk_size</span><span class="p">:</span>
                    <span class="n">chunks</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">subsection</span><span class="p">)</span>
                <span class="k">else</span><span class="p">:</span>
                    <span class="c1"># Split por párrafos
</span>                    <span class="n">paragraphs</span> <span class="o">=</span> <span class="n">subsection</span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="s">"</span><span class="se">\n\n</span><span class="s">"</span><span class="p">)</span>
                    <span class="n">current_chunk</span> <span class="o">=</span> <span class="s">""</span>
                    <span class="k">for</span> <span class="n">para</span> <span class="ow">in</span> <span class="n">paragraphs</span><span class="p">:</span>
                        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">current_chunk</span><span class="p">)</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">para</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="n">max_chunk_size</span><span class="p">:</span>
                            <span class="n">current_chunk</span> <span class="o">+=</span> <span class="n">para</span> <span class="o">+</span> <span class="s">"</span><span class="se">\n\n</span><span class="s">"</span>
                        <span class="k">else</span><span class="p">:</span>
                            <span class="n">chunks</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">current_chunk</span><span class="p">)</span>
                            <span class="n">current_chunk</span> <span class="o">=</span> <span class="n">para</span> <span class="o">+</span> <span class="s">"</span><span class="se">\n\n</span><span class="s">"</span>
                    
                    <span class="k">if</span> <span class="n">current_chunk</span><span class="p">:</span>
                        <span class="n">chunks</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">current_chunk</span><span class="p">)</span>
    
    <span class="k">return</span> <span class="n">chunks</span>
</code></pre></div></div>

<h3 id="3-metadata-filtering">3. <strong>Metadata Filtering</strong></h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Agregar metadata rica
</span><span class="n">vectorstore</span><span class="p">.</span><span class="n">add_documents</span><span class="p">(</span>
    <span class="n">texts</span><span class="o">=</span><span class="n">chunks</span><span class="p">,</span>
    <span class="n">metadatas</span><span class="o">=</span><span class="p">[{</span>
        <span class="s">"source"</span><span class="p">:</span> <span class="s">"paper_transformer.pdf"</span><span class="p">,</span>
        <span class="s">"page"</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
        <span class="s">"section"</span><span class="p">:</span> <span class="s">"Architecture"</span><span class="p">,</span>
        <span class="s">"date"</span><span class="p">:</span> <span class="s">"2017-06-12"</span><span class="p">,</span>
        <span class="s">"author"</span><span class="p">:</span> <span class="s">"Vaswani et al."</span><span class="p">,</span>
        <span class="s">"topic"</span><span class="p">:</span> <span class="s">"deep-learning"</span>
    <span class="p">}</span> <span class="k">for</span> <span class="n">chunk</span> <span class="ow">in</span> <span class="n">chunks</span><span class="p">]</span>
<span class="p">)</span>

<span class="c1"># Buscar con filtros
</span><span class="n">results</span> <span class="o">=</span> <span class="n">vectorstore</span><span class="p">.</span><span class="n">search</span><span class="p">(</span>
    <span class="n">query</span><span class="o">=</span><span class="s">"self-attention mechanism"</span><span class="p">,</span>
    <span class="nb">filter</span><span class="o">=</span><span class="p">{</span><span class="s">"topic"</span><span class="p">:</span> <span class="s">"deep-learning"</span><span class="p">,</span> <span class="s">"date"</span><span class="p">:</span> <span class="p">{</span><span class="s">"$gte"</span><span class="p">:</span> <span class="s">"2017-01-01"</span><span class="p">}},</span>
    <span class="n">k</span><span class="o">=</span><span class="mi">5</span>
<span class="p">)</span>
</code></pre></div></div>

<h2 id="conclusión">Conclusión</h2>

<p>RAG es esencial para aplicaciones de producción con LLMs porque:</p>

<ol>
  <li><strong>Reduce hallucinations</strong> citando fuentes verificables</li>
  <li><strong>Actualización dinámica</strong> sin reentrenar modelos</li>
  <li><strong>Costo-efectivo</strong> vs fine-tuning completo</li>
  <li><strong>Escalable</strong> a millones de documentos</li>
</ol>

<p><strong>Roadmap Completo:</strong></p>
<ol>
  <li>Start: Vanilla RAG (embeddings + vector DB)</li>
  <li>Intermediate: Hybrid search + reranking</li>
  <li>Advanced: Query transformation + self-RAG</li>
  <li>Production: Caché, monitoreo, A/B testing</li>
</ol>

<p><strong>Frameworks Recomendados:</strong></p>
<ul>
  <li><a href="https://python.langchain.com/">LangChain</a> - Ecosistema completo</li>
  <li><a href="https://www.llamaindex.ai/">LlamaIndex</a> - Especializado en RAG</li>
  <li><a href="https://haystack.deepset.ai/">Haystack</a> - Enterprise-ready</li>
</ul>

<p><strong>Papers Clave:</strong></p>
<ul>
  <li><a href="https://arxiv.org/abs/2005.11401">Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks</a></li>
  <li><a href="https://arxiv.org/abs/2310.11511">Self-RAG</a></li>
</ul>

<hr />

<p><strong>Próximo post:</strong> Fine-tuning LLMs con LoRA y PEFT para casos especializados.</p>]]></content><author><name>AI Tech Team</name></author><category term="NLP" /><category term="rag" /><category term="retrieval" /><category term="llm" /><category term="embeddings" /><category term="vector-database" /><category term="langchain" /><summary type="html"><![CDATA[Guía práctica para implementar sistemas RAG (Retrieval-Augmented Generation): embeddings, vector databases, chunking strategies y optimización de retrieval.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://images.unsplash.com/photo-1551288049-bebda4e38f71?w=800" /><media:content medium="image" url="https://images.unsplash.com/photo-1551288049-bebda4e38f71?w=800" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">GPT-4 y el Futuro del Procesamiento de Lenguaje Natural</title><link href="https://dev-jcgi.github.io/blog/blog/2026/01/26/gpt4-futuro-nlp/" rel="alternate" type="text/html" title="GPT-4 y el Futuro del Procesamiento de Lenguaje Natural" /><published>2026-01-26T00:00:00-06:00</published><updated>2026-01-26T00:00:00-06:00</updated><id>https://dev-jcgi.github.io/blog/blog/2026/01/26/gpt4-futuro-nlp</id><content type="html" xml:base="https://dev-jcgi.github.io/blog/blog/2026/01/26/gpt4-futuro-nlp/"><![CDATA[<p><strong>GPT-4</strong> representa un salto cuántico en el procesamiento de lenguaje natural, estableciendo nuevos estándares en comprensión y generación de texto.</p>

<h2 id="características-revolucionarias">Características Revolucionarias</h2>

<p>GPT-4 destaca por sus capacidades mejoradas:</p>

<ul>
  <li><strong>Comprensión contextual mejorada</strong>: Entiende matices y contextos complejos</li>
  <li><strong>Razonamiento avanzado</strong>: Puede resolver problemas lógicos y matemáticos</li>
  <li><strong>Multimodalidad</strong>: Procesa texto e imágenes conjuntamente</li>
  <li><strong>Mayor precisión</strong>: Menos alucinaciones y respuestas más confiables</li>
</ul>

<h2 id="arquitectura-transformer">Arquitectura Transformer</h2>

<p>La base de GPT-4 es la arquitectura Transformer, que utiliza mecanismos de atención para procesar secuencias:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Ejemplo simplificado de atención
</span><span class="kn">import</span> <span class="nn">torch</span>
<span class="kn">import</span> <span class="nn">torch.nn</span> <span class="k">as</span> <span class="n">nn</span>

<span class="k">class</span> <span class="nc">SelfAttention</span><span class="p">(</span><span class="n">nn</span><span class="p">.</span><span class="n">Module</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">embed_size</span><span class="p">,</span> <span class="n">heads</span><span class="p">):</span>
        <span class="nb">super</span><span class="p">(</span><span class="n">SelfAttention</span><span class="p">,</span> <span class="bp">self</span><span class="p">).</span><span class="n">__init__</span><span class="p">()</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">embed_size</span> <span class="o">=</span> <span class="n">embed_size</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">heads</span> <span class="o">=</span> <span class="n">heads</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">head_dim</span> <span class="o">=</span> <span class="n">embed_size</span> <span class="o">//</span> <span class="n">heads</span>
        
        <span class="c1"># Matrices de transformación
</span>        <span class="bp">self</span><span class="p">.</span><span class="n">values</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="n">embed_size</span><span class="p">,</span> <span class="n">embed_size</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">keys</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="n">embed_size</span><span class="p">,</span> <span class="n">embed_size</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">queries</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="n">embed_size</span><span class="p">,</span> <span class="n">embed_size</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">fc_out</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="n">embed_size</span><span class="p">,</span> <span class="n">embed_size</span><span class="p">)</span>
    
    <span class="k">def</span> <span class="nf">forward</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">values</span><span class="p">,</span> <span class="n">keys</span><span class="p">,</span> <span class="n">query</span><span class="p">,</span> <span class="n">mask</span><span class="p">):</span>
        <span class="n">N</span> <span class="o">=</span> <span class="n">query</span><span class="p">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
        <span class="n">value_len</span><span class="p">,</span> <span class="n">key_len</span><span class="p">,</span> <span class="n">query_len</span> <span class="o">=</span> <span class="n">values</span><span class="p">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">keys</span><span class="p">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">query</span><span class="p">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
        
        <span class="c1"># Calcular atención
</span>        <span class="n">energy</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">einsum</span><span class="p">(</span><span class="s">"nqhd,nkhd-&gt;nhqk"</span><span class="p">,</span> <span class="p">[</span><span class="n">queries</span><span class="p">,</span> <span class="n">keys</span><span class="p">])</span>
        
        <span class="k">if</span> <span class="n">mask</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
            <span class="n">energy</span> <span class="o">=</span> <span class="n">energy</span><span class="p">.</span><span class="n">masked_fill</span><span class="p">(</span><span class="n">mask</span> <span class="o">==</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">float</span><span class="p">(</span><span class="s">"-1e20"</span><span class="p">))</span>
        
        <span class="n">attention</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">softmax</span><span class="p">(</span><span class="n">energy</span> <span class="o">/</span> <span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">embed_size</span> <span class="o">**</span> <span class="p">(</span><span class="mi">1</span><span class="o">/</span><span class="mi">2</span><span class="p">)),</span> <span class="n">dim</span><span class="o">=</span><span class="mi">3</span><span class="p">)</span>
        <span class="n">out</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">einsum</span><span class="p">(</span><span class="s">"nhql,nlhd-&gt;nqhd"</span><span class="p">,</span> <span class="p">[</span><span class="n">attention</span><span class="p">,</span> <span class="n">values</span><span class="p">])</span>
        
        <span class="k">return</span> <span class="n">out</span>
</code></pre></div></div>

<h2 id="casos-de-uso">Casos de Uso</h2>

<h3 id="educación">Educación</h3>

<p>GPT-4 está transformando la educación:</p>

<ul>
  <li>Tutorías personalizadas adaptadas al nivel de cada estudiante</li>
  <li>Generación de contenido educativo adaptativo</li>
  <li>Evaluación y feedback instantáneo</li>
  <li>Traducción y explicación de conceptos complejos</li>
</ul>

<h3 id="desarrollo-de-software">Desarrollo de Software</h3>

<p>Los desarrolladores se benefician enormemente:</p>

<ul>
  <li>Asistencia en programación y debugging</li>
  <li>Generación de documentación automática</li>
  <li>Revisión de código y sugerencias de mejoras</li>
  <li>Traducción entre lenguajes de programación</li>
</ul>

<h3 id="creatividad">Creatividad</h3>

<p>Abre nuevas posibilidades creativas:</p>

<ul>
  <li>Escritura creativa y generación de historias</li>
  <li>Brainstorming y generación de ideas</li>
  <li>Creación de contenido marketing</li>
  <li>Guionización y desarrollo de personajes</li>
</ul>

<h2 id="limitaciones-y-desafíos">Limitaciones y Desafíos</h2>

<p>A pesar de sus capacidades, GPT-4 tiene limitaciones:</p>

<ol>
  <li><strong>Conocimiento Limitado</strong>: Datos de entrenamiento hasta una fecha específica</li>
  <li><strong>Alucinaciones</strong>: Puede generar información falsa con confianza</li>
  <li><strong>Sesgos</strong>: Refleja sesgos presentes en los datos de entrenamiento</li>
  <li><strong>Costo Computacional</strong>: Requiere recursos significativos</li>
</ol>

<h2 id="consideraciones-éticas">Consideraciones Éticas</h2>

<p>Es crucial considerar aspectos éticos:</p>

<h3 id="sesgos">Sesgos</h3>

<p>Los modelos pueden perpetuar sesgos presentes en los datos:</p>

<ul>
  <li>Sesgos de género</li>
  <li>Sesgos culturales</li>
  <li>Sesgos socioeconómicos</li>
  <li>Representación desigual</li>
</ul>

<h3 id="uso-responsable">Uso Responsable</h3>

<p>Debemos promover:</p>

<ul>
  <li>Transparencia en el uso de IA</li>
  <li>Verificación de información generada</li>
  <li>Protección de privacidad de datos</li>
  <li>Prevención de uso malicioso</li>
</ul>

<h3 id="impacto-laboral">Impacto Laboral</h3>

<p>La IA generativa está afectando el mercado laboral:</p>

<ul>
  <li>Automatización de tareas creativas</li>
  <li>Necesidad de nuevas habilidades</li>
  <li>Transformación de roles existentes</li>
  <li>Creación de nuevas oportunidades</li>
</ul>

<h2 id="el-futuro-del-nlp">El Futuro del NLP</h2>

<p>Las tendencias emergentes incluyen:</p>

<ul>
  <li><strong>Modelos más eficientes</strong>: Menor costo computacional</li>
  <li><strong>Mayor especialización</strong>: Modelos específicos para dominios</li>
  <li><strong>Multimodalidad avanzada</strong>: Integración de audio, video y texto</li>
  <li><strong>Mejor control</strong>: Mayor control sobre las salidas generadas</li>
  <li><strong>Personalización</strong>: Modelos adaptados a usuarios individuales</li>
</ul>

<h2 id="conclusión">Conclusión</h2>

<p>GPT-4 y los modelos de lenguaje grandes están redefiniendo lo que es posible con el procesamiento de lenguaje natural. A medida que la tecnología avanza, es esencial que nos centremos en el desarrollo ético y responsable, asegurando que estos poderosos sistemas beneficien a toda la humanidad.</p>

<p>El futuro del NLP es prometedor, pero requiere nuestra atención cuidadosa a las implicaciones éticas y sociales.</p>]]></content><author><name>AI Tech Blog</name></author><category term="NLP" /><category term="NLP" /><category term="GPT-4" /><category term="IA Generativa" /><category term="Transformers" /><summary type="html"><![CDATA[Explora las capacidades revolucionarias de GPT-4 y cómo está cambiando la forma en que interactuamos con la IA.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://images.unsplash.com/photo-1677442136019-21780ecad995?w=800" /><media:content medium="image" url="https://images.unsplash.com/photo-1677442136019-21780ecad995?w=800" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Transformers: La Arquitectura que Revolucionó la IA</title><link href="https://dev-jcgi.github.io/blog/blog/2026/01/25/transformers-arquitectura-explicada/" rel="alternate" type="text/html" title="Transformers: La Arquitectura que Revolucionó la IA" /><published>2026-01-25T10:00:00-06:00</published><updated>2026-01-25T10:00:00-06:00</updated><id>https://dev-jcgi.github.io/blog/blog/2026/01/25/transformers-arquitectura-explicada</id><content type="html" xml:base="https://dev-jcgi.github.io/blog/blog/2026/01/25/transformers-arquitectura-explicada/"><![CDATA[<p>La arquitectura <strong>Transformer</strong>, presentada en el paper “Attention is All You Need” (Vaswani et al., 2017), revolucionó el procesamiento del lenguaje natural y se convirtió en la base de modelos como GPT, BERT, T5 y prácticamente todos los LLMs modernos.</p>

<h2 id="por-qué-transformers">¿Por Qué Transformers?</h2>

<p>Antes de Transformers, las arquitecturas dominantes eran <strong>RNNs</strong> (Recurrent Neural Networks) y <strong>LSTMs</strong> (Long Short-Term Memory), que procesaban secuencias de manera secuencial. Esto tenía dos problemas críticos:</p>

<ol>
  <li><strong>Dependencia secuencial</strong>: No se podía paralelizar el entrenamiento</li>
  <li><strong>Problema del gradiente</strong>: Dificultad para capturar dependencias largas</li>
</ol>

<p>Transformers eliminó ambos problemas con un mecanismo revolucionario: <strong>Self-Attention</strong>.</p>

<h2 id="arquitectura-general">Arquitectura General</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Transformer</span><span class="p">(</span><span class="n">nn</span><span class="p">.</span><span class="n">Module</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">d_model</span><span class="o">=</span><span class="mi">512</span><span class="p">,</span> <span class="n">nhead</span><span class="o">=</span><span class="mi">8</span><span class="p">,</span> <span class="n">num_layers</span><span class="o">=</span><span class="mi">6</span><span class="p">):</span>
        <span class="nb">super</span><span class="p">().</span><span class="n">__init__</span><span class="p">()</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">encoder</span> <span class="o">=</span> <span class="n">TransformerEncoder</span><span class="p">(</span><span class="n">num_layers</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">decoder</span> <span class="o">=</span> <span class="n">TransformerDecoder</span><span class="p">(</span><span class="n">num_layers</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">embedding</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Embedding</span><span class="p">(</span><span class="n">vocab_size</span><span class="p">,</span> <span class="n">d_model</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">positional_encoding</span> <span class="o">=</span> <span class="n">PositionalEncoding</span><span class="p">(</span><span class="n">d_model</span><span class="p">)</span>
        
    <span class="k">def</span> <span class="nf">forward</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">src</span><span class="p">,</span> <span class="n">tgt</span><span class="p">):</span>
        <span class="c1"># Embedding + Positional Encoding
</span>        <span class="n">src</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">positional_encoding</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">embedding</span><span class="p">(</span><span class="n">src</span><span class="p">))</span>
        <span class="n">tgt</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">positional_encoding</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">embedding</span><span class="p">(</span><span class="n">tgt</span><span class="p">))</span>
        
        <span class="c1"># Encoder -&gt; Decoder
</span>        <span class="n">memory</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">encoder</span><span class="p">(</span><span class="n">src</span><span class="p">)</span>
        <span class="n">output</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">decoder</span><span class="p">(</span><span class="n">tgt</span><span class="p">,</span> <span class="n">memory</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">output</span>
</code></pre></div></div>

<h3 id="componentes-clave">Componentes Clave</h3>

<ol>
  <li><strong>Input Embedding</strong>: Convierte tokens a vectores densos</li>
  <li><strong>Positional Encoding</strong>: Inyecta información de posición</li>
  <li><strong>Encoder Stack</strong>: 6 capas idénticas (en el paper original)</li>
  <li><strong>Decoder Stack</strong>: 6 capas idénticas con masked attention</li>
  <li><strong>Output Linear + Softmax</strong>: Predicción final</li>
</ol>

<h2 id="self-attention-mechanism">Self-Attention Mechanism</h2>

<p>El corazón de Transformers es el mecanismo de <strong>self-attention</strong>:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">torch</span>
<span class="kn">import</span> <span class="nn">torch.nn</span> <span class="k">as</span> <span class="n">nn</span>
<span class="kn">import</span> <span class="nn">math</span>

<span class="k">class</span> <span class="nc">SelfAttention</span><span class="p">(</span><span class="n">nn</span><span class="p">.</span><span class="n">Module</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">d_model</span><span class="p">,</span> <span class="n">num_heads</span><span class="p">):</span>
        <span class="nb">super</span><span class="p">().</span><span class="n">__init__</span><span class="p">()</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">d_model</span> <span class="o">=</span> <span class="n">d_model</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">num_heads</span> <span class="o">=</span> <span class="n">num_heads</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">head_dim</span> <span class="o">=</span> <span class="n">d_model</span> <span class="o">//</span> <span class="n">num_heads</span>
        
        <span class="c1"># Linear projections
</span>        <span class="bp">self</span><span class="p">.</span><span class="n">W_q</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="n">d_model</span><span class="p">,</span> <span class="n">d_model</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">W_k</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="n">d_model</span><span class="p">,</span> <span class="n">d_model</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">W_v</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="n">d_model</span><span class="p">,</span> <span class="n">d_model</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">W_o</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="n">d_model</span><span class="p">,</span> <span class="n">d_model</span><span class="p">)</span>
        
    <span class="k">def</span> <span class="nf">forward</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">mask</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
        <span class="n">batch_size</span><span class="p">,</span> <span class="n">seq_len</span><span class="p">,</span> <span class="n">d_model</span> <span class="o">=</span> <span class="n">x</span><span class="p">.</span><span class="n">shape</span>
        
        <span class="c1"># Linear projections in batch from d_model =&gt; h x d_k
</span>        <span class="n">Q</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">W_q</span><span class="p">(</span><span class="n">x</span><span class="p">).</span><span class="n">view</span><span class="p">(</span><span class="n">batch_size</span><span class="p">,</span> <span class="n">seq_len</span><span class="p">,</span> <span class="bp">self</span><span class="p">.</span><span class="n">num_heads</span><span class="p">,</span> <span class="bp">self</span><span class="p">.</span><span class="n">head_dim</span><span class="p">)</span>
        <span class="n">K</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">W_k</span><span class="p">(</span><span class="n">x</span><span class="p">).</span><span class="n">view</span><span class="p">(</span><span class="n">batch_size</span><span class="p">,</span> <span class="n">seq_len</span><span class="p">,</span> <span class="bp">self</span><span class="p">.</span><span class="n">num_heads</span><span class="p">,</span> <span class="bp">self</span><span class="p">.</span><span class="n">head_dim</span><span class="p">)</span>
        <span class="n">V</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">W_v</span><span class="p">(</span><span class="n">x</span><span class="p">).</span><span class="n">view</span><span class="p">(</span><span class="n">batch_size</span><span class="p">,</span> <span class="n">seq_len</span><span class="p">,</span> <span class="bp">self</span><span class="p">.</span><span class="n">num_heads</span><span class="p">,</span> <span class="bp">self</span><span class="p">.</span><span class="n">head_dim</span><span class="p">)</span>
        
        <span class="c1"># Transpose for attention: (batch, num_heads, seq_len, head_dim)
</span>        <span class="n">Q</span> <span class="o">=</span> <span class="n">Q</span><span class="p">.</span><span class="n">transpose</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
        <span class="n">K</span> <span class="o">=</span> <span class="n">K</span><span class="p">.</span><span class="n">transpose</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
        <span class="n">V</span> <span class="o">=</span> <span class="n">V</span><span class="p">.</span><span class="n">transpose</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
        
        <span class="c1"># Scaled Dot-Product Attention
</span>        <span class="n">scores</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">matmul</span><span class="p">(</span><span class="n">Q</span><span class="p">,</span> <span class="n">K</span><span class="p">.</span><span class="n">transpose</span><span class="p">(</span><span class="o">-</span><span class="mi">2</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">))</span> <span class="o">/</span> <span class="n">math</span><span class="p">.</span><span class="n">sqrt</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">head_dim</span><span class="p">)</span>
        
        <span class="k">if</span> <span class="n">mask</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
            <span class="n">scores</span> <span class="o">=</span> <span class="n">scores</span><span class="p">.</span><span class="n">masked_fill</span><span class="p">(</span><span class="n">mask</span> <span class="o">==</span> <span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mf">1e9</span><span class="p">)</span>
        
        <span class="n">attention_weights</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">softmax</span><span class="p">(</span><span class="n">scores</span><span class="p">,</span> <span class="n">dim</span><span class="o">=-</span><span class="mi">1</span><span class="p">)</span>
        <span class="n">attention_output</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">matmul</span><span class="p">(</span><span class="n">attention_weights</span><span class="p">,</span> <span class="n">V</span><span class="p">)</span>
        
        <span class="c1"># Concatenate heads
</span>        <span class="n">attention_output</span> <span class="o">=</span> <span class="n">attention_output</span><span class="p">.</span><span class="n">transpose</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">).</span><span class="n">contiguous</span><span class="p">()</span>
        <span class="n">attention_output</span> <span class="o">=</span> <span class="n">attention_output</span><span class="p">.</span><span class="n">view</span><span class="p">(</span><span class="n">batch_size</span><span class="p">,</span> <span class="n">seq_len</span><span class="p">,</span> <span class="n">d_model</span><span class="p">)</span>
        
        <span class="c1"># Final linear projection
</span>        <span class="n">output</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">W_o</span><span class="p">(</span><span class="n">attention_output</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">output</span><span class="p">,</span> <span class="n">attention_weights</span>
</code></pre></div></div>

<h3 id="scaled-dot-product-attention">Scaled Dot-Product Attention</h3>

<p>La fórmula matemática es:</p>

\[\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V\]

<p><strong>¿Por qué dividir por √d_k?</strong></p>
<ul>
  <li>Evita que los productos punto sean muy grandes</li>
  <li>Mejora la estabilidad del gradiente</li>
  <li>Previene que softmax sature</li>
</ul>

<h2 id="multi-head-attention">Multi-Head Attention</h2>

<p>En lugar de una sola atención, Transformers usa <strong>múltiples cabezas</strong> en paralelo:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 8 heads con d_model=512 =&gt; cada head procesa 64 dimensiones
</span><span class="n">num_heads</span> <span class="o">=</span> <span class="mi">8</span>
<span class="n">d_model</span> <span class="o">=</span> <span class="mi">512</span>
<span class="n">head_dim</span> <span class="o">=</span> <span class="n">d_model</span> <span class="o">//</span> <span class="n">num_heads</span>  <span class="c1"># 64
</span>
<span class="c1"># Ventajas:
# 1. Captura diferentes representaciones
# 2. Permite al modelo atender a diferentes posiciones simultáneamente
# 3. Aprende patrones complementarios
</span></code></pre></div></div>

<h2 id="positional-encoding">Positional Encoding</h2>

<p>Como Transformers <strong>no tiene recurrencia</strong>, necesita inyectar información de posición:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>

<span class="k">class</span> <span class="nc">PositionalEncoding</span><span class="p">(</span><span class="n">nn</span><span class="p">.</span><span class="n">Module</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">d_model</span><span class="p">,</span> <span class="n">max_len</span><span class="o">=</span><span class="mi">5000</span><span class="p">):</span>
        <span class="nb">super</span><span class="p">().</span><span class="n">__init__</span><span class="p">()</span>
        
        <span class="c1"># Crear matriz de positional encodings
</span>        <span class="n">pe</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">zeros</span><span class="p">(</span><span class="n">max_len</span><span class="p">,</span> <span class="n">d_model</span><span class="p">)</span>
        <span class="n">position</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">max_len</span><span class="p">,</span> <span class="n">dtype</span><span class="o">=</span><span class="n">torch</span><span class="p">.</span><span class="nb">float</span><span class="p">).</span><span class="n">unsqueeze</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
        
        <span class="c1"># Fórmula del paper original
</span>        <span class="n">div_term</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">exp</span><span class="p">(</span>
            <span class="n">torch</span><span class="p">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">d_model</span><span class="p">,</span> <span class="mi">2</span><span class="p">).</span><span class="nb">float</span><span class="p">()</span> <span class="o">*</span> <span class="p">(</span><span class="o">-</span><span class="n">math</span><span class="p">.</span><span class="n">log</span><span class="p">(</span><span class="mf">10000.0</span><span class="p">)</span> <span class="o">/</span> <span class="n">d_model</span><span class="p">)</span>
        <span class="p">)</span>
        
        <span class="n">pe</span><span class="p">[:,</span> <span class="mi">0</span><span class="p">::</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">sin</span><span class="p">(</span><span class="n">position</span> <span class="o">*</span> <span class="n">div_term</span><span class="p">)</span>  <span class="c1"># Posiciones pares
</span>        <span class="n">pe</span><span class="p">[:,</span> <span class="mi">1</span><span class="p">::</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">cos</span><span class="p">(</span><span class="n">position</span> <span class="o">*</span> <span class="n">div_term</span><span class="p">)</span>  <span class="c1"># Posiciones impares
</span>        
        <span class="n">pe</span> <span class="o">=</span> <span class="n">pe</span><span class="p">.</span><span class="n">unsqueeze</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>  <span class="c1"># (1, max_len, d_model)
</span>        <span class="bp">self</span><span class="p">.</span><span class="n">register_buffer</span><span class="p">(</span><span class="s">'pe'</span><span class="p">,</span> <span class="n">pe</span><span class="p">)</span>
        
    <span class="k">def</span> <span class="nf">forward</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">):</span>
        <span class="c1"># x shape: (batch_size, seq_len, d_model)
</span>        <span class="n">x</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="bp">self</span><span class="p">.</span><span class="n">pe</span><span class="p">[:,</span> <span class="p">:</span><span class="n">x</span><span class="p">.</span><span class="n">size</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span> <span class="p">:]</span>
        <span class="k">return</span> <span class="n">x</span>
</code></pre></div></div>

<p><strong>Ventajas de esta codificación:</strong></p>
<ul>
  <li>Determinística (no se aprende)</li>
  <li>Permite extrapolar a secuencias más largas</li>
  <li>Captura relaciones relativas entre posiciones</li>
</ul>

<h2 id="feed-forward-network">Feed-Forward Network</h2>

<p>Cada capa del encoder/decoder tiene una FFN:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">FeedForward</span><span class="p">(</span><span class="n">nn</span><span class="p">.</span><span class="n">Module</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">d_model</span><span class="p">,</span> <span class="n">d_ff</span><span class="o">=</span><span class="mi">2048</span><span class="p">,</span> <span class="n">dropout</span><span class="o">=</span><span class="mf">0.1</span><span class="p">):</span>
        <span class="nb">super</span><span class="p">().</span><span class="n">__init__</span><span class="p">()</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">linear1</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="n">d_model</span><span class="p">,</span> <span class="n">d_ff</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">dropout</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Dropout</span><span class="p">(</span><span class="n">dropout</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">linear2</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="n">d_ff</span><span class="p">,</span> <span class="n">d_model</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">activation</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">ReLU</span><span class="p">()</span>
        
    <span class="k">def</span> <span class="nf">forward</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">):</span>
        <span class="c1"># FFN(x) = max(0, xW1 + b1)W2 + b2
</span>        <span class="n">x</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">linear1</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
        <span class="n">x</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">activation</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
        <span class="n">x</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">dropout</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
        <span class="n">x</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">linear2</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">x</span>
</code></pre></div></div>

<h2 id="layer-normalization-y-residual-connections">Layer Normalization y Residual Connections</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">EncoderLayer</span><span class="p">(</span><span class="n">nn</span><span class="p">.</span><span class="n">Module</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">d_model</span><span class="p">,</span> <span class="n">num_heads</span><span class="p">,</span> <span class="n">d_ff</span><span class="p">,</span> <span class="n">dropout</span><span class="o">=</span><span class="mf">0.1</span><span class="p">):</span>
        <span class="nb">super</span><span class="p">().</span><span class="n">__init__</span><span class="p">()</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">self_attn</span> <span class="o">=</span> <span class="n">MultiHeadAttention</span><span class="p">(</span><span class="n">d_model</span><span class="p">,</span> <span class="n">num_heads</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">feed_forward</span> <span class="o">=</span> <span class="n">FeedForward</span><span class="p">(</span><span class="n">d_model</span><span class="p">,</span> <span class="n">d_ff</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">norm1</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">LayerNorm</span><span class="p">(</span><span class="n">d_model</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">norm2</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">LayerNorm</span><span class="p">(</span><span class="n">d_model</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">dropout1</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Dropout</span><span class="p">(</span><span class="n">dropout</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">dropout2</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Dropout</span><span class="p">(</span><span class="n">dropout</span><span class="p">)</span>
        
    <span class="k">def</span> <span class="nf">forward</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">mask</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
        <span class="c1"># Multi-Head Attention + Residual + Norm
</span>        <span class="n">attn_output</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">self_attn</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">mask</span><span class="p">)</span>
        <span class="n">x</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="bp">self</span><span class="p">.</span><span class="n">dropout1</span><span class="p">(</span><span class="n">attn_output</span><span class="p">)</span>  <span class="c1"># Residual connection
</span>        <span class="n">x</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">norm1</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>                    <span class="c1"># Layer normalization
</span>        
        <span class="c1"># Feed-Forward + Residual + Norm
</span>        <span class="n">ff_output</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">feed_forward</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
        <span class="n">x</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="bp">self</span><span class="p">.</span><span class="n">dropout2</span><span class="p">(</span><span class="n">ff_output</span><span class="p">)</span>     <span class="c1"># Residual connection
</span>        <span class="n">x</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">norm2</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>                    <span class="c1"># Layer normalization
</span>        
        <span class="k">return</span> <span class="n">x</span>
</code></pre></div></div>

<h2 id="diferencias-encoder-vs-decoder">Diferencias: Encoder vs Decoder</h2>

<h3 id="encoder">Encoder</h3>
<ul>
  <li><strong>Self-attention bidireccional</strong>: puede ver toda la secuencia</li>
  <li>Usado en BERT, RoBERTa (modelos encoder-only)</li>
</ul>

<h3 id="decoder">Decoder</h3>
<ul>
  <li><strong>Masked self-attention</strong>: solo ve tokens anteriores</li>
  <li><strong>Cross-attention</strong>: atiende a la salida del encoder</li>
  <li>Usado en GPT (decoder-only), T5 (encoder-decoder)</li>
</ul>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">DecoderLayer</span><span class="p">(</span><span class="n">nn</span><span class="p">.</span><span class="n">Module</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">d_model</span><span class="p">,</span> <span class="n">num_heads</span><span class="p">,</span> <span class="n">d_ff</span><span class="p">,</span> <span class="n">dropout</span><span class="o">=</span><span class="mf">0.1</span><span class="p">):</span>
        <span class="nb">super</span><span class="p">().</span><span class="n">__init__</span><span class="p">()</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">self_attn</span> <span class="o">=</span> <span class="n">MultiHeadAttention</span><span class="p">(</span><span class="n">d_model</span><span class="p">,</span> <span class="n">num_heads</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">cross_attn</span> <span class="o">=</span> <span class="n">MultiHeadAttention</span><span class="p">(</span><span class="n">d_model</span><span class="p">,</span> <span class="n">num_heads</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">feed_forward</span> <span class="o">=</span> <span class="n">FeedForward</span><span class="p">(</span><span class="n">d_model</span><span class="p">,</span> <span class="n">d_ff</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">norm1</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">LayerNorm</span><span class="p">(</span><span class="n">d_model</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">norm2</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">LayerNorm</span><span class="p">(</span><span class="n">d_model</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">norm3</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">LayerNorm</span><span class="p">(</span><span class="n">d_model</span><span class="p">)</span>
        
    <span class="k">def</span> <span class="nf">forward</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">encoder_output</span><span class="p">,</span> <span class="n">src_mask</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">tgt_mask</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
        <span class="c1"># Masked Self-Attention (solo ve pasado)
</span>        <span class="n">attn_output</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">self_attn</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">tgt_mask</span><span class="p">)</span>
        <span class="n">x</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">norm1</span><span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="n">attn_output</span><span class="p">)</span>
        
        <span class="c1"># Cross-Attention (atiende al encoder)
</span>        <span class="n">attn_output</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">cross_attn</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">encoder_output</span><span class="p">,</span> <span class="n">src_mask</span><span class="p">)</span>
        <span class="n">x</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">norm2</span><span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="n">attn_output</span><span class="p">)</span>
        
        <span class="c1"># Feed-Forward
</span>        <span class="n">ff_output</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">feed_forward</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
        <span class="n">x</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">norm3</span><span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="n">ff_output</span><span class="p">)</span>
        
        <span class="k">return</span> <span class="n">x</span>
</code></pre></div></div>

<h2 id="ejemplo-práctico-traducción">Ejemplo Práctico: Traducción</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">transformers</span> <span class="kn">import</span> <span class="n">AutoTokenizer</span><span class="p">,</span> <span class="n">AutoModelForSeq2SeqLM</span>

<span class="c1"># Cargar modelo preentrenado (T5)
</span><span class="n">model_name</span> <span class="o">=</span> <span class="s">"t5-small"</span>
<span class="n">tokenizer</span> <span class="o">=</span> <span class="n">AutoTokenizer</span><span class="p">.</span><span class="n">from_pretrained</span><span class="p">(</span><span class="n">model_name</span><span class="p">)</span>
<span class="n">model</span> <span class="o">=</span> <span class="n">AutoModelForSeq2SeqLM</span><span class="p">.</span><span class="n">from_pretrained</span><span class="p">(</span><span class="n">model_name</span><span class="p">)</span>

<span class="c1"># Traducir texto
</span><span class="n">text</span> <span class="o">=</span> <span class="s">"translate English to Spanish: Hello, how are you?"</span>
<span class="n">inputs</span> <span class="o">=</span> <span class="n">tokenizer</span><span class="p">(</span><span class="n">text</span><span class="p">,</span> <span class="n">return_tensors</span><span class="o">=</span><span class="s">"pt"</span><span class="p">)</span>

<span class="c1"># Generar traducción
</span><span class="n">outputs</span> <span class="o">=</span> <span class="n">model</span><span class="p">.</span><span class="n">generate</span><span class="p">(</span><span class="o">**</span><span class="n">inputs</span><span class="p">,</span> <span class="n">max_length</span><span class="o">=</span><span class="mi">50</span><span class="p">)</span>
<span class="n">translation</span> <span class="o">=</span> <span class="n">tokenizer</span><span class="p">.</span><span class="n">decode</span><span class="p">(</span><span class="n">outputs</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">skip_special_tokens</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>

<span class="k">print</span><span class="p">(</span><span class="n">translation</span><span class="p">)</span>  <span class="c1"># "Hola, ¿cómo estás?"
</span></code></pre></div></div>

<h2 id="variantes-modernas">Variantes Modernas</h2>

<h3 id="1-gpt-decoder-only">1. <strong>GPT (Decoder-only)</strong></h3>
<ul>
  <li>Solo usa el decoder</li>
  <li>Preentrenamiento: predecir siguiente token</li>
  <li>Casos de uso: generación de texto, chatbots</li>
</ul>

<h3 id="2-bert-encoder-only">2. <strong>BERT (Encoder-only)</strong></h3>
<ul>
  <li>Solo usa el encoder</li>
  <li>Preentrenamiento: masked language modeling</li>
  <li>Casos de uso: clasificación, NER, Q&amp;A</li>
</ul>

<h3 id="3-t5-encoder-decoder-completo">3. <strong>T5 (Encoder-Decoder completo)</strong></h3>
<ul>
  <li>Usa ambos componentes</li>
  <li>Todo se formula como text-to-text</li>
  <li>Casos de uso: traducción, resumen, Q&amp;A</li>
</ul>

<h2 id="optimizaciones-y-mejoras">Optimizaciones y Mejoras</h2>

<h3 id="flash-attention">Flash Attention</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Reduce complejidad de O(N²) a O(N)
</span><span class="kn">from</span> <span class="nn">flash_attn</span> <span class="kn">import</span> <span class="n">flash_attn_func</span>

<span class="n">attention_output</span> <span class="o">=</span> <span class="n">flash_attn_func</span><span class="p">(</span><span class="n">q</span><span class="p">,</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">,</span> <span class="n">causal</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="relative-positional-encoding">Relative Positional Encoding</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Usado en Transformer-XL y T5
# Aprende posiciones relativas en lugar de absolutas
</span><span class="k">class</span> <span class="nc">RelativePositionalEncoding</span><span class="p">(</span><span class="n">nn</span><span class="p">.</span><span class="n">Module</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">d_model</span><span class="p">,</span> <span class="n">max_len</span><span class="o">=</span><span class="mi">512</span><span class="p">):</span>
        <span class="nb">super</span><span class="p">().</span><span class="n">__init__</span><span class="p">()</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">rel_pos_bias</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Parameter</span><span class="p">(</span><span class="n">torch</span><span class="p">.</span><span class="n">randn</span><span class="p">(</span><span class="n">max_len</span><span class="p">,</span> <span class="n">max_len</span><span class="p">,</span> <span class="n">d_model</span><span class="p">))</span>
</code></pre></div></div>

<h3 id="sparse-attention">Sparse Attention</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Usado en Longformer y BigBird
# Atiende solo a ventanas locales + tokens globales
# Permite secuencias mucho más largas
</span></code></pre></div></div>

<h2 id="métricas-de-evaluación">Métricas de Evaluación</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">torchmetrics</span> <span class="kn">import</span> <span class="n">BLEUScore</span><span class="p">,</span> <span class="n">ROUGEScore</span>

<span class="c1"># BLEU para traducción
</span><span class="n">bleu</span> <span class="o">=</span> <span class="n">BLEUScore</span><span class="p">(</span><span class="n">n_gram</span><span class="o">=</span><span class="mi">4</span><span class="p">)</span>
<span class="n">score</span> <span class="o">=</span> <span class="n">bleu</span><span class="p">(</span><span class="n">predictions</span><span class="p">,</span> <span class="n">references</span><span class="p">)</span>

<span class="c1"># ROUGE para resumen
</span><span class="n">rouge</span> <span class="o">=</span> <span class="n">ROUGEScore</span><span class="p">()</span>
<span class="n">score</span> <span class="o">=</span> <span class="n">rouge</span><span class="p">(</span><span class="n">predictions</span><span class="p">,</span> <span class="n">references</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="conclusión">Conclusión</h2>

<p>Transformers cambió el paradigma del NLP por:</p>

<ol>
  <li><strong>Paralelización</strong>: entrena mucho más rápido que RNNs</li>
  <li><strong>Captura de dependencias largas</strong>: self-attention ve toda la secuencia</li>
  <li><strong>Transferencia de aprendizaje</strong>: preentrenar y fine-tunar</li>
  <li><strong>Versatilidad</strong>: funciona en visión, audio, proteínas</li>
</ol>

<p><strong>Papers Clave:</strong></p>
<ul>
  <li><a href="https://arxiv.org/abs/1706.03762">Attention Is All You Need</a> (Vaswani et al., 2017)</li>
  <li><a href="https://arxiv.org/abs/1810.04805">BERT</a> (Devlin et al., 2018)</li>
  <li><a href="https://arxiv.org/abs/2005.14165">GPT-3</a> (Brown et al., 2020)</li>
</ul>

<p><strong>Código Completo:</strong> <a href="https://github.com/karpathy/minGPT">GitHub - Transformer from Scratch</a></p>

<hr />

<p><strong>¿Te gustó este post?</strong> Compártelo y sigue explorando la serie de Deep Learning en este blog.</p>]]></content><author><name>AI Tech Team</name></author><category term="Deep Learning" /><category term="transformers" /><category term="attention" /><category term="nlp" /><category term="deep-learning" /><category term="architecture" /><summary type="html"><![CDATA[Guía completa sobre la arquitectura Transformer: self-attention, multi-head attention, positional encoding y cómo GPT y BERT revolucionaron el NLP.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://images.unsplash.com/photo-1620712943543-bcc4688e7485?w=800" /><media:content medium="image" url="https://images.unsplash.com/photo-1620712943543-bcc4688e7485?w=800" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Redes Neuronales Convolucionales para Visión por Computadora</title><link href="https://dev-jcgi.github.io/blog/blog/2026/01/25/cnn-vision-computadora/" rel="alternate" type="text/html" title="Redes Neuronales Convolucionales para Visión por Computadora" /><published>2026-01-25T00:00:00-06:00</published><updated>2026-01-25T00:00:00-06:00</updated><id>https://dev-jcgi.github.io/blog/blog/2026/01/25/cnn-vision-computadora</id><content type="html" xml:base="https://dev-jcgi.github.io/blog/blog/2026/01/25/cnn-vision-computadora/"><![CDATA[<p>Las <strong>CNN (Convolutional Neural Networks)</strong> son el corazón de la visión por computadora moderna, permitiendo a las máquinas “ver” y comprender imágenes con precisión sin precedentes.</p>

<h2 id="qué-son-las-cnn">¿Qué son las CNN?</h2>

<p>Las CNN son redes neuronales especializadas en procesar datos con estructura de cuadrícula, como imágenes. Se inspiran en el sistema visual humano y utilizan capas de convolución para detectar características.</p>

<h3 id="componentes-clave">Componentes Clave</h3>

<h4 id="capas-convolucionales">Capas Convolucionales</h4>

<p>Detectan características locales mediante filtros:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">torch</span>
<span class="kn">import</span> <span class="nn">torch.nn</span> <span class="k">as</span> <span class="n">nn</span>

<span class="c1"># Ejemplo de capa convolucional
</span><span class="n">conv_layer</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Conv2d</span><span class="p">(</span>
    <span class="n">in_channels</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span>      <span class="c1"># RGB
</span>    <span class="n">out_channels</span><span class="o">=</span><span class="mi">64</span><span class="p">,</span>    <span class="c1"># 64 filtros
</span>    <span class="n">kernel_size</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span>      <span class="c1"># Filtro 3x3
</span>    <span class="n">stride</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span>
    <span class="n">padding</span><span class="o">=</span><span class="mi">1</span>
<span class="p">)</span>

<span class="c1"># Aplicar a una imagen
</span><span class="n">input_image</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">randn</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">224</span><span class="p">,</span> <span class="mi">224</span><span class="p">)</span>  <span class="c1"># Batch, Canales, Alto, Ancho
</span><span class="n">output</span> <span class="o">=</span> <span class="n">conv_layer</span><span class="p">(</span><span class="n">input_image</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Output shape: </span><span class="si">{</span><span class="n">output</span><span class="p">.</span><span class="n">shape</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>  <span class="c1"># [1, 64, 224, 224]
</span></code></pre></div></div>

<h4 id="capas-de-pooling">Capas de Pooling</h4>

<p>Reducen la dimensionalidad preservando información importante:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># MaxPooling - toma el valor máximo
</span><span class="n">maxpool</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">MaxPool2d</span><span class="p">(</span><span class="n">kernel_size</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">stride</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>

<span class="c1"># AvgPooling - toma el promedio
</span><span class="n">avgpool</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">AvgPool2d</span><span class="p">(</span><span class="n">kernel_size</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">stride</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>

<span class="n">pooled</span> <span class="o">=</span> <span class="n">maxpool</span><span class="p">(</span><span class="n">output</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"After pooling: </span><span class="si">{</span><span class="n">pooled</span><span class="p">.</span><span class="n">shape</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>  <span class="c1"># [1, 64, 112, 112]
</span></code></pre></div></div>

<h4 id="capas-fully-connected">Capas Fully Connected</h4>

<p>Realizan la clasificación final:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Flatten + FC layers
</span><span class="n">flatten</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Flatten</span><span class="p">()</span>
<span class="n">fc</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="mi">64</span> <span class="o">*</span> <span class="mi">112</span> <span class="o">*</span> <span class="mi">112</span><span class="p">,</span> <span class="mi">1000</span><span class="p">)</span>  <span class="c1"># 1000 clases
</span></code></pre></div></div>

<h2 id="arquitecturas-famosas">Arquitecturas Famosas</h2>

<h3 id="lenet-5-1998">LeNet-5 (1998)</h3>

<p>La pionera en reconocimiento de dígitos MNIST:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">LeNet5</span><span class="p">(</span><span class="n">nn</span><span class="p">.</span><span class="n">Module</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="nb">super</span><span class="p">(</span><span class="n">LeNet5</span><span class="p">,</span> <span class="bp">self</span><span class="p">).</span><span class="n">__init__</span><span class="p">()</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">conv1</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Conv2d</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">pool</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">MaxPool2d</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">conv2</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Conv2d</span><span class="p">(</span><span class="mi">6</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">fc1</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="mi">16</span> <span class="o">*</span> <span class="mi">4</span> <span class="o">*</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">120</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">fc2</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="mi">120</span><span class="p">,</span> <span class="mi">84</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">fc3</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="mi">84</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span>
        
    <span class="k">def</span> <span class="nf">forward</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">):</span>
        <span class="n">x</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">pool</span><span class="p">(</span><span class="n">torch</span><span class="p">.</span><span class="n">relu</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">conv1</span><span class="p">(</span><span class="n">x</span><span class="p">)))</span>
        <span class="n">x</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">pool</span><span class="p">(</span><span class="n">torch</span><span class="p">.</span><span class="n">relu</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">conv2</span><span class="p">(</span><span class="n">x</span><span class="p">)))</span>
        <span class="n">x</span> <span class="o">=</span> <span class="n">x</span><span class="p">.</span><span class="n">view</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">16</span> <span class="o">*</span> <span class="mi">4</span> <span class="o">*</span> <span class="mi">4</span><span class="p">)</span>
        <span class="n">x</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">relu</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">fc1</span><span class="p">(</span><span class="n">x</span><span class="p">))</span>
        <span class="n">x</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">relu</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">fc2</span><span class="p">(</span><span class="n">x</span><span class="p">))</span>
        <span class="n">x</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">fc3</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">x</span>
</code></pre></div></div>

<h3 id="alexnet-2012">AlexNet (2012)</h3>

<p>Ganadora de ImageNet que inició la revolución del Deep Learning:</p>

<ul>
  <li>8 capas (5 convolucionales, 3 fully connected)</li>
  <li>ReLU activation</li>
  <li>Dropout para regularización</li>
  <li>Data augmentation</li>
</ul>

<h3 id="vgg-2014">VGG (2014)</h3>

<p>Arquitectura profunda y uniforme:</p>

<ul>
  <li>Bloques repetitivos de conv → conv → pool</li>
  <li>Filtros pequeños (3x3)</li>
  <li>Hasta 19 capas de profundidad</li>
</ul>

<h3 id="resnet-2015">ResNet (2015)</h3>

<p>Introduce conexiones residuales que permiten entrenar redes muy profundas:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">ResidualBlock</span><span class="p">(</span><span class="n">nn</span><span class="p">.</span><span class="n">Module</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">in_channels</span><span class="p">,</span> <span class="n">out_channels</span><span class="p">,</span> <span class="n">stride</span><span class="o">=</span><span class="mi">1</span><span class="p">):</span>
        <span class="nb">super</span><span class="p">(</span><span class="n">ResidualBlock</span><span class="p">,</span> <span class="bp">self</span><span class="p">).</span><span class="n">__init__</span><span class="p">()</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">conv1</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Conv2d</span><span class="p">(</span><span class="n">in_channels</span><span class="p">,</span> <span class="n">out_channels</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="n">stride</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">bn1</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">BatchNorm2d</span><span class="p">(</span><span class="n">out_channels</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">conv2</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Conv2d</span><span class="p">(</span><span class="n">out_channels</span><span class="p">,</span> <span class="n">out_channels</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">bn2</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">BatchNorm2d</span><span class="p">(</span><span class="n">out_channels</span><span class="p">)</span>
        
        <span class="bp">self</span><span class="p">.</span><span class="n">shortcut</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Sequential</span><span class="p">()</span>
        <span class="k">if</span> <span class="n">stride</span> <span class="o">!=</span> <span class="mi">1</span> <span class="ow">or</span> <span class="n">in_channels</span> <span class="o">!=</span> <span class="n">out_channels</span><span class="p">:</span>
            <span class="bp">self</span><span class="p">.</span><span class="n">shortcut</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Sequential</span><span class="p">(</span>
                <span class="n">nn</span><span class="p">.</span><span class="n">Conv2d</span><span class="p">(</span><span class="n">in_channels</span><span class="p">,</span> <span class="n">out_channels</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">stride</span><span class="p">),</span>
                <span class="n">nn</span><span class="p">.</span><span class="n">BatchNorm2d</span><span class="p">(</span><span class="n">out_channels</span><span class="p">)</span>
            <span class="p">)</span>
    
    <span class="k">def</span> <span class="nf">forward</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">):</span>
        <span class="n">out</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">relu</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">bn1</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">conv1</span><span class="p">(</span><span class="n">x</span><span class="p">)))</span>
        <span class="n">out</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">bn2</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">conv2</span><span class="p">(</span><span class="n">out</span><span class="p">))</span>
        <span class="n">out</span> <span class="o">+=</span> <span class="bp">self</span><span class="p">.</span><span class="n">shortcut</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>  <span class="c1"># Skip connection
</span>        <span class="n">out</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">relu</span><span class="p">(</span><span class="n">out</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">out</span>
</code></pre></div></div>

<h3 id="efficientnet-2019">EfficientNet (2019)</h3>

<p>Optimización del escalado de profundidad, anchura y resolución:</p>

<ul>
  <li>Compound scaling</li>
  <li>Mejor balance eficiencia/precisión</li>
  <li>Mobile-friendly</li>
</ul>

<h2 id="aplicaciones-modernas">Aplicaciones Modernas</h2>

<h3 id="1-reconocimiento-facial">1. Reconocimiento Facial</h3>

<p>Sistemas de seguridad y autenticación:</p>

<ul>
  <li>Desbloqueo de dispositivos (Face ID)</li>
  <li>Control de acceso</li>
  <li>Búsqueda de personas</li>
  <li>Análisis de expresiones</li>
</ul>

<h3 id="2-diagnóstico-médico">2. Diagnóstico Médico</h3>

<p>Detección temprana de enfermedades:</p>

<ul>
  <li>Detección de tumores en radiografías</li>
  <li>Análisis de retina (diabetes)</li>
  <li>Clasificación de lesiones cutáneas</li>
  <li>Segmentación de órganos en MRI</li>
</ul>

<h3 id="3-vehículos-autónomos">3. Vehículos Autónomos</h3>

<p>Percepción del entorno:</p>

<ul>
  <li>Detección de objetos (peatones, vehículos)</li>
  <li>Reconocimiento de señales de tráfico</li>
  <li>Segmentación semántica de escenas</li>
  <li>Estimación de profundidad</li>
</ul>

<h3 id="4-realidad-aumentada">4. Realidad Aumentada</h3>

<p>Overlays digitales en el mundo real:</p>

<ul>
  <li>Filtros de redes sociales</li>
  <li>Medición de objetos</li>
  <li>Navegación interior</li>
  <li>Gaming (Pokémon GO)</li>
</ul>

<h3 id="5-agricultura-de-precisión">5. Agricultura de Precisión</h3>

<p>Optimización de cultivos:</p>

<ul>
  <li>Detección de plagas</li>
  <li>Monitoreo de salud de plantas</li>
  <li>Estimación de rendimiento</li>
  <li>Gestión de recursos</li>
</ul>

<h2 id="técnicas-de-entrenamiento">Técnicas de Entrenamiento</h2>

<h3 id="data-augmentation">Data Augmentation</h3>

<p>Aumentar variedad de datos de entrenamiento:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">torchvision</span> <span class="kn">import</span> <span class="n">transforms</span>

<span class="n">augmentation</span> <span class="o">=</span> <span class="n">transforms</span><span class="p">.</span><span class="n">Compose</span><span class="p">([</span>
    <span class="n">transforms</span><span class="p">.</span><span class="n">RandomHorizontalFlip</span><span class="p">(),</span>
    <span class="n">transforms</span><span class="p">.</span><span class="n">RandomRotation</span><span class="p">(</span><span class="mi">10</span><span class="p">),</span>
    <span class="n">transforms</span><span class="p">.</span><span class="n">ColorJitter</span><span class="p">(</span><span class="n">brightness</span><span class="o">=</span><span class="mf">0.2</span><span class="p">,</span> <span class="n">contrast</span><span class="o">=</span><span class="mf">0.2</span><span class="p">),</span>
    <span class="n">transforms</span><span class="p">.</span><span class="n">RandomResizedCrop</span><span class="p">(</span><span class="mi">224</span><span class="p">),</span>
    <span class="n">transforms</span><span class="p">.</span><span class="n">Normalize</span><span class="p">(</span><span class="n">mean</span><span class="o">=</span><span class="p">[</span><span class="mf">0.485</span><span class="p">,</span> <span class="mf">0.456</span><span class="p">,</span> <span class="mf">0.406</span><span class="p">],</span>
                        <span class="n">std</span><span class="o">=</span><span class="p">[</span><span class="mf">0.229</span><span class="p">,</span> <span class="mf">0.224</span><span class="p">,</span> <span class="mf">0.225</span><span class="p">])</span>
<span class="p">])</span>
</code></pre></div></div>

<h3 id="transfer-learning">Transfer Learning</h3>

<p>Aprovechar modelos pre-entrenados:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">torchvision.models</span> <span class="k">as</span> <span class="n">models</span>

<span class="c1"># Cargar ResNet pre-entrenado
</span><span class="n">resnet</span> <span class="o">=</span> <span class="n">models</span><span class="p">.</span><span class="n">resnet50</span><span class="p">(</span><span class="n">pretrained</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>

<span class="c1"># Congelar capas base
</span><span class="k">for</span> <span class="n">param</span> <span class="ow">in</span> <span class="n">resnet</span><span class="p">.</span><span class="n">parameters</span><span class="p">():</span>
    <span class="n">param</span><span class="p">.</span><span class="n">requires_grad</span> <span class="o">=</span> <span class="bp">False</span>

<span class="c1"># Reemplazar última capa
</span><span class="n">num_classes</span> <span class="o">=</span> <span class="mi">10</span>
<span class="n">resnet</span><span class="p">.</span><span class="n">fc</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="n">resnet</span><span class="p">.</span><span class="n">fc</span><span class="p">.</span><span class="n">in_features</span><span class="p">,</span> <span class="n">num_classes</span><span class="p">)</span>

<span class="c1"># Solo entrenar la última capa
</span><span class="n">optimizer</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">optim</span><span class="p">.</span><span class="n">Adam</span><span class="p">(</span><span class="n">resnet</span><span class="p">.</span><span class="n">fc</span><span class="p">.</span><span class="n">parameters</span><span class="p">(),</span> <span class="n">lr</span><span class="o">=</span><span class="mf">0.001</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="tendencias-futuras">Tendencias Futuras</h2>

<h3 id="vision-transformers-vit">Vision Transformers (ViT)</h3>

<p>Adaptación de Transformers para visión:</p>

<ul>
  <li>Divide imágenes en patches</li>
  <li>Procesa como secuencias</li>
  <li>Resultados competitivos con CNN</li>
</ul>

<h3 id="few-shot-learning">Few-Shot Learning</h3>

<p>Aprender con pocos ejemplos:</p>

<ul>
  <li>Meta-learning</li>
  <li>Prototypical networks</li>
  <li>Matching networks</li>
</ul>

<h3 id="self-supervised-learning">Self-Supervised Learning</h3>

<p>Aprender sin etiquetas:</p>

<ul>
  <li>Contrastive learning (SimCLR, MoCo)</li>
  <li>Masked image modeling (MAE)</li>
  <li>Rotation prediction</li>
</ul>

<h3 id="eficiencia-computacional">Eficiencia Computacional</h3>

<p>Modelos más pequeños y rápidos:</p>

<ul>
  <li>Quantization</li>
  <li>Pruning</li>
  <li>Knowledge distillation</li>
  <li>Neural Architecture Search (NAS)</li>
</ul>

<h2 id="desafíos-actuales">Desafíos Actuales</h2>

<ol>
  <li><strong>Datos</strong>: Necesidad de grandes datasets etiquetados</li>
  <li><strong>Interpretabilidad</strong>: Entender qué aprende el modelo</li>
  <li><strong>Robustez</strong>: Sensibilidad a adversarial examples</li>
  <li><strong>Sesgo</strong>: Representación desigual en datos</li>
  <li><strong>Privacidad</strong>: Preocupaciones con datos sensibles</li>
</ol>

<h2 id="conclusión">Conclusión</h2>

<p>Las CNN han revolucionado la visión por computadora y continúan evolucionando. Con nuevas arquitecturas, técnicas de entrenamiento y aplicaciones emergiendo constantemente, el campo está más vibrante que nunca.</p>

<p>A medida que las técnicas se vuelven más sofisticadas y accesibles, veremos aún más aplicaciones innovadoras que transformarán industrias y mejorarán nuestras vidas.</p>

<p>Las CNN seguirán siendo fundamentales en la evolución de la IA visual, trabajando en conjunto con nuevas técnicas como Vision Transformers para empujar los límites de lo posible.</p>]]></content><author><name>AI Tech Blog</name></author><category term="Computer Vision" /><category term="Deep Learning" /><category term="CNN" /><category term="Computer Vision" /><category term="PyTorch" /><summary type="html"><![CDATA[Aprende cómo las CNN están revolucionando el campo de la visión por computadora y sus aplicaciones prácticas.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://images.unsplash.com/photo-1535378917042-10a22c95931a?w=800" /><media:content medium="image" url="https://images.unsplash.com/photo-1535378917042-10a22c95931a?w=800" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>