0
0

日本語用Sentence-LUKEモデルを用いたレコメンドモデル作成【Flask編】

Last updated at Posted at 2024-03-08

前回の振り返り

前回の記事では、日本語用Sentence-LUKEモデルを用いたレコメンドモデルをターミナル上で実行することに成功しました!
もし前回の記事を見ていない方は、先に以下の記事を見ることをおすすめします!


今回の目標

今回では、前回作成したモデルを用いて、実際にWEBアプリに落とし込む所までを目標にしていきます!
最終的には、トップページから、今の気分でおすすめのビールをレコメンドする気分診断のページを作っていきます! ※但し、性格診断は省きます。
スクリーンショット 2023-11-18 3.01.03.png
スクリーンショット 2023-11-18 3.01.14.png

ライブラリのインストール

前回と同様に、必要なライブラリをインストールしていきます!
今回は、Flaskというフレームワークを用いるため、以下のコマンドをターミナルで実行してください

ターミナル
conda activate (仮想環境名)     #仮想環境の有効化
ターミナル
conda install flask     #Flaskのインストール

関数化

まずは、前回作成したmodel.pyとFlask部分となるapp.pyを繋ぐための関数化を行なっていきます!
model.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を作成し、以下のコードを記述してください!

コード
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
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
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
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アプリを立ち上げてみてください!
きっとお好みのビールが見つかると思います!
そう信じて、私は今日も一日ビールを楽しもうと思います☺️

スクリーンショット 2023-11-18 4.20.06.png

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0