1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【個人開発】YouTubeコメントの「空気」を読むAIアプリ『EmoReader』を公開しました(FastAPI / Cloud Run / DeBERTa)

Posted at

1. はじめに

未経験からWebエンジニアを目指して学習中の身ですが、ポートフォリオとして「YouTubeのコメントを感情分析するWebアプリ」を開発しました。

本アプリケーションの最大の特徴は、OpenAIなどの外部APIに依存し続けるのではなく、
「AIモデルを独自に蒸留・量子化し、Cloud Run上でサーバーレスに運用している」点です。

フロントエンド開発、バックエンド開発、AIモデルチューニングなどすべて自分1人で行いました。

アプリのURL :
https://emo-reader.jp/

動機 :
「AIを使ったアプリを作りたい」という技術的欲求と、YouTubeを見ていて「この動画、みんなはどう感じたんだろう?(共感・反感)」を一目で知りたいというユーザー視点の欲求からスタートしました。
また動画投稿経験もあり、みんなの感情や反応をざっくりとでも知りたいということを実現するため、開発することにしました。
AIにより感情を分析し、さらにその分析してふるい分けをしたコメントを分析し、AIによるレポート作成機能も実装しました。

2. 作ったもの:『EmoReader』

emo-reader_top.gif

・どんな動画でも、YouTubeのコメントの感情を分析することができます。
(※ただし日本語のコメントしか正しく分析できません!)
emo-reader-zentai.gif

どんなアプリか: YouTubeのURLを貼るだけで、コメントを最大300件取得し、AIが感情を分析・要約・可視化するツールです。

・単なるポジティブ/ネガティブだけでなく、8つの感情(喜び・信頼・恐れなど)に分類。

img_1.png

・「エモちゃん」というキャラクターが、分析結果に合わせて表情を変えてレポートしてくれる。

・それぞれの感情をもとにAIがコメントの分析をし、

①全体的な傾向と雰囲気
②ポジティブ
③ネガティブ
④気になった点
⑤特筆すべき点

と様々なユニークな分析をしてくれます。

img_2.png

・時系列グラフで「動画のどこで感情が動いたか」を可視化。

img_3.png

・すべてのコメントに感情を振り分け、いいねされたコメント順に並べられる
img4.png

3. 使用技術スタック

カテゴリ 技術・ツール 選定理由
Frontend HTML, CSS, JS モダンな構成で作成
Backend Python (FastAPI) 非同期処理が得意でAIライブラリとの親和性が高いため
Infrastructure Google Cloud Run コンテナデプロイが容易でオートスケールするため
Database Supabase (PostgreSQL) 無料かつ高速な扱いが可能なため
AI Model DeBERTa V3 (独自蒸留) 日本語の文脈理解に優れているため
API YouTube Data API v3 コメント取得用

4. 技術的な挑戦

AIモデルの構築

既存のAPIを叩くだけではなく、独自の感情分析モデルを組み込むことにしました。

最初は公開されている学習済みモデルを使うつもりだったのですが、実際にテストしてみるとどうしても分析に時間がかかってしまい、30件のコメントに1分もかかってしまうという問題がありました。

性能(精度)自体は肌感でもかなりいいとは思ったのですが、ONNX化をしたり、バッチ処理や量子化など様々な方法を試しても、やっぱり100件分析するのに1分かかってしまいます。これではどうしても自分が求める基準には達しないので、別の方法を模索することにしました。

そこでふと、以前興味本位でモデルを自作した経験があったことを思い出しました。 https://qiita.com/izaki_shin/items/2b4573ee7fbea5ec8ed6

昔作ったそのモデルを試してみたところ、100件の分析をCPUで行ったにもかかわらず、ものの数秒で完了してしまいました。「これならいける!」とこの方針で進むことを決めたのですが、すぐにまた別の壁にぶつかりました。

データセットの問題です。 以前使った「Wrime」というデータセットは質がかなり高いのですが、研究開発用ということで商用利用ができませんでした。 そこで、データセットを自ら用意し、それを学習させることでオリジナルの感情分析モデルを作ることにしました。

手順としては以下の通りです。

① やり方としてはローカルLLM(Gemma等)に、インターネット上のコメントのような文章を作成してもらう。
② その文章に対し、同じくローカルLLMで8つの感情パラメーターをセットしてもらう。
③ セットしたパラメーターを使い、小規模なモデルをファインチューニングする。

この流れ、いわゆる「AIモデルの蒸留」を行い、独自の感情分析AIモデルを作成しました。
この方式であれば、モデルの単純なコピーではなく、蒸留によって作られた新しいモデルなので、商用利用(広告収入)や改変なども問題なく行えます。

こうして作ったモデルをテストしてみたところ、次のような結果になりました。

【自作モデル】

【処理パフォーマンス】(onnx化 + 量子化)
総データ数: 2000 件
総分析時間: 21.08 秒
1コメント平均: 0.0105 秒
FPS (推定): 94.9 件/秒

【基本精度】
正解率 (Accuracy): 0.6915 (1383/2000)

【分類精度レポート】
              precision    recall  f1-score   support

          喜び       0.87      0.56      0.68       390
         悲しみ       0.70      0.79      0.74       205
          期待       0.66      0.64      0.65       277
          驚き       0.56      0.78      0.65       441
          怒り       0.53      0.67      0.59       194
          恐れ       0.78      0.56      0.65       215
          嫌悪       0.43      0.39      0.41        59
          信頼       0.51      0.41      0.45       219

    f1-score                            0.63      2000

AIによる文章作成なので正確性の判断は難しいですが、Hugging Faceで公開されていた似たような8感情分析モデルと比較テストを行った結果です。

正解率 (Accuracy): 0.6110 (1222/2000) 
f1-score : 0.56

となっていました。

既存モデルと比較してもそこそこの精度が出ており、なにより速度に関しては私のモデルのほうが2〜2.5倍ほど速かったです。

ここからさらに、徹底的な高速化を行いました。
まずはONNX化です。(AIモデルを『どこでも・誰でも・速く』使える共通フォーマットに変換すること)
さらにモデルの量子化も行いました。(モデルのサイズを小さく圧縮して軽くすること)

これにより、最終的には「1コメント平均: 0.0105 秒」という爆速な分析が可能になり、Cloud RunのCPUのみの駆動でも300コメントを5秒以内で分析できるようになりました。

より詳しい技術的な内容については、また別の記事で書ければと思います。

ただ、実際にはDBへの問い合わせ・保存、キャッシュ判定、スキーマ判定など様々な処理が入るため、トータルでは10秒以上どうしてもかかってしまう状態です。ここは今後の最適化で改善していきたいポイントです。

堅牢性を強化

バックエンドの処理、特にキャッシュがない未分析の動画を扱う際の仕組みにもこだわりました。

JobデータベースとSSEを用いたポーリング処理

今回、WebSocketのようなフロントエンドとの双方向通信ではなく、あえてSSE(Server-Sent Events)とポーリング処理を選びました。
理由としては、WebSocketだとどうしてもサーバー側のリソースが余分に必要になるためです。
基本的に「バックエンドの処理がどこまで進んだか」を伝えていく形式のほうが、クライアント側から何度も「終わりましたか?」と問い合わせてくる無駄をなくせますし、接続数が増えたときの負荷も抑えられると考えました。

具体的な処理としては、分析リクエストが来た際、必ずJobデータベースに対象Youtube動画のIDと、UUIDを付けたデータを追加するようにしています。
そして各ステップごとにステータスを更新し、いまどの処理をしているかを記録していきます。

これをすることで、例えば複数のユーザーが同じ動画を分析しようとした際に、無駄な処理を省くことができます。
動画IDを確認して、すでに誰かが分析を開始していれば、後から来たリクエストは「待機」状態にします。
そしてメインの分析処理が完了次第、待機していたすべてのユーザーに一斉に結果を送信します。
こうすることで、YouTube Data APIへのリクエスト数を節約できます。

また、各ステップごとに進捗(ステータス)を保存しているので、もし何らかのトラブルでコンテナが落ちてしまっても、再起動した際にデータベースを確認しに行き、途中で止まっていたジョブを再開させる、といった復旧も可能です。

このJobデータベースは定期的にチェックを行っており、エラーで止まっているデータがあれば再開させたり、何らかの影響で長時間残ってしまった「ゾンビデータ」は強制的に削除したりして、常にデータをクリーンな状態に保つようにしています。

不正アクセスや攻撃を防止する

セキュリティ面では、基本的にユーザーから送られてくるデータは「動画ID」しか許可しない設計にしました。 管理を楽にしつつ、チェックする内容を最小限にするためです。

FastAPIを使って構築しているので、Pydanticのスキーマクラスを作成し、そこでデータを厳密に定義しています。 送られてきたデータがYouTubeのID形式に則っていない場合など、このスキーマ定義を満たさないリクエストはすべて入り口で弾くようにして、不正なデータの流入を防いでいます。

5. バックエンド開発の苦労と工夫

開発を通して痛感したのは、「準備や計画、開発工程はある程度しっかり決めてから始めたほうが良かった」ということです。

開発経験が浅かったこともあり、ローカル環境でテストや実験を行い、それが完了次第、本番環境にアップして軽くテストすればOKだろう、という軽い構想でいました。
しかし実際には、Dockerコンテナ化やCloud Run上にあげてからのエラーがかなり頻発してしまいました。
結局、本番用として想定していたプロジェクトを「テスト環境」という扱いにし、新たに本番環境を一から作成し直すことになりました。

これによって起きた問題が、クラウド側の設定(IAM認証や環境変数など)のやり直しです。
ある程度日が経ってから再度作成することになったため、記憶が曖昧で、なんとか解決したはずの同じようなエラーにまたハマってしまいました。
念のためにメモを残していたおかげで、長時間ハマり続けることは避けられましたが、細かい点はメモしていなかったり、メモ自体が乱雑で探すのに時間がかかったりと、かなり苦労しました。
開発のやり方やログの残し方といった根本的な方針がないと、あとあと面倒なことになるんだなと身をもって学びました。

AIコーディング(Vibe Coding)の落とし穴

今回はAIを使ったコーディング(Vibe Coding)を中心に開発を行っていましたが、こちらも考えなしに進めていたため、途中でリファクタリングをしっかりとしなくてはならない状況になりました。

AIにお願いをして機能を追加していくと、ひとつのファイルにあらゆる機能を詰め込んで作成してしまう癖があるようで、あとで修正しようとした時に「どこに何の機能があるのか」の判断が難しくなってしまったのです。
そのため、開発の初期段階ではありましたが、自分でファイルの構成を考え直し、機能の分担(責任範囲)を振り分ける作業を行いました。これによって、その後の開発はかなりスムーズに進められるようになりました。

AIとの「認識のズレ」と戦う

システム構成として少し複雑になってしまった部分なのですが、私の実装では、エラーが起きた時にジョブを再実行(リトライ)させるための工夫をしていました。 それぞれの工程ごとにif文を加え、「このステータスの時だけはこの関数を実行できる」という構造にし、最初から作成されたジョブなら初期実行、エラーで途中で止まったジョブなら途中から再開する、という仕組みです。

しかし、そういった「あえての構造」であることを知らないAIは、このif文を「余分で無駄な処理」と判断して削除しようとしたり、ロジックを変えてしまったりすることが何度もありました。

なので、AIに修正をお願いした場合でも、必ずテキスト差分のチェックを行い、必要な処理を勝手に消していないか、戻り値が変わっていないかなどを人間が確認しながら進める必要がありました。

AIは非常に便利ですが、やはり最初の全体像は自分の中で理解し、考えながら進めないと後々大変なことになります。シンプルなWebアプリを意識はしましたが、やはり「すべてをAIに丸投げして作成する」というのはまだ難しいように感じます。

AIを使う上で精度が高くなったコツ

開発を通して見つけた、自分なりのAI活用のコツをまとめます。

①詳しく、具体的に伝える
ただ「この処理でエラーが出たらDBに保存して」

と頼むのではなく、

「このファイル『xxx.py』ではDB操作を行っています。バックエンド処理『yyy.py』の関数『zzz』で〜〜という処理を行っているので、ここで例外が起きた場合は "video_ID" や "error" 等の情報をDBに保存してください」

というように、ファイル名や関数の関係性を明確に伝えると精度が上がります。

②説明ファイルを作成して渡す
特にCloud Codeなどではリクエストの制限や節約も重要です。コメントで一気に文章を送るよりも、仕様や前提をまとめた説明ファイルを渡すほうが、指示の理解率が高いように感じました。

③ファイル全体の「置き換えコード」は信用しすぎない
Geminiなどの場合、修正版としてファイル丸ごとのコードを提示してくれることがありますが、それを信用しすぎると、修正箇所以外の必要な処理が消えていたりすることがあります。
面倒でもテキスト差分はしっかり確認したほうが良いです。

④ポイントを絞って修正してもらう
ファイル全体を修正してもらうのではなく、「怪しいと思う関数」だけを渡し、その関数を修正してもらうやり方のほうが、速度も精度も高かったです。
ローカルLLMなどは、読み込ませるテキスト量や生成させる量(トークン数)が多ければ多いほど、処理が難しくなるようです。 なので、修正する部分をピンポイントに絞ることで、AIの性能を高く引き出した状態を維持できます。 そのためにも、リファクタリングをしてファイル構造をシンプルにし、関数を細かく分けておくような「AIに読ませやすい構築」が大事になってくると感じました。

6.今後の展望

・感情分析の制度についてはもっと上げることができると思うのでデータセットをさらに精度を高くするための方法を模索する。

・八つの分析だけでなくモデル自体が非常に高速なので、ポジティブ・ネガティブを判別するモデルを作成し、ダブルチェックのような形で運用することで精度を上げる。

・すべてCPUのみで苦労していますがさらに速度を上げ効率化する方法を模索する。

7. おわりに

ここまで読んでいただきありがとうございました。
未経験からの挑戦でしたが、「企画→モデル構築→Web実装→インフラ構築」を一気通貫で行ったことで、
システム全体のつながりを深く理解できました。 ぜひ使ってみてください!

1
0
1

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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?