Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Julia でモジュールプログラミング(初歩の初歩) for JuliaTokai#15

Julia でモジュールプログラミング(初歩の初歩) for JuliaTokai#15

Julia でモジュールプログラミング(初歩の初歩) for JuliaTokai#15

GOTOH Shunsuke

June 25, 2023
Tweet

More Decks by GOTOH Shunsuke

Other Decks in Technology

Transcript

  1. 自己紹介 • 名前:後藤 俊介 • 所属:有限会社 来栖川電算 • コミュニティ:🌟機械学習名古屋, 🌟JuliaTokai,

    ⭐jl.dev, ⭐Python東海, … • 言語:Julia, Python, Scala(勉強中), … • SNS等:                (@antimon2) • Julia 本 執筆しました!🆕
  2. Julia とは?(1) • The Julia Language • 最新 v1.9.1(2023/06/07) ◦

    LTS:v1.6.7(2022/07/19) • 科学技術計算に強い! • 動作が速い!(LLVM JIT コンパイル)
  3. Julia とは?(2) • Rのように中身がぐちゃぐちゃでなく、 • Rubyのように遅くなく、 • Lispのように原始的またはエレファントでなく、 • Prologのように変態的なところはなく、

    • Javaのように硬すぎることはなく、 • Haskellのように抽象的すぎない ほどよい言語である 引用元:http://www.slideshare.net/Nikoriks/julia-28059489/8
  4. Julia とは?(3) • C のように高速だけど、 Ruby のようなダイナミズムを併せ持っている • Lisp のような真のマクロを持ちながら、

    MATLAB のような直感的な数式表現もできる • Python のように総合的なプログラミングができて、 R のように統計処理も得意で、 Perl のように文字列処理もできて、 MATLAB のように線形代数もできて、 shell のように複数のプログラムを組み合わせることもできる • 超初心者にも習得は容易でありながら、 ハッカーの満足にも応えられる • インタラクティブな動作環境もあって、コンパイルもできる (Why We Created Julia から抜粋・私訳)
  5. 技術情報 • OpenAI.jl(OpenAI API ラッパー) ◦ API: "gpt-3.5-turbo-XXXX"(利用できる場合は "gpt4-xxxx") •

    REPL 組み込みは 作り込み (手段は用意されている) • その他の依存Pkg: ◦ HTTP.jl ◦ DotEnv.jl ◦ ProgressMeter.jl ◦ JSON3
  6. REPL 連携部 function create_mode(repl, main_mode) chat_mode = LineEdit.Prompt("chat> "; prompt_prefix

    = Base.text_colors[:cyan], prompt_suffix = "", # complete = nothing, sticky = true) chat_mode.repl = repl hp = main_mode.hist hp.mode_mapping[:chat] = chat_mode chat_mode.hist = hp _search_prompt, skeymap = LineEdit.setup_search_keymap(hp) _prefix_prompt, prefix_keymap = LineEdit.setup_prefix_keymap(hp, chat_mode) chat_mode.on_done = REPL.respond(repl, main_mode) do line chat_on_done(get_model(), repl, hp, line) # ← ココ! end mk = REPL.mode_keymap(main_mode) b = Dict{Any, Any}[ skeymap, #=repl_keymap,=# mk, prefix_keymap, LineEdit.history_keymap, LineEdit.default_keymap, LineEdit.escape_defaults ] chat_mode.keymap_dict = LineEdit.keymap(b) return chat_mode end
  7. ChatGPT 連携部 (ストリーム対応の場合) function chat_on_done(model::StreamChatAPIModel, repl, hp, line) repl_out =

    REPL.outstream(repl) # ↑注目 contents = sprint() do io create_chat( OPENAI_API_KEY[], string(model), [Dict("role"=>"user", "content"=>line)]; streamcallback=let function (chunk) for line in split(chunk, '\n') line = strip(line) isempty(line) && continue if line != "data: [DONE]" chunk_obj = JSON3.read(line[6:end]) delta = chunk_obj.choices[1].delta if haskey(delta, :content) print(repl_out, delta.content) print(io, delta.content) end end end end end ) end println(repl_out) # 最後に改行を出力 _parse_lines(model, contents, REPL.repl_filename(repl, hp)) end
  8. ChatGPT 連携部 (ストリーム非対応の場合) function chat_on_done(model::ChatAPIModel, repl, hp, line) r =

    tryrequest_with_spinner() do # ↑注目 create_chat( OPENAI_API_KEY[], fetch_model_id(), [Dict("role"=>"user", "content"=>line)], ) end contents = [choice["message"]["content"] for choice in r.response["choices"]] # text = join(contents, '\n') text = contents[begin] # 最初の1件だけ持ってくればOK # output println(REPL.outstream(repl), text) # parse _parse_lines(model, text, REPL.repl_filename(repl, hp)) end
  9. Point • get_model() という関数でモデルオブジェクトを取得 • ↑を chat_on_done() という関数の第1引数に渡して 多重ディスパッチ •

    ⇒モデル(およびその種別)によって処理を分けている • 「機能で実装を分ける」という Julia の基本!
  10. get_model() abstract type APIModel end # : 中略 struct ChatAPIModel

    <: APIModel name::String end struct StreamChatAPIModel <: APIModel name::String end APIModel(name::AbstractString) = APIModel(Val(Symbol(name)), name) @generated function APIModel(::Val, name::AbstractString) # P1, P2 if isdefined(OpenAI, :request_body_live) :(StreamChatAPIModel(String(name))) else :(ChatAPIModel(String(name))) end end Base.string(model::APIModel) = model.name get_model() = APIModel(fetch_model_id()) # `fetch_model_id()` は利用可能なモデル IDを取得する関数(詳細略)
  11. TextDavinci003.jl 別のモデルに対応させる例 module TextDavinci003 import ..AIChatREPL: APIModel, chat_on_done, _parse_lines #

    : 《中略》 struct TextDavinciAPIModel <: APIModel name::String end function APIModel(::Val{Symbol("text-davinci-003")}, name::AbstractString) TextDavinciAPIModel(name) end function chat_on_done(model::TextDavinciAPIModel, repl, hp, line) # : 《略》 end # : 《中略》 end
  12. Point • 別モジュールで import AIChatREPL: APIModel, chat_on_done, _parse_lines • APIModel(::Val{Symbol("text-davinci-003")

    },…) のような形で特定のモデル名に対する独自の APIModel を返す仕組みを多重定義 • ↑の独自 APIModel を第1引数に受け取る chat_on_done() (や _parse_lines())を必要に応 じて多重定義
  13. Point • 別モジュールで派生してほしい型や多重定義してほしい 関数を並べる ◦ 関数は function 《関数名》 end のように引数な

    しで宣言 • その後に include(“《なんたら》.jl”) で読み込む • ⇒分かりやすい!
  14. 参考 • モデルの追加も簡単 ◦ 専用のモジュールを作る ▪ 必要な物を import する ▪

    必要な物を実装する ◦ そのモジュールを import または using する • (詳細例略)