🕌

論文まとめ: GPTQ

に公開

はじめに

最近、以下のようなコードを用いてLLMの量子化をすることが多くなった。引用元

model_id = "facebook/opt-125m"
tokenizer = AutoTokenizer.from_pretrained(model_id)
gptq_config = GPTQConfig(bits=4, dataset = "c4", tokenizer=tokenizer)

このコードが裏側で何をしているか気になったため、GPTQ論文を読んだ。自身の理解も兼ねてまとめる。

タイトル

GPTQ: ACCURATE POST-TRAINING QUANTIZATION FOR GENERATIVE PRE-TRAINED TRANSFORMERS

URL

https://arxiv.org/abs/2210.17323

著者

Elias Frantar∗
IST Austria

Saleh Ashkboos
ETH Zurich

Torsten Hoefler
ETH Zurich

Dan Alistarh
IST Austria & NeuralMagic

採択

Published as a conference paper at ICLR 2023

概要

  • LLMはすごいけれど、高性能なGPUが必要である。
  • 既存の圧縮手法の適用性とパフォーマンスは、GPT モデルの規模と複雑さによって制限される。
  • 本論文では、近似二次情報に基づく、高精度かつ高効率な新しい量子化法である GPTQ を提案する。
  • 1,750 億のパラメータを持つ GPT モデルを約 4 GPU 時間で量子化し、ビット幅を重みあたり 3 ビットまたは 4 ビットに削減し、圧縮されていないベースラインと比較して精度の低下はごくわずかであることを示した。
  • 実験的に、これらの改善により、FP16と比較してエンドツーエンドの推論速度が約3.25倍、よりコスト効率の高いGPU(NVIDIA A6000)を使用した場合は約4.5倍向上することを実証した。

前提

従来、ニューラルネットワークモデルの圧縮手法として、大きく分けて以下の2つが存在する。

  • 学習中の量子化 (Quantization During Training)
  • 学習後の量子化 (Post-Training Quantization - PTQ)

それぞれ見ていく。

学習中の量子化 (Quantization During Training)

モデルの学習中またはファインチューニング中に、重みや活性化関数を低いビット幅で表現する方法である。通常、丸め処理に対する近似的な微分メカニズムを利用する. しかし、数Bレベルのモデルの再学習、ファインチューニングを行うことは、計算コストが高い.

学習後の量子化 (Post-Training Quantization, PTQ)

事前学習済みのモデルを、少量データと短い計算時間で、再学習なしに量子化する手法である。計算コストの面では、LLMに対しても適用しやすいアプローチである。

しかし、PTQの一つであるOptimal Brain Quantization(OBQ)は、入力サイズに対して計算量が三乗で増加するため、LLMに対して用いることは現実的ではない。具体的には、d_{row} × d_{col}の行列 W について、O(d_{row} · d^3_{col}) の計算量が必要である。

そのため、既存の研究(ZeroQuant, LLM.int8(), nuQmm)では、量子化粒度を注意しつつ、最終的には単純な最近傍丸め(Round-To-Nearest quantization, RTN)を用いることが一般的だった。RTNは実装が容易で、LLMに対しても短い時間で適用できるが、高い圧縮率(例えば、8ビット以下)では精度が大きく低下するという課題がある。

既存手法: OBQ

GPTQはOBQを改善したアルゴリズムである。そのため、まずはOBQに関して簡単に説明する。

OBQは重みを1つずつ量子化していき、その都度誤差を最小にするように、他の重みを補正しながら進めるアルゴリズムである。

具体的には、以下の式(2), (3)に基づき計算される。

以下の(2)式は、重みベクトルのある1つの成分 w_q を量子化する際に、誤差(損失)を最小にするように選び、他の成分を更新する方法を示す。

\begin{equation} w_q = \arg\min_{w_q} \frac{(\text{quant}(w_q) - w_q)^2}{[\mathbf{H}^{-1}_F]_{qq}}, \quad \boldsymbol{\delta}_F = -\frac{w_q - \text{quant}(w_q)}{[\mathbf{H}^{-1}_F]_{qq}} \cdot (\mathbf{H}^{-1}_F)_{:,q} \tag{2} \end{equation}
  • w_q: 重みベクトルのある1つの成分
  • \text{quant}(w_q): w_q を量子化した値
  • F: 未量子化要素の集合
  • \mathbf{H}: 量子化しようとしている線形変換の出力誤差に対するヘッセ行列
  • [\mathbf{H}_F^{-1}]_{qq} : 未量子化要素に限定したヘッセ行列の逆行列における対角成分
  • \boldsymbol{\delta}_F: まだ量子化していない他の重み(集合 F)に対して補正を行うベクトル
  • [\mathbf{H}_F^{-1}]_{:,q}: q 列ベクトル

以下の(3)式は、ある重みを量子化したら、その成分が他に与える影響を逆行列から引き算し、その成分を除いた新しい空間での逆行列を再計算する方法を示している。まだ、q 成分が含まれている\mathbf{H}^{-1}から\frac{1}{[\mathbf{H}^{-1}]{qq}}\mathbf{H}^{-1}_{:,q}\mathbf{H}^{-1}_{q,:}を減算することで、q が持っていた影響力を省いている。

\begin{equation} \mathbf{H}^{-1}_{-q} = \left( \mathbf{H}^{-1} - \frac{1}{[\mathbf{H}^{-1}]_{qq}} \, \mathbf{H}^{-1}_{:,q} \, \mathbf{H}^{-1}_{q,:} \right)_{-p} \tag{3} \end{equation}
  • \mathbf{H}^{-1}: 元の逆行列
  • [\mathbf{H}^{-1}]_{qq}: q 番目成分の自己影響(スカラー)
  • \mathbf{H}^{-1}_{:,q}: q 番目列(他成分から q への影響)
  • \mathbf{H}^{-1}_{q,:}: q 番目行(q から他成分への影響)

提案手法: GPTQ

以下3つを行うことで、既存手法OBQの問題点を改善した。

  • Arbitrary Order Insight(任意の順序での量子化の有効性の発見)
  • Lazy Batch-Updates(遅延バッチ更新によるGPUの効率的な利用)
  • Cholesky Reformulation(コレスキー分解を用いた数値的な安定性の向上)

それぞれを見ていく。

Arbitrary Order Insight(任意の順序での量子化の有効性)

ニューラルネットワークの重みを量子化する際に、特定の最適化された順序ではなく、任意の固定された順序で量子化しても、最終的なモデルの精度に大きな影響がないことを発見した。

既存手法のOBQは、量子化誤差が最も小さくなる重みを貪欲に選択し、その順番で量子化を行っていた。この手法は高精度のために必要と考えられていた。

しかし、GPTQでは、特に大規模でパラメータ数の多い層においては、貪欲な順序で量子化することによる精度向上は比較的小さいことを発見した。その理由として、誤差が大きい重みが量子化される時点において、調整できる残りの重みの数が少ないため、最終的な精度に与える影響が相殺される可能性が考えられている。

この発見により、より単純な順序(例えば、列ごとの順序)で量子化を進めることができるため、計算コストを大幅に削減できる。具体的には、OBQでは各重みごとに最適な量子化順序を決定し、その都度、残りの重みを更新する必要があり、計算量が入力サイズに対して三乗だった。一方、GPTQでは、すべての行の重みを同じ順序で量子化することを目指し、層の入力にのみ依存するヘッセ行列の逆行列の更新を、重みの数ではなく列の数だけ行えば済むため、計算量を大幅に削減できる。

図1 GPTQの量子化の過程(GPTQ論文より引用)

図1は、GPTQにおける量子化の過程を直感的に示した図である。

左側の「Inverse Layer Hessian (Cholesky Form)」は、事前に計算された、モデルのあるレイヤーにおけるコレスキー分解されたヘッセ行列の逆行列である。この行列は、どの重みが損失にどの程度影響を与えるかを示すものである。

右側の「Weight Matrix / Block」は、はモデルの重み行列である。各列(縦の帯)ごとに、順番に量子化されていく。色の意味は以下である。

  • オレンジ色:すでに量子化された重み。
  • 白色:現在量子化中の重み。
  • 青色:まだ量子化されていないが、すでに量子化済みの列の影響を受けて更新される重み。

次に、処理の順序を説明する。

  1. 各ブロック内の列を 1列ずつ量子化する。
  2. 各列の量子化では、左側の行列の該当部分を使って、量子化による誤差を最小限に抑えながら処理する。
  3. 量子化された列の影響を踏まえて、残りの未量子化列(青色)を更新する。
  4. この処理を列ごと、ブロックごとに繰り返す。

OBQでは行ごとに独立に量子化していたが、この処理ではすべての行を同じ順序で量子化できる。
全体の実行時間はO(d_{row} · d^3_{col})からO(max\{{d_{row}・d^2_{col}, d^3 _{col}}\})に短縮される。

Lazy Batch-Updates(遅延バッチ更新によるGPUの効率的な利用)

OBQは、各重みの量子化ごとにヘッセ行列の逆行列を更新する必要がある。OBQの手法において、重みごとに行う計算量(FLOPs)は少ないのに対し、メモリアクセスは多い。一方、最新のGPUは数テラFLOPS程度の高い演算性能を持っているが、メモリへのアクセス速度(memory bandwidth、メモリ帯域幅)はそれほど速くない。

メモリアクセスの回数を減らし、一度の計算量を増やすために、GPTQでは、128個の重みを一つのブロックとしてまとめ、同時に量子化を行う。さらに、各重みの量子化後すぐにヘッセ行列の逆行列を更新するのではなく、ブロック全体の量子化が完了した後にまとめて更新を行う(Lazy Batch-Updates, 遅延バッチ更新)。これらにより、計算とメモリアクセスのバランスが改善され、GPUの高い演算処理能力を活用できるようになる。

式で表現すると、以下のようになる。OBQ(式(2), (3))では、各重みがqで表現されていたが、インデックス集合Qに置き換わっている点のみが変更点である。

\boldsymbol{\delta}_F = -(\mathbf{w}_Q - \text{quant}(\mathbf{w}_Q))([\mathbf{H}_F^{-1}]_{QQ})^{-1}(\mathbf{H}_F^{-1})_{:,Q} \tag{4}
\mathbf{H}^{-1}_{-Q} = \left( \mathbf{H}^{-1} - \mathbf{H}^{-1}_{:,Q}([\mathbf{H}^{-1}]_{QQ})^{-1}\mathbf{H}^{-1}_{Q,:} \right)_{-Q} \tag{5}

Cholesky Reformulation(コレスキー分解を用いた数値的な安定性の向上)

本来、H_F は正定値(=すべての固有値が正であること)であってほしい。しかし、数値誤差や量子化の影響で、H_F^{−1}が不定値(=負の固有値を持ったり、固有値が0になること)になる可能性がある。H_F^{−1}が不定値になると、アルゴリズムが残りの重みを誤った方向に積極的に更新し、対応する層の量子化が不正確になる可能性がある。実際には、モデルのサイズが大きくなるにつれて、この問題が発生する確率が増加することを観察した。数十億パラメータを超えるモデルでは、少なくとも数層でほぼ確実に発生する。

そこで、逆行列よりも数値の安定性が高い、三角行列になるようコレスキー分解を行う。(3)式は、q 行目を ([\mathbf{H}F^{-1}]{qq})^{1/2} で割る操作を除けば、コレスキー分解を実施していることに相当する。

最終的なアルゴリズム

図2 GPTQのアルゴリズム

実験

表やグラフのみで簡単に紹介する。

RTN, FP16と比較した際の、GPTQのPerplexity。GPTQは、RTNよりよく、FP16とほぼ同様であることが分かる。

Resnet18(RN18), Resnet50(RN50)による実験では、GPTQは4ビットでは同等のパフォーマンスを発揮し、3ビットでは最も正確な方法よりもわずかに劣る。

GPTQの実行時間。GPTQはOPT175Bを、4.2hで量子化可能である。

読んだ感想

  • 見逃していたら申し訳ないが、既存手法(OBQなど)に対して、GPTQがどの程度実行時間削減になるかの比較が欲しかった。
  • 量子化の際に、GPTQConfigなどをよく呼び出すが、その裏側にどんな手法が使われているかの理解がかなり深まった。

Discussion