代数の文章題を解くプログラムに関する考察

ウェブ技術 デジタルトランスフォーメーション技術 人工知能技術 機械学習 自然言語処理技術 推論技術 セマンティックウェブ技術 深層学習技術 オンライン学習 強化学習技術 チャットボットと質疑応答技術 ユーザーインターフェース技術 知識情報処理技術 推論技術 プログラミング スパースモデリング 確率的生成モデル サポートベクトルマシン LISP Prolog 本ブログのナビ

イントロダクション

GPTの概要とアルゴリズム及び実装例について“で述べているGPTモデルを使ったchatGPTを用いると、以下に示す様に様々な代数の問題を解かせることができる。これらは、単純に答えを出すだけでなく、様々な公式を応用した解法を示してくれるため、万能のAIが出来ているかのような錯覚を受ける。

しかしながら、実際に計算を行ってみるとchatGPTが示してくれた答えが間違っている場合があることに気づく。これは、chatGPTが、あくまでも膨大な学習データを元に、次に何の文字が現れるかを推定しているだけで、本質的な計算を行っていないためである。

cahtGPTで代数の問題を解く(1)

<問題> 2023×1225を計算する

2023 × 1225 を計算します。
2023 × 1225 = 2,480,375
したがって、2023 × 1225 = 2,480,375 です。

実際の答えは2,478,175で不正解

chatGPTで代数の問題を解く(2)因数分解

<問題> \(3x^2-17x-6\)を因数分解する

3x^2 - 17x - 6 を因数分解する方法は次の通りです。

まず、この二次方程式を因数分解するために、次の形式の因数分解を試みます:

3x^2 - 17x - 6 = (ax + b)(cx + d)

このような形で因数分解するためには、a、b、c、d の値を見つける必要があります。

これを展開して元の式と一致させるためには、次のようになります:

(3x + e)(x + f) = 3x^2 + 3xf + ex + ef

ここで、3xf + ex = -17x、ef = -6 という条件を満たす必要があります。

次に、ef = -6 の因数のペアを求めます。-6 の因数は次の通りです:

-6 = -1 * 6 -6 = 1 * -6 -6 = -2 * 3 -6 = 2 * -3

次に、3xf + ex = -17x の条件を考えます。

3xf + ex = x(3f + e)

これを -17x と比較するために、x の係数は 1 である必要があります。

したがって、3f + e = -17 です。

これらの条件を満たす組み合わせを見つけます。

ef = -6 の場合:

-1 * 6 = -6 3f + e = -17

これを解くと、e = -1、f = -6 です。

したがって、3x^2 - 17x - 6 を因数分解すると次のようになります:

3x^2 - 17x - 6 = (3x - 1)(x - 6)

ロジックを組んで解いており、正解も(3x-1)(x-6)で得られている。

chatGPTで代数の問題を解く(3)2変数の連立方程式

<問題> 以下の連立方程式を解く\begin{eqnarray}a+b+c&=&0\\2a+2b+c&=&3\\2a+3b+2c&=&1\end{eqnarray}

この3つの連立方程式を解くために、行列を使用した方法を使います。与えられた方程式は以下のようになります。

a + b + c = 0
2a + 2b + c = 3
2a + 3b + 2c = 1

これを行列形式で表すと、以下のようになります。

[ 1 1 1 ] [ a ] [ 0 ] [ 2 2 1 ] [ b ] = [ 3 ] [ 2 3 2 ] [ c ] [ 1 ]

この行列を係数行列 A とし、ベクトル b を次のように定義します。

A = [ 1 1 1 ] [ 2 2 1 ] [ 2 3 2 ]

b = [ 0 ] [ 3 ] [ 1 ]

そして、未知数ベクトル x を次のように定義します。

x = [ a ] [ b ] [ c ]

このとき、方程式は Ax = b となります。この連立方程式を解くには、x = A^(-1) * b を求める必要があります。ただし、A^(-1) は行列 A の逆行列です。

ここで、逆行列を計算します。

A^(-1) = (1/(ad - bc)) * adj(A)

ここで、adj(A) は行列 A の余因子行列です。余因子行列を計算すると次のようになります。

adj(A) = [ 4 -3 1 ] [ -1 2 -1 ] [ -2 3 -1 ]

そして、det(A) = ad - bc を計算します。

det(A) = (1 * 2 * 2) - (1 * 2 * 2) = 4 - 4 = 0

det(A) が 0 であるため、A の逆行列は存在しません。この意味は、この連立方程式は解を持たないか、無数の解を持つ可能性があるということです。

実際に、方程式を解くと以下のようになります。

a + b + c = 0

2a + 2b + c = 3

2a + 3b + 2c = 1

これらの式から、a + b = -c、2a + 2b = 3 - c、2a + 3b = 1 - 2c が得られます。

式1) と 式2) より、

2a + 2b - (a + b) = 3 - 0 a + b = 3

式1) と 式3) より、

2a + 3b - (a + b) = 1 - 0 a + 2b = 1

これらの式を解くと、

b = 3 - a a + 2(3 - a) = 1 a + 6 - 2a = 1 6 - a = 1 a = 6 - 1 a = 5

そして、b を求めます。

b = 3 - a b = 3 - 5 b = -2

最後に、c を求めます。

c = -a - b c = -5 - (-2) c = -5 + 2 c = -3

したがって、連立方程式の解は a = 5、b = -2、c = -3 です。

chatGPTは拡大係数行列を用いた解法で解いている。52行目の計算が間違っており、その影響を受けて最終的な解も間違ったものが得られている。

代数の文章題を解くSTUDENT

この様な問題を解くタスクは、実はかなり古くから検討されている。例えば”実用Common LISP 読書メモ“に記載されているSTUDENTは、1964年に書かれた初期の言語理解プログラムであり、中学の代数の教科書にあるような文章題を読んで解く様に設計されている。これは、具体的には例えば以下の様な入力を与えると

「If the number of customers Tom gets is twice the square of 20% of the number of advertisements he runs, and the number of advertisements is 45, then what is the number of customers Tom gets?」

(トムが獲得する顧客の数が、彼が掲載する広告の数の20%の二乗の2倍であり、広告の数が45である場合、トムが獲得する顧客の数はいくらか?)

顧客の数が162であることを答えてくれるものとなる。ちなみに、同じインプットをchatGPTに導入すると以下の様な答え(今回は正解)が返ってくる。

Let's break down the problem step by step:

Given information:

The number of advertisements Tom runs is 45.

We need to find:

The number of customers Tom gets.

Let's denote the number of customers as "C" and the number of advertisements as "A."

From the problem statement, we have the following information:

The number of customers Tom gets is twice the square of 20% of the number of advertisements he runs.
Number of advertisements, A = 45.

First, let's calculate 20% of the number of advertisements: 20% of A = 0.20 * 45 = 9

Next, let's find the square of 20% of the number of advertisements: Square of 20% of A = 9^2 = 81

Now, according to the problem statement, the number of customers Tom gets is twice the square of 20% of the number of advertisements he runs: C = 2 * (Square of 20% of A) C = 2 * 81 C = 162

So, the number of customers Tom gets is 162.

このSTUDENTのコードはLISPで書かれており、git上で得ることができる。ここでは、簡単な実装例を以下に示している。

(defun solve-algebraic-word-problem (problem)
  (let* ((variables (find-variables problem))
         (equations (find-equations problem variables))
         (solutions (solve-equations equations variables)))
    solutions))

(defun find-variables (problem)
  (let ((pattern "[a-zA-Z]+"))
    (remove-duplicates (all-matches pattern problem))))

(defun find-equations (problem variables)
  (let ((pattern "[a-zA-Z]+[+\\-*/][a-zA-Z]+"))
    (all-matches pattern problem)))

(defun solve-equations (equations variables)
  (let ((solutions '()))
    (dolist (equation equations)
      (let* ((parts (split-string equation "="))
             (left-side (parse-expression (car parts) variables))
             (right-side (parse-expression (cadr parts) variables))
             (eq (list '= left-side right-side))
             (solution (solve eq variables)))
        (setq solutions (cons solution solutions))))
    solutions))

(defun parse-expression (expression variables)
  (let ((var-values (mapcar (lambda (var) (list var (intern var))) variables)))
    (subst var-values expression)))

;; 代数の文章問題を入力する
(setq problem "3x + 2y = 10\nx - y = 2")

;; 代数の文章問題を解く
(setq solutions (solve-algebraic-word-problem problem))

;; 結果を表示する
(dolist (solution solutions)
  (dolist (variable solution)
    (format t "~a = ~a~%" (car variable) (cadr variable))))

このLISPプログラムでは、solve-algebraic-word-problem関数が代数の文章問題を解くエントリーポイントとなっており、find-variables関数は、問題文から変数を抽出し、find-equations関数は方程式を抽出し、solve-equations関数は方程式を解き、最終的な解を返している。parse-expression関数は式を解析し、変数を対応する値に置換するものとなっている。

Pythonによる代数の文章問題を解くプログラムについて

同様のアプローチを現代のプログラミング言語であるpythonを使って実装してみる。代数の文章問題を解くプログラムを作成するためには、自然言語処理(Natural Language Processing, NLP)と代数の解法アルゴリズムを組み合わせる必要があり、以下のようなアプローチで実装することができる。

  1. プログラムの入力として、代数の文章問題をテキスト形式で取得する。
  2. NLPモデルを使用して、テキストから重要な情報を抽出します。例えば、問題文から変数や条件を抽出する。
  3. 抽出した情報をもとに、代数の式を構築する。例えば、「xの2倍は10」のような条件から、x * 2 = 10のような式を生成する。
  4. 構築した代数の式を解くための数学的なアルゴリズムを適用する。例えば、一次方程式の解法を使って答えを求める。
  5. 得られた答えを出力する。

具体的な実装例は以下の様になる。

import re
from sympy import symbols, Eq, solve

def solve_algebraic_word_problem(problem):
    # 正規表現を使用して文章中の変数を抽出する
    variables = re.findall(r'\b[a-zA-Z]+\b', problem)
    variables = list(set(variables))  # 重複を削除

    # 方程式の未知数となるシンボルを作成する
    symbols_list = symbols(' '.join(variables))

    # 方程式を抽出する
    equations = re.findall(r'[a-zA-Z]+(?:\s*\+\s*[a-zA-Z]+)*\s*=\s*\d+', problem)

    # 方程式を解く
    solutions = []
    for equation in equations:
        # 方程式の左辺と右辺を分割する
        parts = equation.split('=')
        left_side = parts[0].strip()
        right_side = parts[1].strip()

        # 方程式を作成する
        eq = Eq(eval(left_side), int(right_side))

        # 方程式を解く
        solution = solve(eq, symbols_list)
        solutions.append(solution)

    return solutions

# 代数の文章問題を入力する
problem = "3x + 2y = 10\nx - y = 2"

# 代数の文章問題を解く
solutions = solve_algebraic_word_problem(problem)

# 結果を表示する
for solution in solutions:
    for variable, value in solution.items():
        print(f'{variable} = {value}')

このプログラムでは、正規表現を使用して文章中の変数を抽出し、Sympyライブラリを使用して方程式を解いている。プログラムを実行すると、与えられた代数の文章問題の変数とその値が表示される。

これらLISPやPythonのコードの中で行われていることは、文字列のパターンマッチを使って「英文を方程式に変換する」という作業を行い、次に生成された方程式を解くという流れとなる。このアプローチは、chatGPTのように様々な問題に対してアプローチできるわけではなく、決まった問題の入力しか受け付けない。しかしながら、一旦受け付けられれば、その答えにほぼ間違いはれない。

このように、chatGPTやSTUDENT共に一長一短があり、「数学の問題を解く」というタスクに対応するには不十分であることがわかる。それらを解決するには、chatGPTの柔軟性とSTUDENTの確実性を兼ね合わせたものになる。この課題は、General AIを作る上での課題であるということもできる。

課題解決のためのアプローチ

では、chatGPTの柔軟性とSTUDENTの確実性を兼ね合わせるためにはどうすれば良いのか。まずそれぞれの処理フロー分析を行うため以下に記述する。

<STUDENTの処理フロー>

  1. 問題の文脈を設定する: まず、STUDENTに問題の文脈を提供する。例えば、問題文の前提条件や問題の種類、変数の役割などを理解して、データとする。
  2. 問題文を入力する: 代数の文章問題をSTUDENTに入力する。問題文は自然言語で表現されたものであり、問題の条件や要件を含んでいる。
  3. 問題の文章を読み取る: 与えられた代数の文章問題を理解し、問題文から数学的な情報を抽出する。文章中のキーワードやフレーズを特定し、変数、演算子、等号、数値などの数学的な要素を識別する。
  4. 変数と方程式の抽出: 抽出した数学的な要素から、問題に関連する変数と方程式を特定する。変数は問題文中の未知数であり、方程式は変数と数学的な関係を示す等式となる。方程式は、問題の条件や問題文の表現方法に基づいて構築される。
  5. 方程式を解く: 抽出した方程式を解くための代数的な手法を適用する。これには、方程式を正規化し、未知数を求めるための代数的な操作や計算が含まれる。代数的な手法には、連立方程式を解くための行列演算や代数的な等式の変形などがある。
  6. 解の評価: 解析的な解を得た後、問題の文脈に基づいて解を評価する。解が与えられた問題の条件や要件を満たしているかどうかを確認する。また、解を自然言語で解釈し、説明や理解を支援するための解釈を提供することもある。

<ChatGPTの処理フロー>

  1. 問題文を入力する: 代数の文章問題をChatGPTに入力する。問題文は自然言語で表現されたものであり、問題の条件や要件を含んでいる。
  2. 問題の文章を読み取る: 与えられた文章を特徴ベクトル化し、そこから出力ベクトルを生成する。
  3. 解の評価: 出力結果を確認し、不適切な場合はフィードバックを与える。

      これらをフュージョンする方向性としては

      <STUDENTとChatGPTの融合モジュールの処理フロー>

      1. 問題文を入力する: 代数の文章問題を入力する。問題文は自然言語で表現されたものであり、問題の条件や要件を含んでいる。
      2. 問題の文脈を設定する: 入力した文章から問題の文脈を抽出する(深層学習技術を適用)。例えば、問題文の前提条件や問題の種類、変数の役割などを抽出して、データとする。
      3. 問題の文章を読み取る: 与えられた代数の文章問題文から数学的な情報を抽出する(深層学習技術を適用)。文章中のキーワードやフレーズを特定し、変数、演算子、等号、数値などの数学的な要素を識別する。
      4. 変数と方程式の抽出: 抽出した数学的な要素から、問題に関連する変数と方程式を特定する(深層学習技術を適用)。変数は問題文中の未知数であり、方程式は変数と数学的な関係を示す等式となる。方程式は、問題の条件や問題文の表現方法に基づいて構築される。
      5. 方程式を解く手法の推定: 構造化された問題から、適切な手法を推定する(深層学習技術の適用)。
      6. 方程式を解く: 抽出した方程式を解くための代数的な手法を適用する。これには、方程式を正規化し、未知数を求めるための代数的な操作や計算が含まれる。代数的な手法には、連立方程式を解くための行列演算や代数的な等式の変形などがある。
      7. 解の評価: 解析的な解を得た後、問題の文脈に基づいて解を評価する。解が与えられた問題の条件や要件を満たしているかどうかを確認する。また、解を自然言語で解釈し、説明や理解を支援するための解釈を提供することもある。

      ChatGPTのフローのように、すべてを一つのモデルで行うのでなく、フローに合わせた機能モジュールに分割し深層学習等の機械学習モジュール(上記では2,3,4,5に当たる)を構築する。また方程式を解く部分(6)と解の評価の部分(7)に関しては、ロジックをベースに構築することで、chatGPTの柔軟性とSTUDENTの確実性を兼ね合わせたソリューションが組めるものと期待される。

      最近の話題で”“数学特化”の大規模言語モデル「WizardMath」 米Microsoftなどが開発 Llamaモデルを強化“というものがある。これは米Microsoftがオープンソースの事前学習済み大規模言語モデル(LLM)である「Llama-2」に対して、「Reinforcement Learning from Evol-Instruct Feedback」(RLEIF)という新しい方法で微調整を行い数学的推論能力を向上させたというものとなる。この改善により上記の問題が改善されたのかは不明だが、GPT側での改善も進んでいるということが言える。

      それぞれのモジュールに用いられるアルゴリズムと具体的な実装は別途記載予定。

      コメント

      1. […] 代数の文章題を解くプログラムに関する考察 […]

      タイトルとURLをコピーしました