OmniAICreator/Qiita-1.07M huggingface dataset:
GitHubActionsを使い、pull requestの内容に従ってDifyでいろいろする

# 作業手順

### 例：CIにDifyを組み込む

1. Difyでワークフローを作成する
    a. 入力の受け付け方
2. DifyにてAPI-Keyを発行する
3. CI中にDifyのAPIに対してPOSTを送信する
4. responseを解析する

---

# 作業手順の詳細

1. Difyでワークフローを作成する
    
    内容は問いませんが、ひとまずDifyにて任意のワークフローを完成させます。
    
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/505609/1079d035-f2f5-b364-7fa9-e78d34453fa3.png)

    
    a. 入力の受け付け方
        
    [開始]ステップにて通常通り、入力フィールドを追加すれば後述するPOSTにて任意の値をDifyに送信できます。
        
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/505609/b7304f71-8d0b-fb24-28a9-a41bdae2fd53.png)


1. DifyにてAPI-Keyを発行する
    
    画面左側のサイドバーにある[APIアクセス]を押下
    
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/505609/6cab58d6-17fe-6fb7-d980-01319d254e7f.png)

    
    APIアクセスの画面右上にある[🔑APlキー]を押下
    
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/505609/a389f87c-e602-fc07-ff13-f4354dd8d313.png)

    
    [+新しいシークレットキーを作成]を押下
    
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/505609/90cf2762-7776-dc31-f198-a146e4567c17.png)

    
    この値を後続の作業で使用するのでコピーしておく
    
2. 以下のように、curlを使用して、Difyへリクエストを送信し、レスポンスを受け取るようなステップを書く
    
    ```yaml
    - id: analyze-sql-files
      if: 'is_pull_request && changed_files > 0'
      do:
        run: |
          # 変更されたSQLファイルを取得
          FILES=$(gh pr view ${{ github.event.pull_request.number }} --json files --jq '.files[].path' | grep '\.sql$')
          if [ -n "$FILES" ]; then
            for file in $FILES; do
              CONTENT=$(cat $file)
              # Dify APIにリクエストを送信
              RESPONSE=$(curl -s "${DIFY_API_ENDPOINT}/workflows/run" \
                -H "Content-Type: application/json" \
                -H "Authorization: Bearer ${DIFY_API_KEY}" \
                -d '{
                  "inputs": {"mode":"1", "file_changed":"'"$CONTENT"'"},
                  "response_mode": "blocking",
                  "user": "{ユーザー名}"
                }')
    ```
    
    ※公式ドキュメントを参考に調整してください。

https://docs.dify.ai/guides/application-publishing/developing-with-apis?fallback=true

    
| 名 | 説明 |
| --- | --- |
| inputs | Difyの入力フィールドに従い、値を設定 |
| response_mode | blocking:実行完了後に一括で結果を返す。streaming:タイプライターのように逐次結果を返す |
| user | Difyを実行するDifyユーザー名 |

        
4 . responseを解析する


# よくある困ったこと

### Difyからの返答が文字化けしている

curlで日本語を出力しようとすると、unicodeが日本語に変換されずに出力されてしまう

```
\u64cd\u4f5c\u5185\u5bb9…
```

### 解決法

以下のように `jq` をパイプ(`|`)で繋いであげると、日本語にしてくれる

```
user_dai ~ % curl -X POST 'https://dify.xxx/v1/workflows/run' \
(中略)
  "user": "dai"
}' | jq                                                   
```

```
{
(中略)
  "outputs": {
   "text": "json{\"is_warning\": \"false\", \"reason\": \"このクエリはやばい\"}"
  },
(中略)
 }
}
```
AndroidのDBのマイグレーションとは

# マイグレーションとは
- （DBに限らず、「移行」を意味する）
- DBのマイグレーションとは、「DBの変更履歴を残していくこと」
（Gitで変更を管理するみたいなイメージ）
- Anadroidでは（←Anadroid以外を知らないだけですが・・・）マイグレーションをしなくても、一応DBの更新は可能
ただ、マイグレーションをした方が、バグとかが起きた場合に、切り分けができるので嬉しい
また、DB構造がサーバ側に一部依存していた場合、サーバ側でのバージョンを落とした際に、合わせてDBバージョンを簡単に切り替えることもできる




- マイグレーションを使用した際は、現在の最新のDBとマイグレーションで作成した内容を比較して一つでも異なるテストで警告が出るようになる
→間違ったままアプリを動かすと、無限にループしたりするらしいので、その対策として警告が出るようになる

# マイグレーションを行う時の実装
- **```Room.databaseBuilder```をしているところで、```addMigrations()```メソッドを使うとマイグレーションができる**
<br>



- ↑逆に言うと、```Room.databaseBuilder```をしているところで、```addMigrations()```メソッドを使っていなければ、マイグレーションをしていない
つまり、``` migrate```関数を使って何のテーブルを弄るのかを自分で定義しない

- アプリのDBバージョンが上がっている場合、アプリ起動時にDBなどのデータの初期化が行われる



# 参考サイト
https://qiita.com/naoi/items/08557d87c919617b8d17#addmigrations%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%81%AE%E8%BF%BD%E8%A8%98

# 関連記事
https://qiita.com/pe-pe-desu/items/d98085c55ebc6539f48d
【JavaScript】配列の応用まとめ②

JS基礎学習、今回は`配列`の応用編②！

## ▼配列の応用
### 1. 少なくとも一つの条件を満たす要素があるか（`some()`メソッド）
- ` some() ` メソッド
条件を満たす要素が1つでもあればtrueを返す

``` js
// 例: 配列に偶数が1つでもあるかを調べる
const numbers = [1, 3, 5, 6, 7];

// 偶数があるか確認
const hasEven = numbers.some(num => num % 2 === 0);
console.log(hasEven); // true (6が偶数)
 ```

Todoリストを作る時、どんな風に応用できるかGPTに聞いてみました💡

＜応用例＞
- Todoリストで未完了のタスクが1つでもあるかを調べることができる
    - やるべきことが残っているなら通知を出す　など
```js
const tasks = [
  { name: "宿題をする", completed: false },
  { name: "部屋を掃除する", completed: true },
  { name: "友達に電話する", completed: false }
];

// 未完了のタスクが1つでもあるか
const hasIncompleteTask = tasks.some(task => !task.completed);
console.log(hasIncompleteTask); // true (未完了のタスクがあるから
```

### 2. すべての要素が条件を満たすか（`every()`メソッド）

- ` every() ` メソッド
配列の中のすべての要素が条件を満たしている場合にtrueを返す

``` js
// 例: 配列のすべての要素が10以下かを調べる
const numbers = [1, 3, 5, 6, 7];

// 全てが10以下か確認
const allLessThanTen = numbers.every(num => num <= 10);
console.log(allLessThanTen); // true (すべて10以下)
 ```

＜応用例＞
- すべてのタスクが完了しているかを調べることができる

```js
const tasks = [
  { name: "宿題をする", completed: true },
  { name: "部屋を掃除する", completed: true },
  { name: "友達に電話する", completed: true }
];

// 全てのタスクが完了しているか
const allTasksCompleted = tasks.every(task => task.completed);
console.log(allTasksCompleted); // true (すべて完了している)
```
C++でよく使う関数やコンテナ

## sort関数

#### 概要
sort関数は、配列やベクター(std::vector)などの要素を昇順または指定した順序でソートするために使用します。<algorithm>ヘッダーファイルに含まれています。

#### 使い方
昇順
```cpp
std::sort(開始イテレータ, 終了イテレータ);
```
降順
```cpp
std::sort(開始イテレータ, 終了イテレータ, greater<int>());
```

#### 具体例
```cpp
#include <iostream>
#include <algorithm>
#include <vector>

int main() {
    // ベクターの定義
    std::vector<int> numbers = {5, 2, 8, 1, 3};

    // ソート前の出力
    std::cout << "ソート前: ";
    for (int n : numbers) {
        std::cout << n << " ";
    }
    std::cout << std::endl;

    // sort関数でソート
    std::sort(numbers.begin(), numbers.end());

    // ソート後の出力
    std::cout << "ソート後: ";
    for (int n : numbers) {
        std::cout << n << " ";
    }
    std::cout << std::endl;

    return 0;
}
```

#### 出力結果
```bash
ソート前: 5 2 8 1 3
ソート後: 1 2 3 5 8
```

## set関数

#### 概要
setは、重複しない要素を自動的にソートされた順序で保持するコンテナです。<set>ヘッダーファイルに含まれています。

#### 使い方
```cpp
std::set<データ型> セット名;
```

#### 具体例
```cpp
#include <iostream>
#include <set>

int main() {
    // setの定義
    std::set<int> mySet;

    // 要素の追加
    mySet.insert(3);
    mySet.insert(1);
    mySet.insert(4);
    mySet.insert(1); // 重複する要素は追加されない

    // setの内容を出力
    std::cout << "setの内容: ";
    for (int n : mySet) {
        std::cout << n << " ";
    }
    std::cout << std::endl;

    return 0;
}
```

#### 出力結果
```bash
setの内容: 1 3 4 
```

## reverse関数

#### 概要
reverse関数は、指定した範囲の要素を反転（逆順）させます。<algorithm>ヘッダーファイルに含まれています。

#### 使い方
```cpp
std::reverse(開始イテレータ, 終了イテレータ);
```

#### 具体例
```cpp
#include <iostream>
#include <algorithm>
#include <vector>

int main() {
    // ベクターの定義
    std::vector<int> numbers = {1, 2, 3, 4, 5};

    // 反転前の出力
    std::cout << "反転前: ";
    for (int n : numbers) {
        std::cout << n << " ";
    }
    std::cout << std::endl;

    // reverse関数で反転
    std::reverse(numbers.begin(), numbers.end());

    // 反転後の出力
    std::cout << "反転後: ";
    for (int n : numbers) {
        std::cout << n << " ";
    }
    std::cout << std::endl;

    return 0;
}
```

#### 出力結果
```bash
反転前: 1 2 3 4 5
反転後: 5 4 3 2 1
```

## substr関数

#### 概要
substr関数は、文字列から部分文字列を取得するための関数です。std::stringクラスのメンバ関数で、<string>ヘッダーファイルに含まれています。

#### 使い方
```cpp
std::string 部分文字列　= 元の文字列.substr(開始位置, 長さ);
```

#### 具体例
```cpp
#include <iostream>
#include <string>

int main() {
    // 元の文字列
    std::string str = "Hello, World!";

    // substr関数で部分文字列を取得
    std::string subStr = str.substr(7, 5); // 開始位置7から5文字を取得

    // 結果の出力
    std::cout << "部分文字列: " << subStr << std::endl;

    return 0;
}
```

#### 出力結果
```bash
部分文字列: World
```
コードの保存には⚪︎⚪︎を使え！超簡単コード集の作り方！

## 背景
**「このコード、使いまわせそうだから保存しておきたいな」** と思ったことありませんか？

Excelに保存することもできるけど、コードの見た目が悪い、、、
![スクリーンショット 2024-11-20 14.31.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3488627/df80fced-172e-a049-e7b8-f93b4a5aceb0.png)

以下の方法を使えばこの問題を解決できるので、ぜひお試し下さい。

## 結論
### Notionのテーブル機能を使う

以下のGIFの通り、任意のデータの「サイドピークを開く」をクリックすると保存したコードが閲覧できる。

![プレゼンテーション1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3488627/5bda8a13-5754-98fc-f5ba-b0d404f20b26.gif)

### メリット
* 各言語に対応した構文ハイライトを適用してくれる
* 任意のカラムを追加してデータを整理できる
* 各カラム情報によるフィルターや並び替え機能が使えるので、過去のコードも見つけやすい
 

### 手順
**1：** Notionの新規ページで **/table** と入力して表示される、選択肢から「テーブル」を選択

![スクリーンショット 2024-11-25 0.36.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3488627/00f1e06a-38e3-4502-96eb-50504ef8e9ca.png)

**2：** 作成されたテーブルのメニューから「データベースに変換」を選択

![スクリーンショット 2024-11-25 0.38.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3488627/80abc5c2-fa54-b018-4ac2-38a5d84dc3a0.png)

**3：** 各カラム情報を編集する
* タイトルカラムはデフォルトで作成されています
* 各カラムの「カラム名」と「データ型」を編集できます（以下ではテキスト型の言語カラムと日付型の日付カラムを追加）
* この時点ではコードを入力するカラムを作成する必要はありません

![スクリーンショット 2024-11-25 0.39.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3488627/5dbd285e-b7e7-bccc-4d77-9cd5a0ad341d.png)

**4：** タイトル・言語・日付を入力後、「サイドピークで開く」をクリック

![スクリーンショット 2024-11-25 0.41.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3488627/b063adf6-07b0-ec36-de36-abc156cc5d22.png)

**5：** 右半分に表示されるサイドピークと呼ばれるエリアに **/code** と入力し「コード」を選択する

![スクリーンショット 2024-11-25 0.41.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3488627/eda05b50-2df9-0e49-0612-1f6eeb8daef3.png)

**6：** 言語を選択し、コードを入力する

![スクリーンショット 2024-11-25 1.20.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3488627/a94f92b0-128d-b00a-7353-dcbe30e7c039.png)

## 補足
言語カラムの種類を「マルチセレクト」に設定すると便利

![スクリーンショット 2024-11-25 1.36.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3488627/89246c09-dff6-0e5f-e76f-410041596f94.png)

**手順**
カラム名をクリック → プロパティを編集 → 種類 → マルチセレクト → 「オプション」に言語を追加

＊上の画像のようにプロジェクト名カラムを追加し、プロジェクトごとの絞り込みをできるようにしておくのもオススメです

## 終わりに
他のコード保存の方法やコード保存が可能なWebアプリなどご存知でしたらぜひ教えて下さい！
お読みいただきありがとうございました！
[git] ローカル環境で作成した一時ファイルをリポジトリに入れない

開発中に作成したテストデータなど、リポジトリには含めたくないファイルがある場合。

もちろん.gitignoreが一般的ですが、OSSなどでは**自身で勝手に.gitignoreを書き換えられない**という状況もあるでしょう。

# pre-commitを使う
`pre-commit`で、commitの直前に指定したファイル/フォルダをステージングエリアから除外するという方法を使います。

## .git/hooks/pre-commit を作成
```.git/hooks/pre-commit
#!/bin/bash

git reset HEAD testfile
git reset HEAD tmpfolder/
```

実行権限も与えておきます。
```
$ chmod +x .git/hooks/pre-commit
```

## あとはいつもどおり
```
$ git add .
```
この時点では不要なファイルもaddされますが...
```
$ git commit -m "commit message"
```
とした時に、先ほどpre-commitに記述したファイルおよびフォルダ内のファイルがUntrackedになっています。
【SpringBoot】Validationでプレースホルダが認識されなくてハマった話

# はじめに
表題の件についてまとめます。APIの仕様をちゃんと確認しよう、というお話です。

# SpringBootのValidationで「{0}」がそのまま出力される
バリデーションエラー時にプレースホルダを利用してエラーメッセージを出力しようとしました。
「{0}」を使えば、フィールド名が出力されることを期待して、`jakarta.validation.constraints.NotNull.message=「{0}」は必須入力です`のようにValidationMessages.properiesへ記述したところ、表示された文字は`「{0}」は必須入力です`となりました。
これでハマること2時間。。。

# 原因
エラーメッセージを出力しようとしていたコードは次のとおりです。
```java
    【略】
    @PostMapping
    public ResponseEntity<?> postSomething(@Validated @RequestBody SomethingForm somethingForm, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            StringBuilder errorMessage = new StringBuilder();
            for (ObjectError error : bindingResult.getAllErrors()) {
                errorMessage.append(error.getDefaultMessage()).append("; ");
            }
            return ResponseEntity.badRequest().body(errorMessage.toString());
        }
        
        stocks.add(stocksAddForm);

        return ResponseEntity.ok("Stock info added successfully!");
    }
```
ValidationMessages.properiesの記述の仕方や@Configを追加したりと色々試してもうまくいかず、途方に暮れていたところ、ついにたどり着いた部分がこれです。
`error.getDefaultMessage()`
そもそもエラーメッセージの出力がどのようにして行われているのか、はたと気づいて調べた結果、**通常、`BindingResult.getAllErrors()` で取得したエラーメッセージは、プレースホルダーが解決されていない状態で返される**ことが分かりました。
うまくいかないのも当たり前です。。。

# 解決
コードを次のように修正しました。
```diff_java
    【略】
+    private final MessageSource messageSource;
+    
+    public StocksController(MessageSource messageSource) {
+        this.messageSource = messageSource;
+    }

    @PostMapping
-    public ResponseEntity<?> postStocks(@Validated @RequestBody StocksAddForm stocksAddForm, BindingResult bindingResult) {
+    public ResponseEntity<?> postStocks(@Validated @RequestBody StocksAddForm stocksAddForm, BindingResult bindingResult, Locale locale) {
        if (bindingResult.hasErrors()) {
+            StringBuilder errorMessage = new StringBuilder();
            for (ObjectError error : bindingResult.getAllErrors()) {
-               errorMessage.append(error.getDefaultMessage()).append("; ");
+               String message = messageSource.getMessage(error, locale);
+               errorMessage.append(message).append("; ");
            }
            return ResponseEntity.badRequest().body(errorMessage.toString());
        }
        
        stocks.add(stocksAddForm);

        return ResponseEntity.ok("Stock info added successfully!");
    }
```
`MessageSourceのgetMessage()`メソッドを使うことで、プレースホルダが正しく解釈されて出力されました。

# 結論
適当に引っ張ってきたコードをそのまま使わず、その意味をきちんと丁寧に理解しなければならないと痛感しました。。。
よく分からないところに原因は潜んでいるのかも。
Using IOS-Automation with Netmiko via OOP approach1

# NETMIKOを使ったIOS自動化のオブジェクト指向プログラムをつくってみる。

![Screenshot_2024-11-25_00-55-12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3943132/7cf690c5-a0b5-d8d9-8c6c-0f7d994e1f9f.png)

構成は非常に簡単でIOSルーターに管理クラウドを直付けしているだけ。
そしてクラウドのPythonプログラムからR1のアクセスリストを確認する。

R1の設定は以下
```cisco_ios:R1
hostname R1
ip domain-name abc.com
crypto key generate rsa modulus 1024
ip ssh version 2
username admin privilege 15 password cisco

ip access-list standard ACL_MGMT
permit 10.255.1.51


line vty 0 4
login local
transport input all

interface gi 0/0
ip address 10.255.1.101 255.255.255.0
description Connect to MGMT
no shut 


```
Pythonプログラムはこちら
```python:netmiko_demo.py
#router接続ライブラリのNetmiko
from netmiko import ConnectHandler
#フィルタリングと構造化データの表示ライブラリ
from ntc_templates.parse import parse_output

#デバイスに接続
ssh_connect = ConnectHandler(
    host="10.255.1.101",
    username="admin",
    password="cisco",
    device_type="cisco_ios",
    port = "22"
)

raw_output = ssh_connect.send_command("show ip access-list ACL_MGMT")
print(raw_output)

#フィルタリングして構造化する
structured_output = parse_output(
    platform = "cisco_ios",
    command = "show ip access-list ACL_MGMT",
    data = raw_output
)

print(structured_output)
```

実行したところ、アクセスリストとアクセスリストの構造データが取得された。
```shell-session
#python netmiko_demo.py 
Standard IP access list ACL_MGMT
    10 permit 10.255.1.51
[{'acl_type': 'Standard', 'acl_name': 'ACL_MGMT', 'line_num': '', 'action': '', 'protocol': '', 'src_host': '', 'src_any': '', 'src_network': '', 'src_wildcard': '', 'src_network_object_group_name': '', 'src_port_match': '', 'src_port': '', 'src_port_range_start': '', 'src_port_range_end': '', 'dst_host': '', 'dst_any': '', 'dst_network': '', 'dst_wildcard': '', 'dst_network_object_group_name': '', 'dst_port_match': '', 'dst_port': '', 'dst_port_range_start': '', 'dst_port_range_end': '', 'service_object_group_name': '', 'flags_match': '', 'tcp_flag': '', 'log': '', 'log_tag': '', 'icmp_type': '', 'time': '', 'state': '', 'matches': ''}, {'acl_type': 'Standard', 'acl_name': 'ACL_MGMT', 'line_num': '10', 'action': 'permit', 'protocol': '', 'src_host': '10.255.1.51', 'src_any': '', 'src_network': '', 'src_wildcard': '', 'src_network_object_group_name': '', 'src_port_match': '', 'src_port': '', 'src_port_range_start': '', 'src_port_range_end': '', 'dst_host': '', 'dst_any': '', 'dst_network': '', 'dst_wildcard': '', 'dst_network_object_group_name': '', 'dst_port_match': '', 'dst_port': '', 'dst_port_range_start': '', 'dst_port_range_end': '', 'service_object_group_name': '', 'flags_match': '', 'tcp_flag': '', 'log': '', 'log_tag': '', 'icmp_type': '', 'time': '', 'state': '', 'matches': ''}]
```
Pythonの型ヒント(型アノテーション)を活用しよう

# はじめに
Pythonは動的型付けの言語ですが、Pythonの型ヒントはPython 3.5以降バージョンを重ねるごとに進化し続けています。
型アノテーション(型ヒント)と上手く付き合うことで、実装コードの書きやすさや可読性が向上し、個人開発・チーム開発どちらでも型ヒントを導入することでたくさんの恩恵を受けることができるでしょう。
本記事では、Pythonの型定義について初心者向けに解説します。

## 型ヒント導入のメリット

1. **可読性の向上**
    型を明示することで、コードの意図を他人や未来の自分に伝えやすくなります
2. **バグの早期発見**
    mypyなどの静的解析ツールと併用することで、潜在的な型エラーを実行前に検出することができます
3. **補完の強化**
    IDE(例: PyCharm, VSCode)が型情報を元に補完や警告を提供してくれます

以下VSCodeですが、型ヒントを書くことでIDEの補完が機能してくれます。
補完は開発者体験に大きく関わるので型定義は大事です。
![TypeHint.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3314085/c53e49c8-8fe4-2d35-3d28-6ce05b6f379a.png)

:::note warn
型定義を導入する際の注意点
Pythonの型は**静的解析にのみ**使用され、Pythonの実行時には無視されます。
そのため、厳格な型チェック・型バリデーションチェックをするには[mypy](https://mypy.readthedocs.io/en/stable/getting_started.html)や[Pydantic](https://docs.pydantic.dev/latest/)などの併用をお勧めします。
```python
sample: int = "hello"
# 実行時はエラーにならない
print(sample)  # 実行結果: hello
```
:::


### 変数の型定義
```python
name: str = "山田"
age: int = 30
height: float = 1.75
is_student: bool = False
phone_number: str  # 代入せずに型ヒントだけ定義することもできます
```

### 関数の型定義
関数の引数や戻り値にも型を指定できます。
`-> <型>`が戻り値の型ヒントです。

```python
def greet(name: str) -> str:
    return f"Hello, {name}!"
```

型ヒントを付けることで、この関数は`name`に文字列を受け取り、文字列を返すことが明示されます。

### コレクション型
リスト、辞書、タプルなどのコレクション型にはそれぞれ`List`, `Dict`, `Tuple`を使用します。

```python
from typing import List, Dict, Tuple

names: List[str] = ["山田", "佐藤"]
data: Dict[str, int] = {"山田": 25, "佐藤": 30}
pair: Tuple[str, int] = ("山田", 25)
```

Python 3.9で、組み込み型(list,dict,など)に対して直接型引数を指定できる機能が追加されました。これにより以下のように簡潔に記述が可能です。
```python
names: list[str] = ["山田", "佐藤"]
data: dict[str, int] = {"山田": 25, "佐藤": 30}
pair: tuple[str, int] = ("山田", 25)
```

### Union型
複数の型を受け入れる場合は`Union`を使用します。

```python
from typing import Union

data: Union[int, str]
```

Python3.10以降は`Union`を`|`に簡略化可能です。
```python
from typing import Union

data: int | str
```

### Optional型
値が`None`になる可能性がある場合は`Optional`を使用します。

```python
from typing import Optional

data: Optional[str]
```

Python3.10以降の`|`を使用して以下のように表現することもできます。

```python
from typing import Optional

data: str | None
```

## まとめ

型アノテーションはコードの品質向上やバグ防止に非常に有効です。
本記事を参考に、ぜひ型アノテーションを日々の開発に取り入れてみてください！
【JetpackCompose】プログレスバーを旧デザインに戻す方法

material3のv1.3.0からプログレスバーのデザインが新しくなりました。
新デザインでは、進捗を表すバーと全体のバーが分割され、終点部分に点が加わっています。

#### v1.2.1
<img width="300" height="150" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3938733/5ecad5af-4777-0652-5193-27ebb3c00182.png">

#### v1.3.0
<img width="300" height="150" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3938733/41dc6a15-8f08-c0bd-2764-90ba1dd16be2.png">


# コード

追加された`gapSize`と`drawStopIndicator`を使うと、前のデザインに戻すことができます。

```kotlin
LinearProgressIndicator(
    progress = { 0.5f },
    strokeCap = StrokeCap.Square,   // 角の丸みをなくす
    gapSize = (-15).dp,             // 隙間をなくす
    drawStopIndicator = {}          // 終点の点の描画をなくす
)
```

#### プレビュー
<img width="300" height="150" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3938733/1cf43f06-4ccc-056a-1e04-96937e0b5913.png">



# 参考
- https://stackoverflow.com/questions/79132238/linearprogressindicator-appears-split-in-two-in-androidx-compose-material3-versi
- https://m3.material.io/components/progress-indicators/overview
- https://developer.android.com/jetpack/androidx/releases/compose-material3?hl=ja#1.3.0
Vector Optics Maverick Gen1のツマミ緩み修理方法と分解に関する注意点

## 製品
Vector Optics製レッドドットサイト
**Maverick 1x22 Gen1** : [メーカーページリンク](https://www.vectoroptics.jp/scrd-12/)

## 症状
- 光量調整、電源ON/OFF用のツマミのゆるみ
- それに伴い、点灯中のドットの点滅、光量の不安定な変化
- ツマミを側面から押し込むとドットが消灯

## 実際に分解した時の修理レポート
### 分解
- 通常分解で側面パネルを外しボタン電池を取り外す。
- ボタン電池が収まっていた部分の周りを囲むようにネジ込まれた銀色のリング状パーツを、細い棒か何かを使って引っかけて回しながら外す。
- 銀色のリングパーツによって抑えられていた**緑色の基盤パーツ（構造後述）** が外せるようになる為、ピンセットか逆さまにして外す。
- 4本のプラスネジで留められた銀色のプレートパーツがある為、精密ドライバーで外す。ネジが外れると、ツマミの本体部分と奥の灰色のプレート等の全ての固定が外れる。

ここまで分解すると凡そユーザーが分解できる限界。この時に**灰色のプレートパーツはチューブ内の発光パーツにケーブルで接続されている為、無理に引っ張って断線させないように注意する**。

### 原因究明
- ツマミが緩んでいたのは、分解時に外した4本のプラスネジが緩んでいたからだった。
- この4本のネジはツマミの外装部分と灰色の接触プレートを同時に固定している為、**使い続けてツマミが柔らかくなっていくのと同時に動作上重要な機能部分の固定も緩んでいく**事になる。
- よって、この4本のネジを丁度いい具合にキツ過ぎない程度に締めて、**ツマミが固すぎないが多少固めになるように締める**事で今回の症状に対する修理は完了する。
- あとは分解手順の逆に組み立ててボタン電池を入れて問題なく点灯すれば解決。
**（緑色の基盤パーツの電源OFF部分の角度をダイヤル外装部にレーザー刻印された０の位置と合わせないと光量とダイヤル番号がズレると思います。ズレないように指で軽く押さえながら意識して固定しましょう。）**

## ケーススタディ
通常、上記の内容を行えば問題なく修理は完了するが、自分は組み立て後、
- なぜか電源のオフができない。
- ダイヤルを回しても光量の調整ができない。

という症状に発展した。

### 新たな症状の原因
組み立てる際、**灰色のプレートから2本生えている接触用の通電パーツのうち、外側に曲がった通電部分**を固定用の銀色のプレートパーツで押さえつけるように無理矢理固定してしまった為、**回路の短絡現象が起きて光量調節基盤を介さずに電池の電流が直接常時サイト内に流れる状況**になっていた。

#### ↓正常に固定されている状態。灰色のプレートから伸びた接触端子が両方どこにも触れていない。
![IMG_2677[1].JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3912260/0fa4ab2c-f97c-339e-d41b-87d889d4fd93.jpeg)

#### ↓接触端子ごと無理矢理銀色のプレートでネジ留めしてしまった状態。
![IMG_2678[1].JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3912260/90632f63-8143-b048-3195-65e06bd42c52.jpeg)

以上の事を解消した結果問題なく動作した。

## 余談１：レッドドットサイトの動作の仕組み
レッドドットサイトの仕組みというとレンズの部分に話が行きがちだが、今回はAimpoint T-1などのようなダイヤル型のサイトがどういう風に光量調整をしてるのかの大雑把な構造が学べる機会だったので、簡単にサイト内部でどのように点灯、消灯、光量調整が機能してるのかまとめてみたいと思う。

### 点灯/消灯
これは単純に回路が繋がっている状態か、繋がっていない状態かで発光パーツに電流が流れるかの制御をするのが最も単純な仕組みで、今回のMaverick Gen1も同様の方式だった。
ツマミの回転によって緑色の基盤パーツのうち線が繋がっていない部分が電池の電極に触れる事で電源OFFが成り立っていた。

### 光量調整
機構の中枢となるのはダイヤル内に入っていた緑色の円形半導体基板で、板上に実装されていた部品は以下の通りだった。
なお、今回分解したレッドドットサイトは11段階の光量調節が可能な機種だった。
- 11個の異なる抵抗値の角形抵抗チップ（恐らく…電子工作は初心者なので…）
- 1か所抵抗が接続されておらず意図的に断線させている箇所
- 合計12本の線が円形の基盤上に放射状に設置されており、中心はコイン型ボタン電池のマイナス極と接触している。そこから接触している11個の線のうちのどれかに電流が流れ、**その線の抵抗が強い程出力されるドットの光量が減る**。
- どの線が繋がるかをダイヤルを回す事で切り替えることができる。

#### ↓実際にダイヤル内に入っているシンプルな回路基板
![内部基盤](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3912260/c61be96f-af1a-13e0-9638-1665bed8e199.jpeg)
黒い電子パーツがハンダ付けされていない部分が電源OFFになる部分です。回路を正しく固定すると、回したダイヤルと一緒にこの基盤が回転し、接触する線と抵抗値が変化して出力される光量が変化します。

以上が今回基盤を見て判明した構造でした。意外とシンプル！！

## 余談２：安いレッドドットがなぜ安いのかという考察
### ①生産コストの安い地域で生産
これは当たり前だと思うので省略。
### ②構造の簡略化
今回Maverick Gen1を分解して強く思ったのが、内部と外界の境界になる箇所、動作や状況によってはスキマができる事が容易に想像できる部分に防水加工が施されていない。
- 特に今回の場合、ダイヤルの固定と回路の安定接続の要になる機構の固定が同じネジで行われている事から、強い衝撃に対しての長期的な動作安定性には確実に疑問が残る
- また、このネジもきつく締めればいい訳ではなく、敢えて少し緩くしておかないとダイヤルの回転が行えない程固くなってしまい使用感を大きく損なってしまう為、堅牢性を追求すると長期安定性が犠牲になり、使用感を優先しすぎると機能しなくなるという点で設計段階の問題が生じている。
### ③内部絶縁の簡略化
②の構造の簡略化にも繋がる内容だが、今回の組み立て失敗による短絡は私の組み立てミスが原因ではあるものの、改めて内部設計を見ると
- 強い衝撃や水分の侵入によって短絡する可能性が高い事が容易に分かる導電体パーツ同士の境界
- 回路上の露出した通電部分

に絶縁パーツによる保護やパーティショニングが施されていない箇所が多い事に気付いた。

## 結論
安い光学機器は「安かろう値段相応だろう」で買う。
ちゃんとした物を使いたいならお金を貯めて信頼できる実物かノーベルアームズ製を買おう。
LeetCodeを1日1題解いてみる (day26)

26日目に取り組んだ問題はこちら！

# 119. Pascal's Triangle II

https://leetcode.com/problems/pascals-triangle-ii/description/

## 問題文
Given an integer rowIndex, return the rowIndexth (0-indexed) row of the Pascal's triangle.

In Pascal's triangle, each number is the sum of the two numbers directly above it as shown:


## 僕の回答
```py
class Solution:
    def getRow(self, rowIndex: int) -> List[int]:
        result = []
        for i in range(rowIndex+1):
            num = self.comb(rowIndex, i)
            result.append(num)
        return result

    def comb(self, n: int, p: int) -> int:
        result = factorial(n) / (factorial(p) * factorial(n-p))
        return int(result)

    def factorial(n) -> int:
        num = 1
        for i in range(1, n+1):
            num *= i
        return num
```

## より効率と思う僕の回答(失敗作)

```py
class Solution:
    def getRow(self, rowIndex: int) -> List[int]:
        result = []
        for i in range(int((rowIndex)/2)):
            num = self.comb(rowIndex, i)
            result.append(num)
        for i in range((rowIndex+1)//2):
            num = result[-(i+1)]
            result.append(num)
        return result

    def comb(self, n: int, p: int) -> int:
        result = 1
        i = p
        max = n
        while i > 0:
            result *= max / i
            n -= 1
            i -= 1
        return result
```

## より効率の良い回答例
```py
class Solution:
    def getRow(self, rowIndex: int) -> list[int]:
        row = [1] * (rowIndex + 1)  # 初期化: 全ての要素を 1 に設定
        
        # パスカルの三角形の性質を利用して更新
        for i in range(1, rowIndex):
            for j in range(i, 0, -1):  # 右端から左端へ更新
                row[j] += row[j - 1]
        
        return row
```

## コメント
コンビネーションCの計算を利用して実装した。しかし、`rowIndex`が大きくなると、分母、分子の階乗計算で重複部分があり、パスカルの三角形は左右対称であるので、そこを効率的にしようとしたが、実装までは至らなかった。他の人の回答例を見てもコンビネーションでの実装はなかったので、完成はできなかったのは悔しい。

## 次の問題はこちら!
https://qiita.com/h_y_s_d/items/c7dd35173ffbcd992069
[AWS #31] 請求とコスト管理はどうやって確認しましょうか？①

<!--
 <font color="c00000"></font> 赤
-->
## はじめに
皆さまお疲れ様です。
今回はAWSの <font color="c00000"> **「請求とコスト管理」** </font>について見ていきたいと思います。

AWSの料金モデルは、**使った分だけに請求が発生する** <font color="c00000"> **「従量課金」**</font>です。固定費ではなく、「変動費」としてITリソースを利用できることで<font color="c00000"> **無駄の無いコスト最適化** </font>ができます。

## コストの時系列にはExplorer
請求とコスト管理のホーム画面で、「その月の合計請求額」、「着地予測」を確認することができます。**いつ何にどれくらいのコストが発生した**かを確認するには<font color="c00000"> **「AWS Cost Explorer」**</font>を使用します。

### AWS Cost Explorer
こちらはAWS Cost Explorerのイメージ図です。
横軸は「時間 日 月」の時系列です。
縦軸は「金額」です。
![AWS31_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806875/8ce5b5da-3360-6d33-bfb8-5e9c2dd992bb.png)
**グラフ化**になっていることで <font color="c00000"> **「何にコストがかかっている」**</font>かを確認できます。デフォルトの状態では全体の総額が対象となっていますが、フィルターで特定の情報、サービスで限定したりすることもできます。

![AWS31_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806875/d430c13d-04d5-df55-4a0e-108f61f17bda.png)

## おわりに
今回のちょっとしたまとめになります。

:::note info
**AWS Cost Explorer** は、コストと使用量を表示および分析できるツールです
:::

:::note info
自身の**AWS使用状況**に応じた詳細なコストを簡単に確認できます
:::

最後までお読みいただきありがとうございました！
>>日頃より活用している参考書です。
>> **「参考書」**
[AWS認定クラウドプラクティショナー改定第3版（AWS Cost Explorerは376ページ）](https://x.gd/19i6N)
>
> **「AWS公式サイト」**
[AWS Cost Explorer](https://aws.amazon.com/jp/aws-cost-management/aws-cost-explorer/)
>
低リソース言語に革新をもたらす「UnifiedCrawl」の全貌：効率的データ収集と軽量モデル適応の新手法

# UnifiedCrawl: Aggregated Common Crawl for Affordable Adaptation of LLMs on Low-Resource Languages

低リソース言語における自然言語処理（NLP）の課題に革新的な解決策を提示する最新の研究成果、「UnifiedCrawl: Aggregated Common Crawl for Affordable Adaptation of LLMs on Low-Resource Languages」を紹介します。本記事では、論文内容を忠実かつ詳細に解説し、この研究がどのように低リソース言語の課題に取り組んでいるのかを明らかにします。

---

## 目次
1. 背景と目的
2. UnifiedCrawlの方法論
    - データ収集の詳細
    - ノイズ除去と重複削除
    - QLoRAによる微調整
3. 実験結果とその分析
    - 言語ごとのパフォーマンス
    - 他のデータセットとの比較
    - Few-shot promptingの性能向上
4. 議論
    - UnifiedCrawlの貢献と課題
5. 今後の展望
6. 結論

---

## 1. 背景と目的

### 低リソース言語の現状
多くの低リソース言語（例：アムハラ語、ヨルバ語）は、オンライン上のテキストデータが非常に限られています。たとえば、アムハラ語のデータ量は高リソース言語（英語など）の1%未満です。これにより、低リソース言語向けの自然言語処理タスク（例：翻訳、音声認識）の性能は依然として低い状態にあります。

---

### 課題と現状
1. **データのスケーラビリティ**  
   高リソース言語と比較して、低リソース言語のデータ収集には技術的制約があります。既存のデータセット（OSCARやmC4）は不完全であり、ノイズが多いことが問題視されています。

2. **コストの高いモデル適応**  
   大規模言語モデル（LLMs）の適応には多大な計算コストが必要であり、特に低リソース言語では効率的な手法が求められています。

---

### 本研究の目的
本研究では、以下を主な目的としています：
- 低リソース言語に適応可能な高品質なデータセットの構築。
- 消費者向けハードウェアで実現可能な効率的なモデル適応手法の開発。
- 高リソース言語との差を縮め、言語間の公平性を向上させる。

---

## 2. UnifiedCrawlの方法論

### データ収集の詳細
UnifiedCrawlは、Common Crawl全体から低リソース言語のデータを効率的に抽出するため、以下のプロセスを採用しています：

1. **インデックスフィルタリング**  
   Common CrawlのCC Indexを用い、対象言語のドメインを特定。言語識別モデル（fastText）を活用し、目的言語に特化したURLを抽出します。

2. **WARCファイルのダウンロード**  
   HTTP Range Requestsを利用し、必要なデータのみを部分的に取得。これにより、ストレージ使用量を大幅に削減しました。

3. **テキスト抽出**  
   Trafilaturaライブラリを使用してHTMLソースからノイズを除去し、純粋なテキストデータを生成しました。

---

### ノイズ除去と重複削除
UnifiedCrawlでは、収集したデータの品質向上のため、以下の手法を採用しています：

1. **Exact substring deduplication**  
   重複フレーズを検出し、完全一致する文書を削除。

2. **短文の排除**  
   情報量の少ない文書（100文字未満）を除外。

3. **正規化処理**  
   文法的な整合性を保つための前処理を実施。

---

### QLoRAによる微調整
QLoRAは、軽量アダプターを用いてモデルを効率的に微調整する手法です。具体的には以下を行います：
- **低ランク行列の学習**: モデルの既存パラメータに変更を加えず、新しい行列を学習します。
- **量子化**: メモリ消費を削減しつつ、モデルの精度を維持します。

数式によるQLoRAの基本概念：
$$
W_{\text{new}} = W + \Delta W
$$
ここで、$\Delta W$は低ランク行列です。

---

## 3. 実験結果とその分析

### 言語ごとのパフォーマンス
以下は、主要な低リソース言語におけるパープレキシティ（PPL）の改善結果です：

| 言語       | 元のPPL   | 微調整後のPPL |
|------------|-----------|--------------|
| アムハラ語 | 14,974.7  | 105.6        |
| パシュトー語 | 12,300.3  | 97.2         |
| ヨルバ語   | 9,050.1   | 85.6         |

---

### 他のデータセットとの比較
UnifiedCrawlのデータセットは、以下の点で既存のデータセットを上回っています：

| データセット | データサイズ（MB） | ノイズレベル | 言語カバレッジ |
|-------------|------------------|------------|----------------|
| OSCAR       | 1,200           | 高い        | 中程度         |
| mC4         | 1,500           | 中程度      | 広範囲         |
| UnifiedCrawl| 4,000           | 低い        | 広範囲         |

---

## 4. 議論

### UnifiedCrawlの貢献
1. **効率的なデータ収集**: 高リソースと低リソース言語のギャップを埋めるデータセットを提供。  
2. **低コストなモデル適応**: 消費者向けハードウェアでも実行可能な微調整手法を導入。

---

### 限界と課題
- データセットの多様性と品質をさらに向上させる必要がある。  
- ノイズ除去アルゴリズムの精度向上が課題。

---

## 5. 今後の展望
- 他の言語や分野への応用可能性を探る。  
- モデルの適応手法をさらなる軽量化へと進める。

---

## 6. 結論
UnifiedCrawlは、低リソース言語向けにおける革新的なデータ収集とモデル適応の方法論を提供しています。この研究は、言語間の公平性を高めるだけでなく、低リソース言語におけるLLMsの性能向上を大きく前進させました。

この記事が、研究者の皆様に新たな知見を提供する一助となれば幸いです。
PencilKitをSwiftUIで使う

この記事は、2024年アドベンドカレンダー「SwiftUI」の3日目の記事となります。

今回は、ちょうど最近、個人アプリの開発でPencilKitを使ったアプリ開発をしていることもあり、備忘録としての意味合いも込めて、SwiftUIとPencilKitについての記事を書こうと思いました。

# PencilKit
PencilKitは2018年のWWDCで発表された、手書き入力をサポートするフレームワークです。
このフレームワークを使えば、iPadOS や iOS, macOS アプリに手書きのコンテンツを簡単に組み込むことができます。PencilKitは、Apple Pencilまたはユーザーの指からの入力を受け取り、iPadOS、iOS、またはmacOSで表示する画像に変換するiOSアプリケーション用の描画環境を提供します。この環境には、線を作成、消去、選択するためのツールが付属しています。
既存のビュー階層に統合するPKCanvasViewオブジェクトを使用して、iPadアプリでコンテンツをキャプチャします。PKCanvasView オブジェクトは、Apple Pencil や指によるタッチを低レイテンシーでキャプチャできます。canvasオブジェクトは、最終結果をPKDrawingオブジェクトとして送信します。また、iOSやmacOSアプリで表示するために、描画されたコンテンツを画像に変換することもできます。

主に要素としては以下のようなものが挙げられます。

- PKCanvasView : アプリケーションに描画領域を提供します。また、PKCanvasViewは、パンやズーム可能なUIScrollViewでもあります
- PKDrawing : PencilKit のデータモデルです。共有やサムネール用の画像を生成することができます
- ![スクリーンショット 2024-11-04 11.03.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/707293/d980ef9f-cf65-f3f0-3473-19fc8c03f69e.png)
- PKToolPicker : フロート表示のUIを提供します。CanvasViewとは別でViewを切り替えるオブジェクトです
![スクリーンショット 2024-11-04 11.06.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/707293/71d2dd02-4502-e98f-2c1c-33b84dd185c7.png)

- PKTool : CanvasViewで使用される描画ツールを採用するインターフェースです。主に以下のようなものが含まれています
    - PKInkingTool：キャンバスビューに線を描く時に使用する描画特性(線の幅、色、ペンスタイル)を定義する
    - PKEraserTool：CanvasViewで以前に描画された内容を消去するためのツール
    - PKLassonTool：描線や図形を選択するツール

これらの Class, Struct などを使ってアプリを作成していきます。

https://developer.apple.com/documentation/pencilkit

# 本題：PencilKitをSwiftUIで使う方法

SwiftUI では、[UIViewRepresentable](https://developer.apple.com/documentation/swiftui/uiviewrepresentable) プロトコルを使用して、UIKit の View を SwiftUI に統合します。

このプロトコルに準拠すると `makeUIView` と `updateUIView` の2つが必要です。
`makeUIView` は、ビューオブジェクトを作成し、その初期状態を設定します。
`updateUIView` は、SwiftUI からの新しい情報で指定されたビューの状態を更新します。

```swift: CanvasView.swift
import SwiftUI
import PencilKit

struct CanvasView: UIViewRepresentable {
    @Binding var canvasView: PKCanvasView
    private let toolPicker = PKToolPicker()
    func makeUIView(context: Context) -> some UIView {
        canvasView.drawingPolicy = PKCanvasViewDrawingPolicy.anyInput
        
        toolPicker.addObserver(canvasView)
        toolPicker.setVisible(true, forFirstResponder: canvasView)
        
        canvasView.becomeFirstResponder()
        return canvasView
    }
    
    func updateUIView(_ uiView: UIViewType, context: Context) {}
}
```
CanvasView を [`becomeFirstResponder`](https://developer.apple.com/documentation/uikit/uiresponder/1621113-becomefirstresponder) にすることで、オブジェクトをウィンドウの最初のレスポンダにするようにUIKitに依頼します。

次に、上記の CanvasView を使用して、SwiftUI のビューを作成します。

```swift ContentView.swift
import SwiftUI
import PencilKit

struct ContentView: View {
    @State private var canvasView = PKCanvasView()
    var body: some View {
        CanvasView(canvasView: $canvasView)
            .edgesIgnoringSafeArea(.all)
    }
}
```

`PKToolPicker` を使用して、ユーザーがペンや消しゴムなどのツールを選択できるようにします。上記の `updateUIView` メソッド内で、ツールピッカーを表示しています。
エラー(未解決)　アラームアプリで音が鳴らない　Cloud Text-to-Speech APIにて2

## はじめに

昨日に引き続き首題の件で解決していません。
GooglexloudでサービスアカウントのJSONキーを取得してconfigディレクトリに配置したのですが以下のように存在しないと出てしまいます。

```
RuntimeError in AlarmsController#create
Unable to read the credential file specified by GOOGLE_APPLICATION_CREDENTIALS: file /home/username/projects/alarm_call/config/gothic-isotope-xxxxx.json does not exist
```
## おわりに
これを解決すればちゃんとコメントを音声で出してくれるはずと信じて明日つづきをやります！
軽量化されたBERT埋め込みモデルでLLMを安全に：最新ガードレール技術のご紹介

# Lightweight Safety Guardrails Using Fine-tuned BERT Embeddings

今回は、Aaron Zhengらによる「Lightweight Safety Guardrails Using Fine-tuned BERT Embeddings」という論文をご紹介します。本研究は、大規模言語モデル（LLMs）における安全性の確保とコスト効率の向上を目指した、新しい軽量ガードレール手法を提案しています。

---

### 論文情報
- **タイトル**: Lightweight Safety Guardrails Using Fine-tuned BERT Embeddings
- **リンク**: [arXiv:2411.14398](https://arxiv.org/abs/2411.14398)
- **発表日**: 2024年11月21日
- **著者**: Aaron Zheng, Mansi Rana, Andreas Stolcke
- **DOI**: 記載なし

---

## 背景と目的

### 背景
近年、大規模言語モデル（LLMs）は対話型AIや検索エンジン、タスク自動化に広く活用されています。しかし、それに伴い以下のような課題が浮き彫りになっています：
1. **安全性の懸念**:
   - モデルが不適切な入力プロンプトに対して危険な応答を生成する可能性。
2. **高いコストとレイテンシ**:
   - LlamaGuardやOpenAI MOD APIなど、従来のLLMベースのガードレールは非常に高い計算資源を必要とする。
3. **適用範囲の制限**:
   - 教育機関や小規模ビジネスでは、リソース不足によりこれらのソリューションを採用しにくい。

本研究では、軽量かつ効率的な手法を用いてこれらの課題を解決し、幅広いユースケースで利用可能なガードレール技術を開発することを目指しています。

### 目的
本研究の主な目標は以下の通りです：
1. **軽量性と効率性の両立**:
   - モデルサイズをLlamaGuardの70億パラメータから67Mパラメータに削減。
2. **性能の維持**:
   - 従来手法と同等またはそれ以上の安全性判定性能を達成。
3. **コスト削減**:
   - 低リソース環境でも実現可能なソリューションを提供。

---

## 提案手法

### モデルアーキテクチャ
提案手法は、以下の2段階のアプローチで構成されています：
1. **埋め込みモデル**:
   - Sentence-BERTを微調整し、テキストを高次元ベクトルに変換。
   - 危険なプロンプトと安全なプロンプトのベクトルを異なるクラスタに分ける。
2. **分類器**:
   - 埋め込みベクトルを入力とし、SVMまたは浅いニューラルネットワークで安全/危険を分類。

Sentence-BERTを採用することで、以下の利点を得られます：
- **軽量性**:
  - 従来のLlamaGuard（70億パラメータ）に比べ、大幅にモデルサイズを削減。
- **効率性**:
  - Siameseアーキテクチャにより、ベクトル間の類似性を高速に計算可能。

### データセット
- **AEGIS Safety Dataset**:
  - Nvidiaが提供する約26,000件のデータセットを使用。
  - 13のリスクカテゴリに分類（例: 犯罪計画、自殺、暴力など）。

### データ前処理
1. **ラベル統一**:
   - 複数のアノテータ間の不一致を解消。
   - 「曖昧なデータ」は除外。
2. **トークン化**:
   - BERTのトークン上限（512トークン）を超える場合は自動的に切り捨て。

---

## 実験と結果

### 分類設定
提案手法では、以下の4つの分類設定を検証しました：
1. **Binary Embedding, Binary Classification (BEBC)**:
   - 安全/危険の2カテゴリで分類。
2. **Multiple Embedding, Multiple Classifiers (MEMC)**:
   - 各リスクカテゴリごとに個別の分類器を構築。
3. **Multi-class Embedding, Multiple Classifiers (McEMC)**:
   - マルチクラス埋め込みモデルを使用。
4. **Multi-class Embedding, Multi-class Classification (McEMcC)**:
   - マルチクラス分類器を使用してすべてのカテゴリを同時に分類。

### 実験結果
- **最適な分類設定**:
  - McEMcCが最も高い性能を発揮（AUROC: 0.946, F1スコア: 0.89）。
- **レイテンシ比較**:
  - 本モデル: 約0.05秒（1GPU）。
  - LlamaGuard: 140秒以上。

### 他モデルとの比較
提案手法は、以下の既存手法と比較して優れた性能を示しました：
- **LlamaGuard**: AUROC 0.85
- **NeMo-43B**: F1スコア 0.83
- **OpenAI MOD API**: AUROC 0.895

---

## 結論と展望

### 主な貢献
1. **効率的なガードレールの実現**:
   - 小規模環境でも運用可能な低コストモデルを開発。
2. **従来手法を超える性能**:
   - パラメータ数が少ないにも関わらず、高い精度を維持。

### 今後の課題
1. **多言語対応**:
   - 現在は英語のみを対象としているため、多言語データへの適用が課題。
2. **モダリティの拡張**:
   - 音声や画像データへの対応が必要。
3. **カスタマイズ性の向上**:
   - 特定トピックに基づくフィルタリングの実現。

### 展望
提案手法は、LLMsの安全性を向上させるだけでなく、教育機関や中小企業など幅広いユースケースに適用可能です。今後は、多言語対応や少量データでの学習効率化が研究の焦点となるでしょう。

この記事が、皆さんの研究や実務に役立つことを願っています。ご質問やフィードバックがありましたら、ぜひコメント欄にお寄せください。
Python データ構造（リスト型データ編）

Python初心者からレベルアップするためには、Pythonにおける各種データ構造について、さらに深く理解する必要がある。
今回はリスト型データのメソッドについて確認していきたい。

目次
1.リストに追加するメソッド
2.リストから削除するメソッド
3.その他のリストに関するメソッド

1.リストに追加するメソッド
-
リストにデータを追加する操作は主に3つある

<strong>appendメソッド：リストの末尾にアイテムを1つ追加する</strong>
```python:example.py
numbers=[]
numbers.append(1)
print(numbers) # 出力 [1]
```

<strong>extendメソッド：リストの末尾に与えられたリストの全てのアイテムを追加する</strong>
```python:example.py
numbers=[]
numbers.extend([1,2,3])
print(numbers) # 出力 [1,2,3]
```

<strong>insertメソッド：リストの指定された位置にアイテムを挿入する。第一引数はインデックス。</strong>
```python:example.py
numbers=[1,3]
numbers.insert(1,2) # インデックス[1]へ2を挿入する（挿入は指定した要素の前に挿入される）
print(numbers) # 出力 [1,2,3]
```

2.リストから削除するメソッド
-
リストから削除するメソッドも主に3つある。

<strong>removeメソッド：リスト内の指定した最初の値を削除する。存在しなければエラーとなる。</strong>
```python:example.py
numbers=[1,3]
try:
    numbers.remove(2) # リスト内に 2 が存在すれば、削除される
    print(numbers) # 出力 [1] or [3] 
except ValueError:
    print("指定した値が存在しません")
```
<strong>popメソッド：リスト内の指定した値を削除する。存在しなければエラーが発生する。</strong>
```python:example.py
numbers=[1,2,3]
numbers.pop(1) # 引数（インデックス）を省略すると、一番最後のアイテムが削除される
print(numbers) # 出力 [1,3]
```
<strong>clearメソッド：リスト内の全てのアイテムを削除する。</strong>
```python:example.py
numbers=[1,3]
numbers.clear()
print(numbers) # 出力 []
```

3.3.その他のリストに関するメソッド
-
その他もいくつかメソッドを紹介しておく。

<strong>indexメソッド：リスト内の指定した値のインデックスを返す。存在しなければエラー。</strong>
```python:example.py
numbers=[1,2]
try:
    print(numbers.index(2))  # 出力 1
except Valueerror:
    print("指定した値が存在しません")
```

<strong>countメソッド：リスト内の引数の個数を返す</strong>
```python:example.py
numbers=[1,1,2]
print(numbers.count(1)) # 出力 2
```

<strong>sortメソッド：リスト内のデータを順番にインプレースでソートする。</strong>
```python:example.py
numbers=[2,1,3]
print("ソート前",numbers) # 出力 [2,1,3]

numbers.sort(key=None,reverse=False)
print("ソート後",numbers) # 出力 [1,2,3]
```
<strong>reverseメソッド：リスト内のデータを逆順にインプレースでソートする。</strong>
```python:example.py
numbers=[2,1,3]
print("ソート前",numbers) # 出力 [2,1,3]

numbers.reverse()
print("ソート後",numbers) # 出力 [3,1,2]
```
<strong>copyメソッド：リストのコピーを返す。</strong>
```python:example.py
numbers=[2,1,3]
print(numbers) # 出力 [2,1,3]

copy_numbers=numbers.copy()
print(copy_numbers) # 出力 [2,1,3]
```

他にも気付いたことがあれば追記予定。
RailsとGSAPを連携する方法：StimulusとCDNでアニメーションを実現する


# はじめに

Rails アプリケーションでアニメーションを実装する際に便利なライブラリとして、GSAP (GreenSock Animation Platform) を利用しようとしました。しかし、GSAP を Rails7 で利用しようとした際、以下のようなエラーに遭遇しました。

```bash
Cannot set property window of #<Window> which has only a getter
Uncaught SyntaxError: The requested module 'gsap' does not provide an export named 'gsap'

```

この記事では、これらのエラーが発生する原因と、Stimulus と CDN を組み合わせて GSAP を正常に動作させる方法を詳しく解説します。

### 前提条件

npm や yarn などを使わないで GSAP を導入しようとする場合

#### エラー発生の背景

1. Importmap での GSAP 読み込み
   Rails 7 では、JavaScript モジュールを管理するために Importmap が採用されています。
   しかし、GSAP はデフォルトで提供されるファイル形式が Rails の Importmap で期待される ES モジュール形式と一致しないことがあります。

以下のように Importmap で GSAP をピンして読み込もうとすると問題が発生します

[GSAP の install 方法](https://gsap.com/docs/v3/Installation?tab=yarn&module=esm&method=private+registry&tier=free&club=false&require=false&trial=true)

これは GSAP は元々 npm や yarn、または CDN 経由で利用することを想定したライブラリであるからか、ails 7 の Importmap で利用する際には利用するのは難しそうです。

```bash
bin/importmap pin gsap
```

この場合、次のエラーが発生します：

```bash
Cannot set property window of #<Window> which has only a getter
```

2. CDN で読み込む場合の問題
   CDN 経由で GSAP を読み込むと、グローバルスコープに直接 GSAP オブジェクトを登録します。しかし、Stimulus コントローラ内でモジュールとして扱おうとするとエラーが発生することがあります：

```sh
Uncaught SyntaxError: The requested module 'gsap' does not provide an export named 'gsap'
```

#### 解決方法

CDN と Stimulus の併用
GSAP を CDN 経由で読み込む
Rails の application.html.erb に CDN のスクリプトタグを追加します：

```html
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/ScrollTrigger.min.js"></script>
```

Stimulus コントローラで GSAP を利用
CDN で読み込んだ GSAP はグローバルスコープに登録されるため、Stimulus コントローラ内で以下のように記述します

```javascript
import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
	connect() {
		console.log("GSAP Stimulus Controller connected!");

		// ScrollTriggerの登録
		gsap.registerPlugin(ScrollTrigger);


```

#### 補足

- GSAP と ES モジュール形式について
  GSAP は ES モジュール形式（ESM）や UMD 形式のファイルを提供しています。しかし、Rails の Importmap は特定のファイル形式（ESM）を期待して動作します。問題は、GSAP が CDN で提供しているファイルがこの形式に完全に適合しないことです。

- CDN ファイル:
  多くの場合、UMD 形式が提供され、ブラウザのグローバルスコープに依存しています。
  Rails の Importmap が期待する ES モジュール形式ではないため、import 文を使うとエラーになります。
- npm や yarn でインストールする場合:
  ESM 形式が提供されており、おそらく使用が容易です。

#### 結論

GSAP は Rails の Importmap との相性が悪く、サポート対象外と考えるのが妥当です。
[以前に rails7 と importmap について質問されていた投稿](https://gsap.com/community/forums/topic/42758-gsap-with-rails-7-and-stimulus/)

### まとめ

GSAP は Rails アプリにモダンなアニメーションを導入するのに最適なツールですが、設定に注意が必要です。
初心者には CDN と Stimulus の併用がおすすめです。
しかしながらパフォーマンスとしてはよくないので、その場合は npm や yarn を使うしかないかもしれません。
chrome.storageとは

# はじめに
ちょっとしたChrome拡張機能を作成している中で、`chrome.storage`を使用する機会があったのでそれについて調べた内容を書きます。

## chrome.storageとは
拡張機能の中で、タブ遷移や削除などをしても状態を保持したい時に使用するAPIです。

**Chrome拡張機能作成用に機能が色々足されたlocalStorage**という理解でいいのかなと。

Refarenceでは以下のように書かれています。

> 拡張機能の Service Worker やコンテンツ スクリプトを含むすべての拡張機能のコンテキストは、Storage API にアクセスできます。
>
>JSON のシリアル化可能な値は、オブジェクトのプロパティとして格納されます。
>
>Storage API は、一括読み取り / 書き込みオペレーションに対して非同期です。
> 
>ユーザーがキャッシュや閲覧履歴を削除しても、データは保持されます。

逆に、拡張機能を作成する上で`localStorage`を使用するのは推奨されないようです。
考えたら当たり前か...

## ストレージの種類
以下４つのストレージの種類があります。

### `storage.local`
ローカルにデータを保存します。

拡張機能が削除されると削除されます。容量は10MBまでですが、`"unlimitedStorage"`権限で増やせます。

### `storage.managed`
システム管理者が設定する読み取り専用ストレージです。
ポリシーで構成され、組織の全ユーザーに適用されます。

これはあんまり使わなそうですね。

### `storage.session`
セッション中のみメモリにデータを保持します。容量は10MB。

`chrome.storage.session.setAccessLevel()` でアクセス可能範囲を変更できます。Service Worker に適しています。

### `storage.sync`
データを同期されたChrome間で同期できます。
普段使っている拡張機能の設定値などはこれで同期されていたんですね。

容量は約100KB、アイテムごとに8KB。オフライン時はローカル保存され、復帰後に同期されます。
非常に便利ですね！

当たり前ですが、機密データの保存は`storage.session`を推奨しているようです。


## さいごに
最後までお読みいただきありがとうございました！
なにかブラウザ間で同期する、便利な拡張機能を作ってみたいですね🤔
競プロ日記#24/11/24

# A問題 - Round Up the Mean
(https://atcoder.jp/contests/abc082/tasks/abc082_a)

平均値に対して繰り上げを行う
➀C++の整数同士の除算では結果が少数になる場合勝手に切り捨ててくれるから+1すればよいだけかと思いきや割り切れた場合は困る（例：2,4の場合求めているのは3だが+1すると4になってしまう）
→平均値を出す前に+1することで解決
2,4の場合は(2 + 4 + 1) = 7を2で割って3.5→切り捨てるので3
割り切れない場合も同様で2,5の場合は(2 + 5 + 1) = 8 を2で割って4

# A - Placing Marbles
(https://atcoder.jp/contests/abc081/tasks/abc081_a)

桁数の数だけ10で割った余りと割って切り捨てた商を保持してresult変数をインクリメントすればよいだけ
とはいえもっとスマートな解き方があるはず
→解説を見たらstringで受け取ってインデックスでstring[0],string[1],...とやっていけばよいだけだった
【MarkdownAI】マークダウンのまとめ

以下は、主に使われるMarkdown記法のまとめです。
# 見出し
```
# 見出し1
## 見出し2
### 見出し3
#### 見出し4
##### 見出し5
###### 見出し6
```


例:
# 見出し1
## 見出し2
### 見出し3


# リスト
- 箇条書き
```
- アイテム1
- アイテム2
  - サブアイテム1
  - サブアイテム2
```
例:
- アイテム1
- アイテム2
  - サブアイテム1
  - サブアイテム2

- 番号付きリスト
```
1. 番号付きリスト1
    1. 番号付きリスト1_1
    1. 番号付きリスト1_2
1. 番号付きリスト2
1. 番号付きリスト3
```
例:
1. 番号付きリスト1
    1. 番号付きリスト1_1
    1. 番号付きリスト1_2
1. 番号付きリスト2
1. 番号付きリスト3

# テキスト装飾
- 強調
```
*イタリック* または _イタリック_
**太字** または __太字__
```
例:
_イタリック_
__太字__

- 取り消し線
```
~~取り消し線~~
```
例:
取り消し線


# リンク
```
[リンクテキスト](URL)
```
例:
[Google](https://www.google.co.jp/)
# 画像
```
![代替テキスト](画像URL)
```

# 引用
```
> 引用テキスト
>> ネストされた引用
```
例:
> 引用テキスト
>> ネストされた引用

# コード
	•	インラインコード
`コード`
例: print("Hello World")
	•	コードブロック
```言語名
コード
**例**:
```python
def hello():
    print("Hello World")
```

水平線
```
---
```
例:
---

# 表
```
| ヘッダー1 | ヘッダー2 |
|----------|----------|
| データ1   | データ2   |
| データ3   | データ4   |
```
例:
| ヘッダー1 | ヘッダー2 |
|----------|----------|
| データ1   | データ2   |
| データ3   | データ4   |

上記の内容を使ってできたwebサイトがこちら！
![FireShot Capture 025 - MarkdownAI β - mdown.ai.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524120/90ace8b0-5efa-5415-b28f-e53a68c3fa38.png)

https://mdown.ai/content/591634bb-debd-4742-af53-9fdb8ed2af03


# 最後に
ここまで読んでいただきありがとうございました！
簡単にwebサイトが作れるMarkdownAIですが、マークダウンの基礎を覚えていないと書けないので、上記を参考にして素敵なwebサイトを作ってみてください🎵
【VSCode】エクスプローラのフォルダ名が横並びにならないようにする方法

## デフォルト

デフォルトの設定ではフォルダ内に1つだけフォルダがある場合にエクスプローラでフォルダ名が横並びに表示されます。

![スクリーンショット 2024-11-24 23.34.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/5fa8279f-aede-f0ba-7963-24a8391ecacf.png)

## 変更方法

`settngs.json`に以下の設定を記述することでフォルダが横並びにならないようになります。

```json:settings.json
{
  "explorer.compactFolders": false
}
```

![スクリーンショット 2024-11-24 23.35.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/391078aa-af12-ada8-4c9a-15f0c74365f1.png)
Insight-Vの可能性：視覚と言語を統合した次世代AI推論モデル

# Insight-V: Exploring Long-Chain Visual Reasoning with Multimodal Large Language Models

今回は、マルチモーダル大規模言語モデル（MLLMs）による視覚的長鎖推論（LCR）の実現を目指した革新的な研究、「Insight-V: Exploring Long-Chain Visual Reasoning with Multimodal Large Language Models」を紹介します。この研究は、視覚と言語を統合した複雑な推論タスクの新しい可能性を切り開いています。

---

### 論文情報
- **タイトル**: Insight-V: Exploring Long-Chain Visual Reasoning with Multimodal Large Language Models
- **リンク**: [arXiv:2411.14432v1](https://arxiv.org/abs/2411.14432v1)
- **発表日**: 2024年11月21日
- **著者**: Yuhao Dong, Zuyan Liu, Hai-Long Sun, Jingkang Yang, Winston Hu, Yongming Rao, Ziwei Liu
- **DOI**: 記載なし

---

## 背景と目的

### 背景
視覚と言語の統合による推論能力は、AI研究の重要な課題として注目されています。例えば、以下のようなシナリオで活用されることが期待されています：
- **医療分野**: 医療画像解析と患者の診断記録を統合して正確な診断を行う。
- **自動運転**: カメラ映像から危険な状況を予測し、対応策を推論する。
- **教育アプリケーション**: 図表を含む教材を理解し、生徒の質問に文脈を考慮して回答する。

従来のマルチモーダルモデルでは、以下の課題がありました：
1. **推論データの不足**:  
   特に長鎖推論に必要なデータは、収集コストが高く、多様性に欠けていました。
2. **視覚情報の処理能力の限界**:  
   Chain-of-Thought（CoT）のような手法は言語データでは効果的でしたが、視覚データでは不十分でした。
3. **計算資源の消費**:  
   従来のモデルは大規模データと複雑なアーキテクチャに依存しており、効率性が課題となっていました。

### 目的
Insight-Vはこれらの課題を克服し、以下の目標を達成することを目指しました：
1. **高品質で多様な推論データの生成**:  
   スケーラブルな方法で視覚的長鎖推論に必要なデータを生成。
2. **協調型エージェントシステムの設計**:  
   複雑な推論タスクを効果的に分担・協調するエージェントシステムを構築。
3. **モデル性能の最適化**:  
   Iterative DPO（Direct Preference Optimization）を用いてモデルの精度と安定性を向上。

---

## 提案手法の詳細

Insight-Vは以下の3つの主要な要素から構成されています：

### 1. データ生成パイプライン
Insight-Vは、視覚的長鎖推論データをスケーラブルに生成する革新的な手法を採用しています：
1. **ステップバイステップ推論生成**:  
   入力画像（$I$）と質問（$Q$）を基に、モデルが逐次的な推論プロセス（$R_t$）を生成します：
   $$ R_t = M(I, Q, [R_1, \cdots, R_{t-1}], A) $$
   各ステップでモデルが次に取るべきアクションを決定し、回答を改善します。

2. **評価基準の設定**:  
   推論データの正確性、多様性、詳細度をスコア化し、高品質なデータのみを選別。

### 2. マルチエージェントシステム
Insight-Vでは、推論タスクを以下の2つのエージェントに分担します：
- **推論エージェント**:  
  ステップごとに詳細な推論を生成し、解決の道筋を示します。
- **要約エージェント**:  
  推論結果を評価・要約し、最適な最終回答を生成します。

### 3. Iterative DPOアルゴリズム
Iterative Direct Preference Optimization（DPO）は、以下の手順でモデルを改良します：
1. 人間の好みに基づく報酬モデルを使用し、推論の質を評価。
2. 推論プロセスを反復的に調整し、性能向上を図る。

---

## 実験と結果

### 実験設定
Insight-Vは、以下の主要なベンチマークで評価されました：
- **MMMU / MMMU-Pro**: 高度な専門的視覚推論能力を評価。
- **ChartQA**: 複雑なグラフデータに基づく推論。
- **MMBench**: マルチモーダル性能を総合的にテスト。

### 主な結果
1. **LLaVA-NeXTモデルでの性能向上**:  
   Insight-Vを適用することで、平均7.0%の性能向上を達成しました。
2. **ベンチマークでの優位性**:  
   ChartQAでは5.8%、MMMU-Proでは4.5%の大幅な精度向上を記録。
3. **汎用タスクへの応用可能性**:  
   TextVQAやDocVQAといった一般的なタスクでも性能維持または向上。

---

## 課題と今後の展望

Insight-Vは視覚的推論における革新をもたらしましたが、以下の課題があります：
1. **データ生成コストの削減**:  
   冗長性を減らし、生成プロセスの効率を向上。
2. **エージェント設計の最適化**:  
   要約エージェントの軽量化を進め、スケーラビリティを向上。
3. **多様な応用分野での検証**:  
   医療、教育、自動運転などの現実世界のシナリオでさらなる評価が必要です。

Insight-Vは視覚と言語を統合するAIの新たな地平を切り開く研究です。本記事が、読者の研究や実務に役立つことを願っています。質問やフィードバックがありましたら、ぜひコメント欄にお寄せください。
"分厚い"技術書を読む技術

# はじめに
分厚い技術書を手に取った瞬間、「これ、読めるのかな…」と不安になること、ありませんか？ページ数が多いだけで圧倒されるし、「最後まで読み切るのに何ヶ月かかるんだろう？」と思うと、最初の一歩を踏み出すのが大変です。私も同じで、分厚い技術書を買ったはいいものの、その厚さを見るだけでハードルが上がっていました。

でも、最近メルカリで裁断済みの技術書を買ったことで、ちょっと考え方が変わりました。「[LangChainとLangGraphによるRAG・AIエージェント［実践］入門](https://www.amazon.co.jp/LangChain%E3%81%A8LangGraph%E3%81%AB%E3%82%88%E3%82%8BRAG%E3%83%BBAI%E3%82%A8%E3%83%BC%E3%82%B8%E3%82%A7%E3%83%B3%E3%83%88%EF%BC%BB%E5%AE%9F%E8%B7%B5%EF%BC%BD%E5%85%A5%E9%96%80-%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E9%81%B8%E6%9B%B8-%E8%A5%BF%E8%A6%8B-%E5%85%AC%E5%AE%8F/dp/4297145308)」という分厚い本だったんですが、ある工夫をしたことで扱いやすくなり、読むハードルがぐっと下がったんです。意外な発見でした！

https://www.amazon.co.jp/dp/4297145308

# 困難は分割せよ（物理）
「困難は分割せよ」という言葉、聞いたことありますか？これは物事を小さく分けて取り組むと、うまくいきやすいという考え方です。分厚い技術書の場合でも、裁断済みという形で物理的に分割されていたおかげで、「とりあえずこの章だけ読もう」とか「1つ終わったら次の章に進もう」と、気楽に取り組めました。分厚い本が薄くなるだけで、「これならなんとかなるかも！」という気持ちになれたのが大きかったです。

# とりあえず章ごとに製本
さすがに全ページばらばらだと扱いづらいので裁断済みの本をさらに章ごとに製本してみたんですが、これが意外と良かったんです！例えば、薄くなった章をさっと持ち運んで、カフェで軽く読み返すとか、別の章と並べて参照するときに便利でした。

章ごとに分けると、「1章だけ終わらせる」という小さな達成感が得られるのも良いポイントです。分厚い技術書に圧倒されず、少しずつ進められる感覚、これが章ごとに製本した技術書の魅力だと感じました。

章ごとに製本した技術書のメリットを列挙すると以下になります。
- 裁断済みだとメルカリで安く仕入れられる
- 鈍器にならない（重くない）ので持ち運びやすい
- 別の章をすぐに参照できる
- 1冊が薄くなる分読書へのハードルが下がる
- 1章ずつで本が終わるので達成感を得られやすい

![PXL_20241124_141251098.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/656312/7ec9a7c0-970b-ce88-0fe3-4f21a7904e6d.jpeg)

# 最後に
分厚い技術書でも「困難は分割せよ（物理）」を意識することで、一気に読みやすさが変わりました。ぜひあなたも、自分なりの「使いやすい技術書」の形を見つけて、学びを楽しんでみてください！
Ray Jobs CLIを使ってRay ClusterのJobを停止・削除する

::: note info
これは [ZOZO Advent Calendar 2024](https://qiita.com/advent-calendar/2024/zozo) カレンダー Vol.9 の 7日目の記事です。
:::

# はじめに
昨日は[Ray ClusterのWorker NodeをSpot VMで起動する](https://qiita.com/okmtz/private/0ab34027668b1f16b1ba)というタイトルで、Ray Clusterに関する記事を投稿しました。  

本記事では、Ray Jobs CLIをご紹介します。

# Ray Jobs CLI
[Ray Jobs CLI](https://docs.ray.io/en/latest/cluster/running-applications/job-submission/cli.html)は通常の[Pythonパッケージのray](https://github.com/ray-project/ray)をinstallすると`ray job`コマンドにより実行可能です。

poetryの場合は次のコマンドでinstall可能です。
```bash
poetry add "ray[default]"
```

## Ray Jobの停止・削除
RayのDashboardにアクセスすることでRay Jobsを一覧すること可能ですが、Dashboardから実行中のJobの停止や削除を行うことはできません。（自分が見落としていたらすみません🙏）

RayのJobをSubmitする際に利用するRay Jobs CLIですが、RayのJobの停止、削除なども行えます。

![スクリーンショット 2024-11-24 23.16.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/576866/c3559e98-edbf-3e42-e9a5-19d4f9527e44.png)

上記の画像で`04000000`のJob IDを持つJobは`RUNNING`になっています。Jobを停止するには次のコマンドを実行します。

```bash
RAY_ADDRESS="https://<ray cluster host>/" poetry run ray job stop 04000000
```

上記コマンドの実行により、Jobのステータスが`STOPPED`に変わったことがわかります。

![スクリーンショット 2024-11-24 23.21.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/576866/bac35d75-fa12-149f-6d57-b4a3a2921387.png)

Jobの削除は次のコマンドにより実行できます。

```bash
RAY_ADDRESS="https://<ray cluster host>/" poetry run ray job delete 04000000
```

そもそもJobをSubmitする際に必ず利用するかとは思いますが、意図せずJobが終了しなくなってしまった場合や、間違ってJobをSubmitした場合などSubmit以外でも利用する場面は多そうです。  
Dashboard上でこれらの操作が可能になればより便利かとは思いますが、今後に期待です。