2023年12月13日にGoogleが次世代AIであるGemini ProのAPIをリリースした。
少し時間が過ぎたが、ChatGPTとの価格比較と、実装方法について記載。
価格比較:Gemin Pro API vs ChatGPT
Gemini Pro APIの価格
- トークン単位ではなく、文字数単位
- 1000文字あたり、0.00025ドル。outputがinputの2倍の価格
- フリープランある(1分につき60リクエストまで)
- まだ従量課金モデルは「Coming soon」になっていて使えない(2024年1月11日時点)。現時点で1分間に60クエリ以上投げたらどうなるんだろう?多分free tierは超えるので返答こないのかな?それともfreeプランのままで返答くるのかな?
Open AI APIの価格
- フリープランなし
- トークンあたりの従量課金:1000トークンで、gpt-4が0.03ドル、gpt-3.5-turbo-1106が0.0010ドル(入力テキスト)
- 出力テキストが、入力テキストの2倍の価格
- gpt-3.5は、2024年1月25日時点で、入力が50%値下げ、出力が25%値下げとなった。
文字数とトークン数の関係
トークン
- 自然言語処理(NLP)で用いられる、文章を分析する際の基本的な単位です。
- 文章を構成する単語や句読点などを指し、トークン化(tokenization)はこれらの要素に分割するプロセス
- 英語の場合はスペースや句読点が自然な分割点となるが、日本語の場合は形態素解析が必要。
- 英語:1単語(≒4文字)=1トークン。句読点もそれだけでトークン扱い
- 日本語:漢字=2 or 3トークン、ひらがな=1トークン
- 日本語はトークンベースだと料金が高くなるが、単語・文字数ベースだと料金が低くなる
- 英語はトークンベースだと料金が低くなるが、文字数ベースだと料金が高くなる
英語の例文とトークン化:
例文: “The quick brown fox jumps over the lazy dog.”
トークン: [“The”, “quick”, “brown”, “fox”, “jumps”, “over”, “the”, “lazy”, “dog”, “.”]
トークン数: 10、文字数: 43 (スペースを含む)
日本語の例文とトークン化:
例文: “きょうはとてもいい天気です。”
トークン: [“きょう”, “は”, “とても”, “いい”, “天気”, “です”, “。”]
トークン数: 7、文字数: 14 (スペースを含まない)
英語ではスペースや句読点で自然にトークンが分かれるが、日本語では単語の区切りがスペースで表されないため、形態素解析器が単語の境界を見つける必要がある。したがって、同じ内容を表す文章でも、言語によってトークン数と文字数に違いが生じる。
トークン数は、Tokenizerで確認することができる。
https://platform.openai.com/tokenizer
価格比較まとめ:Geminiの圧倒的に安い。
今回は、便宜的に、日本語で1文字=2トークンとして計算
1ドル≒150円
OpenAI API | Gemini Pro API | |
---|---|---|
価格モデル | 従量課金 フリープランなし | フリーミアムモデル 1分間に60クエリまでフリー |
価格(入力テキスト) | GPT3.5-turbo-1106:0.01ドル →1.5円/1000トークン GPT4:0.03ドル →4.5円/1000トークン | 0.00025ドル/1000文字 →0.01875円/1000トークン |
価格(出力テキスト) | GPT3.5-turbo-1106:0.02ドル →3円/1000トークン GPT4:0.06ドル →9円/1000トークン | 0.0005ドル/1000文字 →0.0375円/1000トークン |
計算間違っているのでは?と疑ってしまったのだが、Gemini Pro APIの方がGPT3.5-turboと比較して80倍近く安い!
OpenAIが課金単位としている「トークン」では割高になりがちな日本語では、「文字」でカウントしてくれるGeminiは割りが良い(逆に英語は割りが悪い)というのもあると思うが、それを差し引いても圧倒的に安い。
ちなみに、Gemini Pro APIの性能はChatGPT3.5と同じくらいと言われている。
Gemini Pro APIキーの設定
Geminiには、「Google AI Studio」というWebインターフェースが用意されており、そこでモデルの詳細をチューニングしたり、実際にモデルを使ったりすることができる。
ai.google.devサイトにアクセスして、「Get API key in Google AI Studio」ボタンをクリック
googleが提供している開発者向けサービスを使うには、どのような機能であれ、まずはgoogleクラウドでプロジェクトを作成していることが前提になる。
今回はすでにプロジェクトがあるので、「Create API key in existing project」を選択して、APIキーを発行する。
ターミナルで言われた通りに、作成したAPI keyをクエリに入れて実行すると、すぐにjsonフォーマットで生成した文章が返ってきた。
このシームレスな体験設計良い。
Chat Promptで試してGet code
先ほどはサンプルをターミナルから実行したが、Google AI Studioはプロンプトを試すPlayground環境を用意している(OpenAIと同じ)。
Google AI Studio→Create new→Chat promptを選択。
開いた画面で、天気情報に対して追加の一言メッセージを作成するようなプロンプトを作成してみたら、返信が返ってきた。
内容的にも問題ない。
これで良さそうだと感じたら、画面右上の「Get code」を選択すると、各言語(JavaScript, Python, Kotlin, Swift)で生成されたコードをコピーできる。
これ便利!と思ったのだが、今回使用するのはGo言語なので残念。
Go言語での実装
今回サーバーサイドはGo言語を利用していて、バックエンドでGemini pro APIを利用したいので、go言語のチュートリアルを参照。
https://ai.google.dev/tutorials/go_quickstart?hl=ja
generative-ai-goパッケージをインストール
go get github.com/google/generative-ai-go
generative-ai-goパッケージのドキュメントはこちら
https://pkg.go.dev/github.com/google/generative-ai-go/genai
Go言語でGemini Pro APIを使用
package clocky_be
import (
"context"
"fmt"
"log"
"github.com/google/generative-ai-go/genai"
"google.golang.org/api/option"
)
func GenerateText(config *Config) {
log.Println("GenerateText function called")
ctx := context.Background()
GeminiAPIKey := config.GeminiAPIKey
// Access your API key as an environment variable (see "Set up your API key" above)
client, err := genai.NewClient(ctx, option.WithAPIKey(GeminiAPIKey))
if err != nil {
log.Fatal(err)
}
defer client.Close()
// For text-only input, use the gemini-pro model
model := client.GenerativeModel("gemini-pro")
prompt := genai.Text("Write a story about a magic backpack.")
resp, err := model.GenerateContent(ctx, prompt)
if err != nil {
log.Fatal(err)
}
printResponse(resp)
}
GenerativeModel関数に関するドキュメントはこちら
https://pkg.go.dev/github.com/google/generative-ai-go/genai#GenerativeModel
GenerateContent関数のresponseは*genai.GenerateContentResponse
型。
GenerateContent関数のresponseの構造と中身
resp
の構造を知りたいので、Go の encoding/json
パッケージでjson.MarshalIndent
関数を使用して resp
オブジェクトを整形された JSON 文字列に変換し、それを fmt.Println
で出力してみる。
func GenerateText(config *Config) {
log.Println("GenerateText function called")
ctx := context.Background()
GeminiAPIKey := config.GeminiAPIKey
// Access your API key as an environment variable (see "Set up your API key" above)
client, err := genai.NewClient(ctx, option.WithAPIKey(GeminiAPIKey))
if err != nil {
log.Fatal(err)
}
defer client.Close()
// For text-only input, use the gemini-pro model
model := client.GenerativeModel("gemini-pro")
prompt := genai.Text("今日の天気を教えて")
resp, err := model.GenerateContent(ctx, prompt)
if err != nil {
log.Fatal(err)
}
respJSON, err := json.MarshalIndent(resp, "", " ")
if err != nil {
log.Fatal(err)
}
fmt.Println(string(respJSON))
}
結果がこちら
{
"Candidates": [
{
"Index": 0,
"Content": {
"Parts": [
"申し訳ございませんが、私はリアルタイムの情報を提供することができません。私は2023年2月までに得た知識のみを基に回答を生成しています。最新の天気予報については、気象庁のウェブサイトやテレビ、ラジオなどの天気予報番組をご覧いただくことをお勧めします。"
],
"Role": "model"
},
"FinishReason": 1,
"SafetyRatings": [
{
"Category": 9,
"Probability": 1,
"Blocked": false
},
{
"Category": 8,
"Probability": 1,
"Blocked": false
},
{
"Category": 7,
"Probability": 1,
"Blocked": false
},
{
"Category": 10,
"Probability": 1,
"Blocked": false
}
],
"CitationMetadata": null,
"TokenCount": 0
}
],
"PromptFeedback": {
"BlockReason": 0,
"SafetyRatings": [
{
"Category": 9,
"Probability": 1,
"Blocked": false
},
{
"Category": 8,
"Probability": 1,
"Blocked": false
},
{
"Category": 7,
"Probability": 1,
"Blocked": false
},
{
"Category": 10,
"Probability": 1,
"Blocked": false
}
]
}
}
resp
の中からテキスト情報(Candidates
→ Content
→ Parts
の中身)を取り出したいので、以下のように resp
オブジェクトの対応するフィールドにアクセスする。
応答 (resp
) に含まれる各候補 (Candidates
) をループし、それぞれの候補の内容 (Content
) が存在するかを確認。Content
が存在する場合、その中の Parts
配列に含まれる各テキスト部分をさらにループし、それぞれを出力する。
func printResponse(resp *genai.GenerateContentResponse) {
for _, candidate := range resp.Candidates {
// Content が nil でないことを確認
if candidate.Content != nil {
// Parts に含まれる各テキスト部分をループで処理
for _, part := range candidate.Content.Parts {
fmt.Println(part)
}
}
}
}
main.goファイルで、GenerateText関数を呼び出して、サーバー起動。
package main
func main() {
config := clocky.NewConfig()
clocky.GenerateText(config)
}
完成!
無事、「今日の天気は?」というプロンプトに対して、返答がテキストでコンソールに表示された。返答内容は、まぁ、少し残念だが。
コメント