LoRAファインチューニングのコードを全てAIに書かせてみた

はじめまして。  
実務未経験からエンジニアを目指しています。
本記事は、求職活動におけるポートフォリオとして作成しました。

また、GitHubにソースコードを公開しております。
[GitHub](https://github.com/goodaymmm/LoRA_Java_Python)

- 追記(25/06/18)
英語記事版を公開しました。
[Dev.to](
https://dev.to/ryoto_miyake/ai-powered-development-building-a-javapython-lora-model-without-writing-a-single-line-of-code-1of8)

## はじめに
現在、Java・Pythonの職業訓練に通っています。
日進月歩でAIの進化が進む昨今、「これからのエンジニアは、コードを書くこととどう向き合うべきか？」という問いを持つようになりました。
その答えを探るため、「LoRAファインチューニングのコードを、すべてAIに書かせる」という実験的なプロジェクトに挑戦しました。

この挑戦を通して、AI時代のエンジニアに求められるスキルや、学習すべきことが自分の中で鮮明になりました。
本記事では、その過程で得られた知見や具体的な学びを共有します。

## 🔗 目次

- [🎯 なぜAIにLoRAを書かせようと思ったか？](#1なぜaiにloraを書かせようと思ったか)
- [🛠️ 使用ツール](#2使用ツール)
- [🧩 進め方](#3進め方)
- [✍️ 書かせた内容（プロンプト全文あり）](#4書かせた内容)
- [🐞 詰まった点、AIが間違えた部分](#5詰まった点aiが間違えた部分)
- [🔬 精度検証](#6精度検証)
- [🔁 振り返り](#7振り返り)
- [🏁 最後に](#8最後に)

---

## 1.なぜAIにLoRAを書かせようと思ったか？
<details>
<summary>そもそもLoRAファインチューニングとは？</summary>

ChatGPTから引用

■ 概要

**LoRA（Low-Rank Adaptation）ファインチューニング**とは、
大規模言語モデル（LLM）などの重いニューラルネットワークを効率的にファインチューニングする技術です。全パラメータを更新する従来の方法ではなく、**一部の重み行列に小さな追加構造（低ランク行列）を挿入**して学習することで、メモリ消費と学習コストを大幅に削減します。これにより、比較的低スペックなGPU環境でもファインチューニングが可能となります。

---

■ 例え話で説明

大きな船（LLM）を航海に適応させるとき、船全体を改造（フルファインチューニング）するのは大工事でコストがかかります。LoRAは、船のハンドルやセイルなど**一部の部品だけを取り替えることで操縦性を変える方法**です。つまり、船の本体（モデル本体）をそのままにしつつ、必要な部分にだけ小さなアタッチメント（低ランク行列）を加えて、目的地（タスク）に適応させるイメージです。

---

■ 詳細説明

LoRAの主なアイデアは、ニューラルネットワーク内の**重み行列（例えば全結合層など）に対して、元の重み `W` を固定し、その代わりに `W + ΔW` として `ΔW = A × B` という低ランク行列を学習**するというものです。ここで `A` と `B` は通常ランクが小さく、パラメータ数が圧縮されるため、以下の利点があります：

* **GPUメモリ消費が少ない**（元の重みは凍結）
* **学習時間が短縮される**
* **モデルサイズが膨れない**
* **フルファインチューニングと同等レベルの性能が出せるケースもある**

また、LoRAの適用箇所は多くの場合、**TransformerのAttentionやFFNのLinear層**で、効率を最大化するよう設計されます。

---

■ 用語や概念が用いられる事例

* **ChatGPTなどの大規模LLMのタスク特化（日本語特化、医療応答特化など）**
* **Stable Diffusionなど画像生成モデルのキャラ・作風チューニング**
* **低資源環境（例：8GB VRAMのGPU）での個人開発や研究**
* **RAG（検索拡張生成）などのワークフローで、LoRAを使って事前モデルを軽く微調整**

---
</details>

### きっかけ
生成AIの既存モデルは特化したニッチな内容（専門性の高い分野等）への出力が弱い事に気づき、
これを特化したデータを追加学習させることでそれぞれが抱える問題の解決や効率化があることに着目しました。

そこで、独自の学習データを取り入れる事で解決出来ることを知り、達成するアプローチを模索した結果、LoRAという
「**既存の事前学習済みモデルを維持したまま、追加パラメータのみを訓練することで、用意したデータに特化させる効率的な学習手法** 」を今回採用しました。

### AIに書かせる理由について
プログラミング初学者がLoRAのコードをゼロから書くのは非常にハードルが高いですが、現代にはAIという文明の利器がありました。
この課題を **AIだけに書かせてで解決できるのか？** という疑問に至り、実行に移ります。

---

## 2.使用ツール
- 開発環境
    - Cursor: Claude 3.7 Sonnet搭載のAgent機能を使用
    - Claude Desktop: エラー対応とトークン使用量分散のため併用

CursorとClaudeは月額プランに加入していて、Pro版を利用しています。

発生したエラー等の質問をCursor内のAsk機能ではなく、Claude Desktopを使用しました。
※開発後期ではClaude4.0がリリースされたのでその頃発生した問題はClaude4.0へ質問をしています。

- Claude Code・WindsurfやDevin等を使わなかった理由
    - 構想段階ではトークン使用量が不明だった
    - まだPro版でClaude Codeが使えない時期だった
    - 実行時点で既にCursorとClaudeのProプランに課金していた

---

## 3.進め方
まず実装までのロードマップを洗い出しました。
- 【1】前提準備
- 【2】ベースモデルのダウンロード
- 【3】自作データセットの整形
- 【4】LoRAファインチューニング実行
- 【5】推論用コード実装
- 【6】モデル・結果の展開
<br>

- 【1】前提準備
<details><summary>詳細</summary>
Java・Pythonを学習していたので、この言語に特化したLLM構築することを構想。
完成イメージとしてはChatGPTのような対話型生成AIです。

完成形はGitHubへ公開し、同じ環境を再現してほしかった事も相まって、Dockerも使う事を決めました。
また、GPUを使う必要がある為、CUDAの利用を前提に定義しました。
<br>
</details>

- 【2】ベースモデルのダウンロード
<details><summary>詳細</summary>
ELYZA-japanese-CodeLlama-7bを選択しました。

自分のPCスペックでは7Bレベルがベストだった事、コード生成に強かった事、
日本語追加学習済みであった事が選定理由です。

- 比較検討した代替案
    - Mistral-7B: 汎用性は高いがコード特化度で劣る
    - Gemma-7B: 同様に汎用寄り
    - より大きなモデル: VRAMリソースの制約で除外
<br>
</details>


- 【3】自作データセットの整形
<details><summary>詳細</summary>
スクレイピングを用いて、データを抽出するところからスタートしました。
以下3つから抽出
・GitHub
・CodeContestsのAtcoder

選定理由は以下の通りです。
- GitHub
    - スター1000以上の高評価リポジトリに限定
    - コードレビューを経た高品質なコード
    - 実際のプロダクションレベルのコード構造を学習
- ~~Qiita~~
    - ~~日本語での技術解説が豊富~~
    - ~~コードと説明のペアで学習効果向上~~
    - ~~日本語でのコード生成能力強化~~
- AtCoder
    - アルゴリズム的思考のコードパターン
    - 制約条件下での効率的なコード
    - 競技プログラミングの実践的解法

以上の理由から選定しました。

Qiitaも初期段階ではスクレイピング対象でしたが無くしました。
理由はデータセットの問題です。詳しくは後述します。

抽出後、これらをファインチューニング出来るようJSON形式に変換していきます。
<br>
</details>

【4】LoRAファインチューニング実行
【5】推論用コード実装
【6】モデル・結果の展開
これら3つは詰まった点が発生したので後述に記載します。

---

## 4.書かせた内容

### **全部！**

指示した最初のプロンプトは以下です。

<details>
<summary>プロンプト全文を見る</summary>
<pre><code class="language-bash">以下の要件を満たすように構築してください。
Docker環境下のUbuntuでの実装を想定しているため、DockerFile、Dockercomposeも生成してください。
また、GPUを使ったチューニングをするのでCUDAセットアップも忘れないでください。
想定するGPUスペックはRTX4070TiSuperでVRAMは16GBあります。
物理メモリーも32GB想定です。
CPUはi7-14700K想定です。
<br>
ELYZA-japanese-CodeLlama-7b(URLは記事内では割愛)をローカル環境にダウンロードし、Java・PythonのデータをLoRAファインチューニングすることでローカルCodeLLaMAを構築します。
必要なデータセットは以下の通りです。
・GitHub
・qiita
・CodeContestsからAtcoderの物を使用
これらをスクレイピングします。
<br>
また、スクレイパー間隔はGitHubが1秒、qiitaは2秒の間隔を持ってください。
総件数はGitHub・qiita共に100件スクレイピングしてください。
<br>
Atcoderデータは以下のURLからスクレイピングしてください。
【URLは記事内では割愛】
<br>
CodeLLaMA、GitHub、qiitaのAPIトークンはユーザーがあとから入力します。
環境変数設定をユーザーが入力するので、環境変数をコードには記述してください。
<br>
その後、データセットの整形に入ります。
スクレイピングしたデータセットをJSONに整形するコードを出力してください。
<br>
整形後、LoRAファインチューニングするコードを出力してください。
次に推論用コードを実装してください。
<br>
最後に、これら全ての手順をREADME.MDファイルへ実行コマンドも書き記してください。
</code></pre>
</details>

**もちろん、これだけでは完全に動きませんでした。**

---

## 5.詰まった点	AIが間違えた部分
### DockerComposeの問題
- 問題点
    - 生成されたDockerComposeだと、CPUやメモリ割り当てが適切でなかった
    - Atcoderの解凍に約26-27時間ほど掛かる予想が出力された
- 原因
    - ホストPCスペックを記載していても、適切なDockerComposeを生成してもらえなかった
- 解決策
    - WSL環境考慮＋メモリ・GPU・CPUの割り当て指示を追加プロンプトで補正
    - 自分はWSL→Docker接続をしていたので、WSLConfigも最適化(WSLConfigは手動調整)

以下の修正案プロンプトを指示しました。

<details>
<summary>Dockercomposeの再設定プロンプト</summary>
<pre><code class="language-bash">現状のDockerCompose設定ではホストPCのスペックを活かしきれていません。
ホストPCの環境は
i7-14700Kは20コア（8P+12E）、64GBメモリ、GPUがRTX4070TiSuper（VRAM16GB）です。
CPUを16コア、メモリを最大48GBまで使用可能、GPUを10GB割り当てるように再度設定してください。
ホストPC→WSL→Docker接続で環境構築しているので、WSL用に4GBほどメモリは残してください。
RTX4070TiSuperはMIG非対応デバイスのため、別のアプローチで10GBメモリを割り当ててください。
</code></pre>

✅ MIG（Multi-Instance GPU）とは？

MIG（Multi-Instance GPU）とは、NVIDIAのA100やH100といったデータセンター向けGPUに搭載されている機能で、1枚のGPUを論理的に分割して複数の独立した「仮想GPU」として利用できる技術です。

</details>
<br>
修正後、ホストPC環境を活かした構築が出来たので
この点はより注意深くソースコードを見ておく必要があったため反省です。

### GitHubスクレイピングで発生したAPIエラー
- 問題点
    - スクレイピングについて制約が曖昧だった為、APIエラーが多発
- 原因
    - 決めるべき制約が定まっていなかった
    - フィルタリング設定などが適切でなかった
- 解決策
    - スクレイピングする対象を明確に指定
    - フィルタリングやソートを整備し、スクレイピング精度を高める事にしました
    - リソース配分が適切に割り当てられるよう定義
    - 以上を踏まえた制約を追加指示して書き換えました

<details>
<summary>制約のプロンプト</summary>
<pre><code class="language-bash">GitHubスクレイパーには以下の制約を加えてください。
・API間隔は1秒を持ってください
・encoding: noneのファイルをスキップするようにする
・ファイルサイズの制限を追加する（1MB以下のみ処理）
・.py・.javaファイルのみを検出するフィルタリングを実施
・スターが1000以上のみ抽出
・ディレクトリ探索の最大深さをデフォルトは2で設定
・リソースには余裕があるので、API制限に掛からない程度の並列処理を設定
</code></pre>
</details>

この制約をつけることで、無駄を省略、処理の最適化を図りました。

### ファインチューニングのエラー

- 問題点
    - TensorBoardがインストールされていないエラーが出た
- 原因
    - TensorBoardがインストールされていなかった
- 解決策
    - TensorBoardのインストールにて解決    

AIが生成したコードに、必要なライブラリのインストール定義が漏れていました。
AIも完璧ではなく、こうした人間的なミスをすることがあるという興味深い発見でした。

### 推論中にエラーが多発

- 問題点
    - LoRA Adapterを読み込んで推論すると失敗
    - 1回目、2回目の学習でoreferrerという回答を繰り返されてしまった
- 原因
    - データセットのクリーニングが最適化されていなかった    
    - ベースモデルは良好な結果を出すので問題はLoRA側にあることが判明
- 解決策
    - データセットの再クリーニングを実施、その後LoRA学習を再実行

結果、3回目の学習で問題は解決しました。

### ベンチマークでのエラー
- 問題点
	- LoRAのベンチマーク測定でHumanEvalを推論中、全問不正解になっていた
	- 学習に即した簡易版でテストすると良好な結果は得られていた
- 原因
	- Python PEP 8のインデント標準が4スペースなのに対して、LoRAが3スペースインデントのコードで学習した
	- Qiitaの記事をうまくデータセットに取り込めていなかった
    - 問題は評価方法ではなく、学習済みモデルの品質にあると切り分け
- 解決策
	- データセット形成からやり直す事にし、Qiitaをデータセットから除外しました
	- それに伴い、再形成と再学習するためにコード修正を指示

:::note info
「**データセットの質がモデルの性能を左右する**」と頭では理解していましたが、この失敗を通じてその重要性を痛感しました。これは、実践だからこそ得られた大きな学びです。
:::

そして再実行の結果、完成しましたので精度報告に入ります。

---

## 6.精度検証
### ベンチマーク
- [BigcodeのHumanEval](https://github.com/bigcode-project/bigcode-evaluation-harness)を使用
- 問題数は標準の164問を実行
- Code Llama 7B/ ELYZA-japanese-Llama-7b / LoRA / GPT-4で精度比較

### スコア比較
pass＠1のモデル別スコア比較になります。

### 縦棒グラフ
![humaneval-4models-comparison.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4102631/0c9580bd-11fa-41b4-a65b-0f88829be042.png)

### 横線グラフ
![humaneval-evolution-progress (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4102631/5cb3a1c2-2055-4146-92b0-2f8a11d6fa47.png)

GPT-4のスコアは圧倒的ですが、今回のチューニングによって、ベースモデルから着実に性能を向上させることができました。
これは、小規模なデータセットでも特化させることの有効性を示しており、
より大規模なデータや工夫次第では、さらに上位のモデルに迫る可能性を秘めた結果だと考えています。


次に、今回のチューニングでベースモデルからどの程度性能が向上したのかを円グラフで示します。

![humaneval-improvement-details.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4102631/b7c879ec-0e25-4a6a-acb0-d126aab87471.png)

（なお、ベンチマークの実行ごとにおよそ±3%程度のスコアの揺らぎが見られました）

---

## 7.振り返り
### 良かったこと
- 完走した 

目的を設定し、完走した経験が大きいです。
結末に対してのロードマップも組み立てられ、アプローチもある程度は正しかったと感じています。

- AIだけで書ききった

目標通り達成しました。
プロンプトを適切に指定してあげると、難易度が高い問題も実装出来るくらいレベルは高いです。
適切に指定するためにもアーキテクチャを理解し、言語化する必要は依然としてありそうです。

- 新しい知識の受け入れ

Javaのアプリケーション開発をしたことはありましたが、既存の学習内容をあまり超えませんでした。
今回は、一から知識を入れていくところからスタートとなりましたが、新しい分野に対して知識を入れていくことへの抵抗は全くありませんでした。

生成AIだけで出来るなら、「小規模なチームや個人でも、アイデア次第で価値あるサービスをスピーディに開発できる」という、新しい時代のものづくりの可能性を実感できたことも大きな収穫でした

自分は過去に、動画編集を独学で身につけており、新しい領域に対しての抵抗感は低い方でしたが、
より一層、0からポジティブに取り組むことが出来る実例の一つとして経験を加える事ができました。

### 反省点
- 見積もりの甘さ

原因は開発規模の見積もりを見誤った事です。
今回、2000行程の中規模開発でしたが、このクラス以上を書かせるのであれば、Cursor Agentではなく、仕様書をフェーズやチケットで役割を分担し、作業させることでデバッグやエラー解決などキャッチしやすくなるので、並列処理の開発が得意なClaude Codeを採用して開発を進めるべきだったと反省しています。

今回の学習スケールだと、微々たるもの過ぎてあまり実感がないと思う一方、
少ないデータセットでも向上が見られるのは、良い発見でした。

- データセット選定について

既存のデータセットを用いての実装だったので、比較的容易で形成しやすかったです。
また、安易な選定をしてしまうと、データが濁ってしまう経験が出来たのは非常にプラスです。

運用まで考えると、データセット形成をよりニッチな題材でトライしても良かったかなと感じました。
次回はユーザーエクスペリエンスも視野に入れた開発へ着手しようと考えています。

- アーキテクチャの理解力

LLM構築に関するアーキテクチャの理解が乏しく、特に推論周りのエラーが多発していたように感じました。
理解が深ければ、初期プロンプトをより詳細化でき、手戻りを減らせたと感じます。
今後は実装へ入る前に、関連技術の全体像を把握する時間をより重視し、開発の精度を高めていきたいです。

<br>

### 課題
実際の現場でAIをどこまで流用出来るかが自分の中の課題となっています。

- 主な課題
    - 保守・運用まで含めた構成になっているか？
    - スパゲッティコードになっていないか？
    - セキュリティ設計は？
    - 独自フレームワークなどで構築されていたらそもそもAIで力を発揮出来ない
    - 途中参加のプロジェクトで応用していけるか

これらの課題は個人開発だけでは見えにくい部分であり、ぜひ実務を通して学んでいきたいと考えています。チーム開発でコードの保守性や、プロダクトで求められるセキュリティレベルなど、現場の要求にどう応えるかが私の次の挑戦です。

### 今後の活用法
LLMを活用することで、様々な分野に適応させることが出来ます。
LoRAファインチューニングの成功は、独自性や専門性の高い分野を、いかに質の高い学習データに変換できるかにかかっています。そのため、技術実装以上に、ドメイン知識の構造化とデータセット設計の知見を深めることが重要な差別化要因となると考えます。

### 次に取り組む事
- このプロジェクトで得た知見と自信をもとに、現在はさらに応用的な開発に挑戦しています

MCPクライアントを実際の開発工程を取り入れて作っています。
具体的なアプリケーションは、**パーソナライゼーション**をテーマにしたAndroidアプリです。
これも制作過程をメモしているので、また公開します。

<details>
<summary> MCPとは？</summary>

ChatGPTから引用

■ 概要
MCP（Model Context Protocol） とは、複数のAIモデル（特にLLM：大規模言語モデル）やエージェントが協調・連携する際に、共有される文脈（コンテキスト）やメタ情報を標準化して交換するためのプロトコルです。
目的は、マルチエージェント環境や複合モデル構成において、一貫性ある推論と制御を可能にすることです。
また、**異なるモデル間でも一貫した情報解釈と引き継ぎ**が可能になります。

■ 用語や概念が用いられる事例

| 事例                                     | 内容                                          |
| -------------------------------------- | ------------------------------------------- |
| **RAG + LLMの協調処理**                     | Retrieval部分と生成部分の間で、クエリ意図や履歴をMCP経由で共有       |
| **Agent間通信（Auto-GPT, OpenAgentsなど）**   | 各エージェントがMCPフォーマットで計画・状態・指示をやり取り             |
| **モジュール分離型AI（Planner + Executor構造など）** | 「何をするか（Plan）」と「どう実行するか（Tool）」を文脈として交換       |
| **MCP + Slack連携**                      | Slack側からのユーザー入力にMCPを付与し、LLMに「意図＋履歴＋環境」として渡す |
| **ローカルLLM連携**                          | MCPによってLoRAモデルやベースモデル間で統一したコンテキスト受け渡しが可能    |

このように、単なるプロンプトではなく**システム間での意味あるコンテキストの構造化と共有**がMCPの本質です。
</details>

<details>
<summary> パーソナライゼーションとは？</summary>

ChatGPTから引用
■ 概要
**パーソナライゼーション（Personalization）** とは、ユーザーごとの特性や嗜好、行動履歴などに基づいて、**最適化された情報や体験を提供する手法や概念**です。主にマーケティング、ウェブサービス、AI、教育、医療など幅広い分野で活用されます。
■ 詳細説明

パーソナライゼーションは、ユーザー中心の設計思想に基づいています。主な仕組みは以下のように分類されます：

1. **データ収集**

* **明示的データ（Explicit Data）**：年齢、性別、好みなど、ユーザーが直接入力した情報
* **暗黙的データ（Implicit Data）**：閲覧履歴、クリック履歴、購入履歴など、ユーザーの行動から得られる情報

2. **分析と推論**

* 行動履歴の分析（例：過去に購入した商品からの推薦）
* 機械学習・統計モデルによるクラスタリングやレコメンド
* ルールベースやA/Bテストでの評価

3. **応用の形式**

* **コンテンツの最適化**：ニュースフィード、広告、動画推薦などをユーザーに合わせて変更
* **UI/UXのカスタマイズ**：ダッシュボードや表示レイアウトをユーザーの使用状況に応じて調整
* **マーケティング**：メールやクーポンの内容を個別に変化させる
* **医療**：患者ごとの診断・治療法の最適化（プレシジョン・メディスン）
* **教育**：個別学習スタイルに応じた教材推薦（アダプティブラーニング）

■ 用語や概念が用いられる事例

| 分野           | 事例                        | 説明                       |
| ------------ | ------------------------- | ------------------------ |
| **Eコマース**    | Amazonのレコメンド              | 購買履歴や閲覧履歴に基づいて商品を推薦      |
| **SNS・メディア** | YouTubeのおすすめ動画            | 視聴時間・評価などをもとに動画を自動選別     |
| **教育**       | スマートラーニング                 | 学力や理解度に応じた教材提示（例：atama+） |
| **医療**       | 個別化医療（Precision Medicine） | 遺伝子情報や生活習慣に基づく治療計画       |
| **マーケティング**  | メール配信のセグメンテーション           | 顧客属性ごとに内容や配信時間を最適化       |

</details>

---

## 8.最後に

このプロジェクトを通して、LoRAのコードをゼロから手書きするスキルが直接的に身についたわけではありません。
 しかし、「コードを書く」という行為の本質を問い直し、AIというツールを最大限に活用して目的を達成する、という新しいエンジニアリングの形を実践できたことは、何より大きな収穫でした。
 
また、自身のアウトプット力をエンジニアという土俵に立った時、
どこまで出力できるのか試せるいい機会でしたし、
「AIを使って学ぶ」という姿勢は、自分にとって非常に有意義な手段であると実感しました。

AIというトレンドの中でも、LoRAファインチューニングは構成原理やアプローチ手法を深く学べるテーマであり、  
現代には、初学者でも手をつけられる環境が十分に整っています。

また、「問いの質が成果を決める」というシンプルな真実に触れたことで、  
初学者にとって不足しがちな **"質問力"** を磨く大きな学びにもなりました。

同じようにAIを使って何かを作りたいと考えている方の一助になれば幸いです。  
そして私自身も、この経験をもとにさらなる応用と貢献へと挑戦を続けていきたいと思います。
最後までお読みいただき、ありがとうございました。
<br>

ご意見や懸念点があれば、ぜひコメント等でお知らせいただければ幸いです。
今後の記事や活動に活かしていきたいと考えています。

### 連絡先
本プロジェクトや私自身にご興味を持っていただけましたら、下記よりお気軽にご連絡ください。
- **Email**: `ry.miyake.worker@gmail.com`
- **LinkedIn**: [ryoto-miyake-954a3936a](https://www.linkedin.com/in/ryoto-miyake-954a3936a)