それはすべて、私は他の多くの人と同様にボットを書きたいという事実から始まりました。 ボットは私が常に忘れているあらゆる種類のことを思い出させると想定されていました-最初に実装されるはずだったシナリオは、ボットが午後10時に日中にノートに書き留めたすべてを読んだことを私に伝えることでした。
準備ができたものを探すことから始めました。 しかし、私に出会ったすべてのものは集会で投獄されました-いつ、誰と、どこで、どのくらいの時間。 さらに、誰もが通常の人間の言語を理解しているわけではなく、場所によっては繰り返しのイベントさえありませんでした。 不便。
だからあなた自身を書いてください。 ボットは非常にモジュール化されることがすぐに決定されました。 私が最初に書いたのは、電報とUnixソケット間の単純なブリッジです。 また、このソケットに任意のテキストをスローするシェルスクリプト。 そして、クラウンのエントリ。
./pa_send.sh " !"
一方向のコミュニケーションが登場しました。 まだ別。 何らかの形で人間のフレーズを解析する必要がありました。 そして、別の重要な決定を下しました-フレーズの解析を実際の実行から分離したいのです。 もっと正確に言うと、人間の言語のフレーズから人間の言語のフレーズを生成する翻訳モジュールが別に必要です。
1つは、ChatScriptがそのようなトランスレータとして使用されたときです。 ChatScriptでの作業は「質問に対する回答の選択」であり、「質問の翻訳の選択」が必要ですが、だれが他の目的でツールを使用するのを妨げていますか? ChatScriptに加えて、私はRiveScriptを見て、最終的にそれに焦点を合わせました。
$ ./human2pa.py aragaer , remind aragaer to " "
いくつかの最初のフレーズで十分だったので、ボットの他の部分、特に「脳」に切り替えて、一般的なLispで書くことにしました。 脳は人間の発話を翻訳者に転送し、Botov言語のフレーズを処理します。 次に、ボトフスキーで回答を作成し、同じ翻訳者を介して人間に翻訳してから回答します。
そして、それがどういうもので、どのように機能するのかが本当にわからなくなった状況にいることに気づきました。 そのため、ボット内のさまざまなモジュールのバージョンを何らかの形で修正するというアイデアが生まれました。 これに対処するために、ボトフスキー言語に出会いました。
作業中に作られた言語は不便でした。 それはもちろん、彼らは何らかの形で使用することができます...
10 -
maki-uchi log 10
私は、もっと便利なことをどのように行うことができるかについて考え始めました。 たとえば、解析を簡単にするには:
maki-uchi report count: 10
コロンを単語の一部と見なすと(特別な構文)、p-listに似たことができます:
:maki-uchi report :count 10
しかし、その後、Lispで同じpリストをはるかに簡単な方法で取得できることに突然気付きました。 ボットのモジュール間の相互作用はすべてjsonを介して実装されます。 したがって、人間の発話からjsonにすぐに翻訳できます。 これがどのように解析されるべきかの例として、Dialogflowは通常私の目の前に現れました。 残念ながら、私はそれにあまり興味がありませんでした-まず、私はこの翻訳を自分で実装したい、そして第二に...まだプライバシーの問題があります。 クラウドサービスを信頼していない人にボットを使用してもらいたい。 そして、彼らはそれをセットアップし、ローカルで使用します。
ニューラルネットワークは有効なJSONを生成できますか?
私は実験から始めることにしました。 ボットが取得できるはずのすべてのフレーズの中で、これは翻訳者からのフレーズです。「人がここで何を表現しようとしていたのか正確に理解できませんでした」。 これを行うには、たとえば"hello, world"
が{"unknown": "hello, world"}
変わるなど、任意の行に行けば十分です{"unknown": "hello, world"}
RiveScriptでは、このようなタスクは簡単に解決されます。
+ * - {"unknown": "<star>"}
しかし、私は完全な翻訳のために来ました。 したがって、数日間の反射の後、私の選択はnmtに落ちました。
39語を使用するスクリプトを作成しました。これをためらうことなく入力し、1〜4語の可能な組み合わせをすべて収集しました。 その後、彼は20万人を混合し、12万人を訓練に送り、40人がエラーとクロスバリデーションを計算しました。 それから私はnmtを起動して、これらすべてを学びました。
そしてそれは...失敗でした。
第一に、nmtの観点から、こんにちはと「こんにちは」は完全に異なる単語です。第二に、空白以外の任意の組み合わせも個別の単語です。 nmtが安定している間...私は閉じ引用符と中括弧を書きませんでした。試行錯誤により、元のフレーズがhello world test END
ように見える場合、答えは最終的に正しいはずです。
human last dog black END -> { "unknown" : " human last dog black " }
しかし、不慣れな状況はグリッドを混乱させました:
teach human rest green stack END -> { "unknown" : " teach human rest bag " } stack white light green mirror END -> { "unknown" : " stack white light word " }
そしてこれは、辞書にもある5番目の単語の追加です。 トラブル。
これはおそらく正しいタスクではなかったので、もう一度試してみることにしましたが、より合理的なフレーズを使用することにしました。
余談:タイマー
ボットの潜在的なシナリオの1つは、このような対話であると想定されていました。
- ? - , . -
そのため、さまざまなイベントをさまざまな遅延で、さらには定期的に繰り返して固定できるように、何らかのメカニズムが必要です。 私はその上に何かを書くためだけにそのようなことを外に書くことにしました。 コードは非常にシンプルであることが判明しました-最終的に、私はすでに私の人生で同様のものの具体的な実装に出くわしました。 もちろん、人が何らかの形でタイマーと対話するためにタイマーに直接行くことはありませんが、タイマーコマンドの構文は、どこかで開始するのに十分単純なように見えました。
タイマーは、次の3種類のコマンドを受け入れます。
-
{"command": "add", "name": "", "what": < json>, "delay": , "repeat": }
-繰り返しと遅延はオプションで、残りは必須です。 ゼロ以外の繰り返しは、イベントが周期的であることを意味します。 -
{"command": "modify", "name": "", "what": < json>, "delay": , "repeat": }
-ここで、繰り返し、遅延、およびオプション指定しない場合、対応するパラメーターは名前イベントで変更されません。 -
{"command": "cancel", "name": ""}
-ここではすべてが明らかなようです。
実験番号2
翻訳する必要があるフレーズは次のようになりました。
print "hello" after 30 seconds every 7 seconds modify "asdf" to print every 10 seconds cancel "qwerty"
これらのフレーズはチームと一致している必要があります
{ "command": "add" , "name": "hello" , "delay": 30 , "repeat": 7 } { "command": "modify" , "name": "asdf" , "repeat": 10 } { "command": "cancel" , "name": "qwerty" }
おっと、私は何を忘れた。 ただし、これは実験にわずかに影響しました。
最初に、トレーニング用のファイルを生成するスクリプトを作成しました。
- ランダムアクション-追加、変更、キャンセル
- 各チームには、このチームを意味するいくつかの言葉があります。 たとえば、追加の場合はprint、emit、sendです
- modifyコマンドの場合、名前を追加した後、toが追加され、addに対応する単語のいずれか
- 追加または変更の場合、偶然に遅延や繰り返しが発生する場合と発生しない場合があります。 変更には、2つのうち少なくとも1つが必要です
- 名前フィールドはランダムな文字セットです
- 繰り返しフィールドと遅延フィールドは、もしあれば、乱数(0〜200のオーダー)です。
- 辞書には、開き中括弧と閉じ中括弧、コンマが含まれています。 JSONでフィールド名になる単語は、コロンと共に辞書に分類されます
結果はまあまあです。 英語のテキストに行末マーカーを再度追加する必要がありました。 名前フィールドの値が辞書に含まれている場合、辞書のサイズはトレーニングファイルの行数と同じです。 ただし、これらの名前はトレーニングセットと交差検証セットの間で交差しないため、結果はあまり良くないようです。 とにかく、実際の翻訳では、辞書にない単語があれば、 <unk>
行き<unk>
。
辞書に名前を含めずに、数字を含めて遅延と繰り返しを行うと、すべてがそれほど悪くないことがわかります。 翻訳の結果はほぼ同じですが、彼は少なくとも数を適切に「翻訳」します。
最初の2つの実験からの結論
このアプローチの主な問題は、フレーズの一部を正確に「翻訳」する必要があることと、まったく別の原則に従って処理する必要があることです(この場合、いかなる方法でも処理されない)。 これは、 改善されたnmtの助けを借りて解決されましたが、残念ながら、使用方法に関する簡単なチュートリアルは見つかりませんでした。 たぶん私はひどく見ていました。 何を探すべきかわからない可能性が高くなります。機械学習とNLPが理解できないため、正しいGoogleリクエストを作成することすらできません。 繰り返しますが、私は精神的にDialogflowに戻ります-フレーズ内で個別に処理する必要がある部分を選択できます。 「文章からデータを抽出するためのニューラルネットワーク」のような単語で検索して、 シーケンスのタグ付けにつまずきます。
私は例を見て、これがまさに私が必要とするものであることを理解しています。
実験3
数ステップで「翻訳」を行う予定です。 最初のステップでは、タガーを介して元の文を実行します。 次に、タグがO(大文字のラテン文字oはその他)と異なるすべての単語で新しい文を作成し、タグ自体に置き換えます。
ソースフレーズ
print "hello" after 30 seconds every 7 seconds
タグ
O data_sched_name O data_sched_delay OO data_sched_repeat O
プレースホルダーを含むフレーズ
print data_sched_name after data_sched_delay seconds every data_sched_repeat seconds
その後、フレーズはnmtに移動し、翻訳が判明した後、置換を実行します。 とにかく何かをやり直すので、jsonを生成するのをあきらめます。 代わりに、私は次のようなフレーズを取得します
command add name data_sched_name delay data_sched_delay repeat data_sched_repeat
このjsonからビルドすることは、すでに技術的な問題です。
これが、「翻訳」が行われる方法です。 しかし、まだトレーニングがあります。 すべては以前とほぼ同じですが、今では3つの英語+タグ+翻訳を生成しています。 タガーのトレーニングには英語+タグのペアが必要であり、nmtのトレーニングには交換用の英語+翻訳のペアが必要です。
結果は勝利です。
ついに、前回の実験で何を失ったかがわかりました。 名前と等しい値を追加します。 タガーを再トレーニングする必要はありません。私はnmtを再トレーニングします...
最後に、最後の仕上げ。 私は英語のフレーズに最初から最後まで、またはまったく追加しないでください。 pleaseが存在する場合、完成したjsonの「トーン」フィールドに表示したいです。ボットが丁寧に対処されていることを知るのに役立ちます。 トレーニングデータを生成するためのコードを少し修正し、タガーとnmtを再トレーニングします。 うまくいく!
$ ./translate.py 'please start printing "hello" every 20 seconds after 50 seconds' < nmt> {'delay': 50, 'name': 'hello', 'tone': 'please', 'command': 'add', 'what': 'hello', 'repeat': 20}
最終的な結論
おそらく、nmtなしでも実行できます。 私はフレーズのクラスを知っており、タガーが元の文から抽出したデータから自分で構築します。 理論的には、分類器を使用するだけで十分です。 ここで、「モスクワの明日はどんな天気になるか教えてはいけない」という問題にぶつかりますが、これは適切な再訓練によって扱われます。
別の結論-純粋なpythonでトレーニングデータを生成する複雑なスクリプトを作成する代わりに、RiveScriptを使用して「約人間」のフレーズを生成できます。 タグと一緒に。 これにより、学習と再トレーニングのプロセスが大幅に簡素化されます。
最も重要なこと-今、ボットが理解できる言語にフレーズを翻訳する方法を知っているようです。
私が使用したスクリプトはここから入手できます 。 タガーを機能させるには、数ギガバイトの辞書をダウンロードする必要があります(sequence_taggingディレクトリにグローブを作成します)。 そのような辞書を自分で作成する場合、それなしでできる疑いがあります。 しかし、私はまだ方法がわかりません。