<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Daniel&apos;s Blogs</title>
    <description>My personal blog, Creating for upload personal  research, coding related resource.</description>
    <link>https://danghoangnhan.github.io/</link>
    <atom:link href="https://danghoangnhan.github.io/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Sat, 01 Aug 2026 14:19:02 +0000</pubDate>
    <lastBuildDate>Sat, 01 Aug 2026 14:19:02 +0000</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>One Scheduler, Many Workers: Running Luigi Across Multiple Servers with Zero Downtime</title>
        <description>&lt;p&gt;Every data team I know has the same origin story. One cron job, on one VM, running one Luigi pipeline. It works beautifully — right up until the night the VM reboots mid-run, the morning report doesn’t arrive, and you spend breakfast SSH-ed into a box re-running tasks by hand.&lt;/p&gt;

&lt;p&gt;This post is the story of what comes next: growing that single box into a small fleet — multiple worker servers, a highly-available &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;luigid&lt;/code&gt; scheduler, a network layout you can explain to your security team, a CI/CD pipeline on GitLab that deploys all of it with zero downtime — and, most importantly, &lt;em&gt;why&lt;/em&gt; each piece is shaped the way it is.&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;the-cast-of-characters&quot;&gt;The cast of characters&lt;/h2&gt;

&lt;p&gt;Before any diagrams, meet the actors. It makes everything else obvious.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The scheduler (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;luigid&lt;/code&gt;) is a site foreman with a clipboard.&lt;/strong&gt; He decides who builds what, in which order, and he never, ever lifts a brick himself. This is the single most important fact about Luigi: the central scheduler executes nothing. If the foreman goes home early, the crew already working keeps working — nobody drops a wall halfway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workers are the crew.&lt;/strong&gt; Interchangeable, numerous, and honestly a bit expendable. Any worker can do any task, and if one goes home sick, another picks up his job tomorrow. All the real work — every &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;run()&lt;/code&gt; method — happens here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The artifact store is the warehouse.&lt;/strong&gt; NFS, HDFS, or S3 — wherever task outputs live. Here is Luigi’s quiet superpower: the question “is this task done?” is never answered from anyone’s memory. It’s answered by walking into the warehouse and checking whether the box is on the shelf (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Target.exists()&lt;/code&gt;). Memories crash and reboot; shelves don’t.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The VIP is the phone number.&lt;/strong&gt; A single floating IP (managed by keepalived) that always rings whichever foreman is on duty. Workers, browsers, and monitoring all dial the same number and never need to know which physical machine answered.&lt;/p&gt;

&lt;h2 id=&quot;the-system-architecture&quot;&gt;The system architecture&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;../assets/images/luigi-ha/architecture.svg&quot; alt=&quot;Luigi multi-server system architecture&quot; title=&quot;System architecture&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Two small VMs run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;luigid&lt;/code&gt;, but only the one holding the VIP is &lt;em&gt;active&lt;/em&gt;. The standby idles, receiving a copy of the scheduler’s state checkpoint (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;state.pickle&lt;/code&gt;, rsync-ed every minute or on a shared/DRBD disk). The task-history database (PostgreSQL) records every task event durably, and Prometheus scrapes metrics through the VIP so monitoring survives failovers without noticing them.&lt;/p&gt;

&lt;p&gt;The worker fleet is N identical VMs. Each one runs the same pipeline codebase and a systemd timer that fires the entrypoint — a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WrapperTask&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RangeDaily --of NightlyPipeline&lt;/code&gt; — with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--workers 8&lt;/code&gt;. And here’s the trick that makes scaling trivial: &lt;em&gt;every&lt;/em&gt; VM submits the &lt;em&gt;same&lt;/em&gt; task graph. The scheduler deduplicates, hands each pending task to exactly one worker, and adding throughput is literally “clone another VM.” No leader election, no partitioning scheme, no coordination code.&lt;/p&gt;

&lt;p&gt;Why not two active schedulers? Because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;luigid&lt;/code&gt; is a single process by design — no clustering, no replication. Instead of fighting that, we make the scheduler &lt;em&gt;disposable&lt;/em&gt;: the real source of truth is the warehouse plus the history database. If the scheduler’s memory were wiped entirely, the next timer tick re-submits the graph, every task whose output sits on the shelf is skipped, and life continues. You lose a little context (retry counters), never work.&lt;/p&gt;

&lt;h2 id=&quot;the-network-layer&quot;&gt;The network layer&lt;/h2&gt;

&lt;pre&gt;&lt;code class=&quot;language-mermaid&quot;&gt;flowchart TB
  subgraph OFFICE[&quot;Office / VPN — 10.10.0.0/16&quot;]
    OPS[&quot;engineers&apos; laptops&quot;]
  end
  subgraph PROD[&quot;Production subnet — 10.0.0.0/24&quot;]
    VIP([&quot;VIP 10.0.0.50 — floats between schedulers&quot;])
    S1[&quot;scheduler-01 · 10.0.0.11&quot;]
    S2[&quot;scheduler-02 · 10.0.0.12&quot;]
    W[&quot;worker-01 … worker-N · 10.0.0.21+&quot;]
    GR[&quot;gitlab-runner · 10.0.0.40&quot;]
    PM[&quot;prometheus · 10.0.0.30&quot;]
  end
  subgraph DATA[&quot;Data services&quot;]
    PG[(&quot;PostgreSQL · 10.0.0.60&amp;lt;br&amp;gt;task history&quot;)]
    ST[(&quot;NFS / S3 · 10.0.0.70&amp;lt;br&amp;gt;artifact store&quot;)]
  end
  OPS --&amp;gt;|&quot;TCP 8082 — web UI, allow from VPN only&quot;| VIP
  W --&amp;gt;|&quot;TCP 8082 — scheduler RPC&quot;| VIP
  VIP --- S1
  VIP -.- S2
  S1 &amp;lt;--&amp;gt;|&quot;VRRP — IP protocol 112, multicast 224.0.0.18&quot;| S2
  S1 --&amp;gt;|&quot;TCP 5432&quot;| PG
  W --&amp;gt;|&quot;TCP 2049 NFS or TCP 443 S3&quot;| ST
  PM --&amp;gt;|&quot;TCP 8082 — /metrics via VIP&quot;| VIP
  PM --&amp;gt;|&quot;TCP 9100 — node-exporter&quot;| W
  GR --&amp;gt;|&quot;TCP 22 — SSH rolling deploys&quot;| W
  GR -.-&amp;gt;|&quot;TCP 22&quot;| S1
  GR -.-&amp;gt;|&quot;TCP 22&quot;| S2
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The firewall story is short, and short firewall stories are good firewall stories.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workers never listen. They only dial out.&lt;/strong&gt; A worker VM needs zero inbound ports (SSH from the deploy runner aside) — it dials the scheduler, dials the storage, dials the SMTP relay. That one sentence removes half of a security review.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;From&lt;/th&gt;
      &lt;th&gt;To&lt;/th&gt;
      &lt;th&gt;Port / protocol&lt;/th&gt;
      &lt;th&gt;Why&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Workers, laptops (VPN), Prometheus&lt;/td&gt;
      &lt;td&gt;VIP &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.0.0.50&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;8082/tcp&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Scheduler RPC, web UI, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/metrics&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;scheduler-01 ⇄ scheduler-02&lt;/td&gt;
      &lt;td&gt;each other&lt;/td&gt;
      &lt;td&gt;VRRP (IP protocol 112, multicast &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;224.0.0.18&lt;/code&gt;)&lt;/td&gt;
      &lt;td&gt;keepalived heartbeats — allow it or the VIP flaps&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Active scheduler&lt;/td&gt;
      &lt;td&gt;PostgreSQL&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5432/tcp&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Task history&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Workers&lt;/td&gt;
      &lt;td&gt;NFS / S3&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2049/tcp&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;443/tcp&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Read inputs, write outputs&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Prometheus&lt;/td&gt;
      &lt;td&gt;every VM&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;9100/tcp&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;node-exporter&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;GitLab Runner&lt;/td&gt;
      &lt;td&gt;every VM&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;22/tcp&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Rolling deploys&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Expose exactly one thing to humans: the VIP on 8082, and only from the office/VPN range. Everything else is machine-to-machine inside the production subnet. The only exotic line is VRRP — it’s not TCP, it’s IP protocol 112 on a multicast address, and forgetting to allow it is the classic “why does my VIP bounce every three seconds” incident.&lt;/p&gt;

&lt;h2 id=&quot;the-life-of-a-task&quot;&gt;The life of a task&lt;/h2&gt;

&lt;pre&gt;&lt;code class=&quot;language-mermaid&quot;&gt;stateDiagram-v2
    [*] --&amp;gt; DONE: add_task — output already exists, nothing to do
    [*] --&amp;gt; PENDING: add_task — output missing
    PENDING --&amp;gt; RUNNING: get_work — a free worker claims it
    RUNNING --&amp;gt; DONE: run() succeeds — output renamed into place
    RUNNING --&amp;gt; FAILED: run() raises an exception
    RUNNING --&amp;gt; PENDING: worker vanished — worker_disconnect_delay (60 s)
    FAILED --&amp;gt; PENDING: automatic retry — retry_delay (900 s)
    FAILED --&amp;gt; DISABLED: too many failures — retry_count exceeded
    DONE --&amp;gt; [*]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Let me tell you about one task’s day. Call her &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SalesReport(date=2026-08-01)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;At 02:00, a timer fires on worker-03 and submits the nightly graph. The worker first checks the warehouse — &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SalesReport&lt;/code&gt;’s output isn’t on the shelf — so she registers with the foreman as &lt;strong&gt;PENDING&lt;/strong&gt;. Worker-07 happens to finish something and asks the foreman for work (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get_work&lt;/code&gt;); the foreman hands over &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SalesReport&lt;/code&gt;, and she is now &lt;strong&gt;RUNNING&lt;/strong&gt; — on worker-07, a machine that had nothing to do with submitting her.&lt;/p&gt;

&lt;p&gt;Her &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;run()&lt;/code&gt; writes results to a &lt;em&gt;temporary&lt;/em&gt; path, and only after the last byte lands does an atomic rename put the box on the shelf. She becomes &lt;strong&gt;DONE&lt;/strong&gt; — permanently, provably: any future submission of the graph will see her output and skip her.&lt;/p&gt;

&lt;p&gt;The interesting days are the bad ones. If &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;run()&lt;/code&gt; throws, she goes to &lt;strong&gt;FAILED&lt;/strong&gt;, and the scheduler automatically retries her after &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;retry_delay&lt;/code&gt; (15 minutes) — transient errors need no humans. If worker-07 loses power mid-write, the scheduler notices the missed heartbeats after &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;worker_disconnect_delay&lt;/code&gt; (60 seconds) and returns her to &lt;strong&gt;PENDING&lt;/strong&gt; for another worker; the half-written temp file never touched the real shelf, so nothing downstream can consume garbage. And if she fails too many times (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;retry_count&lt;/code&gt;), she’s parked as &lt;strong&gt;DISABLED&lt;/strong&gt; so a broken task can’t retry-storm the cluster while everyone sleeps.&lt;/p&gt;

&lt;p&gt;That’s the whole system’s resilience in one biography: &lt;em&gt;state lives in outputs, writes are atomic, everything else is retryable.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;when-the-scheduler-dies-zero-downtime-part-one&quot;&gt;When the scheduler dies (zero downtime, part one)&lt;/h2&gt;

&lt;pre&gt;&lt;code class=&quot;language-mermaid&quot;&gt;flowchart LR
  A[&quot;1 · Failure or deploy&amp;lt;br&amp;gt;scheduler-01 crashes, reboots,&amp;lt;br&amp;gt;or is taken down for upgrade&quot;] --&amp;gt; B[&quot;2 · VIP moves&amp;lt;br&amp;gt;keepalived misses VRRP adverts;&amp;lt;br&amp;gt;scheduler-02 claims the VIP&amp;lt;br&amp;gt;within seconds&quot;]
  B --&amp;gt; C[&quot;3 · Promotion&amp;lt;br&amp;gt;notify script restarts luigid,&amp;lt;br&amp;gt;loading the replicated&amp;lt;br&amp;gt;state checkpoint&quot;]
  C --&amp;gt; D[&quot;4 · Workers reconnect&amp;lt;br&amp;gt;same URL — the VIP; RPC layer&amp;lt;br&amp;gt;retries 3 × every 30 s,&amp;lt;br&amp;gt;silently absorbing the gap&quot;]
  D --&amp;gt; E[&quot;5 · No work lost&amp;lt;br&amp;gt;running run() calls never stopped;&amp;lt;br&amp;gt;finished outputs re-detected via&amp;lt;br&amp;gt;Target.exists(), not redone&quot;]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;At some point scheduler-01 will die — a kernel panic, a bad disk, or you, on purpose, at 14:00 on a Tuesday, because upgrades shouldn’t be scary.&lt;/p&gt;

&lt;p&gt;keepalived notices the missed VRRP heartbeats within seconds and moves the VIP to scheduler-02, whose promotion script (re)starts &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;luigid&lt;/code&gt; from the freshest replicated state checkpoint. And the workers? They dial the same phone number they always dialed. Luigi’s RPC client retries a failed call three times with a 30-second wait (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[core] rpc_retry_attempts&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rpc_retry_wait&lt;/code&gt;), so the fleet silently absorbs an outage of a minute and a half — a keepalived flip takes three seconds. Tasks that were RUNNING never even notice, because the foreman never had their bricks in his hands to begin with.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;The upgrade trick:&lt;/strong&gt; make failover the deploy mechanism. Upgrade the &lt;em&gt;standby&lt;/em&gt; first, deliberately flip the VIP to it, verify, then upgrade the former active. The VIP is never unowned for more than seconds, and you always keep one known-good scheduler to fail back to.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;deploying-workers-without-dropping-a-task-part-two&quot;&gt;Deploying workers without dropping a task (part two)&lt;/h2&gt;

&lt;pre&gt;&lt;code class=&quot;language-mermaid&quot;&gt;flowchart LR
  A[&quot;1 · Drain&amp;lt;br&amp;gt;kill -USR1 the worker procs:&amp;lt;br&amp;gt;stop requesting new work,&amp;lt;br&amp;gt;finish running tasks&quot;] --&amp;gt; B[&quot;2 · Deploy&amp;lt;br&amp;gt;worker exits cleanly on its own;&amp;lt;br&amp;gt;roll new pipeline code + venv&amp;lt;br&amp;gt;onto the VM&quot;]
  B --&amp;gt; C[&quot;3 · Rejoin&amp;lt;br&amp;gt;restart the systemd timer —&amp;lt;br&amp;gt;worker registers and&amp;lt;br&amp;gt;pulls tasks again&quot;]
  C --&amp;gt; D[&quot;4 · Verify, then next&amp;lt;br&amp;gt;visualiser + Grafana healthy →&amp;lt;br&amp;gt;move on to worker-02&quot;]
  D -.-&amp;gt;|&quot;repeat per VM — fleet capacity never reaches zero&quot;| A
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Workers deploy like a rolling wave, one VM at a time, and Luigi has a first-class drain built in: send &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SIGUSR1&lt;/code&gt; to a worker process and it stops asking for new work, finishes what it’s running, and exits by itself. Drain, deploy, rejoin, verify, next VM. Fleet capacity never touches zero, and no task is ever killed mid-flight.&lt;/p&gt;

&lt;p&gt;And if a VM dies &lt;em&gt;ungracefully&lt;/em&gt; mid-deploy? Reread the task lifecycle above — the safety net doesn’t care why a worker vanished. Rolling deploys are for smoothness, not for correctness.&lt;/p&gt;

&lt;h2 id=&quot;what-actually-runs-on-each-box&quot;&gt;What actually runs on each box&lt;/h2&gt;

&lt;pre&gt;&lt;code class=&quot;language-mermaid&quot;&gt;flowchart TB
  subgraph SVM[&quot;scheduler-01 VM — Docker Engine · scheduler-02 identical&quot;]
    KA[&quot;keepalived — host process, not a container&amp;lt;br&amp;gt;owns VIP 10.0.0.50 · health-checks luigid :8082&quot;]
    LGC[&quot;luigid container&amp;lt;br&amp;gt;image registry/luigid:3.x · ports 8082:8082&amp;lt;br&amp;gt;restart: unless-stopped&quot;]
    VOL1[(&quot;named volume: luigi-state&amp;lt;br&amp;gt;/var/lib/luigi-server/state.pickle&quot;)]
    CFG1[(&quot;bind mount, read-only&amp;lt;br&amp;gt;/etc/luigi/luigi.cfg&quot;)]
    NE1[&quot;node-exporter container :9100&quot;]
    KA --&amp;gt; LGC
    LGC --&amp;gt; VOL1
    LGC --&amp;gt; CFG1
  end
  subgraph WVM[&quot;worker-01 VM — Docker Engine · worker-02 … N identical&quot;]
    TMR[&quot;host systemd timer or cron&amp;lt;br&amp;gt;docker run --rm registry/pipeline:TAG&quot;]
    WKC[&quot;luigi-worker container&amp;lt;br&amp;gt;image registry/pipeline:TAG · RangeDaily --workers 8&amp;lt;br&amp;gt;exec-form entrypoint → luigi is PID 1, receives SIGUSR1&quot;]
    DATA[(&quot;bind mount: /data&amp;lt;br&amp;gt;shared artifact store — NFS mounted on host&quot;)]
    CFG2[(&quot;bind mount, read-only&amp;lt;br&amp;gt;luigi.cfg&quot;)]
    NE2[&quot;node-exporter container :9100&quot;]
    TMR --&amp;gt; WKC
    WKC --&amp;gt; DATA
    WKC --&amp;gt; CFG2
  end
  WKC --&amp;gt;|&quot;HTTP RPC → VIP :8082&quot;| LGC
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Containerizing this changes &lt;em&gt;how software gets onto a box&lt;/em&gt;, not the architecture. On scheduler VMs, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;luigid&lt;/code&gt; is a long-lived container (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;restart: unless-stopped&lt;/code&gt;) with the state checkpoint on a named volume and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;luigi.cfg&lt;/code&gt; bind-mounted read-only; keepalived stays a host process because VRRP wants to own real network interfaces. On worker VMs, the pipeline ships as an &lt;em&gt;immutable image&lt;/em&gt;: the host timer runs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker run --rm registry/pipeline:TAG&lt;/code&gt;, so nothing on the box ever mutates — a deploy is a tag bump. The drain becomes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker kill --signal=USR1&lt;/code&gt; (use an exec-form ENTRYPOINT so Luigi is PID 1 and actually receives the signal), and the NFS mount lives on the host, bind-mounted in, keeping credentials out of images.&lt;/p&gt;

&lt;h2 id=&quot;shipping-it-all-with-gitlab-cicd&quot;&gt;Shipping it all with GitLab CI/CD&lt;/h2&gt;

&lt;pre&gt;&lt;code class=&quot;language-mermaid&quot;&gt;flowchart LR
  DEV[&quot;developer&amp;lt;br&amp;gt;merge to main&quot;] --&amp;gt; TEST[&quot;stage: test&amp;lt;br&amp;gt;pytest — task logic,&amp;lt;br&amp;gt;completeness, atomic writes&quot;]
  TEST --&amp;gt; BUILD[&quot;stage: build&amp;lt;br&amp;gt;docker build pipeline:$SHA&amp;lt;br&amp;gt;push to registry&quot;]
  BUILD --&amp;gt; REG[(&quot;container&amp;lt;br&amp;gt;registry&quot;)]
  REG --&amp;gt; DW[&quot;stage: deploy-workers · manual&amp;lt;br&amp;gt;GitLab Runner, VM by VM:&amp;lt;br&amp;gt;drain USR1 → wait → bump TAG → verify&quot;]
  DW --&amp;gt; DS[&quot;stage: deploy-scheduler · manual&amp;lt;br&amp;gt;upgrade standby → flip VIP →&amp;lt;br&amp;gt;upgrade old active&quot;]
  DS --&amp;gt; OK([&quot;fleet on the new version —&amp;lt;br&amp;gt;zero downtime, zero lost tasks&quot;])
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The last mile: nobody should SSH around the fleet by hand. A GitLab Runner (shell executor, on its own small VM inside the production subnet, with SSH keys to the fleet) turns the whole rolling-deploy choreography into a button.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;stages&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;deploy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;IMAGE&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$CI_REGISTRY_IMAGE/pipeline&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;TAG&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$CI_COMMIT_SHORT_SHA&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;test&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;python:3.11&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;pip install -r requirements.txt&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;pytest tests/&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;# task logic, completeness, atomic-write discipline&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;build&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;luigi-deploy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;         &lt;span class=&quot;c1&quot;&gt;# our shell runner&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;docker build -t $IMAGE:$TAG .&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;docker push $IMAGE:$TAG&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;deploy-workers&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;deploy&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;manual&lt;/span&gt;                 &lt;span class=&quot;c1&quot;&gt;# a human presses the button&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;production&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;luigi-deploy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;for host in worker-01 worker-02 worker-03; do&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;echo &quot;── rolling $host&quot;&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;# 1. drain: stop taking new tasks, finish running ones&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;ssh luigi@$host &quot;docker kill --signal=USR1 luigi-worker || true&quot;&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;# 2. wait for the container to exit on its own&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;ssh luigi@$host &quot;while docker ps -q -f name=luigi-worker | grep -q .; do sleep 15; done&quot;&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;# 3. bump the tag the systemd timer will use next tick&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;ssh luigi@$host &quot;echo TAG=$TAG | sudo tee /etc/luigi/image.env &amp;amp;&amp;amp; docker pull $IMAGE:$TAG&quot;&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;# 4. verify the scheduler still sees a healthy fleet before moving on&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;curl -fsS http://10.0.0.50:8082/ &amp;gt; /dev/null&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;done&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;deploy-scheduler&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;deploy&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;manual&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;needs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;deploy-workers&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;luigi-deploy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ssh luigi@scheduler-02 &quot;docker pull $CI_REGISTRY_IMAGE/luigid:$TAG &amp;amp;&amp;amp; sudo systemctl restart luigid&quot;&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ssh luigi@scheduler-01 &quot;sudo systemctl stop keepalived&quot;&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# VIP flips to 02&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;sleep 5 &amp;amp;&amp;amp; curl -fsS http://10.0.0.50:8082/ &amp;gt; /dev/null&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# health check via VIP&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ssh luigi@scheduler-01 &quot;docker pull $CI_REGISTRY_IMAGE/luigid:$TAG &amp;amp;&amp;amp; sudo systemctl restart luigid &amp;amp;&amp;amp; sudo systemctl start keepalived&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice how little cleverness the pipeline contains. It doesn’t migrate state, doesn’t pause the world, doesn’t coordinate anything — because the architecture already guarantees that draining a worker loses nothing and flipping the VIP interrupts nobody. Good CI/CD for this system is mostly &lt;em&gt;pressing the buttons in the right order&lt;/em&gt;, and that’s exactly how it should feel. The manual gates are deliberate: tests and builds run on every merge, but a human decides when the fleet rolls.&lt;/p&gt;

&lt;h2 id=&quot;the-safety-net-restated&quot;&gt;The safety net, restated&lt;/h2&gt;

&lt;p&gt;Everything above leans on four properties — and they’re obligations on your &lt;em&gt;pipeline code&lt;/em&gt;, not gifts from the infrastructure. Completeness is output-based, so finished work can never be forgotten or redone. Writes are atomic via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;temporary_path()&lt;/code&gt;, so a dying worker cannot leave a half-truth on the shelf. Dead workers are detected (60 s) and their tasks re-queued; failed tasks retry themselves (15 min). And global &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[resources]&lt;/code&gt; limits are enforced by the scheduler &lt;em&gt;across the whole fleet&lt;/em&gt;, so ten workers can’t accidentally open three hundred database connections or write the same file twice.&lt;/p&gt;

&lt;p&gt;Get those four right and every failure in this post becomes boring. Boring is the goal.&lt;/p&gt;

&lt;h2 id=&quot;configuration-cheat-sheet&quot;&gt;Configuration cheat-sheet&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Setting&lt;/th&gt;
      &lt;th&gt;Section&lt;/th&gt;
      &lt;th&gt;Default&lt;/th&gt;
      &lt;th&gt;Role in this design&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default_scheduler_url&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[core]&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost:8082/&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Point at the &lt;strong&gt;VIP&lt;/strong&gt;, never a physical host&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rpc_retry_attempts&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rpc_retry_wait&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[core]&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;3 / 30 s&lt;/td&gt;
      &lt;td&gt;Workers ride out ~90 s of scheduler downtime&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;state_path&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[scheduler]&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/lib/luigi-server/state.pickle&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;The checkpoint you replicate to the standby&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;record_task_history&lt;/code&gt; + &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[task_history] db_connection&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[scheduler]&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;off&lt;/td&gt;
      &lt;td&gt;Durable audit trail in PostgreSQL&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;worker_disconnect_delay&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[scheduler]&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;60 s&lt;/td&gt;
      &lt;td&gt;How fast a dead worker’s tasks are re-queued&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;retry_delay&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;retry_count&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[scheduler]&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;900 s&lt;/td&gt;
      &lt;td&gt;Automatic retries; park broken tasks as DISABLED&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ping_interval&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[worker]&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;1 s&lt;/td&gt;
      &lt;td&gt;Worker heartbeat driving disconnect detection&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[resources]&lt;/code&gt; keys&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[resources]&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;—&lt;/td&gt;
      &lt;td&gt;Fleet-wide concurrency caps&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;One &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;luigi.cfg&lt;/code&gt;, deployed identically to every VM.&lt;/p&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://luigi.readthedocs.io/en/stable/central_scheduler.html&quot;&gt;Luigi — Using the central scheduler&lt;/a&gt; — running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;luigid&lt;/code&gt;, state path, task history&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://luigi.readthedocs.io/en/stable/configuration.html&quot;&gt;Luigi — Configuration reference&lt;/a&gt; — every default quoted in this post&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://luigi.readthedocs.io/en/stable/luigi_patterns.html&quot;&gt;Luigi — Common patterns&lt;/a&gt; — atomic writes, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RangeDaily&lt;/code&gt;, resources&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://luigi.readthedocs.io/en/stable/execution_model.html&quot;&gt;Luigi — Execution model&lt;/a&gt; — why workers do the work and the scheduler doesn’t&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://luigi.readthedocs.io/en/stable/_modules/luigi/worker.html&quot;&gt;luigi.worker source&lt;/a&gt; — the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SIGUSR1&lt;/code&gt; drain handler&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.keepalived.org/manpage.html&quot;&gt;keepalived documentation&lt;/a&gt; — VRRP, VIPs, notify scripts&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.gitlab.com/ee/ci/yaml/&quot;&gt;GitLab CI/CD reference&lt;/a&gt; — &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gitlab-ci.yml&lt;/code&gt; keywords&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.gitlab.com/runner/executors/shell.html&quot;&gt;GitLab Runner — shell executor&lt;/a&gt; — the deploy runner used here&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 01 Aug 2026 00:00:00 +0000</pubDate>
        <link>https://danghoangnhan.github.io/luigi-multi-server-zero-downtime/</link>
        <guid isPermaLink="true">https://danghoangnhan.github.io/luigi-multi-server-zero-downtime/</guid>
        
        
        <category>data-engineering</category>
        
        <category>devops</category>
        
      </item>
    
      <item>
        <title>Leetcode-2140. Solving Questions With Brainpower</title>
        <description>&lt;h2 id=&quot;intuition&quot;&gt;Intuition&lt;/h2&gt;

&lt;p&gt;We need to split the binary string &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;s&lt;/code&gt; into two non-empty parts—left and right—so that the score, defined as “number of zeros in the left part” plus “number of ones in the right part,” is maximized. If we can quickly determine, for any split position &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;the count of zeros in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;s[0…i]&lt;/code&gt;, and&lt;/li&gt;
  &lt;li&gt;the count of ones in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;s[i+1…n−1]&lt;/code&gt;,&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;then we can evaluate each possible split in constant time. A prefix‐sum array for zeros makes this efficient.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;approach&quot;&gt;Approach&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Build a prefix array of zero counts&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Create &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zeros[i]&lt;/code&gt; = number of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&apos;0&apos;&lt;/code&gt; characters in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;s[0…i]&lt;/code&gt;.&lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;This is done in a single pass:&lt;/p&gt;

        &lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;zeros&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zeros&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;&apos;0&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Enumerate every valid split&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;For split at index &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i&lt;/code&gt; (where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0 ≤ i &amp;lt; n−1&lt;/code&gt;):
        &lt;ul&gt;
          &lt;li&gt;&lt;strong&gt;Left zeros&lt;/strong&gt; = &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zeros[i]&lt;/code&gt;.&lt;/li&gt;
          &lt;li&gt;&lt;strong&gt;Right length&lt;/strong&gt; = &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;n − i − 1&lt;/code&gt;.&lt;/li&gt;
          &lt;li&gt;&lt;strong&gt;Right zeros&lt;/strong&gt; = &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;totalZeros − zeros[i]&lt;/code&gt;, where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;totalZeros = zeros[n−1]&lt;/code&gt;.&lt;/li&gt;
          &lt;li&gt;&lt;strong&gt;Right ones&lt;/strong&gt; = &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;right length − right zeros&lt;/code&gt;.&lt;/li&gt;
          &lt;li&gt;&lt;strong&gt;Score&lt;/strong&gt; = &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zeros[i] + right ones&lt;/code&gt;.&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;Keep track of the maximum score.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Return the maximum score&lt;/strong&gt; after checking all splits.&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;complexity&quot;&gt;Complexity&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Time complexity:&lt;/strong&gt;&lt;br /&gt;
We make two linear passes over the string—one to build the prefix sums and one to evaluate splits—so the total is&lt;br /&gt;
\(O(n)\,.\)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Space complexity:&lt;/strong&gt;&lt;br /&gt;
We use an extra array of size &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;n&lt;/code&gt; to store the prefix sums, so&lt;br /&gt;
\(O(n)\,.\)&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;code&quot;&gt;Code&lt;/h2&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Solution&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;nl&quot;&gt;public:&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maxScore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zeros&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        
        &lt;span class=&quot;c1&quot;&gt;// 1. Build prefix-sum array for zeros&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;zeros&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zeros&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;&apos;0&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        
        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;totalZeros&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zeros&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        
        &lt;span class=&quot;c1&quot;&gt;// 2. Enumerate all splits at i (left = s[0…i], right = s[i+1…n-1])&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;leftZeros&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zeros&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rightLen&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rightZeros&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;totalZeros&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zeros&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rightOnes&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rightLen&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rightZeros&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            
            &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;leftZeros&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rightOnes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;explanation&quot;&gt;Explanation&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Prefix-sum construction&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;We accumulate the count of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&apos;0&apos;&lt;/code&gt; up to each index. This allows constant‑time retrieval of how many zeros appear in any prefix.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Split evaluation&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;For each potential split before the last character, we compute:
        &lt;ul&gt;
          &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;leftZeros&lt;/code&gt; directly from the prefix sum.&lt;/li&gt;
          &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rightOnes&lt;/code&gt; by subtracting the zeros in the right portion from its total length.&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;We then sum these to get the score for that split.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Result&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;We track the maximum across all splits and return it. This transforms an otherwise quadratic‑time brute force into a linear‑time solution by leveraging prefix sums.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;
</description>
        <pubDate>Wed, 01 Jan 2025 00:00:00 +0000</pubDate>
        <link>https://danghoangnhan.github.io/solving-questions-with-brainpower/</link>
        <guid isPermaLink="true">https://danghoangnhan.github.io/solving-questions-with-brainpower/</guid>
        
        
        <category>leetcode</category>
        
      </item>
    
      <item>
        <title>LongLLMLingua Model: A Solution for LLMs in Long Context Scenarios</title>
        <description>&lt;h2 id=&quot;core-challenges&quot;&gt;Core Challenges&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;../assets/images/llmlingua/LongLLMLingua_Motivation.png&quot; alt=&quot;Illustration showing the challenge of long prompts in LLMs&quot; title=&quot;Challenges with LLM int long context&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;1-question-context-relevance-problem&quot;&gt;1. Question-Context Relevance Problem&lt;/h3&gt;

&lt;p&gt;Traditional prompt compression methods face several critical issues when dealing with long contexts:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Traditional Approach:
Input: [Document1, Document2, ..., DocumentN] + Question
Process: Compress each document independently
Problem: Lost relationship between question and relevant content
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This leads to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Information loss from relevant sections&lt;/li&gt;
  &lt;li&gt;Retention of irrelevant information&lt;/li&gt;
  &lt;li&gt;Suboptimal compression ratios&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;2-position-bias&quot;&gt;2. Position Bias&lt;/h3&gt;

&lt;p&gt;LLMs exhibit significant position bias in processing long sequences:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Typical LLM Processing:
Beginning of prompt: High attention &amp;amp; retention
Middle of prompt: Decreased attention
End of prompt: Recency bias
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Impact:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Critical information in middle sections gets overlooked&lt;/li&gt;
  &lt;li&gt;Uneven quality of responses based on information position&lt;/li&gt;
  &lt;li&gt;Inefficient use of context window&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;3-static-compression-limitations&quot;&gt;3. Static Compression Limitations&lt;/h3&gt;

&lt;p&gt;Current compression methods often use fixed compression ratios:&lt;/p&gt;

&lt;h1 id=&quot;longllmlingua-methodology&quot;&gt;LongLLMLingua Methodology&lt;/h1&gt;

&lt;h2 id=&quot;1-question-aware-coarse-to-fine-compression&quot;&gt;1. Question-Aware Coarse-to-Fine Compression&lt;/h2&gt;

&lt;h3 id=&quot;coarse-grained-stage&quot;&gt;Coarse-Grained Stage&lt;/h3&gt;

&lt;p&gt;The first stage focuses on document-level relevance assessment:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Ranking Metric (rk)&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Evaluates document-question association strength&lt;/li&gt;
      &lt;li&gt;Calculates question perplexity conditioned on each document&lt;/li&gt;
      &lt;li&gt;Higher rk indicates stronger relevance to the question&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Hallucination Prevention&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Appends restrictive statements after questions&lt;/li&gt;
      &lt;li&gt;Example: “We can get the answer to this question in the given documents”&lt;/li&gt;
      &lt;li&gt;Reinforces connection between question and context&lt;/li&gt;
      &lt;li&gt;Reduces model tendency to hallucinate&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;fine-grained-stage&quot;&gt;Fine-Grained Stage&lt;/h3&gt;

&lt;p&gt;After identifying relevant documents, the process moves to token-level analysis:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Contrastive Perplexity Evaluation&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Measures token importance relative to the question&lt;/li&gt;
      &lt;li&gt;Compares token perplexity with and without question context&lt;/li&gt;
      &lt;li&gt;Identifies question-relevant tokens&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Token Selection Process&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Prioritizes tokens with high contrastive perplexity&lt;/li&gt;
      &lt;li&gt;Maintains semantic coherence&lt;/li&gt;
      &lt;li&gt;Preserves question-relevant information&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;2-document-reordering-system&quot;&gt;2. Document Reordering System&lt;/h2&gt;

&lt;h3 id=&quot;position-bias-mitigation&quot;&gt;Position Bias Mitigation&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Importance-Based Ordering&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Ranks documents using rk scores&lt;/li&gt;
      &lt;li&gt;Places high-relevance documents strategically&lt;/li&gt;
      &lt;li&gt;Addresses “lost in the middle” phenomenon&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Attention Distribution&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Optimizes document sequence for LLM processing&lt;/li&gt;
      &lt;li&gt;Ensures critical information receives adequate attention&lt;/li&gt;
      &lt;li&gt;Maintains logical flow and coherence&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;3-dynamic-compression-ratio-allocation&quot;&gt;3. Dynamic Compression Ratio Allocation&lt;/h2&gt;

&lt;h3 id=&quot;adaptive-compression&quot;&gt;Adaptive Compression&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Importance-Based Ratios&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Higher importance → Lower compression ratio&lt;/li&gt;
      &lt;li&gt;Lower importance → Higher compression ratio&lt;/li&gt;
      &lt;li&gt;Preserves more content from relevant documents&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Budget Distribution&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Allocates token budget based on document relevance&lt;/li&gt;
      &lt;li&gt;Balances compression across document set&lt;/li&gt;
      &lt;li&gt;Optimizes information retention&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;4-subsequence-recovery&quot;&gt;4. Subsequence Recovery&lt;/h2&gt;

&lt;h3 id=&quot;entity-preservation&quot;&gt;Entity Preservation&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Entity Detection&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Identifies compressed named entities&lt;/li&gt;
      &lt;li&gt;Recognizes important technical terms&lt;/li&gt;
      &lt;li&gt;Maps abbreviated references&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Recovery Process&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Restores original entity forms&lt;/li&gt;
      &lt;li&gt;Maintains contextual accuracy&lt;/li&gt;
      &lt;li&gt;Ensures output coherence&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;implementation-flow&quot;&gt;Implementation Flow&lt;/h2&gt;

&lt;h3 id=&quot;1-initial-processing&quot;&gt;1. Initial Processing&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Parse input documents and question&lt;/li&gt;
  &lt;li&gt;Prepare for coarse-grained analysis&lt;/li&gt;
  &lt;li&gt;Set up compression parameters&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;2-coarse-compression&quot;&gt;2. Coarse Compression&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Calculate document-question relevance&lt;/li&gt;
  &lt;li&gt;Apply ranking metric (rk)&lt;/li&gt;
  &lt;li&gt;Select relevant documents&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;3-document-organization&quot;&gt;3. Document Organization&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Reorder based on importance scores&lt;/li&gt;
  &lt;li&gt;Optimize position distribution&lt;/li&gt;
  &lt;li&gt;Prepare for fine-grained compression&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;4-fine-grained-processing&quot;&gt;4. Fine-Grained Processing&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Calculate contrastive perplexity&lt;/li&gt;
  &lt;li&gt;Apply dynamic compression ratios&lt;/li&gt;
  &lt;li&gt;Select relevant tokens&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;5-post-processing&quot;&gt;5. Post-Processing&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Recover important subsequences&lt;/li&gt;
  &lt;li&gt;Verify entity consistency&lt;/li&gt;
  &lt;li&gt;Ensure output quality&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;optimization-considerations&quot;&gt;Optimization Considerations&lt;/h2&gt;

&lt;h3 id=&quot;1-performance-enhancement&quot;&gt;1. Performance Enhancement&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Batch processing for efficiency&lt;/li&gt;
  &lt;li&gt;Optimal window sizing&lt;/li&gt;
  &lt;li&gt;Resource utilization management&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;2-quality-assurance&quot;&gt;2. Quality Assurance&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Coherence verification&lt;/li&gt;
  &lt;li&gt;Entity accuracy checking&lt;/li&gt;
  &lt;li&gt;Semantic consistency validation&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;system-requirements&quot;&gt;System Requirements&lt;/h2&gt;

&lt;h3 id=&quot;1-model-components&quot;&gt;1. Model Components&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Small language model for perplexity calculation&lt;/li&gt;
  &lt;li&gt;Entity recognition system&lt;/li&gt;
  &lt;li&gt;Compression ratio calculator&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;2-resource-requirements&quot;&gt;2. Resource Requirements&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Memory allocation for document processing&lt;/li&gt;
  &lt;li&gt;Storage for entity mapping&lt;/li&gt;
  &lt;li&gt;Processing capacity for real-time compression&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;methodology-benefits&quot;&gt;Methodology Benefits&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Enhanced Accuracy&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Question-aware compression preserves relevant information&lt;/li&gt;
      &lt;li&gt;Dynamic ratios optimize content retention&lt;/li&gt;
      &lt;li&gt;Entity recovery maintains precision&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Improved Efficiency&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Reduced token count&lt;/li&gt;
      &lt;li&gt;Optimized processing flow&lt;/li&gt;
      &lt;li&gt;Minimized computational overhead&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Better Context Handling&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Position bias mitigation&lt;/li&gt;
      &lt;li&gt;Coherent document organization&lt;/li&gt;
      &lt;li&gt;Preserved semantic relationships&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;limitations-and-considerations&quot;&gt;Limitations and Considerations&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Question-Specific Nature&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Requires recompression for new questions&lt;/li&gt;
      &lt;li&gt;Limited caching potential&lt;/li&gt;
      &lt;li&gt;Computational overhead for repeated processing&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Complex Relationships&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;May miss subtle context connections&lt;/li&gt;
      &lt;li&gt;Challenges with intricate document dependencies&lt;/li&gt;
      &lt;li&gt;Potential for information loss in edge cases&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This methodology represents a comprehensive approach to long-context prompt compression, balancing efficiency with effectiveness while maintaining output quality and relevance.&lt;/p&gt;
</description>
        <pubDate>Mon, 09 Sep 2024 00:00:00 +0000</pubDate>
        <link>https://danghoangnhan.github.io/longlingua/</link>
        <guid isPermaLink="true">https://danghoangnhan.github.io/longlingua/</guid>
        
        
        <category>llm</category>
        
      </item>
    
      <item>
        <title>LLMLingua: Compressing Prompts for Accelerated Inference of Large Language Models</title>
        <description>&lt;h3 id=&quot;the-challenges-of-of-llms&quot;&gt;The Challenges of of LLMs&lt;/h3&gt;

&lt;p&gt;Large language models (LLMs) have revolutionized various applications due to their remarkable capabilities. Advancements in techniques like chain-of-thought prompting and in-context learning have significantly enhanced the ability of LLMs to perform complex reasoning tasks and adapt to specific domains. However, these powerful techniques often result in increasingly long prompts, comprising tens of thousands of tokens.
This trend toward lengthy prompts presents a significant challenge. It leads to substantial computational demands and increased costs for LLM inference, hindering the broader adoption and scalability of LLMs in real-world applications. This situation has created an urgent need to balance the need for comprehensive prompts with the computational efficiency of LLMs.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/images/llmlingua/LLMLingua_motivation.png&quot; alt=&quot;Illustration showing the challenge of long prompts in LLMs&quot; title=&quot;Challenges with LLM Processing&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In addition to the challenges posed by lengthy prompts, several inherent limitations of LLMs further underscore the need for innovative solutions:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Token Limits: LLMs are inherently constrained by token limits, which restrict their ability to handle very long texts . This limitation can be particularly problematic when dealing with tasks that require summarizing lengthy documents or processing extensive conversational histories, as exceeding the token limit can result in information loss .&lt;/li&gt;
  &lt;li&gt;High API Costs: LLMs like GPT-3.5 and GPT-4, while demonstrating impressive performance, come with substantial API costs . These costs can become prohibitive for large-scale experiments, research endeavors, or applications that require frequent interaction with the LLM.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;how-llmlingua-works&quot;&gt;How LLMLingua Works&lt;/h2&gt;

&lt;p&gt;LLMLingua streamlines large language model prompts through three essential components: the Budget Controller for optimal compression allocation, Iterative Token-Level Prompt Compression (ITPC) for context-aware compression, and Distribution Alignment to maintain model consistency.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/images/llmlingua/LLMLingua_framework.png&quot; alt=&quot;Architecture diagram of LLMLingua showing three main components&quot; title=&quot;LLMLingua Framework Architecture&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;budget-controller&quot;&gt;Budget Controller&lt;/h3&gt;

&lt;p&gt;The Budget Controller  manages compression across different prompt components. This module:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Dynamically assigns varying compression ratios based on content importance&lt;/li&gt;
  &lt;li&gt;Prioritizes preservation of instructions and questions over demonstrations&lt;/li&gt;
  &lt;li&gt;Operates at sentence level to maintain linguistic coherence&lt;/li&gt;
  &lt;li&gt;Adapts compression rates based on content criticality and available token budget&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
  &lt;li&gt;A small language model \(\mathcal{M}_s\)&lt;/li&gt;
  &lt;li&gt;The original prompt \(x = (x^{\text{ins}}, x^{\text{dems}}, x^{\text{que}})\)&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Compression ratio for demonstrations(Equation 2):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;\(\tau_{\text{dems}} = \frac{\tau L - (\tau_{\text{ins}}L_{\text{ins}} + \tau_{\text{que}}L_{\text{que}})}{L_{\text{dems}}}\) (2)&lt;/li&gt;
  &lt;li&gt;Where:
    &lt;ul&gt;
      &lt;li&gt;\(\tau\), \(L\): the compression ratio and length for prompt&lt;/li&gt;
      &lt;li&gt;\(\tau_{\text{dems}}\), \(L_{\text{dems}}\): the compression ratio and length for demonstrations&lt;/li&gt;
      &lt;li&gt;\(\tau_{\text{ins}}\), \(L_{\text{ins}}\): the compression ratio and length for instructions&lt;/li&gt;
      &lt;li&gt;\(\tau_{\text{que}}\), \(L_{\text{que}}\): the compression ratio and length for questions&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Compression Ratio Adjustment (Equation 3):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
\[\Delta \tau = \frac{k \tau_{\text{dems}}L_{\text{dems}} - \tilde{L}_D}{L_{\text{ins}} + L_{\text{que}}}\]
  &lt;/li&gt;
  &lt;li&gt;Where:
    &lt;ul&gt;
      &lt;li&gt;\(\Delta \tau\): the adjustment to the compression ratio&lt;/li&gt;
      &lt;li&gt;\(k\): a scaling factor&lt;/li&gt;
      &lt;li&gt;\(\tau_{\text{dems}}\): the compression ratio and length for demonstrations&lt;/li&gt;
      &lt;li&gt;\(L_{\text{dems}}\): the length processed by the lens model&lt;/li&gt;
      &lt;li&gt;\(\tilde{L}_D\):  the desired compressed length&lt;/li&gt;
      &lt;li&gt;\(L_{\text{ins}}\) , \(L_{\text{que}}\): lengths of instructions and questions respectively&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

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

&lt;ol&gt;
  &lt;li&gt;Set the selected demonstration set \(\mathcal{D} = \phi\)&lt;/li&gt;
  &lt;li&gt;Get demonstration compression rate \(\tau_{\text{dem}}\) by Eq.(2)&lt;/li&gt;
  &lt;li&gt;Calculate the perplexity of each demonstration via \(\mathcal{M}_s\)&lt;/li&gt;
  &lt;li&gt;Rank all demonstrations in descending order of their perplexity as a list \((x_{(1)}^{\text{dem}}, ..., x_{(N)}^{\text{dem}})\), where \(N\) is the number of demonstrations, \(x_{(i)}^{\text{dem}}\) is the \(i\)-th demonstration&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;for&lt;/strong&gt; \(i = 1\) &lt;strong&gt;do&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;    &lt;strong&gt;if&lt;/strong&gt; \(L_\mathcal{D} &amp;gt; k \cdot \tau_{\text{dems}}L_{\text{dems}}\) &lt;strong&gt;then&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;        Break&lt;/li&gt;
  &lt;li&gt;    &lt;strong&gt;end if&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;    Append \(x_{(i)}^{\text{dem}}\) to \(\mathcal{D}\)&lt;/li&gt;
  &lt;li&gt;    \(i = i + 1\)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;end for&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Allocate remaining budget to \(x^{\text{ins}}\) and \(x^{\text{que}}\) via Eq. (3)&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ul&gt;
  &lt;li&gt;The subset of demonstrations \(\mathcal{D}\) obtained from coarse-grained compression&lt;/li&gt;
  &lt;li&gt;Additional budget \(\Delta\tau_{\text{ins,que}}\) for the instruction and the question&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;iterative-token-level-prompt-compression&quot;&gt;Iterative Token-Level Prompt Compression&lt;/h3&gt;

&lt;p&gt;ITPC implementing a sophisticated, context-aware approach:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Segments prompts into meaningful units for targeted compression&lt;/li&gt;
  &lt;li&gt;Considers token interdependencies during compression decisions&lt;/li&gt;
  &lt;li&gt;Iteratively refines compression to preserve semantic relationships&lt;/li&gt;
  &lt;li&gt;Maintains critical context that basic token-level compression might miss&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
  &lt;li&gt;A small language model \(\mathcal{M}_s\)&lt;/li&gt;
  &lt;li&gt;The prompt from budget controller \(x&apos; = (x^{\text{ins}}, x^{\mathcal{D}}, x^{\text{que}})\)&lt;/li&gt;
  &lt;li&gt;Target compression rate \(\tau\)&lt;/li&gt;
  &lt;li&gt;Adjusted compression rate \(\Delta\tau_{\text{ins,que}}\)&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ol&gt;
  &lt;li&gt;Set the selected token set \(\mathcal{T} = \phi\)&lt;/li&gt;
  &lt;li&gt;Get segment set \(\mathcal{S}\)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;for&lt;/strong&gt; \(i = 1,2,\ldots,m\) &lt;strong&gt;do&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;    Get the conditional probabilities \(p(s_i)\) via Eq.(5)&lt;/li&gt;
  &lt;li&gt;    Get the compression threshold \(\gamma_i\) with Eq. (6)&lt;/li&gt;
  &lt;li&gt;    Append the compressed token to \(\mathcal{T}\) via Eq.(7)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;end for&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Concatenate all tokens in \(\mathcal{T}\) as \(\tilde{x}\)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt; The compressed prompt \(\tilde{x}\)&lt;/p&gt;

&lt;h3 id=&quot;distribution-alignment&quot;&gt;Distribution Alignment&lt;/h3&gt;

&lt;p&gt;A pre-trained small language model \(\mathcal{M}_s\) and use the data generated by the LLM to perform instruction tuning on \(\mathcal{M}_s\). The optimization of \(\mathcal{M}_s\) can be formulated as:&lt;/p&gt;

&lt;p&gt;\(\min_{\theta_s} \mathbb{E}\left[\frac{1}{N}\sum_{i=1}^N \mathcal{L}(x_i, y_{i,\text{LLM}}; \theta_{\mathcal{M}_s})\right]\),      (8)&lt;/p&gt;

&lt;p&gt;where :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;\(\theta_{\mathcal{M}_s}\): the parameters of \(\mathcal{M}_s\)&lt;/li&gt;
  &lt;li&gt;\((x_i, y_i^{\text{LLM}})\): the pair of instruction \(x_i\) and the LLM generated texts \(y_i^{\text{LLM}}\)&lt;/li&gt;
  &lt;li&gt;\(N\) is the number of all examples used for instruction tuning.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;key-features-and-advantages&quot;&gt;Key Features and Advantages&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;State-of-the-art Performance:&lt;/strong&gt; LLMLingua consistently outperforms existing prompt compression methods, including GPT4-Generation, Random Selection, and Selective-Context. It achieves this while enabling impressive compression ratios (up to 20x), showcasing its ability to retain critical information from the original prompt.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Retention of Reasoning and ICL Capabilities:&lt;/strong&gt; LLMLingua effectively preserves the reasoning and in-context learning capabilities of LLMs, even at high compression ratios.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Generalizability Across Different LLMs&lt;/strong&gt;: The effectiveness of LLMLingua extends beyond the GPT family of models, as demonstrated by its successful implementation with Claude-v1.3 as the target LLM.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Compatibility with Different Small LMs&lt;/strong&gt;: Although the choice of the smaller language model can impact performance, LLMLingua’s design enables its adaptation to various smaller LMs, with satisfactory results achieved even with less powerful models like GPT2-Small.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Reduction in Generated Text Length&lt;/strong&gt;: Prompt compression not only saves computational resources in the input but also contributes to reduced computation in the generation stage, as evidenced by the shorter text length produced by LLMs when using compressed prompts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;practical-implications&quot;&gt;Practical Implications&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Cost Reduction:&lt;/strong&gt; LLMLingua leads to significant cost savings by reducing the number of tokens processed during inference, a crucial factor considering the token-based pricing models of LLMs.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Enabling Longer Contexts:&lt;/strong&gt; By compressing prompts, LLMLingua opens up possibilities for accommodating longer contexts in LLMs, potentially enhancing their performance on tasks requiring extensive background information.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Potential for Downstream Task Performance Improvement:&lt;/strong&gt; By allowing the compression of longer prompts, LLMLingua holds the potential to enhance downstream task performance, enabling the utilization of more comprehensive prompts without incurring excessive computational costs.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Improved LLM Inference Efficiency:&lt;/strong&gt; LLMLingua’s prompt compression can contribute to improved LLM inference efficiency by compressing the KV cache, further optimizing the resource utilization during model inference.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;limitations&quot;&gt;Limitations&lt;/h3&gt;

&lt;p&gt;While LLMLingua delivers significant advantages, it’s essential to acknowledge its limitations:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Performance Drop at Extreme Compression Ratios:&lt;/strong&gt; While LLMLingua mitigates performance decline at high compression ratios, exceeding certain thresholds (e.g., 25x-30x on GSM8K) can still lead to a substantial performance drop. The achievable compression ratio depends on various factors like prompt length, task type, and the number of sentences involved.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Tokenizer Discrepancies:&lt;/strong&gt; The potential for subtle differences between the tokenizers used by the smaller and target LLMs might lead to an underestimation of the prompt’s token length, impacting the accuracy of compression calculations.&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 01 Sep 2024 00:00:00 +0000</pubDate>
        <link>https://danghoangnhan.github.io/llmlingua/</link>
        <guid isPermaLink="true">https://danghoangnhan.github.io/llmlingua/</guid>
        
        
        <category>llm</category>
        
      </item>
    
      <item>
        <title>LLaMA: Open and Efficient Foundation Language Models</title>
        <description>&lt;h2 id=&quot;exploring-the-architecture-training-data-and-training-process-of-llama&quot;&gt;Exploring the Architecture, Training Data, and Training Process of LLaMA&lt;/h2&gt;

&lt;p&gt;The sources provide a detailed explanation of the LLaMA model, covering its architecture, the data it was trained on, and the training process. Here’s a breakdown:&lt;/p&gt;

&lt;h3 id=&quot;model-architecture&quot;&gt;Model Architecture&lt;/h3&gt;

&lt;p&gt;The LLaMA model is built on the &lt;strong&gt;transformer architecture&lt;/strong&gt;, similar to other large language models.  It incorporates several modifications that have been shown to enhance performance and training stability:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Pre-normalization:&lt;/strong&gt;  Instead of normalizing the output of each transformer sub-layer, LLaMA normalizes the input, a technique inspired by the GPT-3 model. This helps improve training stability. LLaMA uses the RMSNorm normalizing function.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;SwiGLU Activation Function:&lt;/strong&gt;  LLaMA replaces the standard ReLU non-linearity with the SwiGLU activation function. This change was inspired by the PaLM model and has been shown to boost performance.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Rotary Embeddings:&lt;/strong&gt; Unlike the original transformer architecture, LLaMA does not use absolute positional embeddings. Instead, it uses &lt;strong&gt;rotary positional embeddings (RoPE)&lt;/strong&gt; added at each layer. This technique comes from the GPTNeo model.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Table 2 in the sources outlines the specific model sizes, architectures, and optimization hyperparameters for the different LLaMA models.&lt;/p&gt;

&lt;h3 id=&quot;training-data&quot;&gt;Training Data&lt;/h3&gt;

&lt;p&gt;A key differentiating factor of LLaMA is its use of &lt;strong&gt;publicly available data&lt;/strong&gt;. This contrasts with many other LLMs that rely on proprietary and inaccessible datasets. The training data for LLaMA is a mixture of text from several sources, ensuring a diverse range of domains and writing styles:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;English CommonCrawl:&lt;/strong&gt; This makes up the largest portion of the training data (67%) and was preprocessed using the CCNet pipeline to remove duplicates, non-English pages, and low-quality content.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;C4:&lt;/strong&gt; Another preprocessed CommonCrawl dataset (15%) known for its diversity.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; Public code repositories under specific licenses (4.5%) were filtered for quality and deduplicated.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Wikipedia:&lt;/strong&gt; Dumps from mid-2022 covering 20 languages using Latin or Cyrillic scripts (4.5%) were cleaned of formatting and extraneous content.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Gutenberg and Books3:&lt;/strong&gt; Public domain books from Project Gutenberg and the Books3 section of The Pile dataset (4.5%) were deduplicated based on content overlap.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;ArXiv:&lt;/strong&gt; Scientific papers in LaTeX format (2.5%) were processed to remove non-content sections and boilerplate.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Stack Exchange:&lt;/strong&gt; Question and answer data from 28 of the largest Stack Exchange websites (2%) was cleaned and sorted by answer score.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The entire training dataset comprises roughly 1.4 trillion tokens after tokenization using byte-pair encoding (BPE). Most tokens are used only once during training, with the exception of Wikipedia and book data, which undergo approximately two epochs.&lt;/p&gt;

&lt;h3 id=&quot;training-process&quot;&gt;Training Process&lt;/h3&gt;

&lt;p&gt;LLaMA’s training approach is similar to other large language models, drawing inspiration from the Chinchilla scaling laws. Some noteworthy aspects of the training process include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Optimizer:&lt;/strong&gt;  The AdamW optimizer is used with specific hyperparameters, including a cosine learning rate schedule and weight decay.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Efficient Implementation:&lt;/strong&gt;  Several optimizations were implemented to enhance training speed:
    &lt;ul&gt;
      &lt;li&gt;An efficient implementation of causal multi-head attention reduces memory and runtime.&lt;/li&gt;
      &lt;li&gt;Checkpointing reduces recomputation of activations during the backward pass.&lt;/li&gt;
      &lt;li&gt;Model and sequence parallelism minimizes memory usage.&lt;/li&gt;
      &lt;li&gt;Computation and communication between GPUs are overlapped for efficiency.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These optimizations resulted in a training speed of approximately 380 tokens per second per GPU on 2048 A100 GPUs, meaning it took roughly 21 days to train the 65B parameter model.&lt;/p&gt;

&lt;p&gt;Figure 1 in the sources shows the training loss over training tokens for different LLaMA models. Notably, the performance of the 7B model continued to improve even after 1 trillion tokens, challenging previous assumptions that larger models are always better. This observation emphasizes LLaMA’s focus on achieving optimal performance at various inference budgets by training on more data rather than simply scaling up model size.&lt;/p&gt;
</description>
        <pubDate>Fri, 01 Dec 2023 00:00:00 +0000</pubDate>
        <link>https://danghoangnhan.github.io/llama/</link>
        <guid isPermaLink="true">https://danghoangnhan.github.io/llama/</guid>
        
        
        <category>llm</category>
        
      </item>
    
      <item>
        <title>Understanding Anchor Boxes in Object Detection</title>
        <description>&lt;p&gt;Anchor boxes play a crucial role in overcoming the limitation of traditional object detection approaches, where each grid cell can detect only one object. By allowing multiple objects to be detected within a single grid cell, anchor boxes significantly enhance the accuracy and flexibility of object detection algorithms. Let’s delve into how anchor boxes work in detail.&lt;/p&gt;

&lt;h2 id=&quot;the-motivation-for-anchor-boxes&quot;&gt;The Motivation for Anchor Boxes&lt;/h2&gt;

&lt;p&gt;In traditional object detection, an image is divided into a grid, and each grid cell is responsible for detecting objects that fall within its boundaries. However, when multiple objects are present in close proximity or overlap, assigning only one object to each grid cell becomes a challenge. This is where anchor boxes come into play.&lt;/p&gt;

&lt;h2 id=&quot;understanding-anchor-boxes&quot;&gt;Understanding Anchor Boxes&lt;/h2&gt;

&lt;p&gt;Anchor boxes are predefined bounding box shapes that serve as reference templates for objects in an image. These boxes encapsulate various object shapes, sizes, and aspect ratios. The number of anchor boxes used can vary depending on the complexity of the dataset and the desired level of detection granularity. For simplicity, let’s consider an example with two anchor boxes.&lt;/p&gt;

&lt;h2 id=&quot;encoding-labels-with-anchor-boxes&quot;&gt;Encoding Labels with Anchor Boxes&lt;/h2&gt;

&lt;p&gt;In the YOLO (You Only Look Once) algorithm, anchor boxes are integrated into the label encoding process. Previously, the label vector for each grid cell consisted of object presence (PC), bounding box coordinates (PX, PY, PH, PW), and class probabilities (C1, C2, C3). However, with anchor boxes, the label vector expands to include two sets of parameters for each anchor box.&lt;/p&gt;

&lt;p&gt;Now, the label vector for each grid cell becomes 16-dimensional (8 dimensions for each anchor box). It can be represented as follows:&lt;/p&gt;

&lt;p&gt;Y = [PC1, PX1, PY1, PH1, PW1, C11, C21, C31, PC2, PX2, PY2, PH2, PW2, C12, C22, C32]&amp;lt;pre&amp;gt;&amp;lt;div class=&quot;bg-black rounded-md mb-4&quot;&amp;gt;&amp;lt;div class=&quot;flex items-center relative text-gray-200 bg-gray-800 px-4 py-2 text-xs font-sans justify-between rounded-t-md&quot;&amp;gt;&lt;br class=&quot;Apple-interchange-newline&quot; /&gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/pre&amp;gt;&lt;/p&gt;

&lt;p&gt;Each anchor box is associated with a unique set of parameters. For example, anchor box 1 might be suitable for tall and skinny objects, while anchor box 2 might be more suitable for wide and fat objects. The object detection algorithm assigns objects to grid cells based on the anchor box that has the highest Intersection over Union (IoU) with the object’s shape.&lt;/p&gt;

&lt;h2 id=&quot;object-assignment-with-anchor-boxes&quot;&gt;Object Assignment with Anchor Boxes&lt;/h2&gt;

&lt;p&gt;When processing an image, the algorithm assigns objects to the grid cell and anchor box pair that provides the best fit for the object’s shape. The assignment is based on calculating the IoU between each anchor box and the object’s ground truth bounding box. The anchor box with the highest IoU is chosen for detection.&lt;/p&gt;

&lt;p&gt;Once the anchor box is selected, the algorithm encodes the object’s presence (PC), the bounding box coordinates (PX, PY, PH, PW), and the class probabilities (C1, C2, C3) into the corresponding positions in the label vector. This process is repeated for all objects in the image.&lt;/p&gt;

&lt;h2 id=&quot;handling-ambiguous-cases&quot;&gt;Handling Ambiguous Cases&lt;/h2&gt;

&lt;p&gt;While anchor boxes offer significant improvements, there are some cases that require careful consideration. For instance, when multiple objects share the same grid cell, or when multiple objects have similar anchor box shapes, conflicts can arise. In such situations, tiebreaking rules or default strategies are often applied to resolve the conflicts and assign objects to the appropriate grid cell and anchor box pair.&lt;/p&gt;

&lt;h2 id=&quot;choosing-anchor-boxes&quot;&gt;Choosing Anchor Boxes&lt;/h2&gt;

&lt;p&gt;Choosing anchor boxes can be done manually or through more advanced techniques. In manual selection, experts analyze the dataset and select anchor boxes that cover a range of object shapes and aspect ratios. These anchor boxes should represent the typical characteristics of the objects expected to be detected.&lt;/p&gt;

&lt;p&gt;Alternatively, advanced methods like K-means clustering can be employed to automatically determine anchor box shapes. This involves grouping together object shapes that are commonly observed in the dataset. By using this approach, anchor boxes that are most representative of the object shapes can be selected, resulting in improved detection performance.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Anchor boxes have revolutionized the field of object detection by enabling the detection of multiple objects within a single grid cell. By associating predefined bounding box shapes with grid cells, anchor boxes enhance the accuracy, flexibility, and specialization of object detection algorithms. Whether chosen manually or through advanced techniques, anchor boxes are a crucial component in empowering object detection algorithms to deliver more precise and reliable results.&lt;/p&gt;
</description>
        <pubDate>Thu, 01 Jun 2023 00:00:00 +0000</pubDate>
        <link>https://danghoangnhan.github.io/anchorboxes/</link>
        <guid isPermaLink="true">https://danghoangnhan.github.io/anchorboxes/</guid>
        
        
        <category>deep-learning</category>
        
        <category>cnn</category>
        
        <category>computer-vision</category>
        
      </item>
    
      <item>
        <title>Data Augmentation</title>
        <description>&lt;hr /&gt;

&lt;h1 id=&quot;deep-learning-for-computer-vision-navigating-the-landscape&quot;&gt;Deep Learning for Computer Vision: Navigating the Landscape&lt;/h1&gt;

&lt;p&gt;Deep learning has made significant advancements in various domains such as computer vision, natural language processing, speech recognition, online advertising, and logistics. However, when it comes to computer vision, there are unique challenges and considerations that researchers and practitioners must navigate. In this article, we explore some observations and insights shared in a speech about deep learning for computer vision, aiming to provide guidance in understanding the literature and building effective computer vision systems.&lt;/p&gt;

&lt;h2 id=&quot;the-spectrum-of-data-availability&quot;&gt;The Spectrum of Data Availability&lt;/h2&gt;

&lt;p&gt;Machine learning problems can be categorized based on the amount of available data. For instance, speech recognition has relatively abundant datasets compared to the complexity of the problem. On the other hand, image recognition or classification, which involves analyzing pixel-level information, still requires more data to achieve optimal performance. Object detection, a task that involves identifying and locating objects within images, often faces an even greater scarcity of data due to the cost of labeling objects and bounding boxes.&lt;/p&gt;

&lt;h2 id=&quot;the-role-of-data-and-hand-engineering&quot;&gt;The Role of Data and Hand-Engineering&lt;/h2&gt;

&lt;p&gt;When it comes to training machine learning models for computer vision tasks, data and hand-engineering play crucial roles in achieving optimal performance. The sources of knowledge can be categorized into two main types:&lt;/p&gt;

&lt;h3 id=&quot;1-labeled-data&quot;&gt;1. Labeled Data&lt;/h3&gt;

&lt;p&gt;Labeled data serves as a direct source of information for supervised learning algorithms. It consists of pairs of input data (x) and corresponding labels (y), allowing the algorithm to learn patterns and make accurate predictions. The availability of labeled data is essential for training machine learning models effectively. In the context of computer vision, the speaker emphasizes that despite the existence of reasonably large datasets for image recognition or classification, the demand for more labeled data remains prevalent. Analyzing vast amounts of pixel information and identifying objects accurately necessitates substantial labeled data, which is often limited in the field of computer vision.&lt;/p&gt;

&lt;h3 id=&quot;2-hand-engineering&quot;&gt;2. Hand-Engineering&lt;/h3&gt;

&lt;p&gt;Hand-engineering refers to the process of designing features, network architectures, and other components of the system manually. In scenarios where abundant labeled data is not available, hand-engineering becomes crucial in compensating for the scarcity of data. It involves leveraging domain knowledge, intuition, and expertise to extract relevant features and design effective network architectures. Hand-engineering plays a significant role in achieving good performance when data is limited, as it allows the model to learn from the available data more efficiently.&lt;/p&gt;

&lt;h2 id=&quot;challenges-in-computer-vision&quot;&gt;Challenges in Computer Vision&lt;/h2&gt;

&lt;p&gt;Computer vision tasks entail learning complex functions, and even with the growing size of datasets, data scarcity remains a challenge. As a result, computer vision has historically relied heavily on hand-engineering. Complex network architectures have emerged to overcome the limitations of data availability. These intricate architectures often require meticulous hyperparameter tuning and exhibit more complexity compared to other domains. Object detection, with its smaller datasets, demands even more specialized components and algorithms.&lt;/p&gt;

&lt;h2 id=&quot;leveraging-transfer-learning&quot;&gt;Leveraging Transfer Learning&lt;/h2&gt;

&lt;p&gt;Transfer learning is a valuable technique in computer vision, especially when working with limited data. It involves utilizing pre-trained models on related tasks to boost performance on the target task. Transfer learning has shown significant improvements in areas such as object detection, where limited labeled data is available. By leveraging pre-existing knowledge captured in pre-trained models, the performance on the target task can be enhanced.&lt;/p&gt;

&lt;h2 id=&quot;balancing-benchmarks-and-real-world-applications&quot;&gt;Balancing Benchmarks and Real-World Applications&lt;/h2&gt;

&lt;p&gt;The computer vision community places significant emphasis on achieving high performance on standardized benchmark datasets and winning competitions. While this focus helps identify effective algorithms, it sometimes leads to techniques that are not suitable for real-world applications. Here are some tips to navigate benchmarks effectively:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Ensembling&lt;/strong&gt; : Ensembling involves training multiple neural networks independently and averaging their outputs. It is a technique commonly used to improve benchmark performance. However, due to increased computational requirements, it is rarely employed in production systems.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Multi-Crop at Test Time&lt;/strong&gt; : Multi-crop at test time is a technique involving data augmentation during testing. It can enhance performance on benchmarks but may significantly impact runtime and is not commonly used in production systems.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These techniques should be carefully considered, keeping in mind the trade-offs between benchmark performance and real-world applicability.&lt;/p&gt;

&lt;h2 id=&quot;leveraging-existing-architectures&quot;&gt;Leveraging Existing Architectures&lt;/h2&gt;

&lt;p&gt;When building production systems, leveraging pre-existing neural network architectures can be advantageous. Open-source implementations provide ready-to-use architectures that can be fine-tuned on specific datasets, saving time and effort. These pre-trained models have often undergone extensive training on large datasets, allowing for a faster start in real-world applications. However, training networks from scratch is still an option for those with sufficient computational resources and expertise.&lt;/p&gt;

&lt;p&gt;In conclusion, deep learning has revolutionized computer vision, but challenges persist due to limited data availability. Hand-engineering and specialized network architectures have been instrumental in compensating for data scarcity. Techniques like transfer learning and leveraging pre-trained models can significantly improve performance in small data regimes. While achieving high benchmarks is important, it is crucial to differentiate between techniques suitable for competitions and those applicable to real-world production systems.&lt;/p&gt;

&lt;p&gt;Understanding the nuances and techniques specific to computer vision enables researchers and practitioners to navigate the field effectively and build robust computer vision systems.&lt;/p&gt;
</description>
        <pubDate>Sat, 15 Apr 2023 00:00:00 +0000</pubDate>
        <link>https://danghoangnhan.github.io/StateofComputerVision/</link>
        <guid isPermaLink="true">https://danghoangnhan.github.io/StateofComputerVision/</guid>
        
        
        <category>deep-learning</category>
        
        <category>cnn</category>
        
        <category>computer-vision</category>
        
      </item>
    
      <item>
        <title>The Importance of Data Augmentation in Computer Vision</title>
        <description>&lt;p&gt;Data augmentation is a crucial technique used to improve the performance of computer vision systems. In the realm of computer vision, where the input is an image composed of countless pixels, the task of understanding the contents of an image can be quite complex. It often requires learning intricate functions to accurately recognize objects or patterns within the image.&lt;/p&gt;

&lt;p&gt;Unlike some other domains, computer vision typically faces a constant need for more data. In the majority of computer vision problems, obtaining more data is highly beneficial. This is where data augmentation comes into play. Whether you are using transfer learning or training a model from scratch, data augmentation can significantly enhance the training process.&lt;/p&gt;

&lt;p&gt;Let’s explore some common data augmentation techniques in computer vision:&lt;/p&gt;

&lt;h2 id=&quot;geometric-transformations&quot;&gt;Geometric Transformations&lt;/h2&gt;

&lt;p&gt;Geometric transformations involve altering the spatial configuration of an image. These transformations help the model become invariant to changes in orientation, scale, or position of objects within the image.&lt;/p&gt;

&lt;h3 id=&quot;mirroring&quot;&gt;Mirroring&lt;/h3&gt;

&lt;p&gt;Mirroring involves flipping an image horizontally or vertically to generate new training examples. For example, if your training set includes an image of a cat, you can create a mirrored version of the same image by flipping it horizontally. This technique can help the model become invariant to the orientation of objects.&lt;/p&gt;

&lt;h3 id=&quot;random-cropping&quot;&gt;Random Cropping&lt;/h3&gt;

&lt;p&gt;Random cropping entails selecting random subsets or crops from an image. This technique allows the model to learn from different parts of the image, enabling it to handle variations in object placement or scale. For instance, if you have an image of a landscape with a cat in the center, you can create multiple random crops that focus on different parts of the image, such as the cat, the background, or both.&lt;/p&gt;

&lt;h2 id=&quot;color-shifting&quot;&gt;Color Shifting&lt;/h2&gt;

&lt;p&gt;Color shifting techniques involve modifying the color distribution of an image to make the model more robust to variations in lighting conditions or color variations in real-world scenarios.Color shifting is another widely used data augmentation technique:&lt;/p&gt;

&lt;h3 id=&quot;distorting-color-channels&quot;&gt;Distorting Color Channels&lt;/h3&gt;

&lt;p&gt;This technique involves adding or subtracting values from the red, green, and blue (RGB) channels of an image. By distorting the color channels, the model becomes more robust to changes in lighting conditions or color variations that may occur in real-world scenarios. For example, you can distort the color channels of an image by adding more red and blue while subtracting from the green channel. This can simulate variations in lighting conditions or changes in the color temperature.&lt;/p&gt;

&lt;h3 id=&quot;pca-color-augmentation&quot;&gt;PCA Color Augmentation&lt;/h3&gt;

&lt;p&gt;PCA Color Augmentation is a technique that utilizes Principles Component Analysis (PCA) to sample values for color distortion. It balances color components while keeping the overall color tint intact. This approach helps the model become invariant to variations in color distribution without affecting the object or content being recognized in the image.&lt;/p&gt;

&lt;p&gt;Implementing data augmentation typically involves a separate thread responsible for loading images from storage and applying the desired transformations. This process can be parallelized with the training process, which can take place on either the CPU or GPU.&lt;/p&gt;

&lt;p&gt;During training, it is common to implement the desired distortions on the fly. This involves having a separate thread responsible for loading images from storage and applying the selected transformations. The distorted images are then passed to the training process, which can take place on either the CPU or GPU. By implementing distortions during training, the model learns to generalize better and becomes more robust to real-world variations.&lt;/p&gt;

&lt;p&gt;To achieve the best results, it’s important to experiment with different hyperparameters for data augmentation. While starting with existing open-source implementations is recommended, customizing the hyperparameters based on specific requirements can capture more invariances and yield better performance.&lt;/p&gt;

&lt;p&gt;In conclusion, data augmentation plays a vital role in enhancing the performance of computer vision models. By generating additional training examples and introducing variations in the data, models become more capable of handling real-world scenarios and improve their generalization capability. Incorporating data augmentation into the training workflow is essential for building robust and accurate computer vision systems.&lt;/p&gt;
</description>
        <pubDate>Fri, 14 Apr 2023 00:00:00 +0000</pubDate>
        <link>https://danghoangnhan.github.io/DataAugmentation/</link>
        <guid isPermaLink="true">https://danghoangnhan.github.io/DataAugmentation/</guid>
        
        
        <category>deep-learning</category>
        
        <category>cnn</category>
        
        <category>computer-vision</category>
        
      </item>
    
      <item>
        <title>Transfer Learning: Accelerating Computer Vision Applications with Pre-Trained Models</title>
        <description>&lt;p&gt;When building computer vision applications, leveraging pre-trained models can significantly speed up the development process and yield faster progress. Instead of training a neural network from scratch, you can download pre-trained weights and use transfer learning to adapt the model to your specific task. In this blog post, we will explore the concept of transfer learning and how it can be applied to computer vision tasks for efficient and effective results.&lt;/p&gt;

&lt;h3 id=&quot;the-power-of-pre-trained-models&quot;&gt;The Power of Pre-Trained Models&lt;/h3&gt;

&lt;p&gt;The computer vision research community has made significant contributions by posting numerous datasets online, such as ImageNet, MS COCO, and Pascal, which serve as valuable resources for training neural networks. These datasets have been used to train models with high-performance accuracy over several weeks or months, utilizing powerful GPUs and complex optimization techniques. By downloading these pre-trained models, you can benefit from the knowledge and expertise that went into their creation, saving you time and effort.&lt;/p&gt;

&lt;h3 id=&quot;transfer-learning-for-custom-tasks&quot;&gt;Transfer Learning for Custom Tasks&lt;/h3&gt;

&lt;p&gt;Let’s consider an example where you want to build a cat detector to recognize your own pet cat. Assuming you have a limited number of images of your cats, you might encounter a data scarcity issue. In such cases, transfer learning becomes invaluable. You can download a pre-trained neural network along with its weights, originally trained on a large dataset like ImageNet, which contains a thousand different classes.&lt;/p&gt;

&lt;p&gt;To adapt the pre-trained model to your specific task, you need to modify the output layer. Instead of predicting one of the thousand classes, you create a new softmax layer with three possible outputs: Tigger, Misty, or neither. The key idea is to freeze the parameters of all the earlier layers in the network and only train the parameters associated with your softmax layer.&lt;/p&gt;

&lt;h3 id=&quot;freezing-and-training-layers&quot;&gt;Freezing and Training Layers&lt;/h3&gt;

&lt;p&gt;Most deep learning frameworks support freezing layers, allowing you to specify which layers’ weights should remain unchanged during training. By freezing earlier layers, which have learned general features, you leverage their fixed functionality to compute feature vectors for input images. These feature vectors are then used as input for training a shallow softmax model that predicts the desired classes.&lt;/p&gt;

&lt;p&gt;To speed up training, you can pre-compute the activations of these frozen layers for all examples in the training set and save them to disk. This way, you don’t need to recalculate the activations for every epoch or pass through the training set, resulting in more efficient computations.&lt;/p&gt;

&lt;h3 id=&quot;adapting-to-different-dataset-sizes&quot;&gt;Adapting to Different Dataset Sizes&lt;/h3&gt;

&lt;p&gt;The amount of labeled data you have for your task plays a crucial role in determining the extent of transfer learning. If you have a small training set, freezing more layers and training only a few parameters might suffice. However, with a larger labeled dataset, you can consider freezing fewer layers and training more parameters in the network.&lt;/p&gt;

&lt;p&gt;For datasets with ample data, you can even replace the last few layers or add your own hidden units, followed by the softmax outputs. This allows you to fine-tune the network’s higher-level representations to better suit your specific task.&lt;/p&gt;

&lt;h3 id=&quot;embracing-transfer-learning-in-computer-vision&quot;&gt;Embracing Transfer Learning in Computer Vision&lt;/h3&gt;

&lt;p&gt;Transfer learning has proven to be highly effective in computer vision applications. By utilizing pre-trained models, you can leverage the knowledge gained from extensive training on large datasets. This approach significantly improves performance, especially when you have limited labeled data or computational resources.&lt;/p&gt;

&lt;p&gt;Unless you have an exceptionally large dataset and sufficient computational resources to train from scratch, transfer learning is an approach worth considering in computer vision. It allows you to benefit from existing expertise, save valuable time, and achieve competitive results with smaller training sets.&lt;/p&gt;

&lt;p&gt;In conclusion, transfer learning offers a powerful strategy to accelerate the development of computer vision applications. By harnessing pre-trained models, you can efficiently adapt them to your specific tasks, overcoming data limitations and achieving impressive results. Embrace transfer learning and unlock the potential of computer vision in&lt;/p&gt;

&lt;p&gt;your projects.&lt;/p&gt;
</description>
        <pubDate>Thu, 13 Apr 2023 00:00:00 +0000</pubDate>
        <link>https://danghoangnhan.github.io/TransferLearning/</link>
        <guid isPermaLink="true">https://danghoangnhan.github.io/TransferLearning/</guid>
        
        
        <category>deep-learning</category>
        
        <category>cnn</category>
        
        <category>computer-vision</category>
        
      </item>
    
      <item>
        <title>EfficientNet: Scaling Neural Networks for Optimal Performance</title>
        <description>
</description>
        <pubDate>Wed, 12 Apr 2023 00:00:00 +0000</pubDate>
        <link>https://danghoangnhan.github.io/Using-Open-Source-Implementation/</link>
        <guid isPermaLink="true">https://danghoangnhan.github.io/Using-Open-Source-Implementation/</guid>
        
        
        <category>deep-learning</category>
        
        <category>cnn</category>
        
        <category>computer-vision</category>
        
      </item>
    
  </channel>
</rss>