著者はCOVID-19救済基金を選択し、 Write forDOnationsプログラムの一環として寄付を受け取りました。

序章

多くの機能がある場合、ソフトウェアとの対話は困難な作業になる可能性があります。 場合によっては、同様のアクションを実行するには、アクションを実行するたびにメニューをナビゲートしたり、フォームに入力したりするなど、手順を繰り返す必要があります。 チャットボットは、ソフトウェアシステムのユーザーが長いプロセスを経ることなく情報にアクセスしたり、アクションを実行したりするのに役立つ仮想アシスタントです。 これらのアシスタントの多くは会話型であり、システムと対話するためのより自然な方法を提供します。

会話型チャットボットを作成するには、Dialogflowなどのプラットフォームを使用してチャットボットを高レベルで設計できます。 または、 spaCy のようなライブラリを使用して自分でビルドすることもできます。これは、高速で堅牢なPythonベースの自然言語処理(NLP)ライブラリです。 spaCyは、ステートメント内の単語が属する品詞の判別、2つのステートメントの意味の類似性の検出などの便利な機能を提供します。

このチュートリアルでは、ユーザーがソフトウェアシステムとの対話を簡素化するのに役立つだけでなく、自然言語(このチュートリアルではアメリカ英語)でユーザーと通信するのに十分なインテリジェントなチャットボットを作成します。 チャットボットはOpenWeatherAPI を使用して、世界のどの都市の現在の天気もユーザーに通知しますが、チャットボットを実装して別のAPIのユースケースを処理することもできます。

前提条件

始める前に、次のものが必要になります。

このチュートリアルは、既に Python に精通していることを前提としています。Pythonの知識を深めたい場合は、 Python3シリーズのコーディング方法を確認してください。 このチュートリアルでは、自然言語処理の知識は必要ありません。

ステップ1—環境を設定する

このステップでは、チャットボットがユーザーの文章を理解するのに役立つspaCyライブラリをインストールします。

前提条件に従ってPythonを設定すると、仮想環境ができあがります。 その環境を活性化しましょう。

環境を設定したディレクトリにいることを確認してから、次のコマンドを実行します。

  1. source my_env/bin/activate

次に、spaCyをインストールします。

  1. pip install -U spacy

最後に、言語モデルをダウンロードします。 spaCyの言語モデルは、事前にトレーニングされたNLPモデルであり、ステートメントを処理して意味を抽出するために使用できます。 英語モデルを使用するので、それをダウンロードします。

次のコマンドを実行します。

  1. python -m spacy download en_core_web_md

次のようなエラーが発生した場合:

Output
ERROR: Failed building wheel for en-core-web-md

wheelをインストールする必要があります。

  1. pip install -U wheel

次に、英語モデルを再度ダウンロードします。

spaCyが正しくインストールされていることを確認するには、Pythonインタープリターを開きます。

  1. python

次に、spaCyをインポートし、英語モデルをロードします。

>>> import spacy
>>> nlp = spacy.load("en_core_web_md")

これらの2つのステートメントがエラーなしで実行される場合は、spaCyがインストールされています。

次に、Pythonインタープリターを閉じます。

>>> exit()

これで、チャットボットでの作業を開始するために必要なすべてのものが揃いました。 次のセクションでは、都市の現在の天気についてOpenWeatherAPIにクエリを実行するスクリプトを作成します。

ステップ2—都市気象プログラムの作成

このセクションでは、ユーザーから都市名を受け取り、OpenWeather APIにその都市の現在の天気を照会し、応答を表示するスクリプトを作成します。

まず、お好みのエディターでweather_bot.pyというPythonファイルを作成して開きます。

  1. nano weather_bot.py

次に、関数を作成して、OpenWeatherAPIから都市の現在の天気を取得します。 この関数は、都市名をパラメーターとして受け取り、都市の天気の説明を返します。

次のコードをweather_bot.pyファイルに追加します。

Weather_bot.py
import requests

api_key = "your_api_key"

def get_weather(city_name):
	api_url = "http://api.openweathermap.org/data/2.5/weather?q={}&appid={}".format(city_name, api_key)

	response = requests.get(api_url)
	response_dict = response.json()

    weather = response_dict["weather"][0]["description"]

	if response.status_code == 200:
		return weather
	else:
		print('[!] HTTP {0} calling [{1}]'.format(response.status_code, api_url))
		return None

まず、requestsライブラリをインポートして、HTTPリクエストを操作および作成できるようにします。 必ずyour_api_keyを独自のAPIキーに置き換えてください。 次の行は、指定された都市の天気を取得するための関数get_weather()の定義を開始します。

この関数では、OpenWeatherAPIのURLを作成します。 このURLは、都市の天気情報(気温、天気の説明、湿度など)を返し、結果をJSON形式で提供します。 その後、APIエンドポイントに対してGETリクエストを行い、結果をresponse変数に格納してから、応答をPython dictionaryに変換してアクセスしやすくします。

次の行では、天気の説明だけをweather変数に抽出し、API応答のステータスコードが200であることを確認します(つまり、リクエストに問題はありませんでした)。 最後に、天気の説明を返します。

リクエストに問題がある場合は、ステータスコードがコンソールに出力され、Noneが返されます。

スクリプトをテストするには、選択した都市(たとえば、ロンドン)でget_weather()関数を呼び出し、結果を出力します。 関数の後に強調表示されたコードを追加します。

〜/ Weather_bot.py
import requests

def get_weather(city_name):

  ...

  return weather

weather = get_weather("London")
print(weather)

スクリプトを保存して実行します。

  1. python weather_bot.py

次のような結果が表示されます。

Output
scattered clouds

これが正常に完了すると、スクリプトから最後の2行を削除できるようになります。

それを開く:

  1. nano weather_bot.py

次に、ファイルの最後にある強調表示された2行を削除します。

〜/ Weather_bot.py
import requests

def get_weather(city_name):

  ...

  return weather

weather = get_weather("London")
print(weather)

ファイルを保存して閉じます。

これで、特定の都市の天気の説明を返す関数ができました。

次のステップでは、ユーザーが都市の現在の天気を取得したいかどうかを判断できるチャットボットを作成します。取得したい場合、チャットボットはget_weather()関数を使用して適切に応答します。

ステップ3—チャットボットを作成する

前の2つの手順では、spaCyをインストールし、特定の都市の天気を取得するための関数を作成しました。 次に、weather_bot.pyスクリプトを使用して、自然言語でユーザーと対話するチャットボットを作成します。

ユーザーのステートメントを都市の天気のチェックを表すステートメントと比較するchatbot()関数を記述します。 この比較を行うには、spaCy 類似性()メソッドを使用します。 このメソッドは、2つのステートメントの意味的類似性、つまり、それらの意味がどの程度類似しているかを計算します。 これは、ユーザーが天気をチェックしようとしているかどうかを判断するのに役立ちます。

まず、スクリプトを開きます。

  1. nano weather_bot.py

次に、spaCyをインポートし、英語モデルをロードします。

〜/ Weather_bot.py
import spacy
import requests

nlp = spacy.load("en_core_web_md")

. . .

ファイル内のget_weather()関数の後に、ユーザーのステートメントを受け入れて応答を返すチャットボットを表すchatbot()関数を作成します。

定義に従って、強調表示されたコードを追加して、比較する2つのステートメントのトークンを作成します。 トークンは、単語や句読点など、ステートメントのさまざまな意味のあるセグメントです。 これは、spaCyが意味的類似性を計算できるようにするために必要です。

〜/ Weather_bot.py
import spacy

. . .

def chatbot(statement):
  weather = nlp("Current weather in a city")
  statement = nlp(statement)

ここで、weatherおよびstatement変数には、対応する各文字列をnlp()関数に渡した結果として、spaCyトークンが含まれています。

ファイルを保存して閉じます。

次に、spaCysimilarity()メソッドをchatbot()関数に導入します。 similarity()メソッドは、2つのステートメントの意味的類似性を01の間の値として計算します。ここで、数値が大きいほど類似性が高くなります。 ユーザーが天気を確認したいという確信を持てるようにするには、類似性に必要な最小値を指定する必要があります。

たとえば、ステートメント2と3の類似性を次のステートメント1とチェックすると、次のようになります。

  1. 都市の現在の天気
  2. ロンドンの天気は? (類似度= 0.86)
  3. ピーナッツバターとゼリー(類似度= 0.31)

これを自分で試すには、Pythonインタープリターを開きます。

  1. python

次に、spaCyをインポートし、英語モデルをロードします。

>>> import spacy
>>> nlp = spacy.load("en_core_web_md")

次に、ステートメント1と2からトークンを作成しましょう。

>>> statement1 = nlp("Current weather in a city")
>>> statement2 = nlp("What is the weather in London?")

最後に、2つのステートメントの意味的類似性を取得しましょう。

>>> print(statement1.similarity(statement2))

次のような結果が表示されます。

Output
0.8557684354027663

低い最小値(たとえば、0.1)を設定すると、チャットボットはステートメント1と同様にステートメント(ステートメント3など)を使用してユーザーを誤って解釈しますが、これは正しくありません。 最小値を高く設定しすぎると(0.9など)、ステートメント2などのステートメント1に実際に類似している一部のステートメントが除外されます。

このチュートリアルでは0.75を任意に選択しますが、プロジェクトで作業するときにさまざまな値をテストすることをお勧めします。

この値をスクリプトに追加してみましょう。 まず、ファイルを開きます。

  1. nano weather_bot.py

次に、次の強調表示されたコードを追加して、最小値を導入します。

〜/ Weather_bot.py
import spacy

. . .

def chatbot(statement):
  weather = nlp("Current weather in a city")
  statement = nlp(statement)
  min_similarity = 0.75

次に、ユーザーのステートメントと天気に関するステートメントとの類似性が、指定した最小類似性値以上であるかどうかを確認します。 これを確認するには、次の強調表示されたifステートメントを追加します。

〜/ Weather_bot.py
import spacy

. . .

def chatbot(statement):
  weather = nlp("Current weather in a city")
  statement = nlp(statement)
  min_similarity = 0.75

  if weather.similarity(statement) >= min_similarity:
    pass

最後のステップは、ユーザーのステートメントから都市を抽出して、それをget_weather()関数に渡して、API呼び出しから天気を取得できるようにすることです。 これを実装するには、次の強調表示された forloopを追加します。

〜/ Weather_bot.py
import spacy

...

def chatbot(statement):
  weather = nlp("Current weather in a city")
  statement = nlp(statement)
  min_similarity = 0.75

  if weather.similarity(statement) >= min_similarity:
    for ent in statement.ents:
      if ent.label_ == "GPE": # GeoPolitical Entity
        city = ent.text
        break

これを行うには、spaCyの名前付きエンティティ認識機能を使用しています。 名前付きエンティティは、人、この場合は都市などの名前を持つ実世界の名詞です。 ユーザーのステートメントから都市の名前を抽出したいとします。

都市名を抽出するには、ユーザーのステートメントですべての名前付きエンティティを取得し、そのうちのどれが地政学的エンティティ(国、州、都市)であるかを確認します。 これを行うには、spaCyがentsプロパティのステートメントから抽出したすべてのエンティティをループし、エンティティラベル(またはクラス)が地政学的エンティティを表す「GPE」であるかどうかを確認します。 そうである場合は、エンティティの名前(そのテキスト)をcityという変数に保存します。

また、elseブロックを追加して、都市が入力されていないケースをキャッチする必要があります。

〜/ Weather_bot.py
import spacy

...

def chatbot(statement):
  weather = nlp("Current weather in a city")
  statement = nlp(statement)
  min_similarity = 0.75

  if weather.similarity(statement) >= min_similarity:
    for ent in statement.ents:
      if ent.label_ == "GPE": # GeoPolitical Entity
        city = ent.text
        break
      else:
        return "You need to tell me a city to check."

これで都市ができたので、get_weather()関数を呼び出すことができます。

〜/ Weather_bot.py
import spacy

...

def chatbot(statement):
  weather = nlp("Current weather in a city")
  statement = nlp(statement)
  min_similarity = 0.75

  if weather.similarity(statement) >= min_similarity:
    for ent in statement.ents:
      if ent.label_ == "GPE": # GeoPolitical Entity
        city = ent.text
        break
      else:
        return "You need to tell me a city to check."

    city_weather = get_weather(city)
    if city_weather is not None:
      return "In " + city + ", the current weather is: " + city_weather
    else:
      return "Something went wrong."
  else:
    return "Sorry I don't understand that. Please rephrase your statement."

OpenWeather APIによってエラーが返された場合は、エラーコードを端末に出力し、get_weather()関数がNoneを返すことを思い出してください。 このコードでは、最初にget_weather()関数がNoneを返すかどうかを確認します。 そうでない場合は、都市の天気を返しますが、そうでない場合は、問題が発生したことを示す文字列を返します。 最後のelseブロックは、ユーザーのステートメントの類似性の値がしきい値に達しない場合を処理するためのものです。 このような場合、ユーザーにステートメントを言い換えるように依頼します。

これらすべてを完了すると、都市の天気をユーザーに会話で伝えることができるチャットボットができました。 このボットとルールベースのチャットボットの違いは、ユーザーが毎回同じステートメントを入力する必要がないことです。 代わりに、さまざまな方法でリクエストを表現したり、タイプミスをしたりすることもできますが、spaCyのNLP機能により、チャットボットはリクエストを理解できます。

ボットをテストしてみましょう。 chatbot()関数を呼び出して、都市の天気を尋ねるステートメントを渡します。次に例を示します。

〜/ Weather_bot.py
import spacy

. . .

def chatbot(statement):

. . .

response = chatbot("Is it going to rain in Rome today?")
print(response)

ファイルを保存して閉じてから、ターミナルでスクリプトを実行します。

  1. python3 weather_bot.py

次のような出力が表示されます。

Output
In Rome, the current weather is: clear sky

これで、動的なユーザーリクエストに応答できるインテリジェントなチャットボットが正常に作成されました。 さらに多くの例を試して、ボットの全機能を見つけることができます。 これを行うには、OpenWeatherおよびその他のソースから他のAPIエンドポイントを取得できます。 チャットボットを拡張するもう1つの方法は、より多くのユーザーリクエストに応答できるようにすることです。 このために、ユーザーのステートメントを複数のオプションと比較して、意味的類似性が最も高いものを見つけることができます。

結論

ユーザーがさまざまな方法でステートメントを表現している場合でも、ユーザーのステートメントに応答するのに十分インテリジェントなチャットボットを作成しました。 チャットボットはOpenWeatherAPIを使用して、ユーザーが指定した都市の現在の天気を取得します。

チャットボットをさらに改善するには、次のことができます。

このチュートリアルの最終的なコードは、このDigitalOceanリポジトリにあります。