Lineチャットボットの製作
AIを載せたLineチャットボットを作成しました。
下記IDまたはQRコードから友達追加できます。
@321kkegz
チャットボット概要
今回製作したチャットボットはこのような感じになっています。
おはように対し、おはよう! と返す。
こんにちはに対し、こんにちは! と返す。
これ以外の文に関してはオウム返しをする。
画像を送るとそれがなんであるか返す。
友達追加してくれた人の情報とお礼のコメントを返す。
以上のことができます。
画像に関してはCifer10というデータセットを用いて学習を行っているので10種類の判別ができます。
コード編
今回は以下の流れで製作しています。
1:学習とモデルの保存
2:メインのコード
・学習とモデルの保存について
このようなコードを書きました。
from keras import optimizers
from keras.applications.vgg16 import VGG16
from keras.datasets import cifar10
from keras.layers import Dense, Dropout, Flatten, Input
from keras.models import Model, Sequential
from keras.utils.np_utils import to_categorical
(X_train, y_train), (X_test, y_test) = cifar10.load_data()
X_train = X_train[:100]
X_test = X_test[:100]
y_train = to_categorical(y_train)[:100]
y_test = to_categorical(y_test)[:100]
input_tensor = Input(shape=(32, 32, 3))
vgg16 = VGG16(include_top=False, weights='imagenet', input_tensor=input_tensor)
top_model = Sequential()
top_model.add(Flatten(input_shape=vgg16.output_shape[1:]))
top_model.add(Dense(256, activation="sigmoid"))
top_model.add(Dropout(0.5))
top_model.add(Dense(10, activation="softmax"))
model = Model(inputs=vgg16.input, outputs=top_model(vgg16.output))
for layer in model.layers[:15]:
layer.trainable = False
#model.load_weights("my_model.h5")
model.compile(loss='categorical_crossentropy',
optimizer=optimizers.SGD(lr=1e-4, momentum=0.9),
metrics=['accuracy'])
model.fit(X_train, y_train, batch_size=64, epochs=10)
score = model.evaluate(X_test, y_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])
model.save('./ファイル名.h5', include_optimizer=False)
転移学習を用いてモデルを作成しています。
Cifer10は非常に多くの画像を含むデータセットであるため、学習の時間がかかります。
なので訓練データ、テストデータの数をそれぞれ100とし時間を削減しています。
これで学習済みモデルの用意ができました。
・メインのコード
ここからはメインのコードをみていきます。
ディレクトリ構造は以下のようになっています。
.
├── Procfile
├── main.py
├── my_model.h5
├── requirements.txt
└── runtime.txt
・main.pyについて
import os
import errno
import tempfile
from flask import Flask, request, abort
from linebot import LineBotApi, WebhookHandler
from linebot.exceptions import InvalidSignatureError
from linebot.models import MessageEvent, TextMessage, ImageMessage, TextSendMessage, FollowEvent
from tensorflow.keras.models import Sequential, load_model
from tensorflow.keras.preprocessing import image
import tensorflow.compat.v1 as tf
import numpy as np
import cProfile
app = Flask(__name__)
line_bot_api = LineBotApi('アクセストークン')
handler = WebhookHandler('チャンネルシークレット')
static_tmp_path = os.path.join(os.path.dirname(__file__), 'static', 'tmp')
try:
os.makedirs(static_tmp_path)
except OSError as exc:
if exc.errno == errno.EEXIST and os.path.isdir(static_tmp_path):
pass
else:
raise
@app.route("/callback", methods=['POST'])
def callback():
signature = request.headers['X-Line-Signature']
body = request.get_data(as_text=True)
app.logger.info("Request body: " + body)
try:
handler.handle(body, signature)
except InvalidSignatureError:
abort(400)
return 'OK'
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
text = event.message.text
if text == 'おはよう':
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text='おはよう!'))
elif text == 'こんにちは':
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text='こんにちは!'))
else:
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text=event.message.text))
@handler.add(FollowEvent)
def handle_follow(event):
profile = line_bot_api.get_profile(event.source.user_id)
line_bot_api.push_message("ユーザーID",
TextSendMessage(text="表示名:{}\nユーザID:{}\n画像のURL:{}\nステータスメッセージ:{}".format(profile.display_name, profile.user_id, profile.picture_url, profile.status_message)))
line_bot_api.reply_message(
event.reply_token, TextSendMessage(text='友達追加ありがとうございます'))
class_label = ["飛行機","自動車","鳥","猫","鹿","犬","蛙","馬","船","トラック"]
model = load_model('my_model.h5')
@handler.add(MessageEvent, message=ImageMessage)
def handle_content_message(event):
message_content = line_bot_api.get_message_content(event.message.id)
with tempfile.NamedTemporaryFile(dir=static_tmp_path, prefix="jp" + '-', delete=False) as tf:
for chunk in message_content.iter_content():
tf.write(chunk)
tempfile_path = tf.name
dist_path = tempfile_path + '.' + "jpg"
dist_name = os.path.basename(dist_path)
os.rename(tempfile_path, dist_path)
filepath = os.path.join('static', 'tmp', dist_name)
img = image.load_img(filepath, target_size=(32,32))
img = image.img_to_array(img)
data = np.array([img])
result = model.predict(data)
predicted = result.argmax()
pred_answer = "これは" + class_label[predicted] + "です"
line_bot_api.reply_message(event.reply_token,TextSendMessage(text= pred_answer))
if __name__ == "__main__":
port = int(os.environ.get('PORT', 8000))
app.run(host ='0.0.0.0',port = port)
・Procfile、runtime.txt、requirements.txtについて。
<Procfile>
web: python main.py
<runtime.txt>
python-3.7.7
<requirements.txt>
absl-py==0.9.0
bleach==3.1.5
click==7.1.2
certifi==2020.4.5.2
chardet==3.0.4
Flask==1.1.2
future==0.18.0
gast==0.3.3
grpcio==1.30.0
#gunicorn==19.7.1
h5py==2.10.0
html5lib==1.0.1
itsdangerous==1.1.0
idna==2.9
Jinja2==2.11.2
Keras==2.4.3
Markdown==3.2.2
MarkupSafe==1.1.1
numpy==1.18.1
oauthlib==3.1.0
pillow==7.1.2
protobuf==3.12.2
PyYAML==5.3.1
requests==2.23.0
scipy==1.4.1
six==1.15.0
tensorboard==2.3.0
tensorflow==2.3.0
termcolor==1.1.0
urllib3==1.25.9
Werkzeug==1.0.1
line-bot-sdk==1.17.0
・改善点/コメント
このラインボットでははじめに書いたようなことしかできません。
Line Massaging APIについて学習を進め、もっとたくさんの機能を実装していきたいと思います。
また、このボットはなぜか時間で反応しなくなります。
webhookを検証すると動くようになるのですが、常時動くように改善していければと思います。
この記事が気に入ったらサポートをしてみませんか?