とことんDevOps | 日本仮想化技術のDevOps技術情報メディア

DevOpsに関連する技術情報を幅広く提供していきます。

日本仮想化技術がお届けする「とことんDevOps」では、DevOpsに関する技術情報や、日々のDevOps業務の中での検証結果、TipsなどDevOpsのお役立ち情報をお届けします。
主なテーマ: DevOps、CI/CD、コンテナ開発、IaCなど
読者登録と各種SNSのフォローもよろしくお願いいたします。

Ornith 9BとQwythos 9B、日本語でコードを書かせるとどちらが強いか?

最近、SNSなどでLLMモデルの解説をする動画をよく観ます。 特にOrnith 9BとQwythos 9Bについてはモデルの評価が高く、気になっていました。

カタログスペックはモデルの配付ページのグラフを見ればわかりますが、実際使えるモデルなのかどうかは実際触ってみないとわかりません。ということで、手元の環境で比較してみることにしました。

なお今回の結果はあくまで試したコード生成タスクにおいての比較結果であるため、すべてのタスクにおいて「こちらのモデルが優秀」としているわけではない点に注意していただいて、この記事をごらんいただければと思います。

テストの条件

次の環境でテストを行いました。生成したコードについては手元にインストール済みのPython 3.12で動作するかテストしました。

  • 実行環境
    • Ollama
  • テストモデル
    • Ornith 9B
    • Qwythos-9B-v2-GGUF Q4_K_M
  • プロンプトは両モデルで同一
  • 生成後は実際に実行して動作確認

テストに使ったQwythos 9Bは、ループバグを修正したV2を使っています。 Ornith 9Bは公式が配布しているモデルを使っています。量子化はどちらもQ4_K_Mでの比較です。

今回のテストにおける実際の結果は次の通りでした。

項目 Ornith 9B Qwythos 9B
日本語コード生成 5/5 (そのまま動作) 2/5 (修正が必要)
英語コード生成 4/5 (スコープ修正のみ) 4/5 (ロジックに問題)
主な問題点 スコープ 勝敗判定
修正量 少ない 多い

※点数は今回の「じゃんけんゲーム」のコード生成タスクをもとにした筆者の評価です。

ここからは、実際にどのような違いがあったのかを詳しく見ていきます。

コード品質比較

Ornith:9b 日本語プロンプト

「Python3で動作するじゃんけんゲームのコードを作りたい。5回勝負で最後に勝敗の集計を行うようなコードで。」のプロンプトで生成されたコードは以下の通りです。

コードのざっくり評価は次の通りです。

  • 生成したコードは仕様通り動作
  • 不正入力はちゃんとブロックした

Ornith 9Bが生成したコード

# -*- coding: utf-8 -*-
"""
じゃんけんゲーム (5回勝負)
"""

import random


def get_choice():
    """プレイヤーの入力を取得し、正規化する"""
    print("グー、チョキ、パーから選んでください")
    choice = input("> ").strip()
    choices = {"グー": 0, "チョキ": 1, "パー": 2}
    while choice not in choices:
        print("不正な入力は無視します。もう一度入力してください")
        choice = input("> ").strip()
    return choices[choice]

def get_computer_choice():
    """コンピュータのチョイス"""
    return random.choice([0, 1, 2])


def judge(player: int, computer: int) -> str:
    """勝敗を判定 (0: グー, 1: チョキ, 2: パー)

    Returns
    -------
    str : "win", "lose", "draw" のいずれか
    """
    if player == computer:
        return "draw"
    # (グー→チョキ→パー→グー) → 勝敗の順序を固定する
    diff = (player - computer) % 3
    return "win" if diff == 1 else "lose"

def main():
    scores = {"win": 0, "draw": 0, "lose": 0}

    for i in range(5):
        print(f"\n--- {i + 1} 戦目 ---")
        player_choice = get_choice()
        computer_choice = get_computer_choice()

        # 表示用ラベル
        labels = {"win", "lose", "draw"}

        result = judge(player_choice, computer_choice)
        print(f"プレイヤー: {player_choice} | コンピュータ: {computer_choice}")
        scores[result] += 1

    # 集計結果を表示
    total = sum(scores.values())
    print("\n========== 最終成績 ==========")
    for name, count in scores.items():
        print(f"{name}: {count} 回")
    print(f"合計: {total} 戦")

if __name__ == "__main__":
    main()

Qwythos-9B 日本語プロンプト

「Python3で動作するじゃんけんゲームのコードを作りたい。5回勝負で最後に勝敗の集計を行うようなコードで。」のプロンプトで生成されたコードは以下の通り。

コードのざっくり評価は次の通りです。

  • Qwythos-9B は日本語でリクエストしても英語のじゃんけんを実装する傾向
  • 不正入力はちゃんとブロックした
  • 引き分けのカウントがない

Qwythos 9Bが生成したコード

import random

def get_choice():
    """ユーザーの入力を検証して返す"""
    while True:
        choice = input("岩(rock) / 紙(paper) / 剪刀(scissors)を選択: ").strip().lower()
        if choice in ['rock', 'paper', 'scissors']:
            return choice
        print("無効な選択肢です。もう一度入力してください。")

def determine_winner(user, computer):
    """単一のラウンドの勝敗を判定"""
    wins = {('rock', 'scissors'), ('paper', 'rock'), ('scissors', 'paper')}
    if (user, computer) in wins:
        return 1
    elif user != computer:
        return -1
    else:
        return 0

# 5ラウンド行う
user_score = 0
computer_score = 0

for round_num in range(1, 6):
    print(f"\n--- {round_num} ラウンド ---")
    user_choice = get_choice()
    computer_choice = random.choice(['rock', 'paper', 'scissors'])

    result = determine_winner(user_choice, computer_choice)

    if result == 1:
        print(f"あなたの選択:{user_choice} | コンピュータの選択:{computer_choice}")
        print("🎉 あなたが勝った!")
        user_score += 1
    elif result == -1:
        print(f"あなたの選択:{user_choice} | コンピュータの選択:{computer_choice}")
        print("😢 コンピュータが勝った...")
        computer_score += 1
    else:
        print(f"あなたの選択:{user_choice} | コンピュータの選択:{computer_choice}")
        print("🤝 引き分けです!")

# 最終結果表示
print("\n===== 最終結果 =====")
print(f"あなたの勝率:{user_score}")
print(f"コンピュータの勝率:{computer_score}")

Qwythos-9B は日本語でリクエストしても英語のじゃんけんを実装する傾向。Thinkingが異常に長かったです。 出す手もグー、チョキ、パーになっていないのもマイナスポイントです。

Thinkingを見る限り、Qwythos-9Bは英語でロジックを組み立てたあと、日本語向けのコードへ変換しているように見えます。その過程で不整合が発生している可能性があります。

  • 不正入力はブロックできている(Invalid choice! Please try again.)
  • 相手の手が表示されない
  • 何回目の勝負なのか表示されない
  • 勝ったか負けたか、あいこなのか表示されない
  • 最終結果の集計はできているが、引き分けをカウントしていない

最終コードも良くありませんでした。日本語プロンプトにもかかわらず、中国語由来の表現と英語が混在しており、日本語向けのコードとしては違和感があります。

  • 不正入力はブロックできている(無効な選択肢です。もう一度入力してください)
  • 「岩(rock) / 紙(paper) / 剪刀(scissors)を選択」
  • 最終結果の集計はできているが、引き分けをカウントしていない

Ornith:9b 英語プロンプト

英語のRPSゲームを実装するため、 プロンプトを英語の"Write a Python 3 rock-paper-scissors game with five rounds and show the final score."に変更して試してみました。

コードのざっくり評価は次の通りです

  • Ornithはコード品質は悪くなかった
  • スコープミスで未定義変数になった

Ornith 9Bが生成したコード

import random

def play_round():
    choices = ["rock", "paper", "scissors"]

    player_choice = input("Your choice (rock/paper/scissors): ").strip().lower()
    while player_choice not in choices:
        print("Invalid! Choose rock, paper, or scissors.")
        player_choice = input("Your choice (rock/paper/scissors): ").strip().lower()

    computer_choice = random.choice(choices)

    if player_choice == computer_choice:
        return "draw"

    winning_moves = {"rock": "scissors", "paper": "rock", "scissors": "paper"}
    if winning_moves[player_choice] == computer_choice:
        return "win"
    else:
        return "lose"

print("=== Rock-Paper-Scissors (5 Rounds) ===\n")

score = 0
for i in range(1, 6):
    result = play_round()
    if result == "win":
        score += 1

    print(f"Round {i}: You chose {player_choice}, Computer chose {computer_choice} → ", end="")

    emoji = {"draw": "🤝", "win": "🎉", "lose": "😢"}
    print(emoji[result])

print(f"\nFinal score: {score}/5")

生成されたコードを実行すると、エラーが発生しました。

=== Rock-Paper-Scissors (5 Rounds) ===

Your choice (rock/paper/scissors): rock
Traceback (most recent call last):
  File "/Users/ytooyama/tmp/code/test/p3.py", line 30, in <module>
    print(f"Round {i}: You chose {player_choice}, Computer chose {computer_choice} → ", end="")
                                  ^^^^^^^^^^^^^
NameError: name 'player_choice' is not defined

次のように修正すると正常に動作しました。

エラーの原因は play_round() のスコープです。 player_choice と computer_choice は play_round() 関数の中だけで作られるローカル変数であり、関数を抜けると、この2つの変数は存在しません。 一方、メインループでは

result = play_round()
print(f"Round {i}: You chose {player_choice}, Computer chose {computer_choice}")

としていますが、この場所ではplayer_choiceとcomputer_choiceが未定義のためエラーになります。 コードを次のように編集することで動くコードにできました。

Ornith 9Bが生成したコードの修正案

import random

def play_round():
    choices = ["rock", "paper", "scissors"]

    player_choice = input("Your choice (rock/paper/scissors): ").strip().lower()
    while player_choice not in choices:
        print("Invalid! Choose rock, paper, or scissors.")
        player_choice = input("Your choice (rock/paper/scissors): ").strip().lower()

    computer_choice = random.choice(choices)

    if player_choice == computer_choice:
        return player_choice, computer_choice, "draw"

    winning_moves = {
        "rock": "scissors",
        "paper": "rock",
        "scissors": "paper"
    }

    if winning_moves[player_choice] == computer_choice:
        return player_choice, computer_choice, "win"
    else:
        return player_choice, computer_choice, "lose"

print("=== Rock-Paper-Scissors (5 Rounds) ===\n")

score = 0

for i in range(1, 6):
    player_choice, computer_choice, result = play_round()

    if result == "win":
        score += 1

    print(f"Round {i}: You chose {player_choice}, Computer chose {computer_choice} → ", end="")

    emoji = {"draw": "🤝", "win": "🎉", "lose": "😢"}
    print(emoji[result])

print(f"\nFinal score: {score}/5")

Qwythos-9B 英語プロンプト

同様に、Qwythos 9Bでも英語のRPSゲームを実装してみます。 コードは一発で通りました。やはり英語だと問題なさそう...といいそうでしたが、コードに重大な問題がありました。

コードのざっくり評価は次の通りです。

  • Thikingは長め
  • コード実行エラーなしだが、致命的な問題が多数

もうちょっと問題を掘り下げてみます。

勝ち判定が成立しない

入力としてr,p,sのいずれかを求められているが、opponent_moveがrock, paper, scissorsになっており、プレイヤーの出す手がおかしいのでelseに入って負けになります。 引き分けの判定は変換後の文字列を使っているのでうまく動きます。

戻り値の判定が誤っている

戻り値の判定も誤っています。Opponent wins this round.という出力にもwinという語句が含まれており、これが誤カウントをしてしまうようです。

実際生成されたコードは次の通りです。

Qwythos 9Bが生成したコード

import random

def play_round():
    moves = {"r": "rock", "p": "paper", "s": "scissors"}
    player_move = input("Choose your move (r = rock, p = paper, s = scissors): ").lower()

    while not (player_move in moves):
        print("Invalid choice. Please enter r, p, or s.")
        player_move = input("Your move: ").lower()

    opponent_move = random.choice(["rock", "paper", "scissors"])

    if moves[player_move] == opponent_move:
        result = "It's a tie!"
    elif (player_move == "r" and opponent_move == "s") or \
         (player_move == "p" and opponent_move == "r") or \
         (player_move == "s" and opponent_move == "p"):
        result = "You win this round! 🎉"
    else:
        result = "Opponent wins this round. 😢"

    print(f"You chose {moves[player_move]}. Opponent chose {opponent_move}.")
    print(result)
    return 1 if "win" in result else (0 if "tie" in result else -1)

player_score, opponent_score = 0, 0

for _ in range(5):
    round_result = play_round()
    player_score += round_result
    opponent_score -= round_result

print("\n--- Final Score ---")
print(f"You: {max(player_score, 0)} | Opponent: {max(opponent_score, 0)}")

これを次のように修正したところ、正常に動作するコードになりました。

Qwythos 9Bが生成したコードの修正案

import random

def play_round():
    moves = {"r": "rock", "p": "paper", "s": "scissors"}

    player_move = input(
        "Choose your move (r = rock, p = paper, s = scissors): "
    ).lower()

    while player_move not in moves:
        print("Invalid choice. Please enter r, p, or s.")
        player_move = input("Your move: ").lower()

    player_hand = moves[player_move]
    opponent_move = random.choice(["rock", "paper", "scissors"])

    if player_hand == opponent_move:
        result = "It's a tie!"
        round_result = 0

    elif (player_hand == "rock" and opponent_move == "scissors") or \
         (player_hand == "paper" and opponent_move == "rock") or \
         (player_hand == "scissors" and opponent_move == "paper"):
        result = "You win this round! 🎉"
        round_result = 1
    else:
        result = "Opponent wins this round. 😢"
        round_result = -1

    print(f"You chose {player_hand}. Opponent chose {opponent_move}.")
    print(result)

    return round_result

player_score = 0
opponent_score = 0
ties = 0

for _ in range(5):
    round_result = play_round()

    if round_result == 1:
        player_score += 1
    elif round_result == -1:
        opponent_score += 1
    else:
        ties += 1
    
print("\n--- Final Score ---")
print(f"You: {player_score} | Opponent: {opponent_score}")
print(f"Ties: {ties}")

いくつか重大な不具合はありましたが、次の部分はQwythos-9Bが実行したコードで良かった部分です。

  • 入力チェックはできている
  • moves 辞書を使っているのは悪くない
  • 関数に分割されていて読みやすい
  • 全体の構成は初心者向けとしては自然

とはいえ、結構手を入れないと想定したコードにはならなかったのも事実です。 プログラムの骨格はできていましたが、ロジックが破綻している状態でした。

しかしOrnithも一発で完璧というわけではなく、英語プロンプトではスコープの扱いを誤り、そのままでは実行できませんでした。

まとめ

今回試した範囲では、日本語ではOrnithが優勢でした。 一方Qwythosは英語では比較的自然なコードを生成しましたが、日本語では英語ベースのThinkingから日本語へ変換する過程で不整合が生じているように見えました。

以下はQwythos-9Bテスト時のThinking内のドラフトコードです。Thinking時点では英語のRPSゲームを実装しています。

Qwythos-9B Thinking内のドラフトコード

9.  **Write the Code Draft:**
    ```python
    import random

    def get_choice(prompt):
        while True:
            choice = input(prompt).strip().lower()
            if choice in ['rock', 'paper', 'scissors']:
                return choice
            print("Invalid choice! Please try again.")

    user_score, computer_score = 0, 0

    for _ in range(5):
        user_choice = get_choice("Your choice (rock/paper/scissors)? ")
        computer_choice = random.choice(['rock', 'paper', 'scissors'])

        # Determine round result
        if (user_choice == "rock" and computer_choice == "scissors") or \
           (user_choice == "paper" and computer_choice == "rock") or \
           (user_choice == "scissors" and computer_choice == "paper"):
            user_score += 1
        elif user_choice != computer_choice:
            computer_score += 1

    print(f"\nResults: User {user_score} - Computer {computer_score}")
    ```

英語プロンプトではOrnithはコード生成自体は良かったものの、ローカル変数のスコープを誤ったためにそのままでは実行できないコードが生成された。

Qwythos-9Bは一見実行できるコードを生成したように見えたが、勝ち判定と戻り値の判定方法にバグがありました。

今回は「じゃんけんゲーム」をお題として、Ornith 9BとQwythos 9Bのモデルの性能比較をしました。 いずれも特定のベンチマークでは一定の性能を出すモデルです。

しかし実際自分の言語で使ってみると、ベンチマークだけでは見えない性能がわかってきます。 ベンチマーク結果を鵜呑みにしないのが重要であるということを改めて感じました。 自分が普段使うプロンプトや言語で実際に試してみることが、モデル選定では最も重要だと感じました。