이 글은 90년대 소프트웨어 기술 문서의 문체를 모방하는 로컬 LLM을 파인튜닝하는 과정을 다룹니다. 저자는 마이크로소프트의 과거 매뉴얼 데이터를 활용해 스타일을 전달하는 실험을 진행하며, RAG 대신 파인튜닝을 선택한 이유와 저비용으로 로컬 환경에서 모델을 세밀하게 조정할 수 있는 방법을 설명합니다. 이는 실무자들에게 AI의 스타일 학습 가능성과 효율적인 데이터 활용법을 보여줍니다.
번역된 본문
1995년도처럼 문서를 작성하는 LLM 파인튜닝하기
게시일: 2026년 6월 1일 · 10분 소요
2030년 예측에서 나는 기술 작성자들이 강력한 하드웨어에서 로컬로 실행되는 특수 LLM을 사용할 것이라고 적었습니다. 엔지니어링 전문가들 사이에서 “로컬 우선”으로의 전환 조짐이 보이지만, 아직은 그 단계에 이르지 않았습니다. 그 이유 중 하나는 연결된 최신 모델(frontier model)이 훨씬 더 강력하기 때문입니다. 하지만 그렇다고 실험을 할 수 없는 것은 아닙니다. 지난주에 나는 정확히 그 일을 했습니다. 80년대와 90년대 소프트웨어 기술 작성자처럼 글을 쓰도록 지시 모델(instruct model)을 파인튜닝하려고 시도했습니다.
연구를 위한 오래된 기술 작성 지식 소환
90년대 기술 작성자처럼 글을 쓰는 개인 로컬 모델을 훈련시키려면 많은 서면 자료가 필요합니다. 예를 들어, 모델이 나처럼 글을 쓰도록 파인튜닝하고 싶다면 이 블로그 하나로는 충분하지 않을 것입니다. 게시 시점에서 겨우 10만 단어 정도이기 때문입니다. 철저한 훈련을 위해서는 더 많은 샘플이 필요하지만, 이를 구하거나 만드는 것은 쉽지 않습니다. 유일한 빠른 방법은 기존 코퍼스를 사용하는 것입니다. 그렇다면 어디서 구할 수 있을까요? Bitsavers를 소개합니다. 이 웹사이트는 오래된 컴퓨터 매뉴얼과 브로셔를 수집하고 스캔합니다. 이는 컴퓨터 역사와 오래된 기술 작성의 귀중한 저장소로, 곳곳에 미러 사이트가 있습니다. 나는 90년대 마이크로소프트 매뉴얼을 좋아하므로, 마이크로소프트 컬렉션을 훈련 자료 출처로 선택했습니다. 이 컬렉션에는 1977년부터 2005년 사이에 출판된 절판된 문서가 포함되어 있으며, 오래된 시스템과 SDK를 다루는 3,700만 단어 이상이 포함되어 있습니다.
OCR된 텍스트 파일을 다운로드하고, 익숙한 파이썬 스크립트를 사용해 인덱스나 프론트매터 같은 노이즈를 제거했습니다. 그런 다음 OpenRouter를 통해 저렴하고 빠른 모델인 gemma-4-26b를 사용해 각 단락의 가독성을 기준으로 “유지” 또는 “삭제”로 분류했습니다. 이 두 번째 작업은 약 8달러가 들었습니다. 하지만 이 두 단계의 정제 과정에도 불구하고, 훈련 데이터에는 나중에야 발견한 노이즈가 남아 있었습니다. 그래도 테스트에는 크게 문제가 없었습니다.
정제된 텍스트를 단락과 섹션 경계에서 훈련 예시로 나누고, 제목에서 구분하며 코드 블록은 그대로 유지했습니다. 각 청크는 Claude의 조언에 따라 약 512토큰으로 제한했습니다. 각 청크에는 템플릿에서 추출한 합성 지시어가 짝지어졌습니다. 최종적으로 JSONL 형식의 192,456개 예시를 얻었습니다. 작은 모델로 더 나은 지시어와 질문을 생성할 수도 있었지만, 나는 성격이 급한 사람입니다.
💡 자료에 대한 참고: 이것은 독립적이고 비상업적인 연구 프로젝트로, 마이크로소프트와 제휴하거나 후원 또는 보증을 받지 않았습니다. 나는 이 절판된 매뉴얼을 개인적인 스타일 전송 실험에만 사용했습니다. 코퍼스, 훈련 데이터, 결과 어댑터는 배포되지 않으며, 파인튜닝된 모델은 내 컴퓨터에 엄격히 로컬로 유지됩니다.
처음부터 훈련하는 대안으로서의 파인튜닝
이상적인 세계에서는 나만의 LLM인 Fabrice를 만들기 위해 수백만 달러를 쓸 수 있을 것입니다. 하지만 나는 부자와 거리가 멀고(그렇지 않으면 이 글을 쓰지 않았을 것입니다), Fabrice의 대안은 파인튜닝입니다. 이는 모델의 “가중치”를 조정해 생성된 각 토큰이 훈련 자료에 의해 조건화되도록 합니다. 나는 파인튜닝을 거대한 빙산을 예인선으로 살짝 조정해 원하는 효과를 얻는 것으로 비유합니다. 왜 파인튜닝이지, 검색 증강 생성(RAG)이 아닐까요? 이 실험에서는 사실 검색보다는 특정 스타일로 행동하고 글을 쓰는 LLM을 원했기 때문입니다. RAG는 사실 검색에 뛰어나지만 여기서는 맥락 지식보다 스타일이 중요했습니다. 전체 훈련에 비해 파인튜닝은 많은 데이터가 필요하지 않아 비용이 저렴합니다. 또한, 항상 파인튜닝을 시도해보고 그 실현 가능성을 확인하고 싶었습니다. 내 컴퓨터(그래픽 카드가 꽤 오래된)에서 모델을 파인튜닝하며 며칠이나 몇 주를 보내지 않기 위해...
Fine-tuning an LLM to write docs like it's 1995 Posted on Jun 1, 2026 · 10 min read In my predictions for 2030 I wrote that tech writers would be using specialized LLMs, running locally on powerful hardware. I see hints of this move to “local first” among engineering pundits, but we’re not there yet, in part because of how much more powerful connected frontier models are. That doesn’t mean we can’t experiment, though. That’s precisely what I did last week, trying to fine-tune an instruct model to write like a software technical writer from the 80s and 90s. Summoning old tech writing lore for research To train a personal, local model to write like a technical writer from the 90s, one needs tons of written sources. If I wanted to fine-tune a model to write like myself, for example, this blog would not be enough, as it’s barely 100k words at the time of this post. You would need more samples for thorough training, and those are not easy to come by, nor simple to produce. The only quick way is to use an existing corpus. Where could I get one? Meet Bitsavers : it’s a website that collects and scans old computer manuals and brochures. It’s an incredibly valuable repository of computer history and ancient tech writing, with mirrors available everywhere. As I’m fond of Microsoft manuals from the 90s, I chose the Microsoft collection as the source of training materials. The collection contains out-of-print docs published between 1977 and 2005: more than 37 million words, covering old systems and SDKs. I downloaded the OCR’d text files and cleaned the content from artifacts and clutter (like indices and frontmatter) using good old Python scripts. I then used a cheap and fast model through OpenRouter , gemma-4-26b, to classify each paragraph as either “keep” or “drop” based on its intelligibility. This second pass cost around 8 dollars. Even with this two-pass cleaning, though, training data retained noise that I discovered only later, but that was largely OK for my tests. I split the sanitized text into training examples on paragraph and section boundaries, breaking at headings and keeping code blocks whole, with each chunk capped at around 512 tokens as per Claude advice. Each chunk was paired with a synthetic instruction drawn from templates. I ended up with 192,456 examples in JSONL format (one JSON object per line). I could have used a small model to also come up with better instructions and questions, but I’m an impatient person. 💡 A note on the materials: This is an independent, non-commercial research project and is not affiliated with, sponsored, or endorsed by Microsoft. I used these out-of-print manuals for personal style-transfer experimentation only. The corpus, training data, and resulting adapters are not being distributed, and the fine-tuned models remain strictly local to my machine. Fine-tuning as an alternative to training from scratch In an ideal world, I would have several millions of dollars lying around, ready to be burned creating my own LLM, Fabrice . Since I’m far from rich (I wouldn’t be writing this otherwise), the alternative to Fabrice is fine-tuning, which involves tweaking the “weights” of a model so that each token generated is conditioned by the training materials. I like to picture fine-tuning as slightly steering the trajectory of a massive iceberg using tugs; just a little, just to get the intended effect. Why fine-tuning and not, say, retrieval-augmented generation (RAG)? Because in this experiment I was not so much interested in retrieving facts, a scenario where RAG excels, as in getting an LLM to behave and write in a specific style, whatever its knowledge of the context. Compared to full training, fine-tuning doesn’t require a massive amount of data, so it’s cheaper. Also, just because: I always wanted to try fine-tuning as a technique and see how feasible it could be. To avoid spending days or weeks fine-tuning a model on my computer, which has a rather old graphic card, I relied on Runpod , an online service for AI developers that provides on-demand pods with pre-configured GPUs and tools for a (relatively) small price. For less than $6 per hour, for example, you can lease a beast of a card, the Nvidia B200 (192gb of memory). The service has a convenient API with configurable auto-recharge and cost control mechanisms. Entering a world full of mysterious buzzwords After deciding to fine-tune a model, I consulted with Claude on the sanest methods to achieve that. We settled on QLoRA (Quantized Low-Rank Adaptation), which achieves fine-tuning not by altering each weight of an LLM, but by “freezing” them and putting an adapter on top, which is a small file that reshapes the model behavior (a bit like a mask, if you will). The Q in QLoRA means that the result is quantized, that is, compressed, reducing memory requirements. Are you still with me? Good. If you think this is dense, it’s because it is. Doing anything with LLMs at home these days is an exercise in compromises: you either sacrifice time, spend money, or curb your ambitious goals. I tried to strike a balance to get something meaningful in less than a weekend. I chose to try fine-tuning on two models, Llama 3.1 8B Instruct and Qwen 2.5 7B Instruct. At their size (around 8B) they run comfortably on a Macbook Air. I also tested a Llama base model (which is not trained to answer questions). I tested fine-tuning under several different conditions: varying the volume of training materials (a subset vs. the full corpus), the number of epochs (training rounds), and structural parameters like the rank. I only hold a superficial knowledge of all this, but I trusted my agent to make the right choices, which I happily questioned at every step. For example, 3 epochs can result in “overfitting” in some cases; in the world of LLMs, that translates to excessive training. Fun times. Run Base Data Epochs Rank Llama instruct-40k Llama 3.1 8B Instruct 40k 1 16 Llama base-40k Llama 3.1 8B (base) 40k 1 16 Qwen-40k Qwen 2.5 7B Instruct 40k 3 16 Qwen-192k Qwen 2.5 7B Instruct 192k 1 16 Qwen-r8 Qwen 2.5 7B Instruct 40k 1 8 Qwen-r16 Qwen 2.5 7B Instruct 40k 1 16 Adapters can only be applied to the target model you fine-tuned for. After training each adapter, I exported them to my laptop and converted and quantized them to a GGUF LoRA file, and then registered it as a local Ollama model I could run in my laptop for benchmarking purposes. The local-conversion approach is faster and requires no GPU, though inference is somewhat slower than a fully merged model. For the test at hand, I did not care about speed that much. Training the adapters for all conditions took perhaps an entire day, including breaks, for a total cost of $50. Along the journey, I lost two adapters: Runpod is unforgiving of budget and deletes pods immediately if funding is zero (there’s a lesson learned, yes). Claude took care of setting up each run and following up with Runpod’s API. The /goal command of Claude Code was quite helpful to loop through each phase (in retrospect, I would have run it in YOLO mode ). This table shows all the models I compared and their conditions: Name What it is llama3.1:8b Unmodified Llama baseline qwen2.5:7b Unmodified Qwen baseline msft-base-40k Llama base (non-instruct) + 40k (control) msft-instruct-40k Llama instruct + 40k, 1 epoch, rank 16 msft-qwen-40k Qwen + 40k, 3 epochs, rank 16 msft-qwen-192k Qwen + 192k, 1 epoch, rank 16 msft-qwen-r8 Qwen + 40k, 1 epoch, rank 8 msft-qwen-r16 Qwen + 40k, 1 epoch, rank 16 Did the style transfer after fine-tuning? I subjected each model to the same prompts: Document malloc(), a staple C function, something the training materials might know about. Document a fictitious ConnectWifi() Win32 API function. No presence in the training materials. Explain what a REST API is in 1990s Microsoft style (the anachronistic test). You can see all the questions and answers in this gist . For the malloc() test, the unmodified models generat