Slackチャットから、flask+ TF-IDF の応答を出力する。自然言語処理(4)
index:
概要
前回の、自然言語処理の関連となります。
slackチャットからの、入力文章を webhook経由で
AI/機械学習サーバからの応答を出力する例となります。
webhookは、aws EC2 ubuntu16です。
slack app設定は、トリガー単語を検出する。
カンタンな手法で、AI側を起動しています。
・構成の概要は、
Slack入力(質問)
=> Skack appトリガー検出
=> webhook (aws EC2)呼ぶ
=> nginx+ uWSGI 経由で flask 接続
=> 入力文章から、TF-IDFで、学習済みの入力文章
の類似文章を探す
=> 出力された、入力文章のindex番号から
応答文章を、引き当てる
=> Slackへ、結果送信
みたいな、流れですね
参考
https://www.sejuku.net/blog/74469
slack appの、追加方法など
・slack appで、
「Outgoing WebHooks」を追加
(発信Webフック )
・webhook の登録
=>EC2 のエンドポイントを指定。
・トリガー誤の登録( 例は、Bot: にしています。)
処理など
__init__.py
class VectBase: # def __init__(self): from flaskr.include.nlp_predict import NlpPredict self.pred=NlpPredict() #ans=self.pred.answers #print("ans-len=", len(ans)) tokens=self.pred.get_data() #print(tokens ) ret= self.pred.train(tokens ) self.vectorize= self.pred.get_vectorize() print("#end-load-vectorize") # def predict(self, text ): text=self.pred.predict(text ) #print(text ) return text # app = Flask(__name__) app.config['JSON_AS_ASCII'] = False vect=VectBase()
起動時に、学習処理して。
API応答速度は、高速にできるような形にしています。
( 文章の、件数が多い場合は。遅くなるかもしれませんが )
・webhook
views.py
@app.route('/test2', methods=['GET', 'POST']) def test2(): print("test2") # print(len(request.form )) ret="sorry, nothing response." if(len(request.form ) > 0): text=request.form['text'] print(text ) ret=vect.predict(text ) #print(ret ) dic = {"text" : ret } return jsonify(dic)
slackから、入力文を受信。
ML評価処理、結果の出力
flask + nginx + uwsgi, の設定
index:
概要
前回の、flask関連となりますが。
nginx + uwsgi と連携し、flask 活用する例となります。
環境
python 3.5
flask
nginx
uwsgi
インストールなど
nginx:
sudo apt-get install nginx
確認、
sudo service nginx status
停止、起動など
sudo service nginx stop
sudo service nginx start
sudo service nginx restart
設定など
上記の、参考ページ
を参考していますので。ほぼ同様ですが
Nginxの設定
/etc/nginx/conf.d/ 、にconf追加
myapp.conf
# server { listen 80; location / { include uwsgi_params; uwsgi_pass unix:///tmp/uwsgi.sock; } }
=> スペースが含まれる場合、エラーになりましたので。
タブを使用しています。
https://github.com/kuc-arc-f/flask_myapp/blob/master/nginx_conf/myapp.conf
sockファイルの場所など、適宣きめてます。
/etc/nginx/sites-enabled/ に、リンク設定有る場合は、けします。(rm )
チェック、
sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
successful が、でれば。OKです。
起動しときます。
sudo service nginx start
uwsgi
インストール:
pip3 install uwsgi
flask の起動ファイルを、作成
app.py
from flask import Flask app = Flask(__name__) @app.route("/") def hello(): return "Hello World!" if __name__ == "__main__": app.run(host='localhost', debug=True)
uwsgi コマンドで、起動します。
=> ブラウザで、設置IP を開くと、 Hello world でます。
uwsgi --socket /tmp/uwsgi.sock --module app --callable app --chmod-socket=666
=> uwsgi コマンドが、実行できない場合。
pip3 インストールに、パス設定が不足している場合は。追加します。
export PATH=$PATH:~/.local/bin/
・設定ファイルからの起動
uwsgi --ini myapp.ini
=>iniファイルからの、起動ができるようです。
TF-IDF+ janome で、類似文章の抽出。 自然言語処理(3)
index:
処理
・前処理、学習
# encoding: utf-8 from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics.pairwise import cosine_similarity import numpy as np from janome.tokenizer import Tokenizer # def get_token(text): t = Tokenizer() tokens = t.tokenize(text) word = "" for token in tokens: part_of_speech = token.part_of_speech.split(",")[0] if part_of_speech == "名詞": word +=token.surface + " " if part_of_speech == "動詞": word +=token.base_form+ " " if part_of_speech == "形容詞": word +=token.base_form+ " " if part_of_speech == "形容動詞": word +=token.base_form+ " " return word words1="利用人数は何人ですか?" words2="契約期間は、ありますか?" words3="オープンソースですか?" words4="オンライン決済は、可能ですか?" words5="製品価格、値段はいくらですか?" #words= get_token(words1 ) #print(words ) #quit() words =[] words.append(words1 ) words.append(words2 ) words.append(words3 ) words.append(words4 ) words.append(words5 ) #print(words ) tokens=[] for item in words: token=get_token(item) tokens.append(token) # #print(tokens ) docs = np.array(tokens) vectorizer = TfidfVectorizer(use_idf=True, token_pattern=u'(?u)\\b\\w+\\b') print(tokens) #quit() vecs = vectorizer.fit_transform(docs )
・評価
fit_transformで、学習した結果を。
適当な、文章で cos類似度の計算
類似度の高い、文章を抽出
入力配列から、index 番号を出力
類似度の高い、文章の出力
str="利用人数は?" #str="契約期間" #str="価格は?" instr = get_token(str ).strip() print("instr=", instr ) x= vectorizer.transform( [ instr ]) #print( "x=",x) num_sim=cosine_similarity(x , vecs) print(num_sim ) index = np.argmax( num_sim ) print("word=", words[index]) print()
テスト
['利用 人数 何 人 ', '契約 期間 ある ', 'オープン ソース ', 'オンライン 決済 可能 ? ', '製品 価格 値段 いくら '] instr= 利用 人数 [[ 0.70710678 0. 0. 0. 0. ]] word= 利用人数は何人ですか?
=> 正しく、抽出できました。
追加している。
文章の数が少ないですが、ある程度。判定処理は
正しいようです。
データセットに、応答分を追加しておくと
会話の応答出力も、出力できそうですね。
Laravel 5.6 + Vue.js + axios , ajax 通信の追加。
index:
概要:
以前の Laravel 関連となり
Laravel 5.6で、Vue.js + axios の例となります。
環境とか
php7.1
Laravel 5.6
Vue.js
axios
axios など追加
・ vue.js の追加方法は、前回と同じです。
・base2.blade.php
親テンプレに、追加
git から、axios を取得。
https://github.com/axios/axios
フォルダは、下記で。axios.min.js
\axios-master\dist\axios.min.js
public\js
下に配置してます。
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script> <script src="{{ asset('js/axios.min.js') }}?A1"></script>
vue.js の実装
・コントローラ
TasksController.php
画面表示、 JSON出力
// public function test3() { $tasks = Task::orderBy('updated_at', 'desc')->get(); //var_dump($tasks ); return view('tasks/test3')->with('task', new Task()); } // public function api_test3() { $tasks = Task::orderBy('updated_at', 'desc')->get(); return response()->json( $tasks ); }
・view側 、test3.blade.php
v-for で、リスト追加しています。
@extends('layouts.base2') @section('title', 'タスク一覧') @section('content') <div class="panel panel-default"> <div class="panel-heading"> 一覧 </div> <div class="panel-body"> </div> </div> <hr /> <div> <!-- vue --> <h1>Test3</h1> <!-- <h3>vue test22:</h3> --> <div id="hello"> <p>Length: @{{ length }}</p> <ul> <li v-for="item in list"> id: @{{ item.id }}, ,name= @{{ item.name }} </li> </ul> </div> <!-- --> <script> var app = new Vue({ el: '#hello', data: { length: 0, pages: [] , list: [], }, created: function(){ axios .get('./api_test3') .then(response =>{ // success this.length = response.data.length; for(var i = 0; i < response.data.length; i++) { // console.log(response.data[i].id ); this.list.push(response.data[i] ); } }) } }); </script> @endsection
表示
=> 画面の起動時にリストの表示を、追加
関連ページ
rails5 + Vue.js + axios, でajax通信の追加。
index:
概要:
rails5.2 で、Vue.js+ axios の構成で
ajaxa通信 機能を追加してみました。
環境とか
rails 5.2.2
vue.js
axios
構築方法など
・vue.js
\app\views\layouts\application.html.erb
親テンプレに、追加
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
・axios
git から、axios を取得。
https://github.com/axios/axios
フォルダは、下記で。axios.min.js
\axios-master\dist\axios.min.js
app 下の、下記に配置し。
\app\assets\javascripts\axios.min.js
application.js で、読み込んでみました。
Vue.js の実装。
・起動時に、通信して。 v-for とかで
表示しています。
・ db、表等は、事前に作成し。接続設定を
完了させておく事が条件となります。
・コントローラ、 books_controller.rb
web-APIで、json レスポンス出力を追加
# def test end # def api_test # @book = Book.find(1 ) @books = Book.all render json: @books end
・view側
\app\views\books\test.html.erb
<p id="notice"><%= notice %></p> <h1>Test</h1> <div> </div> <hr /> <!-- --> <div id="hello"> <p>Length: {{ length }}</p> <ul> <li v-for="(item, i) in list" :key="item"> id: {{ item.id }} , title={{ item.title}}, body={{ item.body }} </li> </ul> </div> <hr /> <!-- <a href="#" onClick="test1()">[ test ]<a> --> <BR /> <br /> <%= link_to '[ Back ]', books_path %> <!-- --> <!-- --> <script> window.onload = function () { load_proc(); } // function load_proc(){ var app = new Vue({ el: '#hello', data: { length: 0, pages: [] , list: [], }, created: function(){ axios .get('/books/api_test') .then(response =>{ // success this.length = response.data.length; for(var i = 0; i < response.data.length; i++) { // console.log(response.data[i] ); this.list.push(response.data[i] ); } }) } }); } </script>
表示
・登録した分、レコード表示されます。
doc2vec + janome で、類似文章の評価。 自然言語処理(2)
index:
コード
・学習、モデル保存
train.py
モデル生成(Doc2Vec) の、パラメータは
任意で、変更する必要がありそうです。
# words1=tokenize("女ははじめて関西線で四日市の方へ行くのだということを三四郎に話した。") words2=tokenize("例の女はすうと立って三四郎の横を通り越して車室の外へ出て行った。") words3=tokenize("けれども暑い時分だから町はまだ宵の口のようににぎやかだ。") print(1, words1 ) print(2, words2 ) print(3, words3 ) #quit() # training_docs = [] #quit() sent1 = TaggedDocument(words=words1 , tags=[1]) sent2 = TaggedDocument(words=words2 , tags=[2]) sent3 = TaggedDocument(words=words3 , tags=[3]) training_docs.append(sent1) training_docs.append(sent2) training_docs.append(sent3) #quit() model = Doc2Vec(documents=training_docs, dm=1, vector_size=300, window=8, min_count=1, workers=4) #model = Doc2Vec(documents=training_docs, dm=1, # size=100, window=8, min_count=1, workers=4) model.train(training_docs, total_examples=model.corpus_count, epochs=50) model.save("./book.model")
・評価
モデルのロード、評価
# encoding: utf-8 from gensim.models import word2vec import numpy as np model = word2vec.Word2Vec.load("./book.model") # def cos_sim(v1, v2): return np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2)) ret2 =cos_sim(model.docvecs[1], model.docvecs[2]) ret3 =cos_sim(model.docvecs[1], model.docvecs[3]) print("1-2:", ret2 ) print("1-3:", ret3 )
・結果
1 ['女', '関西', '線', '四日市', '方', '行く', 'の', 'こと', '三', '四', '郎', '話す'] 2 ['例', '女', 'すう', '立つ', '三', '四', '郎', '横', '通り越す', '車', '室', '外', '出る', '行く'] 3 ['暑い', '時分', '町', '宵の口', 'よう', 'にぎやか'] 1-2: 0.231663 1-3: 0.0584926
=> 3種類の文、読み込み。
1-2の比較、1-3比較、類似度の出力
1-2が、似ているようです。
word2vec + janome で、類似単語の抽出。 自然言語処理(1)
index:
参考
https://blog.aidemy.net/entry/2018/06/15/174827
学習データ
小説データ、 wget 等でコピーしておきます
http://www.aozora.gr.jp/cards/000148/files/794_ruby_4237.zip
sanshiro.txt
コード
・学習の処理
参考記事のと、ほぼ同じですが。
前処理で、形態素解析
モデル定義、学習。学習済モデルの保存
word2vec.Word2Vec のオプションは。任意で変更する
必用がありそうです。
# Word2Vecライブラリのロード from gensim.models import word2vec # train model = word2vec.Word2Vec(word_list, size=100,min_count=5,window=5,iter=100) model.save("./book.model")
・評価
モデルロード、評価
# encoding: utf-8 from gensim.models import word2vec model = word2vec.Word2Vec.load("./book.model") # posit= u"時代" #print(model.__dict__['wv'][ posit ]) print("posit=", posit) results = model.wv.most_similar(positive=[posit], topn=10 ) for result in results: print(result)
・結果
適当なキー(例: 時代) で、評価
model.wv.most_similar(positive=[posit], topn=10 )
topn は、トップ単語の出力数を指定できるようです。
posit= 時代 ('青年', 0.603624701499939) ('趣', 0.5834314823150635) ('的', 0.5281779766082764) ('世', 0.5107872486114502) ('人格', 0.5079870223999023) ('思想', 0.5060170292854309) ('今日', 0.5013590455055237) ('小さん', 0.4987122118473053) ('人生', 0.49362632632255554) ('我々', 0.4930008053779602)