LLMたくさんあるけど、どのモデルがQiitaで一番語られているのか調査してみた


## はじめに
ChatGPTが登場してから早1年半が経過します。
僕はこのツールに完全に魅了されていて、今では欠かせない存在になっています。
とはいえ最近はGPT-4では痒い所に手が届かずもどかしさも感じており、GPT-5のリリースを待ち望んでいます。

さて、日常的に利用しているChatGPTはLLMを利用しているわけですが、そもそもLLMにはGPT以外にどんなモデルが存在しているのか、実は全然把握できていないことに気づいてしまいました。
そこで、
1. LLMにはどんなモデルがあるのか
2. そのモデルの認知度はどの程度なのか

Node-REDとQiitaAPIを活用して調査を行うことにします。

## 方法
* 調査対象となるLLMは、2024年5月11日時点でWikipediaの大規模言語モデル一覧に記載のあるものとする
[大規模言語モデル](https://ja.wikipedia.org/wiki/%E5%A4%A7%E8%A6%8F%E6%A8%A1%E8%A8%80%E8%AA%9E%E3%83%A2%E3%83%87%E3%83%AB#%E5%A4%A7%E8%A6%8F%E6%A8%A1%E8%A8%80%E8%AA%9E%E3%83%A2%E3%83%87%E3%83%AB%E3%81%AE%E4%B8%80%E8%A6%A7)
* Qiitaの記事タイトルに調査対象のLLMの名前があった場合に、そのLLMに関連する記事であると判定し記事数にカウントする
* LLMごとの記事数からランキングを作成し認知度Top10を求める
※認知度が高い=Qiita内の記事数が多いと仮定

## Node-REDのフロー
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3770353/72f2881a-4239-6bb5-b552-ca71214c50e9.png)

↓このフローをjson出力したもの
```json
[{"id":"024dab045cd5e23e","type":"inject","z":"c656482b65c1c8d2","name":"検索ワード","props":[{"p":"search","v":"DBRX","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":100,"y":80,"wires":[["cb5931b9116765fc"]]},{"id":"5d3ae9e6df952388","type":"http request","z":"c656482b65c1c8d2","name":"","method":"GET","ret":"obj","paytoqs":"ignore","url":"https://qiita.com/api/v2/items?query=title:'{{{query}}}'&page={{{page}}}&per_page=100","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":430,"y":80,"wires":[["8d7e5c4885cfc325"]]},{"id":"77cbe6caa2537f37","type":"debug","z":"c656482b65c1c8d2","name":"TGT COUNT","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"titleCounts","targetType":"msg","statusVal":"","statusType":"auto","x":1210,"y":100,"wires":[]},{"id":"eaec243c1ffb5fa4","type":"function","z":"c656482b65c1c8d2","name":"該当記事数算出","func":"// msg.counts = msg.payload.map(item => item.title).length;\n// msg.counts = \nmsg.allDataCounts = msg.allData.length;\n// msg.searchTitles = msg.allData.filter(item => item.title.toLowerCase().includes(msg.search)).map(item => item.title);\n\n// msg.searchに基づいて完全一致するタイトルだけを抽出\nmsg.searchTitles = msg.allData.filter(item =>\n    new RegExp(`\\\\b${msg.search}\\\\b`, 'i').test(item.title)).map(item => item.title);\nmsg.titleCounts = msg.searchTitles.length;\n\n// フィルタリングに合致しなかったタイトルを集める\nmsg.unmatchedTitles = msg.allData.filter(item =>\n    !new RegExp(`\\\\b${msg.search}\\\\b`, 'i').test(item.title)\n).map(item => item.title);\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1020,"y":80,"wires":[["77cbe6caa2537f37","3a62c608fb761218","81a3414fd5d11614"]]},{"id":"3a62c608fb761218","type":"debug","z":"c656482b65c1c8d2","name":"ALLTITLE COUNT","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"allDataCounts","targetType":"msg","statusVal":"","statusType":"auto","x":1230,"y":60,"wires":[]},{"id":"8d7e5c4885cfc325","type":"function","z":"c656482b65c1c8d2","name":"データ格納","func":"msg.counts = msg.payload.map(item => item.title).length;\nmsg.allData = msg.allData.concat(msg.payload);\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":590,"y":80,"wires":[["46fe8c6230b31cb7"]]},{"id":"cb5931b9116765fc","type":"function","z":"c656482b65c1c8d2","name":"変数初期化","func":"msg.allData = [];\nmsg.page = 1;\nmsg.query = encodeURI(msg.search);\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":250,"y":80,"wires":[["5d3ae9e6df952388"]]},{"id":"46fe8c6230b31cb7","type":"switch","z":"c656482b65c1c8d2","name":"取得記事数チェック","property":"counts","propertyType":"msg","rules":[{"t":"lt","v":"100","vt":"num"},{"t":"eq","v":"100","vt":"num"}],"checkall":"true","repair":false,"outputs":2,"x":780,"y":80,"wires":[["eaec243c1ffb5fa4"],["9ffb8ff3f390d23c"]]},{"id":"9ffb8ff3f390d23c","type":"switch","z":"c656482b65c1c8d2","name":"ページ数チェック","property":"page","propertyType":"msg","rules":[{"t":"eq","v":"100","vt":"num"},{"t":"lt","v":"100","vt":"num"}],"checkall":"true","repair":false,"outputs":2,"x":770,"y":160,"wires":[["eaec243c1ffb5fa4"],["c203ff283486333f"]]},{"id":"c203ff283486333f","type":"function","z":"c656482b65c1c8d2","name":"次ページ設定","func":"msg.page = msg.page + 1\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":440,"y":160,"wires":[["5d3ae9e6df952388"]]},{"id":"81a3414fd5d11614","type":"debug","z":"c656482b65c1c8d2","name":"UNMATCH COUNT","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"unmatchedTitles","targetType":"msg","statusVal":"","statusType":"auto","x":1230,"y":140,"wires":[]}]
```

### ポイント① ユーザー認証
このフローを動かすときに、Qiitaのユーザー認証をしています。
別にしなくても目的は達成できますが、やった方が効率的かなと思い対応しています。

↓こちらの記事を参考に行いました。
[Qiita API を使って、記事データを取得する方法](https://qiita.com/wakudar/items/8c594c8cc7bda9b93b4e)

※もし先ほどフローをコピペで使用したい場合は、下記作業をしてください。
1. トークンを発行
2. Node-REDにフローを読み込み
3. http requestノードにトークンを設定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3770353/8e6c0bbe-5053-ead0-a226-a1aa01634029.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3770353/d7b727ce-09ab-b3d8-01fb-ed8049acfe28.png)

### ポイント② 記事数算出ロジック
今回モデル名が記事タイトルに存在しているかどうかで記事数をカウントするのですが、
たとえば、「PaLM」と「PaLM2」この2つのモデルに対して単純にタイトル検索をして記事数を調べようとすると、次のようなことが起こります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3770353/091a9478-e56c-6730-3c1c-a33b867da650.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3770353/59676238-f8e8-243c-49b1-52e170b5c526.png)
今「PaLM2」について検索しているのですが、「PaLM」の記事がヒットしてしまう...
これを解消するために検索したいモデル名を正規表現にしてタイトルをチェックするようにしています。下のような感じです。
```javascript
msg.searchTitles = msg.allData.filter(item =>
    new RegExp(`\\b${msg.search}\\b`, 'i').test(item.title)).map(item => item.title);
```
（ただし、これはこれで問題は発生するので、ノーマルなパターンと正規表現のパターンの両方で集計していきます。）

## ランキング
では認知度が高いLLMモデルは何か集計結果を見ましょう。
| rank | ノーマルな検索 | 正規表現で検索 |
|:-:|:-:|:-:|
| 1 | BERT (285)  | BERT (281)  |
| 2 | GPT-4 (268) | GPT-4 (174)  |
| 3 | GPT-3 (201)  | GPT-3 (134)  |
| 4 | LLaMA (136)  | OPT (97)  |
| 5 | Claude (123)  | LLaMA (97)  |
| 6 | Llama 2 (122)  | Claude (79)  |
| 7 | Llama 3 (119)  | GPT-2 (46)  |
| 8 | Claude 3 (114)  | Gopher (36)  |
| 9 | OPT (101)  | Claude 3 (114)  |
|10 | GPT-2 (97)  | BLOOM (32)  |

※このランキングにでてくる「OPT」「Gopher」「BLOOM」は、実際に検索するとLLMとは別のものについての記事が多いということが分かりました。

この表からノーマルと正規表現のランキングの顔ぶれは似ていることが分かります。
僕としても、そういえば聞いたことがあるなという名前のものばかりなので、それなりに信用できそうな結果なのかなと思いました。
（全然知らない名前のモデルが上位に出てこなくてホッとした...）
このランキングに出てくるGPT以外のLLMはこれからキャッチアップしていこうかなと思います。

## 最後に
今回初めてのNode-REDと不慣れなJavaScriptを使ったので、複雑なフローは組まず、自力でできそうな部分はシステム化しないようにしました。
なので、今回48個分のモデルについて記事数を調べるときに、一つ一つ毎回手打ちで変数名を入力し、48回フローを実行して集計しました（汗）
次にフローを作るときは、このあたりの改善が必須ですね。

以上、最後までお読みいただきありがとうございました！