Googleによる生成AIモデル「Gemma」をMacBook(M2)で動かしてみた

[Supership](https://supership.jp/)の名畑です。「[【ヒプステ】-Battle of Pride 2023-【Cinema Edit】3面ライブスクリーン映像](https://www.youtube.com/watch?v=-_rp6Ns0SYw)」を見てきたんですが、映画館という場の進化は素晴らしいものがあるなと思いました。

## はじめに

:::note
[Gemma 2を対象とした記事](https://qiita.com/nabata/items/428e8882651e8bc677f1)を公開済みですので、よければそちらをご覧ください
:::

**Google**から生成AIモデル[Gemma](https://blog.google/technology/developers/gemma-open-models/)がリリースされました。

- [Google、オープンな生成AIモデル「Gemma」公開　商用利用OK、Geminiと同じ技術の軽量LLM - ITmedia NEWS](https://www.itmedia.co.jp/news/articles/2402/22/news092.html)
- [Gemma が Google Cloud で利用可能に | Google Cloud 公式ブログ](https://cloud.google.com/blog/ja/products/ai-machine-learning/gemma-model-available-in-vertex-ai-and-via-gke)

この**Gemma**を手元のMacBook(M2)で動かしてみました。その過程を記事に残します。

コードはすべて**Python**です。

## 注意

本記事では**gemma-7b**を基本として話を進めていますが、モデルのダウンロードや推論の時間がかなりかかるため、まずは動かすことが目的であれば**gemma-2b**で進めていただければと思います。各**Python**コードに2箇所ある**gemma-7b-it**を**gemma-2b-it**に置き換えるだけです。

## 私の環境

PCは[MacBook Pro(Apple M2 Proチップ)](https://support.apple.com/kb/SP889?locale=ja_JP)です。
OSは[macOS 14 Sonoma](https://www.apple.com/jp/macos/sonoma/)です。

Pythonのバージョンは**3.10.12**です。

```shell-session
$ python --version
Python 3.10.12
```

[accelerate](https://github.com/huggingface/accelerate)、[torch](https://pytorch.org/)、[transformers](https://huggingface.co/docs/transformers/index)を用いるので、入れておきます。

```shell-session
$ pip install accelerate torch transformers        
```

私の環境でのバージョンはそれぞれ**0.27.2**、**2.2.1**、**4.38.1**でした。

```shell-session
$ pip list | grep -e accelerate -e torch -e transformers
accelerate         0.27.2
torch              2.2.1
transformers       4.38.1
```

## モデル

**gemma-7b**(70億パラメータ)と**Gemma 2B**(20億パラメータ)があります。そしてそれぞれに**instruct-tuned**(指示調整)されたものが用意されています。

- [gemma-7b-it](https://huggingface.co/google/gemma-7b-it)
- [gemma-7b](https://huggingface.co/google/gemma-7b)
- [gemma-2b-it](https://huggingface.co/google/gemma-2b-it)
- [gemma-2b](https://huggingface.co/google/gemma-2b)

モデル毎の概要は「[Welcome Gemma - Google’s new open LLM](https://huggingface.co/blog/gemma)」をご覧ください。**Llama 2**や**DeciLM**といった他のモデルとの比較等、要点がまとまっています。

## ライセンスの認証

今回は[Hugging Face](https://huggingface.co/)経由でモデルを入手して実行します。
そのためまずは**Hugging Face**のアカウントを作成します。

アカウントを作成してログイン後、4つあるモデルのいずれかのページに行くと**Gemma**へのアクセスのためのライセンス承認が求められます。

![gemma_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869934/d5549f57-8a82-8f50-34ec-b4624a403957.png)

**Acknowledge license**を押します。

![gemma_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869934/affb1508-1df3-6f04-566d-449bd7f1aec7.png)

**Access Request**のページに遷移しますので、フォームに名前等を記入していきます。
規約も読んで最後の**Accept**を押します。

モデルのページに戻ると、以下のようにアクセス認証が得られたことが確認できます。

![gemma_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869934/0504ccd9-5d07-2029-16b3-fc23ec5bfef7.png)

## アクセストークンの作成

モデルをダウンロードする際のユーザー認証のために**Hugging Face**のアクセストークンを作成します。

**Hugging Face**で[Settings](https://huggingface.co/settings/profile)に移動し、さらに[Access Tokens](https://huggingface.co/settings/tokens)に移動します。

ここで**New Token**を押します。

![gemma_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869934/b2a1ba17-5c2a-1302-162e-2b6c24a3ae8b.png)

**Name**はわかりやすく**gemma**としました。**Role**は読み取りだけなので**read**にしました。

トークン情報をハードコーディングするのはセキュリティ的によろしくないので、自環境の環境変数にセットしました。

まず設定ファイルを開きます。私の環境では**zshrc**です。

```shell-session
$ open ~/.zshrc
```

生成したアクセストークンの値を記録します。
私は**HUGGING\_FACE\_TOKEN**という名前にしました。

```shell-session
export HUGGING_FACE_TOKEN=ここにアクセストークンの値を書く
```

## サンプルコードの実行(CPU)

まずは**MacBook**にて[CPU向けサンプルコード](https://huggingface.co/google/gemma-7b-it#running-the-model-on-a-cpu)を動かしてみました。
アクセストークンに関する部分だけコードを変更しています。

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

hf_token = os.getenv("HUGGING_FACE_TOKEN")
tokenizer = AutoTokenizer.from_pretrained("google/gemma-7b-it", token=hf_token)
model = AutoModelForCausalLM.from_pretrained("google/gemma-7b-it", token=hf_token)

input_text = "Write me a poem about Machine Learning."
input_ids = tokenizer(input_text, return_tensors="pt")

outputs = model.generate(**input_ids)
print(tokenizer.decode(outputs[0]))
```

モデルに対して「**Write me a poem about Machine Learning.**」という文字列を渡しています。
訳すと「**機械学習についての詩を書いてください**」ですね。

初回はモデルのダウンロードが必要となります。上記コードは**7b-it**ですが、サイズとしては**約17GB**でした。時間がかかります。

結果は以下でした。

> In the realm of data, a tale unfolds,

訳すと「**データの領域で、物語が展開されます**」です。
内容はinputに即していますが、非常に短いです。

実行時に下記の警告が出ていました。

> UserWarning: Using the model-agnostic default `max_length` (=20) to control the generation length. We recommend setting `max_new_tokens` to control the maximum length of the generation.

**max\_new\_tokens**の設定を推奨しますという内容でした。

[ドキュメント](https://huggingface.co/docs/transformers/ja/main_classes/text_generation#transformers.GenerationConfig.max_length)によると、**max\_length**は「**入力プロンプト**と**max\_new\_tokens**の合計値の最大」のようです。

デフォルトだと**max\_length**は**20**とかなり小さいため、次は**max\_new\_tokens**として**50**を指定してみることにします。**max\_new\_tokens**を指定すれば**max\_length**も上書きされます。

## サンプルコードの実行(GPU)

次に[GPU向けサンプルコード](https://huggingface.co/google/gemma-7b-it#running-the-model-on-a-cpu)を動かしてみます。

コードは以下です。

```python
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
import os

hf_token = os.getenv("HUGGING_FACE_TOKEN")
tokenizer = AutoTokenizer.from_pretrained("google/gemma-7b-it", token=hf_token)
model = AutoModelForCausalLM.from_pretrained("google/gemma-7b-it", device_map="auto", token=hf_token)

input_text = "Write me a poem about Machine Learning."
input_ids = tokenizer(input_text, return_tensors="pt").to(torch.device("mps"))

outputs = model.generate(**input_ids, max_new_tokens=50)
print(tokenizer.decode(outputs[0]))
```

**MacBook**の**GPU**を使用するために**device**として[MPS(Metal Performance Shaders)](https://developer.apple.com/documentation/metalperformanceshaders) を指定しています。そのために**torch**をimportしています。

また、前述の通り、**max\_new\_tokens**に**50**を指定しました。

結果は以下でした。

> In the realm of data, a tale unfolds,
> Where algorithms dance, stories untold.
> With neural networks, they learn and grow,
> Unveiling patterns, hidden below.
> 
> Data whispers secrets, a treasure trove,
> Machine learning

日本語に翻訳してみると以下です。詩的、かな。

> データの領域で、物語が展開されます。
> アルゴリズムが踊る場所、語られない物語。
> ニューラル ネットワークを使用すると、学習して成長します。
> 下に隠されたパターンを明らかにします。
> 
> データが囁く秘密、宝の山、
> 機械学習

## 会話形式の入力プロンプト

[こちらの説明](https://huggingface.co/google/gemma-7b-it#chat-template)に従い、**input\_text**を以下にしてみました。

```python
input_text = """
<start_of_turn>user
Write a hello world program<end_of_turn>
<start_of_turn>model
"""
```

結果は以下です。

> ```python
> print("Hello, world!")
> ```
> 
> **Output:**
> 
> ```
> Hello, world!
> ```
> 
> **Explanation:**
> 
> * The `print` function is used to print the message to the console.
> * The string "

## 日本語の入力プロンプト

```python
input_text = """
<start_of_turn>user
日本の都道府県を5つ教えてください<end_of_turn>
<start_of_turn>model
"""
```

「**日本の都道府県を5つ教えてください**」という問いかけをしてみました。

結果は以下です。

> 1. 東部：東京都
> 2. 東部：千葉県
> 3. 西部：神戸県
> 4. 東部：岐阜県
> 5. 東部：福岡県

東部、西部? 神戸県?
首をかしげる内容ですが、日本語ではありますし、5つではあります。

## gemma-2b-itでの実行

**2b-it**にして同じことを行ってみました。ちなみにモデルの容量は**約5GB**でした。

コードの変更箇所は以下2行です。

```python
tokenizer = AutoTokenizer.from_pretrained("google/gemma-2b-it", token=hf_token)
model = AutoModelForCausalLM.from_pretrained("google/gemma-2b-it", device_map="auto", token=hf_token)
```

結果は以下です。

> 日本の都道府県は5つです。
> 1. 東京都
> 2. 神奈川県
> 3. 千葉県
> 4. 福岡県
> 5. 大阪府

現実には47都道府県ありますが、質問に即した回答ではあります。

## 最後に

まだまだ結果として誤った部分はあれど、これだけのモデルを商用利用OKとして提供していただけて手元で動かせるのは本当に嬉しいです。

## 宣伝

[SupershipのQiita Organization](https://qiita.com/organizations/supership-inc)を合わせてご覧いただけますと嬉しいです。他のメンバーの記事も多数あります。

Supershipではプロダクト開発やサービス開発に関わる方を絶賛募集しております。
興味がある方は[Supership株式会社 採用サイト](https://supership.jp/recruit/)よりご確認ください。