IBM の Granite Code Models を Google Colaboratory で動かす

## はじめに

IBM 社が OSS として公開した「コード生成LLM」である Granite-8B-Code-Base を Google Colaboratory 上で動かします

GitHub Copilot のように、プログラムコードを生成してくれるモデルです

https://github.com/ibm-granite/granite-code-models

基本的には以前他社のモデルで実行したのと同じ手法になります

https://qiita.com/RyoWakabayashi/items/027fb61f9ea19282a669

https://qiita.com/RyoWakabayashi/items/6c21167121c0dee5bcdc

https://qiita.com/RyoWakabayashi/items/d02a2be5d3851a524c6b

モデルの種類は以下の通り

- ibm-granite/granite-3b-code-base
- ibm-granite/granite-3b-code-instruct
- ibm-granite/granite-8b-code-base
- ibm-granite/granite-8b-code-instruct
- ibm-granite/granite-20b-code-base
- ibm-granite/granite-20b-code-instruct
- ibm-granite/granite-34b-code-base
- ibm-granite/granite-34b-code-instruct

`-base` はコードの補完、 `-instruct` は指示によるコード生成を行います

また、 b = 10億パラメーター を表します

`-3b-` だと 30 億、 `-8b-` だと 80 億です

この記事では 3b と　8b のモデルを使います

## モデルの取得元

モデルは商用利用可能なライセンスで HuggingFace 上に公開されています

https://huggingface.co/collections/ibm-granite/granite-code-models-6624c5cec322e4c148c8b330

モデルのダウンロードに Hugguing Face のアクセストークンが必要になるので、サインアップしてトークンを用意しておきましょう

設定画面の左メニューから「Access Tokens」を開き、「New token」ボタンでトークンが作成できます

https://huggingface.co/settings/tokens

![トークン取得](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/eb7c556b-9361-92c7-cfeb-163442a68bed.png)

## Google Colab のランタイム

Google Colab の GPU ランタイムは3つ存在します

それぞれ VRAM の容量に違いがあります

- T4: 15 GB
- L4: 22.5 GB
- A100: 40 GB

3b や 8b などのパラメーター数が多くなればメモリの容量も多く必要になるため、それに応じたランタイムを選択しましょう

メモリが不足している場合はモデルのロード中に以下の警告メッセージが表示されます

CPU で動かそうとするため推論が極端に遅くなります（実質使用不可能です）

```
WARNING:accelerate.big_modeling:Some parameters are on the meta device device because they were offloaded to the cpu.
```

今回扱うモデルの場合、以下に示す容量が必要です

- 3b: 6.4 GB  = T4 で実行可能
- 8b: 15.4 GB = L4 で実行可能

T4 は無償版で利用可能ですが、 L4 や A100 が必要な場合は有償契約しましょう

https://colab.research.google.com/signup/pricing?hl=ja

## 共通手順

どのモデルを使用する場合も共通の手順です

### ノートブックの設定

新しいノートブックを開きます

上メニューから「ランタイム」 |> 「ランタイムのタイプを変更」を開きます

![スクリーンショット 2024-03-22 17.34.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/d44ea3fd-c019-8f4b-b69b-c225e6629cea.png)

ハードウェアアクセラレータで利用する GPU を選択し、「保存」をクリックします
（3b の場合は T4 GPU 、 8b の場合は L4 GPU）

![スクリーンショット 2024-07-07 20.06.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/fdd6d4f0-b120-0c1d-0826-129898f5bd58.png)

左メニューの鍵アイコンをクリックし、 `HF_TOKEN` という名前で Hugging Face のアクセストークンをシークレットに登録しておきます

「ノートブックからのアクセス」を忘れず ON にしておきましょう

![スクリーンショット 2024-03-24 12.36.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/42bcddf5-e821-7470-a849-51bb52f2bf11.png)

### セットアップ

以降、 Colab のセルにコードを貼り付けて実行します

pip 自体を最新化してから、必要なモジュールをインストールします

```sh
!pip install --upgrade pip
!pip install --upgrade accelerate transformers gradio
```

モジュールをインポートしておきます

```python
import gradio as gr
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
```

:::note warn
`gradio` は最後に UI を作るためのものなので、モデルを動かしたいだけの場合は不要です
:::

### モデルのロード

transformers の機能で Hugging Face からトークナイザー、モデルをダウンロードし、ロードします

`model_name` には利用するモデル名（HuggingFace 上の ID）を指定します

`device_map="auto"` を指定することで、自動的に環境に合った状態（GPU の有無など）でロードしてくれます

```python
model_name = "ibm-granite/granite-8b-code-base"
```

```python
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    torch_dtype="auto",
    device_map="auto",
)
model.eval()
```

ロードすると GPU の使用率が上がります

![スクリーンショット 2024-07-18 14.04.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/2c5c3842-44ca-8af2-0f9a-726b0bf25f10.png)

## コード補完

`-base` が末尾につくモデルを使用し、途中まで書いたコードを補完します

### モデルによるコード補完の実行 

```python
input_text = "def max(a, b, c):"
input_tokens = tokenizer(input_text, return_tensors="pt")
for i in input_tokens:
    input_tokens[i] = input_tokens[i].to(model.device)

with torch.no_grad():
    output_tokens = model.generate(**input_tokens, max_new_tokens=1024)

output = tokenizer.batch_decode(output_tokens, skip_special_tokens=True)

for i in output:
    print(i)
```

`granite-3b-code-base` の場合、実行結果は以下のように出力されます

```python
def max(a, b, c):
    if a > b:
        if a > c:
            return a
        else:
            return c
    else:
        if b > c:
            return b
        else:
            return c

def min(a, b, c):
    if a < b:
        if a < c:
            return a
        else:
            return c
    else:
        if b < c:
            return b
        else:
            return c

def main():
    n = int(input())
    a = list(map(int, input().split()))
    b = list(map(int, input().split()))
    c = list(map(int, input().split()))
    a.sort()
    b.sort()
    c.sort()
    print(max(a[0] + b[1] + c[2], a[1] + b[2] + c[0], a[2] + b[0] + c[1]))

if __name__ == '__main__':
    main()
```

3b なので微妙な結果ですが、意図は汲んでくれています

### UI付きで実行

Gradio を使って簡易的な UI を用意します

https://www.gradio.app/

まず、入力を出力に変換するための関数を用意します

```python
def chat(input_text):
    input_tokens = tokenizer(input_text, return_tensors="pt")
    for i in input_tokens:
        input_tokens[i] = input_tokens[i].to(model.device)

    with torch.no_grad():
        output_tokens = model.generate(**input_tokens, max_new_tokens=1024)

    output = tokenizer.batch_decode(output_tokens, skip_special_tokens=True)

    return output[0]
```

Gradio で入出力を定義します

```python
gr.Interface(
    fn=chat,
    inputs=[gr.Textbox()],
    outputs=[gr.Textbox()]
).launch(debug=True)
```

実行すると、以下のような UI が表示されます

![スクリーンショット 2024-03-22 17.48.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/1c16663a-0032-dbe7-339c-ff2885aa7ed5.png)

左側の input にコードの最初の部分を入力してみます

「Submit」を押すと、右側の output に生成結果が表示されます

![スクリーンショット 2024-07-18 14.09.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/169f67a3-2f45-76e4-6fd5-c4de38f27ea1.png)

### コード補完の問答集

#### Python 距離計算

入力

```python
def distance(point1, point2):
```

3b の出力

距離計算は正しいですが、その後に続く関数は実用的に思えません

```python
def distance(point1, point2):
    return math.sqrt((point1[0] - point2[0]) ** 2 + (point1[1] - point2[1]) ** 2)


def get_angle(point1, point2, point3):
    return math.degrees(math.atan2(point2[1] - point1[1], point2[0] - point1[0]) - math.atan2(point3[1] - point1[1], point3[0] - point1[0]))


def get_angle_between_points(point1, point2):
    return math.degrees(math.atan2(point2[1] - point1[1], point2[0] - point1[0]))


def get_angle_between_points_and_x_axis(point1, point2):
    return math.degrees(math.atan2(point2[1] - point1[1], point2[0] - point1[0]))


def get_angle_between_points_and_y_axis(point1, point2):
    return math.degrees(math.atan2(point2[0] - point1[0], point2[1] - point1[1]))


def get_angle_between_points_and_z_axis(point1, point2):
    return math.degrees(math.atan2(point2[2] - point1[2], point2[1] - point1[1]))


def get_angle_between_points_and_xy_plane(point1, point2):
    return math.degrees(math.atan2(point2[2] - point1[2], point2[0] - point1[0]))


def get_angle_between_points_and_xz_plane(point1, point2):
    return math.degrees(math.atan2(point2[1] - point1[1], point2[0] - point1[0]))


def get_angle_between_points_and_yz_plane(point1, point2):
    return math.degrees(math.atan2(point2[0] - point1[0], point2[2] - point1[2]))


def get_angle_between_points_and_xyz_plane(point1, point2):
    return math.degrees(math.atan2(point2[2] - point1[2], point2[1] - point1[1]))


def get_angle_between_points_and_xy_plane_and_z_axis(point1, point2):
    return math.degrees(math.atan2(point2[1] - point1[1], point2[0] - point1[0]))


def get_angle_between_points_and_xz_plane_and_y_axis(point1, point2):
    return math.degrees(math.atan2(point2[0] - point1[0], point2[2] - point1[2]))


def get_angle_between_points_and_yz_plane_and_x_axis(point1, point2):
    return math.degrees(math.atan2(point2[2] - point1[2], point2[1] - point1[1]))


def get_angle_between_points_and_xyz_plane_and_xy_plane(point1, point2):
    return math.degrees(math.atan2(point2[1] - point1[1], point2[0] - point1[0]))


def get_angle_between_points_and_xyz_plane_and_xz_plane(point1, point2):
    return math.degrees(math.atan2(point2[0] - point1[0], point2[2] - point1[2]))


def get_angle_between_points_and_xyz_plane_and_yz_plane(point1, point2):
    return math.degrees(math.atan2(point2[2] - point1[2], point2[1] - point1[1]))


def get_angle_between_points_and_xyz_plane_and_xyz_plane(point1, point2):
    return math.degrees(math.atan2(point2[1] - point1[1], point2[0] - point1[0]))


def get_angle_between_points_and_xy_plane_and_xy_plane(point1, point2):
    return math.degrees
```

8b の出力

距離の計算以外の関数が実用的なものになっている感じがします

```python
def distance(point1, point2):
    return math.sqrt((point1[0] - point2[0])**2 + (point1[1] - point2[1])**2)

# Function to calculate the angle between two vectors
def angle_between(v1, v2):
    return math.acos(np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2)))

# Function to calculate the area of the triangle
def triangle_area(a, b, c):
    s = (a + b + c) / 2
    return math.sqrt(s * (s - a) * (s - b) * (s - c))

# Function to calculate the area of the polygon
def polygon_area(points):
    area = 0.0
    for i in range(len(points)):
        j = (i + 1) % len(points)
        area += points[i][0] * points[j][1]
        area -= points[j][0] * points[i][1]
    area = abs(area) / 2.0
    return area

# Function to calculate the perimeter of the polygon
def polygon_perimeter(points):
    perimeter = 0.0
    for i in range(len(points)):
        j = (i + 1) % len(points)
        perimeter += distance(points[i], points[j])
    return perimeter

# Function to calculate the centroid of the polygon
def polygon_centroid(points):
    x_sum = 0.0
    y_sum = 0.0
    for i in range(len(points)):
        x_sum += points[i][0]
        y_sum += points[i][1]
    centroid = (x_sum / len(points), y_sum / len(points))
    return centroid

# Function to calculate the inradius of the polygon
def polygon_inradius(points):
    area = polygon_area(points)
    perimeter = polygon_perimeter(points)
    inradius = area / perimeter
    return inradius

# Function to calculate the circumradius of the polygon
def polygon_circumradius(points):
    centroid = polygon_centroid(points)
    distances = [distance(centroid, point) for point in points]
    circumradius = max(distances)
    return circumradius

# Function to calculate the regularity of the polygon
def polygon_regularity(points):
    centroid = polygon_centroid(points)
    distances = [distance(centroid, point) for point in points]
    regularity = max(distances) / min(distances)
    return regularity

# Function to calculate the number of sides of the polygon
def polygon_num_sides(points):
    return len(points)

# Function to calculate the number of vertices of the polygon
def polygon_num_vertices(points):
    return len(points)

# Function to calculate the number of diagonals of the polygon
def polygon_num_diagonals(points):
    num_sides = len(points)
    num_diagonals = num_sides * (num_sides - 3) / 2
    return num_diagonals

# Function to calculate the number of acute angles of the polygon
def polygon_num_acute_angles(points):
    num_acute_angles = 0
    for i in range(len(points)):
        j = (i + 1) % len(points)
        k = (i + 2) % len(points)
        v1 = np.array(points[j]) - np.array(points[i])
        v2 = np.array(points[k]) - np.array(points[i])
        angle = angle_between(v1, v2)
        if angle < math.pi / 2:
            num_acute_angles += 1
    return num_acute_angles

# Function to calculate the number of obtuse angles of the polygon
def polygon_num_obtuse_angles(points):
    num_obtuse_angles = 0
    for i in range(len(points)):
        j = (i + 1) % len(points)
        k = (i + 2) % len(points)
        v1 = np.array(points[j]) - np.array(points[i])
        v2 = np.array(points[k]) - np.array(points[i])
        angle = angle_between(v1, v2)
        if angle > math.pi / 2:
            num_obtuse_angles += 1
    return num_
```

#### Python 画像処理（ガンマ補正）

入力

```python
import cv2
import numpy as np

def adjust_gamma(image, gamma=1.0):
```

3b の出力

ガンマ補正は正しそうですが、意味もなく同じ関数を繰り返しています

```python
import cv2
import numpy as np

def adjust_gamma(image, gamma=1.0):
    # build a lookup table mapping the pixel values [0, 255] to
    # their adjusted gamma values
    invGamma = 1.0 / gamma
    table = np.array([((i / 255.0) ** invGamma) * 255
        for i in np.arange(0, 256)]).astype("uint8")
    # apply gamma correction using the lookup table
    return cv2.LUT(image, table)

def adjust_gamma_image(image, gamma=1.0):
    # build a lookup table mapping the pixel values [0, 255] to
    # their adjusted gamma values
    invGamma = 1.0 / gamma
    table = np.array([((i / 255.0) ** invGamma) * 255
        for i in np.arange(0, 256)]).astype("uint8")
    # apply gamma correction using the lookup table
    return cv2.LUT(image, table)

def adjust_gamma_image_2(image, gamma=1.0):
    # build a lookup table mapping the pixel values [0, 255] to
    # their adjusted gamma values
    invGamma = 1.0 / gamma
    table = np.array([((i / 255.0) ** invGamma) * 255
        for i in np.arange(0, 256)]).astype("uint8")
    # apply gamma correction using the lookup table
    return cv2.LUT(image, table)

def adjust_gamma_image_3(image, gamma=1.0):
    # build a lookup table mapping the pixel values [0, 255] to
    # their adjusted gamma values
    invGamma = 1.0 / gamma
    table = np.array([((i / 255.0) ** invGamma) * 255
        for i in np.arange(0, 256)]).astype("uint8")
    # apply gamma correction using the lookup table
    return cv2.LUT(image, table)

def adjust_gamma_image_4(image, gamma=1.0):
    # build a lookup table mapping the pixel values [0, 255] to
    # their adjusted gamma values
    invGamma = 1.0 / gamma
    table = np.array([((i / 255.0) ** invGamma) * 255
        for i in np.arange(0, 256)]).astype("uint8")
    # apply gamma correction using the lookup table
    return cv2.LUT(image, table)

def adjust_gamma_image_5(image, gamma=1.0):
    # build a lookup table mapping the pixel values [0, 255] to
    # their adjusted gamma values
    invGamma = 1.0 / gamma
    table = np.array([((i / 255.0) ** invGamma) * 255
        for i in np.arange(0, 256)]).astype("uint8")
    # apply gamma correction using the lookup table
    return cv2.LUT(image, table)

def adjust_gamma_image_6(image, gamma=1.0):
    # build a lookup table mapping the pixel values [0, 255] to
    # their adjusted gamma values
    invGamma = 1.0 / gamma
    table = np.array([((i / 255.0) ** invGamma) * 255
        for i in np.arange(0, 256)]).astype("uint8")
    # apply gamma correction using the lookup table
    return cv2.LUT(image, table)

def adjust_gamma_image_7(image, gamma=1.0):
    # build a lookup table mapping the pixel values [0, 255] to
    # their adjusted gamma values
    invGamma = 1.0 / gamma
    table = np.array([((i / 255.0) ** invGamma) * 255
        for i in np.arange(0, 256)]).astype("uint8")
    # apply gamma correction using the lookup table
    return cv2.LUT(image, table)

def adjust_gamma_image_8(image, gamma=1.0):
    # build a lookup table mapping the pixel values [0, 255] to
    # their adjusted gamma values
    invGamma = 1.0 / gamma
    table = np.array([((i / 255.0) ** invGamma) * 
```

8b の出力

実際に使うところまで生成してくれています

```python
import cv2
import numpy as np

def adjust_gamma(image, gamma=1.0):
    # build a lookup table mapping the pixel values [0, 255] to
    # their adjusted gamma values
    invGamma = 1.0 / gamma
    table = np.array([((i / 255.0) ** invGamma) * 255
        for i in np.arange(0, 256)]).astype("uint8")
 
    # apply gamma correction using the lookup table
    return cv2.LUT(image, table)

def main():
    img = cv2.imread('images/lena.png')
    img = adjust_gamma(img, 0.5)
    cv2.imshow('image', img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

if __name__ == '__main__':
    main()
```

####  Elixir 平均値取得

入力

```elixir
defmodule User do
  defstruct name: "", age: 0
end

defmodule UserStats do
  def average_age(users) when is_list(users) do
```

3b の出力

説明を加えてくれました

````elixir
defmodule User do
  defstruct name: "", age: 0
end

defmodule UserStats do
  def average_age(users) when is_list(users) do
    Enum.sum(Enum.map(users, & &1.age)) / length(users)
  end
end
```

In this solution, we define a module called `User` with a struct that includes the `name` and `age` fields. We also define a module called `UserStats` with a function `average_age` that takes a list of `User` structs as input and calculates the average age by summing the ages of all users and dividing by the number of users.
````

8b の出力

モジュールを使うコードまで出力してくれました

```elixir
defmodule User do
  defstruct name: "", age: 0
end

defmodule UserStats do
  def average_age(users) when is_list(users) do
    total_age = Enum.reduce(users, 0, fn user, acc -> acc + user.age end)
    length(users) / total_age
  end
end

# Example usage
user1 = %User{name: "John", age: 25}
user2 = %User{name: "Jane", age: 30}
users = [user1, user2]

IO.puts("Average age of users: #{UserStats.average_age(users)}")
```

#### JavaScript （React）

入力

```javascript
import React, { useState } from'react';

const Counter = () => {
  // useStateフックを使用してカウントの状態を管理
  const [count, setCount] = useState(0);

  // カウントアップの関数
  const increment = () => {
    setCount(count + 1);
  };
```

3b の出力

同じ説明を狂ったように繰り返していて怖いです

````javascript
import React, { useState } from'react';

const Counter = () => {
  // useStateフックを使用してカウントの状態を管理
  const [count, setCount] = useState(0);

  // カウントアップの関数
  const increment = () => {
    setCount(count + 1);
  };

  // カウントダウンの関数
  const decrement = () => {
    setCount(count - 1);
  };

  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
    </div>
  );
};

export default Counter;
```

このコードでは、useStateフックを使用してカウンターの状態を管理しています。useStateフックは、Reactコンポーネントの内部で使用できる、状態と状態の更新を管理するための関数です。useStateフックは、2つの引数を取ります。最初の引数は、状態の初期値です。2番目の引数は、状態を更新するための関数です。

カウンターの状態は、useStateフックの戻り値の配列の最初の要素に格納されます。useStateフックの戻り値の配列の2番目の要素は、状態を更新するための関数です。

カウンターの状態を更新するための関数は、increment関数とdecrement関数です。increment関数は、カウントを1増やす関数です。decrement関数は、カウントを1減らす関数です。

Reactコンポーネントの内部では、setState関数を使用してカウンターの状態を更新できます。setState関数は、Reactコンポーネントの内部で使用できる、状態を更新するための関数です。setState関数は、1つの引数を取ります。最初の引数は、新しい状態です。

setState関数は、Reactコンポーネントの内部で使用できる、状態を更新するための関数です。setState関数は、1つの引数を取ります。最初の引数は、新しい状態です。

setState関数は、Reactコンポーネントの内部で使用できる、状態を更新するための関数です。setState関数は、1つの引数を取ります。最初の引数は、新しい状態です。

setState関数は、Reactコンポーネントの内部で使用できる、状態を更新するための関数です。setState関数は、1つの引数を取ります。最初の引数は、新しい状態です。

setState関数は、Reactコンポーネントの内部で使用できる、状態を更新するための関数です。setState関数は、1つの引数を取ります。最初の引数は、新しい状態です。

setState関数は、Reactコンポーネントの内部で使用できる、状態を更新するための関数です。setState関数は、1つの引数を取ります。最初の引数は、新しい状態です。

setState関数は、Reactコンポーネントの内部で使用できる、状態を更新するための関数です。setState関数は、1つの引数を取ります。最初の引数は、新しい状態です。

setState関数は、Reactコンポーネントの内部で使用できる、状態を更新するための関数です。setState関数は、1つの引数を取ります。最初の引数は、新しい状態です。

setState関数は、Reactコンポーネントの内部で使用できる、状態を更新するための関数です。setState関数は、1つの引数を取ります。最初の引数は、新しい状態です。

setState関数は、Reactコンポーネントの内部で使用できる、状態を更新するための関数です。setState関数は、1つの引数を取ります。最初の引数は、新しい状態です。

setState関数は、Reactコンポーネントの内部で使用できる、状態を更新するための関数です。setState関数は、1つの引数を取ります。最初の引数は、新しい状態です。

setState関数は、Reactコンポーネントの内部で使用できる、状態を更新するための関数です。setState関数は、1つの引数を取ります。最初の引数は、新しい状態です。

setState関数は、Reactコンポーネントの内部で使用できる、状態を更新するための関数です。setState関数は、1つの引数を取ります。最初の引数は、新しい状態です。

setState関数は、Reactコンポーネントの内部で使用できる、状態を更新するための関数です。setState関数は、1つの引数を取ります。最初の引数は、新しい状態です。

setState関数は、Reactコンポーネントの
````

8b の出力

説明を加えてくれました

````javascript
import React, { useState } from'react';

const Counter = () => {
  // useStateフックを使用してカウントの状態を管理
  const [count, setCount] = useState(0);

  // カウントアップの関数
  const increment = () => {
    setCount(count + 1);
  };

  // カウントダウンの関数
  const decrement = () => {
    setCount(count - 1);
  };

  return (
    <div>
      <h1>カウンター</h1>
      <p>現在のカウント: {count}</p>
      <button onClick={increment}>カウントアップ</button>
      <button onClick={decrement}>カウントダウン</button>
    </div>
  );
};

export default Counter;
```

このコードでは、useStateフックを使用してカウントの状態を管理しています。countという変数に現在のカウントを保持し、increment関数とdecrement関数を定義して、それぞれカウントアップとカウントダウンを行っています。

このように、ReactのuseStateフックを用いることで、簡単にカウンターアプリケーションを実装することができます。
````

#### JavaScript （Vue）

入力

```javascript
<template>
  <div style="text-align: center; margin-top: 50px;">
    <h1>Counter: {{ count }}</h1>
    <button @click="increment" style="margin-right: 10px;">Count Up</button>
    <button @click="decrement">Count Down</button>
  </div>
</template>
```

3b の出力

中国語の Markdown に入るような形で生成されました

学習データにかなり引きずられている感じがします

````javascript
<template>
  <div style="text-align: center; margin-top: 50px;">
    <h1>Counter: {{ count }}</h1>
    <button @click="increment" style="margin-right: 10px;">Count Up</button>
    <button @click="decrement">Count Down</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++
    },
    decrement() {
      this.count--
    }
  }
}
</script>
```

### 2.2 组件间通信

#### 2.2.1 父子组件通信

父组件向子组件传递数据

```html
<template>
  <div>
    <h1>Counter: {{ count }}</h1>
    <button @click="increment">Count Up</button>
    <ChildComponent :count="count" />
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue'

export default {
  components: {
    ChildComponent
  },
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++
    }
  }
}
</script>
```

子组件向父组件传递数据

```html
<template>
  <div>
    <h1>Counter: {{ count }}</h1>
    <button @click="increment">Count Up</button>
    <ParentComponent :count="count" />
  </div>
</template>

<script>
import ParentComponent from './ParentComponent.vue'

export default {
  components: {
    ParentComponent
  },
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++
    }
  }
}
</script>
```

#### 2.2.2 兄弟组件通信

兄弟组件之间通过 `ref` 引用传递数据

```html
<template>
  <div>
    <h1>Counter: {{ count }}</h1>
    <button @click="increment">Count Up</button>
    <ChildComponent ref="child" />
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue'

export default {
  components: {
    ChildComponent
  },
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.$refs.child.increment()
    }
  }
}
</script>
```

#### 2.2.3 跨级组件通信

跨级组件之间通过 `provide` 和 `inject` 实现

```html
<template>
  <div>
    <h1>Counter: {{ count }}</h1>
    <button @click="increment">Count Up</button>
    <ChildComponent />
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue'

export default {
  components: {
    ChildComponent
  },
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.$refs.child.increment()
    }
  }
}
</script>
```

```html
<template>
  <div>
    <h1>Counter: {{ count }}</h1>
    <button @click="increment">Count Up</button>
  </div>
</template>

<script>
export default {
  provide() {
    return {
      count: this.count
    }
  },
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++
    }
  }
}
</script>
```

```html
<template>
  <div>
    <h1>Counter: {{ count }}</h1>
    <button @click="increment">Count Up</button>
  </div>
</template>

<script>
export default {
  inject: ['count'],
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++
    }
  }
}
</script>
```

### 2.3 事件处理

#### 2.3.1 事件绑定

```html
<template>
  <div>
    <h1>Counter: {{ count }}</h1>
    <button @click="increment">Count Up</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++
    }
  }
}
</script>
```

#### 2.3.2 事件修饰符
````

8b の出力

説明を加えた最小限の内容になっています

````javascript
<template>
  <div style="text-align: center; margin-top: 50px;">
    <h1>Counter: {{ count }}</h1>
    <button @click="increment" style="margin-right: 10px;">Count Up</button>
    <button @click="decrement">Count Down</button>
  </div>
</template>

<script>
import { ref } from 'vue';

export default {
  setup() {
    const count = ref(0);

    const increment = () => {
      count.value++;
    };

    const decrement = () => {
      count.value--;
    };

    return {
      count,
      increment,
      decrement,
    };
  },
};
</script>
```

In this code, we import the `ref` function from Vue. We then define a `count` variable using `ref` and initialize it with a value of 0.

The `increment` function is responsible for incrementing the `count` variable by 1. It simply updates the `count` value using the `value` property of the `ref` object.

The `decrement` function is responsible for decrementing the `count` variable by 1. It also updates the `count` value using the `value` property of the `ref` object.

Finally, we export the `count`, `increment`, and `decrement` functions as the default export of the component.

By using this code, you will have a Vue.js component that displays a counter and allows the user to increment and decrement the counter by clicking on the respective buttons.
````

#### bash (AWS 操作)

入力

```bash
#!/bin/bash
# 指定プレフィックス内の合計サイズを取得

if [ "$#" -ne 2 ]; then
  echo "Usage: $0 <bucket-name> <prefix>"
  exit 1
fi

BUCKET_NAME=$1
PREFIX=$2

# 指定されたバケットとプレフィックスからオブジェクトのリストを取得
```

3b の出力

このコードだと `--key` に指定する値に $PREFIX が含まれていないため、正しく動作しません

```bash
#!/bin/bash
# 指定プレフィックス内の合計サイズを取得

if [ "$#" -ne 2 ]; then
  echo "Usage: $0 <bucket-name> <prefix>"
  exit 1
fi

BUCKET_NAME=$1
PREFIX=$2

# 指定されたバケットとプレフィックスからオブジェクトのリストを取得
OBJECT_LIST=$(aws s3 ls s3://$BUCKET_NAME/$PREFIX | awk '{print $4}')

# 合計サイズを計算
TOTAL_SIZE=0
for OBJECT in $OBJECT_LIST; do
  SIZE=$(aws s3api head-object --bucket $BUCKET_NAME --key $OBJECT | jq -r.ContentLength)
  TOTAL_SIZE=$((TOTAL_SIZE + SIZE))
done

echo $TOTAL_SIZE
```

8b の出力

これも実は動きません

そもそも与えている入力が悪いのかな
（実は ChatGPT に生成させたコードの前半を入力にしています）

```bash
#!/bin/bash
# 指定プレフィックス内の合計サイズを取得

if [ "$#" -ne 2 ]; then
  echo "Usage: $0 <bucket-name> <prefix>"
  exit 1
fi

BUCKET_NAME=$1
PREFIX=$2

# 指定されたバケットとプレフィックスからオブジェクトのリストを取得
OBJECTS=$(aws s3api list-objects-v2 --bucket $BUCKET_NAME --prefix $PREFIX --query 'Contents[*].{Key:Key,Size:Size}' --output text)

# 合計サイズを計算
TOTAL_SIZE=0
for OBJECT in $OBJECTS; do
  KEY=$(echo $OBJECT | cut -d''-f1)
  SIZE=$(echo $OBJECT | cut -d''-f2)
  TOTAL_SIZE=$(($TOTAL_SIZE + $SIZE))
done

# 合計サイズを表示
echo "Total size of objects with prefix $PREFIX in bucket $BUCKET_NAME is $TOTAL_SIZE bytes"
```

## 指示によるコード生成

作りたいものを指示してコードを生成させます

### コード生成の実行

```python
instruction = "users の age の中央値を取得する Python 関数を生成してください"
chat = [
    { "role": "user", "content": instruction },
]
chat = tokenizer.apply_chat_template(chat, tokenize=False, add_generation_prompt=True)
input_tokens = tokenizer(chat, return_tensors="pt")

for i in input_tokens:
    input_tokens[i] = input_tokens[i].to(model.device)

with torch.no_grad():
    output_tokens = model.generate(**input_tokens, max_new_tokens=1024)

output = tokenizer.decode(
    output_tokens.tolist()[0][input_tokens['input_ids'].shape[-1]:], skip_special_tokens=True
)

print(output)
```

3b の場合の実行結果

偶数の場合が考慮できていません

```python
users = [
    {"name": "John", "age": 25},
    {"name": "Alice", "age": 30},
    {"name": "Bob", "age": 28},
    {"name": "Charlie", "age": 32},
    {"name": "Dave", "age": 27},
]

def get_middle_age(users):
    sorted_users = sorted(users, key=lambda user: user["age"])
    middle_index = len(sorted_users) // 2
    return sorted_users[middle_index]["age"]

middle_age = get_middle_age(users)
print(middle_age) # 28
```

8b の場合の実行結果

偶数の場合まで考慮できています

```python
users = [
    {"name": "Alice", "age": 20},
    {"name": "Bob", "age": 30},
    {"name": "Charlie", "age": 25},
    {"name": "Dave", "age": 35},
]

def get_median_age(users):
    ages = [user["age"] for user in users]
    ages.sort()
    length = len(ages)
    if length % 2 == 0:
        middle_index = length // 2
        return (ages[middle_index - 1] + ages[middle_index]) / 2
    else:
        middle_index = length // 2
        return ages[middle_index]

median_age = get_median_age(users)
print(median_age)
```

### UI付きで実行

```python
def chat(instruction):
    chat = [
        { "role": "user", "content": instruction },
    ]
    chat = tokenizer.apply_chat_template(chat, tokenize=False, add_generation_prompt=True)
    input_tokens = tokenizer(chat, return_tensors="pt")

    for i in input_tokens:
        input_tokens[i] = input_tokens[i].to(model.device)

    with torch.no_grad():
        output_tokens = model.generate(**input_tokens, max_new_tokens=1024)

    output = tokenizer.decode(
        output_tokens.tolist()[0][input_tokens['input_ids'].shape[-1]:], skip_special_tokens=True
    )

    return output
```

```python
gr.Interface(
    fn=chat,
    inputs=[gr.Textbox()],
    outputs=[gr.Textbox()]
).launch(debug=True)
```

### コード生成の問答集

#### Python 距離計算

入力

```text
2点間の距離を計算する Python 関数を生成してください
```

3b の出力

説明文が日本語と英語入り混じっていて不自然です

````markdown
2点間の距離は、 Pythagoras 定理に従い、 x2 + y2 = d2 です。

以下は、 Python から Pythagoras 定理を適用して 2 点間の距離を計算する Python FUNCTION を生成する方法です。

```python
import math

def distance(x1, y1, x2, y2):
    return math.sqrt((x2 - x1)**2 + (y2 - y1)**2)
```

上記の Python FUNCTION は、 x1, y1, x2, y2 の 4 つの引数を受け取り、 x2 - x1, y2 - y1 の 2 つの引数を受け取り、 2 点間の距離を計算します。 したがって、 x1, y1, x2, y2 の 4 つの引数は、 2 点間の x, y 座標です。
````

8b の出力

説明文が自然になっています

````markdown
2点間の距離を計算する Python 関数を生成するには、2点の座標 (x1, y1)、(x2, y2) を受け取り、distance = sqrt((x2 - x1)^2 + (y2 - y1)^2) を計算する simple_distance_function を定義します。

```python
import math

def simple_distance_function(x1, y1, x2, y2):
    distance = math.sqrt((x2 - x1)**2 + (y2 - y1)**2)
    return distance
```

x1, y1, x2, y2 を受け取り、distance = sqrt((x2 - x1)^2 + (y2 - y1)^2) を計算する simple_distance_function を定義します。distance を返します。
````

#### Python 画像処理（ガンマ補正）

入力

```text
画像のガンマ補正を実行する Python 関数を生成してください
```

3b の出力

異常な出力になってしまいました

````markdown
 GaussFilter() です。 GaussFilter() には、 GaussFilter.apply() があります。 GaussFilter.apply() には、 GaussFilter.apply(image, sigma) があります。 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると、 GaussFilter.apply(image, sigma) を使用すると
````

8b の出力

ガンマ補正できていそうです

````markdown
```python
import cv2

def gamma_correction(image, gamma=1.0):
    inv_gamma = 1.0 / gamma
    table = np.array([((i / 255.0) ** inv_gamma) * 255 for i in np.arange(0, 256)]).astype("uint8")
    return cv2.LUT(image, table)
```
````

####  Elixir 平均値取得

入力

```text
users の age の平均値を取得する Elixir 関数を生成してください
```

3b の出力

いい感じのコードです

```elixir
defmodule User do
  defstruct [:name, :age]
end

users = [
  %User{name: "Alice", age: 25},
  %User{name: "Bob", age: 30},
  %User{name: "Charlie", age: 28}
]

defmodule UserAge do
  def average_age(users) do
    Enum.sum(Enum.map(users, & &1.age)) / Enum.count(users)
  end
end

IO.puts(UserAge.average_age(users))

# 27.0
```

8b の出力

モジュールを定義せずに実装しています

reduce を使っているあたりは Elixir 好きっぽいです

````markdown
users = [%{age: 20}, %{age: 30}, %{age: 40}]

Enum.reduce(users, 0, fn user, acc ->
  acc + user.age
end)
|> div(length(users))
|> IO.inspect()

# 20

Enum.reduce/3 を使い、users の age の合計を計算し、length(users)で users の個数を求め、その後に div/2 を使って average を計算します。
````

#### JavaScript （React）

入力

```text
カウンターをボタンクリックで増減させる React のコードを生成してください
```

3b の出力

ちゃんとしたコードになっているようです

````markdown
React では、setState() を使用してカウンター値を更新することができます。setState() を呼び出すと、カウンター値が更新され、UI が更新されます。

以下は、カウンター値を 1 増やすボタンと 1 減らすボタンを持つ React コンポーネントの例です。

```jsx
import React, { useState } from'react';

function Counter() {
  const [count, setCount] = useState(0);

  const incrementCount = () => {
    setCount(count + 1);
  };

  const decrementCount = () => {
    setCount(count - 1);
  };

  return (
    <div>
      <p>カウンター: {count}</p>
      <button onClick={incrementCount}>+1</button>
      <button onClick={decrementCount}>-1</button>
    </div>
  );
}

export default Counter;
```

setState() を呼び出すと、カウンター値が更新され、UI が更新されます。
````

8b の出力

謎の挨拶から始まりましたが、いい感じです

````markdown
こんにちは！

 CountyButton.js

```
import React, { useState } from'react';

const CountyButton = () => {
  const [count, setCount] = useState(0);

  const handleIncrement = () => {
    setCount(count + 1);
  };

  const handleDecrement = () => {
    setCount(count - 1);
  };

  return (
    <div>
      <button onClick={handleIncrement}>+</button>
      <p>Count: {count}</p>
      <button onClick={handleDecrement}>-</button>
    </div>
  );
};

export default CountyButton;
```

CountyButton.jsは、stateを管理するReactの functional componentです。 stateは、countとsetCountです。 countは、ボタンを押すたびに増減する数です。 setCountは、countの状態を更新するメ소ーです。

handleIncrementとhandleDecrementは、それぞれ\+と-を押すと呼び出されます。 これらのメ소ーは、countの状態を更新します。\+を押すとcountが1増加し、-を押すとcountが1減少します。

最後に、renderメ소ーは\+と-の間にcountを表示し、それぞれのボタンをクリックするとcountが増減します。
````

#### JavaScript （Vue）

入力

```text
カウンターをボタンクリックで増減させる Vue のコードを生成してください
```

3b の出力

残念ながら増やす方しか実装してくれませんでした

````markdown
```html
<template>
  <div>
    <button @click="count += 1">Count: {{ count }}</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      count: 0
    };
  }
};
</script>
```
````

8b の出力

減らす方も実装できています

````markdown
```html
<template>
  <div>
    <button @click="count++">Count Up</button>
    <button @click="count--">Count Down</button>
    <p>Count: {{ count }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      count: 0
    };
  }
};
</script>
```

count Esta prop を使用して、count Up と count Down の両方のボタンをクリックするたびに count を increment するか decrement します。count Esta prop を使用して、count Up と count Down の両方のボタンをクリックするたびに count を increment するか decrement します。
````

#### bash (AWS 操作)

入力

```text
AWS から S3 の指定バケット、指定プレフィックス配下にあるファイルの合計サイズを取得する shell script を生成してください
```

3b の出力

なぜ一回バケットのリージョンを取得したのか

ちなみにこれだと合計サイズは取得できませんでした

````markdown
AWS CLI を使用して、指定したバケットとプレフィックスのファイルの合計サイズを得ることができます。

以下は、AWS CLI を使用して、指定したバケットとプレフィックスのファイルの合計サイズを得る shell script の例です。

```bash
#!/bin/bash

# AWS CLI の構成
export AWS_ACCESS_KEY_ID=<your-access-key-id>
export AWS_SECRET_ACCESS_KEY=<your-secret-access-key>
export AWS_DEFAULT_REGION=<your-default-region>

# 合計サイズを得る
aws s3api get-bucket-location --bucket <your-bucket-name>
total_size=$(aws s3 ls s3://<your-bucket-name>/<your-prefix> --summarize | awk '{print $5}')
echo "Total size: $total_size"
```

上記の script を使用すると、指定したバケットとプレフィックスのファイルの合計サイズが表示されます。

注: AWS CLI を使用する前に、ACCESS KEY ID、SECRET ACCESS KEY、デフォルトリージョンを構成する必要があります。
````

8b の出力

スクリプトではなく、コマンド一発で取得する方法を返してくれました

````markdown
S3 の指定バケット、指定プレfixes配下にあるファイルの合計サイズを取得する shell script を生成するには、aws cli を使用します。

aws s3 ls --summarize --recursive s3://<your-bucket-name>/<your-prefix>

ここで、<your-bucket-name>は、S3 バケットの名前を、<your-prefix>は、S3 バケットのプレfixesを表します。

–summarizeオプションは、結果を summaried (集計) します。
–recursiveオプションは、S3 バケットのサブフォルダー内のファイルも検索します。

この҆ コマンドは、S3 バケットの指定プレfixes配下にあるファイルの合計サイズを表示します。
````

## まとめ

3b だと無償プランの T4 GPU でも実行できるのが良いですね

ただし、 `max_new_tokens` が大きいと同じ文言を繰り返しがちになったり、日本語が少し不自然だったり、コードの精度がやはり低かったりします

8b だと有償プランの L4 GPU が必要になるものの、結構良い精度でコードを生成してくれているように思います

また、どちらも日本語にちゃんと対応しているのが良いです