StreamlitとFastAPIでwebアプリを作ってみた（バックエンド編）

## はじめに
今回PythonのライブラリであるStreamlitとWebフレームワークであるFastAPIを用いてwebアプリを作ってみたので記事にまとめました。

前回以下の記事でアプリの概要をまとめました。今回はバックエンドの実装をまとめていきたいと思います。

https://qiita.com/kizataka/items/061624fdcb59c6437b84

バックエンドは以下の記事を参考にしながら進めていきました。

https://qiita.com/nanndot/items/03f986059309d386eeae

### 記事の目次
1. アプリの概要(再掲)
1. ディレクトリ構成
1. DBの準備
1. 感情分析モデルの実装
1. バックエンドの実装
1. まとめ


## 1. アプリの概要(再掲)
一言で言うと、誹謗中傷のような批判的なツイートを排除するアプリです。

具体的には、アプリ上で好きな人物名を入力して検索をかけると、Twitterからその人物名に関するツイートを取得してAIが自動でツイート内容を解析し、肯定的なツイートのみを表示させるようにするというものです。

と、理想はこのように考えていたのですが、途中でTwitterAPIの規約変更によりツイート取得ができないことに気づきました。
アプリ自体のテーマを変更するのは面倒なので、アプリの概要はそのままでKaggleにある文章データを利用してそれをツイート取得とみなし、擬似的に動かせるようにしました。

AIについては自然言語処理による感情分析という技術を用います。

### データセットについて
Kaggleにある[Women's E-Commerce Clothing Reviews](https://www.kaggle.com/datasets/nicapotato/womens-ecommerce-clothing-reviews?resource=download)を例として利用しました。
ECサイトでの婦人服の売上データとなっており、商品分類名や商品に関するレビュー文が格納されています。
レビュー文をツイートに置き換えて考え、ファイルをアップロードすることでツイートを取得できたと仮定しています。

### 使い方
![web_app_movie](https://github.com/kizataka/web_app_sentiment_analyzer/assets/112063667/c514762d-908b-4e8a-b31a-8f00877578e2) 

1. ファイル（Kaggleから取得してきた文章データ）をアップロードします。
2. 文章データが含まれるカラムを選択します。
2. 取得したいレビューに関するキーワードを入力します(本来ならここで人物名を入力し、その人物に関するツイートを取得します)。デモでは`shirt`に関するレビューを取得しています。
2. 取得したいデータ数を指定します。
2. 今回は日付指定は関係ないです。（本来ならどれくらいの期間のツイートを取得するのかを指定します。）
2. 表示するレビューをポジティブにするのかネガティブにするのかを指定します。
2. 分析開始ボタンをクリックします。クリックすると感情分析が始まります。
2. `shirt`に関する肯定的なレビューのみが表示されます。（本来なら検索した人物名に関するポジティブなツイートが表示されます。）

## 2. ディレクトリ構成
```
.
├── app.py                       Streamlitでフロントエンド実装
└── sql_app
	├── __init__.py
	├── main.py                  FastAPIのバックエンド実装
	├── schemas.py               Pydanticによる型定義
	└── sentiment_analysis.py    感情分析モデルの設定
```
ここでは`sql_app`フォルダにあるファイルをメインにまとめていきます。

## 3. DBの準備
アプリを作成する際にはまずDBを構築する必要があります。以下のFastAPIのドキュメントを参考にしました。

https://fastapi.tiangolo.com/ja/tutorial/sql-databases/?h=sql

当初はアプリ開発するならDBは作るものだと勝手に思い込んでいて`SQLite`を使ってDBを構築しようと考えていたのですが、よく考えると今回のアプリではアップロードされたデータに対して感情分析を行って結果を表示するというものなのでDB操作が発生しません。よって、DBの準備は特段必要ありません。

DBが必要ないならバックエンドを実装する必要がないのでは？と一瞬思いましたが、以下の理由からバックエンドも実装することにしました。
* 大量のテキストを感情分析すると処理が重くなってしまいそうなので、フロントエンドとバックエンドに処理を分けた方が良さそう
* 今後機能の追加などでDBを導入したり新たにFastAPIのエンドポイントを追加したりしてアプリケーションを拡張させる可能性があることを考えると、バックエンドの役割を明確に分けておくとやりやすいかも

DBが必要な場合は以下のファイルを用意します。

* DBの基本設定(`database.py`)
* DBモデル定義(`models.py`)
* 型定義(`schemas.py`)
* CRUD処理(`crud.py`)

ただ、`schemas.py`はFastAPI側のモデル定義なので今回は`schemas.py`だけ準備しておきます。




### 型定義
FastAPIではAPIのリクエストやレスポンスのデータ構造を定義するために、Pydanticを用いたモデルを使用する必要があります。ここでは、`schemas.py`にPydanticによるモデルを定義します。

Pydanticについては以下の記事がとてもわかりやすかったです。

https://qiita.com/0622okakyo/items/d1dcb896621907f9002b

```schemas.py
from pydantic import BaseModel

class SentimentRequest(BaseModel):
    text: str

class SentimentResponse(BaseModel):
    sentiment: str
    sentiment_score: float
```

## 4. 感情分析モデルの実装
次に、今回のアプリのメインである感情分析の機能を実装していきます。

ここでは、与えられたテキストに対して感情分析を行う関数を設定していこうと思います。

使用するモデルについてはいくつか選択肢があります。自分でデータセットを集めてモデルを作成するか、公開されている学習済みモデルやAPIを使うなどいくつか方法があります。

今回は簡単に精度の高いモデルを利用できてコストも気にする必要のない`transformers`ライブラリを用いて感情分析を行うことにします。

`transformers`に関しては以下の記事を参考にしました。

https://tt-tsukumochi.com/archives/4105

Kaggleのデータセットは英語の文章がほとんどなので、英語での感情分析を実装します。
パイプラインとして`pipeline('sentiment_analysis')`が用意されています。これを使うことで、文章を入力するだけでその文章の感情ラベル（ポジティブかネガティブか）と感情スコア（感情の度合い）を返してくれます。

```sentiment_analysis.py
from transformers import pipeline

nlp = pipeline('sentiment-analysis')

def analyze_sentiment(text):
    result = nlp(text)
    sentiment = result[0]['label']
    score = result[0]['score']

    return {
        'sentiment': sentiment,
        'sentiment_score': score
    }
```


## 4. バックエンドの実装
感情分析モデルの設定が完了したら最後に`main.py`にルーティング処理を実装していきます。

```main.py
from fastapi import FastAPI
from sql_app.sentiment_analysis import analyze_sentiment
from sql_app import models, schemas

app = FastAPI()

@app.get('/')
async def read_root():
    return {'message': 'Welcome to the sentiment analysis app!'}

@app.post('/analyze_sentiment/', response_model=schemas.SentimentResponse)
async def analyze_sentiment_endpoint(request: schemas.SentimentRequest):
    text = request.text
    analysis_result = analyze_sentiment(text)
    return analysis_result
```
`@app.post`のところでは`sentiment_analysis.py`で設定した感情分析の関数を呼び出して取得した文章の感情分析を行うようにしています。

これでバックエンドの実装は完了です。一応FastAPIが起動するか確認しておきます。

以下のコマンドでFastAPIの動作を確認することができます。

```
$ uvicorn main:app --reload
```
`main:app`というのは`main.py`で定義した`app = FastAPI()`を起動するという意味合いです。
これを実行して、開いたページに
`{'message': 'Welcome to the sentiment analysis app!'}`
と表示されていたらFastAPIは正常に動いています。

## 6. まとめ
今回はバックエンドの実装をメインにまとめました。DBの設計がなかったので思ったよりも簡単に実装することができました。ただCRUD処理などがあった方がもっとアプリっぽくなると思うので今後機能を追加していきたいと思います。

次回はフロントエンドに関してまとめようと思います。

**次回の記事はこちら**

https://qiita.com/kizataka/items/ca32ed76e9472bef66ad