人工知能を作ってみよう

Prologを学習したからには「人工知能」を作ってみましょう。皆さんも「人工知能」の何たるかは知らなくとも、その言葉は聞いたことがあるでしょう。もしかしたら、「人工知能なんか駄目だ」って台詞も聞いたことがあるかもしれない。そもそも「人工知能」って何でしょう。

14.1 人工知能の研究の歴史

人工知能の研究のスタートは何であったか。というのは実はよくわかっていません。人の手で知能を作り出せたら、という願望は大昔からありましたから。でも、恐らくは20世紀初頭の数学者を中心とした研究が基になっていると思います。例えば、ブーリアン・データ型でお馴染みのジョージ・ブールは「論理と確率に関する数学的理論の構築の基礎となる思考法則の研究(1854)」という論文の中で「本論の目的は、推論と判断が実行される際の心の基本的な動作を解明することである。検証すべき法則は、人間の心的能力の最も重要な機能の1つを形成している法則である。構築すべき数学は、人間の知能の数学である。」と述べています。ここには今日の人工知能の研究の根底にある基本的な考え方が存在しています。即ち、人間の脳の振る舞いをある種の計算過程と見なせるような観点が存在する。そして、脳の計算能力の性格を詳細に解明することができたなら、それをコンピュータで再現し、人間の手による人工的な知能を実現することができるはずだ、ということです。

人工知能の研究に理論的な基盤を与えたのは、それから約1世紀後のアラン・チューリングです。チューリングは「知能の機械化は可能である」と考えました。そして、コンピュータ・システムが知能を持っているかどうかを検証するための試験「チューリング・テスト」を提唱しました(1950)。これは、ある問題について人間の試験官が人間とコンピュータ・システムとのそれぞれに質問し、試験官が人間とコンピュータとの区別がつかなかったら、そのコンピュータ・システムは知能を持っていると言える、というものです。勿論、試験官と人間、コンピュータとはそれぞれ別の部屋に入り、会話はテレタイプで行います。

チューリングはチューリング・テストを提唱した論文で以下のように述べています。
多くの人々が「チェスのような複雑なゲームをマスターできるコンピュータなら確かに知能があると見なせる。チェスのような抽象的な活動が知能の研究の道具としてベストだ」と考えている。また、「人間の幼児に対する教育過程を応用して、マシンが質問に答えられるようにすべきだ」とも言える。私は研究の対象を、例えばチェスといったように限定すべきか、それとも人間の言語習得のように汎用的なものにすべきか、どちらにすべきか判らない。多分両方を試すべきだろう。
そして、その後の人工知能の研究はチューリングの言うとおり、2つの方向を行ったり来たりすることとなります。第2の方向を取り、コンピュータに「学習」をさせようとしていた研究は1960年代に挫折します。これは、当たり前の帰結でした。汎用的な知識をコンピュータに学習させるのは、余りにも早すぎた研究でした。「人工知能なんか駄目だ」としきりに言われたのはこの時代です。

1970年代にはいると、コンピュータに極限られた範囲の知識を持たせて、この知識に基づいて問題解決を図る研究が行われるようになります。MYCIN(マイシン)に代表されるエキスパート・システムです。チェス・プログラムもトップレベルのものができあがりました。これらはどれも「ある特定の問題領域では良好な振る舞いを見せるが、それ以外の世界に関してはまるっきり無知だった」という点で共通しています。

1980年代には、再び「学習機械」の研究が盛んになってきました。多分、これから先もチューリングが予言したように、人工知能の研究はこの2つの方向の間を揺れ動きながら進んでいくのだと思われます。

14.2 プロダクション・システム

人工知能システムと一口に言ってもいろいろなものがあります。ここでは最も古典的なエキスパート・システムであるプロダクション・システムを取り上げてみましょう。

14.2.1 メカニズム

プロダクション・システムはルールの集合からなるプロダクション・メモリと断定したもの(assertion)の集合からなるワーキング・メモリ、及び推論エンジンから構成されます。ワーキング・メモリのassertionはワーキング・メモリ・エレメント(WME)とも呼ばれます。例えば、図14.1のようなルールとWMEが存在したとします。

ルールの条件の部分(IFの部分)をleft-hand side(LHS)、アクションの部分(THENの部分)をright-hand side(RHS)と呼びます。推論エンジン(図には示してありませんが)はルールのLHSとWMEとのマッチングを行います。マッチングに成功したルールがあった場合、推論エンジンはルールのRHSに記述されたアクションを実行に移します。これをルールが発火(fire)すると言います。

アクションを実行した結果、新たなWMEが生成されます。これを繰り返すと、予めワーキング・メモリにあったassertion、即ち事実の集合とルール、即ち知識の集合を基に推論し、何らかの推論結果が導き出された、ということになるのです。これがプロダクション・システムの大雑把なメカニズムです。

プロダクション・システム(1)
図14.1 プロダクション・システム(1)


プロダクション・システム(2)
図14.2 プロダクション・システム(2)





推論エンジンの動作は次のようになります。 これらの動作を繰り返すことによって、最終的に存在しているWMEが推論の結論ということになります。 例えば、学生諸君に馴染みがあるものとしては、
          IF 総出席時間が80%以上で教科出席時間が60%以上
          THEN 定期試験受験資格あり

          IF 不合格単位数が8単位未満
          THEN 進級決定
などがあるでしょう。この推論の結果は「進級決定」、「留年決定」などになるはずです。このようにプロダクション・システムはIF-THENの形式で記述されたルールを用いて推論を行い、結論を得ようとします。

14.2.2 推論エンジンのリスト

プロダクション・システムの推論エンジンをゼロから自力で作るのは相当大変です。そこで、以下に推論エンジンの部分のリストを示します。プロダクション・メモリを構成するルールの例も1つ付けておきました。前節ではプロダクション・システムをプロダクション・メモリ、ワーキング・メモリ、推論エンジンに分けて説明しましたが、これは機能的な区分のことでProlog実行時には厳密な区分はありません。プログラムを書くときに、ルールをまとめて記述しておく、位でいいでしょう。また、このプログラムを実行する前には、
          ?- assert(wm(test)).
という操作が必要です。このwm(test)がワーキング・メモリ・エレメントです。勿論、自分でプロダクション・システムを作るときには、必ずしもwm節になっていなくても構いません。

このリストに記述してあるルールは以下の通りです。
          rule(1,[wm(test)],[retract(wm(test)),write(ok),nl]).
これは3つの引数を持ちます。第1パラメータはルールの識別をするための通番です。第2パラメータはLHSにあたるリストで、第3パラメータはRHSにあたるリストです。したがって、このルールは
          rule(1,[もしwm(test)という節が存在していたら],
                 [その節を削除して、画面にokと出力し、改行しなさい]).
ということになります。 上に示したようにwm(test)節を生成したら、
          ?- ps.
と入力してプロダクション・システムを動かしてみてください。
          %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
          % 
          %   プロダクション・システム  
          % 
          %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
          %
          %   推論エンジン
          %     ps.で推論を開始する。
          %     psはps_subを繰り返し呼ぶ。
          %
          ps:-
              repeat,ps_sub,!,
              write(end).

          ps_sub:-
              (matching(M_rules),
               select_rule(M_rules,Rule_no),
               action(Rule_no),
               !,
               fail)
              ;true.

          %
          %   マッチング
          %     条件が該当するルールを全てピックアップする。
          %     CS_listはルール番号のリストとなる。
          %
          matching(M_rules):-
              findall(Rule_no,match_sub(Rule_no),M_rules),
              !,
              M_rules\==[].

          findall(X,G,_):-G,assert(ans(X)),fail.
          findall(_,_,R):-collect([],L),reverse(L,R).

          collect(L1,L2):-
              retract(ans(X)),
              \+(member(X,L1)),
              !,
              collect([X|L1],L2).
          collect(L,L).

          match_sub(Rule_no):-
              rule(Rule_no,LHS,_),
              slice(LHS).

          %
          %   競合解消戦略
          %     ここでは、最初に見つかったルールを採択している。
          %
          select_rule([Rule_no|_],Rule_no).

          %
          %   Actionの実行
          %
          action(Rule_no):-
              rule(Rule_no,_,RHS),
              slice(RHS).

          %
          %   サブルーチン
          %
          % 第1パラメータを逆順にして第2パラメータとする
          reverse(X,Y):-reverse_sub(X,[],Y).

          reverse_sub([],X,X).
          reverse_sub([H|X],Y,Z):-reverse_sub(X,[H|Y],Z).

          % 第1パラメータが第2パラメータに含まれているかどうかの
          % 判定を行う
          member(X,[X|_]).
          member(X,[_|L]):-member(X,L).

          % LHS,RHSなどのリストから1つづつ切り分けて実行する
          slice([]).
          slice([Head|Rest]):-
              Head,
              slice(Rest).

          %
          %   テスト用ルール
          %
          rule(1,[wm(test)],[retract(wm(test)),write(ok),nl]).




《演習問題》
目次に戻る目次に戻る