[Mac M2 Pro 12CPU, Sonoma 14.5, clang++ 15.0.0]
低コスト版のgpt-4o-miniがリリースされたので早速試してみました。
OpenAIが示すスコアではプログラミング能力がgpt-4oの90.2%とさほど変わらない87.2%だったので期待したのですが、私の用途では一発不合格でした。
関数の例を示して新たな関数を作るように指示しているのに例示した関数を含んだ関数を作成するのは論外です。英語なら能力が高いのかもしれません。
![](https://mowareblog.net/wp-content/uploads/2024/07/19ab965374f147c2b04864e3d7f8fed6-2-1024x583.png)
![](https://mowareblog.net/wp-content/uploads/2024/07/5dc3a9a6e31a11f3731fc242e8891458-1024x745.png)
![](https://mowareblog.net/wp-content/uploads/2024/07/bc77a84401fb58ed7b9aff582dd37072-1-1024x745.png)
[Mac M2 Pro 12CPU, Sonoma 14.5, clang++ 15.0.0]
低コスト版のgpt-4o-miniがリリースされたので早速試してみました。
OpenAIが示すスコアではプログラミング能力がgpt-4oの90.2%とさほど変わらない87.2%だったので期待したのですが、私の用途では一発不合格でした。
関数の例を示して新たな関数を作るように指示しているのに例示した関数を含んだ関数を作成するのは論外です。英語なら能力が高いのかもしれません。
[Mac M2 Pro 12CPU, Sonoma 14.3.1, clang++ 15.0.0]
Perplexity APIを使うことで最新情報も取得できるようになったので、第1選択LLMに昇格させました。
間違った情報が混ざっていることもありますが、ラフな情報収集としてはまずまずかと思います。
24/05/17追記
Perplexity APIは回答の言語が不安定(日本語 or 英語)なので、第1選択LLMをgtp-4oに戻しました。Webサービス(無料 or Pro)で使う方が良さそうです。
[Mac M2 Pro 12CPU, Sonoma 14.3.1, clang++ 15.0.0]
5月13日に発表されたgpt-4oを早速導入しました。Perplexity APIも導入しました。
gpt-4oについてはこれから検証していきます。
まず驚いたのがPerplexity API(llama-3-sonar-large-32k-online)のネット検索能力です。年度、日付などはいい加減ですが、プレミアリーグの最新データを取得してくれました。順位、勝敗、得失点は正確でした。
ただしPerplexity APIはgptのようにあまり行間を読んでくれないため、くどくどしくプロンプトを書かないと意図に沿った回答が得られないケースがあります。
またsystemで日本語で回答するように指示しても、英語で返す方が多かったりします。なので最初のプロンプトでも日本語指定のテンプレを自動追記するようにしました。それでも頑なに英語で返すことがあります。
[Mac M2 Pro 12CPU, Sonoma 14.3.1, clang++ 15.0.0]
ChatAIアプリのGUI右側CMDボタンでOpenAI互換APIサーバの起動コマンドを生成し、自動的にターミナルを開いて実行するようにしました(AppleScript)。サーバの状況はターミナルからモニタリングします。
CodeLlama日本語版とチャットしてみると相当に気難しくてうまく誘導しないと正答にたどり着けないことが分かりました。これはこれで面白く、ローカルLLMですから無料というのが魅力です。
gpt-4への課金がどれくらい削減できるか、楽しみです。
[Mac M2 Pro 12CPU, Sonoma 14.3.1, clang++ 15.0.0]
llama.cppでOpenAI互換APIサーバを立ち上げ、ChatGPTのようにチャットできるようにしました。
モデルは ELYZA-japanese-CodeLlama-7b-instruct-q4_K_M.gguf です。
cd /Volumes/DATA_m1/AI/llama.cpp && ./server -m models/ELYZA-japanese-CodeLlama-7b-instruct-q4_K_M.gguf -ngl 1 -c 4096
サーバのURLは以下のようになります。
url : http://localhost:8080/v1/chat/completions
[Mac M2 Pro 12CPU, Sonoma 14.3.1, clang++ 15.0.0]
ローカルLLMの性能を検証するアプリをSwiftUIで製作しました。
modelsディレクトリに入っているggufファイルを選択し、promptを入力してtxtファイル化、llama.cppのmainコマンドを作成します。コマンドはクリップボードに自動コピーされます。あとはターミナルにコマンドを貼り付けて実行し、responseを手動で右側のTextEditorにペーストしてtxtファイルにします。
promptファイルとresponseファイルのプレフィックスはコマンド作成時のタイムスタンプ(yymmdd_hhmmss)になっています。
今回はほとんどclaude-3 opusとgpt-4にコードを考えてもらいました。Buttonラベルの改行表示についてはclaude-3では埒が開かず、gpt-4がテキスト分割を提案して不本意ながら解決しました。SwiftUIに関してはclaude-3とgpt-4はほぼ互角といった印象です。
SwiftUIのmacOS版ではButtonのラベルを改行できないようです。iOS版では問題なく出来るはずですが、macOS版の後進性に若干引いています。
※claude-3利用状況
claude-3のAPIキーを取得して5日経ち、5ドルの無料分がほぼ無くなりました。私の用途ではgpt-4に対する優位性を体感できなかったので、今後は割安なgpt-4メインに戻ります。なおclaude-3 APIの支払いで個人はマイナンバーの入力を求められているため、登録は見送りました。Claude Pro($20/month)への登録もしません。
[Mac M2 Pro 12CPU, Sonoma 14.3.1, clang++ 15.0.0]
対応LLM:gpt-4, gpt-4-vision, claude-3
claude-3ではgptのようにrole:systemは設定できず、systemという独立したパラメータとして扱います。つまりmodelやtemperatureと同じです。
以前の記事で紹介したようにuserのcontentの先頭に追加しても効果は同じだと思いますが、一応公式の方法に従います。
if (urls == ""){
// claude-3 画像なし
requestData = "{\"model\":\"" + model + "\", \"messages\":[{\"role\":\"user\",\"content\":\"" + question + "\"}], \"system\":\"" + systemStr + "\",\"temperature\":0.0, \"max_tokens\":4096}";
} else {
// claude-3 画像あり
string requestData_1 = "{\"model\":\"" + model + "\", \"system\":\"" + systemStr + "\", \"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"" + question + "\"},";
<以下略>
[Mac M2 Pro 12CPU, Sonoma 14.3.1, clang++ 15.0.0]
対応LLM:gpt-4, gpt-4-vision, claude-3
claude-3でも画像のエンコードデータを送れるようにしました。JSONの形式がgpt-4と異なるため、かなり手を入れました。
画像のあるなしに関係なくclaude-3の方がレスポンスに時間が掛かります。
if (model.find("gpt") != string::npos){
// gpt-4
requestData = "{\"model\":\"" + model + "\", \"messages\":[{\"role\":\"system\",\"content\":\"" + systemStr + "\"},{\"role\":\"user\",\"content\":\"" + question + "\"}], \"temperature\":0.0}";
} else {
if (urls == ""){
// claude-3 画像なし
requestData = "{\"model\":\"" + model + "\", \"messages\":[{\"role\":\"user\",\"content\":\"" + systemStr + question + "\"}], \"temperature\":0.0, \"max_tokens\":4096}";
} else {
// claude-3 画像あり
string requestData_1 = "{\"model\":\"" + model + "\", \"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"" + systemStr + question + "\"},";
vector<string> urlList = splitString(urls, '\n');
cout << "urlListの要素数: " << to_string(urlList.size()) << endl;
std::stringstream imageStream;
for (size_t i = 0; i < urlList.size(); ++i) {
// ローカル画像はBase64形式にエンコードする
if (urlList[i].find("https:") != string::npos){
cout << "claude3への画像URL送信には未対応" << endl;
return "";
} else {
string base64_image = file_to_base64(urlList[i]);
string file_extension = get_file_extension(urlList[i]);
string url = "\"media_type\":\"image/" + file_extension + "\",\"type\":\"base64\", \"data\": \"" + base64_image + "\"";
imageStream << "{\"type\": \"image\", \"source\": {" << url << "}}";
}
if (i < urlList.size() - 1) {
imageStream << ",";
}
}
std::string requestData_2 = imageStream.str() + "]}], \"max_tokens\": 4096,\"temperature\": 0.0}";
cout << "requestData_2: \n" << requestData_2.c_str() << endl;
requestData = requestData_1 + requestData_2;
}
}
※ file_to_base64関数他については前回の記事参照
[Mac M2 Pro 12CPU, Sonoma 14.3.1, clang++ 15.0.0]
対応LLM:gpt-4, gpt-4-vision, claude-3
これまでは自分のブログの非公開記事に画像を貼り付けて、そのURLを送信していましたが、ローカル画像をBase64形式にエンコードして送れるようにもしました。
string requestData_1 = "{\"model\":\"" + model + "\", \"messages\":[{\"role\":\"system\",\"content\":\"" + systemStr + "\"},{\"role\":\"user\",\"content\":[{\"type\": \"text\", \"text\":\"" + question + "\"},";
cout << "requestData_1: \n" << requestData_1.c_str() << endl;
vector<string> urlList = splitString(urls, '\n');
cout << "urlListの要素数: " << to_string(urlList.size()) << endl;
std::stringstream imageStream;
for (size_t i = 0; i < urlList.size(); ++i) {
// URLの場合はそのまま使用し、ローカルの場合はBase64形式にエンコードする
if (urlList[i].find("https:") != string::npos){
imageStream << "{\"type\": \"image_url\", \"image_url\": {\"url\":\"" << urlList[i] << "\"}}";
} else {
string base64_image = file_to_base64(urlList[i]);
string file_extension = get_file_extension(urlList[i]);
string url = "data:image/" + file_extension + ";base64," + base64_image;
imageStream << "{\"type\": \"image_url\", \"image_url\": {\"url\":\"" << url << "\"}}";
}
if (i < urlList.size() - 1) {
imageStream << ",";
}
}
std::string requestData_2 = imageStream.str() + "]}], \"max_tokens\": 4096,\"temperature\": 0.0}";
cout << "requestData_2: \n" << requestData_2.c_str() << endl;
requestData = requestData_1 + requestData_2;
#include <boost/archive/iterators/base64_from_binary.hpp>
#include <boost/archive/iterators/transform_width.hpp>
#include <boost/archive/iterators/ostream_iterator.hpp>
#include <fstream>
#include <sstream>
#include <string>
std::string file_to_base64(const std::string& file_path) {
using namespace boost::archive::iterators;
using Base64EncodeIterator = base64_from_binary<transform_width<std::string::const_iterator, 6, 8>>;
std::ifstream file(file_path, std::ios::binary);
std::string file_contents((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
std::stringstream os;
std::copy(Base64EncodeIterator(file_contents.begin()), Base64EncodeIterator(file_contents.end()), ostream_iterator<char>(os));
size_t num = (3 - file_contents.length() % 3) % 3;
for (size_t i = 0; i < num; i++) {
os.put('=');
}
return os.str();
}
std::string get_file_extension(const std::string& file_path) {
size_t dot_pos = file_path.rfind('.');
if (dot_pos == std::string::npos) return "";
return file_path.substr(dot_pos + 1);
}
[Mac M2 Pro 12CPU, Sonoma 14.3.1, clang++ 15.0.0]
対応LLM:gpt-4, gpt-4-vision, claude-3
claude-3ではgpt-4のようにrole:systemの設定ができないため、最初のプロンプトでuserとして指示します。
if (model.find("gpt") != string::npos){
requestData = "{\"model\":\"" + model + "\", \"messages\":[{\"role\":\"system\",\"content\":\"" + systemStr + "\"},{\"role\":\"user\",\"content\":\"" + question + "\"}], \"temperature\":0.0}";
} else {
requestData = "{\"model\":\"" + model + "\", \"messages\":[{\"role\":\"user\",\"content\":\"" + systemStr + question + "\"}], \"temperature\":0.0, \"max_tokens\":1024}";
}