ollamaをUbuntu 24.04 にインストールしてDeepSeek(の量子化モデル)に触れてみる
chatGPTなどの高性能AIに比類し、しかもそれのモデルが公開されており「ローカルPCでもChatGPTのような高性能LLM動かせるじゃないか!」とLLM界隈が盛り上がり、釣られて一般人にも盛り上がり…というわけで最近巷を色々騒がせている「Deepseek」ですが、実は「蒸留モデル」という、元のモデルを教師として学習し直した軽量なモデルであれば、ビックテックが持ってるようなH100やH200などの超超超…強力なGPUが無くてもつおいLLMが動かせると聞きつけて家のRTX4080+Ubuntuでも動かせるかどうか試しました。
「DeepSeekガー中国ガー」って短絡的に言う困った人たちも見かけますが、ローカルで重み(学習済みモデル)を動かすだけなら比較的安全です
(比較的という枕詞をつけているのは、出力された内容を鵜呑みにしないとかそういう当たり前のことです。これはChatGPTや数多のLLMでも同じことなんですけどね…)
前置き長くなりましたが、LLMはモデル(重み)だけあってもそれだけでは動きません。
そのモデルをGPUにロードしたり、プロンプトを送って推論を回したり、回答を得たりする「実行環境」が必要になります。
このLLMの実行環境は巨大なサーバでのみ動くものとか、家のPCで動くものとか、ラズパイでも動くものとか…本当にいろんなものがあります。
今回は「普通のPCで動く」という目標なので、ローカルPC実行環境が必要です。
その中でもgo言語で作られているらしい「ollama(オラマと言うらしい)」を使うのが他パッケージとの依存関係もあまりなくて「非常に導入が楽」というのを聞いたので、これ経由でDeepseekの蒸留モデルをUbuntuで動かしてみます。
ちなみにOllama公式はこちら
https://ollama.com/
https://github.com/ollama/ollama/tree/main
なんかアイコンがアルパカみたいですね。(Alpacaという言語モデルとかollama用のフロントエンドもあるらしい…名前空間…)
ollamaのセットアップ
ollamaのUbuntuへのインストールは、公式のこれを実行すればあっという間に導入できます
https://ollama.com/download
が、、、しかしこのスクリプトは勝手にollamaのサービスを登録したり、勝手にドライバ入れたりと「Ubuntuチョットデキル勢」のボクから見たらちょっと(というかかなり)行儀が悪いので、自分は以下のようにして手動セットアップをしました。
-
ollama用のディレクトリを適当に作る
/home/user/ollama
など、どこでもいいです。
なお、install.sh
を実行すると/usr/local/
にollama関連のファイルが勝手にダウンロードされて勝手に/usr/local/bin
と/usr/local/lib
に置かれ、勝手にollamaサービスが登録されます…半分罠やんけ -
ollamaのバイナリをダウンロード
厳密にやりたければソースからビルドしたほうがいいのですが、流石にソースからのビルドは色々大変で面倒なので公式が用意してくれているバイナリをとってくるのが良いです。
1で作ったディレクトリ内で以下実行
wget https://ollama.com/download/ollama-linux-amd64.tgz
- tgzをそのまま展開
tar zxvf ollama-linux-amd64.tgz
展開するとbinとlibディレクトリができます。
binにollama本体、libに各GPUを使う依存ライブラリやサーバなど(cublasとかrocmとか)が入っています。
(特殊なライセンスなのかもしれないけど、cudartとかこれ再配布ええんや…みたいな感じです)
-
モデルを保存するためのディレクトリを用意
モデルはデフォルトでは/usr/share/ollama
以下に作られてしまいます。
モデルは数GBもしくは数十GBにもなるので、心配なら別のディスクにするなどしたほうが良さげ
これは環境変数OLLAMA_MODELS
で変更が可能とのことで、適切に用意します -
以下のような実行用のシェルスクリプト(
ollama_exec_server.sh
)を作る
ollamaはサーバが動いてその中でLLMが動いて、クライアントがollamaサーバのAPIを叩いて解答を得るみたいな感じになっています
なので、install.sh
などでサービスを登録しないで使う場合は自力でollamaのサーバを建てる必要があります。
いちいち打つのは面倒なので、シェルスクリプトを作って動かしたいときはシェルスクリプトを叩くという運用が良いと思います。
以下のシェルスクリプトのMODELDIR
はモデルを保存するディレクトリ名です。自分の場合はそのままこのスクリプトが置かれているディレクトリにmodelsディレクトリを作ってそこにおいています。
OLLAMA_HOST
は、デフォルトでは127.0.0.1
になっており、外部LANからもアクセスできないようになっているのでこうしています。
このへんは各々セキュリティを考慮して適時変更してください
OLLAMA_ORIGINS
も同じ意味合いです。
#!/bin/bash
CUR="$(cd "$(dirname "$BASH_SOURCE")"; pwd)"
MODELDIR=$CUR/models
export OLLAMA_MODELS=$MODELDIR
export OLLAMA_HOST=0.0.0.0
export OLLAMA_ORIGINS=192.168.*,172.*
./bin/ollama serve
ollama_exec_server.sh
に実行権限を与えて動かす
こんな感じで動いたらとりあえず準備完了。
2025/02/02 15:35:13 routes.go:1187: INFO server config env="map[CUDA_VISIBLE_DEVICES: GPU_DEVICE_ORDINAL: HIP_VISIBLE_DEVICES: HSA_OVERRIDE_GFX_VERSION: HTTPS_PROXY: HTTP_PROXY: NO_PROXY: OLLAMA_DEBUG:false OLLAMA_FLASH_ATTENTION:false OLLAMA_GPU_OVERHEAD:0 OLLAMA_HOST:http://0.0.0.0:11434 OLLAMA_INTEL_GPU:false OLLAMA_KEEP_ALIVE:5m0s OLLAMA_KV_CACHE_TYPE: OLLAMA_LLM_LIBRARY: OLLAMA_LOAD_TIMEOUT:5m0s OLLAMA_MAX_LOADED_MODELS:0 OLLAMA_MAX_QUEUE:512 OLLAMA_MODELS:/home/user/llm/ollama/models OLLAMA_MULTIUSER_CACHE:false OLLAMA_NOHISTORY:false OLLAMA_NOPRUNE:false OLLAMA_NUM_PARALLEL:0 OLLAMA_ORIGINS:[192.168.* 172.* http://localhost https://localhost http://localhost:* https://localhost:* http://127.0.0.1 https://127.0.0.1 http://127.0.0.1:* https://127.0.0.1:* http://0.0.0.0 https://0.0.0.0 http://0.0.0.0:* https://0.0.0.0:* app://* file://* tauri://* vscode-webview://*] OLLAMA_SCHED_SPREAD:false ROCR_VISIBLE_DEVICES: http_proxy: https_proxy: no_proxy:]"
time=2025-02-02T15:35:13.847+09:00 level=INFO source=images.go:432 msg="total blobs: 0"
time=2025-02-02T15:35:13.847+09:00 level=INFO source=images.go:439 msg="total unused blobs removed: 0"
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] POST /api/pull --> github.com/ollama/ollama/server.(*Server).PullHandler-fm (5 handlers)
[GIN-debug] POST /api/generate --> github.com/ollama/ollama/server.(*Server).GenerateHandler-fm (5 handlers)
[GIN-debug] POST /api/chat --> github.com/ollama/ollama/server.(*Server).ChatHandler-fm (5 handlers)
[GIN-debug] POST /api/embed --> github.com/ollama/ollama/server.(*Server).EmbedHandler-fm (5 handlers)
[GIN-debug] POST /api/embeddings --> github.com/ollama/ollama/server.(*Server).EmbeddingsHandler-fm (5 handlers)
[GIN-debug] POST /api/create --> github.com/ollama/ollama/server.(*Server).CreateHandler-fm (5 handlers)
[GIN-debug] POST /api/push --> github.com/ollama/ollama/server.(*Server).PushHandler-fm (5 handlers)
[GIN-debug] POST /api/copy --> github.com/ollama/ollama/server.(*Server).CopyHandler-fm (5 handlers)
[GIN-debug] DELETE /api/delete --> github.com/ollama/ollama/server.(*Server).DeleteHandler-fm (5 handlers)
[GIN-debug] POST /api/show --> github.com/ollama/ollama/server.(*Server).ShowHandler-fm (5 handlers)
[GIN-debug] POST /api/blobs/:digest --> github.com/ollama/ollama/server.(*Server).CreateBlobHandler-fm (5 handlers)
[GIN-debug] HEAD /api/blobs/:digest --> github.com/ollama/ollama/server.(*Server).HeadBlobHandler-fm (5 handlers)
[GIN-debug] GET /api/ps --> github.com/ollama/ollama/server.(*Server).PsHandler-fm (5 handlers)
[GIN-debug] POST /v1/chat/completions --> github.com/ollama/ollama/server.(*Server).ChatHandler-fm (6 handlers)
[GIN-debug] POST /v1/completions --> github.com/ollama/ollama/server.(*Server).GenerateHandler-fm (6 handlers)
[GIN-debug] POST /v1/embeddings --> github.com/ollama/ollama/server.(*Server).EmbedHandler-fm (6 handlers)
[GIN-debug] GET /v1/models --> github.com/ollama/ollama/server.(*Server).ListHandler-fm (6 handlers)
[GIN-debug] GET /v1/models/:model --> github.com/ollama/ollama/server.(*Server).ShowHandler-fm (6 handlers)
[GIN-debug] GET / --> github.com/ollama/ollama/server.(*Server).GenerateRoutes.func1 (5 handlers)
[GIN-debug] GET /api/tags --> github.com/ollama/ollama/server.(*Server).ListHandler-fm (5 handlers)
[GIN-debug] GET /api/version --> github.com/ollama/ollama/server.(*Server).GenerateRoutes.func2 (5 handlers)
[GIN-debug] HEAD / --> github.com/ollama/ollama/server.(*Server).GenerateRoutes.func1 (5 handlers)
[GIN-debug] HEAD /api/tags --> github.com/ollama/ollama/server.(*Server).ListHandler-fm (5 handlers)
[GIN-debug] HEAD /api/version --> github.com/ollama/ollama/server.(*Server).GenerateRoutes.func2 (5 handlers)
time=2025-02-02T15:35:13.848+09:00 level=INFO source=routes.go:1238 msg="Listening on [::]:11434 (version 0.5.7)"
time=2025-02-02T15:35:13.849+09:00 level=INFO source=routes.go:1267 msg="Dynamic LLM libraries" runners="[cuda_v12_avx rocm_avx cpu cpu_avx cpu_avx2 cuda_v11_avx]"
time=2025-02-02T15:35:13.849+09:00 level=INFO source=gpu.go:226 msg="looking for compatible GPUs"
time=2025-02-02T15:35:14.076+09:00 level=INFO source=types.go:131 msg="inference compute" id=GPU-c0069ee1-1e34-b529-d0ac-747bab711016 library=cuda variant=v12 compute=8.9 driver=12.8 name="NVIDIA GeForce RTX 4080" total="15.6 GiB" available="14.6 GiB"
なんかエラーが出たときは…
https://github.com/ollama/ollama/blob/main/scripts/install.sh
を見て、足りないパッケージなどをインストールしましょう。
とくにNvidiaのGPUを使っている場合は、Nvidiaの普通のグラフィックドライバを入れれば問題ないはずです。
(入れ方はubuntu 24.04.1 設定メモを見てください)
という感じで適当なディレクトリに入れても問題なく動きました
ollamaは/usr/share
など決め打ちで見ていなくて、相対ディレクトリで判断してくれているのは非常に好感がもてますね。
Deepseekを動かしてみる
現在(2025/01)DeepseekにはV3とR1があります。
どちらもchatGPTなどに高性能モデルですが話題になっているのがR1とのこと。
これらは次のような違いがあるとのことでした
V3 — 日常的な幅広いタスクが得意
R1 — 専門的で複雑な問題解決が得意
本来のモデルはこれです
https://github.com/deepseek-ai/DeepSeek-R1
https://huggingface.co/deepseek-ai/DeepSeek-R1
全部合わせると数百GBもあって、これはとてもじゃないけど家のGPUでは動かせそうにはありません…
なので手持ちのVRAMになんとか載るような蒸留モデルを使うことになります。
当たり前ですが、モデルの規模が小さいと回答精度も悪くなります
ということで、ollama公式に蒸留モデルを使うコマンドが用意されているので、ここからVRAMに乗っかりそうなモデルを探します。
https://ollama.com/library/deepseek-r1
14bを選ぶと9.0GでこれならボクのGPUのVRAMにも載りそうなので、ollamaサーバをollama_exec_server.shで動かして、別のターミナルから以下実行します。
最初はモデルのダウンロードがあるので時間がかかります
./ollama run deepseek-r1:14b
最初はモデルのダウンロードがあるので時間がかかります
こんな感じで動いてくれれば成功
./ollama run deepseek-r1:14b
pulling manifest
pulling 6e9f90f02bb3... 100% ▕████████████████████████████████████▏ 9.0 GB
pulling 369ca498f347... 100% ▕████████████████████████████████████▏ 387 B
pulling 6e4c38e1172f... 100% ▕████████████████████████████████████▏ 1.1 KB
pulling f4d24e9138dd... 100% ▕████████████████████████████████████▏ 148 B
pulling 3c24b0c80794... 100% ▕████████████████████████████████████▏ 488 B
verifying sha256 digest
writing manifest
success
>>> hello!
<think>
</think>
Hello! How can I assist you today? 😊
>>> こんにちは。今日は寒いです
<think>
Alright, the user said "こんちにちは。今日は寒いです" which means "Hello. It's cold today." in
Japanese.
I need to respond in a friendly way. Maybe acknowledge the cold and ask if they're prepared.
I should keep it simple and conversational.
</think>
こんにちは!今日も寒いですね。防寒対策は万全ですか?
こんな感じで動くことが確認できました。
回答もすぐに返って来て早く、なんか回答するまでの思考が見えるんですね。ローカルLLM初体験なんですがこれは面白い。
電気代さえあれば使いたい放題のローカルLLMはめっちゃ良いですね
なお、私の環境ではRAMは62MBくらい、VRAMは10GBほど消費しておりました
終わりたいときは/bye
と入れると抜けられます。
サーバ側はCtrl+C
を押すと終わることができます。
おわりに
LLMの環境構築って正直面倒そうという感じがあったのですが、ollamaのおかげで蓋を開けるととんでもなく楽で、画像生成AIよりも環境構築が楽なのには驚きました。
これはもうローカルLLMは人類に必要不可欠なものになってきている…ということでしょうかね
あと、Deepseekなのですが今回試した感じでは一応日本語が通りましたが、ときたま中国語を返したりしてしまうようです。
この辺はサイバーエージェントさんが日本語に追加学習(ファインチューン?)したDeepSeek-R1モデルを公開していて、これを使うと日本語で思考してくれて日本語で返してくれるらしいので、今度はこれをollamaで動かしてみたいなと思いました
モデルはここにあるそうです。
(ollamaじゃなくてもTransformer直接使っても良いかも)
https://huggingface.co/cyberagent/DeepSeek-R1-Distill-Qwen-32B-Japanese
https://huggingface.co/cyberagent/DeepSeek-R1-Distill-Qwen-14B-Japanese