科研費小区分のキーワードをBERTでベクトル化して申請する小区分をレコメンドする

# はじめに
[この記事](https://qiita.com/kageazusa/items/a300fc0f0a575ebfee7f)の続きです。
前回は[科研費小区分](https://www.jsps.go.jp/j-grantsinaid/03_keikaku/data/r05/syokubun.pdf)のキーワードをBERTでベクトル化し、次元削減して可視化しました。
今回は、任意のキーワードを入力し、そのキーワードに応じて申請する小区分をレコメンドしてみます。
ベクトル化までの手順は前回と同じです。

# 環境
Google Colaboratory

# コード
## 準備：前回と共通
transformersからBERT東北大モデルを利用します。
```
!pip install torch transformers fugashi ipadic
```
```Python:Python
import pandas as pd
import numpy as np
import torch
from transformers import AutoModel, AutoTokenizer

# Google Driveをマウント
from google.colab import drive
drive.mount('/content/drive')
cd '/content/drive/My Drive/syokubun/'

# データ読み込み
# https://qiita.com/kageazusa/items/a300fc0f0a575ebfee7f の前半参照
df = pd.read_csv("df.csv")
df_new = pd.read_csv("df_new.csv")

# BERTの準備：静的単語ベクトル
# https://zenn.dev/schnell/articles/4acc48c49eb8eb
tokenizer = AutoTokenizer.from_pretrained("cl-tohoku/bert-base-japanese")
model = AutoModel.from_pretrained('cl-tohoku/bert-base-japanese')
token_embeds = model.get_input_embeddings().weight.clone()
vocab = tokenizer.get_vocab()
vectors = {}

for idx in vocab.values():
    vectors[idx] = token_embeds[idx].detach().numpy().copy()

# 準備したデータをベクトル化
vec_list = [] # データ格納用の空のリスト

for i in range(len(df_new)):
  words = df_new.iloc[i, 1]
  tokens = tokenizer.tokenize(words)
  vec = [vectors[tokenizer.vocab[token]] for token in tokens]
  vec_list.append(sum(vec)) #複数トークンに分けられるものは単純に合計する
```
## 任意の単語を入力し、ベクトル化して既存単語との距離を求める
```Python:Python
#入力単語から近いものを探す
input = "動物生理学" #ここを適宜変更

#入力単語を処理
tokens = tokenizer.tokenize(input)
vec = [vectors[tokenizer.vocab[token]] for token in tokens]
vector = sum(vec) #これが入力単語のベクトル

# 各点との距離
distances = np.linalg.norm(vec_list - vector, axis=1)
df_new["distance"] = distances
```
```Python:Python
# 距離の近いキーワード上位10項目を抽出
df_new.sort_values(by="distance", ascending=True).head(10).reset_index(drop=True)
```
```Python:Python
# 小区分別にdistanceを平均し、小さいもの順に並べる
df_result = pd.DataFrame(df_new.groupby("syokubun")["distance"].mean())

# 可読性のため、元のデータフレームと結合
df_result = pd.merge(df, df_result, on="syokubun")
df_result.sort_values(by="distance", ascending=True).head(10).reset_index(drop=True)
```

# 結果
`input = "動物生理学"` の部分を変えた結果です。

## 動物生理学
申請するなら「44050〔動物生理化学、生理学および行動学関連〕」と思われます。
### キーワード
![スクリーンショット 2022-08-12 18.04.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/820036/a1eb4ef3-1b1b-d4ce-c739-912b276bebb0.png)
「動物」と「生理学」関連のキーワードが出ています。

### 小区分
![スクリーンショット 2022-08-12 18.04.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/820036/683a1b29-50ca-87a8-287b-159ad1085744.png)
最上位は「生理学」というキーワードが多い「48020〔生理学関連〕」ですが、「44050〔動物生理化学、生理学および行動学関連〕」も4番目に出ています。

## 生物物理学
申請するとしたら「13040〔生物物理、化学物理およびソフトマターの物理関連〕」「43040〔生物物理学関連〕」などが考えられます。
### キーワード
![スクリーンショット 2022-08-12 18.07.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/820036/0911d1de-68f8-48b5-578d-4986a4a8156f.png)
そのものずばりの「生物物理」が最上位に出ています。

### 小区分
![スクリーンショット 2022-08-12 18.08.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/820036/c6690613-f2e3-e6f4-da18-99b53158414c.png)
全然違いそうなものも上位に出ていますが、「生物物理」というキーワードを含む「47020〔薬系分析および物理化学関連〕」が7位に、「43040〔生物物理学関連〕」が8位に来ています。

## 鞭毛
どの小区分に申請するかは使う生物や手法によります。
### キーワード
![スクリーンショット 2022-08-12 19.48.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/820036/f43424b6-eb5d-5c30-92a8-fdab26bab291.png)
「寄生虫」と「細菌」は実際に近いと思われます（それぞれ鞭毛を持っているものがいます）。

### 小区分
![スクリーンショット 2022-08-12 19.48.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/820036/0ff780c0-0755-ff85-06f3-6cbe8767d35a.png)
言葉だけから判断した時に、「49070〔免疫学関連〕」「43020〔構造生物化学関連〕」は大きく外してはいないような気がします。

## 繊毛
これもどの小区分に申請するかは使う生物や手法によります。鞭毛とほとんど同じ意味で使われる場合がある言葉です。

### キーワード
![スクリーンショット 2022-08-12 19.49.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/820036/0c6ff50a-c8a5-54b4-19e3-07e80d731996.png)
なぜか「ジェンダー」と「フェミニズム」が含まれます。精子の鞭毛、卵管の繊毛が関わるので、「受精」は外してはいないと思います（精子の鞭毛を繊毛と呼ぶことはあまりない気がしますが）。
「繊毛」と前項で挙げた「鞭毛」はBERT東北大モデルでは必ずしも近くないことがわかります（ちなみに「繊毛」と「鞭毛」の距離は2.1764073でした。「繊毛」と「ジェンダー」「フェミニズム」より遠いですが、実際の意味上はもっと近い値になってもいいと思います）。

### 小区分
![スクリーンショット 2022-08-12 19.50.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/820036/27e5f5b9-1746-3c43-601c-3d747b69f41d.png)
こちらの結果は「鞭毛」と共通する部分も多いです。「44020〔発生生物学関連〕」は大きく外してはいない気がします。

## ゾウリムシ
ゾウリムシは材料ないし対象の名前ですが、科研費小区分のキーワードには具体的な生物材料名は含まれません。

### キーワード
![スクリーンショット 2022-08-12 19.50.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/820036/ec70f80f-81f9-470d-da53-5418e4d35d08.png)
ゾウリムシの属する繊毛虫類には寄生性のものもいるので、「寄生虫」は遠くない気がします。「コンクリート」「モデリング」「カリキュラム」は謎です。

### 小区分
![スクリーンショット 2022-08-12 19.51.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/820036/c4bacaab-ae4b-8d68-fcad-8152358d48f2.png)
「鞭毛」「繊毛」と共通部分が多い結果です。ゾウリムシは「泳ぐニューロン(swimming neuron)」と呼ばれることがあるので、「46030〔神経機能学関連〕」は言葉の上では遠くないかもしれません。

# まとめと所感
科研費小区分のキーワードをBERTでベクトル化し、任意の入力キーワードに対して申請する小区分をレコメンドしてみました。適切な小区分を最上位に持ってくるのは今回の手法（静的単語埋め込み）だけでは無理そうという印象ですが、「上位10位に含まれている」ぐらいならなんとなくそれっぽいものが入っている（こともある）ように思います。
小区分別の過去の採択課題のタイトルなどを学習させたりするとより実用に近いものができるかもしれません。

# 参考
- [科研費審査区分表](https://www.jsps.go.jp/j-grantsinaid/03_keikaku/data/r05/syokubun.pdf)
- [科研費小区分のキーワードをBERTでベクトル化して可視化](https://qiita.com/kageazusa/items/a300fc0f0a575ebfee7f)
- [【自然言語処理】BERTの単語ベクトルで「王+女-男」を計算してみる](https://zenn.dev/schnell/articles/4acc48c49eb8eb)
- [ユークリッド空間内の複数の点の中から、最も近い点を探す短いコード](https://analytics-note.xyz/programming/closest-point/)