3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AIがあなたのPCでコードを書く!? ローカルLLMの衝撃と可能性を徹底検証!

Last updated at Posted at 2025-05-29

この記事はどうしてもChatGPTみたいなものを無料でローカルで動かしたいあまりITスキルがない人向けです。
Geminiで十分という人には向きませんのでご注意ください。

こんにちは! 最近「AI」という言葉を耳にしない日はないですよね。ChatGPTのようなAIとチャットしたり、画像を生成したり、私たちの生活はAIによって劇的に変化しつつあります。しかし、これらのAIサービスの多くはインターネット上の「クラウド」と呼ばれる場所で動いていて、使うためには常にインターネット接続が必要です。

「自分のパソコンでAIを動かせたらもっと便利なのに…」

そう思ったことはありませんか? 実はそれが、もう夢物語ではありません! 近年、「ローカルLLM(Large Language Model:大規模言語モデル)」と呼ばれる技術が注目を集めています。これは、皆さんがお使いのパソコン上で直接AIを動かすことができる技術のこと。インターネット接続なしでAIが使えたり、プライバシーが守られたり、その可能性は無限大です。

今回は、そんなローカルLLMの具体的な利用例として、「ウェブサイトのコードをAIに書いてもらう」という実験に挑戦しました。しかも、一口にAIと言っても様々な種類があるんです。今回は3つの異なるAIモデルを使って、同じ指示(プロンプトと呼びます)を与え、どんなコードを生成してくれるのかを徹底的に比較分析してみました。

「難しそう…」と感じた方も大丈夫! 私のようなITスキル初級者の方にも分かりやすいように、専門用語はできるだけ避け、順序立てて丁寧に説明していきますね。さあ、あなたのパソコンでAIがコードを書く、その驚きの世界を一緒に見ていきましょう!


そもそも「ローカルLLM」って何? なぜ注目されているの?

まずは、今回の主役である「ローカルLLM」について、簡単に説明させてください。

LLM(大規模言語モデル)のおさらい

「LLM」とは、大量のテキストデータを学習することで、人間が話す言葉(自然言語)を理解し、文章を生成できるAIのことです。皆さんがよくご存知のChatGPTも、このLLMの一種です。私たちが質問すると、まるで人間が書いたかのような自然な文章で答えてくれますよね。

クラウドLLMとローカルLLMの違い

一般的なLLMサービスの多くは、インターネットの向こう側にある強力なコンピューター(サーバー)で動いています。これを「クラウドLLM」と呼びます。例えば、ChatGPTに質問を投げかけると、あなたの質問はインターネットを通じてOpenAI社のサーバーに送られ、そこでAIが処理を行い、生成された回答が再びインターネットを通じてあなたの元へ送られてくる、という仕組みです。

対して「ローカルLLM」は、あなたの手元のパソコンやスマートフォンで直接AIを動かす技術です。つまり、インターネット接続がなくてもAIを使うことができます。

ローカルLLMの魅力とメリット

なぜ今、ローカルLLMがこれほど注目されているのでしょうか? いくつか大きなメリットがあります。

  1. インターネット接続不要: 旅行中や電波の届かない場所でも、AIを使うことができます。
  2. プライバシー保護: あなたがAIに入力したデータは、あなたのパソコンの外に出ることはありません。機密情報や個人情報などを扱う際も、情報漏洩のリスクを最小限に抑えることができます。
  3. コスト削減: クラウドLLMの多くは、利用量に応じて料金が発生します。ローカルLLMなら、一度モデルをダウンロードしてしまえば、基本的に追加費用はかかりません(ただし、PCの電気代はかかりますが!)。
  4. カスタマイズ性: モデルによっては、特定の用途に合わせて追加学習(ファインチューニング)を行うことで、より専門的なAIを自分の手元で育てることができます。
  5. 透明性: オープンソースのモデルの場合、どのように作られているかを確認することができます。

もちろん、ローカルLLMには「パソコンの性能がある程度必要」「モデルのダウンロードに時間がかかる」といったデメリットもありますが、それを補って余りある魅力があると言えるでしょう。

GPT4ALLとは?

今回の実験で利用した「GPT4ALL」は、そんなローカルLLMを簡単にあなたのパソコンで動かすための無料のソフトウェア(アプリケーション)です。様々なローカルLLMモデルをダウンロードして、GPT4ALL上でチャット形式で利用することができます。難しい設定はほとんど不要なので、AI初心者の方でも比較的簡単に試すことができます。


実験概要:AIに「単位変換ウェブサイト」のコードを書いてもらう!

今回の実験では、ローカルLLMの「コード生成能力」を検証するため、以下のようなプロンプト(AIへの指示文)を与えました。

プロンプト(AIへの指示)

「ポンドをキログラムに変換するなど、欧米で利用されている単位10個を日本で利用されている単位に変換するHTML、CSS、JSで構成された1枚のHTMLスニペットを省略なしの完全な形で教えて」

この指示には、いくつかのポイントがあります。

  • 具体的な機能: 「単位変換」
  • 変換対象: 「欧米で利用されている単位10個」を「日本で利用されている単位」に変換
  • 使用技術: 「HTML」「CSS」「JS(JavaScript)」の3つの技術を組み合わせる
  • 出力形式: 「1枚のHTMLスニペット」「省略なしの完全な形」

つまり、AIには、「ポンドをキログラムに変換するだけでなく、インチをセンチメートルに、ガロンをリットルに…といった具合に、計10種類の単位変換ができるウェブページを、HTMLファイル1つで完結するように作ってね」というお願いをしたわけです。

実験環境

今回の実験は、以下の環境で行いました。

  • ミニPC: CPU: Ryzen 7 5825U / RAM: 32GB / SSD: Nvme 512GB / OS: Windows 11
  • AI実行ソフトウェア: GPT4ALL (最新バージョン 3.10)
  • AIモデルに割り当てたスレッド数: 10スレッド (GPT4ALLの設定で変更)

特に重要なのは、「外付けGPUは接続できず、CPUで実行している」いう点です。多くのAIモデルはGPU(グラフィックボード)を使うことで高速に動作しますが、今回の環境ではCPUの力だけでAIを動かしています。設定上、CPU内蔵のグラフィック機能を使うオプションもありましたが、今回はCPUの計算能力のみを利用して試しました。これにより、一般的なノートパソコンに近い環境での性能を評価することができます。


比較対象の3つのAIモデルたち

今回の実験では、GPT4ALLで利用できる様々なモデルの中から、以下の3つのモデルを比較対象として選びました。いずれも、ローカルで動かすには比較的高性能なPC(RAM 16GB以上)が推奨されるモデルです。

1. Orca 2 (Full)

  • モデルタイプ: LLaMA2をベースにした、指示(Instruction)に特化したモデル。
  • パラメータ数: 130億(13B)
  • 特徴: Microsoftがトレーニングしたモデルで、指示の理解と応答に優れるとされています。
  • ライセンス: 商用利用不可

2. DeepSeek-R1-Distill-Qwen-14B

  • モデルタイプ: Qwen2.5-14Bという大規模なモデルをベースに「推論能力」を強化した蒸留モデル。
  • パラメータ数: 140億(14B)
  • 特徴: 論理的な推論や複雑なタスクの理解に強みを持つとされています。
  • ライセンス: MITライセンス(商用利用可能)

3. mmnga/cyberagent-DeepSeek-R1-Distill-Qwen-14B-Japanese-gguf

  • モデルタイプ: 上記のDeepSeek-R1-Distill-Qwen-14Bを日本語に特化してファインチューニング(追加学習)したモデル。
  • パラメータ数: 140億(14B)
  • 特徴: 日本語での自然な対話や生成に強みを持つことが期待されます。
  • ライセンス: MITライセンス(商用利用可能)

それぞれのモデルが持つ特性が、コード生成というタスクにどう影響するのか、非常に興味深いですね。


実験結果と各モデルのコード比較・詳細分析

それでは、いよいよ実験結果です。それぞれのモデルが生成したコードと、それに対する私の評価を詳しく見ていきましょう。

1. Orca 2 (Full) の結果

<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Unit Conversion</title>
    <style>
      /* Set the column widths to 40% each */
      .column {
        width: 40%;
      }
      
      /* Add some margin around the columns */
      .column {
        margin-bottom: 20px;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <div class="row">
        <div class="column">
          <h3>Imperial Units</h3>
          <ul>
            <li><img src="pound.png" alt="1 pound" /> Pound</li>
            <li><img src="cup.png" alt="1 cup (US)" /> Cup (US)</li>
            <li><img src="quart.png" alt="1 quart" /> Quart</li>
            <li><img src="gallon.png" alt="1 gallon (US)" /> Gallon (US)</li>
          </ul>
        </div>
        <div class="column">
          <h3>Metric Units</h3>
          <ul>
            <li><img src="kilogram.png" alt="1 kilogram" /> Kilogram</li>
            <li><img src="liter.png" alt="1 liter" /> Liter</li>
            <li><img src="meter.png" alt="1 meter" /> Meter</li>
            <li><img src="second.png" alt="1 second" /> Second</li>
          </ul>
        </div>
      </div>
      
      <h3>Convert Between Units</h3>
      <form id="unit-conversion">
        <label for="from-unit">From:</label>
        <select id="from-unit" name="from-unit">
          <option value="pound">Pound</option>
          <option value="cup (US)">Cup (US)</option>
          <option value="quart">Quart</option>
          <option value="gallon (US)">Gallon (US)</option>
        </select>
        
        <label for="to-unit">To:</label>
        <select id="to-unit" name="to-unit">
          <option value="kilogram">Kilogram</option>
          <option value="liter">Liter</option>
          <option value="meter">Meter</option>
          <option value="second">Second</option>
        </select>
        
        <button type="submit">Submit</button>
      </form>
      
      <div class="row">
        <div class="column">
          <h3>Converted Values</h3>
          <ul id="converted-values"></ul>
        </div>
      </div>
    </div>
  </body>
</html>

  • 感想:

    • 多くの単位変換を回答しようとする意図はあったものの、JavaScript(JS)部分が完全に欠落していました。
    • コード自体は英語で書かれており、UIの言語も英語でした。
    • 単位の横に画像のパスが指定されており、別途画像の用意が必要で、簡単に試すには不便でした。
    • 回答を書き始めるまでに約30秒以上かかり、書き終えるまでは2分以内と、他のモデルに比べて比較的速い印象でした。
  • 詳細分析:
    Orca 2は、プロンプトの「HTML、CSS、JSで構成された1枚のHTMLスニペットを省略なしの完全な形で教えて」という要求に対し、HTMLとCSSの基本的な構造は提供しましたが、最も重要な「JS」の部分を生成できませんでした。

    この結果から、Orca 2の以下の特性が見えてきます。

    • プロンプト解釈の浅さ: 「ポンドをキログラムに変換するなど」という機能要求を、単に「単位のリスト表示」と「変換のためのUI要素(フォーム)」と解釈し、その裏にある具体的な計算ロジックの実装の重要性を認識できなかった可能性が高いです。まるで、ウェブサイトの「見た目」は理解したが、「動き」までは理解できなかった、という感じです。
    • コード生成能力の限界: 複雑なプログラミングロジック(JavaScriptでの単位換算式の実装や、入力値の取得・表示更新)を生成する能力が不足していることを示唆しています。Instruction basedモデルとはいえ、すべての指示を深く理解し、正確なコードに落とし込むのは難しいのかもしれません。
    • 高速性とのトレードオフ: 他のモデルより生成速度が速いのは、複雑なJSロジックを生成しなかったことと無関係ではないでしょう。複雑な処理をスキップすることで、応答速度を上げた可能性があります。これは、「早く出すこと」を優先した結果、機能が欠落してしまった、と考えることができます。
    • 外部リソースへの依存: <img>タグにsrc="pound.png"のように画像パスを指定している点も特徴的です。これは、モデルが一般的なウェブページの構造や要素を学習していることを示しますが、AIがコードを生成する際に、それらの画像を一緒に生成することはできません。そのため、生成されたコードを実際に動かすためには、別途画像を用意する必要があり、実用性という点で手間がかかる結果となりました。
    • 英語優先: <html lang="en"> となっており、UI要素も英語です。これは、Microsoftがトレーニングしたモデルであり、主に英語のデータで学習されているためと考えられます。

2. mmnga/cyberagent-DeepSeek-R1-Distill-Qwen-14B-Japanese-gguf の結果

<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>単位変換ツール</title>
    <style>
        .converter {
            max-width: 400px;
            margin: 2rem auto;
            padding: 1.5rem;
            background-color: #f9f9f9;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }

        .input-group {
            margin-bottom: 1rem;
        }

        label {
            display: block;
            margin-bottom: 0.5rem;
            font-weight: bold;
        }

        input[type="number"] {
            width: 20%;
            padding: 0.3rem;
            border: 1px solid #ddd;
            border-radius: 4px;
        }

        select {
            width: 78%;
            padding: 0.3rem;
            margin-left: 0.5rem;
            border: 1px solid #ddd;
            border-radius: 4px;
        }

        button {
            background-color: #4CAF50;
            color: white;
            padding: 0.8rem 2rem;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
        }

        button:hover {
            background-color: #45a049;
        }

        .result {
            margin-top: 1rem;
            padding: 1rem;
            background-color: white;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
    </style>
</head>
<body>
    <div class="converter">
        <h2>単位変換ツール</h2>

        <div class="input-group">
            <label for="pounds">ポンド (lb) → キログラム (kg)</label>
            <input type="number" id="poundInput" placeholder="数値を入力">
            <select id="unitSelect1">
                <option value="lbs_to_kg">ポンドからキログラム</option>
            </select>
        </div>

        <div class="input-group">
            <label for="centimeters">センチメートル (cm) → インチ (in)</label>
            <input type="number" id="inchInput" placeholder="数値を入力">
            <select id="unitSelect2">
                <option value="cm_to_inch">センチメートルからインチ</option>
            </select>
        </div>

        <button onclick="convertUnits()">変換する</button>

        <div class="result" id="result"></div>
    </div>

    <script>
        const conversionRates = {
            lbs_to_kg: 0.4536,
            cm_to_inch: 0.3937
        };

        function convertUnits() {
            // ポンドからキログラム変換
            if (document.getElementById('unitSelect1').value === 'lbs_to_kg') {
                const pounds = parseFloat(document.getElementById('poundInput').value);
                const kg = pounds * conversionRates.lbs_to_kg;
                document.getElementById('result').innerHTML = 
                    `${pounds}ポンドは約${kg.toFixed(2)}キログラムです`;
            }

            // センチメートルからインチ変換
            if (document.getElementById('unitSelect2').value === 'cm_to_inch') {
                const cm = parseFloat(document.getElementById('inchInput').value);
                const inches = cm * conversionRates.cm_to_inch;
                document.getElementById('result').innerHTML =
                    `${cm}センチメートルは約${inches.toFixed(2)}インチです`;
            }
        }

        // 初期表示用
        convertUnits();
    </script>
</body>
</html>

  • 感想:

    • 日本語のUIで生成された点は非常に優れていました。タイトルも「単位変換ツール」と適切でした。
    • しかし、プロンプトで「単位10個」と指定したにもかかわらず、ポンド→キログラムとセンチメートル→インチの2種類の変換しか実装されていませんでした。
    • JavaScriptコードも含まれていましたが、ボタンを押しても期待通りに動作しないなど、不自然な点がありました。具体的には、2つの変換機能のうち、最後に実行されたものしか結果が表示されない問題がありました。
    • 回答を書き始めるまでに約100秒以上かかり、書き終えるまでも数分と、Orca 2よりは時間がかかりました。
  • 詳細分析:
    このモデルはDeepSeek-R1-Distill-Qwen-14Bをベースに日本語に特化してファインチューニング(追加学習)されたモデルです。その日本語への対応力は非常に高く、日本語のUI生成では最も優れていました。

    しかし、いくつか課題も見て取れます。

    • プロンプトの数量指定の無視: 最も大きな課題は、「単位10個」という明確な数量指定を無視して、2つの単位変換しか実装しなかった点です。これは、モデルが複雑なプロンプトの細部までを正確に把握する能力、あるいは指定された数の機能を実装しきる能力に限界があることを示唆しています。日本語の学習データが、プログラミングにおける「数」の概念を正確に捉えるのに十分でなかった可能性も考えられます。
    • JavaScriptロジックの不備: 生成されたJavaScriptコードには、機能的なバグがありました。特に、document.getElementById('result').innerHTML = ... の部分が、常に最後の変換結果で上書きされてしまうため、複数の変換結果を同時に表示できません。また、<select>要素の使い方も不自然で、実質的に選択肢が1つしかないため、単位を選んで変換する「ツール」としては機能していません。これは、モデルが単にコードの構文を生成するだけでなく、そのコードが「機能的に正しく、かつ効率的に動作するか」という点での理解がまだ不十分であることを示しています。
    • 学習データの影響: 日本語に特化するために追加学習がされたことで、自然な日本語での出力は得意になったものの、プログラミングロジックの複雑な部分の精度がベースモデルと比較して低下した可能性も考えられます。特定の言語に特化する「ファインチューニング」は、モデルの得意分野を広げる一方で、元々持っていた汎用的な能力(この場合はコードの正確性)に影響を与えることもあります。
    • 推論速度: ベースモデルと同じDeepSeek系であるため、回答開始までの時間が長い(100秒以上)のは共通の特徴です。しかし、日本語特化によるわずかな処理の差か、生成するコードの量が少ないためか、ベースモデルよりは若干速いという結果でした。

3. DeepSeek-R1-Distill-Qwen-14B の結果

<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>単位変換</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 600px;
            margin: 20px auto;
            padding: 15px;
            background-color: #f0f0f0;
        }

        .container {
            background-color: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
        }

        h1 {
            color: #333;
            text-align: center;
        }

        .converter-group {
            margin-bottom: 20px;
        }

        label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
            color: #666;
        }

        input {
            width: 100%;
            padding: 8px;
            border: 1px solid #ddd;
            border-radius: 4px;
            box-sizing: border-box;
        }

        button {
            background-color: #4CAF50;
            color: white;
            padding: 12px 24px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
        }

        button:hover {
            background-color: #45a049;
        }

        .result {
            margin-top: 20px;
            padding: 15px;
            background-color: #f8f8f8;
            border-radius: 4px;
            border: 1px solid #ddd;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>単位変換</h1>

        <div class="converter-group">
            <label for="inch">インチ:</label>
            <input type="number" id="inch" placeholder="请输入英寸数">
            <button onclick="convertInch()">計算</button>
            <div id="result-inch"></div>
        </div>

        <div class="converter-group">
            <label for="foot">フート:</label>
            <input type="number" id="foot" placeholder="请输入フート数">
            <button onclick="convertFoot()">計算</button>
            <div id="result-foot"></div>
        </div>

        <div class="converter-group">
            <label for="yard">ヤード:</label>
            <input type="number" id="yard" placeholder="请输入ヤード数">
            <button onclick="convertYard()">計算</button>
            <div id="result-yard"></div>
        </div>

        <div class="converter-group">
            <label for="mile">マイル:</label>
            <input type="number" id="mile" placeholder="请输入マイル数">
            <button onclick="convertMile()">計算</button>
            <div id="result-mile"></div>
        </div>

        <div class="converter-group">
            <label for="pound">ポンド:</label>
            <input type="number" id="pound" placeholder="请输入ポンド数">
            <button onclick="convertPound()">計算</button>
            <div id="result-pound"></div>
        </div>

        <div class="converter-group">
            <label for="ounce">オウンス:</label>
            <input type="number" id="ounce" placeholder="请输入オンス数">
            <button onclick="convertOunce()">計算</button>
            <div id="result-ounce"></div>
        </div>

        <div class="converter-group">
            <label for="gallon">ガロン:</label>
            <input type="number" id="gallon" placeholder="请输入ガロン数">
            <button onclick="convertGallon()">計算</button>
            <div id="result-gallon"></div>
        </div>

        <div class="converter-group">
            <label for="quart">クwart:</label>
            <input type="number" id="quart" placeholder="请输入クwart数">
            <button onclick="convertQuart()">計算</button>
            <div id="result-quart"></div>
        </div>

        <div class="converter-group">
            <label for="pint">Pint:</label>
            <input type="number" id="pint" placeholder="请输入Pint数">
            <button onclick="convertPint()">計算</button>
            <div id="result-pint"></div>
        </div>

        <div class="converter-group">
            <label for="fahrenheit">Fahrenheit:</label>
            <input type="number" id="fahrenheit" placeholder="请输入温度数">
            <button onclick="convertFahrenheit()">計算</button>
            <div id="result-fahrenheit"></div>
        </div>

        <button onclick="clearAll()" style="width: 100%; margin-top:20px;">クリア</button>
    </div>

    <script>
        function convertInch() {
            const inch = parseFloat(document.getElementById('inch').value);
            if (isNaN(inch)) {
                showResult('result-inch', '※有効な数値を入力してください');
                return;
            }
            const cm = inch * 2.54;
            showResult('result-inch', `${inch.toFixed(2)}インチは${cm.toFixed(2)}cmです`);
        }

        function convertFoot() {
            const foot = parseFloat(document.getElementById('foot').value);
            if (isNaN(foot)) {
                showResult('result-foot', '※有効な数値を入力してください');
                return;
            }
            const meter = foot * 0.3048;
            showResult('result-foot', `${foot.toFixed(2)}フートは${meter.toFixed(2)}メートルです`);
        }

        function convertYard() {
            const yard = parseFloat(document.getElementById('yard').value);
            if (isNaN(yard)) {
                showResult('result-yard', '※有効な数値を入力してください');
                return;
            }
            const meter = yard * 0.9144;
            showResult('result-yard', `${yard.toFixed(2)}ヤードは${meter.toFixed(2)}メートルです`);
        }

        function convertMile() {
            const mile = parseFloat(document.getElementById('mile').value);
            if (isNaN(mile)) {
                showResult('result-mile', '※有効な数値を入力してください');
                return;
                return;
            }
            const km = mile * 1.60934;
            showResult('result-mile', `${mile.toFixed(2)}マイルは${km.toFixed(2)}キロメートルです`);
        }

        function convertPound() {
            const pound = parseFloat(document.getElementById('pound').value);
            if (isNaN(pound)) {
                showResult('result-pound', '※有効な数値を入力してください');
                return;
            }
            const kg = pound * 0.453592;
            showResult('result-pound', `${pound.toFixed(2)}ポンドは${kg.toFixed(2)}キログラムです`);
        }

        function convertOunce() {
            const ounce = parseFloat(document.getElementById('ounce').value);
            if (isNaN(ounce)) {
                showResult('result-ounce', '※有効な数値を入力してください');
                return;
            }
            const gram = ounce * 28.3495;
            showResult('result-ounce', `${ounce.toFixed(2)}オンスは${gram.toFixed(2)}グラムです`);
        }

        function convertGallon() {
            const gallon = parseFloat(document.getElementById('gallon').value);
            if (isNaN(gallon)) {
                showResult('result-gallon', '※有効な数値を入力してください');
                return;
            }
            const liter = gallon * 3.78541;
            showResult('result-gallon', `${gallon.toFixed(2)}ガロンは${liter.toFixed(2)}リットルです`);
        }

        function convertQuart() {
            const quart = parseFloat(document.getElementById('quart').value);
            if (isNaN(quart)) {
                showResult('result-quart', '※有効な数値を入力してください');
                return;
            }
            const liter = quart * 0.946353;
            showResult('result-quart', `${quart.toFixed(2)}クwartは${liter.toFixed(2)}リットルです`);
        }

        function convertPint() {
            const pint = parseFloat(document.getElementById('pint').value);
            if (isNaN(pint)) {
                showResult('result-pint', '※有効な数値を入力してください');
                return;
            }
            const ml = pint * 473.1562;
            showResult('result-pint', `${pint.toFixed(2)}Pintは${ml.toFixed(0)}mlです`);
        }

        function convertFahrenheit() {
            const fahrenheit = parseFloat(document.getElementById('fahrenheit').value);
            if (isNaN(fahrenheit)) {
                showResult('result-fahrenheit', '※有効な数値を入力してください');
                return;
            }
            const celsius = (fahrenheit - 32) * 5/9;
            showResult('result-fahrenheit', `${fahrenheit.toFixed(1)}°Fは${celsius.toFixed(1)}°Cです`);
        }

        function showResult(id, message) {
            document.getElementById(id).innerHTML = message;
        }

        function clearAll() {
            const inputs = document.querySelectorAll('input');
            inputs.forEach(input => input.value = '');
            const results = document.querySelectorAll('.result');
            results.forEach(result => result.innerHTML = '');
        }
    </script>
</body>
</html>

  • 感想:

    • プロンプトの「単位10個」という要求に対し、見事に10個の単位変換を実装しようとしました。私の意図に最も合致した回答です。
    • HTML、CSS、JavaScriptのすべてが含まれており、コードとして最も実用的な形で提供されました。
    • ユーザーインターフェースは日本語でしたが、入力欄のPlaceholder(入力例のテキスト)が中国語になっていました。
    • 回答を書き始めるまでに約100秒以上かかり、書き終えるまでも数分と、今回試した中で最も時間がかかりました。
  • 詳細分析:
    このモデルは、今回の実験で最も優秀な結果を出しました。

    • プロンプト理解度と「推論能力」の高さ: DeepSeek-R1-Distill-Qwen-14Bは、その名前の通り「推論能力(Reasoning Capability)」の強化を目指しているモデルです。今回の実験では、プロンプトの「欧米で利用されている単位10個」という具体的な数量指定を正確に解釈し、実際に10個の単位変換ロジックを実装できた点が、その高い推論能力を裏付けています。まるで、人間がプロンプトを読んで、それをどう実現するかを論理的に考えたかのようなコード構成です。
    • コードの機能的正確性と実用性: 生成されたコードは、各単位変換が独立した関数としてきちんと実装されており、それぞれに対応する入力欄と結果表示エリアが用意されています。これにより、複数の単位変換を同時に試すことができる、実用的なウェブページが生成されました。数値が入力されなかった場合の基本的なエラーハンドリング(isNaNチェック)も含まれており、細かい気配りも見られます。
    • 言語特性の残り香: <html lang="ja"> と日本語UIを生成しているにも関わらず、入力欄のPlaceholderが「请输入英寸数」(インチ数を入力してください、の中国語)となっていました。これは、モデルが中国語のデータを非常に多く学習しているQwenシリーズをベースにしているため、UIの一部にその痕跡が残ってしまったと考えられます。日本語にローカライズされたモデルではないため、完璧な日本語UIは期待できませんが、この点は面白い現象です。
    • 推論速度と品質のトレードオフ: 最も完成度の高いコードを生成した一方で、回答開始までの時間(100秒以上)と、生成完了までの時間(数分)は今回の中で最も遅い結果となりました。これは、モデルがプロンプトを深く解析し、複雑で正確なコード構造を生成するために、内部でより多くの計算や推論を行っているためと考えられます。時間をかけることで、より高品質で実用的な出力が得られる、という良い例です。開発者としては、多少時間がかかっても良いコードが欲しい場面は多いので、このトレードオフは受け入れやすいでしょう。


総合的な比較分析と考察

今回の実験結果を総合的に見てみましょう。

1. プロンプトの理解度と機能実装の「深さ」

  • DeepSeek-R1-Distill-Qwen-14B: プロンプトの「10個の単位」という数量指定と「変換する」という機能要求の両方を最も正確に理解し、実装しました。まさに「痒い所に手が届く」ような賢さを見せました。
  • mmnga/cyberagent-DeepSeek-R1-Distill-Qwen-14B-Japanese-gguf: 日本語のローカライズは完璧でしたが、「10個の単位」という数量指定を無視し、実装されたコードにもバグがありました。プロンプトの「日本語」という側面はよく理解したものの、「機能性」の側面で精度に欠ける結果となりました。
  • Orca 2 (Full): HTMLとCSSの骨組みは生成したものの、最も重要なJavaScriptによる機能実装を完全に省略してしまいました。プロンプトの意図を最も浅く解釈したと言えるでしょう。

この違いは、モデルがプロンプトをどの程度「深く」理解し、それを具体的な「コード」に落とし込むことができるかという、LLMのコード生成能力の根幹に関わる部分です。単純なキーワードマッチングだけでなく、プロンプト全体の文脈、特に具体的な機能要求や制約を論理的に分析する能力が、DeepSeek-R1-Distill-Qwen-14Bでは際立っていました。

2. 生成コードの「実用性」と「品質」

  • DeepSeek-R1-Distill-Qwen-14B: 生成されたコードは、一部のUIテキストに中国語が残っていたものの、機能的には最も完成度が高く、すぐに使えるレベルでした。エラーハンドリングも含まれており、実用性が高いと言えます。
  • mmnga/cyberagent-DeepSeek-R1-Distill-Qwen-14B-Japanese-gguf: 日本語UIは完璧でしたが、肝心のJavaScriptにバグがあり、期待通りに動作しませんでした。コードの品質という点では、まだ人間の手によるデバッグや修正が必須となるレベルです。
  • Orca 2 (Full): JavaScriptが欠落しているため、機能的には全く実用性がありませんでした。あくまでUIの「ひな形」を生成したに過ぎません。

LLMが生成するコードの価値は、単に「コードが出力されること」ではなく、「そのコードがどれだけ正しく機能し、すぐに利用できるか」という「品質」にかかっています。この点で、DeepSeek-R1-Distill-Qwen-14Bは非常に優れていました。

3. 言語とローカライズの影響

  • DeepSeek-R1-Distill-Qwen-14B: 日本語UIを生成しつつも、中国語のPlaceholderが残ったのは、そのモデルの学習データのベースが中国語圏に由来することを明確に示しています。多言語対応モデルでは、このように特定の言語の「痕跡」が残る場合があります。
  • mmnga/cyberagent-DeepSeek-R1-Distill-Qwen-14B-Japanese-gguf: 日本語特化モデルの強みが最大限に発揮されたのは、完璧な日本語UIの生成でした。しかし、その分、プログラミングロジックの精度に影響が出た可能性も考えられます。言語に特化させることで得られるメリットと、失われるかもしれない汎用性や正確性のトレードオフが垣間見えます。
  • Orca 2 (Full): 完全に英語のUIを生成しました。これは、英語を主とした学習データの影響が強い典型的な例です。

私たちがAIに求める機能は、「自然な対話」「正確な情報処理」の二つに大きく分けられます。日本語に特化したモデルは「自然な対話」において優位性を示しますが、「正確な情報処理」(今回の場合はプログラミングロジック)においては、その特化が必ずしもプラスに働くとは限らない、という複雑な側面があることが示唆されました。

4. 推論速度とリソース消費(CPU実行の現実)

今回の実験は、外付けGPUなしのCPU実行という、一般的なPC環境に近い条件で行われました。

  • 推論開始までの時間: DeepSeek系モデル(DeepSeek-R1-Distill-Qwen-14Bとmmnga/cyberagent-DeepSeek-R1-Distill-Qwen-14B-Japanese-gguf)は、回答を書き始めるまでに100秒以上かかりました。これは、プロンプトを解析し、内部で複雑な推論を行うための「思考時間」とも言えるでしょう。一般的なAIサービス(ChatGPTなど)の瞬時の応答に慣れていると、この待機時間は長く感じるかもしれません。
  • 全体の生成時間: 全てのモデルで、回答を書き終えるまでに数分を要しました。これは、130億~140億という膨大なパラメータを持つモデルが、CPUだけで動いていることを考えれば妥当な時間と言えます。CPUは汎用的な処理が得意ですが、AIの計算に特化したGPUに比べると、どうしても処理速度で劣ります。

この結果は、ローカルLLMをCPUで実行する場合、「ある程度の待ち時間」は覚悟する必要があることを示しています。しかし、その待ち時間と引き換えに、インターネット接続なしでAIがコードを生成してくれるというメリットは非常に大きいです。例えば、プログラミング学習中に分からないコードがあった際に、すぐにAIに相談して解決策を提案してもらえるのは、学習効率を飛躍的に向上させるでしょう。

5. ミニPCでの実行における「メモリ16GB必須」の意味

今回の実験環境はRAM 32GBでしたが、各モデルは「メモリ16GB必須」とされていました。実際に動かしてみて、この16GBという数字が、ローカルLLMを快適に動かすための最低ラインであることがよく分かりました。

LLMは、その膨大なパラメータをメモリに展開して計算を行います。メモリが不足すると、パソコンはSSD(ストレージ)を一時的にメモリとして利用する「スワップ」という現象を起こします。SSDは高速とはいえ、RAMに比べると圧倒的に遅いため、スワップが発生すると処理速度が劇的に低下します。今回の「回答開始まで100秒以上」という遅延の一部は、CPUでの計算に加え、メモリ管理のオーバーヘッドも影響している可能性があります。

Ryzen 7 5825Uのような高性能なCPUであっても、AIの計算には大量のメモリと高速な処理能力が求められます。今回の実験は、「CPU単体でもローカルLLMは動くが、快適に使うためにはRAM 16GBは最低限、可能なら32GB以上が望ましい」という現実を示しています。

6. GPT4ALLの設定(10スレッド割当)の影響

GPT4ALLでCPUの10スレッドをAIに割り当てた設定は、モデルの推論速度に良い影響を与えたと考えられます。CPUのスレッド数が多いほど、AIの計算を並行して処理できるため、高速化が期待できます。しかし、スレッド数を増やしすぎると、かえってオーバーヘッドが発生し、性能が頭打ちになることもあります。今回の環境では、Ryzen 7 5825Uが8コア16スレッドのCPUなので、10スレッドの割り当てはバランスの取れた設定だったと言えるでしょう。


結論と今後のローカルLLMへの期待

今回の実験を通じて、ローカルLLMがコード生成というタスクにおいて、既に実用的なレベルに達していることが分かりました。特にDeepSeek-R1-Distill-Qwen-14Bのように、プロンプトの意図を深く理解し、高品質なコードを生成できるモデルは、私たちのプログラミング学習や開発作業を大きく助けてくれる可能性を秘めています。

一方で、

  • モデルによる得意不得意が大きいこと
  • 日本語対応と機能性の両立がまだ難しい場合があること
  • CPU実行では、クラウドLLMのような瞬時の応答は難しいこと

といった課題も明らかになりました。

しかし、これらの課題は、今後の技術の進化によって必ず解決されていくでしょう。

  • モデルの小型化と高性能化: より少ないパラメータで、より高い性能を発揮するモデルや、より効率的な量子化技術(モデルのデータサイズを小さくする技術)の開発が進むことで、より低スペックなPCでも快適にローカルLLMを動かせるようになるでしょう。
  • 推論エンジンの最適化: GPT4ALLのようなAI実行ソフトウェアや、AIモデルを動かすための内部的な仕組み(推論エンジン)の改善が進めば、CPUでもより高速な処理が可能になるはずです。
  • 日本語特化モデルの進化: 日本語のプログラミングに関する高品質な学習データが増え、より高度なファインチューニング手法が確立されれば、機能的にも完璧な日本語対応のコード生成モデルが誕生する日も近いでしょう。
  • ハイブリッドな利用: 将来的には、普段はローカルLLMを使ってプライバシーを保護しつつ、より高度な機能が必要な時だけクラウドLLMを利用するといった、ハイブリッドな使い方も一般化するかもしれません。

AIは、もはや遠い存在ではありません。あなたのパソコン上で、あなたの指示に従ってコードを書き、あなたのアイデアを形にする手助けをしてくれる、そんな未来はもうすぐそこまで来ています。

私みたいなITスキル初級者の方々も、GPT4ALLのようなツールを使えば、今回のようなAI実験に簡単に挑戦できます。ぜひ、ご自身のPCでローカルLLMを動かし、その驚くべき能力を体験してみてください。きっと、プログラミングやAIの可能性に、新たな興味が湧いてくるはずです!

これからもAI技術の進化から目が離せませんね。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?