<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Yiğit Erdoğan</title>
    <description>The latest articles on DEV Community by Yiğit Erdoğan (@yigtwx).</description>
    <link>https://dev.to/yigtwx</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3700018%2F3cce9226-c830-4409-a7cd-7033159ff287.jpg</url>
      <title>DEV Community: Yiğit Erdoğan</title>
      <link>https://dev.to/yigtwx</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yigtwx"/>
    <language>en</language>
    <item>
      <title>Detecting Objects in Satellite Imagery with YOLOv8: xView + DOTA to YOLO in Practice</title>
      <dc:creator>Yiğit Erdoğan</dc:creator>
      <pubDate>Tue, 28 Jul 2026 10:50:03 +0000</pubDate>
      <link>https://dev.to/yigtwx/detecting-objects-in-satellite-imagery-with-yolov8-xview-dota-to-yolo-in-practice-4dge</link>
      <guid>https://dev.to/yigtwx/detecting-objects-in-satellite-imagery-with-yolov8-xview-dota-to-yolo-in-practice-4dge</guid>
      <description>&lt;p&gt;Satellite imagery breaks most of the assumptions object detectors are built on. Objects are tiny (a car is 10–15 px in a 3000×3000 tile), classes are wildly imbalanced, and the annotations don't come in anything close to YOLO format.&lt;/p&gt;

&lt;p&gt;I spent a while building &lt;strong&gt;&lt;a href="https://github.com/Yigtwxx/dl_xview_yolo" rel="noopener noreferrer"&gt;dl_xview_yolo&lt;/a&gt;&lt;/strong&gt; — a YOLOv8 pipeline for detecting planes, ships, vehicles, bridges and storage tanks in aerial and satellite images, trained on &lt;strong&gt;xView&lt;/strong&gt; and &lt;strong&gt;DOTA&lt;/strong&gt;. This post is about the parts that actually took time: the data conversion, the training config, and the mistakes worth avoiding.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real work is the data conversion
&lt;/h2&gt;

&lt;p&gt;Nobody tells you that ~80% of a detection project is reshaping labels. The two datasets couldn't be more different:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;xView&lt;/strong&gt; ships a single giant &lt;code&gt;.geojson&lt;/code&gt; file. Every object is a feature whose &lt;code&gt;properties&lt;/code&gt; may hold &lt;code&gt;bounds_imcoords&lt;/code&gt;, or &lt;code&gt;bbox&lt;/code&gt;, or &lt;code&gt;xmin/ymin/xmax/ymax&lt;/code&gt;, with inconsistent casing, and the images are 16-bit GeoTIFFs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DOTA&lt;/strong&gt; uses one &lt;code&gt;.txt&lt;/code&gt; per image, with &lt;strong&gt;oriented&lt;/strong&gt; boxes: 8 numbers (four corner points) plus a class name.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So &lt;code&gt;convert_all_to_yolo.py&lt;/code&gt; ended up being a tolerant parser rather than a clean one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;extract_bbox_from_props&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# 1) bounds_imcoords: "x1,y1,x2,y2"
&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;lk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bounds&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;lk&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;imcoords&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;lk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;bb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parse_bounds_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;props&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;bb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;bb&lt;/span&gt;
    &lt;span class="c1"&gt;# 2) bbox as list or string
&lt;/span&gt;    &lt;span class="c1"&gt;# 3) xmin/ymin/xmax/ymax, x_min/..., left/top/right/bottom
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things I'd do the same way again:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Normalize the imagery before anything else.&lt;/strong&gt; GeoTIFFs are frequently 16-bit or single-channel, and &lt;code&gt;cv2.imread&lt;/code&gt; will happily hand you an array OpenCV's JPEG writer chokes on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ensure_bgr_uint8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;im&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;im&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dtype&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uint8&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;im&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NORM_MINMAX&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uint8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;im&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ndim&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;im&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cvtColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;im&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;COLOR_GRAY2BGR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;im&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Convert oriented boxes to axis-aligned early.&lt;/strong&gt; DOTA's polygons become horizontal boxes via min/max on the corner coordinates — a real information loss for rotated ships and bridges, but it lets you train plain &lt;code&gt;yolov8m&lt;/code&gt; instead of the OBB head. I started on &lt;code&gt;yolov8m-obb.pt&lt;/code&gt; and dropped back to detection; the pipeline got dramatically simpler and the mAP was good enough for the use case.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;xs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;span class="n"&gt;ys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;span class="n"&gt;xmin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ymin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;xmax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ymax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ys&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ys&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything then lands in the standard layout — &lt;code&gt;images/{train,val}&lt;/code&gt; + &lt;code&gt;labels/{train,val}&lt;/code&gt; + &lt;code&gt;data.yaml&lt;/code&gt; — and clipped boxes outside &lt;code&gt;[0, 1]&lt;/code&gt; are dropped rather than clamped-and-kept, which quietly removes a class of degenerate labels.&lt;/p&gt;

&lt;h2&gt;
  
  
  Training config that survived contact with a single GPU
&lt;/h2&gt;

&lt;p&gt;The full detection run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;train&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DATA_YAML&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;epochs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;imgsz&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;# small objects need resolution, not more epochs
&lt;/span&gt;    &lt;span class="n"&gt;batch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="c1"&gt;# 1024px is expensive; batch stays tiny
&lt;/span&gt;    &lt;span class="n"&gt;optimizer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AdamW&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;lr0&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.0001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;cos_lr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;dropout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;hsv_h&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.015&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hsv_s&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hsv_v&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;translate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.55&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;mixup&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;copy_paste&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;shear&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;perspective&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# nadir imagery: no perspective to simulate
&lt;/span&gt;    &lt;span class="n"&gt;close_mosaic&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;patience&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reasoning behind the non-obvious ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;imgsz=1024&lt;/code&gt;, not 640.&lt;/strong&gt; This is the single highest-impact knob for aerial data. Downscaling to 640 makes small vehicles disappear into a couple of pixels. The cost is that &lt;code&gt;batch&lt;/code&gt; collapses to 4.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;shear=0, perspective=0&lt;/code&gt;.&lt;/strong&gt; Satellite views are roughly nadir. Simulating perspective distortion generates images the model will never see at inference.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;copy_paste&lt;/code&gt; and &lt;code&gt;mixup&lt;/code&gt; on.&lt;/strong&gt; Class imbalance in xView is brutal; pasting rare instances into other tiles is cheaper than resampling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;close_mosaic=30&lt;/code&gt;.&lt;/strong&gt; Mosaic augmentation is great early and harmful at the end — the last 30 epochs train on real, unstitched tiles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;cos_lr=True&lt;/code&gt; with a low &lt;code&gt;lr0&lt;/code&gt;.&lt;/strong&gt; Fine-tuning from COCO weights onto a domain this different punishes aggressive learning rates.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Results on the validation split:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="mailto:mAP@0.5"&gt;mAP@0.5&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;0.54&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="mailto:mAP@0.5-0.95"&gt;mAP@0.5-0.95&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;0.36&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Precision&lt;/td&gt;
&lt;td&gt;0.67&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recall&lt;/td&gt;
&lt;td&gt;0.71&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Not state of the art, but a usable baseline — and the gap between &lt;a href="mailto:mAP@0.5"&gt;mAP@0.5&lt;/a&gt; and &lt;a href="mailto:mAP@0.5"&gt;mAP@0.5&lt;/a&gt;:0.95 is exactly what you'd expect when boxes are small: localization is approximate even when detection is correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  Serving it: one script, CLI and UI
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;predict_yolo.py&lt;/code&gt; does double duty. With &lt;code&gt;--no-ui&lt;/code&gt; it's a plain batch inference script; without it, it boots a FastAPI app serving a drag-and-drop page that posts an image and gets back an annotated PNG:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/predict&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;predict_endpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;UploadFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;File&lt;/span&gt;&lt;span class="p"&gt;(...)):&lt;/span&gt;
    &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BytesIO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;())).&lt;/span&gt;&lt;span class="nf"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RGB&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;imgsz&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;imgsz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;StreamingResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BytesIO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;ndarray_to_png_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;())),&lt;/span&gt;
                             &lt;span class="n"&gt;media_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;image/png&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Small detail that saves real time: the weights are discovered automatically instead of hardcoded.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;bests&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RUNS&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;train&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;**&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;weights&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;best.pt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;recursive&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After twenty training runs you stop remembering which &lt;code&gt;exp&lt;/code&gt; folder holds the good checkpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three things I'd tell my past self
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Make your train/val split deterministic — and verify it.&lt;/strong&gt; I split by hashing the filename:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_val_split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Python's &lt;code&gt;hash()&lt;/code&gt; for strings is &lt;strong&gt;salted per process&lt;/strong&gt; unless &lt;code&gt;PYTHONHASHSEED&lt;/code&gt; is fixed. Re-run the conversion and images silently move between train and val — which means a model evaluated after a re-conversion may have trained on its own validation set. Use a stable hash instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_val_split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;digest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;md5&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Unify your class taxonomy before merging datasets.&lt;/strong&gt; xView maps &lt;code&gt;type_id - 1&lt;/code&gt; into a 60-class space; DOTA maps 15 class names into indices 0–14. Write both into the same &lt;code&gt;labels/&lt;/code&gt; directory and DOTA's &lt;code&gt;plane&lt;/code&gt; collides with whatever xView class index 0 happens to be. Decide on one taxonomy — or keep the datasets in separate runs — before you spend GPU hours.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Don't hardcode absolute paths.&lt;/strong&gt; Mine started as &lt;code&gt;C:\Users\Asus\Desktop\dl_xview&lt;/code&gt; and it's the first thing anyone cloning the repo has to fix. Read roots from a config file or an environment variable from day one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Yigtwxx/dl_xview_yolo.git
&lt;span class="nb"&gt;cd &lt;/span&gt;dl_xview_yolo
pip &lt;span class="nb"&gt;install &lt;/span&gt;ultralytics opencv-python pillow tqdm numpy torch torchvision fastapi uvicorn

python scripts/convert_all_to_yolo.py     &lt;span class="c"&gt;# xView/DOTA → YOLO&lt;/span&gt;
python scripts/tain_yolo.py &lt;span class="nt"&gt;--epochs&lt;/span&gt; 100  &lt;span class="c"&gt;# train&lt;/span&gt;
python scripts/val_yolo.py                &lt;span class="c"&gt;# evaluate&lt;/span&gt;
python scripts/predict_yolo.py            &lt;span class="c"&gt;# FastAPI UI at 127.0.0.1:7860&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repo: &lt;strong&gt;&lt;a href="https://github.com/Yigtwxx/dl_xview_yolo" rel="noopener noreferrer"&gt;github.com/Yigtwxx/dl_xview_yolo&lt;/a&gt;&lt;/strong&gt; — MIT licensed, issues and PRs welcome. If you've trained detectors on aerial imagery, I'd genuinely like to hear how you handled the tiny-object problem: tiling, higher resolution, or a different architecture entirely.&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>computervision</category>
      <category>python</category>
      <category>deeplearning</category>
    </item>
    <item>
      <title>Building an Offline Document Search Engine for My University</title>
      <dc:creator>Yiğit Erdoğan</dc:creator>
      <pubDate>Sat, 04 Jul 2026 17:36:24 +0000</pubDate>
      <link>https://dev.to/yigtwx/building-an-offline-document-search-engine-for-my-university-56bi</link>
      <guid>https://dev.to/yigtwx/building-an-offline-document-search-engine-for-my-university-56bi</guid>
      <description>&lt;p&gt;Hi everyone,&lt;/p&gt;

&lt;p&gt;I'm Yiğit, a third-year software engineering student. If you have ever tried to find a specific rule about grading, attendance, or academic calendars in university regulation PDFs, you know how frustrating it can be. Information is scattered across dozens of poorly formatted documents, making it almost impossible to find quick answers.&lt;/p&gt;

&lt;p&gt;To solve this, I built &lt;strong&gt;FiratUniversityChatbot&lt;/strong&gt;—an open-source, completely offline Turkish question-answering and document search assistant tailored for Fırat University.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Core Idea
&lt;/h3&gt;

&lt;p&gt;Instead of relying on heavy cloud-based LLMs that might hallucinate academic rules, I wanted a fast, deterministic, and fully local system. The goal was simple: users ask a question, and the app instantly scans local PDFs to return the exact snippet and the source page number. If the answer isn't in the documents, it politely refuses to guess, ensuring zero hallucination.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Tech Stack
&lt;/h3&gt;

&lt;p&gt;To keep the application lightweight, secure, and robust without needing an internet connection, I went with a pure Information Retrieval approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Backend &amp;amp; API:&lt;/strong&gt; Python and FastAPI for high performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Document Processing:&lt;/strong&gt; &lt;code&gt;pdfplumber&lt;/code&gt; to handle the nightmare of university PDFs. The app dynamically detects single or dual columns, filters out headers/footers, and accurately assembles text blocks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search Engine:&lt;/strong&gt; A custom-built BM25 index tailored specifically for the Turkish language. I implemented ASCII normalization, tokenization, synonym expansion (e.g., treating "büt" and "bütünleme" as the exact same intent), and bigram matching.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; A minimal, dependency-free chat interface using HTML, CSS, and Jinja2 templates.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Biggest Challenge
&lt;/h3&gt;

&lt;p&gt;The hardest part was definitely the data extraction and text pipeline. University PDFs are notoriously messy. Building a fallback strategy that can accurately crop dual columns without mixing up paragraphs was a headache. Additionally, fine-tuning the BM25 ranking algorithm with domain-aware tweaks—like intent flags for "pass grade" or "appeals"—took a lot of trial and error to get right.&lt;/p&gt;

&lt;h3&gt;
  
  
  Open for Feedback
&lt;/h3&gt;

&lt;p&gt;The project is completely open-source and ready to be tested. You can run it locally in a virtual environment or deploy it easily via Docker (it's currently live on Hugging Face Spaces too).&lt;/p&gt;

&lt;p&gt;If you are interested in search engines, document parsing, or building fast Python applications, I would love for you to check it out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/Yigtwxx/FiratUniversityChatbot" rel="noopener noreferrer"&gt;FiratUniversityChatbot on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’m highly open to code reviews, architectural feedback, or Pull Requests. Have you ever built a local search tool for your school or company? Let's discuss in the comments!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>python</category>
      <category>devops</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Harnessing Mathematical Chaos: Building a Python PRNG Using the Collatz Conjecture</title>
      <dc:creator>Yiğit Erdoğan</dc:creator>
      <pubDate>Sat, 20 Jun 2026 07:29:01 +0000</pubDate>
      <link>https://dev.to/yigtwx/harnessing-mathematical-chaos-building-a-python-prng-using-the-collatz-conjecture-2eb9</link>
      <guid>https://dev.to/yigtwx/harnessing-mathematical-chaos-building-a-python-prng-using-the-collatz-conjecture-2eb9</guid>
      <description>&lt;p&gt;Hello DEV Community,&lt;/p&gt;

&lt;p&gt;As developers, we frequently rely on standard libraries to handle fundamental computational tasks. When we need a random number, we simply call &lt;code&gt;import random&lt;/code&gt; or use the &lt;code&gt;secrets&lt;/code&gt; module without delving into the underlying mechanics of entropy, seed generation, or the Mersenne Twister algorithm. &lt;/p&gt;

&lt;p&gt;As a third-year Software Engineering student, I wanted to break through that abstraction layer. To truly understand how pseudo-randomness is computationally achieved, I decided to build a Pseudo-Random Number Generator (PRNG) entirely from scratch. &lt;/p&gt;

&lt;p&gt;To make the experiment more challenging, I chose to base the entropy engine on one of the most famous unsolved problems in mathematics: the Collatz Conjecture.&lt;/p&gt;

&lt;p&gt;I would like to introduce my recent project: &lt;strong&gt;&lt;a href="https://github.com/Yigtwxx/bsg-random-number-generator" rel="noopener noreferrer"&gt;BSG Random Number Generator&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Theory: Why the Collatz Conjecture?
&lt;/h3&gt;

&lt;p&gt;For those who might not be familiar, the Collatz Conjecture (also known as the 3n+1 problem) is a mathematical sequence defined by two simple rules applied to any positive integer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the number is even, divide it by 2.&lt;/li&gt;
&lt;li&gt;If the number is odd, multiply it by 3 and add 1.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The conjecture states that no matter what starting value you choose, the sequence will always eventually reach the 4-2-1 loop. However, the path it takes to get there—the "stopping time" and the peak values reached during the sequence—exhibits behavior that is incredibly unpredictable and highly sensitive to the initial state. &lt;/p&gt;

&lt;p&gt;This unpredictable orbital path is a perfect example of deterministic chaos. I wanted to investigate whether this chaos could be harvested and mathematically normalized to generate usable, uniformly distributed random numbers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architectural Overview and Implementation
&lt;/h3&gt;

&lt;p&gt;The BSG Random Number Generator is written in pure Python with zero external dependencies. The core logic revolves around extracting specific metrics from the Collatz sequence to build an internal state mechanism. &lt;/p&gt;

&lt;p&gt;Instead of relying on system time or OS-level entropy pools, the generator uses:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Orbital Lengths:&lt;/strong&gt; The exact number of steps required for a specific seed to reach the 4-2-1 loop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Peak Tracking:&lt;/strong&gt; The maximum integer value achieved during the sequence path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parity Sequences:&lt;/strong&gt; The alternating pattern of odd and even evaluations before termination.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By dynamically updating the seed based on these extracted metrics and applying normalization techniques, the generator successfully outputs floating-point numbers between 0.0 and 1.0, as well as scalable integers. The resulting distribution is surprisingly uniform and demonstrates how mathematical anomalies can be structured into functional code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security Limitations and Disclaimer
&lt;/h3&gt;

&lt;p&gt;As a software engineering practice, it is crucial to clearly define the boundaries of experimental projects. &lt;strong&gt;This generator is strictly an educational and experimental tool.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While it produces uniform distributions suitable for procedural generation, basic simulations, or non-critical randomized logic, it has not been subjected to rigorous statistical test suites like Diehard or NIST. Furthermore, it is deterministic by nature and is &lt;strong&gt;not Cryptographically Secure (CSPRNG)&lt;/strong&gt;. It should never be used for generating cryptographic keys, tokens, or handling sensitive security operations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Seeking Community Feedback
&lt;/h3&gt;

&lt;p&gt;Building the BSG Random Number Generator was a comprehensive exercise in state management, algorithm optimization, and understanding computational entropy in Python. &lt;/p&gt;

&lt;p&gt;I am sharing this project here because I highly value the technical insights of the DEV community. I would appreciate any feedback on the repository, specifically regarding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The structural design and Pythonic efficiency of the code.&lt;/li&gt;
&lt;li&gt;Potential mathematical strategies to extract even higher levels of entropy from the sequence orbits.&lt;/li&gt;
&lt;li&gt;Suggestions for optimizing the state transition logic to prevent performance bottlenecks over millions of iterations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Repository Link:&lt;/strong&gt; &lt;a href="https://github.com/Yigtwxx/bsg-random-number-generator" rel="noopener noreferrer"&gt;Yigtwxx/bsg-random-number-generator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Have you ever tried rebuilding fundamental standard library tools from scratch to better understand their underlying architecture? I would love to hear your thoughts and experiences in the comments below.&lt;/p&gt;

</description>
      <category>python</category>
      <category>programming</category>
      <category>opensource</category>
      <category>showdev</category>
    </item>
    <item>
      <title>50+ Essential Tools for Building Production RAG Systems</title>
      <dc:creator>Yiğit Erdoğan</dc:creator>
      <pubDate>Thu, 08 Jan 2026 09:14:04 +0000</pubDate>
      <link>https://dev.to/yigtwx/50-essential-tools-for-building-production-rag-systems-2l8</link>
      <guid>https://dev.to/yigtwx/50-essential-tools-for-building-production-rag-systems-2l8</guid>
      <description>&lt;p&gt;After researching and documenting the production RAG ecosystem, I've compiled a comprehensive list of &lt;strong&gt;50+ battle-tested tools&lt;/strong&gt; that actually matter when you're scaling Retrieval-Augmented Generation systems from prototype to production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This List?
&lt;/h2&gt;

&lt;p&gt;The gap between "Hello World" RAG tutorials and production-ready systems is massive. This curated collection focuses on the &lt;strong&gt;engineering&lt;/strong&gt; side—real tools for real problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Navigation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Frameworks &amp;amp; Orchestration&lt;/li&gt;
&lt;li&gt;Vector Databases
&lt;/li&gt;
&lt;li&gt;Retrieval &amp;amp; Reranking&lt;/li&gt;
&lt;li&gt;Evaluation &amp;amp; Benchmarking&lt;/li&gt;
&lt;li&gt;Observability &amp;amp; Tracing&lt;/li&gt;
&lt;li&gt;Deployment &amp;amp; Serving&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Frameworks: Choose Your Stack
&lt;/h2&gt;

&lt;h3&gt;
  
  
  LlamaIndex
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Data processing and advanced indexing strategies&lt;/p&gt;

&lt;p&gt;Perfect when you need hierarchical retrieval, knowledge graphs, or complex query engines. The data-first approach makes ingestion pipelines cleaner.&lt;/p&gt;

&lt;h3&gt;
  
  
  LangChain
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Rapid prototyping and maximum ecosystem compatibility&lt;/p&gt;

&lt;p&gt;The largest community means tons of integrations, but watch out for abstraction overhead in production.&lt;/p&gt;

&lt;h3&gt;
  
  
  LangGraph
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Agentic systems with complex workflows  &lt;/p&gt;

&lt;p&gt;When you need cyclic graphs, human-in-the-loop, or stateful multi-step reasoning. The graph-based approach is perfect for advanced agents.&lt;/p&gt;

&lt;h3&gt;
  
  
  Haystack
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Enterprise pipelines requiring auditability&lt;/p&gt;

&lt;p&gt;Type-safe, DAG-based architecture. If you need strict reproducibility and compliance, this is your choice.&lt;/p&gt;




&lt;h2&gt;
  
  
  Vector Databases: Scale Matters
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Database&lt;/th&gt;
&lt;th&gt;Sweet Spot&lt;/th&gt;
&lt;th&gt;Key Advantage&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Chroma&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Local dev &amp;amp; mid-scale&lt;/td&gt;
&lt;td&gt;Zero-config embedded mode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pinecone&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;10M-100M vectors&lt;/td&gt;
&lt;td&gt;Serverless, zero ops&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Qdrant&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;lt;50M vectors&lt;/td&gt;
&lt;td&gt;Best free tier + filtering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Milvus&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Billions of vectors&lt;/td&gt;
&lt;td&gt;Open source at massive scale&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;pgvector&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;PostgreSQL users&lt;/td&gt;
&lt;td&gt;Leverage existing Postgres infra&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Weaviate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hybrid search&lt;/td&gt;
&lt;td&gt;Native vector + keyword&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Pro Tip:&lt;/strong&gt; Start with Chroma locally, graduate to Qdrant for production, scale to Milvus only if you truly need billions of vectors.&lt;/p&gt;




&lt;h2&gt;
  
  
  Retrieval: Beyond Basic Search
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Hybrid Search Pattern
&lt;/h3&gt;

&lt;p&gt;Dense vector search alone misses exact term matches. Sparse keyword search (BM25) alone misses semantics. &lt;strong&gt;Combine them.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tools:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ColBERT&lt;/strong&gt; (via RAGatouille): Token-level matching for superior recall&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cohere Rerank&lt;/strong&gt;: API-based reranker, 10-20% precision boost&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BGE-Reranker&lt;/strong&gt;: Best open-source cross-encoder&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FlashRank&lt;/strong&gt;: Lightweight CPU-only reranking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Real-world pattern:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Retrieve top-100 with fast semantic search&lt;/li&gt;
&lt;li&gt;Rerank to top-5 with cross-encoder&lt;/li&gt;
&lt;li&gt;Feed to LLM&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This 2-stage approach is standard at companies like Notion and Discord.&lt;/p&gt;




&lt;h2&gt;
  
  
  Evaluation: Measure What Matters
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The RAG Triad
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Context Relevance&lt;/strong&gt; - Did we retrieve the right documents?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Groundedness&lt;/strong&gt; - Is the answer faithful to the context?
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Answer Relevance&lt;/strong&gt; - Does it address the question?&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Tools
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Ragas:&lt;/strong&gt; LLM-as-a-Judge evaluation without ground truth&lt;br&gt;&lt;br&gt;
&lt;strong&gt;DeepEval:&lt;/strong&gt; The "Pytest for LLMs", integrates into CI/CD&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Braintrust:&lt;/strong&gt; Online eval for real user interactions&lt;br&gt;&lt;br&gt;
&lt;strong&gt;ARES:&lt;/strong&gt; Stanford's automated eval with statistical confidence&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Critical:&lt;/strong&gt; Always validate your LLM judge against human labels on 100-200 samples. GPT-4 has ~85% agreement with humans, not 100%.&lt;/p&gt;




&lt;h2&gt;
  
  
  Observability: You Can't Fix What You Can't See
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Must-Have Metrics
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Latency percentiles&lt;/strong&gt; (p50, p95, p99)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Token usage per request&lt;/strong&gt; (cost tracking)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retrieval quality&lt;/strong&gt; (distance scores, reranker confidence)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Embedding drift&lt;/strong&gt; (production vs training distribution)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tools
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;LangSmith:&lt;/strong&gt; Gold standard for LangChain, instant trace replay&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Langfuse:&lt;/strong&gt; Open-source, prompt versioning decoupled from code&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Arize Phoenix:&lt;/strong&gt; Visualize embedding clusters, debug retrieval&lt;br&gt;&lt;br&gt;
&lt;strong&gt;OpenLIT:&lt;/strong&gt; OpenTelemetry-native for existing Prometheus/Grafana stacks&lt;/p&gt;




&lt;h2&gt;
  
  
  Deployment: From Laptop to Production
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Three Reference Architectures
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Local Stack (Zero Cost)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LLM:&lt;/strong&gt; Ollama (Llama 3, Mistral)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vector DB:&lt;/strong&gt; Chroma (embedded)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Eval:&lt;/strong&gt; Ragas&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When:&lt;/strong&gt; Prototype validation, no API keys needed&lt;/p&gt;

&lt;h4&gt;
  
  
  Mid-Scale Stack (Speed to Market)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vector DB:&lt;/strong&gt; Qdrant Cloud&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reranker:&lt;/strong&gt; Cohere Rerank API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tracing:&lt;/strong&gt; Langfuse&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LLM:&lt;/strong&gt; OpenAI GPT-4&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When:&lt;/strong&gt; 90% of production use cases&lt;/p&gt;

&lt;h4&gt;
  
  
  Enterprise Stack (The 1%)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vector DB:&lt;/strong&gt; Milvus (distributed)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serving:&lt;/strong&gt; vLLM (self-hosted)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring:&lt;/strong&gt; OpenLIT + custom SLAs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Eval:&lt;/strong&gt; DeepEval in CI/CD&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When:&lt;/strong&gt; Billions of vectors, data sovereignty, dedicated platform team&lt;/p&gt;




&lt;h2&gt;
  
  
  Security: Don't Skip This
&lt;/h2&gt;

&lt;p&gt;Production RAG handles user data. Common threats:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prompt Injection:&lt;/strong&gt; User manipulates retrieval context
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PII Leakage:&lt;/strong&gt; Sensitive data in embeddings or responses
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jailbreaking:&lt;/strong&gt; Bypassing system guardrails&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Essential Tools:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Presidio:&lt;/strong&gt; PII detection before embedding
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NeMo Guardrails:&lt;/strong&gt; Programmable topic constraints
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LLM Guard:&lt;/strong&gt; Input/output sanitization
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PrivateGPT:&lt;/strong&gt; 100% offline RAG for regulated industries&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Real-World Case Studies
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Notion AI
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stack:&lt;/strong&gt; Pinecone + GPT-4 + custom embeddings&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key Insight:&lt;/strong&gt; Hybrid search improved recall by 23%&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Discord (19B messages)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stack:&lt;/strong&gt; ScaNN + custom Rust infra&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key Insight:&lt;/strong&gt; 99.9% recall at 10ms latency with ANN&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Shopify
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Key Insight:&lt;/strong&gt; Domain-specific fine-tuning reduced hallucinations from 18% → 4%&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pattern:&lt;/strong&gt; Everyone uses hybrid search + reranking at scale.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Full Resource
&lt;/h2&gt;

&lt;p&gt;This article covers the highlights. For the complete list of 50+ tools, reference architectures, evaluation frameworks, and anti-patterns to avoid:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/Yigtwxx/Awesome-RAG-Production" rel="noopener noreferrer"&gt;Awesome RAG Production on GitHub&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Comparison tables for every category
&lt;/li&gt;
&lt;li&gt;✅ Decision trees for selecting tools&lt;/li&gt;
&lt;li&gt;✅ RAG pitfalls and how to avoid them
&lt;/li&gt;
&lt;li&gt;✅ Datasets for benchmarking&lt;/li&gt;
&lt;li&gt;✅ Curated books and blogs&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Contributing
&lt;/h2&gt;

&lt;p&gt;Found a tool that should be on the list? Spotted an outdated link? PRs welcome!&lt;/p&gt;

&lt;p&gt;Star the repo to stay updated with new tools and best practices as the RAG ecosystem evolves.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What's your production RAG stack? Drop a comment below!&lt;/strong&gt; &lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>python</category>
      <category>llm</category>
    </item>
  </channel>
</rss>
