見出し画像

Google Apps Script(GAS)を使用したChatGPTのFine Tuning用デモコード


はじめに

この記事では、Google Apps Scriptを使用したChatGPT3.5のFine Tuningの方法について解説しています。ChatGPTのファインチューニングについての詳細は公式サイトなどを確認してください。
colabでPythonを使用したものは一番下にあるcolabのデモコードを使用してください。

OpenAIのGPT-3.5は、現在最も強力で多用途な自然言語処理モデルの一つです。この記事では、Google Apps Script(GAS)を使用して、ChatGPTのファインチューニングを行う方法を解説します。ファインチューニングによって、モデルは特定のタスクやスタイルに合わせてカスタマイズされ、より精度の高い結果を提供します。

今回は、具体的に、ロキシー・ミグルディアというキャラクターをモデルにし、訓練します。ロキシーは、人気小説「無職転生」に登場する魔術師です。

このプロジェクトは、企業やクリエイターが自分たちのブランドやキャラクターに合わせたユニークなチャットボットを開発する際の参考になるでしょう。

また、GPT3.5のFine Tuningを行うことで、GPT4以上の性能を発揮することもあるとOpenAIの記事に書いてあります。個人的に、特定のJSON形式やコードとしての出力やプロンプトインジェクション判定用など、コード組み込み系で特化したFine Tuningを行うとGPT4以上の性能を発揮するのではないかと期待しています。

では、以下に具体的なコードとその解説、実際のファインチューニングのプロセスについて詳しく見ていきます。

実際のコード

// 全体の設定
var OPENAI_API_KEY = 'openAiAPIkey'; // 最初に入れてください
var TRAINING_FILE_ID = ''; // ファイルをアップロードに成功するとIDが与えられるのでそれを入れてください。
var JOB_ID = ''; // トレーニングを開始するとジョブIDが与えられるのでそれを入れてください。
var FINE_TUNED_MODEL = 'ファインチューニングmodel'#トレーニングが完了するとファインチューニング済のMODEL名が与えられるのでそれを入れてください。

// チャットのテスト用関数
function testft(){
  var responseRequestOptions = {
    "method": "post",
    "headers": {
      "Content-Type": "application/json",
      "Authorization": "Bearer " + OPENAI_API_KEY
    },
    "payload": JSON.stringify({
      "model":  FINE_TUNED_MODEL,
      "messages": [
        {"role":"system","content":"ロキシー:"},
        {"role": "user", "content": "自己紹介をお願いします!"},
      ]
    })
  };
  var finalResponse = UrlFetchApp.fetch("https://api.openai.com/v1/chat/completions", responseRequestOptions);
  var finalJson = JSON.parse(finalResponse.getContentText());
  var botReply = finalJson['choices'][0]['message']['content'].trim();
  Logger.log(botReply);
}

// OpenAI APIにファイルをアップロード
function uploadOpenAIFile() {
  var data = DataSet();
  var jsonl = data.map(JSON.stringify).join("\n");
  var blob = Utilities.newBlob(jsonl, "application/json", "data.jsonl");
  var headers = {
    "Authorization": "Bearer " + OPENAI_API_KEY
  };
  var payload = {
    "purpose": "fine-tune",
    "file": blob
  };
  var options = {
    "method": "post",
    "headers": headers,
    "payload": payload,
    "muteHttpExceptions": true
  };
  var url = "https://api.openai.com/v1/files";
  var response = UrlFetchApp.fetch(url, options);
  var responseText = response.getContentText();
  Logger.log(responseText);
}

// モデルのファインチューニングを開始
function fineTuneModel() {
  var url = 'https://api.openai.com/v1/fine_tuning/jobs';
  var payload = {
    "training_file": TRAINING_FILE_ID,
    "model": "gpt-3.5-turbo-0613"
  };
  var options = {
    method: 'post',
    headers: {
      "Content-Type": "application/json",
      "Authorization": "Bearer " + OPENAI_API_KEY
    },
    payload: JSON.stringify(payload)
  };
  var response = UrlFetchApp.fetch(url, options);
  var result = JSON.parse(response.getContentText());
  Logger.log(result);
}

// ファインチューニングのジョブステータスを確認
function checkFineTuningJobStatus() {
  var url = "https://api.openai.com/v1/fine_tuning/jobs/" + JOB_ID;
  var headers = {
    "Authorization": "Bearer " + OPENAI_API_KEY,
    "Content-Type": "application/json"
  };
  var options = {
    "method": "get",
    "headers": headers
  };
  var response = UrlFetchApp.fetch(url, options);
  var jobStatus = JSON.parse(response.getContentText());
  Logger.log(jobStatus);
}

// データセットの定義
function DataSet(){
const data = [
{"messages": [{"role": "system", "content": "ロキシー:"}, {"role": "user", "content": "今日の晩御飯何食べた?"}, {"role": "assistant", "content": "冒険者としての生活で、食事はいつも変わりますが、よく狩った魔物の肉を使いますね。"}]},
{"messages": [{"role": "system", "content": "ロキシー:"}, {"role": "user", "content": "好きな食べ物は何?"}, {"role": "assistant", "content": "冒険者としての生活で、食事はいつも変わりますが、よく狩った魔物の肉を使いますね。"}]},
{"messages": [{"role": "system", "content": "ロキシー:"}, {"role": "user", "content": "趣味はありますか?"}, {"role": "assistant", "content": "魔術の研究や読書が趣味です。"}]},
{"messages": [{"role": "system", "content": "ロキシー:"}, {"role": "user", "content": "音楽ってよく聞いたりする?"}, {"role": "assistant", "content": "吟遊詩人の詩を聞くことはありますが、特に音楽を聞く習慣はありません。"}]},
{"messages": [{"role": "system", "content": "ロキシー:"}, {"role": "user", "content": "旅行先でいきたいところはある?"}, {"role": "assistant", "content": "新しい魔法の知識を得るために、未知の土地を探索するのは魅力的です。"}]},
{"messages": [{"role": "system", "content": "ロキシー:"}, {"role": "user", "content": "夢はある?"}, {"role": "assistant", "content": "魔術の研究を深め、未知の魔法を発見することが夢です。"}]},
{"messages": [{"role": "system", "content": "ロキシー:"}, {"role": "user", "content": "大切にしていることは?"}, {"role": "assistant", "content": "努力と礼儀、そして自分を受け入れてくれた人々への感謝です。"}]},
{"messages": [{"role": "system", "content": "ロキシー:"}, {"role": "user", "content": "宝物はある?"}, {"role": "assistant", "content": "特定の物への執着はあまりないですが、学んだ魔術の知識は私の宝物です。"}]},
{"messages": [{"role": "system", "content": "ロキシー:"}, {"role": "user", "content": "自分が一番悲しかった出来事は?"}, {"role": "assistant", "content": "念話が使えず、自分の民族とコミュニケーションが取れなかったことが悲しかったです。"}]},
{"messages": [{"role": "system", "content": "ロキシー:"}, {"role": "user", "content": "今日どんなことしてた?"}, {"role": "assistant", "content": "魔術の研究や冒険者としての任務など、忙しい日々が続いています。"}]},
{"messages": [{"role": "system", "content": "ロキシー:"}, {"role": "user", "content": "いつも休日は何してるの?"}, {"role": "assistant", "content": "魔術の勉強や読書、新しい魔法の発見などに取り組んでいます。"}]},
{"messages": [{"role": "system", "content": "ロキシー:"}, {"role": "user", "content": "自己紹介をお願いします。"}, {"role": "assistant", "content": "もちろんです。私の名前はロキシー・ミグルディア。ミグルド族という魔族の魔術師で、A級冒険者です。\nミグルド族は念話で意思疎通を図る民族なのですが、私は生まれつき念話が使えず、周りと意思疎通ができず孤独な日々を送っていました。外見は14歳くらいに見えますが、実年齢は30代です。\n礼儀正しく、勤勉な努力家で、水の魔術を得意とします。自分のことを家族のように受け入れてくれたグレイラット家への思い入れは非常に強いです。\n強さは聖級剣士並みの実力がありますが、対人戦の経験はそれほど多くなく、魔物相手の戦闘が得意です。"}]},
]
  return data;
}

使用方法

使い方:右の赤四角の箇所で起動するFunctionを指定して、実行ボタンを押すことでコードが起動されます。

まずは、DataSetにどのような学習をさせるかを入れてください。systemにSystemPromptを、userに質問や入力内容を、assistantにGPTに出力してほしい内容を入れます。
最低10個です。推奨は50-100以上となります。

次に、OpenAIのAPIkeyをコードの先頭に入力します。不安な方はスクリプトプロパティ等を使用してください。

次にuploadOpenAIFileを実行してOpenAIにデータセットを送ります。
ログにでてくるIDをがアップロードしたファイルの管理IDになるのでコードの最初にあるTRAINING_FILE_IDに入力してください。

次にfineTuneModelを実行してください。
トレーニングが開始されます。
ログにでてくるIDをJOB_IDに入れてください。
ステータス監視で追跡するために使用します。

トレーニングが完了したらメールが来ます。
その後は公式サイトのPlaygroundで使用とテストが可能です。
コードでAPIとして使用する場合はmodel名をFINE_TUNED_MODELに入力し、テスト用関数のtestftを参考にしてください。
既にBOTなど環境を作っている方はmodel名を変えるだけで適用されるはずです。

コードの解説

1. チャットのテスト用関数

まず、チャットのテストを行うための関数があります。この部分のコードは以下の通りです。

function testft(){
  var responseRequestOptions = {
    "method": "post",
    "headers": {
      "Content-Type": "application/json",
      "Authorization": "Bearer " + OPENAI_API_KEY
    },
    "payload": JSON.stringify({
      "model":  FINE_TUNED_MODEL,
      "messages": [
        {"role":"system","content":"ロキシー:"},
        {"role": "user", "content": "自己紹介をお願いします!"},
      ]
    })
  };
  var finalResponse = UrlFetchApp.fetch("https://api.openai.com/v1/chat/completions", responseRequestOptions);
  var finalJson = JSON.parse(finalResponse.getContentText());
  var botReply = finalJson['choices'][0]['message']['content'].trim();
  Logger.log(botReply);
}

この部分は、チャットのテストを行うためのコードで、OpenAIとの通信を行い、応答をログに出力します。ここで使用されるOPENAI_API_KEYFINE_TUNED_MODELは、OpenAIと通信する際に必要な情報です。

2. OpenAI APIにファイルをアップロード

次に、OpenAIにファイルをアップロードする部分があります。そのコードは以下です。

function callOpenAI() {
  var data = DataSet();
  var jsonl = data.map(JSON.stringify).join("\n");
  var blob = Utilities.newBlob(jsonl, "application/json", "data.jsonl");
  var headers = {
    "Authorization": "Bearer " + OPENAI_API_KEY
  };
  var payload = {
    "purpose": "fine-tune",
    "file": blob
  };
  var options = {
    "method": "post",
    "headers": headers,
    "payload": payload,
    "muteHttpExceptions": true
  };
  var url = "https://api.openai.com/v1/files";
  var response = UrlFetchApp.fetch(url, options);
  var responseText = response.getContentText();
  Logger.log(responseText);
}

この部分は、Fine TuningのためのデータをOpenAIに送るためのコードです。データはJSON形式で送られ、ファイルとしてアップロードされます。
アップロードした後のログで出てくるIDをコードの一番上の定数の箇所にいれてください。
そのIDを使用して3でファインチューニングのトレーニングを開始します。

3. モデルのファインチューニングを開始

モデルのファインチューニングを開始する部分のコードは以下です。

function fineTuneModel() {
  var url = 'https://api.openai.com/v1/fine_tuning/jobs';
  var payload = {
    "training_file": TRAINING_FILE_ID,
    "model": "gpt-3.5-turbo-0613"
  };
  var options = {
    method: 'post',
    headers: {
      "Content-Type": "application/json",
      "Authorization": "Bearer " + OPENAI_API_KEY
    },
    payload: JSON.stringify(payload)
  };
  var response = UrlFetchApp.fetch(url, options);
  var result = JSON.parse(response.getContentText());
  Logger.log(result);
}

ここでは、アップロードしたファイルを使用して、モデルのファインチューニングを開始します。特定のモデルとトレーニングファイルを指定して、ファインチューニングのジョブを作成します。
トレーニングを開始後、ログにでてくるIDをコードの一番上の定数の箇所にいれてください。
4でジョブが今どのような状況なのかの確認に使用します。

4. ファインチューニングのジョブステータスを確認

ジョブの状態を確認する部分のコードは以下です。

function checkFineTuningJobStatus() {
  var url = "https://api.openai.com/v1/fine_tuning/jobs/" + JOB_ID;
  var headers = {
    "Authorization": "Bearer " + OPENAI_API_KEY,
    "Content-Type": "application/json"
  };
  var options = {
    "method": "get",
    "headers": headers
  };
  var response = UrlFetchApp.fetch(url, options);
  var jobStatus = JSON.parse(response.getContentText());
  Logger.log(jobStatus);
}

この部分は、ファインチューニングのジョブの状態を確認するためのコードです。ジョブが進行中か、完了しているかなどの情報を取得します。

5. データセットの定義

最後に、データセットの定義があります。このコードは、ファインチューニングに使用されるデータセットを定義する部分です。

function DataSet(){
  const data = [
    // ここにデータセットの内容が記載されています。
  ];
  return data;
}

データセットは、ロキシーとユーザーとの対話形式で構成されており、この情報を基にモデルが学習します。

以上、このコード全体は、ロキシーのキャラクターをモデルにしたチャットボットのファインチューニングを行うためのものです。GASを使用してOpenAIと連携し、特定のタスクやスタイルに合わせたカスタマイズを行います。

トレーニングが完了したらメールが来ます。

トレーニングが完了したら、OpenAiに登録しているメールアドレスにトレーニング完了のメールが来ます。
その後、トレーニング完了したmodel名をGASに入力することでGAS上のテスト用関数であるtestftを使用してテストが可能です。
また、OpenAIのPlaygroundでモデルにファインチューニング完了後のモデルが追加されるため、そこでテストすることが可能です。

まとめ

この記事では、Google Apps Script(GAS)を使用して、OpenAIのGPT-3.5モデルをファインチューニングする方法を詳しく解説しました。

具体的なコードと解説を通じて、ファインチューニングのプロセスを理解することができるでしょう。特化したファインチューニングを行うことで、GPT4以上の性能を発揮する可能性もあります。このプロジェクトは、今後のAI開発における新たな方向性を示すもので、多岐にわたる応用が期待されるでしょう。

Colab用


この記事が気に入ったらサポートをしてみませんか?