こしあん
2025-03-05

LLMの推論システムの定式化(2):価格理論の推論速度の統合


34{icon} {views}

LLM推論にかかるGPUインスタンス料金やトークン生成速度、サービス提供単価などを数式で統合し、黒字化の条件を理論的に示した。実験結果では、GPU以外のボトルネックの影響は小さく、高い稼働率と高速なトークン生成が収益化を左右することが明らかになった。

はじめに

  • 前回の記事で、LLM(それ以外のモデルも含む)の推論システムにおけるGPU以外のボトルネックを定式・定量評価できたので、今回はもっと包括的な損益と推論速度の指標を統合し、包括的なコスト理論を作る。
  • これで行いたいことは、「GPUインスタンスの価格」「入出力のトークン数」「サービス提供時のトークン数の価格」「GPU以外のオーバーヘッド時間」「トークン生成速度」などのパラメーターを統合して、損益分岐点(黒字化するための条件)を理論的に計算するというものである
  • LLMの推論APIがなかなか収益を取るのが大変ということは体感的にわかっているが、どのラインなら収益を取れるのかといった包括的な議論を行うための基本的な原則・数値的なシミュレーション例として有効だと考えられる。

前回の振り返り

LLMのやっていることは(MLモデル一般だけど)、以下のマッピング

$$x_i\to \hat{y}_i$$

  • プロンプトが$x_i$であったときに、返答が$\hat{y}_i$
  • データセット全体は$(x_i, y_i)_{i=1}^N$
  • $y_i$は正解データ。通常$y_i$はLLMの訓練するときに利用するもので、推論時は観測できない

$i$番目の推論時間を$T_i$としたとき以下の式で表される。

$$T_i = \mathcal{E}(x_i) + \mathcal{D}(\hat{y}_i|x_i)$$

狭義には、$\mathcal{E}(\cdot)$はトークナイザーなどのエンコード関数、$\mathcal{D}(\cdot)$はLLMなどのデコード関数を示す。

推論システム全体を考えると、SQSからのポーリングやログの書き込みなどの、GPU以外のオーバーヘッドは$\mathcal{E}$側で含める。トークナイザーなどの前処理もこちら。$\mathcal{D}(\cdot)$は純粋なLLMを推論するときの、GPU時間で、Token Per Second(TPS)などの一般的な速度の指標の関数となる。

トークナイザーの計算量など、一定入力トークン数に比例する部分もあるので、プリミティブには$\mathcal{E}(x_i)$は、入力トークン数$|x_i|$の一次関数で近似されれる。これは実験的に過去の記事で検証した

$$\mathcal{E}(x_i)\sim \alpha +\beta|x_i|$$

$\beta$はトークナイザーの計算量など、入力トークン数に対する比例係数で、$\alpha$は固定のオーバーヘッド、すなわち入力SQSからのポーリングやログの書き込み(書き込みをSQSや非同期処理でオフロードした場合も含む)を表す。また、$\alpha$は厳密には、$i$番目のサンプルが入力SQSのバッチ削除のタイミングである($B_i=1$)かどうかで変わり、

$$\mathcal{E}(x_i)\sim\alpha_1 + \alpha_2\mathbf{1}_{B_i=1} + \beta|x_i|$$

のような関数で表現されるが、$\alpha_1, \alpha_2$は平均的には$\alpha$に吸収できる。

今回はこれを発展して、LLMのシステムの損益分岐点、GPUの推論速度、LLMサービスの料金なども組み合わせて定式化していく。

記号の定義

  • 入力トークン数:$|x_i|$
  • 出力トークン数:$|\hat{y_i}|$
  • GPUインスタンスの価格:$p$(単位:USD/hr)
  • サービス提供したときの単位あたりの入力トークンの価格:$\gamma$(単位:USD/Mtoken)
  • サービス提供したときの単位あたりの出力トークンの価格:$\delta$(単位:USD/Mtoken)
  • LLMの出力トークン速度:$\lambda$(単位:token/second)

各単位は、

  • 1時間あたりの秒数:$u_h=3600$
  • 価格を表すときのトークン単位:$u_M=1000000$

1秒あたりのGPUインスタンス価格は、以下の式で表される(単位:USD/second)。

$$\frac{p}{u_h}$$

1トークンあたりの価格は以下の式で表される(単位:USD/token)。

$$\frac{\gamma}{u_M}, \frac{\delta}{u_M}$$

また、OpenAI、Gemini、AnthropicなどのLLMのAPIサービスを見ると、単位あたりの出力トークンは、入力トークンの定数倍で表されることが多い。この定数を$k$とおくと、

$$\delta = k\gamma$$

例えばOpenAIのgpt-4oの場合、1Mトークンあたり入力:出力=2.5ドル:10ドルなので、$k=4$。AnthropicのClaude 3.7 Sonnetの場合は、入力:出力=3:15なので、$k=5$となる。

損益分岐点の定式化

$i$番目のデータに関する推論所要時間は、以下の式で定義した。

$$T_i = \mathcal{E}(x_i) + \mathcal{D}(\hat{y}_i|x_i)$$

$(x_i, y_i)_{i=1}^N$のデータセット全体に対する総所要時間は以下の通りである。

$$\sum_{i=1}^NT_i = \sum_{i=1}^N\mathcal{E}(x_i) + \sum_{i=1}^N\mathcal{D}(\hat{y}_i|x_i)$$

$\mathcal{D}(\cdot)$はLLMのデコード側の処理であるため、この所要時間は、出力トークンをToken per secondで割ったものと仮定できる(以降Σの範囲を省略して書く)。

$$\mathcal{D}(\hat{y}_i|x_i)=\frac{\sum|\hat{y}_i|}{\lambda}$$

LLMのサービスにかかる費用を、GPUインスタンスの費用だけと考えた場合、サービスとして採算を取る場合、データセット全体に対する推論費用は以下の式を満たす必要がある。

GPUインスタンスだけというのは極端な仮定であるが、AWSなどのクラウドサービスは時間課金のものが多いため、他の費用を含める場合はこの$p$に吸収させて計算する。実際一番高いのがGPUインスタンスの費用なので、この仮定をおいてもそこまで不思議ではない。

$$\frac{\gamma}{u_M}\sum|x_i| + \frac{\delta}{u_M}\sum|\hat{y}_i|\geq \frac{p}{u_h}\sum T_i$$

左辺が収益であり、右辺が費用である。サービス提供して黒字になるためには、上記の式を満たさなくてはいけない。これまでの仮定を用いると、以下の式に変形できる。

$$\frac{\gamma}{u_M}\sum|x_i| + \frac{k\gamma}{u_M}\sum|\hat{y}_i|\geq \frac{p}{u_h}\biggl(\sum\mathcal{E}(x_i) + \frac{\sum|\hat{y}_i|}{\lambda}\biggr)$$

Σの中の値は、ほとんどが実験的に求まる値である。

  • $\sum|x_i|$:データセット全体の入力トークン数。入力のプロンプトデータが与えられた時点で既知。
  • $\sum|\hat{y}_i|$:データセット全体の出力トークン数。これは推論してみないことには未知なので、既存のAPIなどで出力トークン数を計測して実験的に求める。
  • $\sum\mathcal{E}(x_i)$:LLMのGPU以外のオーバーヘッド。前回の記事のようにオーバーヘッドを計測する。オーバーヘッドの具体的な値よりも、オーバーヘッドがどの程度低下したらどの程度の損益が改善するなどをシミュレートできるのがこの枠組みのポイント。

損益分岐点のトークン生成速度

上記の式を$\lambda$について解けば、「このインスタンス・入力トークン・出力トークンの条件で黒字化するには、このトークン生成速度(Token per second)以上必要」が理論的に計算できる。

$$\lambda \geq \frac{u_M\cdot p\sum|\hat{y}_i|}{u_h\cdot\gamma(\sum|x_i|+k\sum|\hat{y}_i|)-u_M\cdot p\sum \mathcal{E}(x_i)}$$

※分母は負でないことが条件

損益分岐点の単位価格

同様に、先程の式を$\gamma$について解けば、「このインスタンス・入力トークン・出力トークンの条件で黒字化するには、サービス提供価格(入力トークンの単位価格:USD / Mtoken)をいくら以上必要」が理論的に計算できる。出力トークンの単位価格は$k\gamma$で計算できる。

$$\gamma \geq \frac{u_M\bigl(\lambda\sum\mathcal{E}(x_i)+\sum|\hat{y}_i|\bigr)}{u_h\cdot \lambda\bigl(\sum|x_i|+k\sum|\hat{y}_i|\bigr)}\cdot p$$

損益分岐点のインスタンス価格

同様に、先程の式を$p$について解けば、「この入力・出力トークン数、生成速度、サービス提供価格が与えられたときに黒字化するためには、いくら以下のインスタンス(USD / hr)にしなければいけないのか」が理論的に計算できる。

$$p \leq \frac{u_h\cdot \lambda\bigl(\sum|x_i|+k\sum|\hat{y}_i| \bigr) }{u_M\bigl( \lambda\sum\mathcal{E}(x_i)+\sum|\hat{y}_i|\bigr)}\cdot\gamma$$

Pythonコード

上の3つの式をPythonコードで書くと以下の通り

def calculate_required_lambda(sum_x, sum_yhat, sum_E, p, gamma, u_M=1000000, u_h=3600, k=1.0):
    """
    損益分岐点のトークン生成速度 λ(tokens/second)を計算する関数

    式:
      λ >= (u_M * p * sum_yhat) / (u_h * γ * (sum_x + k * sum_yhat) - u_M * p * sum_E)

    Parameters:
      sum_x   : ∑|x_i|   (入力トークン総数)
      sum_yhat: ∑|ŷ_i|   (出力トークン総数)
      sum_E   : ∑𝓔(x_i)  (エンコード処理などのオーバーヘッド総和)
      p       : GPUインスタンス料金 (USD/hr)
      gamma   : サービス提供単位価格 (USD/Mtoken) ※出力トークンは k·γ で評価
      u_M     : トークンの単位(通常1,000,000)
      u_h     : 1時間の秒数(通常3600)
      k       : 出力トークンの価格倍率

    Returns:
      λ_min (tokens/second):黒字化するために必要な最低トークン生成速度
    """
    numerator = u_M * p * sum_yhat
    denominator = u_h * gamma * (sum_x + k * sum_yhat) - u_M * p * sum_E
    if denominator <= 0:
        raise ValueError("分母が非正です。入力パラメータを確認してください。")
    lambda_min = numerator / denominator
    return lambda_min


def calculate_minimum_gamma(sum_x, sum_yhat, sum_E, p, lambda_value, u_M=1000000, u_h=3600, k=1.0):
    """
    損益分岐点のサービス提供単位価格 γ(USD/Mtoken)を計算する関数

    式:
      γ >= (u_M*(λ*sum_E + sum_yhat)) / (u_h * λ * (sum_x + k*sum_yhat)) * p

    Parameters:
      sum_x   : ∑|x_i|   (入力トークン総数)
      sum_yhat: ∑|ŷ_i|   (出力トークン総数)
      sum_E   : ∑𝓔(x_i)  (エンコード処理などのオーバーヘッド総和)
      p       : GPUインスタンス料金 (USD/hr)
      lambda_value: トークン生成速度 (tokens/second)
      u_M     : トークンの単位(通常1,000,000)
      u_h     : 1時間の秒数(通常3600)
      k       : 出力トークンの価格倍率

    Returns:
      γ_min (USD/Mtoken):黒字化するために必要な最低サービス単位価格
    """
    numerator = u_M * (lambda_value * sum_E + sum_yhat)
    denominator = u_h * lambda_value * (sum_x + k * sum_yhat)
    if denominator == 0:
         raise ValueError("分母が0です。入力パラメータを確認してください。")
    gamma_min = (numerator / denominator) * p
    return gamma_min


def calculate_max_instance_price(sum_x, sum_yhat, sum_E, gamma, lambda_value, u_M=1000000, u_h=3600, k=1.0):
    """
    損益分岐点のインスタンス価格 p(USD/hr)の上限を計算する関数

    式:
      p <= (u_h * λ * (sum_x + k * sum_yhat)) / (u_M * (λ*sum_E + sum_yhat)) * γ

    Parameters:
      sum_x   : ∑|x_i|   (入力トークン総数)
      sum_yhat: ∑|ŷ_i|   (出力トークン総数)
      sum_E   : ∑𝓔(x_i)  (エンコード処理などのオーバーヘッド総和)
      gamma   : サービス提供単位価格 (USD/Mtoken)
      lambda_value: トークン生成速度 (tokens/second)
      u_M     : トークンの単位(通常1,000,000)
      u_h     : 1時間の秒数(通常3600)
      k       : 出力トークンの価格倍率

    Returns:
      max_p (USD/hr):黒字化するために許容される最大のGPUインスタンス料金
    """
    numerator = u_h * lambda_value * (sum_x + k * sum_yhat)
    denominator = u_M * (lambda_value * sum_E + sum_yhat)
    if denominator == 0:
         raise ValueError("分母が0です。入力パラメータを確認してください。")
    max_p = (numerator / denominator) * gamma
    return max_p

実験からの結果

以前の実験より、各パラメーターは以下の値であることが求まっている。エンコード処理などのオーバーヘッド総和$\sum\mathcal{E}(x_i)$は、いろんなパターンがあったが最も現実に近いかつオーバーヘッドが少ない(5)のケースを用いた。これの実測値は以下の通り。

  • アイテム数:34368
  • 入力トークン総数sum_x: 13,822,778
  • 出力トークン総数sum_yhat: 20,953,143
  • オーバーヘッド時間sum_E: 98.887

適当に試算してみる

すごいざっくりとした計算だが、例えば、東京リージョンのGPUインスタンスで、VRAMがL4×4のg6.12xlargeの場合はオンデマンドで6.67USD/hrである。L4はVRAMが1枚あたり24GBあり、12xlargeまで積めば4枚ついてくるのであらかたのLLMはデプロイできると思われる。

また、LLM Performance LeaderBoardには、各LLMのトークン/sが書かれている。ここではClaude 3.7 Sonnetの例を想定し、

  • トークン生成速度lambda_value:80.0
  • サービス提供単位価格gamma: 3
  • 出力トークンの価格倍率k: 5

で計算してみる。この条件で計算すると、

【損益分岐点のトークン生成速度】必要最低生成速度 λ: 109.18 tokens/s
【損益分岐点の単位価格】必要最低サービス提供単位価格 γ: 4.09 USD/Mtoken
【損益分岐点のインスタンス価格】許容される最大 GPU インスタンス料金 p: 4.89 USD/hr

どれも損益分岐点を上回ることができなく、何かしらの工夫が必要である。そもそもL4で110TPSが出るのかというのが一番のあやふやポイントであるし、80TPSなら4ドル:20ドル程度でないと採算が合わない。リザーブドなりSavings Planなどを使えばギリトントンぐらいまでは行けるだろう。

ただし、これはインスタンスの稼働率が100%の想定であり、稼働率が下回ればとたんに赤字を掘るだけになってしまう。

システム的な分析

「最低トークン生成速度(λ)」「最低サービス提供単価(γ)」「最大インスタンス価格(p)」の3つのパラメーターについて、他のパラメーターを変数にして表形式でプロットしてみる。

p   0.5 1   2   3   5   7   10  15  20  30  45
γ   0.1 0.2 0.5 0.8 1   2   3   5   10  20  50
λ   10  20  50  70  100 150 200 500 700 1000    

その他の値は以下で固定する。

sum_x = 13822778      # 入力トークン総数
sum_yhat = 20953143   # 出力トークン総数
sum_E = 98.887        # 全サンプルのエンコードオーバーヘッドの総和
k = 5.0               # 出力トークンの価格倍率
u_M = 1000000         # 単位変換用定数(百万)
u_h = 3600            # 単位変換用定数(時間→秒)

最低トークン生成速度(λ)

縦軸が「インスタンス価格(p)」、横軸が「サービス提供単価(γ)」である。例えばo1-miniのγ=0.15であるが、一番左の列を見ると途方もないトークン生成速度が要求される。おそらくインスタンス価格のpは1~2ドルでないと現実的に厳しいかと思われる。あとはシェアを取って平均稼働率を100%近い状況を作り出せるかというのもポイントだろう。

p/γ 0.1 0.2 0.5 0.8 1 2 3 5 10 20 50
0.5 245.7 122.8 49.1 30.7 24.5 12.3 8.2 4.9 2.5 1.2 0.5
1 491.9 245.7 98.2 61.4 49.1 24.5 16.4 9.8 4.9 2.5 1.0
2 986.2 491.9 196.5 122.8 98.2 49.1 32.7 19.6 9.8 4.9 2.0
3 1482.7 738.8 294.9 184.2 147.3 73.6 49.1 29.5 14.7 7.4 2.9
5 2482.8 1234.1 491.9 307.2 245.7 122.8 81.8 49.1 24.5 12.3 4.9
7 3492.2 1731.8 689.4 430.3 344.1 171.9 114.6 68.7 34.4 17.2 6.9
10 5024.4 2482.8 986.2 615.3 491.9 245.7 163.7 98.2 49.1 24.5 9.8
15 7627.0 3746.1 1482.7 924.3 738.8 368.7 245.7 147.3 73.6 36.8 14.7
20 10292.8 5024.4 1981.6 1234.1 986.2 491.9 327.7 196.5 98.2 49.1 19.6
30 15823.5 7627.0 2986.3 1856.6 1482.7 738.8 491.9 294.9 147.3 73.6 29.5
45 24656.0 11650.2 4511.2 2797.2 2231.9 1110.1 738.8 442.6 221.1 110.5 44.2

最低サービス提供単価(γ)

縦軸が「インスタンス価格(p)」、横軸が「トークン生成速度(λ)」である。ここからわかることは割と速度が命で(稼働率100%なら)、500~1000TPS出てしまえば結構なコスト圧縮ができる。ビックテックがこぞって最新鋭のGPUを買うのも理解できる。おそらく「リアルタイムバッチ推論で速度出せるか?」がかなりのキーポイントになるだろう。

p/λ 10 20 50 70 100 150 200 500 700 1000
0.5 2.45 1.23 0.49 0.35 0.25 0.16 0.12 0.05 0.04 0.02
1 4.91 2.45 0.98 0.70 0.49 0.33 0.25 0.10 0.07 0.05
2 9.82 4.91 1.96 1.40 0.98 0.65 0.49 0.20 0.14 0.10
3 14.72 7.36 2.95 2.10 1.47 0.98 0.74 0.30 0.21 0.15
5 24.54 12.27 4.91 3.51 2.46 1.64 1.23 0.49 0.35 0.25
7 34.36 17.18 6.87 4.91 3.44 2.29 1.72 0.69 0.49 0.35
10 49.08 24.54 9.82 7.01 4.91 3.27 2.46 0.98 0.70 0.49
15 73.62 36.81 14.73 10.52 7.37 4.91 3.68 1.48 1.06 0.74
20 98.16 49.08 19.64 14.03 9.82 6.55 4.91 1.97 1.41 0.99
30 147.25 73.63 29.45 21.04 14.73 9.82 7.37 2.95 2.11 1.48
45 220.87 110.44 44.18 31.56 22.10 14.73 11.05 4.43 3.17 2.22

最大インスタンス価格(p)

縦軸が「サービス提供単価(γ)」、横軸が「トークン生成速度(λ)」である。左上のゾーンはかなり厳しく、もはやGPUインスタンスは採算が取れない領域。時間5ドルのインスタンスで採算を取りたいなら、20TPSならサービス提供単価が入力10-20USD・出力はその5倍、100TPSまで行ければサービス提供単価が2~3USDになり比較的現実味を帯びていく。このへんはいろんなデバイスで試してできるできないを見極めていくのだろう。

γ/λ 10 20 50 70 100 150 200 500 700 1000
0.1 0.02 0.04 0.10 0.14 0.20 0.31 0.41 1.02 1.42 2.03
0.2 0.04 0.08 0.20 0.29 0.41 0.61 0.81 2.03 2.84 4.06
0.5 0.10 0.20 0.51 0.71 1.02 1.53 2.04 5.08 7.11 10.14
0.8 0.16 0.33 0.81 1.14 1.63 2.44 3.26 8.13 11.37 16.22
1 0.20 0.41 1.02 1.43 2.04 3.05 4.07 10.16 14.22 20.28
2 0.41 0.81 2.04 2.85 4.07 6.11 8.14 20.33 28.43 40.56
3 0.61 1.22 3.06 4.28 6.11 9.16 12.21 30.49 42.65 60.84
5 1.02 2.04 5.09 7.13 10.18 15.27 20.36 50.82 71.08 101.40
10 2.04 4.07 10.19 14.26 20.37 30.54 40.71 101.63 142.15 202.79
20 4.07 8.15 20.37 28.52 40.73 61.08 81.42 203.27 284.31 405.58
50 10.19 20.37 50.93 71.29 101.83 152.70 203.56 508.17 710.77 1013.96

GPU以外のボトルネックの影響

サービス提供単価、もしくは最大インスタンス価格の式は単純な比例な式だった。以下は最大インスタンス価格の式である。このように$\gamma$以外は単純な比例係数になっている。

$$p \leq \frac{u_h\cdot \lambda\bigl(\sum|x_i|+k\sum|\hat{y}_i| \bigr) }{u_M\bigl( \lambda\sum\mathcal{E}(x_i)+\sum|\hat{y}_i|\bigr)}\cdot\gamma$$

ここで気になるのは「GPU以外のボトルネックを解消したらどの程度コストパフォーマンスが変わるのか?」という点である。式を見れば分かる通り比例係数の部分のみ着目すれば良く、$\sum\mathcal{E}(x_i)$と$\lambda$の単純な関係になっている。

縦軸に「トークン生成速度(λ)」、横軸に「GPU以外のボトルネック時間(秒)」をおいた。GPU以外のボトルネック時間は前回の実験の最悪ケースが4分17秒で、300の場合に近い。今まで計算例として使用していたのは90が近い。値は比例係数である(γ=1と仮定したものと同じ)。

λ / sum_E 0 30 90 300 Case300/Case0
10 0.20 0.20 0.20 0.20 99.99%
20 0.41 0.41 0.41 0.41 99.97%
50 1.02 1.02 1.02 1.02 99.93%
70 1.43 1.43 1.43 1.42 99.90%
100 2.04 2.04 2.04 2.03 99.86%
150 3.06 3.06 3.05 3.05 99.79%
200 4.07 4.07 4.07 4.06 99.71%
500 10.19 10.18 10.17 10.12 99.29%
700 14.26 14.25 14.22 14.12 99.01%
1000 20.37 20.35 20.29 20.09 98.59%

結論はとても単純で「よっぽど高速(例:TPS1000)でなければほとんど差が出ない」である。仮にこの値が0だったとしても、許容されるGPUインスタンスの価格で0.01USD/hrの差が出るかどうかである。最悪ケースと理想的な最良ケースを比べても1%も差がない。

つまり、運用の負荷を増やしてキューの部分を改善しようとしてもほとんど効果がなく、むしろ運用の効率性を重視してマネージドサービスのSQSを使ったほうがメリットが大きいという総合的な判断ができる。ログの部分を非同期化してオフロードするなどは試しても良いだろう。

限界

この理論はあくまで理想的なケースなので以下の条件を無視している

  • データセットがJapanese MT Benchをひたすら焼きなましたものなので、これに特化した構成になっている。特に2ターンの会話までしか検証していない
  • Reasoningモデルの思考時間を考慮していない
  • Token per secondの変動を考慮していない
  • 稼働率が100%の前提で、インスタンスがアイドルになることを想定していない
  • 当然ながらGPUインスタンス込で、転送量やその他のインスタンス、フロントエンドのホスト、他のステージのホスト費用、開発人件費などを一切無視している

おわりに

実はこれは前から自分が気になっていた内容で、「このへんの包括的な理論ができたら嬉しいな」と思っていた。現実はなんとなく理解していたが思ったよりもシビアで、LLMのAPI特化で収益を得るには結構大変だなというのが如実に出てきた。これだとオンプレのデータセンター建築がラッシュになるのもなんとなく理解できる。データセンターのコストや減価償却はどっかに資料があったので、それをベースに考えると「クラウドGPU換算にしたときにオンプレGPUはどのぐらいの価格で提供できるか?」がざっくり計算できると思う。暇があったらやってみたい。



Shikoan's ML Blogの中の人が運営しているサークル「じゅ~しぃ~すくりぷと」の本のご案内

技術書コーナー

北海道の駅巡りコーナー


Add a Comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です