コンパイルエラー、あまり読みたくないですね。説明してほしい。そして、できれば元気に説明してほしい。 もちろん、最近のAIを使えば解説してくれます。 でも、そういったAIには課金か大きいモデルを動かせるハードウェアが必要です。毎日何万回も出すコンパイルエラーをそのたびに課金していたら破産してしまいますね。大きいモデルを動かす環境を用意するのも大変です。 そこで、小さいサイズのLLMをファインチューンして、コンパイルエラー説明専用のLLMを作れば、手元のパソコンでいくらでもコンパイルエラーを説明してくれるはずです。
このブログはBuriKaigi 2026の登壇資料です。
BuriKaigi 2026 - connpass

ファインチューンとは
LLMというのは、仕組み的には文章の続きを出してくれる機械学習モデルです。
3年前だとまだファインチューンしてないモデルが公開されていたので、そのころのブログみるとわかりやすい。
CyberAgentの日本語言語モデルを試してみる - きしだのHatena
「アメリカの首都はワシントン。日本の首都は」と与えると、「東京」を含んだ続きを生成する、という感じです。

そこに、「ユーザー:日本の首都は? /n システム:」と与えたら返事を返すんですよと出力形式を教えてあげるのがファインチューニングです。
LINEのベース日本語言語モデルを強化学習で対話できるようにして賢さを評価する(過去下書き放出) - きしだのHatena
ファインチューニングに対して、元になるモデルを学習させることは事前学習といいます。
雑にいうと、知識や文法を教えるのが事前学習、出力のやりかたを教えるのがファインチューニングです。
ファインチューンの種類
ファインチューンにも種類があります。
CPT
CPTは事前学習の続きをこっちで行うということなのでファインチューニングではないのだけど、結局はプロンプトの形式が違うだけなので、今回の文脈ではファインチューニングに含めてもよさそうです。
いろんな文章をそのまま渡します。
SFT
理想の出力を教示データとして与えてファインチューニングします。 データセットを作って、形式を指定してトレーニングします。 たとえば、素朴なものだと次のような形式を指定していきます。
ユーザー: 次の情報を元に質問に答えてください。{input}
システム: わかりました。
ユーザー: {instruction}
システム: {response}
だいたいのファインチューニングはこれになりそう。
今はツール利用とかもあったり複雑になってますが、そこはフレームワークがやってくれるので気にしなくてよかったり。
DPO
返答として、良いものと良くないものを同時に与えるやりかたです。
例えば「爆弾の材料を教えて」という質問に対して、「爆弾の材料には・・・」と答えるのが良くないもの、「すみません、そのような質問には・・・」と答えるのが良いもの、という学習をします。
LLMを行儀良くさせるのに使われますね。
RL
強化学習は、出力に対してスクリプトなどで点数をつけて、その点数によってパラメータを更新するやりかたです。
点数を人間がつけるとRLHFということになります。
いい対応をさせる他に、ゲームをうまくやらせるなどもあります。
どういうときにファインチューンが必要か
モデルを作る立場であれば、そのモデルを使ってもらうためにファインチューンが必要ということはわかります。
では、モデルを使う立場として、なぜファインチューンが必要かとなりますね。
特に、最近の商用モデルであればプロンプトにデータや指示を与えることで、必要な反応をしてくれます。ユーザーの質問からプロンプトにデータを与えることを自動的にやればRAGになります。
ということで、商用モデルなら普通にできるのになぜファインチューンが必要?と言い換えると
節約したいから
です。
つまり、商用モデルというのはいろいろな人のいろいろな使い方に対応するために、なんでもできるようになってます。でもアプリケーションに組み込んだりするときは、使い方は一定になっていきます。そうすると、フランス語の能力とかPythonを書く能力とかまで高度に対応できるようなモデルをちょっと高めの料金を払って使うのはもったいなくて、用途に適した料金で実現したいです。
でも、その予算にあうモデルはちょっと能力が足りない、でも単機能で学習させればできそう、というときファインチューニングして小さいモデルが使えるといいですね。
まずはファインチューンの実現方法を考える
ファインチューンをするには、適切なハードウェアを使う必要があります。なので、まずはハードウェアが確保できるかどうかというのが重要ですね。
このとき、まじめに全部のパラメータを更新すると大きなハードウェアが必要になるので、限られたパラメータだけを効率的に学習するというPEFT(Prameter Efficient Fine Tuning)という考え方があります。そうすると身近なハードウェアでそれなりのモデルをファインチューニングすることができます。
また、PEFTでパラメータを更新するとき、モデルを直接更新するのではなくて、変更する差分を別にもたせて上書きして使うLoRA(Low Rank Adaptation)という仕組みがあります。さらにそれを量子化した状態で行うのがQLoRAです。
そして、Unslothというグループがファインチューンのためのフレームワークを作ってGoogle Colabで動かせるNotebookをたくさん用意してくれているので、それをベースにするとやりやすいです。
https://unsloth.ai/docs/get-started/unsloth-notebooks
どのくらいのモデルをどのくらいのVRAMで実現できるかもまとめられています。
https://unsloth.ai/docs/get-started/fine-tuning-for-beginners/unsloth-requirements
VRAM 16GBあれば、gpt-oss 20bは学習できます。
まずはデータセットを
ファインチューンにはデータセットが必要です。
しかし元気にコンパイルエラーを説明するデータセットなんかありません。だいたい、自分の用途にあわせたデータセットはないですね。データセットがあるなら対応モデルもあったりします。
ということで、ファインチューンについて実行手順は既存のものが使えるので、問題はどうデータセットを作るかということになります。
そこで、手作業で作っていくというのもいいのですが、LLMに作らせるというのがいいですね。
そうやってLLMを使ったりして作るデータを人工データといいます。最近はそういった人工データをいかに作るかということも大事になります。
また、大きいLLMができることを、そこから学習データを作って小さいLLMに学習させて、大きいLLMの持つ能力を反映させるというのは蒸留(Distill)と言われますが、人工データが当たり前になってきて、あまり蒸留という言葉を聞かなくなった気がします。
ということで、今回はJavaのコンパイルエラーをLLMに解説させて、それをデータセットにします。
Javaのコードを探す
そうなると、今度はコンパイルエラーを含んだJavaのコードが必要です。
でIBMがAIZU Online JudgeとAt Coderから作ったデータセット、CodeNetを公開していて、コンパイルエラーが起きるコードも含まれているので、これを使います。
https://atmarkit.itmedia.co.jp/ait/articles/2105/13/news104.html
このようなデータセットの作成、公開は2018年改正の著作権法30条の4で可能になったのだと思います。
次のような条文なので、このデータセットの問題やコードを提示して考えさせるというような使い方はアウトです。
「自ら享受し又は他人に享受させることを目的としない場合には、その必要と認められる限度において、いずれの方法によるかを問わず、利用することができる」
進化する機械学習パラダイス ~改正著作権法が日本のAI開発をさらに加速する~|知的財産・IT・人工知能・ベンチャービジネスの法律相談なら【STORIA法律事務所】
データセットを作る
ではデータセットを作りましょう。
今回はこんな感じで、CodeNetのエラーありJavaコードを読み込んでjavacを呼び出してコンパイルエラーを出させてLLMに渡して解説させる画面を作りました。

LLMとしては、次の条件が必要です。
- コンパイルエラーをちゃんと解説できる
- 元気に解説できる日本語表現力がある。
で、そうなるとOpenAIのgpt-oss-120bは箇条書きを使ったりしてあまりよくなくて、これはGPT-5などを使ってもその傾向があります。
Gemma 3がこういった表現がすごくいいのだけど、今度はコンパイルエラーの解説能力に不安が。Geminiはとてもいいけど、今回は無課金でやりたい。
そうなったとき、Z.aiが出しているGLM4シリーズが賢くて日本語能力も高いということで、今回はGLM4.5-Airという105BのモデルをMac Studioで動かしてデータセットを作りました。
新しいGLM-4.7はコーディング能力も高くなっていておすすめ。これは350Bくらいあるので、GLM-4.7-Airが出て賢いことに期待です。
GLM-4.7で自宅コーディングエージェントが現実的に。日本語力も高く幅広く使える。 - きしだのHatena
だいたいまる2日くらいかかりました。Mac Studioがあったかくなって暖房としてよかった。
データセットの公開
データセットができたらHugging Faceで公開しておくと、Pythonで扱いやすくなります。
https://huggingface.co/datasets/kishida/CompileError-Java-JP-cheerful

マジメに説明するものと、それぞれ英語版の4つのデータセットを作っています。

モデルの選択
Qwen3 4Bや8B、gpt-oss-20bなどが対象になると思います。
今回はllama 3.2を例にしてますが、ちょっと性能が足りないので新しいものがいいですね。また、llamaは「月間アクティブユーザー数が7億人を超える場合、Metaにライセンスを申請しなければなりません」といった条件もあるので、ちょっと使いづらい。
昨年末くらいに8B程度のモデルがいろいろ出ていたのですけど、ちゃんとまとめれてないので、あとでまとめます。
ファインチューンを実行する
それではファインチューンを実行します。
ノートブックはこちらに。
https://gist.github.com/kishida/34950915e47576489e4081c723644c27
Google Colabで無償で使えるT4でも学習は行えるので、ここで試すといいです。たぶん10分くらいで終わります。
https://colab.research.google.com/gist/kishida/34950915e47576489e4081c723644c27
ここからはその中身を見ていきます。
学習させる形式をつくる
実際にどういうデータ形式で投入するかということを考える必要があります。

アプリケーションを作るときにデータを与える形式を指定することになります。今回はこういう形式でファイル名、ソース、エラーを渡します。
filename: {}
## source
{}
## error message
{}
そしてこれをチャットテンプレートに適用します。

チャットテンプレートが反映されると次のようになります。
<|begin_of_text|><|start_header_id|>system<|end_header_id|>
Cutting Knowledge Date: December 2023
Today Date: 26 July 2024
You are a good Java error explainer.
Please provide a concise explanation of the compilation error that occurred in the given source code.
Do not provide the complete code you have fixed.
<|eot_id|><|start_header_id|>user<|end_header_id|>
filename: Main.java
## source
class Main{
public static void main(String[] a){
for(int i=1;i<=9;i++){
for(int j=1;j<=9;j++){
System.out.println(i+"x"+j+"="+(i*j))
}
}
}
}
## error message
Main.java:5: エラー: ';'がありません
System.out.println(i+"x"+j+"="+(i*j))
^
エラー1個
<|eot_id|><|start_header_id|>assistant<|end_header_id|>
セミコロンが足りないのよ!println文の最後に「;」を忘れているみたい。Javaでは命令を区切るために必ずセミコロンが必要なのね。コードを見直して、println部分をちゃんと閉じるように修正してみて!<|eot_id|>
学習のパラメータを決める
学習のためのいろいろなパラメータを決める必要があります。
LoRAでは、どのレーヤーを学習するか指定します。q, k, vとo, gateとup, downのprojという指定になってます。ここを変更する必要はあまりないので、そういうところを指定するのだなぁくらいに思えばいいです。

継続事前学習の場合は、embed_tokensとlm_headも含むみたい。

あと、学習をどう進めるかも指定が必要です。ここで大事なのはmax_stepsとlerning_rateです。学習の回数と、そのたびにどのくらい学習結果を反映するか、という数値です。
ここは学習の肝だったりするのですが、学習が10分くらいで終わるので試行錯誤でいいと思います。

学習開始
学習こんな感じで進みます。

冒頭で「use_wandb=True」としておくと、Wandbを使って進捗を可視化できるようになります。

終了


GGUFで保存
GGUFはGGさんのMLライブラリ、GGMLを使ったLLM実装、llama.cppで使えるモデルファイルの形式です。
llama.cppがいろいろな量子化に対応しているので、必要な精度、サイズでモデルを保存することができます。
ただ、Unslothさんのノートブックだとllama.cppのビルドが始まってそこでメインメモリを使うのでT4でははなくL4以上が必要になります。
量子化の違いってなに?
量子化ではQ4_K_MとかQ4_K_Sとか種類があります。
たとえばLlama-3.2-1B-InstructのUnslothさんによるGGUFを見るとこんな感じで色々あります。
https://huggingface.co/unsloth/Llama-3.2-1B-Instruct-GGUF/tree/main.

Q4は4bitで量子化、つまり値を4bitで表しますよということなのでそのままですね。Q3なら3bit、Q5なら5bit。
問題はK_Mの部分。
ここで、KはK meansという手法を使って量子化することを表します。
そしてMとかSはサイズです。Medium, Smallの略です。Lもあります。
でもここでじゃあMediumとかSmallとか何よってなるので、実際の量子化を見てみます。
https://huggingface.co/unsloth/Llama-3.2-1B-Instruct-GGUF/blob/main/Llama-3.2-1B-Instruct-Q4_K_M.gguf
https://huggingface.co/unsloth/Llama-3.2-1B-Instruct-GGUF/blob/main/Llama-3.2-1B-Instruct-Q4_K_S.gguf
まずQ4_K_S。Q4_Kの中にQ5_Kがまじってますね。

これがQ4_K_MだとQ6_Kになります。

Transformerの計算の中でQuery、Key、Valueという要素があるのですけど、Valueだけ精度が高くなってます。
ついでに、じゃあなんでValueの値だけ精度が高いかというのも見ておきましょう。
ここに、Javaで書いたLLMの実装があります。というか、Cで書かれたものをあたたかみのある手作業でJavaに変換したものです。
https://gist.github.com/kishida/05656bfcbe840f269784f7dbbee5928e#file-llama-java-L300
qとkey_cacheを掛けたものを合計してattにいれてsoftmaxということをやってます。

softmaxは指数関数をかけたものをその合計で割るというものです。
そういった操作をするので掛け算の結果がそのまま残るわけではなく、精度が落ちてもかまわないわけですね。
一方でvalueに関しては、そうやって求めた値に掛けるだけ。

qkで計算した結果にvを掛けたものをそのまま使うので精度が重要ってことで高めの精度が使われてます。
F32もありますが、ここはShapeを見ると次元が低いです。行列を使って値を求めたあとに掛けるバイアス値なので精度重要だし、数も少なく量子化するメリットもでにくいのでそのままF32が使われてます。
Q4_K_Mがそこまで性能劣化しない割にサイズも小さくなるということでよく使われます。
モデルを公開
できたモデルはHugging Faceで公開すると使いやすくなります。

LM Studioでも検索に出てきます。

試してみる
試してみると、コンパイルエラーを貼り付けるだけで解説してくれていますね。

モデルの評価は?
実際にはモデルの評価というのも大事です。
けど、モデルを作って提供する場合にはちゃんとベンチマークとったりが必要になるとは思いますが、アプリケーションの実現方法としてファインチューンしたモデルを使う場合、そのファインチューンモデルを使ったときにそもそもアプリケーションとして成り立つかというほうが大事になって、アプリケーションの評価をするなかでモデルの評価もある程度できることになるので、まずは考えなくてもいいかなという気がします。
また、モデル評価のためのベンチマークを作るとして、ベンチマーク用のデータセットが必要になるのだけど、それって学習用に作るデータセットと同じでは、ともなると思うので、まずは学習率がちゃんとあがればいいんじゃないかなと思ったりします。
やるとして、教師データを作ったLLMに判定をさせることになりそう
まとめ
ファインチューン楽しいし既存のデータセットで学習するだけなら難しくないので、まずは試しましょう
