前回の振り返り
前回の記事では、日本語用Sentence-LUKEモデルを用いたレコメンドモデルをターミナル上で実行することに成功しました!
もし前回の記事を見ていない方は、先に以下の記事を見ることをおすすめします!
今回の目標
今回では、前回作成したモデルを用いて、実際にWEBアプリに落とし込む所までを目標にしていきます!
最終的には、トップページから、今の気分でおすすめのビールをレコメンドする気分診断のページを作っていきます! ※但し、性格診断は省きます。
ライブラリのインストール
前回と同様に、必要なライブラリをインストールしていきます!
今回は、Flaskというフレームワークを用いるため、以下のコマンドをターミナルで実行してください
conda activate (仮想環境名) #仮想環境の有効化
conda install flask #Flaskのインストール
関数化
まずは、前回作成したmodel.pyとFlask部分となるapp.pyを繋ぐための関数化を行なっていきます!
model.pyを以下のように書き換えてください!
コード
from transformers import MLukeTokenizer, LukeModel
import sentencepiece as spm
import torch
import scipy.spatial
import pandas as pd
class SentenceLukeJapanese:
def __init__(self, model_name_or_path, device=None):
self.tokenizer = MLukeTokenizer.from_pretrained(model_name_or_path)
self.model = LukeModel.from_pretrained(model_name_or_path)
self.model.eval()
if device is None:
device = "cuda" if torch.cuda.is_available() else "cpu"
self.device = torch.device(device)
self.model.to(device)
def _mean_pooling(self, model_output, attention_mask):
token_embeddings = model_output[
0
] # First element of model_output contains all token embeddings
input_mask_expanded = (
attention_mask.unsqueeze(-1).expand(token_embeddings.size()).float()
)
return torch.sum(token_embeddings * input_mask_expanded, 1) / torch.clamp(
input_mask_expanded.sum(1), min=1e-9
)
@torch.no_grad()
def encode(self, sentences, batch_size=8):
all_embeddings = []
iterator = range(0, len(sentences), batch_size)
for batch_idx in iterator:
batch = sentences[batch_idx : batch_idx + batch_size]
encoded_input = self.tokenizer.batch_encode_plus(
batch, padding="longest", truncation=True, return_tensors="pt"
).to(self.device)
model_output = self.model(**encoded_input)
sentence_embeddings = self._mean_pooling(
model_output, encoded_input["attention_mask"]
).to("cpu")
all_embeddings.extend(sentence_embeddings)
return torch.stack(all_embeddings)
# 既存モデルの読み込み
def recommend(query): #関数化
MODEL_NAME = "sonoisa/sentence-luke-japanese-base-lite"
model = SentenceLukeJapanese(MODEL_NAME)
sentences = []
#CSVファイルパスの代入(自分のパスを入れてね!)
csv_file_path = 'beersking_kansei.csv'
# 読み込む列の名前を指定
target_column_name = 'introduction_text'
# CSVファイルをDataFrameとして読み込む
data = pd.read_csv(csv_file_path)
# 指定した列のデータをリストに追加
sentences = data[target_column_name].tolist()
#理想のクラフトビールのイメージを文章で受け取る
sentences.append(query)
# クラフトビールの説明文、受け取った文章をエンコード(ベクトル表現に変換)
sentence_embeddings = model.encode(sentences, batch_size=8)
# 類似度上位5つを出力
closest_n = 1
distances = scipy.spatial.distance.cdist(
[sentence_embeddings[-1]], sentence_embeddings, metric="cosine"
)[0]
results = zip(range(len(distances)), distances)
results = sorted(results, key=lambda x: x[1])
# Print the recommendations from the second column of the CSV file
print("\n\n======================\n\n")
print("Query:", query)
print("\nあなたにおすすめのクラフトビールは:")
#for i, distance in results[1:closest_n+1]: # Exclude the query itself
#print(data.iloc[i]['name'])
index= data[data['introduction_text']==sentences[results[1][0]].strip()].index[0]
return data.iloc[index,1],data.iloc[index,5],data.iloc[index,2]
app.pyの作成
次に、Flaskでアプリを作る際に重要な、app.pyを記述していきます!
VSCode上にapp.pyを作成し、以下のコードを記述してください!
コード
import os
from flask import (
Flask,
request,
render_template)
from model import recommend #model.pyからrecommend関数をインポート
app = Flask(__name__)
@app.route('/', methods=['GET', 'POST']) #トップページのルーティング
def top():
return render_template('top.html')
@app.route('/kibun1', methods=['GET']) #気分入力ページのルーティング
def kibun1():
return render_template('kibun1.html')
@app.route('/kibun2', methods=['GET','POST']) #出力ページのルーティング
def kibun2():
if request.method == "GET":
return render_template('kibun2.html')
elif request.method == "POST":
favs = request.form.getlist("fav") #name属性がfavのcheckboxから複数の値を取得
data_str = ",".join(favs)
name,imageurl,url = recommend(data_str)
return render_template('kibun2.html', name=name,imageurl=imageurl,url=url) #左辺がHTML、右辺がPython側の変数
if __name__ == "__main__":
app.run(debug=True)
Flaskの基本が知りたいという方は、こちらの動画を参考にすることをおすすめします!
HTMLの作成
次に、先ほど記述したルーティングに従って、HTMLを書いていきます!
top.html
<!doctype html>
<html lang="ja">
<head>
<title>アプリのタイトル名
</title>
<meta charset="utf-8"/>
</head>
<body>
<h1>これはTOPページです</h1>
<a href="/kibun1">気分でビールをレコメンド!</a>
</body>
</html>
kibun1.html
<!doctype html>
<html lang="ja">
<head>
<title>気分レコメンド</title>
<meta charset="utf-8" />
</head>
<body>
<a href="/">TOPへ戻る</a>
<h1>気分診断のページです!</h1>
<div class="kibunform">
<form action="/kibun2" method="POST">
<label><input type="checkbox" name="fav" value="スッキリ">スッキリ</label>
<label><input type="checkbox" name="fav" value="コク">コク</label>
<label><input type="checkbox" name="fav" value="甘さ">甘さ</label>
<label><input type="checkbox" name="fav" value="フルーティー">フルーティー</label>
<label><input type="checkbox" name="fav" value="和風">和風</label>
<label><input type="checkbox" name="fav" value="苦味">苦味</label>
<label><input type="checkbox" name="fav" value="低アルコール">低アルコール</label>
<label><input type="checkbox" name="fav" value="高アルコール">高アルコール</label>
<!--nameタグを必ず"fav"にしてください!app.pyの方でname属性がfavのやつ拾ってます!-->
<input type="submit" value="今の気分を送信!">
</form>
</div>
</body>
</html>
kibun2.html
<!doctype html>
<html lang="ja">
<head>
<title>気分レコメンド</title>
<meta charset="utf-8" />
</head>
<body>
<h1>あなたの気分に合うビールは、"{{name}}"です!</h1> <!--{{変数名}}でPython側の変数をHTMLで表示-->
<img src= "{{ imageurl }}" style="margin: 0px; vertical-align: top; width: 60%;">
<br>
<a href="{{url}}">このビールの詳細を見る!</a>
<br>
<a href="/">TOPへ戻る</a>
</body>
</html>
最後に
3つのファイルが書けたらpython app.pyをターミナルで実行してWebアプリを立ち上げてみてください!
きっとお好みのビールが見つかると思います!
そう信じて、私は今日も一日ビールを楽しもうと思います☺️