[2025-10-27] 大幅改訂。
MLX は Apple Silicon 専用のライブラリです(MLXではじめる機械学習 が参考になります)。MLX LM は MLX で LLM をするパッケージです。Vision Language Model 用の MLX-VLM というパッケージもあります。
MLX LM で Gemma 3 27B 8bit を使う例:
from mlx_lm import load, generate
from mlx_lm.sample_utils import make_sampler
model, tokenizer = load("mlx-community/gemma-3-27b-it-8bit")
sampler = make_sampler(
    temp=0,    # temperature
    # top_p=0,
    # min_p=0,
    # top_k=0,
)
prompt = "まどマギで好きな登場人物は?"
if tokenizer.chat_template is not None:
    prompt = tokenizer.apply_chat_template(
        [{"role": "user", "content": prompt}],
        tokenize=False,
        add_generation_prompt=True,
    )
response = generate(model, tokenizer, prompt=prompt,
                    max_tokens=1024, verbose=True, sampler=sampler)
まどマギの登場人物はみんな魅力的で、誰か一人を選ぶのは難しいですね! それでもあえて言うなら、私は**鹿目まどか**が一番好きです。 最初は普通の女の子に見えて、物語が進むにつれて、その優しさや誰かを守りたいという強い意志が明らかになっていく過程に心を打たれました。そして、最終的に彼女が選んだ道は、多くの人に希望と感動を与えたと思います。 もちろん、ほむらやマミ、さやか、キュゥべえなど、他のキャラクターもそれぞれ深く掘り下げられていて、魅力的です。 あなたは誰が好きですか? 理由も教えていただけると嬉しいです!
tokenizer.apply_chat_template() で変換された prompt は
'<bos><start_of_turn>user\nまどマギで好きな登場人物は?<end_of_turn>\n<start_of_turn>model\n'
となります。add_generation_prompt=False にすると
'<bos><start_of_turn>user\nまどマギで好きな登場人物は?<end_of_turn>\n'
になります。これでも答えてくれますが、鹿目まどかでなく美樹さやかになるなど、まったく違った答えになります。なお、tokenize=True にすると文字列ではなくトークン列になりますが、これは回答に影響しないようです。
上位3位までのトークンの確率分布を出力してみます:
import mlx.core as mx
from mlx_lm import load, generate
model, tokenizer = load("mlx-community/gemma-3-27b-it-8bit")
def sampler(x):
    a = mx.argsort(-x, axis=-1)[0]
    s = mx.sum(mx.exp(x[0]))
    print(repr(tokenizer.decode(int(a[0]))))
    for t in a[:3]:
        i = int(t)
        print(f"  {mx.exp(x[0][i])/s:7.5f} {repr(tokenizer.decode(i))}")
    return mx.argmax(x, axis=-1)
prompt = "How many 'r's in strawberry? Answer with numbers only."
if tokenizer.chat_template is not None:
    prompt = tokenizer.apply_chat_template(
        [{"role": "user", "content": prompt}],
        tokenize=False,
        add_generation_prompt=True,
    )
response = generate(model,
                    tokenizer,
                    prompt=prompt,
                    sampler=sampler,
                    verbose=False)
print(repr(response))
本来は sampler() には確率の対数が渡るはずなので、合計 s で割る必要はないはずですが、誤差を減らすためにこうしてみました。
結果は次のようになり、「3」がほぼ確率1で出力されることがわかります。
'3' 1.00000 '3' 0.00000 '2' 0.00000 '4' '' 0.99219 ' ' 0.00522 '\n' 0.00000 '\n\n\n\n' ' ' 0.87891 ' ' 0.11963 '\n' 0.00000 ' ' '3' 
別の例でやってみましょう。
prompt = "Write a one-sentence bedtime story about a unicorn."
if tokenizer.chat_template is not None:
    prompt = tokenizer.apply_chat_template(
        [{"role": "user", "content": prompt}],
        tokenize=False,
        add_generation_prompt=True,
    )
response = generate(model,
                    tokenizer,
                    prompt=prompt,
                    sampler=sampler,
                    verbose=False)
print(repr(response))
'Cel' 0.91406 'Cel' 0.07520 'Luna' 0.01306 'With' 'est' 0.50000 'est' 0.50000 'este' 0.00000 'esta' 'ia' 1.00000 'ia' 0.00000 'ria' 0.00000 'ina' ' the' 1.00000 ' the' 0.00000 ',' 0.00000 ' tw' ' unicorn' 1.00000 ' unicorn' 0.00000 ' Unicorn' 0.00000 'unicorn' ...(略)... 'Celestia the unicorn sprinkled stardust over the sleeping forest, ensuring every creature dreamed sweet dreams filled with rainbows and magic, before gently closing her own eyes for the night.\n\n\n\n'
91.4%がCelest...、7.5%がLuna...で始まりますが、これは学習データの91.4%がCelest...で始まっていたというわけではなく、このモデルとプロンプトの組合せではCelest...で始まることが多いというだけです。こちらの「トークンごとの確信度の出力」の節もご覧ください。