<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[The Crow's Nest]]></title><description><![CDATA[Your Argument Is Invalid - My Programmer Is A Bird]]></description><link>https://blog.kinglycrow.com/</link><image><url>https://blog.kinglycrow.com/favicon.png</url><title>The Crow&apos;s Nest</title><link>https://blog.kinglycrow.com/</link></image><generator>Ghost 5.47</generator><lastBuildDate>Fri, 10 Apr 2026 20:38:17 GMT</lastBuildDate><atom:link href="https://blog.kinglycrow.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[No Skill. No Taste.]]></title><description><![CDATA[<figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kinglycrow.com/content/images/2026/02/Gemini_Generated_Image_fgdy61fgdy61fgdy.png" class="kg-image" alt="Post title, Gartner magic quadrant show casing skill and taste and joke HN post about a todo app" loading="lazy" width="2000" height="1091" srcset="https://blog.kinglycrow.com/content/images/size/w600/2026/02/Gemini_Generated_Image_fgdy61fgdy61fgdy.png 600w, https://blog.kinglycrow.com/content/images/size/w1000/2026/02/Gemini_Generated_Image_fgdy61fgdy61fgdy.png 1000w, https://blog.kinglycrow.com/content/images/size/w1600/2026/02/Gemini_Generated_Image_fgdy61fgdy61fgdy.png 1600w, https://blog.kinglycrow.com/content/images/2026/02/Gemini_Generated_Image_fgdy61fgdy61fgdy.png 2000w" sizes="(min-width: 720px) 720px"><figcaption>Ironic image generated with nano banana to showcase my point.&#xA0;</figcaption></figure><p><br>I was reading a <a href="https://news.ycombinator.com/item?id=47077036&amp;ref=blog.kinglycrow.com">thread on HN</a> and I started writing this super long comment and rewriting and editing and thought, hey, if I&apos;m doing this I clearly care enough about the state of Show HN and</p>]]></description><link>https://blog.kinglycrow.com/no-skill-no-taste/</link><guid isPermaLink="false">6997ee0e640d540073484140</guid><category><![CDATA[LLMs]]></category><category><![CDATA[Opinion]]></category><dc:creator><![CDATA[crow]]></dc:creator><pubDate>Fri, 20 Feb 2026 06:00:21 GMT</pubDate><media:content url="https://blog.kinglycrow.com/content/images/2026/02/Gemini_Generated_Image_fgdy61fgdy61fgdy.png" medium="image"/><content:encoded><![CDATA[<figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kinglycrow.com/content/images/2026/02/Gemini_Generated_Image_fgdy61fgdy61fgdy.png" class="kg-image" alt="No Skill. No Taste." loading="lazy" width="2000" height="1091" srcset="https://blog.kinglycrow.com/content/images/size/w600/2026/02/Gemini_Generated_Image_fgdy61fgdy61fgdy.png 600w, https://blog.kinglycrow.com/content/images/size/w1000/2026/02/Gemini_Generated_Image_fgdy61fgdy61fgdy.png 1000w, https://blog.kinglycrow.com/content/images/size/w1600/2026/02/Gemini_Generated_Image_fgdy61fgdy61fgdy.png 1600w, https://blog.kinglycrow.com/content/images/2026/02/Gemini_Generated_Image_fgdy61fgdy61fgdy.png 2000w" sizes="(min-width: 720px) 720px"><figcaption>Ironic image generated with nano banana to showcase my point.&#xA0;</figcaption></figure><img src="https://blog.kinglycrow.com/content/images/2026/02/Gemini_Generated_Image_fgdy61fgdy61fgdy.png" alt="No Skill. No Taste."><p><br>I was reading a <a href="https://news.ycombinator.com/item?id=47077036&amp;ref=blog.kinglycrow.com">thread on HN</a> and I started writing this super long comment and rewriting and editing and thought, hey, if I&apos;m doing this I clearly care enough about the state of Show HN and HN in general to write a post on it. I&apos;ve written code since I was 11. I&apos;ve worked on larger distributed systems, web apps, databases, search and more. I have many opinions on the transformation of our profession that is currently underway. Most of all, there is now an illusion of a lower barrier to entry. There is a magic quadrant made up of taste and skill. And too many people over estimate their taste[0] and their skill (or never care in the first place).<br><br>LLMs have people everywhere super excited they can finally build their dream applications! The only problem is, no one needs their dream application. We see it everyday now, someone posts some obvious vibe coded app which is poorly crafted and clearly derivative of an idea so thoroughly saturated it&apos;s literally leaking. This is the lowest part of the quadrant. No skill and no taste. The overall suffusion of this into the broader scene rightly has the more sensitive of us up in arms. It&apos;s noise, it&apos;s spam, it&apos;s a perversion of the years of skill we&apos;ve spent accruing. <br></p><p><br>The only problem there is you might have skill, but do you have taste? This problem itself isn&apos;t new. HN of all places has always been a matter of taste. Things people found interesting made it to the front page, things they did not languished. You could build the most finely abstracted todo app of all time and your app would be dead on arrival. However, if you built something that resonated with a large enough group of people it never mattered how well built the app was or how technically complex.</p><p> &#xA0;</p><p>I&apos;ve seen plenty of content on HN that could not have been more than a simple crud app that rocketed to the front page. What comes to mind immediately was a <a href="https://en.wikipedia.org/wiki/This_Website_Will_Self-destruct?ref=blog.kinglycrow.com">little app</a> that died if someone hadn&apos;t posted a message on it in 24 hours. Inherently simple, but quite popular. It was pure taste.</p><p> &#xA0;</p><p>Taste and skill are related, the more saturated something is the higher skill you need to cross the taste threshold to make people care. It&apos;s not that there will never be another interesting todo app, it&apos;s that it has to be so tasteful as to cross our maximal standards and pre-existing expectations of them.<br><br>LLMs have exposed this more thoroughly than any other time in tech so far. The sin isn&apos;t that someone uses an LLM to generate an application[1], vibe[2] or not. The sin is they lacked enough skill and enough taste to cross the actual threshold the rest of us need to see for the work to not be slop. <br><br></p><p>An obvious and recent example of this is OpenClaw. It is a bit of a software nightmare (sorry Peter, I know you&apos;re good), but it&apos;s highly tasteful even being pretty vibey. People ate it up immediately and because there was such an interest the lack of technical soundness and security was overlooked (or begrudgingly put up with)</p><p><br><br>The lack of taste only presents a problem now, because it&apos;s so much easier for people who thought they have more taste than they actually do to post every little idea they have. This is a real problem and I think it will taper off because people will learn proper etiquette or face disappointment. It&apos;s a massive educational period for a lot of people that we&apos;ve all had years to internalize.</p><p><br><br>It has the same stink of crypto on it right now that anyone can get rich. Most of them won&apos;t. This is the illusion of the lower barrier of entry, the barrier has always been taste and LLMs do nothing to remove this barrier. They amplify it.</p><p><br><br>Anyway this is all to say whether you have skill or not, you better learn to be tasteful before you decide to slop all over everyone.</p><p><br><br>[0] Taste is totally dependent on the group you&apos;re building for, discerning whether you have good taste and to whom is totally a process where you do have to put things out to people, but the bar has not now and not in my years ever been on the floor so I assert there&apos;s a minimal universal taste we all have and you should at least clear that before putting things out there.</p><p>[1] I&apos;ve been writing code for 20 years, I am super experienced in my domains and I review and sand off the edges, make changes myself etc. I vibe code almost 0% of the time.</p><p>[2] Vibing means you need to have exceptional taste to cross the bar. I don&apos;t care if you do it, but you need to own the outcome.<br><br></p>]]></content:encoded></item><item><title><![CDATA[Finetuning RWKV 14bn with QLORA in 4Bit]]></title><description><![CDATA[<p>It was surprisingly easy to get this working, and I think that&apos;s a good thing.</p><p>First I looked at existing LORA implementations of RWKV which I discovered from the very helpful <a href="https://discord.gg/bDSBUMeFpc?ref=blog.kinglycrow.com">RWKV Discord</a>. The link I found in the discord landed me at <a href="https://mysymphony.jp.net/a/rwkv-character?ref=blog.kinglycrow.com">&quot;How to Train Your</a></p>]]></description><link>https://blog.kinglycrow.com/rwkv-qlora-4bit/</link><guid isPermaLink="false">64710e15e8572f01717bf9a7</guid><category><![CDATA[LLMs]]></category><category><![CDATA[NLP]]></category><category><![CDATA[RWKV]]></category><dc:creator><![CDATA[crow]]></dc:creator><pubDate>Fri, 26 May 2023 23:41:49 GMT</pubDate><content:encoded><![CDATA[<p>It was surprisingly easy to get this working, and I think that&apos;s a good thing.</p><p>First I looked at existing LORA implementations of RWKV which I discovered from the very helpful <a href="https://discord.gg/bDSBUMeFpc?ref=blog.kinglycrow.com">RWKV Discord</a>. The link I found in the discord landed me at <a href="https://mysymphony.jp.net/a/rwkv-character?ref=blog.kinglycrow.com">&quot;How to Train Your Raven&quot;</a>, shout out to the author Nana. From that blog post I found the general lora <a href="https://github.com/Blealtan/RWKV-LM-LoRA/blob/main/RWKV-v4neo/src/model.py?ref=blog.kinglycrow.com">implementation from Blealtan</a>.</p><p>You can see starting on line 153 and going up to line 192 the modules they&apos;ve chosen to augment with LORA. Which are the linear attention and feed forward portions of the network, and more specifically they are later applied to the linear layers for receptance, key and value.</p><pre><code class="language-python3">class LoraLinear(nn.Module):

    def __init__(self, in_features: int, out_features: int, bias: bool):
        super().__init__()

        self.weight = nn.Parameter(torch.empty((out_features, in_features)))
        assert bias == False, &quot;Biased LoraLinear not supported&quot;

        r, alpha, dropout = LORA_CONFIG[&quot;r&quot;], LORA_CONFIG[
            &quot;alpha&quot;], LORA_CONFIG[&quot;dropout&quot;]
        self.lora_A = nn.Parameter(torch.empty(r, in_features))
        self.lora_B = nn.Parameter(torch.empty(out_features, r))
        self.lora_dropout = nn.Dropout(dropout)
        self.scaling = alpha / r

        nn.init.kaiming_uniform_(self.weight, a=math.sqrt(5))
        nn.init.kaiming_uniform_(self.lora_A, a=math.sqrt(5))
        nn.init.zeros_(self.lora_B)

    def forward(self, x):
        return (
            F.linear(x, self.weight) + self.scaling *
            F.linear(F.linear(self.lora_dropout(x), self.lora_A), self.lora_B))


@functools.wraps(LoraLinear)
def make_linear_att(*args, **kwargs):
    if &quot;att&quot; in LORA_CONFIG[&quot;parts&quot;] and LORA_CONFIG[&quot;r&quot;] &gt; 0:
        return LoraLinear(*args, **kwargs)
    else:
        return nn.Linear(*args, **kwargs)


@functools.wraps(LoraLinear)
def make_linear_ffn(*args, **kwargs):
    if &quot;ffn&quot; in LORA_CONFIG[&quot;parts&quot;] and LORA_CONFIG[&quot;r&quot;] &gt; 0:
        return LoraLinear(*args, **kwargs)
    else:
        return nn.Linear(*args, **kwargs)
</code></pre><p>After looking at the RWKV paper and the model architecture, combined with this LORA implementation I figured that these were the correct modules to target.</p><p>Recently I have implemented my own fine tuning library on top of Transformers, Accelerate and PEFT which is <a href="https://github.com/iantbutler01/ditty?ref=blog.kinglycrow.com">iantbutler01/ditty</a>, and the rest of this was simply integrating QLORA into Ditty under an experimental flag.</p><h2 id="implementing-qlora-in-your-transformers-based-pipeline-using-peft">Implementing QLORA in your Transformers based Pipeline using PEFT</h2><p>You can install the development versions of Transformers, PEFT and Accelerate with the below snippet. I recommend doing this in a separate <a href="https://virtualenv.pypa.io/en/latest/?ref=blog.kinglycrow.com">virtualenv</a> so that potential breaking changes do not interfere with the other work that you may be doing!</p><figure class="kg-card kg-code-card"><pre><code>pip install -U git+https://github.com/huggingface/transformers.git 
pip install -U git+https://github.com/huggingface/peft.git
pip install -U git+https://github.com/huggingface/accelerate.git
pip install --upgrade bitsandbytes</code></pre><figcaption>Development versions are needed because this isn&apos;t stable yet!</figcaption></figure><p>After this you can follow the excellent <a href="https://huggingface.co/blog/4bit-transformers-bitsandbytes?ref=blog.kinglycrow.com">blog post</a> introducing the method from Huggingface, I&apos;ll show my implementation of this into Ditty as well with the few changes needed to support RWKV.</p><p>After checking some flags set in my library, I enable 4bit and pass the necessary config to Bitsandbytes per the HF post,</p><pre><code class="language-python3">  		if self.l8bit and self.l4bit:
            raise ValueError(&quot;Cannot set both l8bit and l4bit to True.&quot;)

        if self.l4bit and experimental:
            self.bnb_config = BitsAndBytesConfig(
                load_in_4bit=True,
                bnb_4bit_use_double_quant=True,
                bnb_4bit_quant_type=&quot;nf4&quot;,
                bnb_4bit_compute_dtype=torch.bfloat16,
            )
        elif self.l4bit and not experimental:
            raise ValueError(&quot;To use 4bit, `experimental` must be set to True.&quot;)
        elif self.l8bit:
            self.bnb_config = BitsAndBytesConfig(
                load_in_8bit=l8bit, llm_int8_enable_fp32_cpu_offload=fp32_cpu_offload
            )</code></pre><pre><code class="language-python3"># I pass the BNB config directly to the model loading step

self.model = AutoModelForCausalLM.from_pretrained(
            self.model_name_or_path,
            device_map=&quot;auto&quot;,
            quantization_config=self.bnb_config
        )</code></pre><p>To enable targeting of RWKV modules I first printed out the model to see how they are named in the Transformers version just in case they&apos;re different.</p><pre><code>RwkvForCausalLM(
  (rwkv): RwkvModel(
    (embeddings): Embedding(50277, 5120)
    (blocks): ModuleList(
      (0): RwkvBlock(
        (pre_ln): LayerNorm((5120,), eps=1e-05, elementwise_affine=True)
        (ln1): LayerNorm((5120,), eps=1e-05, elementwise_affine=True)
        (ln2): LayerNorm((5120,), eps=1e-05, elementwise_affine=True)
        (attention): RwkvSelfAttention(
          (time_shift): ZeroPad2d((0, 0, 1, -1))
          (key): Linear4bit(in_features=5120, out_features=5120, bias=False)
          (value): Linear4bit(in_features=5120, out_features=5120, bias=False)
          (receptance): Linear4bit(in_features=5120, out_features=5120, bias=False)
          (output): Linear4bit(in_features=5120, out_features=5120, bias=False)
        )
        (feed_forward): RwkvFeedForward(
          (time_shift): ZeroPad2d((0, 0, 1, -1))
          (key): Linear4bit(in_features=5120, out_features=20480, bias=False)
          (receptance): Linear4bit(in_features=5120, out_features=5120, bias=False)
          (value): Linear4bit(in_features=20480, out_features=5120, bias=False)
        )
      )
      (1-39): 39 x RwkvBlock(
        (ln1): LayerNorm((5120,), eps=1e-05, elementwise_affine=True)
        (ln2): LayerNorm((5120,), eps=1e-05, elementwise_affine=True)
        (attention): RwkvSelfAttention(
          (time_shift): ZeroPad2d((0, 0, 1, -1))
          (key): Linear4bit(in_features=5120, out_features=5120, bias=False)
          (value): Linear4bit(in_features=5120, out_features=5120, bias=False)
          (receptance): Linear4bit(in_features=5120, out_features=5120, bias=False)
          (output): Linear4bit(in_features=5120, out_features=5120, bias=False)
        )
        (feed_forward): RwkvFeedForward(
          (time_shift): ZeroPad2d((0, 0, 1, -1))
          (key): Linear4bit(in_features=5120, out_features=20480, bias=False)
          (receptance): Linear4bit(in_features=5120, out_features=5120, bias=False)
          (value): Linear4bit(in_features=20480, out_features=5120, bias=False)
        )
      )
    )
    (ln_out): LayerNorm((5120,), eps=1e-05, elementwise_affine=True)
  )
  (head): Linear(in_features=5120, out_features=50277, bias=False)
)</code></pre><p>So in this case we&apos;re looking at &apos;key&apos;, &apos;value&apos; and &apos;receptance&apos;. Then I added a few small changes to my existing pipeline in Ditty.</p><pre><code class="language-python3">        if &quot;gpt-neox&quot; in self.model_name_or_path:
            target_modules = [&quot;query_key_value&quot;, &quot;xxx&quot;]

        if &quot;rwkv&quot; in self.model_name_or_path:
            target_modules = [&quot;key&quot;, &quot;value&quot;, &quot;receptance&quot;]

        peft_config = LoraConfig(
            task_type=TaskType.CAUSAL_LM,
            target_modules=target_modules,
            inference_mode=False,
            r=8,
            lora_alpha=16,
            lora_dropout=0.05,
            bias=&quot;none&quot;
        )


        if self.l4bit:
            from peft import prepare_model_for_kbit_training
            self.model = prepare_model_for_kbit_training(
                self.model, use_gradient_checkpointing=self.gradient_checkpointing
            )
        elif self.l8bit:
            self.model = prepare_model_for_int8_training(
                self.model, use_gradient_checkpointing=self.gradient_checkpointing
            )</code></pre><p>Its worth calling out that I disabled gradient checkpointing, the first time I tried to run with it enabled it errored out so I left it off. I think it&apos;s worth investigating and seeing how this can be enabled however.</p><p>That&apos;s really all there is to it. I also have a few things like bfloat16 enabled for the training pipeline, you can see my configuration using a subclassed Ditty pipeline here:</p><pre><code>if __name__ == &quot;__main__&quot;:
    pipeline = RWKVPipeline(
        dataset_name=&quot;databricks/databricks-dolly-15k&quot;,
        model_name_or_path=&quot;RWKV/rwkv-raven-14b&quot;,
        gradient_checkpointing=False,
        block_size=512,
        grad_accum=32,
        batch_size=1,
        l4bit=True,
        l8bit=False,
        experimental=True,
        fp16=True,
        use_bfloat16=True
        
    )

    pipeline.run()</code></pre><p>The link to the implementation can be found <a href="https://github.com/iantbutler01/rwkv-raven-qlora-4bit-instruct/blob/main/main.py?ref=blog.kinglycrow.com">here</a>.</p><p>At the time of writing this the fine tuning process has finished but I have not yet evaluated the model for quality, I will update when I do!</p>]]></content:encoded></item><item><title><![CDATA[Trying to steer LLM output towards correctness using MIPS]]></title><description><![CDATA[What if you could find the candidate token that when combined with the previous outputs would produce the smallest cosine distance to the input prompt for each step of the generation, with the idea that this would align it more closely with the input prompt and prevent the model from going off task.]]></description><link>https://blog.kinglycrow.com/steering-llm-output-towards-correctness/</link><guid isPermaLink="false">645d96f3e8572f01717bf5af</guid><category><![CDATA[NLP]]></category><category><![CDATA[LLMs]]></category><dc:creator><![CDATA[crow]]></dc:creator><pubDate>Wed, 17 May 2023 02:37:49 GMT</pubDate><content:encoded><![CDATA[<h2 id="foreword">Foreword</h2><p>This is what has finally convinced me to launch a blog. I do a lot of programming, but I don&apos;t necessarily believe that the web needs yet another programmer talking about their development experiences or that my experiences generally merit being shared, but I&apos;ve been following the NLP space since 2016 and with how exciting of a time it is, I can&apos;t help but throw my hat in the ring. I hope you, dear reader, find this interesting if not useful. -KC</p><h2 id="what-if">What if...?</h2><p>Recently I began work on what people are referring to as a &quot;semi autonomous agent&quot; framework, <a href="https://github.com/iantbutler01/asimov?ref=blog.kinglycrow.com">Asimov</a>. I decided that though AutoGPT is cool, the ability to mix and match smaller open source models with proprietary models like GPT4 may yield better results. I also have different opinions on how these agent loops should be structured and how tasks can be achieved. While working on this I began to experiment with <a href="https://huggingface.co/databricks/dolly-v2-12b?ref=blog.kinglycrow.com">Dolly V2</a> a 12BN parameter instruct tuned model released by Databricks. Frankly, I found the small model to be quite capable but the moment you reached a little outside of the zone of tasks it was trained on it fell apart pretty quickly. </p><p>At the same time I began to look for ways to stabilize the generation of JSON from these models. While doing so I stumbled on <a href="Structural Alignment: Modifying Transformers (like GPT) to Follow a JSON Schema">Structural Alignment: Modifying Transformers (like GPT) to Follow a JSON Schema, or Clownfish</a> from Ben Newhouse. Ben achieved the ability to consistently output valid JSON by taking the logits (scores for a set of potential tokens before being converted to a proper probability) produced by an LLM and testing them against a streaming JSON parser for a valid token matching the current part of the JSON schema being asked for and then would pin all other logits to -infinity, very harshly steering the model away from other paths.</p><p>This sparked a thought in me. What if you could also do so with the prompt fed to the model? What if you could find the candidate token that when combined with the previous outputs would produce the smallest cosine distance to the input prompt for each step of the generation and selecting that candidate, with the idea that this would align it more closely with the input prompt and prevent the model from going off task. &#xA0;This is basically a form of MIPS or maximum inner product search but for choosing the next LLM output token instead of a document.</p><h2 id="first-try">First try!</h2><p>Given the input prompt and the current set of tokens generated from the model so far:</p><ul><li>Produce a document embedding for the prompt using the model&apos;s input embedding layer, by taking all token embeddings in the input prompt summing them and then dividing by the number of tokens in the prompt.</li><li>Produce embeddings for all of the tokens created so far. </li><li>Take the logits for the next set of potential tokens and iterate through them.</li><li>During the iteration, produce an embedding of the current iteration&apos;s candidate token</li><li>Sum the embeddings of all the tokens produced thus far and the embedding of the current candidate token.</li><li>Divide those summed vectors by the length of all previous tokens, plus one for the candidate token to produce a naive document embedding.</li><li>Take the cosine distance of the document embedding and the prompt embedding.</li><li>Take the candidate token with the smallest distance, and set the logits of all the others to -infinity using a mask.</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kinglycrow.com/content/images/2023/05/image.png" class="kg-image" alt loading="lazy" width="800" height="425" srcset="https://blog.kinglycrow.com/content/images/size/w600/2023/05/image.png 600w, https://blog.kinglycrow.com/content/images/2023/05/image.png 800w" sizes="(min-width: 720px) 720px"><figcaption>The original implementation</figcaption></figure><h3 id="first-problem-first-solution">First problem, first solution.</h3><p>Great, this was going to be so cool and really improve model output! Except... I forgot that each token generated by the model is actually a subword, LLMs generally use a byte pair encoding (BPE) and produce output at a subword level. Think &apos;a&apos; &apos;pp&apos; &apos;le&apos; and not &apos;apple&apos;. It&apos;s a little more complicated than that with respect to BPE, but that gets the general point across. What this means in practice is that even if the token minimizes the distance to the prompt it may not, when joined with other tokens, produce a coherent english word. And in fact, that is exactly what happened.</p><pre><code>Youmedscimonit unmist Syrac Gmb DeborPtrFromString&#xFFFD; enthus&#xFFFD;
</code></pre><p>That was the first set of tokens generated by the implementation above. You&apos;ll notice &apos;PtrFromString&apos; this is what gave me the hint that I was in fact seeing an issue produced by the subwords because this is very likely a super rare token and not something broken down into component subwords by the BPE scheme. You can learn more about BPE and why I would think that from this <a href="https://huggingface.co/docs/transformers/tokenizer_summary?ref=blog.kinglycrow.com#bytepair-encoding-bpe">concise overview of BPE by Huggingface</a>. Back to the drawing board. </p><p>Maybe I could maintain some type of state, some combination of ngrams and test them to see if english was being produced, maybe implement some kind of back tracking to go through other generations if this wasn&apos;t happening. Maybe just check a dictionary. Ultimately, all of those ideas are either more complex than I wanted to deal with or very lossy like the dictionary idea because it would be highly inflexible, not unlike the problem the BPE had sought to correct for model generation. <br><br>I realized I was kind of being dumb. Logits are scores, and they represent the functional confidence that from some sampling scheme a token would be produced in occurrence with the other tokens in the output. It naturally encodes for fighting gibberish. I just wanted to influence that confidence, not entirely zero it out or pin it to -infinity. I should just subtract the distance from the each logit instead. Now the logic encoded against gibberish and had a bias towards the input prompt. Would it work? Initially I thought so and the prompt below would seem to confirm it, but I&apos;ve run into some interesting benchmark results that I discuss later on in the post.<br><br>For the prompt:</p><!--kg-card-begin: markdown--><pre><code>Please write me a task planning application using golang for the API and nextjs for the frontend. Assume that everything you need to build the application is already installed.
</code></pre>
<!--kg-card-end: markdown--><p>Using the <a href="https://huggingface.co/databricks/dolly-v2-12b?ref=blog.kinglycrow.com">Dolly V2 12bn</a> model, this is the first output the model generated without applying the steering:</p><!--kg-card-begin: markdown--><pre><code>step 1: Fetch data from google sheets
 step 2: Parse the data from google sheets into a database structure
 step 3: Push the data into Google cloud storage

 google sheets data: https://docs.google.com/spreadsheets/d/1ZK4Z0uB5fC7_XNGo8cDRz7V5L9B_N9SJvDdE9KfKzsKJ0JWG3uvIjzOJLZNQM8EoQg1HmNyQ4/edit?usp=sharing

 gcloud command to push the data to google cloud storage:
 gcloud storage blob write [YOURBUCKETNAME]/[FILENAME] [URL] -- application/octet-stream
 gcloud storage -v upload [YOURBUCKETNAME]/[FILENAME]

 nextjs frontend code:
 // fetch data from google cloud storage
 const { storage } = require(&quot;@google-cloud/storage-backend&quot;);
 // parse the data from google cloud storage into a database structure
 async function getDataFromStorage() {
   const response = await storage.bucket().file
</code></pre>
<!--kg-card-end: markdown--><p>And this is the first output it generated with the newly improved steering:</p><!--kg-card-begin: markdown--><pre><code>You will need to use a postgresql database for this application but you do not need to create a database. The code for the application can be found here:
app.get(&quot;/&quot;, function (req, res) {
res.render(&quot;index.njk&quot;);
});

// TODO: Add task planning application functionality

/*
This is a start but you will need to complete the following:

Add functionality to allow users to create, edit, and delete tasks
Add functionality to allow users to assign tasks to users
Add functionality for users to view all their tasks
Add pagination to display tasks in the API
Add task statuses (to-do, done, in-progress)
Add due dates to tasks
Add notifications to tasks
You will also need to complete the following:

Handle errors in your application
Configure your database
After completing this exercise, you can delete the TODO comments to finish your application.

https://exercism.com/tracks/haskell/exercises/task-planning-application

*/

Next.js is a simple, fast and flexible framework
</code></pre>
<!--kg-card-end: markdown--><p>In the words of my friend who I was incessantly DMing as I was working on this, &quot;Night and Day&quot;. However, as stated above, when considering the benchmarking I believe this was a fluke.</p><h2 id="second-problem-second-solution">Second problem, second solution</h2><p>The original implementation was pretty slow. We&apos;re talking a minute or more for 100 tokens. If you&apos;ve made it this far and have some experience you&apos;ll have noticed a big loop and some pretty inefficient math that barely uses the benefits of torch. To fix this problem, we can vectorize everything! As it turns out all the calculations from the general document embeddings to calculating the distance of candidates to the prompt are able to be turned into stacked matrices and then computed in one go versus iterating. Some of the calculations are also able to be run once and cached instead of being called every iteration.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.kinglycrow.com/content/images/2023/05/Screen-Shot-2023-05-09-at-12.18.26-AM.png" class="kg-image" alt loading="lazy" width="2000" height="1170" srcset="https://blog.kinglycrow.com/content/images/size/w600/2023/05/Screen-Shot-2023-05-09-at-12.18.26-AM.png 600w, https://blog.kinglycrow.com/content/images/size/w1000/2023/05/Screen-Shot-2023-05-09-at-12.18.26-AM.png 1000w, https://blog.kinglycrow.com/content/images/size/w1600/2023/05/Screen-Shot-2023-05-09-at-12.18.26-AM.png 1600w, https://blog.kinglycrow.com/content/images/size/w2400/2023/05/Screen-Shot-2023-05-09-at-12.18.26-AM.png 2400w" sizes="(min-width: 720px) 720px"><figcaption>Everything is a matrix!</figcaption></figure><p>This brought everything in the realm of usability, I would even call it fast. A lot of this was low hanging fruit, but it is still always enjoyable to take a piece of code that works, but works slowly, and crank out some multiple hundreds percent speed up. I won&apos;t go through the exercise of benchmarking this for exactly how much of an increase but it&apos;s now more than useable. </p><h2 id="my-concerns-and-lukewarm-results">My concerns and lukewarm results</h2><p>I haven&apos;t been able to produce numbers that show the success I was seeing above, and I had run this many times both with and without QueryAlignment and subjectively the QueryAlignment appeared better to me. Naturally I wanted to put some numbers to this.</p><p>It turns out finding benchmarks for generating long task output is somewhat challenging, so I decided to try this against <a href="https://github.com/openai/human-eval?ref=blog.kinglycrow.com">HumanEval</a> which is a set of code generation tasks and after trying a few different combinations of tests and making edits here and there as I discovered issues with my implementation, I managed to perform about 5% worse than the stock Dolly V2 12Bn model, which is honestly a pretty surprising result.</p><p>I should note that of the seven task categories Dolly V2 12BN was trained for, code generation was not one of them, even though it has clearly been trained on code. In general it only managed to score about 3.5% without query alignment and 3% with. Even with that, having seen many subjectively good generations somewhat related to codegen while using this alignment method I had expected at least a marginal improvement.</p><h2 id="conclusion">Conclusion</h2><p>It was pretty interesting to see such a result from this even though it wasn&apos;t a smashing success. In fact I really think there is a lot of room for both research and immediately practical uses in augmenting the logits produced by these LLMs. In a lot of cases the correct sequence exists within the search space and just requires a nudge in the right direction to produce them much like with Ben Newhouse&apos;s Clownfish JSON parser. Methods like this seem pretty under explored, at least in language models. Other models in other spaces like Stable Diffusion have things like ControlNet which is a more advanced form of shaping outputs from a model and we don&apos;t quite have anything like that for LLMs at least not as mainstream or publicly released. I did see that Huggingface has a sampling solution that makes use of a smaller LLM to aid a larger one and keep it on task and it wouldn&apos;t surprise me if OpenAI has something even more advanced than that.</p><p>If anyone has any ideas for better ways to test the effectiveness of this method, or improvements I could make that might shake up the lukewarm results, I&apos;d be happy to hear them! All of this code is available in this repo and my generations for the humaneval benchmark are there as well under benchmarks <a href="https://github.com/iantbutler01/asimov?ref=blog.kinglycrow.com">https://github.com/iantbutler01/asimov</a></p><h3 id="future-work-other-problems">Future work, other problems</h3><ul><li>Could you apply this to focus a model&apos;s outputs to keep it more factual relative to some text that is not the prompt itself?</li><li>Is there a more effective way to use this to score the logits than just subtracting the distance from the score or multiplying the distances with the score?</li><li>Occasionally the model will just output the prompt itself</li><li>What other processors could we implement to improve the quality of smaller models that can be run locally?</li><li>This solution uses pretty naive embedding schemes to create document embeddings, maybe use something like SBert to produce better representations</li></ul>]]></content:encoded></item></channel></rss>