AIモデルのメモリ使用量を60~70%も削減し安価で低性能なハードウェアでも動作するようにできるオープンソースの量子化手法「SINQ」をHuaweiが発表 - GIGAZINE
この記事読んで「ええやん」と思った。要は量子化ビット数Q4で精度めっちゃあがったよ、ということらしい。タイトルのとおりの手順と検証メモ。SINQのチュートリアルを環境にあわせて書き換えて実行する感じ。
結論的にM2 Pro Mac mini 32GBで、一応実行はやったと思うんだが、ちょっと使えないレベルの遅さではあった。うーん。使い方が違うのか、MPSで最適化されてないのか。
環境
- M2 Pro Mac mini
- 12CPU, 19GPU
- 32GB RAM
- SSD 外付け HIKSEMI FUTURE 4TB
- macOS 14.7.7
- SINQ
- DeepSeek R1
不要なアプリを終了する
大量のメモリを消費するので、不要なアプリは全部終了する。アクティビティモニタでメモリくってるやつ全部終了する。普通に落ちかねない。
ダウンロードから量子化コードの実行まで
SINQを入れる
https://github.com/huawei-csl/SINQ
# 1. Clone the repository
git clone https://github.com/huawei-csl/SINQ.git
cd SINQ
# 2. Install dependencies
pip install -r req.txt
# 3. Install SINQ
pip install .モデルのダウンロード
ツールのインストール。
pip install huggingface-hubダウンロード。32Bの場合。
huggingface-cli download deepseek-ai/DeepSeek-R1-Distill-Qwen-32B --local-dir ./deepseek_32b_fp1614Bの場合は
huggingface-cli download deepseek-ai/DeepSeek-R1-Distill-Qwen-14B --local-dir ./deepseek_14b_fp16ただ実際にやると、1つ8GBあるxxx.safetensorsのダウンロードが99%で切れるという嫌がらせが発生した。パラレルでダウンロードするとそうなる気がする。しかしなんで毎回99%でキレますか……。これについては仕方ないので、不足分を個別でダウンロードした。ブラウザからダウンロードするなら以下から。
- 32B: deepseek-ai/DeepSeek-R1-Distill-Qwen-32B at main
- 14B: deepseek-ai/DeepSeek-R1-Distill-Qwen-14B at main
CLIから個別でやるなら以下。個別でやっても止まる。100%で止まる。ブラウザが一番安定している……?
# ディレクトリ作る
LOCAL_MODEL_DIR="./deepseek_32b_fp16"
mkdir -p $LOCAL_MODEL_DIR
# 不足しているやつを落とす
huggingface-cli download deepseek-ai/DeepSeek-R1-Distill-Qwen-32B \
--local-dir $LOCAL_MODEL_DIR \
--include "model-00003-of-000008.safetensors" \
--resumedeepseek_32b_fp16ディレクトリを確認して揃っていればOK。
量子化コードの実行
量子化コードの実行。以下を実行するとdeepseek_32b_q4_sinqに保存される。14Bは32を14に置換。
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
from sinq.patch_model import AutoSINQHFModel
from sinq.sinqlinear import BaseQuantizeConfig
model_name = "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B"
local_path = "./deepseek_32b_fp16"
tokenizer = AutoTokenizer.from_pretrained(local_path)
# 修正: offload関連のオプションを削除し、シンプルにロード
model = AutoModelForCausalLM.from_pretrained(
local_path,
torch_dtype=torch.bfloat16,
device_map="cpu", # まずCPUにロード
low_cpu_mem_usage=True # メモリ効率的なロード
)
quant_cfg = BaseQuantizeConfig(
nbits=4,
group_size=64,
tiling_mode="1D",
method="sinq"
)
# Macのデバイス判定
if torch.backends.mps.is_available():
current_device = "mps"
else:
current_device = "cpu"
# 量子化実行
qmodel = AutoSINQHFModel.quantize_model(
model,
tokenizer=tokenizer,
quant_config=quant_cfg,
compute_dtype=torch.bfloat16,
device=current_device
)
# 保存
save_dir = "deepseek_32b_q4_sinq"
AutoSINQHFModel.save_quantized_safetensors(
qmodel,
tokenizer,
save_dir,
verbose=True,
max_shard_size="4GB",
)
テストコードの実行
import torch
from transformers import AutoTokenizer
from sinq.patch_model import AutoSINQHFModel
# 保存したディレクトリ
save_dir = "deepseek_32b_q4_sinq"
# トークナイザーのロード
tokenizer = AutoTokenizer.from_pretrained(save_dir)
# Macのデバイス判定
if torch.backends.mps.is_available():
device = "mps"
print("🚀 Using MPS (Apple Silicon GPU)")
else:
device = "cpu"
print("💻 Using CPU")
# 量子化モデルのロード
qmodel = AutoSINQHFModel.from_quantized_safetensors(
save_dir,
device=device, # CUDA → MPS or CPU
compute_dtype=torch.bfloat16,
)
print("✅ Model loaded successfully!")
# 推論テスト
prompt = "Hi"
inputs = tokenizer(prompt, return_tensors="pt").to(device) # 同じデバイスに
print(f"\n📝 Prompt: {prompt}")
print("🤔 Generating response...\n")
with torch.inference_mode():
out_ids = qmodel.generate(
**inputs,
max_new_tokens=10, # 短い応答
do_sample=False,
pad_token_id=tokenizer.eos_token_id # 警告を避ける
)
response = tokenizer.decode(out_ids[0], skip_special_tokens=True)
print(f"🤖 Response:\n{response}")
実行するととても頑張ることになる。スワップやばかったのでアプリ終了しまくったら10GBくらいになった。乗り切らないのか……。

CPUはそれほどでもない。

13分ほど経過したら返事がきた。

0.013tokens/秒ってことなの……?
14Bで再チャレンジ
14Bにして再チャレンジした。
import torch
from transformers import AutoTokenizer
from sinq.patch_model import AutoSINQHFModel
import time
save_dir = "deepseek_14b_q4_sinq"
tokenizer = AutoTokenizer.from_pretrained(save_dir)
# 明示的にMPSを指定
device = "mps"
print(f"🚀 Using device: {device}")
qmodel = AutoSINQHFModel.from_quantized_safetensors(
save_dir,
device=device,
compute_dtype=torch.bfloat16,
)
# デバイス確認
print(f"🔍 Model is on: {next(qmodel.parameters()).device}")
# 短いテスト
prompt = "Hi"
inputs = tokenizer(prompt, return_tensors="pt").to(device)
print(f"🔍 Input is on: {inputs['input_ids'].device}")
print("\n⏳ Generating 20 tokens...\n")
start = time.time()
with torch.inference_mode():
out_ids = qmodel.generate(
**inputs,
max_new_tokens=20,
do_sample=False,
pad_token_id=tokenizer.eos_token_id
)
elapsed = time.time() - start
print(tokenizer.decode(out_ids[0], skip_special_tokens=True))
print(f"\n⏱️ {elapsed:.1f}s ({20/elapsed:.2f} tokens/sec)")結果
m2: ~/git/tama/SINQ> python test_code_14b_2.py
You are using the default legacy behaviour of the <class 'transformers.models.llama.tokenization_llama_fast.LlamaTokenizerFast'>. This is expected, and simply means that the `legacy` (previous) behavior will be used so nothing changes for you. If you want to use the new behaviour, set `legacy=False`. This should only be set if you understand what it means, and thoroughly read the reason why this was added as explained in https://github.com/huggingface/transformers/pull/24565 - if you loaded a llama tokenizer from a GGUF file you can ignore this message.
🚀 Using device: mps
100%|████████████████████████████████████████████████| 147/147 [00:00<00:00, 65072.58it/s]
100%|████████████████████████████████████████████████| 337/337 [00:00<00:00, 30431.46it/s]
🔍 Model is on: mps:0
🔍 Input is on: mps:0
⏳ Generating 20 tokens...
Hi<think>
</think>
Hello! How can I assist you today? 😊
⏱️ 171.5s (0.12 tokens/sec)10倍速い……けど……。0.12 tokens/sec……。
CPUで再チャレンジ
もしかしてCPUのほうが早かったりするのかな、と思って再チャレンジ。
m2: ~/git/tama/SINQ> python test_code_14b_cpu.py
You are using the default legacy behaviour of the <class 'transformers.models.llama.tokenization_llama_fast.LlamaTokenizerFast'>. This is expected, and simply means that the `legacy` (previous) behavior will be used so nothing changes for you. If you want to use the new behaviour, set `legacy=False`. This should only be set if you understand what it means, and thoroughly read the reason why this was added as explained in https://github.com/huggingface/transformers/pull/24565 - if you loaded a llama tokenizer from a GGUF file you can ignore this message.
💻 Using device: cpu
100%|████████████████████████████████████████████████| 147/147 [00:00<00:00, 70207.55it/s]
100%|████████████████████████████████████████████████| 337/337 [00:00<00:00, 31066.19it/s]
🔍 Model is on: cpu
⏳ Generating 20 tokens on CPU...
Hi<think>
</think>
Hello! How can I assist you today? 😊
⏱️ CPU: 280.0s (0.07 tokens/sec)oh... 0.07 tokens/sec かぁ……。
ollama
一応ollamaでも超簡易的に見てみる。
m2: ~/git/tama/SINQ> time ollama run deepseek-r1:14b "Hi"
Hello! How can I assist you today? 😊
real 0m8.628s
user 0m0.026s
sys 0m0.029s
m2: ~/git/tama/SINQ> time ollama run deepseek-r1:14b "Hi"
Hello! How can I assist you today? 😊
real 0m1.854s
user 0m0.017s
sys 0m0.017s初回はモデルをロードしてなかったので。でもはやい。2回目が10トークンで1.9sくらいだから、5tokens/sくらいはあるんよな。
ついでに32Bも動かす。
m2: ~/git/tama/SINQ> time ollama run deepseek-r1:32b "Hello"
Hello! How can I assist you today? 😊
real 0m19.901s
user 0m0.038s
sys 0m0.052s
m2: ~/git/tama/SINQ> time ollama run deepseek-r1:32b "Hi"
Hello! How can I assist you today? 😊
real 0m3.248s
user 0m0.017s
sys 0m0.030s3tokens/s くらい出ている。
所感
ollamaだと同じdeepseek-r1のQ4_K_Mで普通に使えているので、これは明らかに遅いなぁ。なんか間違えているかなぁ。
LLMになんか情報ない?と聞きつつ調べても直接的なのはみつからなんだが、まぁしかしチュートリアルもcuda決め打ちだし、MPS対応がまだ最適化されてないんじゃね、というような感じ。とすると、Macだと結局ollama使い続けるのが楽かつ現実的という話になるね。うーん。
気が向いたら持っているRTX4070でもチャレンジしてみる。14BのQ4ならギリのるはず……。

コメント