さくっと論文検索アプリを作ってみた

最近アプリ開発（主に機械学習を活用した）にハマっているので開発したアプリケーションの動機や開発内容等をブログにまとめていこうと思います！これからアウトプットをつくろうとしている方の参考になったら嬉しいです。

今回は言語モデルを活用した類似論文検索アプリケーション開発について書いていきます。以下にコードやアプリのURLを記載しておくので、ぜひフォローお願いします！

github：https://github.com/nomnomnonono/Paper-Search
hugging face（アプリ公開先）：https://huggingface.co/spaces/nomnomnonono/fairness-paper-search

## 背景
研究を行っていくうえで関連研究のサーベイはとても重要です。しかし，特に機械学習分野では一日に数多くの論文が提出されており、そのすべてに目を通すことは不可能に近い状況になっています。主要国際会議に絞れば人出での収集・調査も可能ですが、arxiv等もチェックしたい場合には機械的に論文を抽出することが必要になってきます。

## 目的
論文投稿サイト（今回はarxiv）から自身が指定した論文（タイトル，アブストラクト、キーワード）と類似した論文を検索するアプリケーションの開発。

## 開発内容
上記目的を達成するために行った開発を4つに分割して説明します。
### 1. 論文取得
arXiv APIのPythonラッパーである```arxiv```を用いて論文情報の取得を行なった。
具体的な使用方法は[公式ドキュメント](https://pypi.org/project/arxiv/)を参考にしてほしいが、論文カテゴリやタイトル、投稿日時を指定したクエリを作成することで、自由に論文情報の取得ができます。

下記コードでクエリで指定した論文情報を取得し、

```
search = arxiv.Search(
    query="",
    max_results=100,  # 最大取得件数
    sort_by=arxiv.SortCriterion.SubmittedDate,  # 投稿日時でソート
)
```
次のような形で順番に情報を取り出します。
```
for result in search.results():
    (何らかの処理)
    ...
```
今回は```タイトル```、```アブストラクト```、```著者```、```論文URL```、```投稿年・月```の情報を取得しました。
```
title = result.title.replace("'", "").replace('"', "")
abstract = result.summary.replace("'", "").replace('"', "")
author = str(result.authors[0])
url = str(result.links[0])
year, month, _ = str(result.published).split(" ")[0].split("-")
```

### 2. 埋め込みベクトル作成
取得した論文の```タイトル```と```アブストラクト```を論文検索に使用できるように埋め込みベクトルに変換します。
埋め込みモデルには[Sentence BERT](https://arxiv.org/abs/1908.10084)を使いました。自然言語処理モデルであるBERTの一種であり、事前学習済みのBERTに改良を施すことでより高い精度の文章ベクトル生成が可能にした手法らしいです。[【入門】Sentence BERT](https://zenn.dev/en2enzo2/articles/a574b52bb8d116)
Sentence BERTは```sentence-transformers```というフレームワークをインストールすることで簡単に使用することができます！便利すぎる...
また[学習済みモデル](https://www.sbert.net/docs/pretrained_models.html)も公開されており、自身のアプリケーションや実行環境に適したモデルを選択することができます。今回はHugging Face Spaceの無料環境にデプロイするため、モデルサイズが比較的小さいながらも埋め込み精度の高い```all-MiniLM-L6-v2```を用いることにしました。

```
from sentence_transformers import SentenceTransformer

model = SentenceTransformer("all-MiniLM-L6-v2")
title_embed = model.encode(["first title", "second title", "third title"])
```
モデルを学習済みモデルを指定して定義し、複数の文章を含んだリストをモデルに入力することで複数の埋め込みベクトルを取得することができます（cpuでもかなり高速に動作しました）。

### 3. ベクトル検索
これで```タイトル```と```アブストラクト```をベクトル化することができたため、入力された文章との類似度を計算し、類似論文を出力することを考えます。
類似度をどのように定義するか、計算するかが問題となるのですが、今回は一般的な指標であるコサイン類似度を用います。ベクトル$x,y$のコサイン類似度$cos(x,y)$は以下の式で計算できます。
$$cos(x,y) = \cfrac{\langle x, y\rangle}{||x||~||y|||}$$
計算が面倒なように思えるのですが、全てのベクトルのノルムを1に標準化しておくことでベクトルの内積として求めることができます。
具体的にコードで示すと、以下のようになります。
```
# title_embed: n×v
# input_embed: 1×v
prob = np.dot(title_embed, input_embed)  # ベクトル間の類似度を計算
rank = np.argsort(prob)[::-1][0 : topK]  # ベクトルを類似度が大きい順に並び替えて、上位topK個分取り出す
```
```arxiv```で取得した各論文タイトルと入力ベクトルの類似度を計算し、類似度が大きい順に並び替えることで類似論文検索を行っています。この結果をもとに```pandas```の```DataFrame```や```MySQL```等のDBから論文情報を引っ張ってくれば機能としては完成です！

### 4. Gradioでアプリケーション化
機能としては完成したのでこれをアプリケーション化します。今回は簡単にWebアプリが作成できる```Gradio```というフレームワークを用いました。デモアプリを作成するのにはもってこいで、短時間でさまざまなアプリケーションを作りたい人やWeb開発に詳しくない人にはおすすめです！（Gradioの詳しい解説は他の資料に任せます）
またGradioで作成したアプリはHugging Face Spacesに簡単に（無料で）デプロイできるのも嬉しいポイントです。

Hugging Faceに公開している実際のアプリ画面はこんな感じです。
![スクリーンショット 2023-05-03 19.47.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1201545/b50969a5-3431-8d56-79e4-658bfbf63420.png)

### まとめ
ここまで読んでいただきありがとうございます！
初めてのアプリ開発の記事となっているため説明が曖昧だったり不十分かと思いますので、お気軽にコメントしていただけたらと思います。
また記事にしていないアプリも数多くあり、これからもどんどん投稿していこうと思っているのでQiitaやGitHubのフォローもお願いします:bow: