本記事は、Lambda LabsのPublic Cloudを使ってLoRA(QLoRA中心)を手早く回すための実践ガイドです。インスタンス起動から学習スクリプト、推論、コスト最適化、トラブルシューティングまでを網羅します。
どの見出しレベルまで表示するかデフォルトH2H3H4
※ “arkb-toc-off”クラスを持つ見出しは表示されません。
概要
- 目的: A100 80GBを使い、QLoRAで大規模モデルを軽量学習する
- 対象読者: 初めてLambda LabsでGPUを借りてLoRAを回したい人
- ゴール: 学習と推論が動き、無駄課金を避ける運用まで到達
サービスとインスタンスタイプの選び方
- Public Cloud: すぐ使えるオンデマンドGPU。まずはこれで問題なし
- 1-Click Clusters: H100の大規模分散。分散学習や巨大推論で利用
- インスタンス例(推奨・この記事の前提)
- a100-80gb
- GPU: A100 ×1
- vCPU: 12
- RAM: 85GB
- 料金目安: 約 $1.79/h
- Base Imageの選び方
- すぐ学習したい: PyTorchなどのDLフレームワーク入り
- 自由に構成したい: 素のUbuntu
料金とストレージの考え方
- デフォルトで約200GB NVMe SSD(構成により増減)
- Filesystemは保持している限り課金が継続
- Running中: GPU/CPU課金 + Filesystem課金
- Stopped中: GPU/CPU課金なし + Filesystem課金あり
- TerminatedかつFilesystem削除: 課金ゼロ
- 使い終わりにデータを退避し、不要なFilesystemは削除が最も安全
セキュリティとネットワーク(Firewall)
- 基本はGlobal firewall rulesでOK
- 追加で必要になりやすいポート
- JupyterLab: TCP 8888
- APIサーバ: TCP 8000 や 443
- 注意: SSH(22)は必ず開ける。不要なポートは閉じる
事前準備(初回のみ)
- SSH鍵の用意と登録
- 手元でssh-keygenで作成
- Lambda Dashboard → SSH Keys に公開鍵を登録
- APIキーの発行
- Lambda Dashboard → API Keys
- 自動停止などのAPI操作用に環境変数へ保存
インスタンス起動
GUI
- Dashboard → Launch Instance
- Instance type: A100 80GB など
- Region: 近いリージョンを選択
- Base Image: Ubuntu + PyTorch 推奨
- Filesystem: 新規作成(200GB〜用途に応じて)
- Firewall: SSHのみ or 必要ポート最小限
- Launch → 数分でRunning
API(参考)
curl -u $LAMBDA_API_KEY: \\
-X POST \\
-H "Content-Type: application/json" \\
-d '{"instance_ids":["YOUR_INSTANCE_ID"]}' \\
<https://cloud.lambdalabs.com/api/v1/instance-operations/start>
接続と環境確認
# SSH接続
ssh -i ~/.ssh/your_key.pem ubuntu@<INSTANCE_IP>
# GPU確認
nvidia-smi
# Pythonと主要ライブラリ確認
python3 --version
pip list | grep torch
LoRA学習ワークフロー(QLoRA+FFN対象)
実行要件の目安
- A100 80GB ×1でもQLoRAなら実用的
- 20B級モデルでのSFT(約3万トークン×3エポック)で15〜40分目安
データ(SFT形式、JSONL)
{"messages":[
{"role":"system","content":"あなたは◯◯ドメインの厳密な回答者。根拠は文章からのみ。"},
{"role":"user","content":"第3章の◯◯について説明して"},
{"role":"assistant","content":"…独自資料に基づく模範解答…"}
]}
- packingは必ずON(高速化と安定化に効く)
LoRAの対象(FFNのみ)
- NeoX系: mlp.dense_h_to_4h, mlp.dense_4h_to_h
- Llama系参考: gate_proj, up_proj, down_proj
- 不一致時はprint(model)でモジュール名を確認し置換
LoRAを行う際、キー、クエリ、バリュー、アウトプットの計算で使われる層それぞれを少ない回数でも学習する方がどれか一つの層に何回も学習を行うよりも良い結果が得られます。
学習スクリプト例(PEFT/TRL)
# train_ffn_[lora.py](<http://lora.py>)
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from peft import LoraConfig, get_peft_model
from trl import SFTTrainer, SFTConfig
import torch, json
MODEL_NAME = "gpt-oss-20b" # 実際のHFリポ名に置換
DATA_PATH = "train.jsonl"
tok = AutoTokenizer.from_pretrained(MODEL_NAME, use_fast=True)
tok.pad_token = tok.eos_token
bnb = BitsAndBytesConfig(
load_in_4bit=True, bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16, bnb_4bit_use_double_quant=True
)
model = AutoModelForCausalLM.from_pretrained(
MODEL_NAME, quantization_config=bnb, torch_dtype=torch.bfloat16, device_map="auto"
)
lora = LoraConfig(
r=8, lora_alpha=16, lora_dropout=0.05,
target_modules=["mlp.dense_h_to_4h","mlp.dense_4h_to_h"], # Llama系なら gate/up/down_proj
task_type="CAUSAL_LM"
)
model = get_peft_model(model, lora)
def load_sft(path):
with open(path, "r", encoding="utf-8") as f:
for line in f:
yield json.loads(line)
train_dataset = list(load_sft(DATA_PATH))
cfg = SFTConfig(
output_dir="outputs_ffn_lora",
num_train_epochs=3,
per_device_train_batch_size=2,
gradient_accumulation_steps=16,
learning_rate=1e-4,
lr_scheduler_type="cosine",
warmup_ratio=0.05,
logging_steps=10,
save_steps=200,
bf16=True,
packing=True,
max_seq_length=2048
)
trainer = SFTTrainer(
model=model, tokenizer=tok, train_dataset=train_dataset,
dataset_text_field=None, formatting_func=None, args=cfg
)
trainer.train()
[trainer.save](<http://trainer.save>)_model("outputs_ffn_lora")
- 単機実行
python3 train_ffn_[lora.py](<http://lora.py>)
- 複数GPU(Accelerate)
accelerate config # 初回のみ
accelerate launch --num_processes 2 train_ffn_[lora.py](<http://lora.py>)
推論(LoRAアダプタ適用)
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel
import torch
base = "gpt-oss-20b"
tok = AutoTokenizer.from_pretrained(base, use_fast=True)
model = AutoModelForCausalLM.from_pretrained(base, load_in_4bit=True, device_map="auto")
model = PeftModel.from_pretrained(model, "outputs_ffn_lora").to(torch.bfloat16)
prompt = "独自資料の◯◯について根拠を述べて回答してください。"
ids = tok(prompt, return_tensors="pt").to(model.device)
out = model.generate(**ids, max_new_tokens=300)
print(tok.decode(out[0], skip_special_tokens=True))
コスト最適化(自動停止の仕組み)
- 発想: GPU使用率が0%ならAPIでStopを呼ぶ
- cronで1時間ごとにチェック
例: auto_stop.py
import subprocess, json, os
API_KEY = os.environ.get("LAMBDA_API_KEY")
INSTANCE_ID = os.environ.get("LAMBDA_INSTANCE_ID")
def get_gpu_usage():
result = subprocess.check_output(
["nvidia-smi", "--query-gpu=utilization.gpu", "--format=csv,noheader,nounits"]
).decode().strip().split("\\n")
usage = [int(x) for x in result if x.strip().isdigit()]
return sum(usage)/len(usage) if usage else 0
def stop_instance():
payload = json.dumps({"instance_ids": [INSTANCE_ID]})
cmd = [
"curl","-s","-u",f"{API_KEY}:",
"-X","POST","-H","Content-Type: application/json",
"-d",payload,"<https://cloud.lambdalabs.com/api/v1/instance-operations/stop>"
]
[subprocess.run](<http://subprocess.run>)(cmd, check=True)
if __name__ == "__main__":
avg = get_gpu_usage()
print(f"Average GPU Usage: {avg}%")
if avg == 0:
print(f"GPU idle → stopping {INSTANCE_ID}")
stop_instance()
else:
print("GPU in use → keep running")
cron登録例
0 * * * * /usr/bin/python3 /home/you/auto_[stop.py](<http://stop.py>) >> ~/auto_stop.log 2>&1
よくあるトラブルとFAQ
- Q. モジュール名が合わずLoRA対象にならない
- A. print(model)でFFNモジュール名を確認し、target_modulesを書き換える
- Q. OOMが出る
- A. per_device_train_batch_sizeを下げる。gradient_accumulation_stepsを上げる。max_seq_lengthを縮める
- Q. 出力が崩れる
- A. target_modulesにlm_headを追加して語彙選択を安定させる
- Q. 事実性が弱い
- A. RAG併用を検討。LoRAはスタイルや頻出知識の上書きに向く
- Q. 停止と終了の違いは?
- A. Stopは電源OFFだがディスクは残る。Terminateはサーバ削除で、Filesystemも削除すれば課金ゼロ
まとめ(最短フロー)
- インスタンス起動
- SSH接続
- 学習実行(QLoRA+packing ON)
- 推論でアダプタ適用
- データ退避
- StopまたはTerminate+Filesystem削除で無駄課金を防止