SlideShare une entreprise Scribd logo
1  sur  10
Elixir入門 番外編
「Elixir+Phoenixで弱々しい
AIを作る、の裏側」
2017/05/07 ver0.5作成
1
はじめに
このスライドは、現在Qiitaで連載中の同名のコラムの裏話や、
ElixirやPhoenix、ないしは各種ライブラリの使い方のコツやテク
ニックを散文的に紹介するものです
かいつまんで、適当にお伝えしている & ちょくちょく内容が書き換
わるため、冒頭に記載しているバージョンで、どこまで見たかを
チェックしていただければと思います(^_^)
2
#1の裏話や解説など
キャッチーなタイトルだったせいか、フォロワー/コラムがゼロの状態から、4日
間で500PV (シリーズ累計は1,500PV) 突破でありがとうございますw
さて、MeCabモジュールが、Windowsでは調子悪いおかげで、depsフォル
ダ配下のコードを見るきっかけができ、同時にMeCabモジュール程度なら自
作してしまおう、と考えを改め、その結果、#3~4でCaboChaモジュールを
自作する、という流れに繋がっていたりします (:os.cmdがなかなか便利)
あと、Windowsのみ、MeCabのインストール先について書いていますが、こ
れは、#3で行う、MeCabのNEologd辞書入れ替えで使うmecab-dict-
compile.cmdが「C:Program Files (x86)MeCabdic」以外を受
け付けない (=パラメータ指定があるが効かない) ため、MeCabをその他の
フォルダ配下にインストールすると、NEologdやIPADICの辞書フォルダだけ
「C:Program Files (x86)MeCabdic」フォルダ作って置く、や、イン
ストールされたコマンドを手動で修正しなければいけない、という間抜けな事
態に陥ります (インストール段階では気付きようも無いですが…)
3
#2の裏話や解説など
まず、recompile()と打つのがメンドクなったんで、当初予定に無かった
Phoenix化した (説明も手抜き) のはナイショです(^^)
Phoenixで、MVC以外を作ったことが無かったんで、単純なGETパラメータ
の受け取りを調べるのに、案外手間取ってしまいました (実際、事例もあま
り無い) が、実際できるようになると、テスト中とか、なかなか便利ですね
あと、末尾の方に出ていた「warning: the underscored variable
“_params” is used after~」ですが、これは先頭「_」付きの変数/引
数を、「後続の処理では使わない」という、Elixirにおけるルールに引っかかっ
ているためですね (ちなみに#4では、終端関数の引数に多用しています)
直すには、以下のようなコードにすればOKです
def index(conn, params) do
render conn, "index.html", _params: params
end
4
#3の裏話や解説など
実は、Qiitaの連載の構想は、「NEologdを使うのが案外面白かった」とい
うのが先にあり、Elixirのデータ変換をアレコレ活用でき、NEologdを使う
ケースを何にしようかな、と思った結果、人工無能にしよう、となりました
あなたが自然言語解析オタクなら、NEologdを使い倒すフロントエンドとし
て、Phoenix (とCabochaモジュール) をぜひご活用くださいw
xml_parserモジュールが、変換結果をタプルで返すのには、ちょっとビックリ
しました (xml_to_mapモジュールの「変換が不完全」よりはマシですが)
HTTPoison等に慣れていると、マップとしてパターンマッチ、というのが定石
なので、タプルのまま行くか、マップに一度変換するかは、悩みました (結局
は今後の使い勝手も加味して、マップ化しちゃいましたけどね)
それにしても、CaboChaは、実に優秀なプロダクトですね (サポートベクター
マシン部分のコードとか、以下URL見ながら、リーディングしてみようかしら)
yu-hatva.hatenablog.com/entry/2012/09/27/135815
5
#4の裏話や解説など
このスライドは、ほぼ#4のコード解説のために作ったようなもんです(^^)
まず、lib/cabocha.exの下記ですが、何しているコードか分かりますか?
これは、 「Regex.named_captures()」 を使って、「文字列が正規表
現でパターンマッチしたら、それをマップに変換」しているのです
def feature_map( feature ) do
Regex.named_captures( ~r/
^
(?<part_of_speech>[^,]*),
*?(?<part_of_speech_subcategory1>[^,]*),
*?(?<part_of_speech_subcategory2>[^,]*),
*?(?<part_of_speech_subcategory3>[^,]*),
*?(?<conjugation_form>[^,]*),
*?(?<conjugation>[^,]*),
(?<lexical_form>[^,]*),
(?<yomi>[^,]*),
(?<pronunciation>.*)
$
/x, feature )
end
6
#4の裏話や解説など
先頭の「~r」は正規表現の開始、「^」と末尾の「$」は、対象の先頭から末
尾を示す正規表現です
以降は、()で囲われた、マップに変換する部分で、各値の区切りであるカン
マ毎に、「[^,]*」で次にカンマが出現するまでの全ての文字を拾い、マップ
の値として適用しています
なお、2~6行目先頭の「*?」は、CaboCha (内部処理的にはMeCab)
の品詞解析の結果が「*」という値の場合、無効なので、これを除外するた
めの記載として、マップに拾われない()の外でマッチして捨てています
ちなみに、iexで「h Regex.named_captures」と打つと、割と分かりやす
いサンプルで教えてくれますよ
7
defmodule Relation do
…
def nodes( terminate_id, [ %{ "chunk" => %{ "id" => id, "link" => link } } | tail ], body, node_ids ) do
concat_node_ids = case terminate_id == [ link ] do
true ->
inner_ids = case link == "-1" do
true -> []
false ->
[ ids ] = Enum.map( [ id ], &( nodes( [ &1 ], body, body, [] ) ) )
ids
end
case inner_ids == [] do
true -> node_ids ++ [ id ]
false -> node_ids ++ [ id, inner_ids ]
end
false -> node_ids
end
nodes( terminate_id, tail, body, concat_node_ids )
end
def nodes( _terminate_id, [], _body, node_ids ), do: node_ids
#4の裏話や解説など
lib/Relation.exですが、「_」付きの引数が多用されています
これは、引数としては受け取るけど、後続で利用しないので、未使用でも
warning出さないでね、というコンパイラへの指示となります
ループ元データと、連結作成されるリスト (文字列) 以外の3つ以上の引数
を持つような再帰処理の終端関数でちょくちょく使うケースが多いです
8
#4の裏話や解説など
同じくlib/Relation.exですが、戻り値をリスト([~]) で受け取る、という
Elixirならではの独特の表記があります
これは、戻り値として、「余計なリスト」で包まれて返ってくるようなケースにて、
その余計なリストを除外するためのテクニックです
Enum.mapは、余計なリストを付ける代表です
defmodule Relation do
…
def nodes( terminate_id, [ %{ "chunk" => %{ "id" => id, "link" => link } } | tail ], body, node_ids ) do
concat_node_ids = case terminate_id == [ link ] do
true ->
inner_ids = case link == "-1" do
true -> []
false ->
[ ids ] = Enum.map( [ id ], &( nodes( [ &1 ], body, body, [] ) ) )
ids
end
case inner_ids == [] do
true -> node_ids ++ [ id ]
false -> node_ids ++ [ id, inner_ids ]
end
false -> node_ids
end
nodes( terminate_id, tail, body, concat_node_ids )
end
def nodes( _terminate_id, [], _body, node_ids ), do: node_ids
9
終わりに
このスライドで説明した箇所以外でも、細かなテクニックを使っている箇所が
ありますので、「ここがよく分からない」という箇所があれば、ぜひQiitaでコメン
トくださいm(__)m
#4が特に顕著ですが、再帰の中で、別の関数の再帰を呼び出すといった、
比較的、複雑な処理が頻繁に出てきますので、読み応えもありますが、キッ
チリ読み込んで、糧として吸収してください
一方で、もっとスマートに書ける箇所が、恐らく幾つもありますが、時間と慣
れの都合上、直し切れていませんので、「こう書いたらもっと良いよ」とかあれ
ば、そちらもコメントいただけると嬉しいです
それに該当しそうな箇所で、先に弁明しておくと、リスト連結を先頭で無く、
性能の悪い末尾にしているのは、コードを解説/理解しやすくするためであ
り、ここは手抜き等では無いことを予めお伝えしておきます

Contenu connexe

Plus de fukuoka.ex

Plus de fukuoka.ex (7)

重力プログラミング入門「第1回:地球の重力下で人工衛星を公転軌道に乗せる」
重力プログラミング入門「第1回:地球の重力下で人工衛星を公転軌道に乗せる」重力プログラミング入門「第1回:地球の重力下で人工衛星を公転軌道に乗せる」
重力プログラミング入門「第1回:地球の重力下で人工衛星を公転軌道に乗せる」
 
AI入門「第1回:AIの歴史とTensorFlow」
AI入門「第1回:AIの歴史とTensorFlow」AI入門「第1回:AIの歴史とTensorFlow」
AI入門「第1回:AIの歴史とTensorFlow」
 
やや関数型を意識した風Elixir/Phoenixご紹介
やや関数型を意識した風Elixir/Phoenixご紹介やや関数型を意識した風Elixir/Phoenixご紹介
やや関数型を意識した風Elixir/Phoenixご紹介
 
AI入門「第2回:Scala/Spark/Mahoutでレコメンドエンジンを作る」
AI入門「第2回:Scala/Spark/Mahoutでレコメンドエンジンを作る」AI入門「第2回:Scala/Spark/Mahoutでレコメンドエンジンを作る」
AI入門「第2回:Scala/Spark/Mahoutでレコメンドエンジンを作る」
 
Elixir入門「第2回:PC間で通信するアプリをサクっと書いてみる」
Elixir入門「第2回:PC間で通信するアプリをサクっと書いてみる」Elixir入門「第2回:PC間で通信するアプリをサクっと書いてみる」
Elixir入門「第2回:PC間で通信するアプリをサクっと書いてみる」
 
Elixir入門「第1回:パターンマッチ&パイプでJSONパースアプリをサクっと書いてみる」【旧版】※新版あります
Elixir入門「第1回:パターンマッチ&パイプでJSONパースアプリをサクっと書いてみる」【旧版】※新版ありますElixir入門「第1回:パターンマッチ&パイプでJSONパースアプリをサクっと書いてみる」【旧版】※新版あります
Elixir入門「第1回:パターンマッチ&パイプでJSONパースアプリをサクっと書いてみる」【旧版】※新版あります
 
Elixir入門「第3回:Phoenix 1.2で高速Webアプリ & REST APIをサクッと書いてみる」【旧版】※新版あります
Elixir入門「第3回:Phoenix 1.2で高速Webアプリ & REST APIをサクッと書いてみる」【旧版】※新版ありますElixir入門「第3回:Phoenix 1.2で高速Webアプリ & REST APIをサクッと書いてみる」【旧版】※新版あります
Elixir入門「第3回:Phoenix 1.2で高速Webアプリ & REST APIをサクッと書いてみる」【旧版】※新版あります
 

Elixir入門「番外編:Elixir+Phoenixで弱々しいAIを作る、の裏側」