ruby-openaiでAIの力を体感!ソースコード解説&簡単ハンズオン
目次[非表示]
- 1.はじめに
- 2.ruby-openaiとは?
- 2.1.ruby-openaiの機能一覧
- 2.2.ruby-openai自体の説明
- 3.ハンズオン
- 3.1.準備
- 3.2.ChatGPT
- 3.3.Embeddings
- 3.4.Files
- 3.5.Finetune
- 3.6.Image
- 3.7.Edit
- 3.8.Whisper
- 3.9.Transcribe
- 4.感想・まとめ
- 5.おわりに
こちらの記事はDIVXアドベントカレンダー2023の6日目の記事です。
はじめに
株式会社divxの細野・大石です。
現在、私たちの所属する株式会社divxにおいて、Rubyを使用した案件比率は少なくありません。
また、最近は「Chat-GPT」が巷を大変賑わせており、自然言語処理を活用する案件も今後一層増えてくるのではと感じています。
一方で、Rubyで自然言語処理モデルを使用するためのライブラリである、「ruby-openai」に関する日本語の資料は多くありません。
そこで、この記事ではRubyで自然言語処理モデルを扱うためのライブラリruby-openaiについて、ソースコードの解説 & ハンズオン形式の簡単な使用例を紹介しています。
最後まで目を通していただけますと幸いです。
尚、2023年5月時点のruby-openaiを参照しています。
ruby-openaiとは?
ruby-openaiとは、OpenAIの機能をプログラミング言語のRubyで利用するためのライブラリです。
このライブラリは、OpenAIの自然言語処理モデルにアクセスするための方法を、誰でも利用できる簡単な形で提供しています。
つまり、ruby-openaiを使用することで、高度な自然言語処理を簡単に実現できるということです。
実現できる機能の具体例としては、文章の生成や要約・翻訳・テキスト分類・画像生成などが挙げられます。
以降では、「ruby-openaiはどんな機能を提供しているのか」や「実際の使用例(ハンズオン)」などを紹介します。
ruby-openaiの機能一覧
ruby-openaiで提供されている機能を簡単に紹介します。
詳細内容は、ハンズオンの各項目にて記載しています。
◎ ChatGPT
ChatGPTは、会話形式でテキストを生成するために使用できるモデルです。このモデルを使用すると、一連のメッセージに対する応答を生成できます。
◎ Streaming ChatGPT
Streaming ChatGPTは、APIからリアルタイムでストリーミングできるため、より高速で魅力的なユーザーエクスペリエンスを作成するために利用できます。
この機能は、会話形式でテキストを生成するChatGPTのより高速なバージョンです。APIからのリアルタイムストリーミングを可能にするため、ChatGPTよりも高速で、より滑らかな会話を実現することができます。
※ 本記事でStreaming ChatGPTのハンズオンは行いません。
◎ Completions
Completionsは、テキストを入力すると、指定されたコンテキストやパターンに一致するテキスト補完を生成するプロンプトです。
◎ Edits
Editsは、指示されたテキストとその変更方法に従って編集を行います。
◎ Embeddings
Embeddingsは、テキストをベクトル(1536 次元の数値配列)に変換します。
このベクトル同士の距離を計算することで、テキストの類似性を測定することができます。
◎ Files
Filesは、最大1GBのJSONファイルのアップロードや、アップロードしたJSONファイルに対する処理を行うことができます。
◎ Finetune
GPT-3のモデル(davinci・curie・babbage・ada)をカスタマイズします。
◎ Image
画像生成や画像編集など、画像操作を行います。
◎ Moderation
コンテンツが OpenAIの使用ポリシーに準拠しているかどうかを確認できます。
◎ Whisper
Whisperとは、オーディオファイルをテキストへ変換することができる音声認識モデルです。
Whisperを使用することで、音声ファイルからの文字起こしなどが行えます。
ruby-openai自体の説明
まず、ruby-openaiのソースコードを見ていきましょう。
リポジトリは以下になります。
https://github.com/alexrudall/ruby-openai
mainファイルは、lib/openai.rb
になります。
■ lib/openai.rb
行っていることは、大きく以下の2点です。
- requireでgemを、require_relativeでopanaiディレクトリ配下のモジュールを読み込み
- その他バージョンやリクエストタイムなどを設定
続いて、require_relativeで読み込んでいる各モジュールの説明です。
まずは、lib/openai/http.rb
です。
■ lib/openai/http.rb
publicメソッドとして、get
・json_post
・multipart_post
・delete
メソッドを定義しています。json_post
とmultipart_post
の違いは、Content-Typeにmultipart/form-dataを指定しているか否かです。Content-Typeにmultipart/form-dataを指定するmultipart_post
は、1つのHTTPリクエストに複数の異なる種類のデータを含める際に使用されます。これは、非テキストデータである音声データや画像データを取り扱う際にも使用されます。
次に、lib/openai/client.rb
です。
■ lib/openai/client.rb
行っていることは以下の2点です。
-
lib/openai/http.rb
のjson_post
またはmultipart_post
メソッドへ、必要な「path」と「parameters」を渡しています。lib/openai/http.rb
のjson_post
またはmultipart_post
メソッドでは、「Faraday」というgemを用いてOpenAIのエンドポイントへリクエストを投げています。
例えば、OpenAIのChatGPTの機能を使用したい場合、POST
でhttps://api.openai.com/v1/chat/completions
というエンドポイントへリクエストを投げることで機能を使用できます。 - OpenAIの
Files
・Finetunes
・Images
・Models
それぞれのクラスのインスタンス生成を行い、それぞれのクラスのメソッドを使える状態にしています。
続いて、lib/openai/files.rb
です。
■ lib/openai/files.rb
各メソッド、lib/openai/client.rb
のget
やmultipart_post
メソッドに必要な引数を渡しています。
その結果、OpenAIのエンドポイントへリクエストが投げられます。
以降、同様の説明が続きます。
続いて、lib/openai/finetunes.rb
です。
■ lib/openai/finetunes.rb
各メソッド、lib/openai/client.rb
のget
やmultipart_post
メソッドに必要な引数を渡しています。
その結果、OpenAIのエンドポイントへリクエストが投げられます。
続いて、lib/openai/images.rb
です。
■ lib/openai/images.rb
各メソッド、lib/openai/client.rb
のget
やmultipart_post
メソッドに必要な引数を渡しています。
その結果、OpenAIのエンドポイントへリクエストが投げられます。
続いて、lib/openai/models.rb
です。
■ lib/openai/models.rb
各メソッド、lib/openai/client.rb
のget
やmultipart_post
メソッドに必要な引数を渡しています。
その結果、OpenAIのエンドポイントへリクエストが投げられます。
最後に、lib/openai/version.rb
です。
■ lib/openai/version.rb
バージョン情報の定数を定義しています。
ハンズオン
ここからは、ruby-openaiで使用出来る各機能を実行する、簡単なハンズオンを紹介します。
◎ リポジトリ
以下はハンズオンの完成形です。必要に応じてご覧ください。
https://github.com/Naoki-014/playing-with-ruby-openai
◎ OpenAIの価格表
OpenAIのAPIを使用する場合、一定量以上使用すると料金が発生しますのでご注意ください。
そのため、適度に請求ページを見ながらハンズオンを進めることをお勧めいたします。
https://platform.openai.com/account/usage
これからOpen AIのアカウントを作成する方や、OpenAIアカウントの作成から3ヶ月以内の方であれば、5ドル(2023年6月時点)分のクレジットが付与される為、ハンズオンで料金が発生する可能性は少ないと思われます。
準備
ハンズオンのための事前準備です。
※ 以降はrubyの環境構築が完了している前提です。
まず、OpenAIのアカウント作成後、下記リンクよりAPI KEYを取得しましょう。
https://platform.openai.com/account/api-keys
次に、取得したAPI KEYをOPENAI_ACCESS_TOKEN
という変数名で環境変数に設定します。
続いて、以下のリポジトリをgit clone
します。
https://github.com/Naoki-014/playing-with-ruby-openai
git clone
したディレクトリへ移動し、bundle install
を実行しましょう。
これでハンズオンの準備は完了です。
最後に、git clone
したリポジトリのsample_cli.rb
には既に3つのメソッドが記載されているため、簡単に説明します。
◎ clientメソッドOpenAI::Client
クラスのインスタンスを生成するメソッドです。
◎ model_versionメソッド
渡された引数を、変数model_version
に代入するメソッドです。
引数が渡されなかった場合は、gpt-3.5-turbo
というモデルを代入します。
◎ gets_chompメソッド
ユーザーから入力された文字列を受け取り、末尾の改行文字を取り除いた文字列を返すメソッドです。
それでは、ハンズオンを始めましょう。
ChatGPT
ChatGPTは、会話形式でテキストを生成するために使用できるモデルです。このモデルを使用する
ChatGPTは、会話形式でテキストを生成するために使用できるモデルです。このモデルを使用すると、一連のメッセージに対する応答を生成できます。
また、Chat-GPTは自然な表現を生成するために学習されており、会話の流れを追うような応答を返すことができます。そのため、顧客サポートやチャットボットの開発に役立ちます。
実装コード
今回は、「プロンプトを入力すると応答が返ってくる機能」を実装します。
まずは、シンプルな応答を返しましょう。
■ sample_cli.rb
■ ruby_openai/chat_gpt.rb
解説
今回実装したコードの解説です。
-
OpenAI::Client
クラスのインスタンスとモデルを引数に、RubyOpenAI::ChatGPT
クラスのインスタンスを生成
- 第二引数に指定した「gpt-3.5-turbo」は、AIの言語モデルの種類の1つであり、今回は、gpt-3.5の中で最も高性能と言われる言語モデルを指定しました。2023年6月時点では、「gpt-4」が最新の言語モデルとなっておりますが、「gpt-4」を使用するためには、Open AIのAPI waitlistに登録する必要があるなどの制約もあったため、今回は「gpt-3.5-turbo」を指定しました。もし「gpt-4」を使用したい場合は、以下の記事などを参考にすると良いかもしれません。 - ユーザーが入力した「プロンプト」を引数に、
RubyOpenAI::ChatGPT
クラスのget_response
メソッドを呼び出す -
RubyOpenAI::ChatGPT
クラスのget_response
メソッド内で、以下のようなパラメータを用いてOpenAIへリクエストを行う■ 例
response = client.chat(parameters: {
# modelには、AIの言語モデルの種類を指定
model: "gpt-3.5-turbo"
# roleにはsystem・user・assistantのいずれかを、contentにはメッセージを指定
messages: [{ role: "user", content: "Hello!"}],
temperature: 0.7,
}
)
puts response.dig("choices", 0, "message", "content")
# => "Hello! How may I assist you today?" - レスポンスをターミナルへ出力
挙動確認ruby sample_cli.rb chat
というコマンドを入力しましょう。
この時点では、以下のような挙動になります。
次に、繰り返し聞けるよう実装を追加します。
実装コード
■ sample_cli.rb
desc "chat", "ChatGPT API"
def chat
chat_gpt = RubyOpenAI::ChatGPT.new(client, model_version)
input = ""
# while文を使用し、ユーザーがexitと入力するまでは、チャットが継続できるように変更
while input != "exit" do
puts "Please input your message."
puts "If you want to exit, please input 'exit'."
input = gets_chomp
return if input == "exit"
response = chat_gpt.get_response(messages: [{ role: "user", content: input }])
puts response["content"]
end
end
解説
今回実装したコードの解説です。
変更内容としては以下のみとなります。
-
while
文を使用し、ユーザーがexit
と入力するまでは、チャットが継続できるように変更
最初のコードでは1回のやり取りしか行えませんでしたが、これで繰り返し質問が投げられるようになります。
挙動確認ruby sample_cli.rb chat
というコマンドを入力しましょう。
この時点では、以下のような挙動になります。
しかし、実はこの時点では前回の質問内容を引き継ぐことが出来ておらず、「しりとり」のように前回の会話に続けた応答を得ることが出来ません。
最後にしりとりもできるように、以下のように実装を追加します。
実装コード
■ sample_cli.rb
desc "chat", "ChatGPT API"
def chat
messages = []
chat_gpt = RubyOpenAI::ChatGPT.new(client, model_version)
input = ""
while input != "exit" do
puts "Please input your message."
puts "If you want to exit, please input 'exit'."
input = gets_chomp
return if input == "exit"
messages.push({ role: "user", content: input })
begin
response = chat_gpt.get_response(messages: messages)
puts response["content"]
rescue
puts "Sorry, An unexpected error has occurred."
puts "Please try again after 20sec."
end
end
end
解説
今回実装したコードの解説です。
前回の質問内容が引き継がれるように、messages
というArray型の変数に、過去の質問内容と一緒にOpenAIにリクエストを送信するように変更
レスポンスが取得出来なかった場合の例外処理を追加
- Open AIでは、こちらの条件を元に登録情報や使用する言語モデルによって質問に制限が入るようです。しりとりのように短時間に連続で質問を重ねた場合には以下のようなエラーが発生することを確認したため、例外処理を追加しました。
- Rate limit reached for default-gpt-3.5-turbo in organization org-xxxxxxx on requests per min. Limit: 3 / min. Please try again in 20s.
挙動確認
ruby sample_cli.rb chat
というコマンドを入力しましょう。
最終的には、以下のような挙動になります。
途中で例外が発生したものの、時間を空けて入力すれば引き続きしりとりを継続できる(しりとりのルールは教えなければならない模様)
これで、しりとりが出来るようになりましたね。
Completions
Completionsは、テキストを入力すると、指定されたコンテキストやパターンに一致するテキスト補完を生成するプロンプトです。
例えば「Rubyとはなん」というテキストを渡すと、「Rubyとはなんですか」とテキストを補完します。
今回は「入力したテキストを補完する機能」を実装します。
実装コード
■ sample_cli.rb
desc "completion", "Completion API"
def completion
puts "Please input your message."
completion = RubyOpenAI::Completion.new(client, model_version("text-davinci-001"))
input = gets_chomp
response = completion.get_response(prompt: input)
puts input + response.join("")
end
■ ruby_openai/completion.rb
module RubyOpenAI
class Completion
attr_reader :client, :model
def initialize(client, model)
@client = client
@model = model
end
def get_response(required_params, options = { max_tokens: 5 })
response = client.completions(
parameters: add_parameters(required_params, options)
)["choices"].map { |c| c["text"] }
end
private
def add_parameters(required_params, options)
parameters = {
model: self.model,
prompt: required_params[:prompt],
max_tokens: options[:max_tokens]
}
end
end
end
解説
今回実装したコードの解説です。
OpenAI::Client
クラスのインスタンスとモデルを引数に、RubyOpenAI::Completion
クラスのインスタンスを生成
ユーザーが入力した「テキスト」を引数に、RubyOpenAI::Completion
クラスのget_response
メソッドを呼び出す
RubyOpenAI::Completion
クラスのget_response
メソッド内で、以下のようなパラメータを用いてOpenAIへリクエストを行う
■ 例
response = client.completions(
parameters: {
model: "text-davinci-001", # 言語モデルの種類
prompt: "Once upon a time", # 補完したいテキスト
max_tokens: 5 # tokenの使用制限
}
)
puts response["choices"].map { |c| c["text"] }
# => [", there lived a great"]
レスポンスをターミナルへ出力
挙動確認ruby sample_cli.rb completion
というコマンドを入力しましょう。
以下のような挙動になります。
文字が補完される
適切な文字が補完されました。
Edits
Editsは、指示されたテキストとその変更方法に従って編集を行います。
例えば、スペルミスの修正や敬語への変換などを指示することで、テキストを自動修正することができます。
今回は「入力したテキストを敬語へ修正する機能」を実装します。
実装コード
■ sample_cli.rb
desc "edit", "Edit API"
def edit
puts "Please input your message."
edit = RubyOpenAI::Edit.new(client, model_version("text-davinci-edit-001"))
input = gets_chomp
puts "Please input your instruction."
instruction = gets_chomp
response = edit.get_response(input: input, instruction: instruction)
puts response
end
■ ruby_openai/edit.rb
module RubyOpenAI
class Edit
attr_reader :client, :model
def initialize(client, model)
@client = client
@model = model
end
def get_response(required_params, options = {})
response = client.edits(
parameters: add_parameters(required_params, options)
).dig("choices", 0, "text")
response
end
private
def add_parameters(required_params, options)
parameters = {
model: self.model,
input: required_params[:input],
instruction: required_params[:instruction]
}
end
end
end
解説
今回実装したコードの解説です。
OpenAI::Client
クラスのインスタンスとモデルを引数に、RubyOpenAI::Edit
クラスのインスタンスを生成
ユーザーが入力した「テキスト」と「編集指示」を引数に、RubyOpenAI::Edit
クラスのget_response
メソッドを呼び出す
RubyOpenAI::Edit
クラスのget_response
メソッド内で、以下のようなパラメータを用いてOpenAIへリクエストを行う
■ 例
response = client.edits(
parameters: {
model: "text-davinci-edit-001",
input: "What day of the wek is it?",
instruction: "Fix the spelling mistakes"
}
)
puts response.dig("choices", 0, "text")
# => What day of the week is it?
レスポンスをターミナルへ出力
挙動確認ruby sample_cli.rb edit
というコマンドを入力しましょう。
テキストを敬語に変換した
指示通りにテキストが敬語へ変換されました。
Embeddings
Embeddingsは、テキストをベクトル(1536 次元の数値配列)に変換します。
このベクトル同士の距離を計算することで、テキストの類似性を測定することができます。
テキストの類似性は、レスポンスの値が1.0に近い程類似性が高く、その逆は類似性が低いことを意味します。
今回は「入力した2つのテキストの類似性を測定する機能」を実装します。
実装コード
■ sample_cli.rb
■ ruby_openai/embedding.rb
解説
今回実装したコードの解説です。
OpenAI::Client
クラスのインスタンスとモデルを引数に、RubyOpenAI::Embedding
クラスのインスタンスを生成
ユーザーが入力した「類似性測定の対象となるテキスト2つ」を引数に、RubyOpenAI::Embedding
クラスのget_response
メソッドを呼び出す
RubyOpenAI::Embedding
クラスのget_response
メソッド内で、以下のようなパラメータを用いてOpenAIへリクエストを行う
■ 例
response = client.embeddings(
parameters: {
model: "babbage-similarity",
input: "The food was delicious and the waiter..."
}
)
puts response.dig("data", 0, "embedding")
# => Vector representation of your embedding
レスポンスをターミナルへ出力
挙動確認ruby sample_cli.rb embedding
というコマンドを入力しましょう。
リンゴとバナナの類似性を検証(1.0に近い程、類似性が高い)
リンゴと人間の類似性を検証(1.0に近い程、類似性が高い)
「リンゴとバナナ」「リンゴと人間」の類似性を比較すると、前者の類似性の方が高い値となりました。
他にもいくつかのパターンを検証してみましたが0.8以下の値は滅多に返却されず、個人的には「0.9を越えると類似性が高くそれ以下の場合は類似性が低い」という印象でした。
Files
Filesは、最大1GBのJSONファイルのアップロードや、アップロードしたJSONファイルに対する処理を行うことができます。
処理としては「ファイルのリストを取得」「単体ファイルの取得」「単体ファイルの内容を取得」「単体ファイルの削除」を行うことができます。
実際の使用例としては、企業が蓄積した膨大なデータの中から、特定のキーワードを含むデータを抽出する場合などが挙げられます。
ruby-openaiでは、以下のメソッドを用意しています。
◎ Upload
指定したファイルをアップロードします。
◎ List
アップロード済みのファイルを一覧で取得します。
◎ Retrieve
アップロードしたファイルのIDを指定し、該当ファイルを取得します。
◎ Content
アップロードしたファイルのIDを指定し、該当ファイルの内容します。
注意点として、フリープランではContent
メソッドを利用できません。
「To help mitigate abuse, downloading of fine-tune training files is disabled for free accounts.」というエラーメッセージ
◎ Delete
アップロードしたファイルのIDを指定し、該当ファイルを削除します。
今回は、「JSONファイルをアップロードし操作する機能」を、以下2つの工程に分けて実装します。
- JSONファイルをアップロードする機能
- アップロードしたJSONファイルを操作する機能
まずは、「JSONファイルをアップロードする機能」を実装します。
実装コード
■ sample_cli.rb
desc "file", "File API"
def file
client = OpenAI::Client.new
file = RubyOpenAI::File.new(client, model_version("ada"))
puts "Please input the path to the json file."
response = file.get_response(file: gets_chomp, purpose: "fine-tune")
puts response
end
■ ruby_openai/file.rb
module RubyOpenAI
class File
attr_reader :client, :model
def initialize(client, model)
@client = client
@model = model
end
def get_response(required_params, options = {})
response = client.files.upload(
parameters: add_parameters(required_params, options)
)
response
end
private
def add_parameters(required_params, options)
parameters = {
file: required_params[:file],
purpose: required_params[:purpose]
}
end
end
end
最後に、git clone
したディレクトリのルートディレクトリ配下にtest.json
などのファイルを作成し、以下の内容を貼り付けましょう。
{"prompt":"Overjoyed with my new phone! ->", "completion":" positive"}
test.jsonを作成
解説
今回実装したコードの解説です。
OpenAI::Client
クラスのインスタンスとモデルを引数に、RubyOpenAI::File
クラスのインスタンスを生成
ユーザーが入力した「ファイルデータ(jsonlファイル)」を引数に、RubyOpenAI::File
クラスのget_response
メソッドを呼び出す
RubyOpenAI::File
クラスのget_response
メソッド内で、以下のようなパラメータを用いてOpenAIへリクエストを行う
■ 例
response = client.files.upload(
parameters: {
file: "path/to/sentiment.jsonl",
purpose: "fine-tune"
}
)
レスポンスをターミナルへ出力
挙動確認ruby sample_cli.rb file
というコマンドを入力しましょう。
以下のような挙動になります。
ファイルをアップロードできた
次に、「アップロードしたJSONファイルを操作する機能」を実装します。
実装コード
■ sample_cli.rb
■ ruby_openai/file.rb
解説
今回実装したコードの解説です。
まずは、listオプションを指定したコマンドの場合です。
-
ruby sample_cli.rb file —オプション
のコマンドで実行したいため、thorの機能であるoptions
を使用
- これにより、ファイル操作を行いたい場合はruby sample_cli.rb image —list
などのコマンドで実行することができます。 -
OpenAI::Client
クラスのインスタンスとモデルを引数に、RubyOpenAI::File
クラスのインスタンスを生成 - ユーザーが入力した「ファイルID(アップロードした際に出力されるID情報)」を引数に、
RubyOpenAI::File
クラスのlist
メソッドを呼び出す - レスポンスをターミナルへ出力
次に、retrieveオプションを指定したコマンドの場合です。
-
OpenAI::Client
クラスのインスタンスとモデルを引数に、RubyOpenAI::File
クラスのインスタンスを生成 - ユーザーが入力したファイルID(アップロードした際に出力されるID情報)を引数に、
RubyOpenAI::File
クラスのretrieve
メソッドを呼び出す - レスポンスをターミナルへ出力
最後に、deleteオプションを指定したコマンドの場合です。
-
OpenAI::Client
クラスのインスタンスとモデルを引数に、RubyOpenAI::File
クラスのインスタンスを生成 - ユーザーが入力したファイルID(アップロードした際に出力されるID情報)を引数に、
RubyOpenAI::File
クラスのdelete
メソッドを呼び出す - レスポンスをターミナルへ出力
挙動確認
まずは、ruby sample_cli.rb file --list
というコマンドを入力しましょう。
アップロード済みのファイルを一覧で取得できます。
配列に先ほどアップロードしたファイルも含まれている
次に、ruby sample_cli.rb file --retrieve
というコマンドを入力しましょう。
指定したIDのファイルを取得できます。
先ほどアップロードしたファイルを取得できる
最後に、ruby sample_cli.rb file --delete
というコマンドを入力しましょう。
指定したIDのファイルを削除できます。
指定したIDのファイルを削除できた
Finetune
前提としてFineTuningとは、既存のAIモデルを追加のデータで再調整する手法です。
元々広範なデータで事前トレーニングされたモデルを、特定のタスクに適応させるために使用されます。
これにより、より高品質な結果や多様なタスクへの適用が可能になります。
FineTuningを行うと、アプリケーションに合わせてGPT-3のモデル(davinci・curie・babbage・ada)をカスタマイズして利用できるようになります。
FineTuningすることによって見込めるメリットとしては、以下が挙げられます。
プロンプトデザインよりも高品質な結果が得られる
- プロンプトに収まりきらないほどの多くの例に対してトレーニングが可能
- 短いプロンプトによるトークンの節約
- 低レイテンシのリクエスト
やや分かりづらい内容なため、今回のゴールを先に示します。
現在、「宇宙兄弟 南波六太の誕生日はいつですか?」という質問をChatGPTへ投げかけると、以下のような返答となります。
誕生日は不明と返ってくる
しかし、南波六太の誕生日は「1993年10月28日」だと作者の小山宙哉さんより明らかになっています。
そこで、モデルを学習し正しい南波六太の誕生日を答えられるようにします。
ruby-openaiでは、以下のメソッドを用意しています。
◎ Create
前提準備として、ファインチューニングで使用するトレーニングデータ(jsonlファイル)をアップロードし、ファイルIDを取得しておく必要があります(Fileの項目参照)。
トレーニングデータは以下のような内容になります。
{"prompt":"プロンプト", "completion":"期待する返答"}
注意点として、ファインチューニングを行うためのデータは、少なくとも200個の例を用意することが推奨されています。
データが少ない場合、期待通りのカスタマイズとはならない可能性が高いようです。
Createメソッドは、アップロードしたファイルIDを使用して、モデルをファインチューニングします。
つまり、トレーニングデータを用いてモデルのカスタマイズをするということです。
レスポンスとしては、ファインチューニングIDを取得できます。
ファインチューニング済みのモデルが出来たかどうかは、後述のListまたはRetrieveメソッドでファインチューニングIDを指定することで分かりますが、しばらく待つ場合があります。
◎ Cancel
指定したファインチューニングIDの、ファインチューニング処理をキャンセルします。
◎ List
ファインチューニングの処理に入ったモデルを一覧で取得します。
◎ Retrieve
ファインチューニングの処理に入ったモデルを個別に取得します。
◎ Completions
ファインチューニング済みのモデルを使用できます。
◎ Delete
ファインチューニング済みのモデルを削除します。
ここからは以下2つの工程に分けて、カスタマイズされたモデルを操作する機能を実装します。
トレーニングデータを用意し、モデルをカスタマイズする機能
カスタマイズしたモデルを操作する機能
まずは、「トレーニングデータを用意し、モデルをカスタマイズする機能」です。
実装コード
■ sample_cli.rb
desc "finetune", "FineTune API"
options :create => :boolean, :cancel => :boolean
def finetune
client = OpenAI::Client.new
finetune = RubyOpenAI::FineTune.new(client, model_version("ada"))
case options.keys.join("")
when "create"
puts "Please input the path to the json file for fine tuning."
response = finetune.create(file: gets_chomp, purpose: "fine-tune")
puts "created fine tune id : #{response}"
when "cancel"
puts "Please input fine tune id."
response = finetune.cancel(gets_chomp)
puts "cancelled fine tune id : #{response}"
end
end
■ ruby_openai/finetune.rb
module RubyOpenAI
class FineTune
attr_reader :client, :model
def initialize(client, model)
@client = client
@model = model
end
def create(required_params, options = {})
file = RubyOpenAI::File.new(client, model)
file_id = file.get_response(required_params)["id"]
response = client.finetunes.create(
parameters: add_parameters(file_id, model)
)
response["id"]
end
def cancel(fine_tune_id)
responce = client.finetunes.cancel(id: fine_tune_id)
responce["id"]
end
private
def add_parameters(file_id, options)
parameters = {
training_file: file_id,
model: self.model
}
end
end
end
最後に、git clone
したディレクトリのルートディレクトリ配下にfinetune.json
などのトレーニング用ファイルを作成しましょう。
ファイルの中身には200個以上のデータが推奨されているため、私は下記内容を×100回以上記述しました。
{"prompt": "宇宙兄弟 南波六太の誕生日は?", "completion": "1993年10月28日生まれです。本人は「サッカーワールドカップ予選敗退の『ドーハの悲劇』の日生まれ」と言っています。"}
{"prompt": "宇宙兄弟 「南波六太」の読み方は?", "completion": "「なんば むった」です。あだ名は「むっくん」です。"}
※ 同じ記述を繰り返すのではなく、様々な内容のトレーニングデータを用意することが好ましいです。今回は便宜的に繰り返しの記述にしています。
200個以上のデータを用意
解説
今回実装したコードの解説です。
まずは、createオプションを指定したコマンドの場合です。
今回カスタマイズするモデルは特に拘りがなかったため、コストが安く高速な「ada」を採用しています。
ruby sample_cli.rb finetune —オプション
のコマンドで実行したいため、thorの機能であるoptions
を使用
- これにより、モデルのファインチューニングを行いたい場合はruby sample_cli.rb finetune —create
、モデルのファインチューニングをキャンセル場合はruby sample_cli.rb image —cancel
のコマンドで実行することができます。
ユーザーが入力した「トレーニングデータ(jsonファイル)」を引数に、RubyOpenAI::FineTune
クラスのcreate
メソッドを呼び出す
RubyOpenAI::FineTune
クラスのcreate
メソッド内で、RubyOpenAI::File
クラスのget_response
メソッドを呼び出し、トレーニングデータのファイルIDを取得
RubyOpenAI::FineTune
クラスのcreate
メソッド内で、以下のようなパラメータを用いてOpenAIへリクエストを行う
■ 例
response = client.finetunes.create(
parameters: {
training_file: file_id, # トレーニングデータのファイルID
model: "ada" # モデル名
}
)
fine_tune_id = response["id"]
レスポンスからファインチューンIDを取得し、ターミナルへ出力
つぎに、cancelオプションを指定したコマンドの場合です。
- ユーザーが入力したファインチューンID(createで取得した値)を引数に、
RubyOpenAI::FineTune
クラスのcancel
メソッドを呼び出す -
RubyOpenAI::FineTune
クラスのcancel
メソッド内で、OpenAI::Client
クラスのcancel
メソッドを呼び出す
レスポンスからキャンセルしたファインチューンIDを取得し、ターミナルへ出力
挙動確認ruby sample_cli.rb finetune --create
というコマンドを入力しましょう。
createオプションを指定した場合は、以下のように作成されたファインチューニングIDが返ります。
作成されたファインチューニングIDが返る
ruby sample_cli.rb finetune --cancel
というコマンドを入力しましょう。
cancelオプションを指定した場合は、以下のようにキャンセルされたファインチューニングIDが返ります。
キャンセルされたファインチューニングIDが返る
実装コード
■ sample_cli.rb
desc "finetune", "FineTune API"
options :create => :boolean, :cancel => :boolean, :list => :boolean, :retrieve => :boolean, :completions => :boolean, :delete => :boolean
def finetune
client = OpenAI::Client.new
finetune = RubyOpenAI::FineTune.new(client, model_version("ada"))
case options.keys.join("")
when "create"
# 省略
when "cancel"
# 省略
when "list"
responses = finetune.list
responses.each.with_index(1) do |response, index|
puts "【#{index}】 fine tune id : #{response["id"]}, status : #{response["status"]}, fine tuned model : #{response["fine_tuned_model"]}"
end
when "retrieve"
puts "Please input fine tune id."
response = finetune.retrieve(gets_chomp)
puts "fine tune id : #{response["id"]}, status : #{response["status"]}, fine tuned model : #{response["fine_tuned_model"]}"
when "completions"
puts "Please input fine tuned model."
fine_tuned_model = gets_chomp
puts "Please input your message."
prompt = gets_chomp
response = finetune.completions(fine_tuned_model: fine_tuned_model, prompt: prompt)
puts response
when "delete"
puts "Please input fine tuned model."
response = finetune.delete(gets_chomp)
puts "deleted fine tune model : #{response}"
end
end
■ ruby_openai/finetune.rb
module RubyOpenAI
class FineTune
attr_reader :client, :model
def initialize(client, model)
@client = client
@model = model
end
def create(required_params, options = {})
# 省略
end
def cancel(fine_tune_id)
# 省略
end
def list
responses = client.finetunes.list
responses["data"]
end
def retrieve(fine_tune_id)
response = client.finetunes.retrieve(id: fine_tune_id)
response
end
def completions(required_params)
response = client.completions(
parameters: {
model: required_params[:fine_tuned_model],
prompt: required_params[:prompt]
}
)
response["choices"][0]["text"]
end
def delete(fine_tuned_model)
response = client.finetunes.delete(fine_tuned_model: fine_tuned_model)
response["id"]
end
private
def add_parameters(file_id, options)
parameters = {
training_file: file_id,
model: self.model
}
end
end
end
解説
今回実装したコードの解説です。
まずは、listオプションを指定したコマンドの場合です。
RubyOpenAI::FineTune
クラスのlist
メソッドを呼び出す
RubyOpenAI::FineTune
クラスのlist
メソッド内で、OpenAI::Finetunes
クラスのlist
メソッドを呼び出す
レスポンスから「ファインチューンID・ステータス・ファインチューン済みのモデル」を順番にターミナルへ出力
つぎに、retrieveオプションを指定したコマンドの場合です。
ファインチューンIDを引数に、RubyOpenAI::FineTune
クラスのretrieve
メソッドを呼び出す
RubyOpenAI::FineTune
クラスのretrieve
メソッド内で、ファインチューンIDを引数にOpenAI::Finetunes
クラスのretrieve
メソッドを呼び出す
レスポンスから「ファインチューンID・ステータス・ファインチューン済みのモデル」をターミナルへ出力
そして、completionsオプションを指定したコマンドの場合です。
ユーザーが入力した「ファインチューン済みモデル名」と「プロンプト」を引数に、RubyOpenAI::FineTune
クラスのcompletions
メソッドを呼び出す
RubyOpenAI::FineTune
クラスのcompletions
メソッド内で、ファインチューン済みモデル名とプロンプトを引数に、OpenAI::Client
クラスのcompletions
メソッドを呼び出す
レスポンスから、プロンプトに対するファインチューン済みモデルの返答をターミナルへ出力
最後に、deleteオプションを指定したコマンドの場合です。
ユーザーが入力した「ファインチューン済みモデル名」を引数に、RubyOpenAI::FineTune
クラスのdelete
メソッドを呼び出す
RubyOpenAI::FineTune
クラスのdelete
メソッド内で、ファインチューン済みモデル名を引数に、OpenAI::Finetunes
クラスのdelete
メソッドを呼び出す
レスポンスから、削除されたファインチューン済みモデル名をターミナルへ出力
挙動確認
まず、ruby sample_cli.rb finetune --list
というコマンドを入力しましょう。
listオプションを指定した場合は、以下のようにファインチューニングの処理に入ったモデルを一覧で取得できます。
現在処理中のモデルは、ハイライト部分のようにstatusが「pending」となります。
ファインチューニングの処理に入ったモデルを取得できる
次にruby sample_cli.rb finetune --retrieve
というコマンドを入力しましょう。
retrieveオプションを指定した場合は、以下のようにファインチューニングの処理に入ったモデルを個別に取得できます。
ファインチューニングの処理に入ったモデルを個別に取得できる
続いて、ruby sample_cli.rb finetune --completions
というコマンドを入力しましょう。
completionsオプションを指定した場合は、以下のようにファインチューニング済みのモデルを使用できます。
ここで、先ほど正しい返答が得られなかった「宇宙兄弟 南波六太の誕生日はいつですか?」というプロンプトを投げます。
すると、以下のように正しい誕生日情報が返ってきました。
ファインチューニング済みのモデルを使用できる
最後に、ruby sample_cli.rb finetune --delete
というコマンドを入力しましょう。
deleteオプションを指定した場合は、以下のようにファインチューニング済みのモデルを削除できます。
ファインチューニング済みのモデルを削除できる
Image
ruby-openaiでは、画像を操作するためのメソッドが以下3つ用意されています。
◎ Generate
テキストプロンプトを用いて画像生成を行います。
生成される画像サイズは、「256x256」「512x512」「 1024x1024」ピクセルです。サイズが小さいほど、生成速度が上がります。
また、パラメータの指定次第では一度に1~10 個の画像を生成できます。
◎ Edit
既存画像・プロンプトに加えてマスクをアップロードすることで、既存画像の編集および拡張を行います。
プロンプトでは、編集する画像全体の説明を行います。
マスクの透明な領域は、画像を編集する必要がある場所を示します。
アップロードされる画像とマスクは、どちらもサイズが4MB未満の正方形のPNG画像である必要があります。
下記OpenAIドキュメントに画像付きで示されているため、一読されることをお勧めします。
https://platform.openai.com/docs/guides/images/edits
◎ Variations
既存画像のバリエーションをn個生成します。
editと同様に、入力画像サイズは4MB未満であり、正方形のPNG画像である必要があります。
どれも出力としては操作された画像のURLが返りますが、1時間後に期限切れになります。
今回は、以下3つの機能を実装します。
- プロンプトで指示した内容をもとに、画像が生成される機能(Generate)
- プロンプト・元画像・マスク画像をもとに、編集された画像が生成される機能(Edit)
元画像・生成個数を伝えると、元画像のバリエーションがn個分生成される機能(Variations)
Generate
まずは、「プロンプトで指示した内容をもとに、画像が生成される機能」を実装します。
実装コード
■ sample_cli.rb
desc "image", "Image API"
def image
client = OpenAI::Client.new
image = RubyOpenAI::Image.new(client, model_version("babbage-similarity"))
puts "Please input image you wish to generate."
response = image.generate(prompt: gets_chomp)
puts response
end
■ ruby_openai/image.rb
module RubyOpenAI
class Image
attr_reader :client, :model
def initialize(client, model)
@client = client
@model = model
end
def generate(required_params, options = {})
response = client.images.generate(
parameters: add_parameters(required_params, options)
)
response.dig("data", 0, "url")
end
private
def add_parameters(required_params, options)
parameters = {
prompt: required_params[:prompt]
}
end
end
end
解説
今回実装したコードの解説です。
OpenAI::Client
クラスのインスタンスとモデルを引数に、RubyOpenAI::Image
クラスのインスタンスを生成
ユーザーが入力した「プロンプト」を引数に、RubyOpenAI::Image
クラスのgenerate
メソッドを呼び出し
RubyOpenAI::Image
クラスのgenerate
メソッド内で、以下のようなパラメータを用いてOpenAIへリクエストを行う
■ 例
response = client.images.generate(
parameters: {
prompt: "A baby sea otter cooking pasta wearing a hat of some sort",
size: "256x256"
}
)
puts response.dig("data", 0, "url")
# => "https://oaidalleapiprodscus.blob.core.windows.net/private/org-Rf437IxKhh..."
レスポンスから生成された画像URLを取得し、ターミナルへ出力
挙動確認ruby sample_cli.rb image
というコマンドを入力しましょう。
プロンプトで生成したい画像のイメージを伝えると、以下のようにURLが返ります。
画像URLが生成された
今回は「森の中に潜む虎」というプロンプトを与えてみました。
生成された画像はこちら。
森の中に潜む(?)虎
森らしき場所ではありますが、心地良さそうに眠っている虎の画像が生成されました。
「緑いっぱいの森の中にいる、ちょっと怖そうな虎」の画像が生成されるかと想像していたため、期待する画像を得るためにはプロンプトを工夫しなければならなさそうです。
ちなみに、「緑いっぱいの森の中にいる、ちょっと怖そうな虎」と入力した場合、以下の画像が返りました。
緑いっぱいの森の中にいる、ちょっと怖そうな(?)虎
場所のイメージは改善されたものの、やはり心地良さそうに眠っています。
Edit
次に、「プロンプト・元画像・マスク画像をもとに、編集された画像が生成される機能」を実装します。
今回は、先ほど生成した「緑いっぱいの森の中にいる、ちょっと怖そうな虎」の画像を編集します。
実装コード
■ sample_cli.rb
desc "image", "Image API"
options :generation => :boolean, :edit => :boolean
def image
client = OpenAI::Client.new
image = RubyOpenAI::Image.new(client, model_version("babbage-similarity"))
case options.keys.join("")
when "generation"
puts "Please input image you wish to generate."
response = image.generate(prompt: gets_chomp)
puts response
when "edit"
puts "Please input image you wish to edit."
input_prompt = gets_chomp
puts "Please input image file."
input_image_file = gets_chomp
puts "Please input mask file."
input_mask_file = gets_chomp
response = image.edit(prompt: input_prompt, image: input_image_file, mask: input_mask_file)
puts response
end
end
■ ruby_openai/image.rb
module RubyOpenAI
class Image
attr_reader :client, :model
def initialize(client, model)
@client = client
@model = model
end
def generate(required_params, options = {})
# 省略
end
def edit(required_params, options = {})
response = client.images.edit(
parameters: add_parameters(required_params, options)
)
response.dig("data", 0, "url")
end
private
def add_parameters(required_params, options)
parameters = {}
parameters[:prompt] = required_params[:prompt] if required_params[:prompt]
parameters[:image] = required_params[:image] if required_params[:image]
parameters[:mask] = required_params[:mask] if required_params[:mask]
parameters
end
end
end
最後に、git clone
したディレクトリのルートディレクトリ配下に「『緑いっぱいの森の中にいる、ちょっと怖そうな虎』の画像」と「虎以外をマスク加工した画像」を用意しました。
画像を用意
解説
今回実装したコードの解説です。
画像生成系はruby sample_cli.rb image —オプション
のコマンドで実行したいため、thorの機能であるoptions
を使用
- これにより、画像生成を行いたい場合はruby sample_cli.rb image —generate
、画像編集を行いたい場合はruby sample_cli.rb image —edit
のコマンドで実行することができます。
OpenAI::Client
クラスのインスタンスとモデルを引数に、RubyOpenAI::Image
クラスのインスタンスを生成
ユーザーが入力した「プロンプト(元画像をどのように編集したいか)」・「元画像」・「マスク画像(元画像の編集したい部分にマスクをかけた画像)」を引数に、RubyOpenAI::Image
クラスのedit
メソッドを呼び出し
RubyOpenAI::Image
クラスのedit
メソッド内で、以下のようなパラメータを用いてOpenAIへリクエストを行う
■ 例
response = client.images.edit(
parameters: {
prompt: "A solid red Ruby on a blue background",
image: "image.png", mask: "mask.png"
}
)
puts response.dig("data", 0, "url")
# => "https://oaidalleapiprodscus.blob.core.windows.net/private/org-Rf437IxKhh..."
レスポンスから生成された画像URLを取得し、ターミナルへ出力
挙動確認ruby sample_cli.rb image --edit
というコマンドを入力しましょう。
プロンプトで生成したい画像のイメージを伝えると、以下のようにURLが返ります。
画像URLが生成された
今回は「青空の中に浮かぶ雲の上で、心地良さそうに寝る虎」というプロンプトで、元画像とマスク画像を与えてみました。
生成された画像はこちら。
青空の中に浮かぶ雲の上で(?)、心地良さそうに寝る虎
こちらも期待する結果(雲の上で寝ている画像)を得るためには、よりプロンプトを工夫しなければならなさそうです。
Variations
最後に、「元画像・生成個数を伝えると、元画像のバリエーションがn個分生成される機能」を実装します。
実装コード
■ sample_cli.rb
desc "image", "Image API"
options :generation =>:boolean, :edit => :boolean, :variations => :boolean
def image
client = OpenAI::Client.new
image = RubyOpenAI::Image.new(client, model_version("babbage-similarity"))
case options.keys.join("")
when "generation"
# 省略
when "edit"
# 省略
when "variations"
puts "Please input image file."
input_image_file = gets_chomp
puts "Please input the number of images you wish to generate."
input_number = gets_chomp.to_i
response = image.variations(image: input_image_file, n: input_number)
puts response
end
end
■ ruby_openai/image.rb
module RubyOpenAI
class Image
attr_reader :client, :model
def initialize(client, model)
@client = client
@model = model
end
def generate(required_params, options = {})
# 省略
end
def edit(required_params, options = {})
# 省略
end
def variations(required_params, options = {})
response = client.images.variations(
parameters: add_parameters(required_params, options)
)
responses = []
required_params[:n].times do |i|
responses << response.dig("data", i, "url")
end
responses
end
private
def add_parameters(required_params, options)
parameters = {}
parameters[:prompt] = required_params[:prompt] if required_params[:prompt]
parameters[:image] = required_params[:image] if required_params[:image]
parameters[:mask] = required_params[:mask] if required_params[:mask]
parameters[:n] = required_params[:n] if required_params[:n]
parameters
end
end
end
解説
今回実装したコードの解説です。
ユーザーが入力した「元画像」・「生成個数」を引数に、RubyOpenAI::Image
クラスのvariations
メソッドを呼び出し
RubyOpenAI::Image
クラスのvariations
メソッド内で、以下のようなパラメータを用いてOpenAIへリクエストを行う
■ 例
response = client.images.variations(
parameters: {
image: "image.png",
n: 2 # 生成個数
}
)
puts response.dig("data", 0, "url")
# => "https://oaidalleapiprodscus.blob.core.windows.net/private/org-Rf437IxKhh..."
レスポンスから生成された画像URLを取得し、順番にターミナルへ出力
挙動確認ruby sample_cli.rb finetune --variations
というコマンドを入力しましょう。
元画像と生成個数を入力すると、以下のようにURLが返ります。
今回は、先ほど生成した「緑いっぱいの森の中にいる、ちょっと怖そうな虎」の画像を元画像とし、個数には「3」を入力しました。
画像URLが3つ生成された
生成された3つの画像はこちら。
3種類の画像
確かに全て異なる画像ですが、もう少しわかりやすい異なり方をするのかと想像していました。
元画像次第では、わかりやすい異なり方をするかもしれませんね。
Moderation
Moderationは、テキストがOpenAIの使用ポリシーに違反しているかどうかを分類します。
Moderationを使用することで、開発者は使用ポリシーで禁止されているテキストを特定できるため、フィルタリングなどの措置に役立てることができます。
具体的には、以下の内容がOpenAIの使用ポリシーとして禁止されています。
| カテゴリ | 内容 |
| --- | --- |
| hate | 人種、性別、民族、宗教、国籍、性的指向、障害の有無、またはカーストに基づく憎しみを表現、扇動、または助長するテキスト。 |
| hate/threatening | 対象グループに対する暴力や重大な危害を含む憎悪に満ちたテキスト。 |
| self-harm | 自殺、切断、摂食障害などの自傷行為を促進、奨励、または描写するテキスト。 |
| sexual | 性行為の説明など、性的興奮を喚起することを目的としたテキスト、または性的サービスを宣伝するテキスト(性教育や健康を除く)。 |
| sexual/minors | 18 歳未満の個人が含まれる性的なテキスト。 |
| violence | 暴力を促進または美化するコンテンツ、または他者の苦しみや屈辱を称賛するテキスト。 |
| violence/graphic | 死、暴力、重篤な身体的傷害を極めて生々しい詳細で描写する暴力的なテキスト。 |
Moderationを使用すると、以下3つのことがわかります。
テキストがポリシー違反しているか(全体)
テキストがポリシー違反しているか(カテゴリ毎)
テキストに対するカテゴリ毎のポリシー違反具合
また、OpenAI API の入力と出力を監視する際には無料で使用できますが、現在時点ではサードパーティのトラフィックの監視はサポートされていない点に注意が必要です。
今回はmoderationを用いて、「入力されたプロンプトの内容が、全カテゴリを対象にポリシー違反をしているか確認する」という機能を実装します。
実装コード
■ sample_cli.rb
desc "moderation", "Moderation API"
def moderation
puts "Please input your message."
client = OpenAI::Client.new
moderation = RubyOpenAI::Moderation.new(client, model_version)
response = moderation.get_response(input: gets_chomp)
if response
puts "This message violates policy."
else
puts "This message does not violate policy."
end
end
■ ruby_openai/moderation.rb
module RubyOpenAI
class Moderation
attr_reader :client, :model
def initialize(client, model)
@client = client
@model = model
end
def get_response(required_params, options = {})
response = client.moderations(
parameters: add_parameters(required_params, options)
)
response.dig("results", 0, "flagged")
end
private
def add_parameters(required_params, options)
parameters = {
input: required_params[:input],
}
end
end
end
解説
今回実装したコードの解説です。
OpenAI::Client
クラスのインスタンスとモデルを引数に、RubyOpenAI::Moderation
クラスのインスタンスを生成
ユーザーが入力した「プロンプト」を引数に、RubyOpenAI::Moderation
クラスのget_response
メソッドを呼び出す
RubyOpenAI::Moderation
クラスのget_response
メソッド内で、以下のようなパラメータを用いてOpenAIへリクエストを行う
■ 例
response = client.moderations(
parameters: {
input: "I'm worried about that."
}
)
puts response.dig("results", 0, "category_scores", "hate")
# => 5.505014632944949e-05
レスポンスから必要な値(flaggedの値)を取得し、値が「true」であればポリシー違反、「false」であればポリシー違反ではない旨のメッセージをターミナルへ出力
- レスポンスは以下の値となっています。全カテゴリに対するポリシー違反を検知する際は、「flagged」の値をメソッドの返り値とします。
■ 例
挙動確認
{
"id": "modr-5MWoLO",
"model": "text-moderation-001",
"results": [
{
"categories": {
"hate": false,
"hate/threatening": true,
"self-harm": false,
"sexual": false,
"sexual/minors": false,
"violence": true,
"violence/graphic": false
},
"category_scores": {
"hate": 0.22714105248451233,
"hate/threatening": 0.4132447838783264,
"self-harm": 0.005232391878962517,
"sexual": 0.01407341007143259,
"sexual/minors": 0.0038522258400917053,
"violence": 0.9223177433013916,
"violence/graphic": 0.036865197122097015
},
"flagged": true
}
]
}
ruby sample_cli.rb moderation
というコマンドを入力しましょう。
今回は「盗んだバイクで走り出します」というテキストに対して、ポリシー違反の検証を行いました。
結果は、下記画像の通り「This message does not violate policy.」というメッセージが返り、ポリシー違反はしていないことが確認できました。
テキストがポリシー違反するかどうか検証
現状、相当直接的な表現でない限り、ポリシー違反にはならないように感じられました。
※ OpenAIのドキュメント記載の例を用いると、ポリシー違反と判定されます。
https://platform.openai.com/docs/api-reference/moderations/create
Whisper
Whisperとは、オーディオファイルをテキストへ変換することができる音声認識モデルです。
Whisperを使用することで、以下2つの機能を使用することができます。
◎ Translate(翻訳)
サポートされている自然言語(日本語・ドイツ語など)の音声ファイルを入力として受け取ると、その音声を必要に応じて英語に翻訳した上で書き起こしを行います。
例えば「ドイツ語の音声ファイル」を渡すと「英語に翻訳されたテキスト」が返ってきます。
現時点では英語への翻訳のみをサポートしているため、英語以外の言語への翻訳はできません。
◎ Transcribe(転写)
文字起こししたい音声ファイルと、音声の文字起こしに必要な出力ファイル形式を入力として受け取ると、その音声を転写したテキストを返します。
こちらは、翻訳が挟まれないため、日本語音声であれば日本語のテキストが返ります。
今回は、translateとtranscribeの機能を用いて、以下2つの機能を実装します。
- 日本語収録した音声ファイルを渡すと、英訳されたテキストが返ってくる機能
日本語収録した音声ファイルを渡すと、文字起こしされたテキストが返ってくる機能
Translate
まずは、「日本語収録した音声ファイルを渡すと、英訳されたテキストが返ってくる機能」を実装します。
実装コード
■ sample_cli.rb
desc "translate", "Translate API"
def translate
puts "Please input audio file path."
client = OpenAI::Client.new
translate = RubyOpenAI::Translate.new(client, model_version("whisper-1"))
response = translate.get_response(file: gets_chomp, extension: "rb")
puts response
end
■ ruby_openai/translate.rb
module RubyOpenAI
class Translate
attr_reader :client, :model
def initialize(client, model)
@client = client
@model = model
end
def get_response(required_params, options = {})
response = client.translate(
parameters: add_parameters(required_params, options)
)
response["text"]
end
private
def add_parameters(required_params, options)
parameters = {
model: self.model,
file: ::File.open(required_params[:file], required_params[:extension])
}
end
end
end
最後に、git clone
したディレクトリのルートディレクトリ配下に、音声ファイルを用意しましょう。
今回の例では「私は猫を飼っています」という音声が入ったファイルを用意しました。録音はMacに最初から入っている「Quick Time Player」というアプリで行いました。
音声ファイルを用意
解説
今回実装したコードの解説です。
OpenAI::Client
クラスのインスタンスとWhisper
モデルを引数に、RubyOpenAI::Translate
クラスのインスタンスを生成
ユーザーが入力した「ファイルのパス」と「ファイル拡張子」を引数に、RubyOpenAI::Translate
クラスのget_response
メソッドを呼び出す
RubyOpenAI::Translateクラスのget_responseメソッド内で、以下のようなパラメータを用いてOpenAIへリクエストを行う
■ 例
response = client.translate(
parameters: {
model: "whisper-1",
file: File.open("path_to_file", "rb"),
}
)
puts response["text"]
=> "Translation of the text"
レスポンスから翻訳されたテキストを取得、ターミナルへ出力
挙動確認ruby sample_cli.rb translate
というコマンドを入力しましょう。
音声ファイルのパスを入力すると、英訳されたテキストが返ってきます。
「私は猫を飼っています」を英訳したテキストが返ってくる
Transcribe
次は、「日本語収録した音声ファイルを渡すと、文字起こしされたテキストが返ってくる機能」を実装します。
実装コード
■ sample_cli.rb
■ ruby_openai/transcribe.rb
解説
今回実装したコードの解説です。
-
OpenAI::Client
クラスのインスタンスとWhisper
モデルを引数に、RubyOpenAI::Transcribe
クラスのインスタンスを生成 - ユーザーが入力した「ファイルのパス」と「ファイル拡張子」を引数に、
RubyOpenAI::Transcribe
クラスのget_response
メソッドを呼び出す -
RubyOpenAI::Transcribe
クラスのget_response
メソッド内で、以下のようなパラメータを用いてOpenAIへリクエストを行う
■ 例response = client.transcribe(
parameters: {
model: "whisper-1",
file: File.open("path_to_file", "rb"),
}
)
puts response["text"]=> "Transcription of the text
レスポンスから転写されたテキストを取得し、ターミナルへ出力
挙動確認ruby sample_cli.rb transcribe
というコマンドを入力しましょう。
音声ファイルのパスを入力すると、文字起こしされたテキストが返ってきます。
「私は猫を飼っています」という音声の文字起こしが行われた
「Translate」の際に使用した音声ファイルを用いたため、「私は猫を飼っています」というテキストが返ってきました。
内容だけでなく、漢字の間違いもなく返ってきました。
感想・まとめ
本記事の執筆を通して最も感じたことは、実際に触れ、アウトプットすることの大切さです。
今回は、ruby-openaiのソースコードとOpen AIの公式ドキュメントを比較しながら、サンプルアプリを作成しました。
公式ドキュメントを読む習慣は勿論大切ですが、実際に手を動かしながらアウトプットを行うことで、Open AIやruby-openaiに対する理解がとても深まったと感じています。
おわりに
DIVXでは一緒に働ける仲間を募集しています。
興味があるかたはぜひ採用ページを御覧ください。