# Bitsandbytes

[bitsandbytes](https://github.com/bitsandbytes-foundation/bitsandbytes) ライブラリは、ハードウェアアクセラレータの関数に対する軽量な Python ラッパーを通じて、LLM 用の量子化ツールを提供します。メモリフットプリントを削減することで、限られた計算リソースを用いて大規模モデルを扱うことを可能にします。

bitsandbytes のコア機能として、以下を提供します:

- **量子化されたLinearレイヤー**: 標準的なPyTorchのlinearレイヤーを、メモリ効率に優れた量子化された代替レイヤーに置き換える`Linear8bitLt`および`Linear4bit`レイヤー
- **最適化されたオプティマイザー**: `optim`モジュールを通じて、一般的なオプティマイザーの8ビット版を提供し、メモリ要件を削減しながら大規模モデルの学習を可能にします
- **行列積**: 量子化形式を活用した最適化された行列積演算

bitsandbytesには、主に2つの量子化機能があります。

1. **LLM.int8()** - 推論をより利用しやすくする8ビット量子化手法であり、性能の大幅な低下を伴いません。単純な量子化とは異なり、[LLM.int8()](https://hf.co/papers/2208.07339)は重要な計算に対して動的に高い精度を維持し、モデルの感度の高い部分における情報損失を防ぎます。

2. **QLoRA** - 学習可能な低ランク適応（LoRA）重みの小さなセットを挿入することで、学習可能性を維持しながらモデルをさらに圧縮する4ビット量子化技術。

> **注:** ユーザーフレンドリーな量子化体験を得るには、`bitsandbytes` の [コミュニティスペース](https://huggingface.co/spaces/bnb-community/bnb-my-repo) を利用できます。

bitsandbytes をインストールするには、以下のコマンドを実行してください。

```bash
pip install --upgrade transformers accelerate bitsandbytes
```

ソースからコンパイルするには、[bitsandbytes インストールガイド](https://huggingface.co/docs/bitsandbytes/main/en/installation)の手順に従ってください。

## ハードウェアの互換性

bitsandbytesは、CUDAバージョン11.8 - 13.0のNVIDIA GPU、Intel XPU、Intel Gaudi (HPU)、およびCPUでサポートされています。追加のプラットフォームへのサポートに向けた取り組みが進行中です。フィードバックの提供やテストにご興味がある場合は、詳細については[bitsandbytesリポジトリ](https://github.com/bitsandbytes-foundation/bitsandbytes)をご覧ください。

### NVIDIA GPU (CUDA)

このバックエンドは、Linux x86-64、Linux aarch64、および Windows プラットフォームでサポートされています。

| 機能 | 最低限のハードウェア要件 |
|---------|-------------------------------|
| 8ビットオプティマイザー | NVIDIA Pascal (GTX 10X0 シリーズ、P100) 以降の GPU * |
| LLM.int8() | NVIDIA Turing(RTX 20X0 シリーズ、T4)以降の GPU |
| NF4/FP4量子化 | NVIDIA Pascal (GTX 10X0 シリーズ、P100) 以降の GPU * |

### Intel GPU(XPU)

このバックエンドは、Linux x86-64 および Windows x86-64 プラットフォームでサポートされています。

### Intel Gaudi (HPU)

このバックエンドは、Gaudi2 および Gaudi3 において Linux x86-64 でサポートされています。

### CPU

このバックエンドは、Linux x86-64、Linux aarch64、および Windows x86-64 プラットフォームでサポートされています。

## 量子化の例

[BitsAndBytesConfig](/docs/transformers/main/ja/main_classes/quantization#transformers.BitsAndBytesConfig) を [from_pretrained()](/docs/transformers/main/ja/main_classes/model#transformers.PreTrainedModel.from_pretrained) に渡すことでモデルを量子化できます。これは、[Accelerate](https://huggingface.co/docs/accelerate/index) をサポートし、[torch.nn.Linear](https://pytorch.org/docs/stable/generated/torch.nn.Linear.html) レイヤーを含むモデルであれば、モダリティを問わず適用できます。

モデルを8ビットに量子化するとメモリ使用量が半分になり、大規模モデルでは`device_map="auto"`を設定することで、利用可能なすべてのGPUに重みを効率的に分散できます。

```py
from transformers import AutoModelForCausalLM, BitsAndBytesConfig

quantization_config = BitsAndBytesConfig(load_in_8bit=True)

model_8bit = AutoModelForCausalLM.from_pretrained(
    "bigscience/bloom-1b7", 
    device_map="auto",
    quantization_config=quantization_config
)
```

デフォルトでは、[torch.nn.LayerNorm](https://pytorch.org/docs/stable/generated/torch.nn.LayerNorm.html) などの他のすべてのモジュールは、デフォルトの torch dtype に設定されます。これらのモジュールのデータ型は、`dtype` パラメータで変更できます。`dtype="auto"` を設定すると、モデルの `config.json` ファイルで定義されたデータ型でモデルが読み込まれます。

```py
import torch
from transformers import AutoModelForCausalLM, BitsAndBytesConfig

quantization_config = BitsAndBytesConfig(load_in_8bit=True)

model_8bit = AutoModelForCausalLM.from_pretrained(
    "facebook/opt-350m", 
    device_map="auto",
    quantization_config=quantization_config, 
    dtype="auto"
)
model_8bit.model.decoder.layers[-1].final_layer_norm.weight.dtype
```

モデルが8ビットに量子化された後、Transformersとbitsandbytesの最新バージョンを使用していない限り、量子化された重みをHubにプッシュすることはできません。最新バージョンを使用している場合、[push_to_hub()](/docs/transformers/main/ja/main_classes/model#transformers.utils.PushToHubMixin.push_to_hub)を使用して8ビットモデルをHubにプッシュできます。まず量子化のconfig.jsonファイルがプッシュされ、次に量子化されたモデルの重みがプッシュされます。

```py
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig

quantization_config = BitsAndBytesConfig(load_in_8bit=True)

model = AutoModelForCausalLM.from_pretrained(
    "bigscience/bloom-560m", 
    device_map="auto",
    quantization_config=quantization_config
)

model.push_to_hub("bloom-560m-8bit")
```

モデルを4ビットに量子化すると、メモリ使用量が4分の1に削減されます。大規模モデルでは、`device_map="auto"`を設定することで、利用可能なすべてのGPUに重みを効率的に分散できます。

```py
from transformers import AutoModelForCausalLM, BitsAndBytesConfig

quantization_config = BitsAndBytesConfig(load_in_4bit=True)

model_4bit = AutoModelForCausalLM.from_pretrained(
    "bigscience/bloom-1b7",
    device_map="auto",
    quantization_config=quantization_config
)
```

デフォルトでは、[torch.nn.LayerNorm](https://pytorch.org/docs/stable/generated/torch.nn.LayerNorm.html) などの他のすべてのモジュールは `torch.float16` に変換されます。これらのモジュールのデータ型は、`dtype` パラメータで変更できます。`dtype="auto"` を設定すると、モデルの `config.json` ファイルで定義されたデータ型でモデルが読み込まれます。

```py
import torch
from transformers import AutoModelForCausalLM, BitsAndBytesConfig

quantization_config = BitsAndBytesConfig(load_in_4bit=True)

model_4bit = AutoModelForCausalLM.from_pretrained(
    "facebook/opt-350m",
    device_map="auto",
    quantization_config=quantization_config, 
    dtype="auto"
)
model_4bit.model.decoder.layers[-1].final_layer_norm.weight.dtype
```

4ビットモデルをシリアライズして[push_to_hub()](/docs/transformers/main/ja/main_classes/model#transformers.utils.PushToHubMixin.push_to_hub)でHubにプッシュできるように、bitsandbytesの最新バージョンがインストールされていることを確認してください。4ビットモデルをローカルに保存するには、[save_pretrained()](/docs/transformers/main/ja/main_classes/model#transformers.PreTrainedModel.save_pretrained)を使用します。

```py
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig

quantization_config = BitsAndBytesConfig(load_in_4bit=True)

model = AutoModelForCausalLM.from_pretrained(
    "bigscience/bloom-560m", 
    device_map="auto",
    quantization_config=quantization_config
)

model.push_to_hub("bloom-560m-4bit")
```

> [!WARNING]
> 8ビットおよび4ビットの学習は、*追加*パラメータの学習のみサポートされています。

`get_memory_footprint` を使用して、メモリフットプリントを確認してください。

```py
print(model.get_memory_footprint())
```

`quantization_config`なしで、[from_pretrained()](/docs/transformers/main/ja/main_classes/model#transformers.PreTrainedModel.from_pretrained)を使用して量子化モデルを読み込みます。

```py
from transformers import AutoModelForCausalLM, AutoTokenizer

model = AutoModelForCausalLM.from_pretrained("{your_username}/bloom-560m-8bit", device_map="auto")
```

## LLM.int8

このセクションでは、オフロード、外れ値のしきい値、モジュール変換のスキップ、ファインチューニングなど、8ビット量子化の特定の機能について詳しく見ていきます。

### オフローディング

8-bitモデルは、CPUとGPU間で重みをオフロードすることで、非常に大きなモデルをメモリに収めることができます。CPUにディスパッチされた重みは**float32**で保存され、8-bitに変換されません。例えば、[BitsAndBytesConfig](/docs/transformers/main/ja/main_classes/quantization#transformers.BitsAndBytesConfig)を通じて[bigscience/bloom-1b7](https://huggingface.co/bigscience/bloom-1b7)のオフロードを有効にします。

```py
from transformers import AutoModelForCausalLM, BitsAndBytesConfig

quantization_config = BitsAndBytesConfig(llm_int8_enable_fp32_cpu_offload=True)
```

`lm_head` 以外のすべてを GPU に収めるよう、カスタムのデバイスマップを設計し、`lm_head` は CPU にディスパッチします。

```py
device_map = {
    "transformer.word_embeddings": 0,
    "transformer.word_embeddings_layernorm": 0,
    "lm_head": "cpu",
    "transformer.h": 0,
    "transformer.ln_f": 0,
}
```

次に、カスタムの`device_map`と`quantization_config`を指定してモデルを読み込みます。

```py
model_8bit = AutoModelForCausalLM.from_pretrained(
    "bigscience/bloom-1b7",
    dtype="auto",
    device_map=device_map,
    quantization_config=quantization_config,
)
```

### 外れ値の閾値

「outlier」とは、特定の閾値を超える隠れ状態の値を指し、これらの値はfp16で計算されます。通常、これらの値は正規分布（[-3.5, 3.5]）に従いますが、大規模モデルでは分布が非常に異なる場合があります（[-60, 6]や[6, 60]など）。8ビット量子化は値が約5程度の場合に良好に機能しますが、それを超えると、パフォーマンスに大きなペナルティが発生します。適切なデフォルトの閾値は6ですが、不安定なモデル（小規模モデルやファインチューニング中）では、より低い閾値が必要になる場合があります。

モデルに最適な閾値を見つけるには、[BitsAndBytesConfig](/docs/transformers/main/ja/main_classes/quantization#transformers.BitsAndBytesConfig)の`llm_int8_threshold`パラメータを調整してみてください。例えば、閾値を`0.0`に設定すると、精度がわずかに低下する可能性があるものの、推論が大幅に高速化されます。

```py
from transformers import AutoModelForCausalLM, BitsAndBytesConfig

model_id = "bigscience/bloom-1b7"

quantization_config = BitsAndBytesConfig(
    llm_int8_threshold=0.0,
    llm_int8_enable_fp32_cpu_offload=True
)

model_8bit = AutoModelForCausalLM.from_pretrained(
    model_id,
    dtype="auto",
    device_map=device_map,
    quantization_config=quantization_config,
)
```

### モジュール変換をスキップ

Jukebox などの一部のモデルでは、すべてのモジュールを 8 ビットに量子化すると不安定になる可能性があるため、量子化を省略する必要があります。Jukebox の場合、[BitsAndBytesConfig](/docs/transformers/main/ja/main_classes/quantization#transformers.BitsAndBytesConfig) の `llm_int8_skip_modules` パラメータを使用して、`lm_head` モジュールをスキップする必要があります。

```py
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig

model_id = "bigscience/bloom-1b7"

quantization_config = BitsAndBytesConfig(
    llm_int8_skip_modules=["lm_head"],
)

model_8bit = AutoModelForCausalLM.from_pretrained(
    model_id,
    dtype="auto",
    device_map="auto",
    quantization_config=quantization_config,
)
```

### ファインチューニング

[PEFT](https://github.com/huggingface/peft) ライブラリは、[flan-t5-large](https://huggingface.co/google/flan-t5-large) や [facebook/opt-6.7b](https://huggingface.co/facebook/opt-6.7b) のような大規模モデルのファインチューニングを、8ビット量子化と組み合わせてサポートしています。モデルは自動的にGPUにロードされるため、トレーニング時に `device_map` パラメータを渡す必要はありません。ただし、`device_map` パラメータ（`device_map="auto"` は推論時のみ使用すべきです）でデバイスマップをカスタマイズすることも可能です。

## QLoRA

このセクションでは、4ビット量子化の特定の機能、例えば計算データ型の変更、Normal Float 4 (NF4) データ型、およびネスト量子化について解説します。

### 計算データ型

[BitsAndBytesConfig](/docs/transformers/main/ja/main_classes/quantization#transformers.BitsAndBytesConfig) でデータ型を float32（デフォルト値）から bf16 に変更して、計算を高速化します。

```py
import torch
from transformers import BitsAndBytesConfig

quantization_config = BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_compute_dtype=torch.bfloat16)
```

### 通常の Float 4 (NF4)

NF4 は [QLoRA](https://hf.co/papers/2305.14314) の論文で提案された 4 ビットデータ型で、正規分布から初期化された重みに適応されています。4 ビットのベースモデルの学習には NF4 を使用してください。

```py
from transformers import BitsAndBytesConfig

nf4_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
)

model_nf4 = AutoModelForCausalLM.from_pretrained(model_id, dtype="auto", quantization_config=nf4_config)
```

推論において、`bnb_4bit_quant_type`はパフォーマンスに大きな影響を与えません。ただし、モデルの重みと整合性を保つため、`bnb_4bit_compute_dtype`と`dtype`の値を使用する必要があります。

### ネストされた量子化

ネスト量子化により、追加の性能コストなしにさらなるメモリを節約できます。この機能は、すでに量子化された重みに対して2回目の量子化を実行し、パラメータあたり追加で0.4ビットを節約します。例えば、ネスト量子化を使用すると、シーケンス長1024、バッチサイズ1、4ステップの勾配蓄積を有効にした設定で、16GBのNVIDIA T4 GPU上で[Llama-13b](https://huggingface.co/meta-llama/Llama-2-13b)モデルのファインチューニングを行うことができます。

```py
from transformers import BitsAndBytesConfig

double_quant_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_use_double_quant=True,
)

model_double_quant = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-13b-chat-hf", dtype="auto", quantization_config=double_quant_config)
```

## bitsandbytesモデルのデクアンタイズ

量子化されたモデルは、[dequantize()](/docs/transformers/main/ja/main_classes/model#transformers.PreTrainedModel.dequantize) を使用して元の精度に復元できますが、これにより品質が低下する可能性があります。デクォンタイズされたモデルを収容するのに十分な GPU メモリがあることを確認してください。

```python
from transformers import AutoModelForCausalLM, BitsAndBytesConfig, AutoTokenizer

model = AutoModelForCausalLM.from_pretrained("facebook/opt-125m", BitsAndBytesConfig(load_in_4bit=True))
model.dequantize()
```

## リソース

8-bit量子化の詳細については、[Hugging Face Transformers、Accelerate、bitsandbytesを用いた大規模transformers向け8-bit行列積のやさしい入門](https://huggingface.co/blog/hf-bitsandbytes-integration)を参照してください。

Try 4-bit quantization in this [notebook](https://colab.research.google.com/drive/1ge2F1QSK8Q7h0hn3YKuBCOAS0bK8E0wf) and learn more about its details in [Making LLMs even more accessible with bitsandbytes, 4-bit quantization and QLoRA](https://huggingface.co/blog/4bit-transformers-bitsandbytes).

