(この記事は Elixir (その2)とPhoenix Advent Calendar 2016 6日目の記事です)
AIには、「強いAI」と「弱いAI」という分類がありますが、Elixirと、MeCabやWikipedia API等を使って、「弱いAI」の中でも、更に貧弱ゥな「弱々しいAI」(笑)を何回かに分けて作ってみます
ザックリとした仕様としては、こんな感じですかね
- 対話して、言われた文脈を何となく解釈して、それとない返事を返す
- 聞いた言葉から受けた印象から、感情のようなものが揺らぎ、返事が変化する
- 足りない知識は、Wikipediaに取得しに行き、当たり障り無い感じで引用する
なんとも頭の悪いAIになりそうな気配しかしませんが、アレコレしているうちに魂みたいなものが宿るか、哲学的ゾンビができあがるかも知れない
ひとまず今回は、MeCabで日本語の文章をパースして、ワンパターンな意味解釈をするところまで作ります
なお、本コラム中の「Elixirの書き方」については、あまり細かく説明をしていないので、「ここの書き方が分からない」とか「この処理が何をしているのかよく分からない」等あれば、コメントいただければ、回答します
ElixirとMeCabのインストール
まず、Elixirをインストールします
下記からダウンロードしてインストールするか、yumやapt、Homebrew等でインストールしてください
Elixirダウンロード: http://elixir-lang.org/install.html
Dockerを使うのであれば、Dockerをあらかじめインストールした上で、Elixirイメージを持ってきます
Dockerダウンロード: https://www.docker.com/get-docker
※Windows版やMac版は、Stableが調子悪い(docker pullが失敗する)ため、そのときはEdge版で治ることが多い
> docker pull trenpixster/elixir
> docker run -it trenpixster/elixir /bin/bash
# uname -a
Linux 8abfc1bd7754 4.9.12-moby #1 SMP Tue Feb 28 12:11:36 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
次に、MeCabを下記からダウンロードしてインストールするか、yum等でインストールしてください
MeCabダウンロード: http://taku910.github.io/mecab/#download
ちなみに、Windows版MeCabは、「C:\Program Files (x86)\MeCab\」以外の場所にインストールすると、次回以降で大変な思いをすることになるので、インストール先は変えないほうが無難です
あと、辞書の文字コードは、「UTF-8」を選択してください
Elixirプロジェクト作成、MeCabモジュール導入/修正
Elixirプロジェクトを作成します
mix new mini_ai
cd mini_ai
MeCabをElixirで利用する準備として、mix.exsにMeCabを追加します
defmodule MiniAi.Mixfile do
use Mix.Project
…
defp deps do
[
{ :mecab, "~> 1.0" },
]
end
…
MeCabモジュールを取得します(要ネット接続)
mix deps.get
WindowsネイティブのElixirを利用する場合、そのままのコンソールでは、Elixirが扱うUTF-8を表示できないため、事前に切り替えておきます
chcp 65001
また、Windowsでは動かないMeCabモジュール内のUNIX依存コードがあるため、以下パスのファイルを修正する必要があります
98: x when x == nil or x == false ->
99: """
100: cat <<EOS.907a600613b96a88c04a | mecab #{mecab_option}
101: #{str}
102: EOS.907a600613b96a88c04a
103: """
これを以下のように書き換えます
98: x when x == nil or x == false ->
99: "echo #{str} | mecab #{mecab_option}"
なお、deps.getで取得したモジュールは、上記のようなコード書き換えを行っても自動的にはリビルドされないため、もし_build/dev/lib/mecabフォルダが作られている場合は削除しておきます(後述の手順でリビルドが走ります)
MeCabでのパースを実装
lib/配下にmini_ai.exというファイルができているので、以下の内容に書き換えます
defmodule MiniAi do
def listen( message \\ "あなたは弱々しいAIですか?" ) do
Mecab.parse( message )
end
end
コンソールからの日本語入力に不自由しそうなので、今回はコード内に日本語文章を入れることにしました
(どこかの回で自由入力可能な状態にもっていきます)
これで準備は整いましたので、iex(Elixirのインタラクティブシェル)を起動し、ビルドします
iex -S mix
Eshell V8.0 (abort with ^G)
==> mecab
Compiling 1 file (.ex)
Generated mecab app
==> mini_ai
Compiling 1 file (.ex)
Generated mini_ai app
Interactive Elixir (1.4.2) - press Ctrl+C to exit (type h() ENTER for help)
iex>
iexから実装したコードを呼び出すと、MeCabでパースされ、文章が「単語毎のMapのリスト」として分解されます
iex> MiniAi.listen()
[%{"conjugation" => "", "conjugation_form" => "", "lexical_form" => "あなた",
"part_of_speech" => "名詞", "part_of_speech_subcategory1" => "代名詞",
"part_of_speech_subcategory2" => "一般",
"part_of_speech_subcategory3" => "", "pronunciation" => "アナタ\r",
"surface_form" => "あなた", "yomi" => "アナタ"},
%{"conjugation" => "", "conjugation_form" => "", "lexical_form" => "は",
"part_of_speech" => "助詞", "part_of_speech_subcategory1" => "係助詞",
"part_of_speech_subcategory2" => "", "part_of_speech_subcategory3" => "",
"pronunciation" => "ワ\r", "surface_form" => "は", "yomi" => "ハ"},
%{"conjugation" => "", "conjugation_form" => "", "lexical_form" => "AI",
"part_of_speech" => "名詞",
"part_of_speech_subcategory1" => "固有名詞",
"part_of_speech_subcategory2" => "一般",
"part_of_speech_subcategory3" => "", "pronunciation" => "アイ\r",
"surface_form" => "AI", "yomi" => "アイ"},
%{"conjugation" => "基本形", "conjugation_form" => "特殊・デス",
"lexical_form" => "です", "part_of_speech" => "助動詞",
"part_of_speech_subcategory1" => "", "part_of_speech_subcategory2" => "",
"part_of_speech_subcategory3" => "", "pronunciation" => "デス\r",
"surface_form" => "です", "yomi" => "デス"},
%{"conjugation" => "", "conjugation_form" => "", "lexical_form" => "か",
"part_of_speech" => "助詞",
"part_of_speech_subcategory1" => "副助詞/並立助詞/終助詞",
"part_of_speech_subcategory2" => "", "part_of_speech_subcategory3" => "",
"pronunciation" => "カ\r", "surface_form" => "か", "yomi" => "カ"},
%{"conjugation" => "", "conjugation_form" => "", "lexical_form" => "?",
"part_of_speech" => "記号", "part_of_speech_subcategory1" => "一般",
"part_of_speech_subcategory2" => "", "part_of_speech_subcategory3" => "",
"pronunciation" => "?\r", "surface_form" => "?", "yomi" => "?"},
%{"conjugation" => "", "conjugation_form" => "", "lexical_form" => "",
"part_of_speech" => "", "part_of_speech_subcategory1" => "",
"part_of_speech_subcategory2" => "", "part_of_speech_subcategory3
ここから、各単語を拾うには、以下のようなコードで、「単語毎のMapのリスト」のうち、単語だけを抽出し続け、それをリストに追加していきます
defmodule MiniAi do
def listen( message \\ "あなたはAIですか?" ) do
Mecab.parse( message )
|> get_words( [] )
end
def get_words( [ %{ "surface_form" => word } | tail ], words ), do: get_words( tail, words ++ [ word ] )
def get_words( [], words ), do: words
end
コード修正したら、iexの中でrecompile()とすると、リビルドされます
iex> recompile()
iex> MiniAi.listen()
["あなた", "は", "AI", "です", "か", "?", ""]
意味解釈(ごくカンタンなもの)
キチンとした意味解釈は、次回行うとして、今回は、「AI」という単語が出現したらAIのウンチクを語り、「AI」が無ければそっけない返答をする...そんなウザいAIに仕上げて、今回は完了としましょう
defmodule MiniAi do
def listen( message \\ "あなたはAIですか?" ) do
IO.puts( "あなた「#{message}」" )
answer = Mecab.parse( message )
|> get_words( [] )
|> reply
IO.puts( "貧弱AI「#{answer}」" )
end
def get_words( [ %{ "surface_form" => word } | tail ], words ), do: get_words( tail, words ++ [ word ] )
def get_words( [], words ), do: words
def reply( words ) do
case words |> Enum.any?( &( &1 == "AI" ) ) do
true -> "はい、そうです。「弱いAI」は思考できませんが、「強いAI」は人のように思考できるんですよ。"
false -> "ふーん( ´,_ゝ`)"
end
end
end
リビルドして、実行すると、ウンチクを語ります
iex> recompile()
iex> MiniAi.listen
あなた「あなたはAIですか?」
貧弱AI「はい、そうです。「弱いAI」は思考できませんが、「強いAI」は人のように思考できるんですよ。」
:ok
文章から「AI」が無くなると、まるで興味無い返答になります
iex> recompile()
iex> MiniAi.listen
あなた「あなたはマシーンですか?」
貧弱AI「ふーん( ´,_ゝ`)」
:ok
次回は、もう少し、頭良い感じにしましょう
p.s.
6/8(木)開催の《fukuoka.ex #1》発足MeetUp、満員御礼です。ご来訪を楽しみにお待ちしております
(※立ち見席とか検討するので、ダメ元でよろしければ、引き続きお申込みどうぞ)