AI News の公開と、裏側の OpenAI の活用話
AI News という、AI・データサイエンス(DS)・機械学習(ML)関係の話題を集め、AIで3行に要約して配信するサイトを公開しました。twitter @AINewsDev や Atomフィードでも配信しています。数日前から運用していて、手前味噌ですが便利に情報集めに使えています。また英語記事も日本語で要約されるので便利です。
なぜ作ったのか
以前、 @syou6162 さん作の ML-News で、ML系の情報を便利に読ませてもらっていたのですが、Twitter API の有料化の話が出た前後で閲覧できないようになってしまったようでした。
そのため、DS・ML系の話題を追いかけるのが大変になって、そのうち同じようなサイトを作ろうかな、と思っていた所でChatGPT(GPT4)というふつうに便利なLLMが登場しました。その後は御存知の通り、それらの話題が大量に溢れ、情報過多で見きれないなー、ただ自分の知りたい情報はちゃんと読みたい、概要(記事のHTMLにかかれているdescriptionは大抵最初の100文字程度が多く、あまり判断できない)を見て判断して中味をきちんと読めたらいいよな〜、あれっこれそもそもLLM(ChatGPT)でできるんじゃない?というわけで作ってみた次第です。
構築技術の話
Webスクレイピング、本文抽出、Webサイト周りの実装、みたいな話はふつうのWeb開発の話なので置いておいて、まずは ChatGPT や OpenAI APIを使ったお話を。
記事判定
Webでスクレイピングしてきた記事に対して、その記事がAI関連記事かどうかを判定する実装を作ります。機械学習をやっている方なら、正解ラベルがあればとりわけAI記事orそうでないの二値分類なら簡単だよね、という話なんですが、逆に正解ラベルの作成がめんどくさいですよね。人でやるのではなくて、AI自体が記事を判断して欲しい。
というわけで今回はまずのラベル付けをGPT-3.5 にやってもらいました。ただ、AIっぽい話題を例えば数値で0.0~1.0で振り分けてもらうことが以外と安定しない。どうにかいい感じに出力を確率分布にする、というのを筆を尽くしてプロンプトを書いたのですが私のプロンプト力ではうまくいかず。欲しい出力は softmaxの確率分布的な出力なので、むしろ下手に文章にするより、まんま softmax 関数を通してと書いたら、結果良い感じになりました。最終的なプロンプトはこんな感じ。
AIっぽいカテゴリー複数と「その他」カテゴリーを評価させます。実際に後述の「400字程度の概要」を与えて判定させた時の結果はこんな感じの出力が多く、各値が 0.0~1.0 で、かつ合計が 1.0 になって softmax ぽさありますね。
{
"AI": 0.0,
"Machine Learning": 0.0,
"Data Science": 0.0,
"Data Analysis": 0.0,
"Statistics": 0.8,
"Deep Learning": 0.0,
"kaggle": 0.0,
"ChatGPT": 0.0,
"MLOps": 0.0,
"Generative AI": 0.0,
"LLM": 0.0,
"Others": 0.2
}
ただ、こんな出力になることもあります。各値は 0.0~1.0 なのですが合計が 1.0 を超えていますね、softmax とは…?
{
"AI": 0.5,
"Machine Learning": 1.0,
"Data Science": 1.0,
"Data Analysis": 1.0,
"Statistics": 0.5,
"Deep Learning": 0.0,
"kaggle": 0.0,
"ChatGPT": 0.8,
"MLOps": 0.0,
"Generative AI": 0.0,
"LLM": 0.0,
"Others": 0.2
}
この出力をちゃんとした softmax 関数に通すと、結果はこんな感じ。0.0~1.0の分布かつ合計が1.0なので、これを使いましょう。
{
"AI": 0.08285351386643752,
"Machine Learning": 0.13660235066384355,
"Data Science": 0.13660235066384355,
"Data Analysis": 0.13660235066384355,
"Statistics": 0.08285351386643752,
"Deep Learning": 0.05025319642492017,
"kaggle": 0.05025319642492017,
"ChatGPT": 0.11184054543123119,
"MLOps": 0.05025319642492017,
"Generative AI": 0.05025319642492017,
"LLM": 0.05025319642492017,
"Others": 0.06137939271976228
}
このデータで、"Others" が最も高くなったものでラベルわけをし、その後は人力でチェックして間違っているラベルを正した所(N=550, AI関連=200, その他=350)、人力修正前との比較ではなんと正解率約94%。おおー、かなり高い正解率ですね。AIが出力した結果をもとに間違ってそうなものをチェックする、という形なのでバイアスがかかってしまったラベル付けではありますが、それでもかなりの正解率。間違っている内容もきわどい物も多い。もっとプロンプトを調整したり、GPT4を使えばさらに上げれそうですが、ゴールは分類器を作るための正解ラベルを作ることなので、それは達成できたのでこれで一旦は良しとします。
なお、カテゴリをたくさん書いているのは「機械学習・AI・データサイエンス…orそれ以外」的な内容で書くと、gpt3.5では出力が安定しない感じだったためです。
こんな感じで人間が確認しながらラベル修正。1からラベル付けするよりはだいぶ楽でした、がそれでも面倒…。
分類器を作る
正解ラベル550件は結構楽して作れたので、このラベルを元にAI関連の内容なのかどうかの分類器を作りましょう。特徴量の作成には OpenAI Embeddings API の text-embedding-ada-002
を利用して、記事本文を1536次元のベクトル(embedings)に変換します。1k tokens あたりの価格も gpt-3.5-turbo
の 20% なのも嬉しいですね。
なおOpen AI のブログによると、分類予測なら text-similarity-davinci-001
のほうが精度が高いようなのですが、今後色々なことに embeddings を使っていきたいので、汎化性能を重視して text-embedding-ada-002
を選んでます。
さて、これで1536次元の特徴量ができたので、正解ラベルを train / valid / test データに分けて分類器を作りましょう。今回は Kaggler おなじみの lightgbm で実装してみます。といっても lightgbm を使うのが数カ月ぶりなので、ドキュメント見ながら実装も面倒だったので ChatGPT に頼んだ所、動くコードがサクッと出てきてびっくり。ほぼコードママで利用できました。
このgistのコードは train 80%, valid 10%, test 10% の分割ですが、最終的にはデータは量も多くないので調整して train 60%, valid 30%, test 10% という割合で分けています。これで学習したデータの正解率は、valid acc 0.987
で test acc が 1.0
でした。テストデータの正解率100%!!! といってもテストデータは10%の55件なのでたまたまなので、random seed を適当に変えてやると 0.96~1.0 の正解率。text-embedding-ada-002
の特徴量でも分類課題でかなり良いスコアになりました。train が330件(60%分)しかないNLP分類でこのスコアはすごい。
これでAI関連orそうでない、の記事判定ができる分類器が完成しました。その後、様々なデータソースを追加したので、今今は時々AI関連でない記事もすり抜けて表示されてしまってますが、あとで再学習させてもっと賢くする予定です。
記事の要約作成
記事の要約をどう作るかですが、お金があれがまるっとgpt-4
に要約してもらうと一番精度高く正確です。ただコストが、gpt-3.5-turbo
と比較すると token あたりコストが15倍かかります。15倍。趣味でやるには結構なコストがかかってしまうので、できるだけ安価に仕上げたい。
gpt-4 と 3.5 に本文の先頭約4k token文を400字程度に要約させ主観で比較すると、4のほうがより良い要約ですが、3.5に比べて抜群に良いわけではない感じでした。値段コストと処理時間を考慮し、まずは gpt3.5 で 本文の先頭約4k token分を400文字ぐらいに要約するタスクにかけて要約させます。
4と3.5で内容に差が出たのは、さらに短くしたときで、twitterに投稿するために80文字前後まで情報圧縮した場合はgpt4のほうがかなりよい要約でした。またプロンプトで「日本語で80字前後」と制約条件を定義した時も、4のほうがその制約条件にかなりあった形で出力されます。3.5はかなり長くなったりすることがあり、 twitter 投稿の文字数制限があるので、この制約条件を守ることはありがたかった。
また1プロンプトで複数のことをこなすこともGPT4のほうが断然得意で、「80字前後」「カジュアルな文体で80字前後」「絵文字3字で要約を表現」「3行に要約して表示」というタスクを1回の処理で並列に実行させてもGPT4ならほぼ確実にこなし、3.5は複数タスクを1回の処理でこなすことは難しそうでした。
現在は以下のプロンプトで行っています。
これのプロンプトでGPT4を通すと、こんな感じで複数のタスクの結果を取得できます。
{
"Bullets": ["約2650チーム中15位で金メダル獲得", "Kaggle Competitions Masterの称号取得", "CV・LB相関が観測できず、最終結果は大幅なshake予想"],
"Summary": "Kaggleのコンペティションで15位の金メダルを獲得し、Kaggle Competitions Masterの称号を手に入れた。",
"SummaryEmojis": "🏆Kaggleのコンペで15位の金メダル🥇を獲得し、Kaggle Competitions Master👑の称号を手に入れた🎉",
"Emojis": "🥇👑🎉"
}
というわけで、要約等の作成は
- まず400字程度に3.5で要約。お金も時間も節約
- 更に400字の要約からさらに複数の短い要約作成するタスクは4.0で。お金も時間もかかるが精度は高い。
という二回に分けて行ってます。
なおプロンプトは巷で言われている通り、日本語より英語がで入力したほうが基本精度が高い出力が得られました。私は英語が苦手なのでDeepLで翻訳した英語なんですが、それでも英語のほうが良かったです。
ふつうの Web 開発の話
今まで書いた OpenAI・機械学習周りの処理以外にも、以下の実装を作りました。こちらのほうが開発全体の時間の7割ぐらいかかった感じで、まぁそれぐらい時間かかるよね…という感じでした。
- Python で様々なサイトのスクレイパー
- Python で裏側のデータベース等へのデータ保存
- 実行バッチの作成
- Next.js + TypeScript + Chakra UI でのWebサイト構築
この実装もだいたい WebUI の ChatGPT 3.5 / 4 が出力したコードをベースに書いたのですが、普通に仕事でも書いている Python まわりの実装は自分で書いたほうが早かったかなと思ったりもししつ、ちょっとした実装、例えば簡単な処理を行う関数や正規表現書いて、というのにはとても便利でした。
Next.js や TS は久しぶりすぎる、かつ Chakra UI は初めてだったので、こちらのコード生成は知識が少ない分、ChatGPTが生成したコードベースはとても役に立ちました。ただ Next.js は現最新バージョンの 13 ではなく、たぶんChatGPT学習時の11/12ぐらいのコードベースだったので、非推奨な構成のコードが時々でてきましたがそれはご愛嬌ということで。
また ChatGPT 4.0 のほうが品質が高いのですが、速度が遅いので、ちょっとしたコード生成は 3.5 のほうをもっぱら使ってました。3.5 で怪しい場合や、条件をたくさん入れてコードを入れる場合に 4.0 を使う感じで。適材適所。
現状の GPT 3.5 / 4.0 でも VSCode のインテグレートが進めば、それだけでもさらに非常に開発が便利なのに、更にコード生成が賢くなるであろう1-2年年後は、仕様の要件定義だけして、あとはdiff見てy/Nを押したりちょっとフィードバックしたりするだけの開発が実現味を帯びてきますね。
AI News の今後
今は最低限のところを作った、というだけなので、今後もちまちまと改善をしていく予定です。自分専用のSandboxで、かつ自分も便利に使えているWebサイトなので、盆栽を弄る感じで楽しいですね、盆栽いじりをしたことはないのですが。今は二値分類にしか利用していない記事の embeddings も色々なことに活用できそうですし、しばらく盆栽いじりが進みそうです。