📘

MojoでOpenAIを使おう!簡単導入ガイド

2024/06/15に公開

概要

この記事の対象者:
MojoでOpenAIのモジュールを使いたい開発者

この記事の内容:
MojoでOpenAIのAPIを利用する方法とその実行結果

この記事を読むとわかること:
MojoでOpenAIのAPIを利用するためのコード例と手順、および実行結果の詳細

序章

いつも通りの平穏な日、突然目に飛び込んできたのは、一つの奇妙な記事だった
https://github.com/modularml/mojo/discussions/1656

MojoでOpenAIのモジュールが使えないなんて、そんな。。。
私は、「Mojo openai」と何度も検索をかけてみた、しかし結果は同様に何もヒットしなかった
私は悲壮感に耐えられず、そんな訳で作ってみました!

成果物

実際に、動いている様子です↓

コードは以下です↓

from python import Python

fn chat_response() -> None:
    try:
        var openai = Python.import_module("openai")
        var os = Python.import_module("os")
        var dot_env = Python.import_module("dotenv")
        dot_env.load_dotenv()
        var api_key =  os.getenv('OPENAI_KEY')
        var client = openai.OpenAI(api_key=api_key)
        var dictionary = Python.dict()
        dictionary["role"] = "user"
        var builtins = Python.import_module("builtins")
        var time_function = Python.import_module("time")

        dictionary["content"] = builtins.input("Enter your message: ")
        var start = time_function.time()
        var messages = Python.list()
        messages.append(dictionary)
        
        var completion = client.chat.completions.create(
            model="gpt-3.5-turbo",
            messages=messages
        )
        print(completion.choices[0].message.content)
        var end = time_function.time()
        var seconds = builtins.round((end - start),2)
        print("Chatbot response took", seconds, "seconds to run")
    except e:
        print("An error occurred:", e)

fn main() -> None:
    chat_response()

Mojoの文法

そもそもmojoとは?という方はこちらを参考にしてくださいませ

Mojoはpythonと似ている部分はありますが、違うところもあります
実際にコードを書いてみて、特に気になった点をピックアップしていみました

型システム

  • mojoでは静的型付けですので、関数の引数や戻り値には型が必要です
  • pythonと同様に変数に型つきで宣言することもできます
  • ちなみに文字列はString、整数はInt、浮動小数はFloat大文字始まりなのが注意です
    例:
    fn chat_response() -> None
    

関数定義

  • mojoでは関数をfnキーワードで定義します
  • 関数の引数や戻り値には型が必要です
    例:
    fn chat_response()
    

変数宣言

  • mojoでは変数をvarキーワードまたはletキーワードで宣言します
  • varが変数で、letが定数です
    例:
    var start = time_function.time()
    

main関数

  • mojoでは、実行時に**fn main():**が実行されます
  • if __name__ == "__main__"は不要です
    例:
    fn main() -> None:
    

モジュールインポート

  • mojoではPython.import_moduleを用いてpythonモジュールをインポートします
  • Python.import_moduleを用いる場合try文やraiseを使用します
  • mojoモジュールはpythonと同様に先頭にimport <namespace>を利用します[*1]
    例:
    var openai = Python.import_module("openai")
    

コードの補足

冒頭のコードの流れは以下の通りです

  1. main関数が実行される
  2. chat_responseが呼び出される
  3. openaiosなどモジュールが定義される
  4. os.getenv('OPENAI_KEY')でAPIKeyが定義される
  5. builtins.input("Enter your message: ")でユーザの入力を受け付ける
  6. client.chat.completions.createでapiにアクセスする
  7. completion.choices[0].message.contentで結果を表示する

特に、自分がハマった点として以下が挙げられます

  • Python.import_moduleを利用する際にはtyr文を使うこと
  • キーボードからの入力には、builtins.inputを使うこと
  • 時間の計測には、pythonモジュールtimeを利用すること
  • 少数の切り上げは、pythonモジュールroundを利用すること

(mojoでこんな便利な機能があるよという方、ぜひアドバイスお願いします!)

再現方法(GPT3.5)

有料のopenaiのapiキーが必要となります
まずは、お試しでという方はGPT2の場合まで飛んでください!

git clone

成果物のリポジトリを作成しましたので、ご利用くださいまし[*2]

git clone https://github.com/tyukei/mojo-gpts.git

mojoのセットアップ

こちらを参考にセットアップします
セットアップ済みの人はスキップでOKです

apiキー発行

こちらのリンクよりapiキーを発行してください
キーはコピーしときましょう
https://platform.openai.com/api-keys

.envファイルの設定

まず、.env.copyファイルを.envファイルに名前を変更します
そして、.envファイルの中に、先ほど取得したapiキーを設定します。

OPENAI_KEY="sk-xxxxxxxx...."

mojoの実行

以下のコマンドよりmojoを実行することができます
成功すれば、冒頭のデモ動画のようにgpt3.5apiの応答が返ってくるはずです!

mojo gpt35.mojo

実行結果

試しに、How many people in the world?と入力してみます

As of 2021, the estimated global population is around 7.9 billion people.

と返ってきました

2021年と情報は古いですが、およそ正確な答え[*3]でした

ちなみに2024年時点では世界人口は81億人[*4]でした
(75億っていつの話だ?)

追加実験

GPT2の場合

apiキーなしでも動作確認できるよう、gpt2で実装しました
以下のコマンドを入力します

mojo gpt2.mojo

するとEnter your message:と表示されます
試しに、How many people in the world?と入力してみます

AI: 1

と返ってきました😇

Pythonの場合

一応、pythonバージョンでも作ってみました

gpt3.5は以下のコマンドより実行できます

python gpt35.py

gpt2は以下のコマンドより実行できます

python gpt2.py

実行時間の比較

同じプロンプトを与えた時の、pythonとmojoの応答速度を比較してみました

GPT3.5の場合
mojoは0.57秒で、pythonは0.60秒でした

GPT2の場合
mojoは1.00秒でpythonは1.17秒でした

若干mojoの方が早かったですが、そこまで大きな差はなさそうです
シンプルなコードで実行時間を比較するのはナンセンスかもしれませんが、
1サンプルとして捉えていただければと思います

結論

参照

*1
https://docs.modular.com/mojo/lib

*2
https://github.com/tyukei/mojo-gpts/tree/main

*3
https://www.stat.go.jp/data/sekai/pdf/2021al.pdf

*4
https://www.unfpa.org/sites/default/files/pub-pdf/swp2024-english-240327-web.pdf

Discussion