🙄

mdx.jpを用いてYOLOv11のクラス分類(くずし字認識)の学習を試す

2024/11/06に公開

概要

mdx.jpを用いてYOLOv11のクラス分類(くずし字認識)の学習を行う機会がありましたので、備忘録です。

データセット

以下の「くずし字データセット」を対象にします。

http://codh.rois.ac.jp/char-shape/book/

データセットの作成

yoloの形式に合致するようにデータセットを整形します。まず、書名ごとに分かれているデータをフラットにマージします。

#| export
class Classification:

    def create_dataset(self, input_file_path, output_dir):
        # "../data/*/characters/*/*.jpg"
        files = glob(input_file_path)
        
        # output_dir = "../data/dataset"

        for file in tqdm(files):
            cls = file.split("/")[-2]

            output_file = f"{output_dir}/{cls}/{file.split('/')[-1]}"

            if os.path.exists(output_file):
                continue

            # print(f"Copying {file} to {output_file}")

            os.makedirs(f"{output_dir}/{cls}", exist_ok=True)

            shutil.copy(file, output_file)

次に、以下のようなスクリプトにより、データセットを分割します。

    def split(self, input_dir, output_dir, train_ratio = 0.7, val_ratio = 0.15):
        if os.path.exists(output_dir):
             shutil.rmtree(output_dir)

        # クラスディレクトリの取得
        classes = [d for d in os.listdir(input_dir) if os.path.isdir(os.path.join(input_dir, d))]

        # データを分割して保存
        for cls in tqdm(classes):
            class_dir = os.path.join(input_dir, cls)
            files = [os.path.join(class_dir, f) for f in os.listdir(class_dir) if os.path.isfile(os.path.join(class_dir, f))]
            
            # シャッフルして分割
            random.shuffle(files)
            train_end = int(len(files) * train_ratio)
            val_end = int(len(files) * (train_ratio + val_ratio))
            
            train_files = files[:train_end]
            val_files = files[train_end:val_end]
            test_files = files[val_end:]
            
            # 保存ディレクトリを作成
            for split, split_files in zip(["train", "val", "test"], [train_files, val_files, test_files]):
                split_dir = os.path.join(output_dir, split, cls)
                os.makedirs(split_dir, exist_ok=True)
                
                # ファイルをコピー
                for file in split_files:
                    shutil.copy(file, os.path.join(split_dir, os.path.basename(file)))

        print("データの分割が完了しました。")

結果、1,086,326画像のデータセットが作成されました。

Ultralytics HUBの利用(失敗しました。)

まず、Ultralytics HUBの利用を考え、データセットにアップロードを試みましたが、以下のようにエラーが出てしまいました。データセットの作成方法に誤りがあるのかなど、原因が分かりかねました。

mdx.jpのオブジェクトストレージへのアップロード

以下のようなコマンドにより、データセットをオブジェクトストレージにアップロードします。

s3cmd sync data/split_dataset_full.zip s3://satoru196/dataset/

参考:初期設定

以下に公式のマニュアルがあります。

https://docs.mdx.jp/ja/index.html#オブジェクトストレージの利用方法例

sudo apt install s3cmd
s3cmd --configure
...
Access key and Secret key are your identifiers for Amazon S3. Leave them empty for using the env variables.
Access Key: ★(アクセスキーを入力)
Secret Key: ★(秘密鍵を入力)
Default Region [US]: ★us-east-1

Use "s3.amazonaws.com" for S3 Endpoint and not modify it to the target Amazon S3.
S3 Endpoint [s3.amazonaws.com]: ★s3ds.mdx.jp

Use "%(bucket)s.s3.amazonaws.com" to the target Amazon S3. "%(bucket)s" and "%(location)s" vars can be used
if the target S3 system supports dns based buckets.
DNS-style bucket+hostname:port template for accessing a bucket [%(bucket)s.s3.amazonaws.com]: ★s3ds.mdx.jp

Encryption password:
Path to GPG program [/usr/bin/gpg]:
Use HTTPS protocol [Yes]:
HTTP Proxy server name:

Test access with supplied credentials? [Y/n]
Please wait, attempting to list all buckets...
Success. Your access key and secret key worked fine :-)  <==これが出ればOK!

Now verifying that encryption works...
Not configured. Never mind.
Save settings? [y/N] ★y

アップロード

s3cmd put (任意のファイル名) s3://(バケット名)

ダウンロード

s3cmd get s3://(バケット名)/(任意のオブジェクトキー名)

mdx.jpでの操作

mdx.jpの1GPUパックを利用します。

以下のようなコマンドにより、データセットをオブジェクトストレージからダウンロードします。

s3cmd get s3://satoru196/dataset/split_dataset_full.zip
unzip split_dataset_full.zip

そして、以下のようなスクリプトにより、学習を実行します。

your_training_script.py
from ultralytics import YOLO

# YOLOv8の分類モデルをロード
model = YOLO('yolo11x-cls.pt')  # Nanoサイズの分類モデル

# データセットとトレーニング設定
model.train(
    data='/home/mdxuser/yolo/split_dataset_full',  # データセットのパス
    epochs=10,                 # エポック数
    # imgsz=224,                 # 入力画像サイズ
    batch=256                   # バッチサイズ(オプション)
)

ssh接続が切れても学習を継続できるように、tmuxを使います。

tmux new -s my_training_session
python your_training_script.py

実行後、以下のようなチェックが走ります。

Downloading https://github.com/ultralytics/assets/releases/download/v8.3.0/yolo11x-cls.pt to 'yolo11x-cls.pt'...
100%|██████████| 56.9M/56.9M [00:01<00:00, 47.4MB/s]
Ultralytics 8.3.27 🚀 Python-3.10.12 torch-2.5.1+cu124 CUDA:0 (NVIDIA A100-SXM4-40GB, 40339MiB)
engine/trainer: task=classify, mode=train, model=yolo11x-cls.pt, data=/home/mdxuser/yolo/split_dataset_full, epochs=10, time=None, patience=100, batch=16, imgsz=224, save=True, save_period=-1, cache=False, device=None, workers=8, project=None, name=train, exist_ok=False, pretrained=True, optimizer=auto, verbose=True, seed=0, deterministic=True, single_cls=False, rect=False, cos_lr=False, close_mosaic=10, resume=False, amp=True, fraction=1.0, profile=False, freeze=None, multi_scale=False, overlap_mask=True, mask_ratio=4, dropout=0.0, val=True, split=val, save_json=False, save_hybrid=False, conf=None, iou=0.7, max_det=300, half=False, dnn=False, plots=True, source=None, vid_stride=1, stream_buffer=False, visualize=False, augment=False, agnostic_nms=False, classes=None, retina_masks=False, embed=None, show=False, save_frames=False, save_txt=False, save_conf=False, save_crop=False, show_labels=True, show_conf=True, show_boxes=True, line_width=None, format=torchscript, keras=False, optimize=False, int8=False, dynamic=False, simplify=True, opset=None, workspace=4, nms=False, lr0=0.01, lrf=0.01, momentum=0.937, weight_decay=0.0005, warmup_epochs=3.0, warmup_momentum=0.8, warmup_bias_lr=0.1, box=7.5, cls=0.5, dfl=1.5, pose=12.0, kobj=1.0, label_smoothing=0.0, nbs=64, hsv_h=0.015, hsv_s=0.7, hsv_v=0.4, degrees=0.0, translate=0.1, scale=0.5, shear=0.0, perspective=0.0, flipud=0.0, fliplr=0.5, bgr=0.0, mosaic=1.0, mixup=0.0, copy_paste=0.0, copy_paste_mode=flip, auto_augment=randaugment, erasing=0.4, crop_fraction=1.0, cfg=None, tracker=botsort.yaml, save_dir=runs/classify/train
train: /home/mdxuser/yolo/split_dataset_full/train... found 758321 images in 3538 classes: ERROR ❌️ requires 4328 classes, not 3538
val: /home/mdxuser/yolo/split_dataset_full/val... found 162622 images in 2901 classes: ERROR ❌️ requires 4328 classes, not 2901
test: /home/mdxuser/yolo/split_dataset_full/test... found 165383 images in 4328 classes ✅ 
Overriding model.yaml nc=80 with nc=4328

                   from  n    params  module                                       arguments                     
  0                  -1  1      2784  ultralytics.nn.modules.conv.Conv             [3, 96, 3, 2]                 
  1                  -1  1    166272  ultralytics.nn.modules.conv.Conv             [96, 192, 3, 2]               
  2                  -1  2    389760  ultralytics.nn.modules.block.C3k2            [192, 384, 2, True, 0.25]     
  3                  -1  1   1327872  ultralytics.nn.modules.conv.Conv             [384, 384, 3, 2]              
  4                  -1  2   1553664  ultralytics.nn.modules.block.C3k2            [384, 768, 2, True, 0.25]     
  5                  -1  1   5309952  ultralytics.nn.modules.conv.Conv             [768, 768, 3, 2]              
  6                  -1  2   5022720  ultralytics.nn.modules.block.C3k2            [768, 768, 2, True]           
  7                  -1  1   5309952  ultralytics.nn.modules.conv.Conv             [768, 768, 3, 2]              
  8                  -1  2   5022720  ultralytics.nn.modules.block.C3k2            [768, 768, 2, True]           
  9                  -1  2   3264768  ultralytics.nn.modules.block.C2PSA           [768, 768, 2]                 
 10                  -1  1   6529768  ultralytics.nn.modules.head.Classify         [768, 4328]                   
YOLO11x-cls summary: 309 layers, 33,900,232 parameters, 33,900,232 gradients, 115.4 GFLOPs
Transferred 492/494 items from pretrained weights
Ultralytics HUB: View model at https://hub.ultralytics.com/models/VMzOVApOedDA0LLYqdho 🚀
AMP: running Automatic Mixed Precision (AMP) checks...

「Ultralytics HUB: View model at 」のURLを参照することで、学習経過を確認することができました。

バッチサイズ

当初、batchサイズを未指定で実行していました。デフォルトは16のようですが、以下のように、GPUのメモリ使用量が低い状態でした。

      Epoch    GPU_mem       loss  Instances       Size
       1/10      1.94G      5.908         16        224:  16%|█▌        | 7471/47386 [08:16<43:23, 15.33it/s]

A100の40GBメモリを活かすために、batchサイズを256に変更しました。結果、以下のように1エポックあたりの実行時間を短縮することができました。

      Epoch    GPU_mem       loss  Instances       Size
       1/10      22.7G      8.017        256        224:  12%|█▏        | 349/2962 [02:44<20:18,  2.14it/s]

参考:tmux

tmuxで現在のセッション一覧を表示する

tmux ls

特定のセッションに再接続する

tmux attach -t 0

tmuxセッションから抜ける(デタッチする)

Ctrl + b を押してから d を押します。

学習結果

学習結果のモデルは、以下のHugging Faceのスペースからご利用いただけます。

https://huggingface.co/spaces/nakamura196/yolov11x-cls-codh-char

精度については、改善の余地があるかと思いますが、以下のように、正しく結果を返却してくれるケースもありました。

上記では、以下の文字画像を利用しています。

https://mojiportal.nabunken.go.jp/

まとめ

yolo, mdx.jpなどを使った学習の参考になりましたら幸いです。

Discussion