|
YOLOv8 ultralytics github의 원본코드를 기반으로 내용 작성.
내용에 들어가기 앞서 짧게 설명하자면, YOLOv8에서는 데이터셋으로 이미지 파일과 라벨링 데이터가 포함되어있는 txt 파일로 분리되며 이미지 데이터셋의 형식은 jpg(JPEG)나 png를 사용한다.
(json파일 형식을 사용하기 위한 Dataset Class가 존재하기도 한다. - ultralytics.data.dataset.GroundingDataset)
위 이미지와 같이 이미지만 들어있는 images와 라벨링 데이터가 들어가있는 labels 폴더 내부 각각 train, val 폴더로 나뉘어지며 이름이 같은 jpg파일과 txt파일이 코드를 통해 일치되며 학습과 검증을 수행한다.
test 데이터셋 같은 경우도 동일하게 적용되며
https://docs.ultralytics.com/ko/datasets/detect/#ultralytics-yolo-format
DATASET.YAML
YOLO model에서 데이터셋 호출을 위해서 기본적으로 설정해야 하는 파일
ultralytics/ultralytics/cfg/dataset/custum.yaml
데이터셋이 포함되어 있는 전체 데이터셋 폴더 위치 path와 내부 학습, 검증, 테스트 데이터셋의 세부 경로인 train, val, test로 나뉜다.
1 2 3 4 5 6 7 | ultralytics의 coco8.yaml파일의 내용. # Train/val/test sets # 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..] path: ../datasets/coco8 # dataset root dir train: images/train # train images val: images/val # val images test: # test images | cs |
경로를 지정하는 방법으로는 3가지가 존재한다.
(경로를 지정할 떄 lables가 기준이 아닌 images가 기준이 된다.)
1) 경로 : 데이터셋이 들어가 있는 디렉토리의 경로를 지정. (절대경로와 상대경로 모두 가능하다.)
2) 파일 : 이미지의 경로를 나열한 파일을 지정하는 것으로 train의 경우 ../dataset/train/img1.jpg, ../dataset/train/img2.jpg처럼 하나의 txt파일 내부에 각 이미지의 경로를 나열하는 방식으로 저장 후 txt의 경로를 지정한다.
3) 리스트 : 이미지 하나하나의 경로를 직접 적어주는 방식 ["/dataset/train/img1.jpg", "dataset/train/img2.jpg"]를 train:이후 적어주어 이미지 각각을 선택한다.
(파일과 리스트 형식으로 데이터셋을 지정한 예시를 찾아보았으나 예시가 보이지 않아 다음에 찾게 되면 링크를 적기로 하였다.)
GlobalWheat2020.yaml 파일이 동일 dataset경로에 있는데 해당 데이터셋은 아래와 같이 하나의 dataset폴더에 모든 데이터셋을 넣고 내부 폴더로 분할시켜주었다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | path: ../datasets/GlobalWheat2020 # dataset root dir train: # train images - images/arvalis_1 - images/arvalis_2 - images/arvalis_3 - images/ethz_1 - images/rres_1 - images/inrae_1 - images/usask_1 val: # val images - images/ethz_1 test: # test images - images/utokyo_1 - images/utokyo_2 - images/nau_1 - images/uq_1 | cs |
해당 방식은 리스트를 이용한 방식으로 yaml형식의 파일은 ' : ' 이 변수 명 이후에 적혀있다면 이후 ' - '를 통해 리스트를 생성할 수 있다.
train을 python형식으로 고친다면 train["images/arvalis_1", "images/arvalis_2", "images/arvalis_3"] 과 동일하게 볼 수 있다.
이번 프로젝트에서의 dataset.yaml파일에서의 경로 설정은 1번 경로 선택 방식을 채택하였다.
1 2 3 4 | path: D:/User/dataset/Compete_COCO # dataset root dir train: images/train # train images (relative to 'path') val: images/val # val images (relative to 'path') test: D:/User/dataset/test # test images | cs |
데이터셋 클래스 설정
데이터셋의 경로 설정 이후 해당 데이터셋에 대한 클래스를 지정해줘야 한다.
'names'의 변수 명을 가진 리스트 형태로 작성되며 일반적으로는 해당 클래스 선언 이후 yaml 파일이 종료된다.
COCO dataset | Tiger-pose dataset |
names: 0: person 1: bicycle 2: car 3: motorcycle 4: airplane 5: bus 6: train 7: truck 8: boat 9: traffic light 10: fire hydrant 11: stop sign | # Keypoints kpt_shape: [12, 2] # number of keypoints, number of dims (2 for x,y or 3 for x,y,visible) flip_idx: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] # Classes names: 0: tiger |
COCO dataset의 클래스 선언의 일부로 0~79의 총 80개의 클래스를 소지하고 있다. | tiger-pose.yaml의 클래스 선언부로 클래스를 선언하는 부위 이외의 추가적인 요소가 존재한다. 호랑이의 포즈에 대한 추적으로 kpt_shape의 경우 포즈 구별에 대한 포인트의 수와 차원의 수로 이루어져 있다. flip_idx는 찍히는 방향(호랑이가 좌측을 향해있는지 우측을 향해 있는지)에 따른 계산을 하기 위해 존재한다. |
Keypoint는 Classes의 일부로 객체 탐지에서 "호랑이"로 추측되는 객체가 존재한다면 해당 객체에 대해 신체 부위 포인트를 지정하기 위해서 Keypoint가 추가적으로 사용되는 데이터이다.
데이터셋 다운로드
대부분의 유저들은 처음 공부하거나 기존 코드를 사용하려 할 때 데이터셋을 미리 구현해두고 코드를 다운받지 않고 코드를 다운로드 받고 이에 해당하는 데이터셋을 찾기 때문에 대중적이거나 공개된 데이터셋의 경우 yaml파일을 읽어들이면서 존재하지 않는다면 파일을 다운로드 받을 수 있게 한다.
내부에 형식이 잘 정리되어있는 경우에는 아래와 같이 링크 하나만 존재하기도 하며
{ data8.yaml }
1 2 | # Download script/URL (optional) download: https://github.com/ultralytics/assets/releases/download/v0.0.0/dota8.zip | cs |
내부에 형식이 추가적으로 필요한 데이터셋의 경우 추가적인 작업을 진행해주기도 한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # Download script/URL (optional) ------------------------------------------------------------------ download: | from ultralytics.utils.downloads import download from pathlib import Path # Download dir = Path(yaml['path']) # dataset root dir urls = ['https://zenodo.org/record/4298502/files/global-wheat-codalab-official.zip', 'https://github.com/ultralytics/assets/releases/download/v0.0.0/GlobalWheat2020_labels.zip'] download(urls, dir=dir) # Make Directories for p in 'annotations', 'images', 'labels': (dir / p).mkdir(parents=True, exist_ok=True) # Move for p in 'arvalis_1', 'arvalis_2', 'arvalis_3', 'ethz_1', 'rres_1', 'inrae_1', 'usask_1', \ 'utokyo_1', 'utokyo_2', 'nau_1', 'uq_1': (dir / 'global-wheat-codalab-official' / p).rename(dir / 'images' / p) # move to /images f = (dir / 'global-wheat-codalab-official' / p).with_suffix('.json') # json file if f.exists(): f.rename((dir / 'annotations' / p).with_suffix('.json')) # move to /annotations | cs |
이번 프로젝트에서 Classes의 설정은 bus와 car 두개의 Classes 이외의 locs와 Actions가 추가적으로 사용되었다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | # Classes names: 0: Car 1: Bus # Locations locs: 0: VehLane 1: OutgoLane 2: IncomLane 3: Jun 4: parking # Actions actions: 0: IncatLft 1: Break 2: IncatRht 3: HazLita | cs |
Classes는 차량과 버스를 구별하는 방식으로 사용되었고 locs는 자신의 차량에 대한 상대 차량의 위치(상태)와 Actions는 상대 차량의 후미등의 on_off에 대한 정보가 들어갔다.
DATALOADER-dataset.py
YOLO는 데이터셋 호출시 공통적으로 class YOLODataset을 사용한다.
해당 관련 코드는 ultralytics/ultralytics/cfg/data/dataset.py에 있다.
생성자에 대한 초기화는 다음과 같이 이루어진다.
1 2 3 4 5 6 7 | def __init__(self, *args, data=None, task="detect", **kwargs): self.use_segments = task == "segment" self.use_keypoints = task == "pose" self.use_obb = task == "obb" self.data = data assert not (self.use_segments and self.use_keypoints), "Can not use both segments and keypoints." super().__init__(*args, **kwargs) | cs |
head의 속성에 따라 'segment', 'keypoint', 'obb'에 대한 속성을 파악하고 data는 자체적으로 받아들인다.
head의 속성을 입력하지 않는다면 'detect'으로 자동 설정된다.
다음 cache_lables 함수는 데이터셋 중 labeles 정보를 읽을 때 쓰이는 함수로 txt파일을 인덱스 형식으로 처리하여 정보를 읽는 방식을 설정한다.
원본 | 프로젝트 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | with ThreadPool(NUM_THREADS) as pool: results = pool.imap( func=verify_image_label, iterable=zip( self.im_files, self.label_files, repeat(self.prefix), repeat(self.use_keypoints), repeat(len(self.data["names"])), repeat(len(self.data["locs"])), repeat(len(self.data["actions"])), repeat(nkpt), repeat(ndim), ), ) pbar = TQDM(results, desc=desc, total=total) for im_file, lb, shape, segments, keypoint, nm_f, nf_f, ne_f, nc_f, msg in pbar: nm += nm_f nf += nf_f ne += ne_f nc += nc_f if im_file: x["labels"].append( { "im_file": im_file, "shape": shape, "cls": lb[:, 0:1], # n, 1 "loc": lb[:, 1:2], # n, 1 "action": lb[:, 2:2+action_len], # n, 1 "bboxes": lb[:, 2+action_len:], # n, 4 "segments": segments, "keypoints": keypoint, "normalized": True, "bbox_format": "xywh", } ) if msg: msgs.append(msg) pbar.desc = f"{desc} {nf} images, {nm + ne} backgrounds, {nc} corrupt" pbar.close() | cs |
이후의 dataset.py는 mosaic 데이터 증식 기법과 mixup 기법 및 다른 데이터셋 형식에 대한 로드의 내용이 들어가 있다.
DATALOADER-augment.py
(프로젝트 코드로 진행 - loc와 action만 제거하면 원본 코드와 거의 동일하다, 다른 부분은 체크하고 넘길 예정.)
데이터 증식 관련된 함수들과 클래스가 모여있는 장소로 mosaic와 mixup 데이터 증식 기법부터 포함하여 픽셀을 건드리는 수준의 데이터 증식 기법과 공간적 측면을 건드리는 데이터 증식 기법을 가지고 있다.
데이터 증식에 대해서 직접적으로 건드리는 클래스는 class Albumentations이고 이 클래스가 가지고 있는 데이터 증식 기법들은 아래 하단의 사이트에 정리되어있다.
https://albumentations.ai/docs/getting_started/transforms_and_targets/#spatial-level-transforms
Albumentations는 공간적 속성을 변경하는 spatial_transforms과 pixel에 대해 값 똑은 색상 등을 변환하는 데이터 증식 기법들에 대해 Compose 클래스에 정리한다.
spatial_transforms에 나열되어있는 집합의 원소들은 사용 가능한 데이터 증식 기법들을 나타낸것이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | def __init__(self, p=1.0): """Initialize the transform object for YOLO bbox formatted params.""" self.p = p self.transform = None prefix = colorstr("albumentations: ") try: import albumentations as A check_version(A.__version__, "1.0.3", hard=True) # version requirement # List of possible spatial transforms spatial_transforms = { "Affine", "BBoxSafeRandomCrop", "CenterCrop", "CoarseDropout", "Crop", "CropAndPad", "CropNonEmptyMaskIfExists", "D4", "ElasticTransform", "Flip", "GridDistortion", "GridDropout", "HorizontalFlip", "Lambda", "LongestMaxSize", "MaskDropout", "MixUp", "Morphological", "NoOp", "OpticalDistortion", "PadIfNeeded", "Perspective", "PiecewiseAffine", "PixelDropout", "RandomCrop", "RandomCropFromBorders", "RandomGridShuffle", "RandomResizedCrop", "RandomRotate90", "RandomScale", "RandomSizedBBoxSafeCrop", "RandomSizedCrop", "Resize", "Rotate", "SafeRotate", "ShiftScaleRotate", "SmallestMaxSize", "Transpose", "VerticalFlip", "XYMasking", } # from https://albumentations.ai/docs/getting_started/transforms_and_targets/#spatial-level-transforms # Transforms T = [ A.Blur(p=0.01), A.MedianBlur(p=0.01), A.ToGray(p=0.01), A.CLAHE(p=0.01), A.RandomBrightnessContrast(p=0.0), A.RandomGamma(p=0.0), A.ImageCompression(quality_lower=75, p=0.0), ] # Compose transforms self.contains_spatial = any(transform.__class__.__name__ in spatial_transforms for transform in T) self.transform = ( A.Compose(T, bbox_params=A.BboxParams(format="yolo", label_fields=["class_labels"])) if self.contains_spatial else A.Compose(T) ) LOGGER.info(prefix + ", ".join(f"{x}".replace("always_apply=False, ", "") for x in T if x.p)) except ImportError: # package not installed, skip pass except Exception as e: LOGGER.info(f"{prefix}{e}") | cs |
사용자는 T로 설정되어 있는 리스트에 원하는 증식 기법을 설정해줄 수 있다.
1 2 3 4 5 6 7 8 9 10 | # Transforms T = [ A.Blur(p=0.01), A.MedianBlur(p=0.01), A.ToGray(p=0.01), A.CLAHE(p=0.01), A.RandomBrightnessContrast(p=0.0), A.RandomGamma(p=0.0), A.ImageCompression(quality_lower=75, p=0.0), ] | cs |
이후 해당 클래스에서 Compose내역을 확인하게 되는데 Compose 즉 기본 데이터 증식(변환)을 제외하고 사용자가 설정하는 T 리스트를 읽어 적용되는 증식 기법을 확인하게 된다.
1 2 3 4 5 6 7 | # Compose transforms self.contains_spatial = any(transform.__class__.__name__ in spatial_transforms for transform in T) self.transform = ( A.Compose(T, bbox_params=A.BboxParams(format="yolo", label_fields=["class_labels"])) if self.contains_spatial else A.Compose(T) ) | cs |
self.contains_spatial은 any내부의 for in문을 통해 T의 리스트중 spatial_transfoms와 동일한 이름을 가진 데이터 증식 기법이 있는지 확인하게 되고 존재한다면 contains_spatial값을 True로 반환하고 존재하지 않다면 False를 반환한다.
이후 self.transform에서 공간적 변환이 포함된 경우에는 BboxParams를 통해 labels 데이터셋에 있는 segmentation 픽셀의 값이나 Rounding Box의 위치 정보를 이미지와 동일하게 변환해준다. (공간적 증식 이후 픽셀값관련 데이터 증식 진행.)
self.transform 의 문법이 이상하다 느낄 수 있지만 해당 문법은 삼항연산자로 보면 이해할 수 있다.
C++ 삼항 연산자 기본 형태 | Python 삼항 연산자 기본 형태 |
조건식 ? 반환값1 : 반환값2 | 반환값1 if 조건식 else 반환값2 ( if문이나 콜론( : )이 없는 형태이다. 조건식이 True면 반환값1을 False이면 반환값2를 반환한다. ) |
이후 호출문으로 Compose내역에 따라 데이터 증식을 적용시킨 이후 labels 데이터를 반환한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | def __call__(self, labels): """Generates object detections and returns a dictionary with detection results.""" if self.transform is None or random.random() > self.p: return labels if self.contains_spatial: cls = labels["cls"] loc = labels["loc"] action = labels["action"] if len(cls): im = labels["img"] labels["instances"].convert_bbox("xywh") labels["instances"].normalize(*im.shape[:2][::-1]) bboxes = labels["instances"].bboxes concatenated_labels = np.concatenate([cls[:, None], loc[:, None], action[:, None]], axis=1) # TODO: add supports of segments and keypoints new = self.transform(image=im, bboxes=bboxes, class_labels=concatenated_labels) # transformed if len(new["class_labels"]) > 0: # skip update if no bbox in new im labels["img"] = new["image"] transformed_labels = np.array(new["class_labels"]) labels["cls"] = transformed_labels[:, 0] labels["loc"] = transformed_labels[:, 1] labels["action"] = transformed_labels[:, 2:] bboxes = np.array(new["bboxes"], dtype=np.float32) labels["instances"].update(bboxes=bboxes) else: labels["img"] = self.transform(image=labels["img"])["image"] # transformed return labels | cs |
인덱스 영역에 대해서 이야기를 하자면 넘파이 형식으로 되어있는 2차원 행렬이 있는 상태를 기반으로 했을 때 다음과 같은 형태를 가지고 있다. transformed_lables가 2차원 행렬 형태이며 {0, 1, 2 : }는 각각에 대한 정보를 가지고 있는 형태.
프로젝트의 경우에는 객체검출 외 분할작업과 추가적으로 위치와 후미등 상태 여부도 파악하기 위해서
transformed_labels에 넘파이 형태로 넣은 다음 인덱스 형태로 적용시켰으나 원본 코드에서는 Rounding Box정보와 클래스의 정보만 있기에 바로 적용시킨 것을 확인할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | if self.transform is None or random.random() > self.p: return labels if self.contains_spatial: cls = labels["cls"] if len(cls): im = labels["img"] labels["instances"].convert_bbox("xywh") labels["instances"].normalize(*im.shape[:2][::-1]) bboxes = labels["instances"].bboxes # TODO: add supports of segments and keypoints new = self.transform(image=im, bboxes=bboxes, class_labels=cls) # transformed if len(new["class_labels"]) > 0: # skip update if no bbox in new im labels["img"] = new["image"] labels["cls"] = np.array(new["class_labels"]) bboxes = np.array(new["bboxes"], dtype=np.float32) labels["instances"].update(bboxes=bboxes) else: labels["img"] = self.transform(image=labels["img"])["image"] # transformed return labels | cs |
위처럼 구현된 Albumentation 클래스는 같은 모듈의 v8_transforms 클래스를 통해 Mosaic, CopyPaste, RandomPerspective, 등과 함께 Compose 로 구성되어 반환되고 v8_transforms 클래스는 dataset.py의 YOLODataset클래스 내부의 build_transforms 함수에서 선언되어 구현된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | def build_transforms(self, hyp=None): """Builds and appends transforms to the list.""" if self.augment: hyp.mosaic = hyp.mosaic if self.augment and not self.rect else 0.0 hyp.mixup = hyp.mixup if self.augment and not self.rect else 0.0 transforms = v8_transforms(self, self.imgsz, hyp) else: transforms = Compose([LetterBox(new_shape=(self.imgsz, self.imgsz), scaleup=False)]) transforms.append( Format( bbox_format="xywh", normalize=True, return_mask=self.use_segments, return_keypoint=self.use_keypoints, return_obb=self.use_obb, batch_idx=True, mask_ratio=hyp.mask_ratio, mask_overlap=hyp.overlap_mask, bgr=hyp.bgr if self.augment else 0.0, # only affect training. ) ) return transforms | cs |
데이터 증식된 내용 확인 방법 (추가 확인 중)
https://github.com/ultralytics/ultralytics/issues/14190
위 사이트에 나와있는 코드들에 대해 파라미터 일치와 패키지 설치 등의 작업을 수행해보았으나 온전히 실행되지 않음.
(unicodeDecodeError나 TypeError등이 주를 이룸.)
yaml 사용 이유
yaml은 YAML Ain't Markup Language로 마크업 단어가 아니라는 정직한 이름을 가지고 있다. (발음은 야물...이라고 한다.)
yaml은 기존의 xml이나 json처럼 역할은 같지만 보다 사용자 친화적임에 중점을 두고 있다.
기존의 xml이나 json은 중괄호 대괄호 등에 각각의 역할이 주어지는 반면 yaml은 들여쓰기 하나로 해결해주어 구조 파악 등에 도움을 주고 주석 추가 여부도 다양한 사람이 입문하기 좋은 역할을 하여 YOLO에서 쓰이는 것으로 추정된다.
json같은 경우 labels의 역할도 이미지의 경로와 함께 저장하여 특정 YOLO모델에서 쓰이나 yaml을 사용하는 모델의 대부분인 것은 편의성과 기존 라이브러리 호출에 있어 용이하기 때문에 yaml을 주로 사용한다.
데이터셋의 입력 사이즈의 경우 ultralytics/cfg/default.py에서 설정할수 있다.(명령행인자로 직접 줄 수도 있다.)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | # Ultralytics YOLO 🚀, AGPL-3.0 license # Default training settings and hyperparameters for medium-augmentation COCO training task: segment # (str) YOLO task, i.e. detect, segment, classify, pose mode: train # (str) YOLO mode, i.e. train, val, predict, export, track, benchmark # Train settings ------------------------------------------------------------------------------------------------------- model: # (str, optional) path to model file, i.e. yolov8n.pt, yolov8n.yaml data: # (str, optional) path to data file, i.e. coco8.yaml epochs: 100 # (int) number of epochs to train for time: # (float, optional) number of hours to train for, overrides epochs if supplied patience: 100 # (int) epochs to wait for no observable improvement for early stopping of training batch: 32 # (int) number of images per batch (-1 for AutoBatch) imgsz: 640 # (int | list) input images size as int for train and val modes, or list[w,h] for predict and export modes | cs |
해당 모듈을 들여다보면 imgsz라는 변수가 존재하는데 해당 자료형에는 하나의 int형 변수와 list를 넣을 수 있다.
int형 변수를 넣게 된다면 해당 설정한 값이 가장 큰 치수를 기준으로 기존의 이미지의 크기를 변경하여 데이터셋을 구성하고 list 자료형을 넣게 된다면 해당 리스트에 맞게 규격을 맞춰준다.
예시를 들어 원본 1280*720에 대해 imgsz를 640으로 넣는다면 데이터셋은 640*360으로 이미지를 전처리하여 구성한다.
imgsz를 리스트 형태로 [640*480]으로 구성한다면 원본 이미지가 해당 규격(w,h)에 맞게 설정된다.
만일 이미지의 크기를 정사각형 형태로 640*640으로 구성하고 싶다면 리스트를 [640*640]으로 입력하여도 가능하지만 또 다른 변수 rect를 False에서 True로 변환시켜주는 방법도 있다 해당 변수가 True라면 640으로 입력하여도 정사각형 형태로 변환한다.
1 | rect: False # (bool) rectangular training if mode='train' or rectangular validation if mode='val' | cs |
모델 크기 출력
YOLO에서 제공하는 모델은 여러가지 버전(Detection, Segmentation, Pose 등)이 있다. 그중에서도 모델의 크기에 따라 나누기도 하는데 ultralytics/ultralytics/cfg/model에는 각 YOLO버전에 대한 모델이 제공되어있고 그중 yolov10의 경우 yolov10*.yaml형태로 모델이 제공된다.
모델의 크기 순서대로 n(Nano) < s(Small) < m(Medium) < b(Big) < l(Large) < x(Extra Large) 이다.
내부를 확인하면 C2f 대신 C2fCIB (객체 인근의 정보를 추가 학습)를 사용하는 등의 레이어 수정과 모델의 scales에 있어 깊이, 너비, max_channels를 조절하며 모델의 크기와 복잡성이 변경된다.
depth는 모델의 레이어를 얼마나 반복할지를 의미하고 width는 출력 채널 수를 조절하며 max_channels는 최대 채널 개수를 의미한다.
해당 내용 관련 사이트
다음과 같은 코드의 경우 (yolo10l.yaml) l이라는 리스트에 대해서 값을 주어지고 백본에서 from으로 이전 레이어의 노드 개수를 입력 개수로 지정한 이후 (이미지가 10*10이면 100이 입력단으로 들어간다.) 64개의 kernel을 사용해서 채널을 64개를 가지게 된다. 여기에서 depth는 [-1, 1, Conv, [64, 3, 2]] 에서 두번째 파라미터에 속하여 1번 반복함을 의미한다.
만일 depth와 width를 계산하였을 때 소수점이 나온다면 반올림을 진행하며 0.5미만으로 내려가는 경우 최소 1의 값을 부여한다.
두번째 리스트의 경우에도 마찬가지로 64개의 채널 수를 가진 레이어를 가져와 입력단으로 사용하고 한번 반복하며 출력 채널 수를 128로 설정함을 의미한다.
max_channels의 경우 다양한 사이트에서 최대 채널 수로 설정한다. 출력 레이어의 수가 max_channels보다 높게 설정되어있다면 코드 내부에서 max_channels 값으로 수정하여 조절한다.
몇몇개의 레이어에서 max_channels의 값을 초과하는 채널을 소지하나 이는 몇가지의 레이어를 Concat레이어를 이용해 이은 결과이다.
https://github.com/ultralytics/ultralytics/issues/17450
첫댓글 yanl 사용 이유
입력 사이즈 들어가는 default.py 추가
모델 크기 출력
증식 내용 결과 확인.