Apple SiliconとPyTorchで生成系AI

# はじめに

M1 MacのMetal Performance Shaderに対応したPyTorchがStableリリースされていたので、これを機にApple SiliconのGPUで高速に動作する生成系AIをローカルに導入してみます。

## 環境要件

- Python 3.10
- Apple Silicon 搭載 Mac (M1/M2 Mac)
- 16 GB 以上のメモリ (Apple SiliconではCPUメモリとGPUメモリは共通です)
- 数GB ~ 数十GB の空ディスク容量 (大規模モデルをローカルにダウンロードします)

私は M2 Macbook Pro (16 GB) でやってます。ラップトップで大規模モデルが動くなんてすごい時代ですね。

# 仮想環境の作成

本記事ではpipを用いていくつかのPythonライブラリをインストールします。無闇にライブラリを追加していくことは事故のもとになるので、仮想環境を作っておきましょう。

venv (Python標準の仮想環境管理ツール) の場合
```zsh:terminal
python3.10 -m venv GenAI

source GenAI/bin/activate
```

conda環境の場合 (miniforgeなど)
```zsh:terminal
conda create --name GenAI python=3.10

conda activate GenAI
```

pythonは3.9以上であれば (おそらく) 大丈夫です。仮想環境を忘れずにactivateしておきましょう(一敗)。

# PyTorchのインストール

Pythonで手軽に実行できるように作られた生成系AIの多くは「PyTorchライブラリー」として公開されています。つまり、生成系AIを実行するためには事前にPyTorchをインストールしておく必要があります。

PyTorchのバージョン1.12からApple Metalへの対応が始まり、Apple SiliconマシンのGPUを用いた高速な学習・推論が可能になりました。

<https://pytorch.org/blog/introducing-accelerated-pytorch-training-on-mac/>

2023年6月現在では普通にインストールしたPyTorchでM1 MacのGPUが使用できるようになっています。

```zsh:terminal
pip install torch
```

インストールが終わったら、2点ほど重要なことをチェックをしておきましょう。

```python:terminal
% python # Pythonの対話モードを起動

>>> import torch

>>> print(torch.__version__)
2.0.1 # Pytorchのバージョンが2.0以上であればOK.

>>> print(torch.backends.mps.is_available())
True # Trueが出ればOK. deviceとしてApple SiliconのGPUが使える.

>>> quit()
```

実行時間等のベンチマークスコアは以下の記事が参考になります。

- <https://zenn.dev/hidetoshi/articles/20220731_pytorch-m1-macbook-gpu>

- <https://zero-cheese.com/10287/>


# 各生成系AIのインストール

## イラスト生成AI (Stable Diffusion) の場合

Stable Diffusionは「プロンプト」と呼ばれる文章から画像を生成するtext-to-imageモデルです。Diffusionモデルという技術が用いられています。
ミュンヘン大学のチーム「CompVis LMU」が開発し、のちにStability AI社、CompVis LMU、Runway社の三者によって共同で公開されました。

<https://ja.wikipedia.org/wiki/Stable_Diffusion>

### huggingFace の設定

Stable Diffusionのモデルは「huggingFace」というサイトで公開されているものを利用します。モデルをローカルにダウンロードする際に、huggingFaceで発行されるトークンが必要となります。

まず、以下のサイトからhuggingFaceのアカウントを作成します。
<https://huggingface.co/>

次に、以下のサイトからトークンを発行します。生成に使うだけならば、権限は「Read」を選ぶと良いです。
<https://huggingface.co/settings/tokens>

次に、手元のマシンでトークンを有効化します。まず「huggingface_hub」をインストールします。

```zsh:terminal
pip install huggingface_hub
```

これについてくる「huggingface-cli」というものでトークンを有効化できます。

```zsh:terminal
% huggingface-cli login

To login, `huggingface_hub` requires a token generated from https://huggingface.co/settings/tokens .
Token:[先ほど発行したトークンを入力する]
```

すると、トークンをgit credentialに保存するか尋ねられます。

```zsh:terminal
Add token as git credential? (Y/n)
% y

Token is valid (permission: read).
Your token has been saved in your configured git credential helpers (osxkeychain).
Your token has been saved to /****/
Login successful
```

yesでもnoでも大丈夫ですが、M1 Macの場合はyesを選択すると osxkeychain (Apple製品に標準搭載されている「キーチェーン機能」) に保存されます。Appleアカウントと紐付けてトークンが保存されるので便利だと思います。


:::note warn

huggingface_hubをインストールする前にhuggingface-cliを使用しようとするとエラーが発生します。

```zsh:terminal
% huggingface-cli login

zsh: command not found: huggingface-cli
```

また、huggingface-cliはHomebrewからインストールすることも可能です。

:::

### 🧨 Diffusers のインストール

🧨 Diffusersは、huggingFaceが提供する、推論・学習に必要な機械学習周りの機能が揃ったツールボックスです。

<https://huggingface.co/docs/diffusers/installation>

これを使うと、huggingFaceで配布されているモデルのダウンロードや管理が楽にできます。

```zsh:terminal
pip install diffusers
```

🤗 Diffusersの実行に必要な 🤗 transformerと、CPUメモリの使用をいい感じにしてくれる 🤗 accerelate もインストールしておきます。

```zsh:terminal
pip install transformers
pip install accelerate
```

Stable Diffusionモデルの中には Scipy を必要とするモデルもあるので、あらかじめインストールしておくと良いです。

```
pip install scipy
```

### 実行

これでようやく画像生成の準備が整いました。
ここでは [huggingFace公式のHow to](https://huggingface.co/docs/diffusers/optimization/mps) に倣って、AIベンチャー企業であるRunway社の研究チーム「RunwayML」のStable Diffusionモデル

<https://huggingface.co/runwayml/stable-diffusion-v1-5>

を利用することにしましょう。以下のPythonコードを実行します。

```python: StableDiffusionTest.py
from diffusers import DiffusionPipeline # モジュールのインポート

pipe = DiffusionPipeline.from_pretrained("runwayml/stable-diffusion-v1-5") # Diffusion モデルを指定する
pipe = pipe.to("mps") # M1 Mac の GPU を指定する

pipe.enable_attention_slicing() # RAM が 64 GB 未満の場合はこの一行を入れるとよい

prompt = "a photo of an astronaut riding a horse on mars" # 生成プロンプト

image = pipe(prompt).images[0] # 画像生成

image.save('output.jpg') # 画像保存
```

たった10行足らずで使えるなんて驚きですね。初回の実行ではモデルのダウンロード (数GB程度) が自動で行われます。2階目以降はローカルにダウンロードしたモデルを勝手に使用してくれます。

> **実行例**
> ![output0.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684531/5ab9a60d-f572-83f8-8c03-2de6447b0e21.jpeg)


Stable Diffusionのモデルは他にも、

- University of Munich のコンピュータビジョン研究チーム (元CompVis LMU) が提供しているモデル
<https://huggingface.co/CompVis/stable-diffusion-v1-4>

- Stability AI社が提供しているモデル
<https://huggingface.co/stabilityai/stable-diffusion-2-1>

などがあります。

### 参考記事

- https://zenn.dev/michy/articles/102a0558096199
- https://blog.interstellar.co.jp/2023/03/02/stable-diffusion-for-local/

## チャットAI (FlexGen) の場合

FlexGenは大規模言語モデル（LLM: Large Language Model）をシングルGPUで実行することができる自然言語処理エンジンです。具体的には、Meta社が開発したOPT（Open Pre-trained Transformer）を動かしてAIと会話することができます。ChatGPTと同じTransformerモデルの技術が用いられています。

### インストール

FlexGenをインストールしていきます。GitHubのリポジトリ

https://github.com/con3office/FlexGen

を 直接クローンしてインストールする必要があります。

:::note warn

GitHubのREADME.mdには

```zsh:terminal
pip install flexgen
```

でインストールできるとありますが、これでインストールされるバージョンはM1 Macに対応していません。

:::

まず、GitHubリポジトリをクローンします。

```zsh:terminal
git clone https://github.com/con3office/FlexGen.git
```

続いて、M1 Macに対応した `m1` ブランチに切り替えてからFlexGenをインストールします。

```zsh:terminal
cd FlexGen
git checkout m1
pip install -e .
```

最後に、大規模言語モデルをダウンロードします。同時に使用するデバイスとして`mps`を指定しておきます。

```zsh:terminal
python3 -m flexgen.flex_opt --model facebook/opt-1.3b --platform mps:0
```

モデルとしては、以下の4種類が選べます。

- `--model facebook/opt-1.3b`: 最小のモデル。シングルGPUで動作可能
- `--model facebook/opt-6.7b`: 中程度のモデル。15GBのGPUメモリが必要
- `--model facebook/opt-30b`: 大きいモデル。90GB程度のGPUメモリを推奨。
- `--model facebook/opt-175b`: 最大のモデル

実行すると、ベンチマークの結果が表示されます。

```zsh:
TorchDevice: mps:0
  cur_mem: 2.6426 GB,  peak_mem: 0.0000 GB
TorchDevice: cpu
  cur_mem: 2.6426 GB,  peak_mem: 0.0000 GB
model size: 2.443 GB	cache size: 0.398 GB	hidden size (p): 0.008 GB
peak gpu mem: 0.000 GB	projected: False
prefill latency: 2.882 s	prefill throughput: 710.538 token/s
decode latency: 3.849 s	decode throughput: 32.217 token/s
total latency: 6.731 s	total throughput: 19.016 token/s
```

### 実行

FlexGenのサンプルコードとして用意されている `apps/chatbot.py` でAIと会話してみましょう。サンプルコードの実行にはPythonのdeeplライブラリが必要です。

```zsh:terminal
pip install deepl
```

さっそく会話していきましょう。以下のコマンドをターミナルで実行します。

```zsh:terminal
python3 -m flexgen.apps.chatbot --model facebook/opt-1.3b --platform mps:0
```

会話は英語で行う必要があります。何も入力せず`Enter`キーを押すと終了します。

```zsh:実行例
% python3 -m flexgen.apps.chatbot --model facebook/opt-1.3b --platform mps:0

Initialize...
A chat between a curious human and a knowledgeable artificial intelligence assistant.
Human: Hello! What can you do?
Assistant: As an AI assistant, I can answer questions and chat with you.
Human: What is the name of the tallest mountain in the world?
Assistant: Everest.
Human:{英語で会話する}
```

言語として`--dpl-lang ja`を指定するとdeeplを通して翻訳してくれます ([DeepL API](https://www.deepl.com/pro-api?cta=header-pro-api) の認証キーが必要です)。

```zsh:実行例
% python3 -m flexgen.apps.chatbot --model facebook/opt-1.3b --platform mps:0 --dpl-apikey {発行した認証キー} --dpl-lang ja

Initialize...
A chat between a curious human and a knowledgeable artificial intelligence assistant.
Human: Hello! What can you do?
Assistant: As an AI assistant, I can answer questions and chat with you.
Human: What is the name of the tallest mountain in the world?
Assistant: Everest.
Human: {日本語で会話する} こんにちは
Assistant: 私はAIアシスタントで、質問に答えることができます。 [5.59s]
Human:
```

参考

- https://note.com/kotatsurin/n/nc93d736cdc8f
- https://dev.classmethod.jp/articles/flexgen-in-colaboratory/

## 作曲AI (MusicGen) の場合

MusicGenは、プロンプトやメロディーから音楽を作曲する言語モデルです。Meta社がオープンソースで開発しています。ChatGPTと同じTransformerモデルの技術が用いられています。

<https://github.com/facebookresearch/audiocraft>

### インストール

以下のコマンドで簡単にインストールできます。

```zsh:terminal
pip install -U audiocraft
```

個人的にはこちらの方が本命だったのですが、リリース直後だということもあってまだmpsに対応していないため、現時点でM1 Macユーザーが取れる選択肢はcpuのみとなっています。(mpsを指定するとPyTorchのAMPが使えないためエラーが起きます)。今後機能更新があればこの記事も更新します。

### 実行

以下のPythonスクリプトを実行します。

```python: MusicGenTest.py
from audiocraft.models import MusicGen
from audiocraft.data.audio import audio_write

model = MusicGen.get_pretrained('small', device='cpu') # Macの場合、現時点ではcpuのみが選択できます。

model.set_generation_params(duration=8)  # duration [秒] の曲を生成します。
wav = model.generate_unconditional(4)    # まず「条件なし」で4つのサンプルを生成します。生成系AIではよくある手法です。
descriptions = ['happy rock', 'energetic EDM', 'sad jazz']
wav = model.generate(descriptions)  # descriptionリストに3つのプロンプトがあるので、それぞれについて1曲ずつ、計3曲が生成されます。

for idx, one_wav in enumerate(wav):
    # -14 db LUFSでラウドネスノーマライズを行ったものを、0.wavなどのファイル名で保存します。
    audio_write(f'{idx}', one_wav.cpu(), model.sample_rate, strategy="loudness")
```

GPUを使用していないので、軽い'small'モデルでも8秒の曲を3曲生成するのに4分ほどかかります。Macbook Pro (M2チップ、メモリ16GB) で 'medium'モデルを使用すると、8秒1曲の生成に12分ほどかかりました。今後に期待ですね。

> **実行例**
> <iframe width="100%" height="300" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/1543644928&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true"></iframe><div style="font-size: 10px; color: #cccccc;line-break: anywhere;word-break: normal;overflow: hidden;white-space: nowrap;text-overflow: ellipsis; font-family: Interstate,Lucida Grande,Lucida Sans Unicode,Lucida Sans,Garuda,Verdana,Tahoma,sans-serif;font-weight: 100;"><a href="https://soundcloud.com/satshout" title="satshout" target="_blank" style="color: #cccccc; text-decoration: none;">satshout</a> · <a href="https://soundcloud.com/satshout/musicgen-samples" title="MusicGen Samples" target="_blank" style="color: #cccccc; text-decoration: none;">MusicGen Samples</a></div>