TensorFlow を使用したコンテキスト チャットボットの実装

TensorFlow を使用したコンテキスト チャットボットの実装

日常のチャットでは、文脈が重要です。 TensorFlow を使用してチャットボット フレームワークを構築し、コンテキスト処理メカニズムを追加してボットをよりインテリジェントにします。

[[195156]]

「あなたの手の中に世界が広がる」 — ベティ・ニューマン・マグワイア (http://www.bettynewmanmaguire.ie/)

多くのチャットボットに会話のコンテキストが欠けているのはなぜか、疑問に思ったことはありませんか?

すべての会話シナリオにおけるコンテキストの重要性を考慮すると、この機能をどのように追加すればよいでしょうか?

次に、島の原付レンタルショップを例に、チャットボットフレームワークを作成し、会話モデルを構築します。この中小企業向けチャットボットは、レンタル時間やレンタルオプションなどに関する簡単な質問を処理する必要があります。また、ロボットには、同じ日のレンタル情報を照会するなど、いくつかのコンテキスト情報を処理できるようにしたいと考えています。この問題が解決できれば、多くの時間を節約できます。

チャットボットを構築するには、次の 3 つの手順に従います。

  1. TensorFlow を使用して会話意図モデルを記述します。
  2. 次に、会話を処理するチャットボット フレームワークを構築します。
  3. 最後に、コンテキスト情報をレスポンシブ ハンドラーに組み込む方法について説明します。

モデルでは、TensorFlow の高レベル API である tflearn フレームワークを使用し、開発ツールとして IPython を使用します。

1. TensorFlow を使用して会話意図モデルを記述します。

完全なノートブックドキュメントはここにあります。

チャットボット フレームワークでは、会話の意図の構造を定義する必要があります。最も簡単で便利な方法は、次のように JSON 形式のファイルを使用することです。

チャットボットの意図

各セッション インテントには次のものが含まれます。

  • タグ(一意の名前)
  • スキーマ(ニューラル ネットワーク テキスト分類器が分類する必要がある文)
  • 回答(回答として使用される文章)

後で、いくつかの基本的なコンテキスト要素も追加します。

まず、必要なパッケージをいくつかインポートしましょう。

# NLP に必要なもの
nltk をインポートする
nltk.stem.lancaster から LancasterStemmer をインポート
ステマー = LancasterStemmer()

# Tensorflow に必要なもの
numpyをnpとしてインポートする
tflearnをインポートする
テンソルフローをtfとしてインポートする
ランダムにインポート

TensorFlow をまだ知らない場合は、このチュートリアルまたはこのチュートリアルに従ってください。

# チャットボットのインテントファイルをインポートする
jsonをインポート
open('intents.json') を json_data として実行します:
    インテント = json.load(json_data)

コード内の JSON ファイルはここからダウンロードできます。次に、コード、データ、分類子のファイルを整理し始めます。

単語 = []
クラス = []
ドキュメント = []
ignore_words = ['?']
# インテントパターンの各文をループする
intents['intents']内のintentについて:
    意図['patterns']内のパターン:
        # 文中の各単語をトークン化する
        w = nltk.word_tokenize(パターン)
        # 単語リストに追加
        単語を拡張する(w)
        # コーパス内の文書に追加する
        ドキュメント.append((w, インテント['タグ']))
        # クラスリストに追加
        intent['tag']がクラスにない場合:
            クラス.append(intent['tag'])

# 各単語を語幹から切り出し、重複を削除します
words = [stemmer.stem(w.lower())、w が words に含まれるか、w が ignore_words に含まれていない場合]
単語 = sorted(list(set(単語)))

# 重複を削除
クラス = sorted(list(set(classes)))

print (len(ドキュメント), "ドキュメント")
print (len(classes), "classes", classes)
print (len(words), "ユニークな語幹を持つ単語", words)

ドキュメントのリスト(各文)を作成しました。各文はいくつかの語幹で構成され、各ドキュメントは特定のカテゴリに属します。

27 件の文書
9 つのクラス ['さようなら'、'挨拶'、'営業時間'、'モペット'、'本日営業'、'お支払い'、'レンタル'、'ありがとう'、'本日']
44 個の固有の語幹単語 ["d"、'a'、'ar'、'bye'、'can'、'card'、'cash'、'credit'、'day'、'do'、'doe'、'good'、'goodby'、'hav'、'hello'、'help'、'hi'、'hour'、'how'、'i'、'is'、'kind'、'lat'、'lik'、'mastercard'、'mop'、'of'、'on'、'op'、'rent'、'see'、'tak'、'thank'、'that'、'ther'、'thi'、'to'、'today'、'we'、'what'、'when'、'which'、'work'、'you']

たとえば、語幹 tak は take、taking、takers などに一致します。実際のプロセスでは、役に立たないエントリの一部を削除することもできますが、ここではこれで十分です。

残念ながら、このデータ構造は TensorFlow では使用できないため、このデータを単語から数値テンソルにさらに変換する必要があります。

# トレーニングデータを作成する
トレーニング = []
出力 = []
# 出力用の空の配列を作成する
output_empty = [0] * len(クラス)

# トレーニング セット、各文の単語の集まり
ドキュメント内のdocの場合:
    # 単語のバッグを初期化する
    バッグ = []
    # パターンのトークン化された単語のリスト
    パターン単語 = doc[0]
    # 各単語の語幹
    pattern_words = [stemmer.stem(word.lower()) は pattern_words 内の単語に対して実行されます]
    # 単語の配列のバッグを作成する
    勝利を言葉で表すと:
        bag.append(1) wがpattern_words内にある場合、そうでない場合はbag.append(0)

    # 出力は各タグに対して '0'、現在のタグに対して '1' です
    出力行 = リスト(出力空)
    出力行[クラス.インデックス(doc[1])] = 1

    トレーニング.append([バッグ、出力行])

# 特徴量をシャッフルしてnp.arrayに変換します
ランダムシャッフル(トレーニング)
トレーニング = np.array(トレーニング)

# トレーニングリストとテストリストを作成する
train_x = リスト(トレーニング[:,0])
train_y = リスト(トレーニング[:,1])

データの順序が入れ替わっていることにご注意ください。 TensorFlow は、トレーニングされたモデルの精度をテストするために、データの一部をテスト データとして選択します。

単一の x ベクトルと y ベクトルを見ると、これは bag of words モデルであり、一方は一致させるパターンを表し、もう一方は一致のターゲットを表します。

train_x の例: [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1] 
train_yの例: [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]

次に、モデルを構築しましょう。

# 基礎となるグラフデータをリセットする
tf.reset_default_graph()
# ニューラルネットワークを構築する
ネット = tflearn.input_data(shape=[なし、len(train_x[0])])
ネット = tflearn.fully_connected(ネット、8)
ネット = tflearn.fully_connected(ネット、8)
ネット = tflearn.fully_connected(ネット、len(train_y[0])、アクティベーション='softmax')
ネット = tflearn.regression(ネット)

# モデルを定義してテンソルボードを設定する
モデル = tflearn.DNN(ネット、tensorboard_dir='tflearn_logs')
# トレーニングを開始する(勾配降下アルゴリズムを適用する)
モデルをフィット(train_x、train_y、n_epoch=1000、batch_size=8、show_metric=True)
モデルを保存します('model.tflearn')

このモデルは、この記事と同じ 2 層ニューラル ネットワーク モデルを使用します。

tflearn でのモデルのインタラクティブな構築

作業のこの部分は完了したので、後続のコードで使用できるようにモデルとドキュメントを保存する必要があります。

# すべてのデータ構造を保存する
輸入ピクルス
pickle.dump( {'words':words, 'classes':classes, 'train_x':train_x, 'train_y':train_y}, open( "training_data", "wb" ) )
[[195157]]

チャットボットフレームワークの構築

この部分の完全なコードはここにあります。

応答を処理するためのシンプルなステート マシンを構築し、前のセクションのインテント モデルを分類子として使用します。チャットボットの仕組みを知りたい場合は、ここをクリックしてください。

前のセクションと同じパッケージをインポートし、前のセクションと同じようにモデルと文をアンピクルする必要があります。チャットボット フレームワークはモデルとは別に構築されていることに注意してください。インテント スキーマが変更されない限り、モデルを再構築する必要はありません。インテント スキーマが変更された場合は、モデルを再実行する必要があります。数百のインテントと数千のパターンがあるため、モデルの構築には数分かかる場合があります。

# すべてのデータ構造を復元する
輸入ピクルス
データ = pickle.load( open( "training_data", "rb" ) )
単語 = データ['単語']
クラス = データ['クラス']
train_x = データ['train_x']
train_y = データ['train_y']

# チャットボットのインテントファイルをインポートする
jsonをインポート
open('intents.json') を json_data として実行します:
    インテント = json.load(json_data)

次に、TensorFlow (tflearn フレームワーク) を使用してトレーニングしたモデルをインポートする必要があります。パート 1 で行ったように、最初に TensorFlow モデル構造を定義する必要があることに注意してください。

# 保存したモデルをロードする
モデルをロードします('./model.tflearn')

会話の意図の処理を開始する前に、ユーザー入力データから bag-of-words を生成する方法が必要です。この方法は以前使用した方法と同じです。

def clean_up_sentence(文):
    # パターンをトークン化する
    sentence_words = nltk.word_tokenize(文)
    # 各単語の語幹
    sentence_words = [stemmer.stem(word.lower()) は sentence_words 内の単語に対して実行されます]
    sentence_wordsを返す

# 単語のバッグ配列を返す: 文中に存在するバッグ内の各単語に対して 0 または 1
def bow(文、単語、show_details=False):
    # パターンをトークン化する
    sentence_words = clean_up_sentence(文)
    #言葉の袋
    バッグ = [0]*len(単語数)  
    sentence_words 内の s の場合:
        enumerate(単語)のi,wの場合:
            w == sの場合: 
                バッグ[i] = 1
                show_detailsの場合:
                    print ("バッグの中に見つかりました: %s" % w)

    戻り値 (np.array(bag))
p = bow("今日はお店は開いていますか?", 単語)
印刷 (p)
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 1 0]

これで、レスポンス ハンドラーの構築を開始できます。

ERROR_THRESHOLD = 0.25
def classify(文):
    # モデルから確率を生成する
    結果 = model.predict([bow(文, 単語)])[0]
    # しきい値以下の予測を除外する
    結果 = [[i,r] i,r が enumerate(results) の場合 r>ERROR_THRESHOLD]
    # 確率の強さで並べ替え
    結果のソート(キー=lambda x: x[1], 逆順=True)
    戻り値リスト = []
    結果のrについて:
        return_list.append((クラス[r[0]], r[1]))
    # 意図と確率のタプルを返す
    戻り値 return_list

def response(文、userID='123'、show_details=False):
    結果 = 分類(文)
    # 分類がある場合は、一致するインテントタグを検索します
    結果:
        # 処理する一致がある限りループします
        結果:
            i をインテント['intents']に代入:
                # 最初の結果に一致するタグを見つける
                i['t​​ag'] == results[0][0]の場合:
                    # インテントからのランダムな応答
                    戻り値 print(random.choice(i['responses']))

            結果.pop(0)

response() に渡される各文は分類されます。私たちの分類器は model.predict() 関数を使用してカテゴリ予測を行いますが、これは非常に高速です。モデルによって返される確率値は、定義した意図と一致しており、潜在的な応答のリストを生成するために使用されます。

1 つ以上の分類結果がしきい値を超える場合、意図に一致するラベルを選択して処理します。カテゴリのリストをスタックとして扱い、最適なものが見つかるかスタックが空になるまで、このスタックから適切な一致を探します。

モデルが最も可能性の高いラベルとその確率を返す例を見てみましょう。

classify('今日はお店は開いていますか?')
[('今日オープン', 0.9264171123504639)]

「今日はお店が開いていますか?」は、このインテント内のどのパターンにも含まれていないことに注意してください: 「パターン: ["今日は開いていますか?"、"今日はいつ開いていますか?"、"今日の営業時間は何時ですか?"]」。ただし、「オープン」と「今日」という用語は、私たちのモデルにとって非常に役立ちます (意図を選択する上で決定的な役割を果たします)。

次に、ユーザーの入力データから結果を生成します。

応答('今日はお店は開いていますか?')
営業時間は毎日午前9時から午後9時までです

さらにいくつかの例:

応答('現金は受け付けますか?')
VISA、Mastercard、AMEXがご利用いただけます

応答('どのようなモペットをレンタルしますか?')
ヤマハ、ピアジオ、ベスパのモペットをレンタルしています

応答('さようなら、また後で')
さようなら!またすぐに戻ってきてください。
[[195158]]

次に、いくつかの基本的なコンテキストを組み合わせて、トレーラーレンタル チャットボットなどのチャットボットを設計してみましょう。

コンテクスト

バイクのレンタルに関する質問に対応し、レンタルについていくつか質問したいと考えていました。ユーザーの問題は非常に簡単に理解でき、コンテキストも非常に明確である必要があります。ユーザーが「今日」と尋ねた場合、レンタル情報のコンテキストは時間枠を入力することになり、コミュニケーションで時間を無駄にしないように、どの自転車であるかも指定できると最適です。

これを実現するには、フレームワークに「状態」という別の概念を追加する必要があります。これには、この新しい概念と元の意図を維持するためのデータ構造が必要です。

状態マシンは保守、復元、コピーなどが非常に簡単に行えるようにする必要があるため、すべてのデータを辞書などのデータ構造に保存することが非常に重要です。

次に、基本的なコンテキストの応答プロセスを示します。

# ユーザーコンテキストを保持するデータ構造を作成する
コンテキスト = {}

ERROR_THRESHOLD = 0.25
def classify(文):
    # モデルから確率を生成する
    結果 = model.predict([bow(文, 単語)])[0]
    # しきい値以下の予測を除外する
    結果 = [[i,r] i,r が enumerate(results) の場合 r>ERROR_THRESHOLD]
    # 確率の強さで並べ替え
    結果のソート(キー=lambda x: x[1], 逆順=True)
    戻り値リスト = []
    結果のrについて:
        return_list.append((クラス[r[0]], r[1]))
    # 意図と確率のタプルを返す
    戻り値 return_list

def response(文、userID='123'、show_details=False):
    結果 = 分類(文)
    # 分類がある場合は、一致するインテントタグを検索します
    結果:
        # 処理する一致がある限りループします
        結果:
            i をインテント['intents']に代入:
                # 最初の結果に一致するタグを見つける
                i['t​​ag'] == results[0][0]の場合:
                    # 必要に応じてこのインテントのコンテキストを設定します
                    i に 'context_set' がある場合:
                        show_details の場合: print ('context:', i['context_set'])
                        コンテキスト[ユーザーID] = i['context_set']

                    # このインテントがコンテキストに依存しており、このユーザーの会話に適用されるかどうかを確認します
                    i または \ に 'context_filter' がない場合
                        (コンテキスト内の userID と i 内の 'context_filter' および i['context_filter'] == context[userID]):
                        show_details: print ('tag:', i['tag']) の場合
                        # インテントからのランダムな応答
                        戻り値 print(random.choice(i['responses']))

            結果.pop(0)

コンテキスト状態は、各ユーザーの状態が含まれる辞書です。各ユーザーには一意の ID (例: セル番号) を使用します。これにより、フレームワークとステート マシンは複数のユーザーの状態を同時に維持できるようになります。

# ユーザーコンテキストを保持するデータ構造を作成する
コンテキスト = {}

次のように、インテント処理プロセスにコンテキスト情報を追加しました。

                i['t​​ag'] == results[0][0]の場合:
                    # 必要に応じてこのインテントのコンテキストを設定します
                    i に 'context_set' がある場合:
                        show_details の場合: print ('context:', i['context_set'])
                        コンテキスト[ユーザーID] = i['context_set']

                    # このインテントがコンテキストに依存しており、このユーザーの会話に適用されるかどうかを確認します
                    i または \ に 'context_filter' がない場合
                        (コンテキスト内の userID と i 内の 'context_filter' および i['context_filter'] == context[userID]):
                        show_details: print ('tag:', i['tag']) の場合
                        # インテントからのランダムな応答
                        戻り値 print(random.choice(i['responses']))

インテントがコンテキスト情報を設定したい場合は、次のようにします。

{“タグ”: “レンタル”,
 「パターン」: [「原付を借りられますか?」「原付を借りたいのですが」…],
 「回答」: [「今日借りる予定ですか、それとも今週後半ですか?」]、
 “context_set”: “レンタル日”
 }

別のインテントをコンテキストに関連付けたい場合は、次のようにします。

{“タグ”: “今日”,
 「パターン」: [「今日」],
 「回答」: [「本日のレンタルについては、1–800-MYMOPED までお電話ください」、…]、
“コンテキストフィルター”: “レンタル日”
 }

このように情報ベースが構築されると、ユーザーがコンテキスト情報なしで単に「今日」と入力した場合、「今日」というユーザーの意図は処理されません。ユーザーが「今日」と入力すると、これは時間応答であり、つまりインテント ラベル「レンタル」をトリガーし、このインテントが処理されます。

応答('モペットをレンタルしたい')
今日レンタルしたいですか、それとも今週後半ですか?

レスポンス('今日')
当日レンタルの場合は1-800-MYMOPEDまでお電話ください

コンテキスト情報も変更されました:

コンテクスト
{'123': 'レンタル日'}

「挨拶」の意図は、新しい会話を開始したいことを示す「こんにちは」と言うのと同じように、コンテキスト情報を明確にするために定義します。プログラム内の情報を確認できるように、「show_details」パラメータも追加しました。

応答("こんにちは!", show_details=True)
コンテクスト: ''
タグ: 挨拶
また会えて嬉しいです

もう一度「today」という単語を入力してみると、面白いことが起こります。

レスポンス('今日')
毎日午前9時から午後9時まで営業しています

分類('今日')
[('今日', 0.5322513580322266), ('今日オープン', 0.2611265480518341)]

まず、文脈情報なしでは「今日」に対する反応は異なります。分類の結果、適切な意図が 2 つ見つかりましたが、「opentoday」が選択されました。したがって、ランダム性は比較的大きく、コンテキスト情報は非常に重要です。

応答("ありがとう、素晴らしい")
お力になれて、嬉しいです!
[[195159]]

今、私たちが考慮する必要があるのは、会話を文脈の中にどのように位置づけるかだけです。

状態処理

そうです、あなたのロボットは他の誰かのものではなく、あなた専用のロボットになります。状態を再構築したくない場合は、モデルとドキュメントを再ロードしてください。ロボット フレームワークを呼び出すたびに、モデル状態をロードする必要があります。

それほど難しくはありません。ステートフルチャットボットフレームワークを独自のプロセスで実行し、RPC(リモートプロシージャコール)またはRMI(リモートメソッド呼び出し)を使用して呼び出すことができます。Pyroを使用することをお勧めします。

ユーザー インターフェイス (クライアント) は通常、HTTP や SMS などのようにステートレスです。

チャットボット クライアントは Pyro 関数を介して呼び出しを行い、状態サービスはそれによって処理されます。素晴らしいと思いませんか?

Twilio SMS ボット クライアントの構築方法に関するステップバイステップ ガイドはこちら、Facebook ボットの構築方法に関するガイドはこちらです。

ローカル変数に状態を保存しない

すべての状態情報は、簡単に永続化、再ロード、またはアトミックにコピーできる辞書などのデータ構造に配置する必要があります。

各ユーザーの会話とコンテキストは、一意である必要があるユーザー ID の下に保存されます。

シナリオ分析を行うために、一部のユーザーの会話情報をコピーします。この情報が一時変数に保存されると、処理が非常に難しくなります。これが最大の考慮事項です。

[[195160]]

これで、チャットボット フレームワーク、コンテキスト情報を記憶できるボットの構築方法、テキストの分析方法を学習しました。将来的には、チャットボットもコンテキストを分析できるようになるでしょう。これは大きなトレンドです。

意図の構築を会話応答のコンテキストに関連付けることで、さまざまな会話環境を作成できます。

今すぐ試してみましょう!

<<:  顔認識はどれくらい強力ですか? AIFRテクノロジーはあなたを数分で「スター」に変えます

>>:  WeiboにおけるSparkベースの大規模機械学習の応用

ブログ    

推薦する

...

ライフル銃で動くロボット犬の発明者が恐怖を巻き起こす:プログラミング制御は恐れる必要はない

[[429985]]先週、米国陸軍協会(AUSA)の会議がワシントンで開催されました。アメリカのロボ...

アマゾンとファーウェイの機械学習面接を経験すると、試験官はこれらの答えを聞きたがっていることが判明

[[245589]]ジョージ・セイフ氏はこれまで、主にデータサイエンスや機械学習関連の職種を対象に、...

インテリジェントロボットを活用してビジネス運営を強化する方法

インテリジェントロボットはビジネスの世界で大きな注目を集めています。スマートロボットの使用には、効率...

Daguan Data: ナレッジグラフと Neo4j の簡単な分析

現在のビッグデータ業界では、アルゴリズムのアップグレード、特に機械学習の導入により、「パターン発見」...

すべてのトップオブジェクト検出アルゴリズムを統合: FAIRオープンソースDetectron

昨日、Facebook AI Research (FAIR) は、業界で最も先進的な物体検出プラット...

AIが世界に登場しようとしています。機械は人間の倫理を破壊するのでしょうか?

AIがますます普及するにつれて、倫理的な問題をどのように解決できるでしょうか? AIという言葉は近...

ジェフ・ディーンの1万語の記事:2020年のGoogleの10大分野におけるAI技術の発展

ジェフ・ディーン氏は数万語に及ぶ長文の記事を公開し、過去1年間のGoogleのさまざまな分野での成果...

Python は R を抜いて、データ サイエンスと機械学習プラットフォームで最も人気のある言語になるのでしょうか?

最近、kdnuggets はデータ サイエンスと機械学習言語の使用に関するアンケート調査を実施しまし...

ハッカーが、さまざまなネットワーク攻撃コードを自動生成できる悪質なAIツールFraudGPTを公開

7月31日、「ハッカーがAIを使って犯罪ツールを作る」という研究者の懸念が徐々に現実のものとなりつつ...

Google:MLの発展を牽引する転移学習とは何でしょうか?丨NeurIPS 2020

機械学習の分野でよく使われる分類学習タスクでは、訓練された分類モデルの精度と高い信頼性を確保するため...

「林季」が中国国際サービス貿易交易会に登場しました! Orange Cloud AIエコシステムが従来の産業の束縛を打ち破る

9月3日午後、「オレンジクラウドテクノロジーイノベーションプラットフォームが産業企業のデジタル変革を...

データが足りない場合はどうなりますか?コンピュータビジョンデータ拡張手法の概要

データが足りない場合はどうすればいいですか?学者たちは、ディープラーニングモデルにおけるデータ不足の...

自動運転の安全性の問題をどう解決するのか?まずは名前を変えてみましょう。

現在、新世代情報技術の急速な発展に伴い、自動運転をはじめとした新興産業がますます台頭しています。世界...