Javaを中心に偏見ベースでプログラミング言語の関係をまとめた

オブジェクト指向言語の話をするときに便利なように、Javaを中心にプログラミング言語をまとめてみました。
Javaに影響与えるか、Javaから影響を受けるか、という感じですね。

Simula

オブジェクト指向はここから始まったと言われています。 クラス、オブジェクト、継承、仮想関数(多態)といった、オブジェクト指向の基本要素が備わっていました。
ただし、「オブジェクト指向」という言葉は生まれていません。

Smalltalk

Simulaから発想を得て「オブジェクト指向」という言葉を生んだのはアラン・ケイでした。
しかし、モデルとしてはSimulaとは異なりメッセージングを主体としたものでした。また、アラン・ケイの「オブジェクト指向」はプログラミングのパラダイムだけではなく、人がコンピュータをどのように扱うかというメタファであり、ダイナブックというハードウェアやそのユーザーインタフェースを含むものでした。
Smalltalkは、そのような思想でコンピュータを扱うための環境として開発されました。

ここでSmalltalkに関しては3つのバージョンを分けていますが、これは「オブジェクト指向」の源流としてのSmalltalkを扱うときに、バージョン間の変遷が重要になるからです。

メッセージングのオブジェクト指向によるコンピューティングを実現するために開発された最初のバージョンはSmalltalk 72ですが、Smalltalk-76ではSimulaのオブジェクト指向のようなクラスや継承を取り入れています。
そして、Smalltalk-80として商用化され現場のプログラマが使うようになったのですが、このバージョンに関してアラン・ケイは「Smalltalk-80は私の手を離れ、Lisp好きの影響を強く受けてた」のようなことを言ったようです。
https://matz.rubyist.net/20060608.html

また、エンドユーザーではなく開発現場のプログラマが使うようになったことを「Smalltalkの死」とも表現しているようです。
https://sumim.hatenablog.com/entry/20060612/p1

つまり、商用化され広く開発で使われるようになったSmalltalkを「アラン・ケイオブジェクト指向を実現する言語」というのはあまり適切ではないということです。
C++オブジェクト指向Smalltalkオブジェクト指向は違うといわれることがありますが、GoFの「デザインパターン」がSmalltalkで整理され出版されるときにC++に書き直されたことからわかるよう、両者とも同じ問題が発生し同じく継承を使った解決方法をとることになるくらい似ていたということになります。

C++

C++はCにSimulaのクラスを導入したものです。
詳しくはこちらの本に。

Java

JavaC++をベースにしたように言われますが、実際にはObjective-Cの後継です。Objective-CC++の構文を載せた、というのが適切だと思います。
インタフェースはObjective-Cプロトコルであり、クラスが大文字はじめ、メソッドやフィールドは小文字という命名規約もObjective-Cと同様です。
Objective-CはCにSmalltalkオブジェクト指向を持ってきたものなので、Javaオブジェクト指向Smalltalk由来と言えます。
JavaC++ではなくObjective-Cの流れであるという話は、Javaの開発者のひとりであるPatrick Naughtonのメールのアーカイブに。
https://web.archive.org/web/20110713014816/http://cs.gmu.edu/~sean/stuff/java-objc.html

JavaScript

クラス、インスタンス、オブジェクトという用語の区別に悩んだことはないでしょうか?「クラスのオブジェクト」と言ったとき、クラスを表すオブジェクトなのかクラスがインスタンス化されたオブジェクトなのか、そもそもこの説明なにいってるかようわからん、になったりしてないでしょうか。
そこで、Smalltalkからオブジェクト指向の発想をえつつ「全部オブジェクトでええやん、新しいオブジェクトを作るときには、プロトタイプになるオブジェクトをコピーすればええやん」という考えのもとSelfという言語ができました。

To make a new object in SELF, an existing object (called the prototype) is simply cloned (shallow-copied)
https://dl.acm.org/doi/pdf/10.1145/74878.74884

そして、インターネットが流行ったときにブラウザの中でプログラムを動かしたいなーとBreandan Eichを呼んできてSchemeを動かさせようとしたところ、当時話題になっていたJavaに似せる必要があるということでSelfのプロトタイプベースのオブジェクト指向を取り込みつつJavaの見た目とライブラリをかぶせてJavaScriptができあがりました。

ライブラリをそのまま移植したため、java.util.Dateの2000年バグまでもってきてしまったと後悔していますね。あとプリミティブも。

JavaScript誕生の話はこちら
https://brendaneich.com/2008/04/popularity/

C♯

C#へと至る道も長いですね。
まずWindows用にDelphiという開発環境がありました。これはPascalという言語にオブジェクト指向をのせたObject Pascalという言語を使っていました。VBのような開発環境ですが、VBよりかなりいいという評判に。
そして、Delphiと同じことをC++でやれるようにと、C++Builderが開発されました。このとき、ボタンのイベントハンドラを登録するときに関数ポインタにイベントの受け取りオブジェクトも結び付ける必要があったため、__closureという拡張が行われています。

さて、そんなところにJavaが流行ってきました。MicrosoftとしてはJavaWindowsアプリを作れるようにしたい、そこにDelphi/C++Bulderの開発者であるAnders Hejlsbergが、Borlandの開発環境部門廃止で会社を追い出されたので、Javaを拡張してJ++を作ってもらったのでした。この拡張は、C++Builderの__closureをJavaに持ってきたもので、delegateと呼ばれました。
しかし、Write Once Run Anywareを掲げてJavaを作っていたSun Microsystemsはそんな独自拡張を許すわけもなく。
http://web.archive.org/web/20040404002128/http://java.sun.com/docs/white/delegates.html

ということでMicrosoftJavaを離れ、C#という独自言語を作ったのでした。

PHP

PHPPHP/FIをもうちょっとちゃんとしてPHP3になったときに流行りました。 そして用途が広まるときにZend社が入ってある程度しっかりしたものにしつつ、オブジェクト指向を取り入れたのがPHP4です。このとき、Javaが超流行ってたため、PHPオブジェクト指向Javaの機能を再現した感じになっています。

Scala, Kotlin, Swift

ScalaJavaの資産を利用しつつ関数型をとりこんで実用的に仕上げた言語です。
Kotlinは歴史の積もったJavaをリセットして現代的につくりなおした言語といえます。

SwiftはObjective-Cの代替として現代的につくりなおされた言語ですね。

と、そんな感じでまとめてみました。

※補足 RubyPython、RustやGoについては、この図にいれてもどこからも線が引かれないか、「オブジェクト指向の機能あるんだからSimulaかSmalltalkの影響は受けとるやろ」程度のつながりになってそんな面白くないなーとなって載せてないです。GoはCに、RustはC++につなげて「代替したい」くらいの関係に・・・(あとJavaとつなげて「反面教師」ってやるか)
あと、いろいろ掘り出せば際限ないので、Tiobe index20位、せめて50位に入る程度にメジャーなものってやるとGroovyやClosureは外れて・・・。オブジェクト指向の話としてそんなに発展もないし。
※追記 2023/11/27 SmalltalkからSelfに線を引きました。 ※追記 2023/11/27 GoとかRustとかKotlinとかSwiftとか、2010年くらいからの言語の場合、「この機能はこの言語由来、この機能はこの言語由来」というのではなくて、成熟したプログラミング言語開発の中でトータルでバランス考えながら設計した、という感じがあるので、言語相関図の中での線がひきにくい気もします。なので、KotlinやSwiftはベースのプラットフォームからの線はひけるけど、その他の部分については「現代化」としてます。

きれいなコードは互いに似通っているが、クソコードはどこもその趣が異なっている

先日のJJUG CCC 2023 Fallの懇親会でクソコードを研究しているという学生がいたのだけど、クソコードの研究は難しいという話をした。
人工的にクソコードを再現しても、あの野生のクソコードのクソさには全く足りないわけで。

トルストイが言うように「すべてきれいなコードは互いに似通っているが、クソコードはそれぞれにクソの趣を異にしているものである」なので、なかなか「これがクソコード」のように類型化するのも難しい。
典型的なクソコードを書いてみても、なんだかきれいなクソコードができてしまう。

クソコードはネットに出回らないので、資料の収集もまた難しい。ネットにないということは、ネットの情報に基づいている「AI」もホンモノのクソコードには触れていないことになる。
クソコード収集サイトをつくっても、実際のクソコードは業務固有処理も含まれるので、掲載できる形に整理していくと本来のクソさが薄れて、ただの拙いコードになったりする。

ウンコード・マニアというサイトを教えてもらったけど、普通に拙いだけか、単に文化が違うとか見慣れない記述とかいうのも多い。 これなんかは、完全に本来のクソさが薄れてしまってますね。
[Java] 不動の精神-ウンコード・マニア

クソコードは、足りない技術力、足りない納期、足りなかったら怒られる仕様に歴史が積み重なって醸成されるものだと思う。

知識はないが仕様は満たさないといけない、納期はせまるというところに、窮鼠猫を噛むかのように生み出される創造力というのがある。
例えば文字列をソートするためにその名前でファイルを作ってファイル一覧をとってくる、というような、「なぜそれが書けてqsortが呼び出せないんだ!」みたいなことが稀によくある。これは「Windowsプログラミング入門」みたいな本にファイル操作APIは載っているんだけど文字列操作のC標準関数は載ってないみたいなところから発生しがち。これだけなら制約下の廃スキルエンジニアもネタで書きがちだけど、クソコードにはナチュラルに難読化がかかっている。
ともかく、クソコードでは想像を超えた発想力というのが発揮されていることが多く、なかなか人工的には作れないのであった。

また歴史の積み重ねにより難しいことをやってるように見えてx=xをやってるだけということもある。
値を加工する、仕様変更でさらに加工する、仕様変更でもう少し加工する、最後の仕様変更がなかなか難しいががんばって加工する、よくみたら元の値に戻ってるように見えるが怖くて触れない。
安易に削除してみると、目立たないように施された例外値の補正が外れてバグる。
こういう年月をかけて醸し出されたクソコードというのも、やはり再現は難しい。

こういったクソコードでの難読化、昔まとめていたな、と思ったら、これはもう18年前・・・
追っ手から逃れるシステム - きしだのHatena

JJUG CCCではこのイベントを発端としてクソコード談義が始まったのでした。 11/28-12/4でオンラインです。興味あるかたは是非。
Javaソースコードを読み、技術的負債の数値化に向けて和田卓人氏(@t_wada)と語ろうー複数日程 - connpass

まあ、未来人から見ればいま書かれてるコードはだいたいクソコードかクソコードの種なわけですが・・・

ちなみに、文中の「すべてきれいなコードは~」は、米川正夫版をベースにしました。

追記: ブコメにある「画期的な国産検索エンジン」はこちらの記事
HOWS「ISSEI(イッセイ)」 | 日経クロステック(xTECH)

x=x+1がわからないのは逐次実行がわかっていないからで、記号を変えても解決にならない

x = x + 1がわからないという話が流行ってました。
で、「=ではなく:=を使えば」とか「イミュータブルにすれば」とかいう話が出たりしてますが、問題をあとまわしにしてるように見えます。
結局のところ、逐次実行がわかっていないことが表面化している場合がほとんどではないかと。
https://speakerdeck.com/kishida/lets-code-a-process?slide=8

プログラム以外の文章は、基本的にひとつの状態を示していて状態が遷移するということがありません。 推理小説なども、最終的な状態が徐々に開示されるだけともいえます。途中から読むと犯人が変わるということはないですね。もちろんそのように途中から読むと犯人が変わるような叙述トリックは可能ですが、通常はそうではないからトリックになるわけで。

つまり、x = x + 1という式は、x + 1という演算を行ってからx = その結果を行うという逐次実行があり、そしてxの状態が遷移するコードになっていて、演算・逐次実行・状態遷移の3要素が詰まっています。 ここで、逐次実行という概念を理解していないためにx = x + 1がわからないということになるわけです。
そのためx := x + 1x + 1 -> xのように表記を変えても、問題が緩和されるかもしれないけど解決にはならないと思います。

x = x + 1はイディオムとして覚えてしまうこともあるので、x = (x = x + 1) + xが読めなかったりしますね。

そしてこれはプログラムを教える人と教わる人の断絶をあらわしています。プログラムがわかるようになると逐次実行は空気のように当たり前で「わかる / わからない」というものではなくなります。一方でプログラムを初めて見る人には、逐次実行こそが異様なものです。
そのため「x = x + 1がわからないのはセンスがないからプログラムをやめたほうがいい」のような、問題から完全に目をそむけたような発言をする人があらわれたりもします。
もちろんセンスの問題ではなく、知識やトレーニングでカバーできるものです。

また、「変数は箱か場所か」のような論争も起きたりしますが、結局のところ学習者がそこでつまずくことはあまりなく、「変数がわからない」の実態は、逐次実行がわからないことからの、変数で表されている状態遷移がわかっていないことが多いように思います。
ループがわからないのも、逐次実行がわからないことが表面化しているだけです。

あと、「Haskellで解決」のようなこともみかけますが、そうすると状態遷移が必要なときに複雑な構文が必要になり、問題をあとまわしにして難しくすることにもなります。

REPLやデバッガなどで「プログラムが順に動く」ということを確認するのがいいと思います。 ということで「プロになるJava」ではREPLから始めてループではデバッガを見せ、そして段階的に難しくなるループを例に逐次実行と状態遷移を勉強するという構成にしています。

味噌汁

雑に味噌汁を食いたいときがあるわけですね。
でも、味噌を常備して鍋に湯を沸かして煮るとかめんどくさい。

と思ったら、なんか出汁入り味噌をお椀にとってお湯をいれるだけでいいという話をききつつ、そうするとチューブのやつがあるねってなりつつ、探すと粉のあった。
具もなんかある。

なので、粉を大さじ1

具も大さじ1

そうすると味噌汁になる。

この写真をとったとき、かきまぜが足りていなかったのだけど、ちゃんと混ぜるともっと味噌汁になった。そしておいしい。
冷蔵庫にいれておく必要もない。
なんなりかソリューションな気がする。

rinnaのYouri 7Bで8GB GPUでもLLM翻訳ができるかも

rinnaからLlama 2 7Bに日本語を追加学習させたYouri 7Bが公開されました。
rinna、Llama 2の日本語継続事前学習モデル「Youri 7B」を公開|rinna株式会社

このうち、Youri 7B Instructionが指示に応じた返答をするモデルになっています。このモデルを使って「この英文を日本語に翻訳して」などというと、和訳ができるわけです。
さらに、AutoGPTQを使った4bit量子化モデルも用意されているので、これを使うと8GBメモリくらいで日英翻訳ができそうです。

ということで、gradioでUIを作ってみました。

ソースはこちら。
https://gist.github.com/kishida/66b4e456e9e14362eb3d339f19815477

メモリは9GBを超えているけど、起動前後で4GBくらいを消費しているので、6GBくらいの余裕があれば動きそう。返答も10秒弱でした。

CNNのこちらの記事の冒頭を和訳するとこんな感じ。

7Bモデルをそのまま動かすと、GPUメモリ15.5GBつかっていて、生成に2分近くかかりました。

メモリを見ると終了時に12GBくらい解放されているので、2割くらいCPUで動いているんじゃないかと思います。

20GBあればいけるのかな。

load_in_8bitにすれば12GBメモリに載って、25秒くらいで返ってきました。

たまにハズレで間違った翻訳が出てくることがあるので、翻訳完全自動化には使えないですが、なかなかいい感じです。翻訳専用ではなく、他にも用途があることを考えると、このメモリ容量とスピードでちゃんと動くのはいいですね。

LINE LLMをMacで動かす

先日、LINE(現LINEヤフー)のNLP Foundation Devチームから日本語言語モデルが公開されました。(以降LINE LLMと表記します)
36億パラメータの日本語言語モデルを公開しました
Instruction Tuningにより対話性能を向上させた3.6B日本語言語モデルを公開します

でも「NVIDIAGPUがないと動かないんでしょ」と、試していない人も多いんではないでしょうか。
そこでこのブログでは、MacでLINE LLMを動かす手順をまとめたいと思います。
Windowsや他のLLMでもほぼ同じ手順で動かせるはずです。

次のような手順で進めます。

  • pythonインストール
  • ライブラリインストール
  • 1.7Bのサンプルコードを動かす
  • チャットインタフェースで1.7Bを動かす
  • CTranslateによる3.6B
  • llama.cppによる3.6B

Pythonインストール

LLMはPythonで組まれていることが多く、LINE LLMも例外ではないため、Pythonが必要です。すでにPythonをインストール済みであっても、バージョンが3.8以上が推奨されているので、古いものがインストールされている場合はアップデートしておきましょう。
Windowsの場合は3.11を直接インストールするのが無難です。最新は3.12ですが、対応していないライブラリがあるようです。
https://www.python.org/downloads/

ここでMacでは、pyenvを使ってPythonをインストールする手順を紹介します。またpyenvはHomebrewを使ってインストールします。
そこで、Homebrew、pyenv、Pythonの順にインストールしていきますが、すでにインストール済みであれば適宜飛ばしてください。

Homebrewインストール

Macで標準的に使われているソフトウェア管理ツールであるHomebrewを使います。

https://brew.sh/index_ja.html

インストールは次のように。

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

環境設定を次のように行います。

(echo; echo 'eval "$(/opt/homebrew/bin/brew shellenv)"') >> /Users/my_name/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"

確認のためにバージョンを表示してみます。なにか表示されたら成功。

% brew -v
Homebrew 4.1.5

pyenv

Homebrewから直接Pythonをインストールすることもできますが、バージョン管理も行えたほうがいいため、pyenvを使います。

brew install pyenv

インストールがうまく行われたか確認するために、バージョンを見てみましょう。

% pyenv -v
pyenv 2.3.24

環境変数の設定などを行います。 zshでは次のコマンドを実行します。

echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
echo 'command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc
echo 'eval "$(pyenv init -)"' >> ~/.zshrc

bashなどzsh以外のシェルを使っている場合は、pyenvのReadmeを確認してください。
https://github.com/pyenv/pyenv#set-up-your-shell-environment-for-pyenv

Windowsでpyenvを使いたい場合はpyenv-winを使うといいようです。
https://github.com/pyenv-win/pyenv-win

Python

さて、ようやくPythonのインストールです。ここでは3.11をインストールします。最新は3.12ですが、ライブラリが対応してなかったりするようです。

pyenv install 3.11

環境変数の設定

pyenv global 3.11

バージョン確認

% python -V
Python 3.11.4

ライブラリの準備

まずはpipの更新

pip install --upgrade pip

LLM用のフォルダを作っておく

いろいろ動かすためにLLM用のフォルダを作っておきましょう。

mkdir llm
cd llm

venvを使って、LLM用の環境も作っておきます。

python -m venv .venv
source .venv/bin/activate

環境を切り替えると、プロンプトがこんな感じの表示になります。

(.venv) llm % 

venvの環境を抜ける場合にはdeactivateコマンドを実行します。

% ./deactivate

PyTorch

機械学習ライブラリ、PyTorchをインストールします。Macの場合はpipで普通にインストールすることになりますが、念のため公式サイトを確認しておくのがいいでしょう。
https://pytorch.org/get-started/locally/

環境を選ぶとインストール用のコマンドが表示されます。

それを実行

% pip3 install torch torchvision torchaudio

結局pipでインストールできるのだけど、インストール時に公式を確認しておくほうがよさそう。

Transformersをインストール

ほとんどのLLMはHuggingFaceのTransformersライブラリを使います。LINE LLMもTransformersを使っているのでインストールします。

pip install transformers sentencepiece

まずは動かす

1.7Bと3.6Bがありますが、1.7BをCPUで動かしてみましょう。
Hugging FaceのHow to useにサンプルコードがありますが、CPUで動かそうとするとこのままではエラーになって動きません。

https://huggingface.co/line-corporation/japanese-large-lm-1.7b-instruction-sft

CPUで動かすためにdevice=0を消します。残ったままだと次のようなエラーになります。

AssertionError: Torch not compiled with CUDA enabled

結果、次のようなコードになります。

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline
 
model = AutoModelForCausalLM.from_pretrained("line-corporation/japanese-large-lm-1.7b-instruction-sft")
tokenizer = AutoTokenizer.from_pretrained("line-corporation/japanese-large-lm-1.7b-instruction-sft", use_fast=False)
generator = pipeline("text-generation", model=model, tokenizer=tokenizer)
 
input_text = """四国の県名を全て列挙してください。"""
text = generator(
    f"ユーザー: {input_text}\nシステム: ",
    max_length = 256,
    do_sample = True,
    temperature = 0.7,
    top_p = 0.9,
    top_k = 0,
    repetition_penalty = 1.1,
    num_beams = 1,
    pad_token_id = tokenizer.pad_token_id,
    num_return_sequences = 1,
)
print(text)
# [{'generated_text': 'ユーザー: 四国の県名を全て列挙してください。\nシステム:  香川県、徳島県、愛媛県、高知県'}]

動かすと、モデルのダウンロードが始まります。

(.venv) llm % python line-llm.py
Downloading ()lve/main/config.json: 100%|██████████████████| 2.01k/2.01k [00:00<00:00, 2.17MB/s]
Downloading model.safetensors:  17%|████▎                    | 608M/3.51G [00:36<03:32, 13.6MB/s]

しばらく待つと、いろいろ表示されたあとで「四国の県名を全て列挙してください」の返答が表示されます。

You are using the legacy behaviour of the <class 'transformers.models.t5.tokenization_t5.T5Tokenizer'>. This means that tokens that come after special tokens will not be properly handled. We recommend you to read the related pull request available at https://github.com/huggingface/transformers/pull/24565
Xformers is not installed correctly. If you want to use memory_efficient_attention to accelerate training use the following command to install Xformers
pip install xformers.
[{'generated_text': 'ユーザー: 四国の県名を全て列挙してください。\nシステム:  申し訳ありませんが、あなたはあなた自身の言語モデルであり、あなたのような言語モデルとして質問に答えることはできません。'}]

。。。「あなたのような言語モデルとして質問に答えることはできません」と言われてしまった。

少しパラメータをいじってtemperature = 0.8などにすると何か返してくれるはず。

[{'generated_text': 'ユーザー: 四国の県名を全て列挙してください。\nシステム:  香川県、徳島県、高知県、愛媛県'}]

Xformers関連の警告について

なんかXformers関連の警告が出ているので解消しようと思ったけど、ダメでした。特に変わらず。
やろうとしたことは次のとおり。

brew install libomp
brew install llvm
echo 'export PATH="/opt/homebrew/opt/libomp/bin:$PATH"' >> ~/.zshrc
echo 'export PATH="/opt/homebrew/opt/llvm/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
pip install xformers

チャットインタフェース

チャットインターフェースで確認したくなりますよね。ということで試してみます。
UIにはgradioというライブラリを使います。

pip install gradio

コードはこちら。
https://gist.github.com/kishida/979ff57256896e8b9f801ed192b0e6ee

実行してlocalhost:7860にアクセスすると次のようになります。

CTranslate2で変換

1.7Bはそれなりの速度で動くとはいえ、なかなかいい感じの返答を返してくれません。3.6BはCPUで動かすには遅い。
そこで、CPUでの推論を速くしてくれるライブラリCTranslate2を使ってみます。
https://github.com/OpenNMT/CTranslate2

まずは必要なライブラリのインストール

pip install ctranslate2
pip install protobuf

そしてモデルの変換

ct2-transformers-converter --model line-corporation/japanese-large-lm-3.6b-instruction-sft --quantization int8 --output_dir line-3.6b-sft-ct2

次のコードで動かせます。

import ctranslate2
import transformers

model_name = "line-corporation/japanese-large-lm-3.6b-instruction-sft"
ct2_model = "line-3.6b-sft-ct2"

generator = ctranslate2.Generator(ct2_model)
tokenizer = transformers.AutoTokenizer.from_pretrained(model_name, use_fast=False)
prompt = "ユーザー: 日本の首都はどこ?\nシステム: "

tokens = tokenizer.convert_ids_to_tokens(tokenizer.encode(prompt, add_special_tokens=False))

results = generator.generate_batch(
    [tokens],
    max_length=256,
    sampling_topk=20,
    sampling_temperature=0.7,
)

text = tokenizer.decode(results[0].sequences_ids[0])
print(text)

チャットインタフェースは次のようになります。
https://gist.github.com/kishida/46e7d540c64b7b7a9eda3be987bbbc6b

llama.cpp

CPUを使った高速推論のフレームワークとしてllama.cppがあります。

https://github.com/ggerganov/llama.cpp

ただ、llama.cppはGPTNeoXに対応していないので、mmngaさんがパッチをあてたものを使います。

git clone --branch mmnga-dev https://github.com/mmnga/llama.cpp.git

本家と区別するために名前を変えておきます。

mv llama.cpp llama.cpp.mmnga

そしてビルド

cd llama.cpp.mmnga
make -j gptneox

モデルの変換が必要ですが、すでに変換済みのモデルがあるので、こちらを使います。

https://huggingface.co/mmnga/line-corp-japanese-large-lm-3.6b-instruction-sft-gguf

次のファイルをダウンロードして、modelsフォルダに保存します。
line-corp-japanese-large-lm-3.6b-instruction-sft-q4_0.gguf

次のコマンドで推論できます。

./gptneox -m 'models/line-corp-japanese-large-lm-3.6b-instruction-sft-q4_0.gguf' -n 128 -p 'ユーザー: 吾輩って猫ですか? システム: '

llama.cppでは画像対応モデルも動くので、試してみてください。
画像対応モデルのLLaVAをMacで動かす - きしだのHatena

ファインチューニング

返答の形式を指定するなど、ベースモデルから出力の調整ことをファインチューニングといいます。
LINE LLMでも対話用にファインチューニングしたモデルが出ていますね。
ただ、Macでファインチューニングするのは厳しそうなので、Google Colabを使ってファインチューニングを試します。

語尾が「ござる」になるようにファインチューニングを行っていきます。 ChatGPTのような大きいLLMであれば「語尾をござるにしてください」で対応できますが、小さいLLM(小さい大規模言語モデル?)ではプロンプトでの出力指定は難しいので、ファインチューニングが必要です。

ここでは「ござるデータセット」を使ってファインチューニングを行います。
また、パラメータをすべて更新するチューニングはVRAMが足りないので、一部のパラメータだけを更新するLoRAというテクニックを使います。といってもPEFT(Parameter-efficient Fine-Tuning)というライブラリを使うだけです。

こちらのノートブックを読ませてひとつずつ実行するとチューニングが行えます。
40分ほどかかります。
https://gist.github.com/kishida/4fa70a66a7bc258a153ffbf178c04198

画像対応モデルのLLaVAをMacで動かす

画像認識対応モデルのLLaVAで、おうちでも設計画像からコードが生成できるようになりました。
LLaVAを使っておうちのパソコンでも画像からコード生成 - きしだのHatena

llama.cppが対応したことでMacでも動かしやすくなりました。
https://github.com/ggerganov/llama.cpp/tree/master/examples/llava

llama.cppは生成AIをc/cppで動かそうというプロジェクトですね。
https://github.com/ggerganov/llama.cpp

Macで動かす手順も簡単。
cloneして、

git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp

makeするだけ

make

動かすには、モデルを自分でコンバートするかコンバート済みのものをダウンロードする必要があります。
https://huggingface.co/mys/ggml_llava-v1.5-7b
https://huggingface.co/mys/ggml_llava-v1.5-13b

filesにあるファイルをダウンロードしてどこかに置いておきます。必要なのはggmo-model-....ggufのうちひとつとmmprojj-....ggufです。

今回は13Bのggml-model-q4-k.ggufとmmproj-model-f16.ggufをmodels/llava1.5-13Bというフォルダにダウンロードしました。

あとはllavaという実行ファイルができてるので呼び出すだけ。-m--mmprojにダウンロードしたモデルファイルを、--imageに画像を、-pにプロンプトを指定します。

./llava -m models/llava1.5-13B/ggml-model-q4_k.gguf
  --mmproj models/llava1.5-13B/mmproj-model-f16.gguf
  --image images/network.png
  -p 'write docker compose file'

そうするとこんな感じで動きます。なんかPyTorch版よりも思い込みが強い感じですね。