Asosiy qismga o'tish

Arxitektura: yolo11.yaml qatorma-qator

4-bob 13 kod bloki 4377 so'z

Bu bobning kodi: kod/04_arxitektura.py — kitob papkasidan ishga tushiring: python kod/04_arxitektura.py

Model — bu bitta yaml fayl

YOLO11 ning butun tuzilishi Python kodida emas, 50 qatorli matn faylida yozilgan. Ultralytics shu faylni o'qib, tarmoqni qurib beradi. Fayl ultralytics paketining ichida turibdi:

kod/04_arxitektura.pyyangi fayl · 1-qator: from kitob_yordam import fig, show
python21 qator
import inspect
import math
import re
from pathlib import Path

import numpy as np
import pandas as pd
import torch
import yaml
import ultralytics
from ultralytics import YOLO
from ultralytics.utils import ASSETS, LOGGER
from ultralytics.utils.torch_utils import get_flops, get_num_params

LOGGER.setLevel("ERROR")                      # model qurilganda chiqadigan jadval va log larni o'chiramiz

papka = Path(ultralytics.__file__).parent / "cfg" / "models"
yaml_yol = papka / "11" / "yolo11.yaml"
matn = yaml_yol.read_text()
print(yaml_yol.relative_to(Path(ultralytics.__file__).parent.parent), "\n")
print(matn[matn.index("scales:"):matn.index("# YOLO11n head")].rstrip())
Chiqish
ultralytics/cfg/models/11/yolo11.yaml 

scales: # model compound scaling constants, i.e. 'model=yolo11n.yaml' will call yolo11.yaml with scale 'n'
  # [depth, width, max_channels]
  n: [0.50, 0.25, 1024] # summary: 181 layers, 2624080 parameters, 2624064 gradients, 6.6 GFLOPs
  s: [0.50, 0.50, 1024] # summary: 181 layers, 9458752 parameters, 9458736 gradients, 21.7 GFLOPs
  m: [0.50, 1.00, 512] # summary: 231 layers, 20114688 parameters, 20114672 gradients, 68.5 GFLOPs
  l: [1.00, 1.00, 512] # summary: 357 layers, 25372160 parameters, 25372144 gradients, 87.6 GFLOPs
  x: [1.00, 1.50, 512] # summary: 357 layers, 56966176 parameters, 56966160 gradients, 196.0 GFLOPs

# YOLO11n backbone
backbone:
  # [from, repeats, module, args]
  - [-1, 1, Conv, [64, 3, 2]] # 0-P1/2
  - [-1, 1, Conv, [128, 3, 2]] # 1-P2/4
  - [-1, 2, C3k2, [256, False, 0.25]]
  - [-1, 1, Conv, [256, 3, 2]] # 3-P3/8
  - [-1, 2, C3k2, [512, False, 0.25]]
  - [-1, 1, Conv, [512, 3, 2]] # 5-P4/16
  - [-1, 2, C3k2, [512, True]]
  - [-1, 1, Conv, [1024, 3, 2]] # 7-P5/32
  - [-1, 2, C3k2, [1024, True]]
  - [-1, 1, SPPF, [1024, 5]] # 9
  - [-1, 2, C2PSA, [1024]] # 10

Har bir qatlam qatori to'rtta narsa: [from, repeats, module, args].

Ustun Ma'nosi Misol
from kirish qaysi qatlamdan keladi; −1 = oldingi qatlam [-1, 6] — oldingi va 6-qatlam
repeats blok ichida necha marta takrorlanadi 2
module blok turi (ultralytics/nn/modules/ dagi klass) C3k2
args blok parametrlari; birinchisi — chiqish kanallari [256, False, 0.25]

Izohlardagi P1/2 ... P5/32 — qatlam chiqishi asl rasmdan necha barobar kichik: P3/8 — 8 barobar (640 → 80). 3-bobda shu o'lchamlarni formulaga solib tekshirgan edik.

Bitta fayl, besh model: depth va width

Faylda kanallar "katta model" uchun yozilgan: birinchi Conv da 64 kanal. Lekin 3-bobda yolo11n ning birinchi qatlamida 16 kanal ko'rdik. Farq scales qatoridan keladi: n: [0.50, 0.25, 1024] — depth (chuqurlik) koeffitsiyenti 0.5, width (kenglik) 0.25, kanal chegarasi 1024. Ultralytics ning nn/tasks.py faylidagi parse_model funksiyasi ularni shunday qo'llaydi:

Kanallar 8 ga karrali qilib yaxlitlanadi — GPU hisobi shunday tezroq. 64 · 0.25 = 16. Formulani har qatlam uchun hisoblab, haqiqiy model bilan solishtiramiz:

kod/04_arxitektura.pydavomi
python24 qator
def kanal(c, w, cmax):
    """parse_model dagi qoida: chegaralash, kenglikka ko'paytirish, 8 ga karrali qilib yuqoriga yaxlitlash."""
    return math.ceil(min(c, cmax) * w / 8) * 8

model = YOLO("weights/yolo11n.pt")
tarmoq = model.model.eval()
shakllar = {}
ilgaklar = [q.register_forward_hook(lambda m, i, o, k=k: shakllar.__setitem__(k, tuple(o.shape[1:])))
            for k, q in enumerate(tarmoq.model[:23])]
with torch.no_grad():
    tarmoq(torch.zeros(1, 3, 640, 640))
for h in ilgaklar:
    h.remove()

cfg = yaml.safe_load(matn)
d, w, cmax = cfg["scales"]["n"]
qatorlar = []
for i, (f, n, mod, args) in enumerate(cfg["backbone"] + cfg["head"]):
    if mod in ("Conv", "C3k2", "SPPF", "C2PSA"):
        qatorlar.append({"qatlam": i, "modul": mod, "yaml: kanal": args[0], "yaml: takror": n,
                         "formula: kanal": kanal(args[0], w, cmax), "formula: takror": max(round(n * d), 1),
                         "haqiqiy kanal": shakllar[i][0],
                         "haqiqiy takror": len(tarmoq.model[i].m) if mod in ("C3k2", "C2PSA") else n})
show(pd.DataFrame(qatorlar), 20)
qatlammodulyaml: kanalyaml: takrorformula: kanalformula: takrorhaqiqiy kanalhaqiqiy takror
0Conv641161161
1Conv1281321321
2C3k22562641641
3Conv2561641641
4C3k2512212811281
5Conv512112811281
6C3k2512212811281
7Conv1 024125612561
8C3k21 024225612561
9SPPF1 024125612561
10C2PSA1 024225612561
13C3k2512212811281
16C3k22562641641
17Conv2561641641
19C3k2512212811281
20Conv512112811281
22C3k21 024225612561

Formula har qatorda haqiqiy model bilan bir xil. Endi beshala o'lchamni og'irliksiz, faqat yaml dan quramiz:

kod/04_arxitektura.pydavomi
python7 qator
qatorlar = []
for s in "nsmlx":
    m = YOLO(f"yolo11{s}.yaml").model             # og'irliksiz: tasodifiy vaznlar, faqat tuzilish
    qatorlar.append({"model": f"yolo11{s}", "[depth, width, max_kanal]": str(cfg["scales"][s]),
                     "parametr, mln": get_num_params(m) / 1e6, "GFLOPs": get_flops(m, 640),
                     "2-qatlam ichida": type(m.model[2].m[0]).__name__})
show(pd.DataFrame(qatorlar))
model[depth, width, max_kanal]parametr, mlnGFLOPs2-qatlam ichida
yolo11n[0.5, 0.25, 1024]2.6246.614Bottleneck
yolo11s[0.5, 0.5, 1024]9.45921.718Bottleneck
yolo11m[0.5, 1.0, 512]20.11568.528C3k
yolo11l[1.0, 1.0, 512]25.37287.613C3k
yolo11x[1.0, 1.5, 512]56.966195.959C3k

Parametrlar va GFLOPs yaml izohlaridagi raqamlarga aynan mos keladi. Oxirgi ustunga qarang: n va s da 2-qatlam ichida oddiy Bottleneck, m, l, x da esa og'irroq C3k. Buni ham parse_model qiladi: if scale in "mlx": c3k = True. Ya'ni katta modellar faqat kengroq va chuqurroq emas, ba'zi bloklari ham boshqacha.

C3k2: ikki yo'l va qisqa tutashuv

C3k2 — YOLO11 ning asosiy "ishchi" bloki, 23 ta qatlamdan 8 tasi shu. U YOLOv8 dagi C2f dan meros oladi va forward funksiyasini o'zgarishsiz ishlatadi. Kodning o'zi (ultralytics/nn/modules/block.py):

kod/04_arxitektura.pydavomi
python9 qator
from ultralytics.nn.modules.block import Bottleneck, C2f, C3k2

def manba(f):
    """Funksiya kodini docstring siz ko'rsatadi."""
    return re.sub(r'\n\s*"""[\s\S]*?"""', "", inspect.getsource(f))

print("C3k2 ning ota klassi:", C3k2.__mro__[1].__name__, "\n")
print(manba(C2f.forward))
print(manba(Bottleneck.forward))
Chiqish
C3k2 ning ota klassi: C2f 

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        y = list(self.cv1(x).chunk(2, 1))
        y.extend(m(y[-1]) for m in self.m)
        return self.cv2(torch.cat(y, 1))

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))

Qatorma-qator:

  1. cv1(x).chunk(2, 1) — 1 × 1 konvolyutsiya kanallarni ikki qismga bo'ladi.
  2. Birinchi qism hech narsaga tegmasdan o'tadi. Ikkinchisi Bottleneck lardan o'tadi, va har birining natijasi ro'yxatga qo'shiladi.
  3. cv2(torch.cat(y, 1)) — hamma qismlar kanal bo'yicha yopishtiriladi va 1 × 1 konvolyutsiya bilan aralashtiriladi.

Bottleneck ning o'zi — ikki 3 × 3 konvolyutsiya va qisqa tutashuv (residual, shortcut):

Blok natijani noldan emas, kirishga "tuzatish" sifatida hisoblaydi. O'qitishda gradient x + yo'li orqali to'g'ridan-to'g'ri oldingi qatlamlarga o'tadi — chuqur tarmoq shu sababli o'qitiladi (ResNet g'oyasi, CV_KITOBI 4-bob). Bo'linish esa hisobni tejaydi: kanallarning yarmi og'ir qismga umuman kirmaydi.

SPPF: uchta max-pool — uch xil o'lcham

9-qatlam. Kodi:

kod/04_arxitektura.pydavomi
python6 qator
from ultralytics.nn.modules.block import SPPF

print(manba(SPPF.forward))
sppf = tarmoq.model[9]
print("max-pool:", sppf.m, "| necha marta:", getattr(sppf, "n", 3))   # eski .pt faylda n maydoni yo'q
print("yolo11n.pt dagi SPPF da 'n' maydoni bormi:", hasattr(sppf, "n"))
Chiqish
    def forward(self, x: torch.Tensor) -> torch.Tensor:
        y = [self.cv1(x)]
        y.extend(self.m(y[-1]) for _ in range(getattr(self, "n", 3)))
        y = self.cv2(torch.cat(y, 1))
        return y + x if getattr(self, "add", False) else y

max-pool: MaxPool2d(kernel_size=5, stride=1, padding=2, dilation=1, ceil_mode=False) | necha marta: 3
yolo11n.pt dagi SPPF da 'n' maydoni bormi: False

Kodda getattr(self, "n", 3) bejiz emas: yolo11n.pt fayli n parametri qo'shilishidan oldingi versiyada saqlangan va undagi SPPF obyektida bu maydon yo'q. Kutubxona eski fayllar ham ishlashi uchun standart qiymatni (3) oladi. Real loyihalarda ham shunday: model fayli kutubxona versiyasidan eski bo'lishi mumkin.

Bir xil 5 × 5 max-pool ketma-ket uch marta qo'llanadi va to'rtala natija (asl + 3 ta) yopishtiriladi. Nega? YOLOv4 davridagi asl SPP bloki bitta kirishga 5 × 5, 9 × 9 va 13 × 13 lik uchta alohida max-pool qo'llardi — uch xil o'lchamdagi "atrofga qarash". 5 × 5 ni ikki marta qo'llash 9 × 9 bilan, uch marta qo'llash 13 × 13 bilan aynan bir xil natija beradi. Tekshiramiz:

kod/04_arxitektura.pydavomi
python8 qator
x = torch.randn(1, 128, 20, 20)
katta = lambda k: torch.nn.functional.max_pool2d(x, k, stride=1, padding=k // 2)   # asl SPP: bitta katta oyna
y1 = sppf.m(x)
y2 = sppf.m(y1)
y3 = sppf.m(y2)
print("5x5 bir marta  vs 5x5  :", float((y1 - katta(5)).abs().max()))
print("5x5 ikki marta vs 9x9  :", float((y2 - katta(9)).abs().max()))
print("5x5 uch marta  vs 13x13:", float((y3 - katta(13)).abs().max()))
Chiqish
5x5 bir marta  vs 5x5  : 0.0
5x5 ikki marta vs 9x9  : 0.0
5x5 uch marta  vs 13x13: 0.0

Farq aynan nol. Lekin hisob kamroq: har nuqtada 5 × 5 oyna 25 ta taqqoslash, uch marta — 75 ta. Alohida 5, 9, 13 lik oynalar esa 25 + 81 + 169 = 275 ta taqqoslash. "F" (fast) — shundan. 3-bobda SPPF dan keyin katakning ko'rish maydoni birdan butun rasmga yetganini ko'rgan edik: 13 × 13 oyna 20 × 20 lik to'rda, har katak 32 piksel bo'lsa, 400 dan ortiq pikselni qamraydi.

C2PSA: attention

10-qatlam — YOLO11 ga yangi qo'shilgan blok. Ichida transformerlardagi attention bor: har bir katak qolgan hamma kataklarga "qaraydi" va qaysi biri o'zi uchun muhimligini hal qiladi:

N = 20 · 20 = 400 ta katak. Har katak uchun uchta vektor hisoblanadi: Q (query — "men nima qidiryapman"), K (key — "menda nima bor") va V (value — "men beradigan ma'lumot"). QKᵀ — 400 × 400 lik jadval: har juftlik qanchalik mos. softmax har qatorni 1 ga yig'iladigan og'irliklarga aylantiradi. √d_k ga bo'lish qiymatlarni juda katta bo'lib ketishdan saqlaydi. Ultralytics dagi kod:

kod/04_arxitektura.pydavomi
python6 qator
from ultralytics.nn.modules.block import Attention

print(manba(Attention.forward))
att = tarmoq.model[10].m[0].attn
print(f"boshlar (heads): {att.num_heads}, d_k (key_dim): {att.key_dim}, d_v (head_dim): {att.head_dim}, "
      f"1/sqrt(d_k) = {att.scale:.4f}")
Chiqish
    def forward(self, x: torch.Tensor) -> torch.Tensor:
        B, C, H, W = x.shape
        N = H * W
        qkv = self.qkv(x)
        q, k, v = qkv.view(B, self.num_heads, self.key_dim * 2 + self.head_dim, N).split(
            [self.key_dim, self.key_dim, self.head_dim], dim=2
        )

        attn = (q.transpose(-2, -1) @ k) * self.scale
        attn = attn.softmax(dim=-1)
        x = (v @ attn.transpose(-2, -1)).view(B, C, H, W) + self.pe(v.reshape(B, C, H, W))
        x = self.proj(x)
        return x

boshlar (heads): 2, d_k (key_dim): 32, d_v (head_dim): 64, 1/sqrt(d_k) = 0.1768

Formulani haqiqiy qatlam ustida qo'lda qayta hisoblab tekshiramiz. Kirish — 3-bobdagi kabi bus.jpg:

kod/04_arxitektura.pydavomi
python23 qator
import cv2

rasm = cv2.resize(cv2.imread(str(ASSETS / "bus.jpg")), (640, 640))[:, :, ::-1].copy()   # BGR -> RGB
xin = torch.from_numpy(rasm).permute(2, 0, 1)[None].float() / 255

saqla = {}
h = att.register_forward_hook(lambda m, i, o: saqla.update(kirish=i[0], chiqish=o))
with torch.no_grad():
    tarmoq(xin)
h.remove()

a = saqla["kirish"]                                              # (1, 128, 20, 20)
B, C, H, W = a.shape
N = H * W
with torch.no_grad():
    qkv = att.qkv(a).view(B, att.num_heads, 2 * att.key_dim + att.head_dim, N)
    q, k, v = qkv.split([att.key_dim, att.key_dim, att.head_dim], dim=2)   # har boshga Q, K, V
    A = torch.softmax(torch.einsum("bhdn,bhdm->bhnm", q, k) * att.scale, dim=-1)   # (1, boshlar, N, N)
    y = torch.einsum("bhdm,bhnm->bhdn", v, A).reshape(B, C, H, W)                # og'irlangan V yig'indisi
    y = att.proj(y + att.pe(v.reshape(B, C, H, W)))              # pe: joylashuv ma'lumoti (3x3 depthwise conv)
print("attention jadvali shakli:", tuple(A.shape))
print("har qator yig'indisi (birinchi 3 ta):", A[0, 0, :3].sum(-1).numpy().round(4))
print("qo'lda hisob vs qatlam chiqishi, eng katta farq:", float((y - saqla["chiqish"]).abs().max()))
Chiqish
attention jadvali shakli: (1, 2, 400, 400)
har qator yig'indisi (birinchi 3 ta): [          1           1           1]
qo'lda hisob vs qatlam chiqishi, eng katta farq: 0.0

Ikki katak nimaga "qarashini" ko'raylik: bitta odam turgan katak va bino devoridagi katak.

kod/04_arxitektura.pydavomi
python18 qator
import matplotlib.pyplot as plt

natija = YOLO("weights/yolo11n.pt").predict(ASSETS / "bus.jpg", verbose=False)[0]   # alohida nusxa: predict
                                                                  # tarmoqni fuse qiladi, bizniki o'zgarmasin (3-bob)
xc, yc = natija.boxes.xywh[3, :2].tolist()                        # 1-bobdagi o'rtadagi odam (ishonchi 0.86)
H0, W0 = natija.orig_shape
odam = (int(yc / H0 * 20), int(xc / W0 * 20))                     # 20x20 to'rdagi (qator, ustun)
bino = (2, 15)                                                    # yuqori o'ng: bino devori
_, axes = plt.subplots(2, 2, figsize=(8.5, 8.5))
for qator, (nom, (r, c)) in zip(axes, [("odam", odam), ("bino", bino)]):
    for bosh, ax in enumerate(qator):
        xarita = A[0, bosh, r * 20 + c].reshape(20, 20).numpy()
        ax.imshow(rasm, alpha=0.55)
        ax.imshow(xarita, cmap="inferno", alpha=0.6, extent=(0, 640, 640, 0), interpolation="nearest")
        ax.add_patch(plt.Rectangle((c * 32, r * 32), 32, 32, fill=False, color="cyan", lw=2.5))
        ax.set_title(f"{nom} katagi {r, c}, {bosh}-bosh", fontsize=10)
        ax.axis("off")
fig("attention.png", "Moviy ramka — so'rovchi katak. Yorug' joylar — u eng ko'p e'tibor bergan kataklar.")

Moviy ramka — so'rovchi katak. Yorug' joylar — u eng ko'p e'tibor bergan kataklar.

Moviy ramka — so'rovchi katak. Yorug' joylar — u eng ko'p e'tibor bergan kataklar.

Ikki bosh ikki xil ishlaydi. 0-bosh asosan yaqin va o'xshash joyga qaraydi: odam katagi — shu odamning o'zidagi yuqori va pastki kataklarga, bino katagi — bino devorining boshqa kataklariga. 1-bosh butun rasm bo'ylab tarqalgan: odam katagi boshqa odamlar turgan joylarga ham, masalan o'ng chetdagi odamning oyoqlariga qaraydi. Bu vazifalarni hech kim belgilamagan — tarmoq o'qitish davomida o'zi shunday taqsimlagan. Konvolyutsiya faqat qo'shnilarni ko'radi, attention esa bitta qadamda rasmning ikki uchini bog'laydi.

Attention nega faqat 10-qatlamda, eng kichik to'rda? Chunki jadval N × N:

kod/04_arxitektura.pydavomi
python3 qator
for tor in (20, 40, 80):
    n = tor * tor
    print(f"{tor}x{tor} to'r: N = {n:5d}, attention jadvali N x N = {n * n:>12,} ta son (bitta bosh uchun)")
Chiqish
20x20 to'r: N =   400, attention jadvali N x N =      160,000 ta son (bitta bosh uchun)
40x40 to'r: N =  1600, attention jadvali N x N =    2,560,000 ta son (bitta bosh uchun)
80x80 to'r: N =  6400, attention jadvali N x N =   40,960,000 ta son (bitta bosh uchun)

Kataklar soni 16 barobar oshsa, jadval 16² = 256 barobar kattalashadi: 80 × 80 to'rda bitta bosh uchun 41 millionga yaqin son. Shuning uchun attention eng qo'pol to'rda, arzon joyda qo'llanadi, va u yerdan global ma'lumot neck orqali boshqa to'rlarga tarqatiladi.

Neck: FPN va PAN

Backbone ning chuqur qatlamlari "nima" ni yaxshi biladi (20 × 20 to'rda katta ko'rish maydoni, attention), lekin qo'pol: har katak 32 piksel. Sayoz qatlamlar aniq joyni biladi (80 × 80, har katak 8 piksel), lekin "tushunishi" kam. Neck ikkalasini aralashtiradi:

  • Yuqoridan pastga (FPN, Lin va boshq., 2017): 20 × 20 dagi boy ma'lumot 2 barobar kattalashtirilib (Upsample), 40 × 40 va keyin 80 × 80 dagi backbone chiqishi bilan yopishtiriladi (Concat).
  • Pastdan yuqoriga (PAN, Liu va boshq., 2018): 80 × 80 dagi aniq joy ma'lumoti qadam-2 li Conv bilan qaytadan 40 × 40 va 20 × 20 ga olib chiqiladi.
BackboneNeck: yuqoridan pastga (FPN)Neck: pastdan yuqoriga (PAN)Detect0–2: Conv, Conv, C3k264 × 160 × 1603–4: Conv, C3k2128 × 80 × 80 (P3)5–6: Conv, C3k2128 × 40 × 40 (P4)7–10: Conv, C3k2, SPPF, C2PSA256 × 20 × 20 (P5)14–16: Upsample, Concat, C3k264 × 80 × 8011–13: Upsample, Concat, C3k2128 × 40 × 4017–19: Conv, Concat, C3k2128 × 40 × 4020–22: Conv, Concat, C3k2256 × 20 × 20P3 80²P4 40²P5 20²

Kanallar × balandlik × kenglik — yolo11n uchun, 640 × 640 kirishda (o'lchamlar yuqoridagi ilgaklardan). Gorizontal strelkalar — Concat bilan yopishtiriladigan yon ulanishlar.

Neck qatlamlarining haqiqiy ulanishlari va shakllari:

kod/04_arxitektura.pydavomi
python8 qator
qatorlar = []
for k in range(11, 23):
    q = tarmoq.model[k]
    qatorlar.append({"qatlam": k, "turi": type(q).__name__, "from": str(q.f),
                     "kirish shakllari": " + ".join(str(shakllar[j if j >= 0 else k - 1]) for j in (q.f if isinstance(q.f, list) else [q.f])),
                     "chiqish (C, H, W)": str(shakllar[k])})
show(pd.DataFrame(qatorlar), 12)
print("Detect kirishlari (from):", tarmoq.model[23].f)
Chiqish
Detect kirishlari (from): [16, 19, 22]
qatlamturifromkirish shakllarichiqish (C, H, W)
11Upsample-1(256, 20, 20)(256, 40, 40)
12Concat[-1, 6](256, 40, 40) + (128, 40, 40)(384, 40, 40)
13C3k2-1(384, 40, 40)(128, 40, 40)
14Upsample-1(128, 40, 40)(128, 80, 80)
15Concat[-1, 4](128, 80, 80) + (128, 80, 80)(256, 80, 80)
16C3k2-1(256, 80, 80)(64, 80, 80)
17Conv-1(64, 80, 80)(64, 40, 40)
18Concat[-1, 13](64, 40, 40) + (128, 40, 40)(192, 40, 40)
19C3k2-1(192, 40, 40)(128, 40, 40)
20Conv-1(128, 40, 40)(128, 20, 20)
21Concat[-1, 10](128, 20, 20) + (256, 20, 20)(384, 20, 20)
22C3k2-1(384, 20, 20)(256, 20, 20)

Concat kanallarni qo'shadi: 12-qatlamda 256 (kattalashtirilgan 10-qatlam) + 128 (backbone ning 6-qatlami) = 384. Detect ga uchta xarita boradi: 16 (80 × 80, kichik obyektlar), 19 (40 × 40) va 22 (20 × 20, katta obyektlar). 80 × 80 xaritaga yetib kelgan ma'lumot endi faqat sayoz qatlamdan emas: unda 10-qatlamdagi attention va SPPF ning global ko'rinishi ham bor.

Parametrlar qayerda

kod/04_arxitektura.pydavomi
python8 qator
qismlar = {"backbone (0–10)": range(0, 11), "neck (11–22)": range(11, 23), "head: Detect (23)": range(23, 24)}
jami = sum(p.numel() for p in tarmoq.parameters())
qatorlar = []
for nom, oraliq in qismlar.items():
    p = sum(t.numel() for i in oraliq for t in tarmoq.model[i].parameters())
    qatorlar.append({"qism": nom, "parametr": p, "ulush, %": round(p / jami * 100, 1)})
qatorlar.append({"qism": "jami", "parametr": jami, "ulush, %": 100.0})
show(pd.DataFrame(qatorlar))
qismparametrulush, %
backbone (0–10)1 365 47252
neck (11–22)793 69630.2
head: Detect (23)464 91217.7
jami2 624 080100

Parametrlarning yarmi backbone da. Detect esa bitta qatlam bo'lsa ham, har oltinchi parametr unda. Uning ichini 5-bobda ochamiz.

YOLO26 da nima o'zgargan

2-bobda YOLO26 ning tanasi YOLO11 bilan bir xil bloklardan qurilganini aytgan edik. Ikki yaml faylni qatlamma-qatlam solishtiramiz (izohlarsiz):

kod/04_arxitektura.pydavomi
python10 qator
import difflib

def qatlamlar(yol):
    """yaml dan faqat qatlam qatorlari va bosh sozlamalari, izohlarsiz."""
    return [re.sub(r"\s*#.*", "", l).rstrip() for l in yol.read_text().splitlines()
            if l.strip().startswith(("- [", "end2end", "reg_max"))]

farq = difflib.unified_diff(qatlamlar(papka / "11" / "yolo11.yaml"), qatlamlar(papka / "26" / "yolo26.yaml"),
                            "yolo11.yaml", "yolo26.yaml", lineterm="", n=0)
print("\n".join(farq))
Chiqish
--- yolo11.yaml
+++ yolo26.yaml
@@ -0,0 +1,2 @@
+end2end: True
+reg_max: 1
@@ -10 +12 @@
-  - [-1, 1, SPPF, [1024, 5]]
+  - [-1, 1, SPPF, [1024, 5, 3, True]]
@@ -14 +16 @@
-  - [-1, 2, C3k2, [512, False]]
+  - [-1, 2, C3k2, [512, True]]
@@ -17 +19 @@
-  - [-1, 2, C3k2, [256, False]]
+  - [-1, 2, C3k2, [256, True]]
@@ -20 +22 @@
-  - [-1, 2, C3k2, [512, False]]
+  - [-1, 2, C3k2, [512, True]]
@@ -23 +25 @@
-  - [-1, 2, C3k2, [1024, True]]
+  - [-1, 1, C3k2, [1024, True, 0.5, True]]

- bilan boshlangan qator YOLO11 da, + bilan boshlangani YOLO26 da. O'zgarishlar oz va aniq:

  • Bosh sozlamalari: end2end: True (NMS siz bosh) va reg_max: 1 (DFL yo'q) — 5-bobning mavzusi.
  • SPPF: [1024, 5, 3, True] — max-pool soni aniq 3, va oxirgi True — SPPF atrofida ham qisqa tutashuv (y + x).
  • Neck dagi C3k2 lar (13, 16, 19-qatlamlar): False → True, ya'ni c3k=True — ichida oddiy Bottleneck o'rniga og'irroq C3k, xuddi yolo11m dagi kabi, lekin n o'lchamida ham.
  • Oxirgi C3k2 (22-qatlam): takror 2 → 1 va [1024, True, 0.5, True] — to'rtinchi argument attn=True, ya'ni P5 chiqishi oldida yana bitta attention bloki.
  • Backbone ning qolgan qismi (0–8 va 10-qatlamlar) — aynan bir xil.

Asosiy fikr

  • YOLO11 = yaml fayl + parse_model. Bitta fayldan beshta model: kanallar width ga, takrorlar depth ga ko'paytiriladi, m/l/x da esa ba'zi bloklar og'irroq turga almashadi.
  • Asosiy g'ishtlar: C3k2 (bo'linish + qisqa tutashuv), SPPF (5 × 5 max-pool uch marta = 5, 9, 13 lik oynalar), C2PSA (400 katak ustida attention, faqat eng kichik to'rda).
  • Neck (FPN + PAN) chuqur qatlamlarning "nima" ma'lumotini va sayoz qatlamlarning "qayerda" ma'lumotini aralashtiradi va Detect ga uch o'lchamli xarita beradi: 80², 40², 20².

Mashqlar

  1. kanal() formulasi bilan yolo11s va yolo11x ning 10-qatlami (C2PSA) necha kanal bo'lishini qo'lda hisoblang. Keyin YOLO("yolo11s.yaml") bilan tekshiring. Nega x da 1024 · 1.5 = 1536 emas?
  2. attention.png ni boshqa katak uchun chizing: masalan avtobus g'ildiragi. Qaysi bosh "yaqin atrofga", qaysi biri "uzoqqa" qaraydi?
  3. yolo11.yaml nusxasini oling va neck dan PAN qismini (17–22-qatlamlar) olib tashlab, Detect ni [[16, 13, 10], ...] ga ulang. Model quriladimi? Parametrlar qanchaga kamaydi? (Og'irliksiz qurish yetarli, o'qitish shart emas.)

Bu bobni bepul o'qidingiz.

Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.

Telegramda yozish Narxlar