Skip to content
Published on

コンピュータービジョン完全攻略: CNN、ViT、YOLO、Stable Diffusionまで

Authors

はじめに

コンピュータービジョン(Computer Vision)は、機械が画像や動画を「理解」するAIのコア分野です。スマートフォンの顔認証、自動運転車の障害物認識、医療画像診断支援——これらすべてにコンピュータービジョン技術が使われています。

このガイドではピクセルレベルの基礎から最新のVision Transformer、Stable Diffusionまで、PyTorchコード例を交えながら体系的に解説します。


1. 画像の基礎: ピクセル、チャンネル、畳み込み

1.1 デジタル画像の構造

デジタル画像はピクセル(pixel)の2Dグリッドです。

  • グレースケール画像: H x W 形状の2D配列、各ピクセル値は0〜255
  • カラー画像(RGB): H x W x 3 形状の3Dテンソル、各チャンネル0〜255
  • 解像度: 画像サイズ(例: 1920x1080)、ピクセル密度(DPI)
import torch
import torchvision.transforms as T
from PIL import Image

# 画像の読み込みとテンソル変換
img = Image.open("sample.jpg").convert("RGB")
transform = T.Compose([
    T.Resize((224, 224)),
    T.ToTensor(),          # [0,255] -> [0.0,1.0], HWC -> CHW
    T.Normalize(mean=[0.485, 0.456, 0.406],
                std=[0.229, 0.224, 0.225])
])
tensor = transform(img)   # shape: [3, 224, 224]
print(f"Shape: {tensor.shape}, dtype: {tensor.dtype}")

1.2 畳み込みと主なフィルター

畳み込み(Convolution)は小さなカーネル(フィルター)を画像全体にスライドさせて特徴を抽出します。

import torch
import torch.nn.functional as F

# 3x3 Sobelエッジ検出カーネル(水平方向)
kernel = torch.tensor([
    [-1, 0, 1],
    [-2, 0, 2],
    [-1, 0, 1]
], dtype=torch.float32).unsqueeze(0).unsqueeze(0)  # [1, 1, 3, 3]

# グレースケール画像に畳み込みを適用
gray_tensor = T.ToTensor()(T.Grayscale()(img)).unsqueeze(0)
edges = F.conv2d(gray_tensor, kernel, padding=1)
カーネルの種類目的活用例
Sobelエッジ検出(水平/垂直)自動運転の車線認識
Gaussianブラリング、ノイズ除去画像前処理
Laplacianエッジ強調先鋭化
Average平均ブラーダウンサンプリング

1.3 Albumentationsデータ拡張パイプライン

import albumentations as A
from albumentations.pytorch import ToTensorV2
import cv2

train_transform = A.Compose([
    A.RandomResizedCrop(224, 224, scale=(0.7, 1.0)),
    A.HorizontalFlip(p=0.5),
    A.ColorJitter(brightness=0.4, contrast=0.4,
                  saturation=0.4, hue=0.1, p=0.8),
    A.GaussianBlur(blur_limit=(3, 7), p=0.2),
    A.ShiftScaleRotate(shift_limit=0.05, scale_limit=0.1,
                       rotate_limit=15, p=0.5),
    A.Normalize(mean=(0.485, 0.456, 0.406),
                std=(0.229, 0.224, 0.225)),
    ToTensorV2(),
])

image = cv2.cvtColor(cv2.imread("sample.jpg"), cv2.COLOR_BGR2RGB)
augmented = train_transform(image=image)["image"]

2. CNNアーキテクチャの発展史

2.1 主要アーキテクチャの年表

アーキテクチャ主な貢献ImageNet Top-1
1998LeNet-5最初の実用的CNN、畳み込み+プーリング構造-
2012AlexNetReLU、Dropout、GPU学習63.3%
2014VGGNet3x3カーネルを深く積み重ね74.4%
2015ResNet-50スキップ接続、残差学習76.0%
2017DenseNet全レイヤーの直接接続77.4%
2019EfficientNet-B7複合スケーリング84.4%
2022ConvNeXt-LTransformerのCNN設計原則を適用86.6%

2.2 ResNet: 残差学習の革命

ResNetの核心は**スキップ接続(残差接続)**です。入力xを出力に直接加算することで勾配消失問題を解決します。残差ブロックの出力F(x) + xを微分するとdF/dx + 1となり、最小勾配1が常に保証されます。

import torch
import torch.nn as nn
import torchvision.models as models

class ResNetClassifier(nn.Module):
    def __init__(self, num_classes: int, pretrained: bool = True):
        super().__init__()
        weights = models.ResNet50_Weights.IMAGENET1K_V2 if pretrained else None
        self.backbone = models.resnet50(weights=weights)
        in_features = self.backbone.fc.in_features
        self.backbone.fc = nn.Sequential(
            nn.Dropout(0.3),
            nn.Linear(in_features, num_classes)
        )

    def forward(self, x):
        return self.backbone(x)

def train_one_epoch(model, loader, optimizer, criterion, device):
    model.train()
    total_loss, correct = 0.0, 0
    for images, labels in loader:
        images, labels = images.to(device), labels.to(device)
        optimizer.zero_grad()
        outputs = model(images)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        total_loss += loss.item() * images.size(0)
        correct += (outputs.argmax(1) == labels).sum().item()
    return total_loss / len(loader.dataset), correct / len(loader.dataset)

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = ResNetClassifier(num_classes=10).to(device)
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4, weight_decay=1e-2)
criterion = nn.CrossEntropyLoss()

2.3 EfficientNet: 複合スケーリング

EfficientNetは**幅(width)、深さ(depth)、解像度(resolution)**を一緒にスケーリングする複合係数(compound coefficient)を導入しました。

import timm

# EfficientNet-B4のファインチューニング
model = timm.create_model(
    "efficientnet_b4",
    pretrained=True,
    num_classes=100,
    drop_rate=0.3
)

# バックボーンを固定(特徴抽出モード)
for name, param in model.named_parameters():
    if "classifier" not in name:
        param.requires_grad = False

2.4 ConvNeXt: CNNの近代化

ConvNeXtはTransformerの設計原則(大きなカーネル、LayerNorm、GELU、少ない活性化関数)をpure CNNに適用し、Swin Transformerと同等の性能を達成しました。

model = timm.create_model(
    "convnext_large",
    pretrained=True,
    num_classes=1000
)
# 7x7 depthwise conv、LayerNorm、GELU活性化を使用

3. 物体検出: YOLO、DETR、Faster R-CNN

3.1 検出アプローチの比較

アプローチモデル速度精度特徴
Anchor-based 2-stageFaster R-CNN遅い高いRPN+分類器の分離
Anchor-based 1-stageYOLOv8速い中〜高シングルパス推論
Anchor-free 1-stageYOLOv10、FCOS非常に速い高いNMS不要、アンカー不要
TransformerDETR中程度高いエンドツーエンド、関係モデリング

3.2 YOLOv8の実践的使用法

from ultralytics import YOLO

# 事前学習済みモデルの読み込み
model = YOLO("yolov8n.pt")   # nano: 速度優先
# model = YOLO("yolov8x.pt") # extra-large: 精度優先

# 単一画像の推論
results = model("image.jpg", conf=0.25, iou=0.45)
for result in results:
    for box in result.boxes:
        cls = int(box.cls[0])
        conf = float(box.conf[0])
        x1, y1, x2, y2 = box.xyxy[0].tolist()
        print(f"Class: {result.names[cls]}, Conf: {conf:.2f}")

# カスタムデータセットでのファインチューニング
model.train(
    data="custom.yaml",
    epochs=100,
    imgsz=640,
    batch=16,
    lr0=0.01,
    device=0
)

# 評価
metrics = model.val()
print(f"mAP50: {metrics.box.map50:.3f}")
print(f"mAP50-95: {metrics.box.map:.3f}")

3.3 YOLOv10: NMS不要の検出

YOLOv10はNon-Maximum Suppression(NMS)の後処理を排除し、**二重ラベル割り当て(dual label assignment)**と一貫性マッチングによってエンドツーエンドの学習を実現しました。

from ultralytics import YOLO

model = YOLO("yolov10n.pt")
# NMSなしで最終結果を直接返す — 低レイテンシ
results = model.predict("video.mp4", stream=True)
for frame_result in results:
    print(frame_result.boxes)

3.4 DETR: Detection Transformer

DETRは二部マッチングロスを用いて、アンカーとNMSなしでボックスの最終セットを直接予測します。

import torch
from transformers import DetrImageProcessor, DetrForObjectDetection
from PIL import Image

processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50")
model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50")

img = Image.open("image.jpg")
inputs = processor(images=img, return_tensors="pt")
outputs = model(**inputs)

target_sizes = torch.tensor([img.size[::-1]])
results = processor.post_process_object_detection(
    outputs, target_sizes=target_sizes, threshold=0.9
)[0]

for score, label, box in zip(
    results["scores"], results["labels"], results["boxes"]
):
    name = model.config.id2label[label.item()]
    print(f"{name}: {score:.3f} at {[round(i, 2) for i in box.tolist()]}")

4. セグメンテーション: DeepLab、Mask R-CNN、SAM

4.1 セグメンテーションの種類

  • セマンティック(Semantic): ピクセルごとにクラスラベルを付与(車、道路、空など)
  • インスタンス(Instance): 同じクラスの個別オブジェクトを区別(車1、車2など)
  • パノプティック(Panoptic): セマンティック+インスタンスの統合

4.2 SAM: Segment Anything Model

MetaのSAMは**プロンプト(点、ボックス、マスク)**を受け取り、任意のオブジェクトをゼロショットでセグメンテーションします。Image Encoder(ViT-H)、Prompt Encoder、Mask Decoderの3モジュール構造で、SA-1Bデータセット(10億マスク)で学習された汎用セグメンテーションモデルです。

import torch
import numpy as np
import cv2
from segment_anything import sam_model_registry, SamPredictor

sam = sam_model_registry["vit_h"](checkpoint="sam_vit_h_4b8939.pth")
sam.to("cuda")
predictor = SamPredictor(sam)

# 画像の設定
image = cv2.cvtColor(cv2.imread("image.jpg"), cv2.COLOR_BGR2RGB)
predictor.set_image(image)

# ポイントプロンプトによるセグメンテーション
input_point = np.array([[500, 375]])  # クリック位置 (x, y)
input_label = np.array([1])           # 1=前景、0=背景

masks, scores, logits = predictor.predict(
    point_coords=input_point,
    point_labels=input_label,
    multimask_output=True   # 複数のマスク候補を返す
)

best_mask = masks[scores.argmax()]
print(f"Mask shape: {best_mask.shape}, Score: {scores.max():.3f}")

# ボックスプロンプトによるセグメンテーション
input_box = np.array([100, 100, 400, 400])
masks_box, _, _ = predictor.predict(
    box=input_box[None, :],
    multimask_output=False
)

4.3 DeepLabV3+のセマンティックセグメンテーション

DeepLabV3+はASPP(Atrous Spatial Pyramid Pooling)でマルチスケールのコンテキスト情報を捉えます。

import torch
import torchvision.models.segmentation as seg_models

model = seg_models.deeplabv3_resnet101(
    weights=seg_models.DeepLabV3_ResNet101_Weights.COCO_WITH_VOC_LABELS_V1
)
model.eval()

with torch.no_grad():
    output = model(tensor.unsqueeze(0))["out"]  # [1, num_classes, H, W]
    pred = output.argmax(dim=1).squeeze()        # [H, W]
    print(f"セグメンテーションマップ shape: {pred.shape}")

4.4 Mask R-CNNのインスタンスセグメンテーション

from torchvision.models.detection import maskrcnn_resnet50_fpn_v2
from torchvision.models.detection import MaskRCNN_ResNet50_FPN_V2_Weights
from torchvision.utils import draw_segmentation_masks
import torchvision.transforms.functional as TF
from PIL import Image

weights = MaskRCNN_ResNet50_FPN_V2_Weights.DEFAULT
model = maskrcnn_resnet50_fpn_v2(weights=weights, box_score_thresh=0.75)
model.eval()

img = Image.open("people.jpg").convert("RGB")
inp = weights.transforms()(img).unsqueeze(0)

with torch.no_grad():
    predictions = model(inp)

pred = predictions[0]
masks = (pred["masks"] > 0.5).squeeze(1)
img_uint8 = (TF.to_tensor(img) * 255).byte()
result = draw_segmentation_masks(img_uint8, masks, alpha=0.5)

5. Vision Transformer: ViT、Swin、DINOv2

5.1 ViTの核心原理

ViT(Vision Transformer)は画像を**固定サイズのパッチ(16x16)**に分割し、各パッチをトークンとしてTransformerに入力します。CNNと異なり、局所的なinductive biasなしにグローバルな関係を学習します。

import timm
import torch

# ViT-Base/16のファインチューニング
model = timm.create_model(
    "vit_base_patch16_224",
    pretrained=True,
    num_classes=10,
    img_size=224
)

# ViTには強いデータ拡張 + AdamW + cosineスケジュールが効果的
data_config = timm.data.resolve_model_data_config(model)
transforms_train = timm.data.create_transform(**data_config, is_training=True)
transforms_val = timm.data.create_transform(**data_config, is_training=False)

optimizer = torch.optim.AdamW(
    model.parameters(), lr=1e-3, weight_decay=0.05
)
scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(
    optimizer, T_max=100
)

5.2 Swin Transformer: 階層的ViT

Swin Transformerは階層的特徴マップShifted Window AttentionでCNNの局所性をViTに導入しました。各ステージで解像度を半分にしてチャンネルを増やし、FPNとの互換性を保ちます。

モデル解像度パラメータImageNet Top-1
ViT-B/1622486M81.8%
Swin-T22428M81.3%
Swin-B22488M83.5%
Swin-L384197M87.3%
DINOv2-L518307M86.3%

5.3 DINOv2: 自己教師あり学習の頂点

DINOv2はラベルなしの大規模画像データで学習した汎用ビジョンエンコーダです。**自己教師あり学習(self-supervised learning)**でImageNetの教師あり学習モデルを凌駕します。

import torch
import torchvision.transforms as T

# DINOv2特徴抽出器(ファインチューニングなしで即使用可能)
dinov2 = torch.hub.load("facebookresearch/dinov2", "dinov2_vitl14")
dinov2.eval().cuda()

preprocess = T.Compose([
    T.Resize(518),
    T.CenterCrop(518),
    T.ToTensor(),
    T.Normalize(mean=[0.485, 0.456, 0.406],
                std=[0.229, 0.224, 0.225]),
])

with torch.no_grad():
    # [B, 3, 518, 518] -> [B, 1024] 特徴
    features = dinov2(preprocess(img).unsqueeze(0).cuda())
    print(f"Feature dim: {features.shape}")  # [1, 1024]

# DINOv2はk-NNベースの分類器でも優れた性能を示す

6. 生成モデル: GAN、Diffusion、ControlNet

6.1 生成モデルの比較

生成モデル代表モデル学習方式長所短所
GANStyleGAN3敵対的学習高速生成学習不安定、モード崩壊
VAEVQ-VAE-2再構成+KL安定した学習ぼやけた画像
DiffusionDDPM、DDIMノイズ除去最高品質生成が遅い
LDMStable Diffusion潜在空間拡散品質+速度のバランス高いGPUメモリ

6.2 Stable Diffusion: 潜在拡散モデル

Stable Diffusionは**潜在拡散モデル(Latent Diffusion Model: LDM)**です。U-Netが潜在空間でノイズを段階的に除去します。

  • 順方向プロセス: 画像にガウシアンノイズをTステップにわたって追加
  • 逆方向プロセス: U-Netがnoisy latent z_t、timestep t、テキスト埋め込みを受け取りノイズ成分epsilonを予測
  • VAEデコーダー: 最終的な潜在ベクトルをピクセル空間に復元
from diffusers import StableDiffusionPipeline, DPMSolverMultistepScheduler
import torch

# SDXL 1.0 テキストから画像への生成
pipe = StableDiffusionPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0",
    torch_dtype=torch.float16,
    use_safetensors=True,
)
pipe.scheduler = DPMSolverMultistepScheduler.from_config(
    pipe.scheduler.config
)
pipe.to("cuda")

image = pipe(
    prompt="a photorealistic cat on a desk, 8k, studio lighting",
    negative_prompt="blurry, low quality, cartoon",
    num_inference_steps=25,
    guidance_scale=7.5,
    width=1024,
    height=1024
).images[0]
image.save("generated.png")

6.3 ControlNet: 構造条件付き生成

ControlNetはエッジマップ、深度マップ、ポーズなどの構造的条件で画像生成を精密に制御します。

from diffusers import StableDiffusionControlNetPipeline, ControlNetModel
from diffusers.utils import load_image
import cv2
import numpy as np
from PIL import Image

controlnet = ControlNetModel.from_pretrained(
    "lllyasviel/sd-controlnet-canny",
    torch_dtype=torch.float16
)
pipe = StableDiffusionControlNetPipeline.from_pretrained(
    "runwayml/stable-diffusion-v1-5",
    controlnet=controlnet,
    torch_dtype=torch.float16
).to("cuda")

# Cannyエッジマップの生成
ref_image = np.array(load_image("reference.jpg"))
edges = cv2.Canny(ref_image, 100, 200)
edges_rgb = np.stack([edges] * 3, axis=-1)

result = pipe(
    "a beautiful landscape painting",
    image=Image.fromarray(edges_rgb),
    num_inference_steps=20
).images[0]
result.save("controlnet_output.png")

7. 実践パイプライン: DataLoaderからTensorRTまで

7.1 カスタムDatasetとDataLoader

from torch.utils.data import Dataset, DataLoader
from pathlib import Path
import albumentations as A
from albumentations.pytorch import ToTensorV2
import cv2

class CustomDataset(Dataset):
    def __init__(self, root: str, split: str = "train"):
        self.root = Path(root)
        self.image_paths = list(
            (self.root / split / "images").glob("*.jpg")
        )
        self.label_paths = [
            self.root / split / "labels" / p.with_suffix(".txt").name
            for p in self.image_paths
        ]
        self.transform = self._get_transforms(split)

    def _get_transforms(self, split: str):
        if split == "train":
            return A.Compose([
                A.RandomResizedCrop(224, 224, scale=(0.7, 1.0)),
                A.HorizontalFlip(p=0.5),
                A.ColorJitter(brightness=0.4, contrast=0.4,
                              saturation=0.4, hue=0.1, p=0.8),
                A.GaussianBlur(blur_limit=(3, 7), p=0.2),
                A.Normalize(mean=(0.485, 0.456, 0.406),
                            std=(0.229, 0.224, 0.225)),
                ToTensorV2(),
            ])
        return A.Compose([
            A.Resize(256, 256),
            A.CenterCrop(224, 224),
            A.Normalize(mean=(0.485, 0.456, 0.406),
                        std=(0.229, 0.224, 0.225)),
            ToTensorV2(),
        ])

    def __len__(self):
        return len(self.image_paths)

    def __getitem__(self, idx):
        image = cv2.cvtColor(
            cv2.imread(str(self.image_paths[idx])), cv2.COLOR_BGR2RGB
        )
        label = int(self.label_paths[idx].read_text().strip())
        augmented = self.transform(image=image)
        return augmented["image"], label

train_dataset = CustomDataset("data/", split="train")
train_loader = DataLoader(
    train_dataset, batch_size=32, shuffle=True,
    num_workers=4, pin_memory=True, persistent_workers=True
)

7.2 ONNXエクスポート

import torch
import torch.onnx

model.eval()
dummy_input = torch.randn(1, 3, 224, 224, device="cuda")

torch.onnx.export(
    model,
    dummy_input,
    "model.onnx",
    export_params=True,
    opset_version=17,
    do_constant_folding=True,
    input_names=["input"],
    output_names=["output"],
    dynamic_axes={
        "input":  {0: "batch_size"},
        "output": {0: "batch_size"}
    }
)
print("ONNXエクスポート完了: model.onnx")

# ONNX Runtimeで検証
import onnxruntime as ort
import numpy as np

sess = ort.InferenceSession(
    "model.onnx", providers=["CUDAExecutionProvider"]
)
input_name = sess.get_inputs()[0].name
result = sess.run(None, {input_name: dummy_input.cpu().numpy()})
print(f"ONNX推論結果のshape: {result[0].shape}")

7.3 TensorRT最適化

# trtexecを使ったTensorRT変換
trtexec \
  --onnx=model.onnx \
  --saveEngine=model_fp16.engine \
  --fp16 \
  --workspace=4096 \
  --optShapes=input:8x3x224x224
import tensorrt as trt
import numpy as np
import pycuda.driver as cuda
import pycuda.autoinit

def trt_inference(engine_path: str, input_data: np.ndarray) -> np.ndarray:
    logger = trt.Logger(trt.Logger.WARNING)
    with open(engine_path, "rb") as f:
        engine = trt.Runtime(logger).deserialize_cuda_engine(f.read())
    context = engine.create_execution_context()

    input_mem = cuda.mem_alloc(input_data.nbytes)
    output = np.empty((input_data.shape[0], 1000), dtype=np.float32)
    output_mem = cuda.mem_alloc(output.nbytes)

    cuda.memcpy_htod(input_mem, input_data)
    context.execute_v2([int(input_mem), int(output_mem)])
    cuda.memcpy_dtoh(output, output_mem)
    return output

クイズ: コンピュータービジョンの深い理解

Q1. ResNetのスキップ接続が勾配消失問題を解決する仕組みとは?

答え: 逆伝播時に勾配がスキップ接続を経由して直接伝達されるため、深いレイヤーでも消失することなく流れます。

解説: 一般的な深いニューラルネットワークでは、逆伝播の勾配はレイヤーを通過するごとに乗算で小さくなります。ResNetの残差ブロックF(x) + xを微分するとdF/dx + 1となり、最小勾配1が常に保証されます。これにより100層以上でも安定した学習が可能になります。

Q2. YOLOがFaster R-CNNよりリアルタイム推論に適している理由とは?

答え: YOLOは単一パス(single forward pass)で検出を完了しますが、Faster R-CNNはRegion Proposal Networkと分類器の2段階が必要です。

解説: Faster R-CNNは(1)RPNによる候補領域の生成、(2)RoI Pooling、(3)分類と回帰という多段構造です。YOLOは画像をグリッドに分割し、1回のCNNパスで全ボックスとクラスを同時に予測します。YOLOv8nはA100 GPUで80+ FPSを達成できます。

Q3. Vision TransformerがCNNよりも大規模データで優れた性能を発揮する理由とは?

答え: ViTのSelf-Attentionは全パッチ間のグローバルな関係を学習し、inductive biasなしにデータから直接最適な表現を習得します。

解説: CNNは局所性(locality)と並進等変性(translation equivariance)というinductive biasを内蔵しています。少ないデータではこのバイアスが役立ちますが、大規模データ(JFT-300Mなど)では表現力を制限します。ViTはバイアスなしにグローバルなパターンを自由に学習できるため、データが十分にある場合にCNNを凌駕します。

Q4. Stable DiffusionのDenoisingプロセスにおけるU-Netの役割とは?

答え: U-Netは各タイムステップで潜在ベクトルに追加されたノイズを予測して除去し、テキスト条件(CLIP埋め込み)をcross-attentionで統合します。

解説: 順方向プロセス(forward process)では画像にガウシアンノイズをTステップにわたって追加します。逆方向プロセス(reverse process)ではU-Netがnoisy latent z_t、タイムステップt、テキスト埋め込みを入力としてノイズ成分epsilonを予測します。VAEデコーダーが最終的な潜在ベクトルをピクセル空間に復元します。

Q5. SAMのプロンプトベースのセグメンテーションが従来の方法と異なる点とは?

答え: SAMは点、ボックス、マスクなど様々なプロンプトを受け取り、追加学習なしに任意のオブジェクトをゼロショットでセグメンテーションします。

解説: 従来のセグメンテーションモデル(DeepLab、Mask R-CNN)は特定のクラス集合に対して教師あり学習されます。SAMはSA-1Bデータセット(10億マスク)で学習された汎用モデルで、クラスに関わらずユーザーが指定した領域を分離します。Image Encoder(ViT-H)、Prompt Encoder、Mask Decoderの3モジュール構造を持ちます。


まとめ: 学習ロードマップ

コンピュータービジョンは急速に発展する分野です。推奨される学習経路:

  1. 基礎: OpenCV、NumPyによる画像処理 → torchvisionの実践
  2. 分類: ResNet/EfficientNetのファインチューニング → カスタムデータセット
  3. 検出: YOLOv8の実験 → カスタム学習 → ONNX/TensorRTでのデプロイ
  4. セグメンテーション: SAMの実験 → Mask R-CNN/DeepLabV3+のカスタム学習
  5. 上級: ViT/DINOv2による特徴抽出 → Stable Diffusionのファインチューニング

各ステップでKaggleコンペティション実際のプロジェクトに応用することが、最も効果的な学習方法です。コンピュータービジョンの世界へようこそ!