Hatena::ブログ(Diary)

西尾泰和のはてなダイアリー

2017-01-03

書評:夢見るプログラム

だいぶ前に著者の加藤博士から頂いていたもののいろいろ忙しくて紹介が遅れました。

この本は1999年に「人工無脳は考える」を立ち上げて「人と会話をするソフトウェア」(人工無脳)について研究・考察を続けてきた加藤真一さんによる本です。特に2章では21ページにわたって、Elizaに始まる人工無脳50年の歴史を俯瞰していてとても良いです。人工知能による会話フレームワークが鳴り物入りで紹介されたりしたときに、中身を見てルールベースだったりすると、僕とかは「ゅぃぼっと」かよ!とツッコみたくなるわけです。だけどゅぃぼっとは1996年だからもう20年も前なんですねぇ。

ルールベースの人工無脳が、育つにつれて「ルール追加の手間あたりの賢くなった実感」がどんどん0に近づいて、ルールの作り手のモチベーション維持ができなくなることは、その頃から既に知られていたように思います。機械学習の分野では同様に「教師データ追加の手間あたりの精度の向上」が指数的に減衰する問題が知られていて、それへの対処として能動学習(Active Learning)などのアプローチが生まれているわけです。そこら辺の知見を取り入れて昔より良くなったのかな〜と思ったら20年前と変わらないルールベースのフレームワークだったりすると「ゅぃぼっと」かよ!とツッコみたくもなるというものです。

3章4章では90ページほど使って、実際に基本のルールベース人工無脳を実装しながら解説していきます。冒頭の「舞台設定とキャラクタ設定を作る」は僕にはない発想で目から鱗でした。後半ではルールベースの人工無脳にありがちな「直前の発言だけに反応すると話題が続かない」への対処として文脈をマルコフ近似しようとするアプローチは面白かったです。

夢みるプログラム 人工無脳・チャットボットで考察する会話と心のアルゴリズム

2015-01-21

FlaskをJSから叩いてエラーになった時に対話的デバッグする方法

WebアプリフレームワークのFlask(が内部で使っているWerkzeug)は、デバッグモードをONにしておくとエラーが起きた時にそのエラー画面からブラウザ上で対話的にコードを実行したり変数を表示させたりしてデバッグが出来る。これは超便利。なのだけど、最近JSからAPIを叩くケースが多くなって、その場合JSが予期しないHTMLを受け取ってパースに失敗するだけなので折角の対話的デバッグが生かせない。

そこで、エラー時には新しいwindowを開いて、レスポンスの内容をそちらに書き出すようにした。初回だけポップアップブロックが発動してしまうけど、それ以降はこれでAPIでも対話的デバッグが出来る。

$.ajax({
  url:'/buggy/', 
  data: 'q=42', 
  success: function(x){console.log(x)}, 
  error:function(x){
    var w = window.open('', 'debug_stuff', 'width=540,height=150');
    w.document.open();
    w.document.write(x.responseText);
    w.document.close();
  }
});

サーバ上でpdbデバッグという案(thanks @cocoatomo)に関しては、サーバのコード中でpdb.set_trace()で止めた場合は、コンソール上で対話的にデバッグすることはできるのですけど、python -mpdb server.pyした場合、例外発生時期待通りに対話的デバッガに入らない。おそらくFlask自体の例外処理と干渉してる。

スタックトレースを生かしたまま持っておいて、デバッグ用の画面にリダイレクトしているだけなので、リダイレクトのURLを適当に拾ってブラウザからそのURLにアクセスすればデバッグ画面を開けます。」(thanks @methane) がとても参考になったけども、どうも僕の環境ではリダイレクトはしていないみたいに見えて、あとXMLHttpRequestがリダイレクト先に勝手にアクセスするので面倒くさい。

このアドバイスがヒントになって、最終的にはここに書かれている内容に辿り着いた:

debugging - Posting JSON and python Flask - any techniques to use the Werkzeug debugger? - Stack Overflow

2015-01-17

改善案を考える・人工無脳について振り返る

彼> この会話で何が起きて欲しいですか?

僕> このプログラムの改善案について話し、それについて質問されることで考えを整理し、新しいアイデアを見つけたい

彼> あなたはその改善案をどう知るのですか?

僕> ユーザの発話の中からどの部分をキーフレーズとして抽出するかについて、最初のバージョンでは直前の発話だけから抽出してランダムに選んでいて、それでは話がどんどんずれていくので次のバージョンでは過去の発話も含めてランダムで選ぶようにして、それだと直前の発話から選ばれる確率がどんどん下がってしまうのでエビングハウスの忘却曲線的なのを入れて、IDFで重み付けして、スコアの高いものほど選ばれやすいようにしたのだけども、それでもイマイチなので抜本的に変更しようと思っている。

彼> その直前は、何のようですか?

僕> 直前の発話はまさに今聞いた内容で、やはりそれをきちんと聞いている風の反応をしないとユーザが手応えのなさを感じてしまう

彼> その風は、どんな種類の風ですか?

僕> 相手の発言を受け止めていることを、相手が実感できる

彼> 発話は、どんな種類の発話ですか?

僕> 質問に対するユーザの回答で、

彼> 発話は、どのあたりにありますか?

僕> 質問に対するユーザの反応で、それが長いほど傾聴がうまく言っている証拠、と書こうとしたのだが失敗した。発話は入力としてプログラムに入ってくる

彼> その発話について、他に何かありますか?

僕> 発話が長くなるように適切なキーフレーズを選んで質問することが重要。

彼> 傾聴について、他に何かありますか?

僕> 傾聴こそがこのプログラムがユーザに与える価値なので、まずは傾聴を定量的に計測する尺度を明確にして、その尺度の上で最適化を掛けるのがよいのではないかと考えている。

彼> 発話は、何のようですか?

僕> 発話はユーザという果実から搾り出されたジュースみたいなものだ。絞っているのが質問。

彼> 入力は、どんな種類の入力ですか?

僕> 現状ではキーボードを叩いてチャットのような形で入力している。音声入力が好きな人もいるけど、それは優先度の低いパスだと思う。

彼> そのキーボードは、何のようですか?

僕> 考えを綴る道具って考えるとペンのようだな。

彼> あなたはパスをどう知るのですか?

僕> まあ、音声入力ではなくキーボード入力に選択と集中するのは特に根拠の無い選択ではある。

彼> ユーザは、どこにありますか?

僕> コンピュータの前にいる。当面は僕がユーザだと思って良い。

彼> 音声入力は、どんな種類の音声入力ですか?

僕> NG そちらの話を掘り下げたいとは思っていない

彼> 傾聴は、何のようですか?

僕> ユーザの発話の中から適切なキーフレーズを見つけて、それについて問いかけることで更にたくさんの発話を促す、芋掘りみたいにつながり合っているアイデアを蔓が切れないように気をつけながら引っ張って、土から引きずり出すようなプロセス。

彼> そのキーフレーズは、どこにありますか?

僕> キーフレーズはユーザの発話の中にあって、どこがキーフレーズであるかを判定するのが目下の重要課題

彼> そのキーフレーズは、どんな種類のキーフレーズですか?

僕> 今は、形態素解析で単語を抽出した後名詞を結合してキーフレーズを作っているが、その結果IDFの辞書に登場しないものに関してはスコアが上限に張り付いてしまう。別のスコア付けの方法が必要で、そのスコアは「ユーザがどれだけ長い返答を返すか」に相関する

彼> そのキーフレーズについて、他に何かありますか?

僕> 構成単語やNグラムの特徴量からロジスティック回帰かパッシブアグレッシブみたいな方法でスコア付けする。あ、あと直前の発話の中のキーフレーズは特別扱いで高い優先度にすべき。あとこれを書いていて気づいたけど、今、名詞が連続しているとそれは全部くっつけたもの1個だけをキーフレーズにしてしまっているけど「直前発話中のキーフレーズ」だと「直前発話中」だけをキーフレーズにするんじゃなくて、部分列全部をスコア計算したい。

彼> そのキーフレーズは、何のようですか?

僕> フックのようで、そのフックで引っ掛けて引っ張りだす。

彼> キーフレーズは、どのあたりにありますか?

僕> 直接的にはユーザの入力の中にあるのだけども、過去の発話全部が共通のキーフレーズを介してネットワーク上につながっていて、キーフレーズは個々の発話をつなぐ紐であり、1つの発話の中だけに出てくるキーフレーズはそれほど重要ではない。何度も出てきて幾つもの発話で描写された側面をつなぎあわせているようなタイプのキーフレーズが重要。だから、キーフレーズは発話の中にあるって考えは適切ではないな。それは発話の間にあって、発話の上をゆるやかに覆っているイメージが適切か。

彼> あなたはそのキーフレーズをどう知るのですか?

僕> プログラムがどういう質問を返してきたかによって、プログラムが何をキーフレーズとして認識しているかがわかる。その認識が正しくない時にそれをプログラムに教える手段があればよいのか。

彼> 考えは、どこにありますか?

僕> キーフレーズは発話の中にあるって考えは、発話からキーフレーズを抽出する処理を書いたことに起因する思い込みの中にある

彼> ユーザについて、他に何かありますか?

僕> ユーザは適切な質問をされると楽しくなってさくさく返答が掛ける。だからそれを計測すると質問が適切であったかどうかが計測できるはずか。やはりターン制の対話プログラムではなくリアルタイムなチャットの形にして、入力時間なんかを特徴量として取ることが重要か。

彼> あなたはそのターン制をどう知るのですか?

僕> ターン制を選択したのは単に実装上一番楽であることが理由。必須ではないと当初から考えてた。リアルタイム化も難しくないよ、基本的には人間の発話を待てばいいだけなので。

彼> その実装上は、何のようですか?

僕> よくある間違いだな。何が顧客にとっての価値につながるかではなく、何が自分にとって楽であるかで実装を選択してしまうという間違い。

彼> スコア付けは、どこにありますか?

僕> 今の実装では、入力を受け取って形態素解析した後、その形態素列を受け取ってスコアその他の「環境」を更新する関数がある。キーフレーズのスコア付けがその関数の最初の仕事だったけど、今では拡張されて状態遷移で対話のフローを管理することも引き受けている。

彼> あなたはその状態遷移をどう知るのですか?

僕> 今はMetaphors in Mindを参考にしながらハードコーディングしている。

彼> ターン制について、他に何かありますか?

僕> 簡単な事で、今は入力欄でユーザがEnterを押したことを、即座にターンエンドの宣告として解釈しているわけだけど、現実のリアルタイムのチャットではそうではない。続きを書き始めている時、話が完了してない時はターンエンドではない。続きを書き始めているのはCUIアプリではなくHTML+JSとかのインターフェイスに変えれば簡単に取れる。

彼> ハードコーディングは、どんな種類のハードコーディングですか?

僕> 今の状態を保持する変数があって、ある状態でどういう条件を満たしたらどういう状態に遷移するかってのがコードで実装されている

彼> その条件について、他に何かありますか?

僕> 特にパラメータのあるタイプの条件では、本当にその条件が適切かどうかっていう検証がないのは問題。そこは十分なデータがあれば自動調整することもありえるかと。

彼> その条件は、どんな種類の条件ですか?

僕> 具体的には例えばキーフレーズの抽出数が一定量を超えたら、とか。

彼> その一定量は、どんな種類の一定量ですか?

僕> 今はフィーリングで決め打ちになっている。実際に、一度大きくしすぎて期待した遷移がなかなか起きないこともあった。今もそう。あー、あとはキーワード抽出だった時代に比べてキーフレーズ抽出に変えたから抽出される数が減っているのにそれに合わせてないのも原因か。今後キーフレーズの部分列もキーフレーズにするように変えたら、今度は抽出数がぐっと増えることになるな。

彼> その抽出数は、どこにありますか?

僕> 単純に、今までに出てきたキーフレーズとそのスコアを溜めている変数があって、それのサイズで見ている。

彼> 部分列は、どんな種類の部分列ですか?

僕> 複数の単語で構成されているキーフレーズに

彼> 関数について、他に何かありますか?

僕> キーボードの調子が悪い…Enterキーが2度押しになってしまうとか酷い……。複数の単語で構成されているキーフレーズに関して、その列の一部を取り出してできるフレーズもすべてキーフレーズとしよう、という意味ね。関数は環境更新の話だったか。他には…いろいろな機能を引き受けすぎの感はあるな。でもここはホットスポットなので今はグチャッとしてても仕方ないと思う。状態の更新は今はハードコードでも、最終的には行列に変わると思っている。決定論的な遷移は確率的な遷移の特殊形にすぎないわけで、内部状態が外部からの入力により確率的に遷移して、その状態によって外部へのアウトプットが確率的に決まる、というモデルを考えていて、現状前半が決定論的、後半が確率的になっている状態だけど、前半も確率的になるのが適当だし、それは遷移行列で表現されるだろう。

彼> その2度押しについて、他に何かありますか?

僕> NG その話題を掘り下げるのはやめよう。

彼> その2度押しは、何のようですか?

僕> NG やはり「それやめよう」コマンドは必要だな。直前の質問を作成するのに使われたキーフレーズのスコアを減らすコマンドとして。

彼> その確率的は、どんな種類の確率的ですか?

僕> 今のプログラムの出力を決める確率モデルは単純で、状態が決まるとその状態で出力可能な複数の質問テンプレートが決まり、質問テンプレートに埋め込むキーフレーズは別途抽出されており、その直積の集合から「既出の質問」と「NG登録された質問」を取り除いたものからのサンプリングになる。

彼> 確率的は、どこにありますか?

僕> 今は出力を返す関数の中にハードコードされている。

彼> 確率的について、他に何かありますか?

僕> これも今はハードコードされているけど、出力確率の行列になってもよいと思う。資料中の会話例から頑張って教師データを作ったら隠れマルコフモデルのパラメータ推定問題に帰着するなぁ。めんどくさいし、あんまり顧客価値の改善につながらないと思うのでやらないけど。頑張って教師データを作って学習させるよりは、普段使っているうちに徐々に賢くなる方がいい。

彼> そのハードコードは、どこにありますか?

僕> 出力を返す関数の中。

彼> 教師データは、何のようですか?

僕> 負担。性格的な問題かもしれないけど、僕にはマメさがないので、教師データを事前に頑張って整備したら性能が上がります、っていう系統の手法はあんまり好きになれない。

彼> 決定論的について、他に何かありますか?

僕> 昔の人工無脳はおおよそ決定論的だった。キーワードがマッチした時に出力される言葉が決まっていた。だけどその後、ひとつのキーワードに対して複数の出力を対応付けることができるものが出てきて、その場合、キーワードのマッチした出力の集合からランダムにサンプリングして出力されるような実装になっていた。今から考えると、あれが決定論的な対話から確率的な対話への分かれ道だったかもしれない。

彼> その人工無脳は、どんな種類の人工無脳ですか?

僕> キーワードとそれに対する出力のペアを持っていて、ユーザが入力した時に、その入力の中にキーワードが見つかればそれに対応した出力を出す。このシステムとの比較で説明すれば、キーフレーズ抽出の部分が、あらかじめ登録されているキーワードの文字列パターンマッチで置き換えられている

彼> 遷移は、どこにありますか?

僕> 今説明した範囲では昔の人工無脳に状態遷移はない。僕が中学生の時に作った人工無脳はその点がちょっと改良されていて、文字列パターンマッチの対象がプログラムの発話自体にも拡張されていて、かつ不可視なラベルを埋め込む構文を用意したため、キーワードと出力のペアっていう従来型のデータ形式の枠組みを引き継ぎつつ、内部状態の遷移を記述できた。それまでの人工無脳が刺激に対して単発で反応するだけで記憶を持たなかったのに対して、僕のフレームワークを使えば複数回の出力で構成されるようなやりとりを記述することができた。

彼> その出力は、どのあたりにありますか?

僕> データファイルの中に、キーワードと一対一対応する形で記述されていた。

彼> ハードコードについて、他に何かありますか?

僕> そういう意味では、その人工無脳はなんらの確率モデルをハードコードしていない。全部データ側に押し出されていて、システム側は状態遷移を記述出来るだけの表現能力を持った記述言語を提供することに徹していたといえるだろうなぁ。

彼> その人工無脳は、何のようですか?

僕> 子供だね。知識を与えれば与えるほど成長してより賢くなる。しかしとにかくデータ量を増やせば賢くなるはずだというアプローチは、データ追加の労力あたりの賢くなった実感が徐々に飽和しだしてしまった。アトムを作った天馬博士と同じで、自分の子供だと思っていたアトムが成長しないことに気づいて愛が失われてしまった。

彼> そのデータ量は、どんな種類のデータ量ですか?

僕> 人間が直接手作業で入力することで作られたデータの量。だからさっきの話だと学習データに相当するもので、負担なわけだ。しかし当時は汎化性能って考え方がなかったから、入力した以上に処理できる量をスケールさせる方法がわからなかったんだな。

彼> 人工無脳について、他に何かありますか?

僕> 人間と対話することを主目的とするプログラム、っていう観点から言うと、Siriだとかしゃべってコンサルだとかの形で対話経験のあるユーザはかなり増えたんじゃないのかな。Herって映画もあったし。ただ、20年前のその人工無脳ブームは「Appleが作って与えた人工の人格との会話が楽しくてブームが起きた」のではなく「個々人が自分の好きな仮想の人格を実装して、自分だけの人工無脳を作ることが出来る」というところが顧客価値になっていたと思う。お仕着せの仮想人格と話すことではなく、自分でつくり育てることの方に価値を見出すユーザがいるっていうのが重要なポイントだと思うなぁ。

彼> その人工無脳は、どのあたりにありますか?

僕> インターネット上にチャットルームがあって、まあPerlで書かれたCGIなので簡単に設置や改造ができて、そいつに人工無脳が搭載されている、という時代があったわけだよ。そうか、最近の若い人にとってチャットってのはSkypeやLINEなのか。そうだとすると人工無脳を設置するのはそう手軽ではないか。

所見:当初の目的からずれていってるなー。当初の目的に照らし合わせて有益かもしれない内容を整理すると:

  • 音声入力を避けてキーボード入力に専念していることに、特に根拠は無い
  • 「キーフレーズはユーザの発話の中にある」と考えていたが、これは根拠の無い思い込みにとらわれていた。「キーフレーズは発話の間にある」と捉え直すことで、キーフレーズの役割は複数の発話を結びつけることであり、発話をまたいだ出現回数こそが本質的に重要な尺度なのに今はそれを使ってないってことに気づいた
  • また、適切でないキーフレーズが選ばれている場合、ユーザはそれに気づくことができるので、その気づきをプログラムに教える手段を用意すべき
  • 当初考えていた「ユーザの発話の長さが長くなるように機械学習で適切なキーフレーズを選ぶためのモデルを作る」をやる上では、ユーザの入力時間とかも取れたほうが良いのではないかということで、まずはリアルタイムチャット形式へのUIの変更のほうが優先度高。

という感じか。

2014-01-04

抽象化に関して「知りたいこと」を募集します

来る1/10〜12の「第55回プログラミング・シンポジウム」にて「抽象化」をテーマとしたセッションを行います。

セッション概要

「抽象化」をキーワードに、参加者から「知りたいこと」を募集、みんなでそれに対する答えを考えてワイワイする会です。

「知りたいこと」の例

  • プログラムを書く上でどこまで抽象化するのか?
  • どうやって適当な抽象化レベルを判断する?
  • 抽象化する上での適切な言語の選択がある?
  • 抽象的に考えることをどうやったら習得できる?教えられる?
  • そもそも「抽象化」って何?
  • 抽象化のオーバーヘッドはどこまで軽減できるのか?
  • OSの権限分離のような強制的抽象化と、フレームワークのような強制でない抽象化について、何か違いはあるか?
  • 抽象化をうまくやる,あるいは抽象的に考えるには,どんな環境・制約があるといいのか?(今回「知りたいこと」という制約を付けたことが具体例。他には?環境の具体例としては大学における学習環境、ホワイトボードたくさん、議論しやすい什器など。他には?)

いきさつ

最近、僕は「学ぶ方法」の改善に興味があり、Facebook上で色々それに関することをつぶやいていました。それが幹事の目に止まり「これをテーマにセッションをしたら面白いのではないか?」ということで企画がスタートしました。

考えてみると「抽象化」をテーマにした議論ってのはあんまり経験したことがありません。これは、異なる専門分野の人が「プログラミング」という一段抽象的なキーワードで集まっている、プログラミングシンポジウムならではの試みといえるかもしれません。

このテーマには「正解」がないでしょうし、みなさんから寄せられた「しりたいこと」に答えが出せるかどうかすらわかりません。かなりチャレンジングなセッションです。有益なものになるように、最善を尽くしたいと思っています。

募集

そこで、抽象化に関して「知りたいこと」を募集します。

事前に情報があるほうが、ファシリテーションは楽になります。またセッションのファシリテーションがどう設計されているかに関して、事前に知っている参加者が多いほど現場の運営は楽になります。

「『知りたいこと』を募集する」と制限しているのは、制限された方が「なんでもいいから意見をお寄せください」と言われるよりも、思いつきやすいからです。

制限対象として「知りたいこと」を選んだのは、これが少し手を加えれば「問い」の形になるので、それに対する他の人の意見を引き出しやすいからです。

現場でのファシリテーションは大筋では「視点の異なる人から具体的な情報を集めてホワイトボード上で共有」→「異なる情報の間の共通の構造を探す」という流れになるかと思います。つまり、これも「抽象化」のプロセスです。

「知りたいこと」を募集します。よろしくお願いいたします。

本セッションのおおまかなアジェンダ

状況に応じて柔軟に変えるつもりですが目下の予定としては以下のとおりです。

  • 開始までに付箋などで質問を募集し続ける
  • 開始後、ホワイトボードでどんな質問があったかを可視化
  • 追加の質問を口頭で募集する
  • 質問の文面について、わからない単語や質問の意図などの質問を募集する
  • 全部の質問を近いものが近くに来るように配置し直す
  • 質問に対する回答を募集する
  • 回答に対する反論を募集する
  • 抽象的な発言に対して実例を募集する
  • 異なる実例の間の共通点を探す

参考文献

まとめ

https://gist.github.com/nishio/8378624

2013-01-17

仕事を中国に“アウトソーシング”は頭がいいか

自分の仕事を無断で中国に“アウトソーシング”していた従業員」が頭がいいとかなんとか話題になってたけど、給与水準の異なる国にアウトソースすることのメリットは8ヶ月前に発表した(アイデアを塩漬けにしない-世界中の人に手伝ってもらう方法-)し、会社の仕事を全部アウトソースすることのデメリットについても既に書いた(Yoshioriの質問に対する解答)ので、僕にとっては今更感が強い。


で、仕事を中国にアウトソースした彼が頭がいいのかどうか、という話。無断で会社のVPNに接続させてただって?アホとしか言いようがない。全部丸投げ?問題にならないように切り分ける能力が欠如してたとしか思えない。いくらでも方法はあるのに。

具体的な戦術は、具体的な状況に依存するが、もちろん「上司を説得する」という方法もありだし「将来必要になるコードを事前にオープンソースプロジェクトとして作らせておく」だの「サードパーティライブラリにある自分にとって重要度の高いバグを直させる」だの「フレームワークの使い方のノウハウを溜めるために業務と無関係な、しかし流用できるところのたくさんある別のプロジェクトをさせる」だの、色々選択肢はある。

その中で僕が一番お気に入りなのが「副業を投げる」というもの。業務にそもそも関係ないから切り分けを考える必要がない。切り分けを考えるのにも頭を使うからね。で、それを実践したのが件の発表中で紹介したプロジェクトなのだ。このプロジェクトがきっかけで、カナダの某社からコンタクトがあって、現在別のプロジェクトが進んでいる。

だからね、重要なことなので繰り返しておくと、アウトソースは賢いが、それを会社に損害を与えるような方法でやるのはアホだ。そんなアホでも個人でアウトソース出来る恐ろしい時代なんだ、今は。