【Python】簡単なコードで人工無能(チャットボット)の実装


投稿日 2019年11月30日 >> 更新日 2023年3月2日

今回は1枚のスクリプトファイルによる簡単なコードで人工無能(チャットボット)の実装をしていきたと思います。

人工無能に関してはこちらを「Wikipedia:人工無脳」をご参照ください。

個人的な回答としては、PCやスマホやその他多くのシステムは人工無能と言えるかと思います。

それらのシステムは機械的に反応するだけですが、何らかのアクションに相応しい言葉を返すコードを加えることで、会話をしているような感覚でシステムを使えるようになると思います。

現在代表的な人工無能と言えば、AppleのSiriやGoogleアシスタントです。

他にも多くのものがありますが、1966年に発明された初期の人工無能であるELIZA(イライザ)を参考に作られたとも言われております。

Elizaアプリも実装してみたのでよろしければこちらもご覧ください。

Pythonのチャットボットフレームワークを使用した実装はこちらです。

第三次AIブームによって自然言語処理や音声認識の技術が向上したことにより(厳密にはビッグデータの利用やハードウェアの性能が向上)、文章や声によるアクションにも柔軟に反応できるようになっています。

これらのシステムにディープラーニングなどの応用技術を組み込むことで人工知能へと進化することができると思っています。

ここでは非常に簡単な実装になりますが、単純な自動化プログラムに加え、人工知能の入り口だと思っているので興味のある方はぜひ試してみてください。

実行環境&使用ライブラリ

実行環境
Windows Subsystem for Linux
Python 3.6.8
pip 9.0.1

単純なチャットボットの作成

準備は特に必要ありませんが、ターミナル上でPythonを実行できる環境であれば大丈夫です。

そして1枚のPythonファイルを作成します。

私であればWebサイト名の1部を取って「zero.py」としました。

まずはプログラム全体がどのようになっていくか俯瞰できるよう先に見せておきます。

極めて単純です。

# zero.py

while True:
    print('----------\n アシスタントの「ゼロ」です。\n 何かお困りですか?\n [yes] or [no]\n-----')
    word = input('\n 入力:')

    if word == 'yes':
        print('----------\n お困りの内容を教えて下さい。\n 無い場合は「no」と入力してください。\n-----')
        word = input('\n 入力:')

        if word == 'no':
            print('----------\n では、良い一日をお過ごしください。\n-----')
            break
        else:
            print('----------\n 「{}」ですね。\n わかりました。\n 改善するよう努力しますね。\n-----'.format(word))
            break

    elif word == 'no':
        print('----------\n では、さようなら\n-----')
        break

whileループから始まり、input関数のユーザーからの入力に対しif文で返すだけの処理となります。

条件分岐のif文を適切に行うことにより、「会話」をしているような返しを実装できます。

whileループ

whileループ(while文)は、ブロック内に書かれたコードを繰り返し処理してくれます。

ある条件に到達したときにループから抜けたり、強制終了させることもできます。

「test.py」の例では、1が格納されているnum変数が5に達した時、ループから抜けるといった処理です。

# test.py

num = 1

while num <= 5:
    print(num)
    num += 1
# 実行

$ python3 test.py

1
2
3
4
5

特に制限をかけずに実行させると無限ループになります。

# test.py

num = 1

while True:
    print(num)
    num += 1
# 実行

# 終了はキーボード「Ctrl」+「C」。
$ python3 test.py

1
2
3
.
.
.
77777

途中でループを抜けたい場合は、何らかの処理の後に「break」と記述します。

# test.py

num = 1

while True:
    print(num)
    num += 1
    print(num)
    break
# 実行

$ python3 test.py

1
2

何らかの処理に、「continue」と記述することでその後の処理をスキップすることができます。

# test.py

num = 1

while True:
    print(num)
    num += 1
    continue
    break
# 実行

# 「break」をスキップし無限ループになります。
# 終了はキーボード「Ctrl」+「C」。

$ python3 test.py

1
2
3
.
.
.
77777

次にユーザーがテキストを入力できるようにする、input関数を実装します。

input関数

input関数は入力を受け付けるためのコードで、HTMLで言うinputタグのような処理です。

inputタグ同様、何らかの処理を加えない限り無限ループのような処理になってしまいます。

whileループと組み合わせて使われるので、先ほどの処理「5以上でループから抜ける」にinput関数を追記してみます。

# test.py

num = 1

while num <= 5:
    print(num)
    num += 1
    input()

実行し「work」と記述すると次の処理へと繰り替えされているのが分かると思います。

# 実行

$ python3 test.py

1
work
2
work
3
work
4
work
5

次に、input関数に与えられた値のタイプを見ていきます。

# test.py

while True:
    input_type = input()
    print(type(input_type))
# 実行

$ python3 test.py

work
<class 'str'>
1
<class 'str'>

input関数は文字列の「str」を返すので、整数として置き換える場合は、「int()」関数に与える必要があります。

# test.py

while True:
    input_type = input()
    print(type(input_type))
    input_num = input()
    input_int = int(input_num)
    print(type(input_int))

1回目のinput関数では「str」を返しますが、2回目では「int()」関数の引数に変数を渡し変換しているので整数のintタイプになります。

もちろん「int()」の引数に文字列を与えてしまうとエラーとなってしまいます。

# 実行

$ python3 test.py

1
<class 'str'>
1
<class 'int'>
work
<class 'str'>
work
Traceback (most recent call last):
  File "test.py", line 24, in <module>
    input_int = int(input_num)
ValueError: invalid literal for int() with base 10: 'work'

次に条件分岐の「if文」を実装しますが、条件に一致するように「str型」か「int型」かに注意して実装させましょう。

条件分岐(if文)

何らかの入力によって処理を分岐させるには、「if文」を使用します。

イメージ的には、フォルダーやディレクトリのように各階層には異なる処理をするファイルが存在します。

それを1枚のファイル内で実装させるのであれば「if文」を使うのが相応しいと思います。

ではinput関数と組み合わせ条件付きの処理を行います。

# test.py

work = input()

if work == 'red':
    print('赤')
elif work == 'blue':
    print('青')
elif work == 'yellow':
    print('黄色')
else:
    print('その他')

work変数に与えられた値をif文の比較演算子で一致した場合にTrueを返します。

一致しない場合は、Falseとなるので上から下に向かって一致する値を探します。

# 実行

$ python3 test.py

red
赤
# 実行

$ python3 test.py

blue
青
# 実行

$ python3 test.py

yellow
黄色
# 実行

$ python3 test.py

black
その他

fi文にもう一つ「exit」を比較する演算子を加えて、「break」処理で終了するプログラムをwhileループを取り入れて実行してみましょう。

# test.py

while True:
    work = input()

    if work == 'red':
        print('赤')
    elif work == 'blue':
        print('青')
    elif work == 'yellow':
        print('黄色')
    elif work == 'exit':
        print('プログラムを終了します。')
        break
    else:
        print('その他')
# 実行

$ python3 test.py

red
赤
blue
青
yellow
黄色
black
その他
exit
プログラムを終了します。

数字を扱った条件分岐を試してみます。

プログラム実行時に読み込む間隔を少し空けたいので、Python標準モジュールの「time」を使ってそれぞれの処理に1秒間隔空けて実行してみます。

# test.py

import time


num = 1

while True:
    num += 1
    time.sleep(1)

    if num <= 5:
        print('一段目')
    elif num <=10:
        print('二段目')
    elif num <= 15:
        print('三段目')
    elif num <= 20:
        print('四段目')
    else:
        print('Falseで終了')
        break
# 実行

$ python3 test.py

一段目
一段目
一段目
一段目
二段目
二段目
二段目
二段目
二段目
三段目
三段目
三段目
三段目
三段目
四段目
四段目
四段目
四段目
四段目
Falseで終了

if文を上手く使うことによって、会話形式のプログラムを実装することができます。

whileループ・print関数・input関数・break or continue・if文などの部品を組み合わせ自分好みの処理を作成してみましょう。

「format」関数は、引数に与えた値を文字列内に組み込む事ができます。

# zero.py

while True:
    print('----------\n アシスタントの「ゼロ」です。\n 何かお困りですか?\n [yes] or [no]\n-----')
    word = input('\n 入力:')

    if word == 'yes':
        print('----------\n お困りの内容を教えて下さい。\n 無い場合は「no」と入力してください。\n-----')
        word = input('\n 入力:')

        if word == 'no':
            print('----------\n では、良い一日をお過ごしください。\n-----')
            break
        else:
            print('----------\n 「{}」ですね。\n わかりました。\n 改善するよう努力しますね。\n-----'.format(word))
            break

    elif word == 'no':
        print('----------\n では、さようなら\n-----')
        break

if文内の比較されるボキャブラリーを増やしたり、何かしらの値が一致された時に他のプログラムを実行したりなど、いくらでも拡張することができます。

実用的な処理を加える

人工無能(チャットボット)を実用的に活用したいと思ったときに、受け答えのできるボットを目指したいと思います。

そのためにはユーザーから与えられた「データ」を覚えておく必要があります。

zero.pyの例で言うと「お困りの内容を教えて下さい。」という相手の困っている情報を聞き出しています。


----------
 お困りの内容を教えて下さい。
 無い場合は「no」と入力してください。
-----

 入力:人工無能とは
----------
 「人工無能とは」ですね。
 わかりました。
 改善するよう努力しますね。
-----

相手が入力してくれた文をformat関数で返すだけのプログラムで終了してしまっているので、入力してもらった内容をテキストファイルに保存されるようなプログラムを加えていきたいと思います。

そのようにすれば、次回同じ内容を聞かれたときに、何かしらの処理を加えて応答することができるようになります。

テキストファイルの書き込み・読み込み

Pythonでテキストファイルを作成するには、「open()」関数を使用します。

open関数の引数に、「テキストファイル名」「書き込みモード」or「読み込みモード」といった記述をしていきます。

2種類の方法があるのでそれぞれ見ていきましょう。

whileループとinput関数を取り入れて実行すると確認しながら行えるので、組み合わせて実行します。

# test.py

while True:
    work = input('入力:')
    """ 書き込みモード """
    file = open('test.txt', 'w')
    file.write(work)
    file.close()
    """ 読み込みモード """
    text = open('test.txt', 'r')
    print(text.read())
    text.close()

値が入力されると、書き込みモード「w」として引数で指定したテキストファイルが新規作成されます。

「write」により入力された値を書き込み「close」でファイルを閉じます。

作成されたファイルを読み込みモード「r」を指定して、「read」を使って文字列として出力しています。

上記の方法では、ファイルを書き込んだり読み込んだりした後に、必ず「close()」で閉じなければなりません。

Pythonを実行しているディレクトリに「test.txt」が作成されているので、input関数に入力しながら確認してみましょう。

もちろんopen関数に渡す引数のモードやオプションは色々あるので、別途で調べてみてください。

次に2つ目の方法です。

2つ目の方法は、「with文」を使った処理です。

with文

with文はある一定の処理を一度に行ってくれます。

先ほど「open()」でファイルを開き「close()」で閉じるといった処理を行いましたが、「close()」を省いて実行することができます。

# test.py

while True:
    work = input('入力:')

    with open('test.txt', 'w') as f:
        f.write(work)

    with open('test.txt', 'r') as f:
        print(f.read())

1つ目の方法で行った処理と全く同じ処理です。

書き込みモード「w」ではテキストをそのまま上書きして保存してしまいますが、追記モードの「a」を与えることで後方へとテキストを追記します。

# test.py

while True:
    work = input('入力:')

    with open('test.txt', 'a') as f:
        f.write(work)

    with open('test.txt', 'r') as f:
        print(f.read())
# 実行

$ python3 test.py

入力:テスト
テスト

入力:テスト
テスト
テスト

入力:テスト
テスト
テスト
テスト

入力:テスト
テスト
テスト
テスト
テスト

入力:

ではこの処理を人工無能の処理内に組み込んでみます。

# zero.py

while True:
    print('----------\n アシスタントの「ゼロ」です。\n 何かお困りですか?\n [yes] or [no]\n-----')
    word = input('\n 入力:')

    if word == 'yes':
        print('----------\n お困りの内容を教えて下さい。\n 無い場合は「no」と入力してください。\n-----')
        word = input('\n 入力:')

        if word == 'no':
            print('----------\n では、良い一日をお過ごしください。\n-----')
            break
        else:
            """ 追記 """
            with open('input_text.txt', 'a') as f:
                f.write(word+'\n')
            print('----------\n 「{}」ですね。\n わかりました。\n 改善するよう努力しますね。\n-----'.format(word))
            break

    elif word == 'no':
        print('----------\n では、さようなら\n-----')
        break

ユーザーからの情報を保存できるようになりました。

例外処理(try & except文)

次に読み込む処理です。

ファイルを読み込むには既にファイルが存在していなければなりません。

例えば上記のコードと同じような流れを作りwith文を追加してみます。

# test.py

while True:
    work = input('入力:')

    if work == 'yes':
        print('2段目')
        work = input('入力:')

        with open('test.txt', 'a') as f:
            f.write(work+'\n')
            print(work)

    elif work == 'data':

        with open('test.txt', 'r') as f:
            print(f.read())

ユーザーが入力しテキストファイルが作られていればprint()により出力することができます。

しかしテキストファイルが作成されていなければ「FileNotFoundError」となりプログラムが終了してしまいます。

エラーを回避し処理を継続させるには、例外処理の「try & except文」を使います。

ここではその一部を紹介しますが、「try」による実行に失敗した時に実行させる処理で、条件分岐に少し似ています。

# test.py

while True:
    work = input('入力:')

    if work == 'yes':
        print('二段目')
        work = input('入力:')

        with open('test.txt', 'a') as f:
            f.write(work+'\n')
            print(work)

    elif work == 'data':

        """ 例外処理 """
        try:
            with open('test.txt', 'r') as f:
                print(f.read())
        except FileNotFoundError:
            print('データはありません')

一度「test.txt」ファイルを削除して実行してみましょう。

# 実行

$ python3 test.py

入力:data
データはありません
入力:yes
二段目
入力:人工知能
人工知能
入力:data
人工知能

入力:

エラーを回避し継続させることができました。

さっそく新しい機能を人工無能に組み込んでみます。

# zero.py

while True:
    print('----------\n アシスタントの「ゼロ」です。\n 何かお困りですか?\n [yes] or [no]\n-----')
    word = input('\n 入力:')

    if word == 'yes':
        print('----------\n お困りの内容を教えて下さい。\n 無い場合は「no」と入力してください。\n-----')
        word = input('\n 入力:')

        if word == 'no':
            print('----------\n では、良い一日をお過ごしください。\n-----')
            break
        else:

            with open('input_text.txt', 'a') as f:
                f.write(word+'\n')
            print('----------\n 「{}」ですね。\n わかりました。\n 改善するよう努力しますね。\n-----'.format(word))
            break

    elif word == 'no':
        print('----------\n では、さようなら\n-----')
        break
    """ 追加 """
    elif word == 'data':

        try:
            with open('input_text.txt', 'r') as f:
                data = f.read()
                print('-----保存データ-----\n'+ data+'\n'+ '-----')
                break
        except FileNotFoundError:
            print('----------\n データはありません。\n ^^^^^^^^^^^^^^^^^^^')
            continue

これでユーザーからの情報を保存し、保存されたテキストファイルの中身を確認することができるようになりました。

このように単純な機能を組み合わせることによって色々な処理を実装できます。

コードが増えて見難くなってきたら、他のファイルから関数として呼び出して実行することもできますし人によってやりかたは千差万別です。

ぜひオリジナルの人工無能(チャットボット)を開発してみてください。

それでは以上となります。

最後までご覧いただきありがとうございました。