ElixirでのAIテクニックのコーディング:生成およびテストアルゴリズム
最近、私はI.I.の分野でプロジェクトに取り組んでいます。 (人工知能)。 私の予想によると、このプロジェクトの作業にはかなりの時間がかかります。 目標はElixirで100%プロジェクトを作成することでしたが、この決定を下す前に、I.Iの分野で最も人気のあるソリューションのいくつかを実装できることを確認する必要がありました。 Elixirで。 数日間にわたって、私はI.I.の分野の研究者が使用する最も効果的な手法のいくつかを研究しました。 ソフトウェアで問題を解決します。
この記事では、 Generate and test algorithm (試行錯誤とも呼ばれる)と呼ばれるメソッドについて簡単に説明し、このメソッドのバリエーションを使用して、Elixirプログラミング言語を使用して簡単な問題を解決します。
I.I. 今日最も有名なものは、機械学習、自然言語処理、ロボット工学です。 しかし、私にとっては、IIでの私の好きな分野です。 自動推論として知られています。 私が実証するプログラムは、この領域IIに分類されます。
問題
非常に大きな自然数のグループの合計が偶数かどうかについて疑問文を作成し、この質問に対する答えを作成するプログラムを作成するタスクが与えられたとします。 自動推論の方法を使用して、この単純な問題を解決してみましょう。
解決策
自動推論アルゴリズムは、上記のような単純なタスクに最適です。 アルゴリズムの仕組みは非常に簡単です。 3つの主要なステップがあります。
1. 2. , 3. , , 1 3
この概念を念頭に置いて、「結果はx + yでも同じですか?」という質問に答えることができるスマートプログラムを作成しましょう。 この演習では、偶数の合計のみを対象とするため、合計をカウントした結果が偶数であることをシステムが検出すると、それに応じて期待される結果が見つかったことを確認して対応します。 通常、この手法を使用して真の答えを得た場合、すべての処理を停止する必要があります。 ただし、このケースでは、システムによって準備されたすべての質問が聞こえたときにのみ停止します。 そして最後に、偶数ではない数の合計の質問に対して、いいえと答えます。
コード
このタスクのソリューションを作成するとき、これから見るコードがElixirのコア機能のほとんどをカバーしていることに気付きました。 したがって、この時点までにElixirを使用したことがない場合、これらのコード例は、言語がどのように機能するかの実用的なアイデアを提供します。
最初のステップは、すべての実装コードを含むSumAndTest
というモジュールを作成することです。
defmodule SumAndTest do @moduledoc """ This script is designed to show the functionality of the AI Algorithm that is known as Generate and Test. It will produce the results to a addition question and answer whether or not the answer is even. """
Elixitはモジュールを使用してコードを整理します。 神に感謝、クラスなし!!!
- @moduledocはコメントを追加するためのマクロです。これにより、このモジュールの目的を簡単に説明できます。
「結果x + yは偶数ですか?」という質問に答える必要があります。 それでは、この質問を含む関数を作成していきましょう。
defp addition_question(first_numbaer, second_number) do "Is the result of #{first_numbaer} + #{second_number} even?" end
2つの引数を使用して、
addition_question
関数を定義します
- Elixir文字列補間の構文はRubyと同じです
さて、質問があります。 次のステップでは、システムが質問に回答するための2つの可能な回答を特定します。 Elixirの機能を使用して、渡された引数に応じて同じ関数の代替定義を提供します。
defp say_answer(true) do "YES! Even result found." end defp say_answer(false) do "No." end
true
渡された場合、yesと言い、false
場合、noとだけ言います
- 引数の照合は、Elixirを他のほとんどのプログラミング言語と区別する重要な機能です。 人工知能のプログラミングに非常に適しています。
私たちはイエスとノーに答える能力を生み出しました。 しかし、この応答をトリガーするものは何ですか? 2つの数の合計の結果が偶数かどうかを判断する必要があります。 対応する関数を作成します。
defp even?(number) when is_number(number) do if rem(number, 2) == 0, do: true, else: false end
関数の本体を単にrem(number, 2) == 0
として書き換えることもできます。これは、それ自体でtrue
またはfalse
返しtrue
(およそ。)。
Elixirには、
when
キーワードを使用して関数の引数の検証を決定する機能があります。 引数が検証条件を満たさない場合、この関数定義は不適切と見なされ、次の定義のいずれかが適用される可能性があります(約Per。)
Elixirには1行に3項演算子の類似体があります
-
rem()
は、カーネルモジュール(コア)に属するElixir関数です。 除算の残りを完全に決定します。
これで主要な構成要素の準備が整いましたが、自動推論の作成に取り組んでいます。 システムがこの質問に正しく答えていることを確認できるように、システム自体に自動的に質問が出されるようにします。 これを担当するgenerate
関数を作成しましょう。
defp generate(amount_of_questions) when is_number(amount_of_questions) do generate(0, amount_of_questions) end defp generate(accumulator, amount_of_questions) when amount_of_questions >= 1 do question = addition_question(Enum.random(1..100_000), Enum.random(1..100_000)) build_list(question) generate(accumulator + 1, amount_of_questions - 1) end defp generate(total, 0) do IO.puts "#{total} addition questions generated." :ok end
混乱していますか? 必要ありません。 Elixirは通常、ループを使用しません。 関数が主なツールです。 必要に応じて、再帰を使用してアクションを再実行します。
パターンマッチングを使用すると、必要な数の質問を
generate
メソッドで取得し、同じメソッドのバージョンに渡すことができますが、引数は2つです。 最初の引数の値は0です。これは、質問の数の初期値がすでに生成されているためです。
2番目のオプションである
generate
関数は、2番目の引数が少なくとも1である2つの引数を受け取った場合にのみ呼び出されます。
Enum.random
関数は、指定された範囲を取り、この範囲の上限と下限内の乱数を返します。 これは私たちのシステムに最適です。どの数字が使用されるかはわかりません。 これにより、システムの正しい動作が完全にチェックされます!
質問が生成された後、すべての質問のリストを使用してシステムをチェックできるように、質問を保存する方法が必要です。 これを行うには、近い将来実装する
build_list
関数を使用します。
最後に、関数の最後で再度
generate
関数を呼び出します。今回はバッテリー(カウンター)を1つ増やし、生成する必要のある質問の総数から単位を引きます。
- 再帰が完了すると、質問カウンターは0になり、生成された合計質問数に関する情報が表示されます。
完成間近です。 しかし、他に重要なことがあります。 generate/2
関数では、 build_list
関数を呼び出しました。 なぜこれが必要なのですか?
Elixirには不変のデータ構造があります。 これは、現在の関数以外では状態がサポートされていないことを意味します。 これは、高度な可変性を持つシステムを作成すると物事が非常に迅速に複雑になる可能性があるため、IIプログラミングに最適です。 天文学的な量の未知の可能性に対処できる分野では、ある程度の一貫性が必要です。 この一貫性はデータです。
これらの質問を作成しましたが、関数を終了した後にシステムがそれらの質問を失わないように、それらを保存する方法が必要です。 これがエリクサーエージェントの出番です。 Elixirのエージェントは、プログラムが状態を保持することを許可します。 これにより、時間に関係なくデータ構造の変更を追跡できます。 エージェントを使用するのは非常に簡単で、ジョーアームストロング(Erlangの作成者)がErlangとElixirで「データベースは必要ありません!」と言う主な理由かもしれません。 。 最初に空のリストを含むエージェントを作成しましょう。 このリストには、質問が保存されます。
defp start_list do Agent.start(fn() -> [] end, [name: __MODULE__]) end
2つの引数を持つエージェントの作成を確認します。 1つ目は、空のリストを返す関数です。 2番目の引数はキーワードリストです。 関数は、現在のモジュールから名前を取得します。
- (注)エージェントには常に名前を付けてください!
わあ! エージェントには初期化する機能があり、状態を含めることができるので、先に出てきたbuild_list
関数にbuild_list
で実装するのが流行です。
defp build_list(question) do Agent.update(__MODULE__, fn(list) -> [question | list] end) end
- この関数は1つの引数-質問を取ります。実際、
build_list
は2つの引数をとるAgent.update
関数のラッパーです。 最初の引数はエージェントの名前です。 2番目の引数は、現在のリストを取得し、現在の質問をリストの新しいバージョンに追加する関数です。
システムが回答する必要があるすべての質問を表示するには、エージェントの現在の状態を取得する必要があります。 Agent.get/2
関数を使用するだけです。 この関数のquestions
呼び出します。
defp questions do Agent.get(__MODULE__, &(&1)) end
-
Agent.get
は2つの引数を取ります。 現在のモジュールの名前、および関数fn(x) -> x end
の短いバージョン。
ここで、すべての処理を1つの順次関数呼び出し(パイプライン)で実行できるように、このすべてのロジックをバインドする必要があります。 パイプラインは次のことを行う必要があります...
1. . 2. . 3. . 4. . 5. "" "".
ここでは、パイプライン演算子(またはパイプライン演算子)を使用します。 この演算子は、Elixirプログラミング言語の筋肉組織であり、I.I。処理アルゴリズムになります LISPの場合よりもはるかに明確です。
defp answer_to(question) do Regex.scan(~r/(\d+) \+ (\d+)/, question, [capture: :all_but_first]) |> List.flatten |> Enum.map(&String.to_integer(&1)) |> Enum.reduce(0, fn(n, acc) -> n + acc end) |> even? |> say_answer end
- パイプライン演算子は、その前にある関数呼び出しの結果を取得し、次の関数の最初の引数として使用します。
大きな数値をanswer_to/1
は、正規表現を変更して質問行から各数値の最後の桁のみを選択することでanswer_to/1
関数をわずかに最適化できます。合計のパリティは変わらないためです( answer_to/1
次の2つのオプションは元の記事にはありません)
defp answer_to(question) do Regex.scan(~r/\d*(\d) \+ \d*(\d)/, question, [capture: :all_but_first]) |> List.flatten |> Enum.map(&String.to_integer(&1)) |> Enum.reduce(0, fn(n, acc) -> n + acc end) |> even? |> say_answer end
または、 Bitwiseモジュールを使用して、バイナリシステムの数値の最後の桁(1または0)のみを考慮することができます。 ^^^
演算子は、引数のビット単位のXOR演算を計算します。
use Bitwise defp answer_to(question) do Regex.run(~r/(\d+) \+ (\d+)/, question, [capture: :all_but_first]) |> List.flatten |> Enum.map(&String.to_integer(&1)) |> Enum.reduce(0, fn(n, acc) -> acc ^^^ (n &&& 1) end) |> even? |> say_answer end
次に、システムのユーザーがシステムの動作を確認できるように、質問とその回答を表示する表示機能を作成しましょう。
defp display_answer_for(question) do IO.puts(question) IO.puts(answer_to(question)) end
-
IO
は、デバイスの標準入出力の操作のためのメインモジュールです。
ついに! システムの最後の部分にいます。 このプロセス全体をトリガーする方法が必要です。 次のことを行うスタートアップ関数が必要です...
1. `start_list`, . 2. . 20-. 3. .
この機能は次のようになります。
def start do start_list generate(20) Enum.each(questions, &display_answer_for(&1)) end
- エージェントが起動した後、システムに20の質問を生成するように指示できます。 これらの質問ごとに、質問とその回答を表示します。
準備ができました! SumAndTest.start
を呼び出すとSumAndTest.start
ますか? システムは、対応する回答を含む20のランダムな質問を生成します。 以下の結論を参照してください!!
$ iex Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace] Interactive Elixir (1.3.1) - press Ctrl+C to exit (type h() ENTER for help) iex(1)> c("sum_and_test.ex") [SumAndTest] iex(2)> SumAndTest.start 20 addition questions generated. Is the result of 31956 + 95609 even? No. Is the result of 56902 + 37929 even? No. Is the result of 63154 + 23758 even? YES! Even result found. Is the result of 22268 + 66438 even? YES! Even result found. Is the result of 76068 + 36127 even? No. Is the result of 14158 + 84195 even? No. Is the result of 55174 + 13171 even? No. Is the result of 53028 + 68694 even? YES! Even result found. Is the result of 82027 + 39083 even? YES! Even result found. Is the result of 32349 + 70547 even? YES! Even result found. Is the result of 41416 + 37714 even? YES! Even result found. Is the result of 91326 + 32635 even? No. Is the result of 42663 + 21205 even? YES! Even result found. Is the result of 90054 + 71218 even? YES! Even result found. Is the result of 38305 + 69972 even? No. Is the result of 59014 + 3954 even? YES! Even result found. Is the result of 55096 + 34449 even? No. Is the result of 89363 + 16018 even? No. Is the result of 60760 + 12438 even? YES! Even result found. Is the result of 10044 + 47646 even? YES! Even result found. :ok
おわりに
これはおそらく、すべての人工知能アルゴリズムの中で最も単純です。 そのシンプルさが、Automating the Futureの最初の記事でこのアルゴリズムについて書くことにした理由です。 可能性のある結果がごく少数しか現れないという問題を扱っている場合は、試行錯誤法の使用を検討します。
投稿者:クエンティン・トーマス