<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://abidikhairi.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://abidikhairi.github.io/" rel="alternate" type="text/html" /><updated>2026-09-19T19:20:46+00:00</updated><id>https://abidikhairi.github.io/feed.xml</id><title type="html">Khairi Abidi | LLM / AI Engineer &amp;amp; NLP for Bioinformatics Researcher</title><subtitle>AI/LLM engineer with experience shipping production ML and data systems, leading engineering teams, and researching protein language models for bioinformatics.</subtitle><author><name>Khairi Abidi</name></author><entry><title type="html">KCNG Explorer: Probing a Protein Language Model on the ‘Silent’ Kv6 Channels</title><link href="https://abidikhairi.github.io/blog/kcng-explorer/" rel="alternate" type="text/html" title="KCNG Explorer: Probing a Protein Language Model on the ‘Silent’ Kv6 Channels" /><published>2026-09-19T00:00:00+00:00</published><updated>2026-09-19T00:00:00+00:00</updated><id>https://abidikhairi.github.io/blog/kcng-explorer</id><content type="html" xml:base="https://abidikhairi.github.io/blog/kcng-explorer/"><![CDATA[<p>I just shipped <a href="https://huggingface.co/spaces/khairi/KCNG-Explorer">KCNG Explorer</a>,
a small research tool for poking at the <strong>KCNG</strong> subfamily of voltage-gated
potassium channel subunits — KCNG1 through KCNG4, better known as Kv6.1–Kv6.4.
They’re an odd corner of the Kv family: on their own they don’t form a
functional channel at all. They’re “silent” subunits that only work as
heterotetramers with Kv2, modulating its gating rather than conducting current
by themselves. That makes them a nice, self-contained test case for asking
what a protein language model actually understands about a sequence.</p>

<h2 id="what-it-does">What it does</h2>

<p>KCNG Explorer wraps a pretrained ESM Cambrian (ESMC) masked language model
around a focused workflow:</p>

<ul>
  <li><strong>Load a sequence</strong> — fetch directly by UniProt accession, or paste your own
FASTA.</li>
  <li><strong>Score every point mutation</strong> — for the loaded sequence, the app computes a
full log-likelihood-ratio (LLR) matrix over all 19 alternative residues at
every position, so you get the model’s implied fitness landscape for the
whole protein in one pass.</li>
  <li><strong>Cross-check against real variants</strong> — those LLR scores are joined against
UniProt’s annotated natural variants (skipping any where the wild-type
residue doesn’t match the given sequence, e.g. a mismatched isoform), so you
can see whether the model’s notion of “tolerated” vs. “disruptive” lines up
with what’s actually observed in nature.</li>
  <li><strong>Look at the structure</strong> — the same protein is rendered in 3D, preferring
an experimental PDB structure via UniProt’s cross-references and falling
back to an AlphaFold DB prediction (fetched live from AlphaFold’s API,
since model versions get republished over time) when no experimental
structure exists.</li>
</ul>

<p>Because the mutation matrix is <code class="language-plaintext highlighter-rouge">19 × sequence length</code> model calls, it’s cached
per-session and only recomputed when the active sequence actually changes —
everything else (variant table, heatmap, structure viewer) just re-renders off
whatever’s already in state.</p>

<h2 id="from-streamlit-prototype-to-a-gradio-space">From Streamlit prototype to a Gradio Space</h2>

<p>The app started life as a Streamlit prototype and got ported to Gradio so it
could be deployed as a Hugging Face Space with GPU access (<code class="language-plaintext highlighter-rouge">@spaces.GPU</code>,
ZeroGPU-backed). Both UIs still live side by side in the repo, following the
same state-resolution pattern — a sidebar action resolves to
<code class="language-plaintext highlighter-rouge">(sequence, accession, matrix, matrix_sequence)</code>, and a single render function
turns that into every panel. Keeping resolution and rendering separate is what
let the migration be mostly a matter of swapping <code class="language-plaintext highlighter-rouge">st.session_state</code> for
<code class="language-plaintext highlighter-rouge">gr.State</code> rather than rewriting the app logic.</p>

<p>The less glamorous part of shipping it was the dependency graph. <code class="language-plaintext highlighter-rouge">esm &gt;= 3.4.0</code>
is a hard floor for the ESMC wrapper API this app needs, which drags in
<code class="language-plaintext highlighter-rouge">transformers &lt; 5.0</code>, which caps <code class="language-plaintext highlighter-rouge">huggingface-hub &lt; 1.0</code> — and Gradio raised
its own <code class="language-plaintext highlighter-rouge">huggingface-hub</code> floor above that starting at 6.18.0. So the Space is
deliberately pinned to <code class="language-plaintext highlighter-rouge">gradio==6.17.3</code>, the newest release where that
three-way constraint is still satisfiable at all. Fun way to relearn that
“just bump the version” isn’t always an option.</p>

<h2 id="try-it">Try it</h2>

<p>The Space is live at
<a href="https://huggingface.co/spaces/khairi/KCNG-Explorer">huggingface.co/spaces/khairi/KCNG-Explorer</a> —
load any KCNG1–KCNG4 accession and see where the model agrees (or disagrees)
with nature on what this “silent” channel family can tolerate.</p>]]></content><author><name>Khairi Abidi</name></author><category term="protein-language-models" /><category term="bioinformatics" /><category term="gradio" /><summary type="html"><![CDATA[I just shipped KCNG Explorer, a small research tool for poking at the KCNG subfamily of voltage-gated potassium channel subunits — KCNG1 through KCNG4, better known as Kv6.1–Kv6.4. They’re an odd corner of the Kv family: on their own they don’t form a functional channel at all. They’re “silent” subunits that only work as heterotetramers with Kv2, modulating its gating rather than conducting current by themselves. That makes them a nice, self-contained test case for asking what a protein language model actually understands about a sequence.]]></summary></entry><entry><title type="html">Welcome to My Blog</title><link href="https://abidikhairi.github.io/blog/welcome-to-my-blog/" rel="alternate" type="text/html" title="Welcome to My Blog" /><published>2026-09-19T00:00:00+00:00</published><updated>2026-09-19T00:00:00+00:00</updated><id>https://abidikhairi.github.io/blog/welcome-to-my-blog</id><content type="html" xml:base="https://abidikhairi.github.io/blog/welcome-to-my-blog/"><![CDATA[<p>This is the first post on my blog. To add a new one, drop a Markdown file
into <code class="language-plaintext highlighter-rouge">_posts/</code> named <code class="language-plaintext highlighter-rouge">YYYY-MM-DD-title.md</code> with front matter like this one -
Jekyll picks it up automatically and it shows up on the <a href="/blog/">Blog</a> page.</p>]]></content><author><name>Khairi Abidi</name></author><category term="meta" /><summary type="html"><![CDATA[This is the first post on my blog. To add a new one, drop a Markdown file into _posts/ named YYYY-MM-DD-title.md with front matter like this one - Jekyll picks it up automatically and it shows up on the Blog page.]]></summary></entry></feed>