Gemini API を活用した数学作問自動化の試み
- 2024/12/10
- ブログ
- Gemini API を活用した数学作問自動化の試み はコメントを受け付けていません
Googleが提供するGenerative AIモデルの一つにGemini APIがあります。このAPIに適切なプロンプトを与えることで、複雑な処理を簡単に実現することができます。今回はこのAPIを利用し、数学の問題と解答・解説を自動作成するプログラムを作成してみます。
この記事の目次
Gemini APIについて
Geminiとは
Gemini は、Google が提供している生成AIモデルです。従来の生成AIモデルと比較した際の最大の特徴は、マルチモーダルな生成AIである点です。Geminiではテキスト以外にも、画像や動画、音声、コードといった幅広い形式の情報を理解することができます。これにより、生成コンテンツの幅が広がったり、質が向上するなどのメリットがあります。
Gemini APIとは
Gemini はWebブラウザ上での使用が前提となりますが、Gemini API はGemini の機能を様々なシステムやアプリケーションに組み込むためのインターフェースです。モデルや出力形式などを細かく設定することができ、ユースケースに合わせて最適化できることが特徴です。現時点で Python, Node.js に対応しています。
プロジェクト概要
環境の準備
- Python: ver 3.11
- Gemini API: gemini-1.5-flash
- ライブラリ:
dotenv
,google.generativeai
,json
,os
事前準備
Google AI StudioでAPI key を作成
* クレジットカード登録などは求められず、ある程度の制約はあるものの基本的に無料で利用できる
目標
Gemini APIを用いて数学の問題・解答・解説を自動作成し、json形式で取得する
プログラム概要
APIキーの設定
import os
import google.generativeai as genai
GOOGLE_API_KEY = os.getenv('GOOGLE_API_KEY')
genai.configure(api_key=GOOGLE_API_KEY)
事前に取得したGemini API キーを設定します。
AIモデルの設定
gemini_pro = genai.GenerativeModel("gemini-1.5-flash")
Gemini のモデルを選択し、設定します。
現時点(2024年12月初旬)でAPIとして使用できるモデルは、以下のとおりです。
- models/gemini-1.5-flash
- models/gemini-1.5-flash-8b
- models/gemini-1.5-pro
- models/gemini-1.0-pro
各モデルごとに無料枠での利用制限や機能に差があるため、用途に合わせて選択する必要があります。 *各モデルの詳細情報やアップデート情報はこちらを参照
今回のプロジェクトでは現時点での最新モデルである、”gemini-1.5-flash”を使用します。
プロンプトの受け渡しと結果の取得
prompt = f"数学の問題と解答例を{num_problems}個作成してください。単元は{genre}で、難易度は{difficulty}です。解説は、公式の利用、計算過程、解答に至るまでの思考過程などを含めて記述してください。"
print(f"プロンプト:{prompt}")
response = gemini_pro.generate_content(prompt)
print(response.text)
プロンプトを設定し、レスポンスを受け取ります。今回は「単元」「難易度」「問題数」を自由に設定できるようにプロンプトを作成しました。
テスト実行では、「二次関数」「高校1年生レベル」「2」と設定して実行してみます。
出力結果
## 問題1
**問題:** 頂点が点$(-1, 2)$ で、点$(1, 6)$ を通る二次関数 $y = a(x+1)^2 + 2$ の $a$ の値を求めよ。また、その二次関数の式を求めよ。
**解答例:**
**1. 思考過程:**
この問題は、二次関数の頂点形 $y = a(x-p)^2 + q$ を利用します。頂点 $(p, q)$ が $(-1, 2)$ と与えられているので、$p = -1$, $q = 2$ となります。よって、二次関数の式は $y = a(x+1)^2 + 2$ と表せます。さらに、点 $(1, 6)$ を通るので、$x = 1$ のとき $y = 6$ となります。この条件を使って $a$ の値を求めます。
**2. 計算過程:**
点 $(1, 6)$ を二次関数 $y = a(x+1)^2 + 2$ に代入すると、
$6 = a(1+1)^2 + 2$
$6 = a(2)^2 + 2$
$6 = 4a + 2$
$4a = 6 - 2$
$4a = 4$
$a = 1$
よって、$a$ の値は 1 です。
**3. 解答:**
$a = 1$ より、二次関数の式は $y = (x+1)^2 + 2$ となります。
## 問題2
**問題:** 二次関数 $y = x^2 - 4x + 3$ のグラフの軸、頂点、$x$ 切片、$y$ 切片を求めよ。また、そのグラフを描いてください。
**解答例:**
**1. 思考過程:**
この問題は、二次関数の標準形 $y = ax^2 + bx + c$ から、軸、頂点、$x$ 切片、$y$ 切片を求める問題です。軸は $x = -\frac{b}{2a}$ の式で求められます。頂点は軸上の点なので、軸の$x$座標を元の式に代入することで$y$座標を求めます。$x$ 切片は $y = 0$ としたときの $x$ の値、$y$ 切片は $x = 0$ としたときの $y$ の値です。
**2. 計算過程:**
与えられた二次関数 $y = x^2 - 4x + 3$ は標準形 $y = ax^2 + bx + c$ と比較して、$a = 1$, $b = -4$, $c = 3$ となります。
* **軸:** 軸は $x = -\frac{b}{2a} = -\frac{-4}{2(1)} = 2$ です。
* **頂点:** $x = 2$ を元の式に代入すると、$y = 2^2 - 4(2) + 3 = 4 - 8 + 3 = -1$ となります。よって、頂点は $(2, -1)$ です。
* **$x$ 切片:** $y = 0$ とすると、$x^2 - 4x + 3 = 0$ となります。因数分解すると $(x-1)(x-3) = 0$ なので、$x = 1$ または $x = 3$ です。よって、$x$ 切片は $(1, 0)$ と $(3, 0)$ です。
* **$y$ 切片:** $x = 0$ とすると、$y = 0^2 - 4(0) + 3 = 3$ となります。よって、$y$ 切片は $(0, 3)$ です。
**3. 解答:**
軸:$x = 2$
頂点:$(2, -1)$
$x$ 切片:$(1, 0)$, $(3, 0)$
$y$ 切片:$(0, 3)$
(グラフは、軸、頂点、$x$切片、$y$切片を用いて放物線を描きましょう。下に凸の放物線になります。)
これらの解答例が、高校1年生レベルの二次関数に関する理解の助けになれば幸いです。 グラフを描く際には、方眼紙などを用いて正確に描画することをお勧めします。
概ねプロンプトに沿った期待通りの回答が得られましたが、今回の目標の一つである「json形式で結果を受け取る」が達成できていません。なので、必ずjson形式で結果を得られるよういくつか試行してみます。
プロンプトで矯正を図る
以下のようなプロンプトに書き換え、再度実行してみます。
prompt = f"数学の問題と解答例を{num_problems}個作成してください。単元は{genre}で、難易度は{difficulty}です。解説は、公式の利用、計算過程、解答に至るまでの思考過程などを含めて記述してください。解答は、問題文、解答、解説のキーを持つJSONオブジェクトとして出力し、JSONオブジェクトのキーは、\"problem\", \"answer\", \"explanation\"としてください。"
出力結果
```json
[
{
"problem": "二次関数 y = x² - 4x + 3 のグラフの頂点の座標と、x軸との交点の座標を求めよ。",
"answer": "頂点の座標:(2, -1)、x軸との交点の座標:(1, 0), (3, 0)",
"explanation": "この問題は、二次関数のグラフの性質を利用して解きます。\n\n**1. 頂点の座標:**\n二次関数 y = ax² + bx + c の頂点のx座標は、x = -b/(2a) で求められます。この問題では、a = 1, b = -4, c = 3 なので、頂点のx座標は x = -(-4) / (2 * 1) = 2 となります。\n次に、x = 2 を元の関数に代入してy座標を求めます。y = 2² - 4(2) + 3 = 4 - 8 + 3 = -1 となります。したがって、頂点の座標は (2, -1) です。\n\n**2. x軸との交点の座標:**\nx軸との交点とは、y = 0 となる点です。よって、x² - 4x + 3 = 0 を解けば良いことになります。この二次方程式は因数分解を用いて解くことができます。\n x² - 4x + 3 = (x - 1)(x - 3) = 0\nよって、x = 1 または x = 3 となります。したがって、x軸との交点の座標は (1, 0) と (3, 0) です。\n\n**思考過程:**\nまず、問題文で求められているものが頂点とx軸との交点であることを理解する必要があります。頂点を求めるには、頂点の公式を利用します。x軸との交点は、y=0を代入した方程式を解くことで求めることができます。この方程式は因数分解できるため、比較的容易に解くことができます。もし因数分解が困難な場合は、解の公式を用いる必要があります。"
},
{
"problem": "二次関数 y = 2x² + 8x + 5 について、x = -2 のときのyの値を求め、その点における接線の傾きを求めよ。",
"answer": "y = -3、接線の傾き = -4",
"explanation": "この問題は、二次関数の値と微分係数(接線の傾き)を求める問題です。\n\n**1. x = -2 のときの y の値:**\nx = -2 を y = 2x² + 8x + 5 に代入します。\ny = 2(-2)² + 8(-2) + 5 = 2(4) - 16 + 5 = 8 - 16 + 5 = -3\nよって、x = -2 のときの y の値は -3 です。\n\n**2. 接線の傾き:**\n接線の傾きは、関数の導関数(微分係数)をその点のx座標で評価することで求まります。\nまず、y = 2x² + 8x + 5 を x について微分します。\ny' = d/dx (2x² + 8x + 5) = 4x + 8\n次に、x = -2 を y' に代入します。\ny'(-2) = 4(-2) + 8 = -8 + 8 = 0\nよって、x = -2 の点における接線の傾きは 0 です。\n\n**思考過程:**\nまず、問題文で求められているものが、特定のx座標におけるyの値と、その点での接線の傾きであることを理解する必要があります。yの値は、関数の式にxの値を代入することで直接求まります。接線の傾きは、関数を微分して得られる導関数を用いて求めます。導関数は、その関数の各点における接線の傾きを表す関数です。"
}
]
```
json形式での出力には成功しましたが、”json”という単語とバッククオートが回答に含まれており、純粋なjsonではないため、この後の処理で正常に扱えません。
Gemini で用意されている設定値を利用
実はプロンプトで試行錯誤する必要はなく、Gemini API側で json形式で値を返すための設定値が用意されています。設定も至って簡単です。AIモデル選択時のコードに任意設定値として設定するだけで、回答の形式をjsonで固定することができます。
gemini_pro = genai.GenerativeModel("gemini-1.5-flash", generation_config={"response_mime_type": "application/json"})
加えて、プロンプトにて、jsonのキーを指定しておきます。
prompt = f"数学の問題と解答例を{num_problems}個作成してください。単元は{genre}で、難易度は{difficulty}です。解説は、公式の利用、計算過程、解答に至るまでの思考過程などを含めて記述してください。解答は、問題文、解答、解説のキーを持つJSONオブジェクトとして出力し、JSONオブジェクトのキーは、\"problem\", \"answer\", \"explanation\"としてください。また、JSONオブジェクトのルートキーは必ず \"questions\" にしてください。"
それでは再度実行してみます。
出力結果
{
"questions": [
{
"problem": "二次関数 y = 2x² - 8x + 5 のグラフの頂点の座標と、x軸との交点の座標を求めなさい。",
"answer": "頂点:(2, -3) x軸との交点:(2 ± √2)/2, 2 ∓ √2)/2",
"explanation": "この問題は、二次関数のグラフの性質を利用して解きます。\n\n**1. 頂点の座標**\n\n二次関数 y = ax² + bx + c の頂点のx座標は、x = -b/(2a) で求められます。この問題では、a = 2, b = -8, c = 5 なので、頂点のx座標は x = -(-8) / (2 * 2) = 2 となります。\n\n次に、x = 2 を元の二次関数に代入して、y座標を求めます。\ny = 2(2)² - 8(2) + 5 = 8 - 16 + 5 = -3\n\nよって、頂点の座標は (2, -3) です。\n\n**2. x軸との交点**\n\nx軸との交点とは、y = 0 となるxの値です。したがって、2x² - 8x + 5 = 0 を解きます。この二次方程式は因数分解できないので、解の公式を使います。\n\n解の公式: x = (-b ± √(b² - 4ac)) / (2a)\n\nこの問題では、a = 2, b = -8, c = 5 なので、\n\nx = (8 ± √((-8)² - 4 * 2 * 5)) / (2 * 2) = (8 ± √(64 - 40)) / 4 = (8 ± √24) / 4 = (8 ± 2√6) / 4 = 2 ± √6/2\n\nよって、x軸との交点のx座標は (2 + √6)/2 と (2 - √6)/2 です。\n\nしたがって、x軸との交点の座標は ((2 + √6)/2, 0) と ((2 - √6)/2, 0) となります。"
},
{
"problem": "二次関数 y = -x² + 4x + 5 のグラフとx軸で囲まれた部分の面積を求めなさい。",
"answer": "36/3",
"explanation": "この問題は、二次関数のグラフとx軸で囲まれた部分の面積を求める問題です。まず、x軸との交点を求めます。\n\ny = -x² + 4x + 5 = 0\n\nこの方程式を解くと、x = -1, 5 となります。よって、x軸との交点は (-1, 0) と (5, 0) です。\n\nグラフは上に凸の二次関数なので、x軸との交点間の面積は、定積分を用いて求めることができます。\n\n求める面積 S は、\n\nS = ∫(from -1 to 5) (-x² + 4x + 5) dx\n\n= [-x³/3 + 2x² + 5x] (from -1 to 5)\n\n= (-125/3 + 50 + 25) - (1/3 + 2 -5)\n\n= (-125/3 + 75) - (-8/3)\n\n= (-125 + 225)/3 + 8/3\n\n= 100/3 + 8/3 = 108/3 = 36\n\nしたがって、二次関数 y = -x² + 4x + 5 のグラフとx軸で囲まれた部分の面積は36です。"
}
]
}
無事純粋なjson形式で結果を取得することができました。
今後の課題
今回の試みは概ね期待通りの結果となりましたが、課題も見つかりました。今回わかった最大の課題は、「AIの限界」です。生成AIといえどまだ完全ではないため、時々解答として示した値と解説内で導き出した値が一致しないなどといった致命的な欠点が露呈することがありました。今回例として出した結果でもいくつか見受けられるため、テスト作成ツールとして本格的に実用化するには大きすぎる欠点といえます。GeminiはFine tuning にも対応しているため、こちらでチューニングを試みるのも一つの手ではあるかもしれませんが、まずはモデルの進化に期待したいところです。
まとめ
今回はGemini API を用いて数学の作問自動化をしてみました。ひとまずjson形式で期待通りの回答を得られることを目標に今回のプロジェクトを立ち上げましたが、最終的な目標としては、一つのアプリケーションとしてPDF形式などで実際にテスト形式で出力できるようにすることを目指しています。また、数学だけでなく他の科目にも応用できると思いますので、色々と試してみたいと思います。また進展があれば記します。
今回のプロジェクトの全体コードは以下のgithubにあげているので、興味があればどうぞ。
カテゴリー: