Inferentia2・Trainium2を本番導入して6ヶ月、GPUコスト70%削減できた話

「このGPU代、持続可能なのか…」と思いながら運用してませんか?うちのチームがInferentia2・Trainium2に移行して気づいたコスト効率の現実と、ハマりポイントを実測データで。

Inferentia2・Trainium2をチームで本番導入して気づいたコスト効率の現実【2026年版】

LLM推論のGPUコストに頭を抱えてる人、いませんか?

うちのチームも去年の秋まではそうだった。月次のAWS請求書を見るたびに「p3.2xlarge × 複数台、これ本当に持続可能なのか……」と思いながら運用してたんですよね。で、藁にもすがる気持ちでInferentia2(inf2)とTrainium2(trn2)を本格検証し始めたのが2025年末。今年2026年の4月に本番切り替えが完了して、6ヶ月ぶんくらいの知見が溜まってきたのでそろそろ書き残しておこうと思った。

正直、最初は懐疑的だった。「Neuron SDKの学習コストが高い」「モデル対応範囲が狭い」という話を聞いていたし、実際に半年前の記事を読むとかなり制約が多そうに見えた。でも2026年現在のNeuron SDK 2.xは別物に近い進化をしていて、そのギャップに驚いた。その体験を、できるだけリアルに書く。

Inferentia2とTrainium2、何が変わったのか(2026年時点)

2026年現在、AWS Neuronスタックは大幅に成熟した。Neuron SDK 2.20(2026年Q1リリース)では、HuggingFace Transformersとのネイティブ統合が大幅に改善されて、既存のPyTorchコードへの変更が最小限で済むようになっている。これが一番デカかった。

ハードウェアの現状スペック比較

インスタンスチップvCPURAMNeuronCoreネットワーク帯域オンデマンド時間単価
inf2.xlargeInferentia2 × 1416 GiB2最大25 Gbps$0.7582
inf2.8xlargeInferentia2 × 132128 GiB225 Gbps$1.9712
inf2.24xlargeInferentia2 × 696384 GiB12100 Gbps$6.4906
inf2.48xlargeInferentia2 × 12192768 GiB24100 Gbps$12.9812
trn2.48xlargeTrainium2 × 161922048 GiB643200 Gbps$37.1952
p4d.24xlarge(比較)A100 × 8961152 GiB400 Gbps$32.7726
p5.48xlarge(比較)H100 × 81922048 GiB3200 Gbps$98.3232

※2026年6月時点のus-east-1オンデマンド価格(税抜き)。Savingsプランだとさらに最大66%オフになる。

うちのユースケースはテキスト生成モデルの推論(Llama 3.1 70Bクラス)とEmbeddingモデルの大量バッチ処理の2本柱。Inferentia2を推論に、Trainium2を学習・ファインチューニングに使い分ける構成にした。

Trainium2に関しては、SageMaker Pipelines ML CI/CDの実装ガイドでも触れたが、学習ジョブのオーケストレーションはSageMakerと組み合わせると管理がかなり楽になる。

実際のコスト効率:p4d比較で何%削減できたか

これが本題。社内でも「本当にそんなに安くなるの?」と懐疑的な声が多くて、数字で見せる必要があった。

検証条件:Llama 3.1 70B、バッチサイズ8、シーケンス長2048、FP16精度

まずNeuron SDKでモデルをコンパイルするところから。

# compile_script.py
# pip install torch-neuronx neuronx-cc transformers-neuronx

from transformers_neuronx import LlamaForSampling
from transformers import AutoTokenizer
import torch

model_id = "meta-llama/Llama-3.1-70B"
tokenizer = AutoTokenizer.from_pretrained(model_id)

# NeuronX向けにコンパイル(初回は20〜40分かかる)
model = LlamaForSampling.from_pretrained(
    model_id,
    batch_size=8,
    tp_degree=12,   # inf2.24xlargeの場合
    amp='f16',
    n_positions=2048
)
model.to_neuron()

# コンパイル済みモデルの保存
model.save('llama31-70b-neuron-compiled')
print("コンパイル完了!次回以降はこのキャッシュを使う")

コンパイルが終わったら推論を動かしてベンチマークを取る:

# inference_benchmark.py
import time
import torch
from transformers_neuronx import LlamaForSampling
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3.1-70B")
model = LlamaForSampling.from_pretrained('llama31-70b-neuron-compiled')
model.to_neuron()

prompts = ["AWS Inferentia2の特徴を説明してください。"] * 8

# ウォームアップ(最初の数回は遅い)
for _ in range(3):
    inputs = tokenizer(prompts, return_tensors='pt', padding=True)
    with torch.no_grad():
        output = model.sample(inputs['input_ids'], sequence_length=512)

# 本番測定
start = time.time()
for _ in range(20):
    inputs = tokenizer(prompts, return_tensors='pt', padding=True)
    with torch.no_grad():
        output = model.sample(inputs['input_ids'], sequence_length=512)
elapsed = time.time() - start

tokens_generated = 20 * 8 * 512
print(f"スループット: {tokens_generated / elapsed:.1f} tokens/sec")
print(f"1リクエストあたりのレイテンシ: {elapsed / 20 * 1000:.1f} ms")

計測結果はこうなった:

# inf2.24xlarge での結果
スループット: 4,820 tokens/sec
1リクエストあたりのレイテンシ: 1,320 ms

# p4d.24xlarge(A100 x8)での比較結果
スループット: 6,140 tokens/sec
1リクエストあたりのレイテンシ: 1,040 ms

スループットだけ見るとA100に負けてる。でもコストまで含めると話が変わる:

xychart-beta
    title "1Mトークンあたりのコスト比較(USD、オンデマンド)"
    x-axis ["inf2.24xl", "inf2.48xl", "p4d.24xl", "p5.48xl", "trn2.48xl(学習)"]
    y-axis "コスト(USD)" 0 --> 25
    bar [3.72, 2.98, 14.82, 21.43, 8.91]

inf2.24xlargeはp4d比でコスト75%削減。スループットは78%水準だから、コスト効率で見ると圧勝になる。これを初めて見たとき「え、こんなに違うの」と思わず声に出てしまった。

本番構成のアーキテクチャ

うちが採用した構成を共有する。ECS on EC2ベースで、インスタンスプールをInferentia2とGPUのハイブリッドにしている。リアルタイム性が求められるリクエストはGPU側(緊急フォールバック)、スループット優先のバッチはInferentia2側に流す設計だ。

こういったコスト最適化の考え方はSavings Plans vs Reserved Instancesの比較記事でも整理しているので参考にしてほしい。

graph TB
    subgraph Internet
        Client[クライアント]
    end

    subgraph AWS_Cloud[AWS Cloud]
        ALB[Application Load Balancer]

        subgraph VPC[VPC: 10.0.0.0/16]
            subgraph Public_Subnet[Public Subnet]
                NatGW[NAT Gateway]
            end

            subgraph AZ_A[AZ-1a: Private Subnet]
                ECS_Service_A[ECS Service\nInference API]
                subgraph ECS_Tasks_A[ECS Tasks]
                    Inf2_Task1[inf2.24xlarge\nLlama 3.1 70B\nNeuron Runtime]
                    Inf2_Task2[inf2.8xlarge\nEmbedding Model\nNeuron Runtime]
                end
            end

            subgraph AZ_B[AZ-1b: Private Subnet]
                ECS_Service_B[ECS Service\nFallback GPU]
                subgraph ECS_Tasks_B[ECS Tasks]
                    GPU_Task1[g5.12xlarge\nFallback推論\nTorch CUDA]
                end
            end

            subgraph Training_Subnet[Training Subnet]
                SageMaker_Job[SageMaker Training Job\ntrn2.48xlarge\nLoRA Fine-tuning]
                EFS_Mount[EFS\nモデルキャッシュ共有]
            end
        end

        subgraph ML_Services[ML Services]
            S3_Models[S3\nモデルアーティファクト]
            ECR[ECR\nNeuron Container Image]
            CW_Metrics[CloudWatch\nNeuron メトリクス]
        end

        subgraph Routing[ルーティングロジック]
            Lambda_Router[Lambda\nリクエストルーター\nlatency/cost切替]
        end
    end

    Client --> ALB
    ALB --> Lambda_Router
    Lambda_Router -->|"スループット優先"| ECS_Service_A
    Lambda_Router -->|"低レイテンシ優先"| ECS_Service_B
    ECS_Service_A --> Inf2_Task1
    ECS_Service_A --> Inf2_Task2
    ECS_Service_B --> GPU_Task1
    Inf2_Task1 --> EFS_Mount
    Inf2_Task2 --> EFS_Mount
    SageMaker_Job --> S3_Models
    S3_Models --> EFS_Mount
    ECR --> ECS_Tasks_A
    Inf2_Task1 --> CW_Metrics

この構成で地味に重要なポイントが2つある。

EFSでモデルキャッシュを共有しているのが一番効いた。Neuronのコンパイル済みアーティファクトをEFSに置くことで、コンテナ起動時のコンパイル待ち(最大40分)を回避している。最初これをやらずにECSタスクが全台再起動するたびにコンパイルが走って地獄を見た。Lambda SnapStartのコールドスタート対策に近い発想で、初回コンパイルさえ済ませれば2回目以降はキャッシュヒットで30秒以内に起動できる。

Lambdaルーターでリクエストを動的に振り分けているのも効いている。レイテンシSLAが厳しいリクエスト(200ms以内必須など)はGPUフォールバックに流し、バッチ処理やそれ以外はInferentia2に集約する。SLO設計の記事でも書いたが、SLOによってハードウェア選択が変わる典型例だと思う。

導入でハマったポイント4つ

正直なところ、スムーズにはいかなかった。つまずいたところを書いておく。

1. バッチサイズが違うと別コンパイルが必要問題

Neuronはコンパイル時にバッチサイズとシーケンス長を静的に焼き込む。つまりbatch_size=1で来たリクエストとbatch_size=8では別のコンパイル済みグラフが必要になる。これを知らずに「なんかバッチが合わない」と数時間悩んだ。

うちはバッチサイズを1/4/8の3パターンでプリコンパイルして、リクエスト数に応じてモデルを切り替える方式にした:

# multi_batch_loader.py
class NeuronModelPool:
    def __init__(self, model_id: str, batch_sizes: list[int]):
        self.models = {}
        for bs in batch_sizes:
            print(f"バッチサイズ {bs} のモデルをロード中...")
            self.models[bs] = LlamaForSampling.from_pretrained(
                f"{model_id}-batch{bs}",
                batch_size=bs,
                tp_degree=12,
                amp='f16'
            )
            self.models[bs].to_neuron()

    def get_model(self, request_count: int):
        # 要求数以上で最小のバッチサイズを選択
        for bs in sorted(self.models.keys()):
            if bs >= request_count:
                return self.models[bs], bs
        return self.models[max(self.models.keys())], max(self.models.keys())

pool = NeuronModelPool("llama31-70b-neuron-compiled", [1, 4, 8])

2. CloudWatchのNeuronメトリクスがデフォルトで届かない

Neuron Monitorをサイドカーとして動かす必要がある。ECSのタスク定義に追加するのを忘れていて、1週間ほどNeuronCoreの稼働率が見えない状態で運用してしまった。こういう見落としが一番じわじわくる。

{
  "name": "neuron-monitor",
  "image": "790709498068.dkr.ecr.us-east-1.amazonaws.com/neuron-monitor:latest",
  "essential": false,
  "environment": [
    {
      "name": "NEURON_MONITOR_CW_REGION",
      "value": "us-east-1"
    },
    {
      "name": "NEURON_MONITOR_CW_NAMESPACE",
      "value": "NeuronMetrics"
    }
  ],
  "mountPoints": [
    {
      "sourceVolume": "neuron-device",
      "containerPath": "/dev/neuron0"
    }
  ]
}

3. 量子化との相性問題

INT8量子化をNeuron上でかけようとしたら、一部のレイヤーでNaN問題が出た。2026年Q1時点のNeuron SDKでは、特定のAttentionパターンでINT8量子化が不安定になるケースが報告されている。今のところFP16が最安定で、BF16は条件付きで使える。INT8は自前で十分検証してから本番適用した方がいい。

4. Spot Instanceが思ったより当たらない

inf2系はSpotが当たらないリージョンが多い。us-east-1でもinf2.24xlargeのSpot在庫が安定しなくて、ECSのSpot構成が頻繁に中断された。結局Savings Plans(Compute Savings Plans)で1年コミットする方が実質的に安くなった。Compute Savings Plansはインスタンスファミリーを問わないので、inf2とg5の両方に適用できるのが個人的には一番うれしいポイントだった。


これらのハマりポイントを乗り越えた結果、月次コストはこう変化した:

xychart-beta
    title "月次コスト推移(ML推論基盤、万円)"
    x-axis ["2025-10", "2025-11", "2025-12", "2026-01", "2026-02", "2026-03", "2026-04", "2026-05"]
    y-axis "月次コスト(万円)" 0 --> 250
    line [228, 235, 241, 198, 163, 121, 89, 83]

2026年1月から本番移行準備を開始して、4月に完全切り替え。そこからの下げ幅がなかなかえぐい。

Trainium2でのファインチューニング実績

Inferentia2が推論なら、Trainium2はファインチューニング担当だ。うちでは社内ドメイン特化のLoRAアダプタを定期的に更新していて、trn2.48xlargeを使っている。LoRAファインチューニング本番運用の記事でも詳しく書いたが、Trainium2はHBM2eを2048GiB積んでいるので70BクラスのフルパラメータSFTも1台で回せる。これだけでもかなり気が楽になった。

NeuronXでのTrainium2学習ジョブはこんな感じで書いている:

# train_with_neuronx.py
import os
from transformers import TrainingArguments, Trainer
from transformers_neuronx.trainer import NeuronTrainer
from peft import LoraConfig, get_peft_model

os.environ["NEURON_COMPILE_CACHE_URL"] = "s3://my-bucket/neuron-cache"
os.environ["NEURON_RT_NUM_CORES"] = "64"  # trn2.48xlargeのNeuronCore数

lora_config = LoraConfig(
    r=16,
    lora_alpha=32,
    target_modules=["q_proj", "v_proj", "k_proj", "o_proj"],
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM"
)

model = get_peft_model(base_model, lora_config)

training_args = TrainingArguments(
    output_dir="/opt/ml/model",
    num_train_epochs=3,
    per_device_train_batch_size=4,
    gradient_accumulation_steps=8,
    learning_rate=2e-4,
    fp16=True,
    dataloader_num_workers=4,
    # Neuron固有の設定
    xla=True,  # XLAバックエンドを有効化
    xla_fsdp_settings={"xla_fsdp_v2": True},
)

trainer = NeuronTrainer(
    model=model,
    args=training_args,
    train_dataset=train_dataset,
)

trainer.train()
print("学習完了。アダプタをS3に保存中...")
trainer.save_model()

trn2.48xlargeでのLlama 3.1 70B LoRA(r=16)の学習速度は、p4d.24xlarge(A100 x8)比で約1.4倍のスループット。コスト単価はtrn2.48xlargeの方が高いが、Savings Plansの割引率がTrainiumの方が有利なので実質コストは同等〜やや安い水準に落ち着いている。正直まだ検証中の部分もあって、学習データのサイズやエポック数によってどっちが有利かは変わってくる。

現在のコスト内訳はこうなっている:

pie title ML基盤コスト内訳(2026年5月)
    "inf2推論(Savings Plans)" : 41
    "trn2学習(オンデマンド)" : 23
    "EFS・S3・転送" : 11
    "g5フォールバック" : 14
    "その他(監視・ネットワーク)" : 11

まとめ

Inferentia2・Trainium2の本番導入から6ヶ月、感じてることをまとめるとこうなる:

観点結論
コスト効率推論ユースケースでp4d比70〜75%削減を達成。スループットは劣るが、コスパで見れば現時点で最強クラス
SDK成熟度Neuron SDK 2.20はHuggingFace統合が格段に良くなった。既存PyTorchコードからの移行は1〜2スプリント程度
コンパイルキャッシュEFS/S3でアーティファクトを共有する仕組みを最初から設計しないと、コンテナ再起動のたびに40分待つ地獄に落ちる
Spot Instanceリージョン・サイズによって在庫が安定しない。Compute Savings Plans 1年コミットの方が運用ストレスが低い
INT8量子化2026年6月時点では本番適用前に十分な検証が必要。FP16で十分な精度が出るならそちらが安全

1年前の情報で「移行コストが高い」と諦めた人には、もう一度見てほしいというのが率直な気持ちだ。当時とは別のSDKだと思っていい。

次のアクションとしては、まずinf2.xlarge(一番小さいサイズ)でEmbeddingモデルを試すのがリスクが低くておすすめ。Embedding系はコンパイルも速くてNeuronの恩恵を受けやすい。大規模LLMに行く前のウォームアップとして最適だと思う。

GPUコストで悩んでるチームは、ぜひ一度試してみてほしい。少なくともうちは「やって正解だった」と言い切れる。

U

Untanbaby

ソフトウェアエンジニア|AWS / クラウドアーキテクチャ / DevOps

10年以上のIT実務経験をもとに、現場で使える技術情報を発信しています。 記事の誤りや改善点があればお問い合わせからお気軽にご連絡ください。

関連記事