Upload 5 files
Browse files- F1-score ์ธก์ .py +37 -0
- train.py +707 -0
- ์๋ณธ์ด๋ฏธ์ง๊ฒฐ๊ณผ์ถ์ถ.py +26 -0
- ์ด๋ฏธ์ง ์ด๋.py +28 -0
- ์ ํdetect.py +98 -0
F1-score ์ธก์ .py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pandas as pd
|
| 2 |
+
from sklearn.metrics import f1_score
|
| 3 |
+
|
| 4 |
+
# ํ์ผ ๊ฒฝ๋ก ์ค์
|
| 5 |
+
test_result_path = r"C:\Users\202408\Desktop\venv\yolov7\yolov7\junga_image_text_v03\test\test_result.xlsx"
|
| 6 |
+
output_path = r"D:\์ง์ \ํฌ์ธ๋ฉ\์ฌ์
๊ณผ์ \์ ๋ถ๊ณผ์ \01. 2024๋
์ ์กฐ์
AI์ตํฉ ๊ธฐ๋ฐ ์กฐ์ฑ ์ฌ์
\์ ์์ ๋ฐ\35. test์ฉ ์ด๋ฏธ์ง\output.xlsx"
|
| 7 |
+
save_path = r"C:\Users\202408\Desktop\venv\์ ์์ ๋ฐ_์ด๋ฏธ์ง_๋ผ๋ฒจ๋ง\๋ฐ์ดํฐ_๋ผ๋ฒจ๋ง(v.0.3)\05. ํ
์คํธ\merged_with_f1_score.xlsx" # ์ ์ฅ ๊ฒฝ๋ก
|
| 8 |
+
|
| 9 |
+
# ์์
ํ์ผ ์ฝ๊ธฐ
|
| 10 |
+
test_result = pd.read_excel(test_result_path)
|
| 11 |
+
output = pd.read_excel(output_path)
|
| 12 |
+
|
| 13 |
+
# ์ปฌ๋ผ๋ช
๋ณ๊ฒฝ
|
| 14 |
+
test_result.rename(columns={'Filename': 'ํ์ผ๋ช
', 'Predicted Class': '์์ธก๊ฐ'}, inplace=True)
|
| 15 |
+
output.rename(columns={'์ ํ๋ช
': '์ค์ ๊ฐ'}, inplace=True)
|
| 16 |
+
|
| 17 |
+
# ํ์ผ๋ช
๊ธฐ์ค์ผ๋ก ๋ฐ์ดํฐ ๋ณํฉ
|
| 18 |
+
merged_df = pd.merge(test_result, output, on='ํ์ผ๋ช
', how='inner')
|
| 19 |
+
|
| 20 |
+
if not merged_df.empty:
|
| 21 |
+
# ๋ฐ์ดํฐ ํ์
๋ณํ
|
| 22 |
+
merged_df['์ค์ ๊ฐ'] = merged_df['์ค์ ๊ฐ'].astype(str)
|
| 23 |
+
merged_df['์์ธก๊ฐ'] = merged_df['์์ธก๊ฐ'].astype(str)
|
| 24 |
+
|
| 25 |
+
# F1-Score ๊ณ์ฐ
|
| 26 |
+
f1 = f1_score(merged_df['์ค์ ๊ฐ'], merged_df['์์ธก๊ฐ'], average='weighted')
|
| 27 |
+
print("F1-Score:", f1)
|
| 28 |
+
|
| 29 |
+
# F1-Score๋ฅผ ๋ฐ์ดํฐํ๋ ์์ ๋ง์ง๋ง ํ์ ์ถ๊ฐ
|
| 30 |
+
f1_row = {'ํ์ผ๋ช
': 'F1-Score', '์ค์ ๊ฐ': f1, '์์ธก๊ฐ': 'F1-Score'}
|
| 31 |
+
merged_df = merged_df._append(f1_row, ignore_index=True)
|
| 32 |
+
|
| 33 |
+
# ๋ฐ์ดํฐํ๋ ์ ์ ์ฅ
|
| 34 |
+
merged_df.to_excel(save_path, index=False)
|
| 35 |
+
print(f"์์
ํ์ผ์ด ์ฑ๊ณต์ ์ผ๋ก ์ ์ฅ๋์์ต๋๋ค: {save_path}")
|
| 36 |
+
else:
|
| 37 |
+
print("๋ ๋ฐ์ดํฐ์
์์ ์ผ์นํ๋ ํ์ผ๋ช
์ด ์์ต๋๋ค.")
|
train.py
ADDED
|
@@ -0,0 +1,707 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
import logging
|
| 3 |
+
import math
|
| 4 |
+
import os
|
| 5 |
+
import random
|
| 6 |
+
import time
|
| 7 |
+
from copy import deepcopy
|
| 8 |
+
from pathlib import Path
|
| 9 |
+
from threading import Thread
|
| 10 |
+
|
| 11 |
+
import numpy as np
|
| 12 |
+
import torch.distributed as dist
|
| 13 |
+
import torch.nn as nn
|
| 14 |
+
import torch.nn.functional as F
|
| 15 |
+
import torch.optim as optim
|
| 16 |
+
import torch.optim.lr_scheduler as lr_scheduler
|
| 17 |
+
import torch.utils.data
|
| 18 |
+
import yaml
|
| 19 |
+
from torch.cuda import amp
|
| 20 |
+
from torch.nn.parallel import DistributedDataParallel as DDP
|
| 21 |
+
from torch.utils.tensorboard import SummaryWriter
|
| 22 |
+
from tqdm import tqdm
|
| 23 |
+
|
| 24 |
+
import test # import test.py to get mAP after each epoch
|
| 25 |
+
from models.experimental import attempt_load
|
| 26 |
+
from models.yolo import Model
|
| 27 |
+
from utils.autoanchor import check_anchors
|
| 28 |
+
from utils.datasets import create_dataloader
|
| 29 |
+
from utils.general import labels_to_class_weights, increment_path, labels_to_image_weights, init_seeds, \
|
| 30 |
+
fitness, strip_optimizer, get_latest_run, check_dataset, check_file, check_git_status, check_img_size, \
|
| 31 |
+
check_requirements, print_mutation, set_logging, one_cycle, colorstr
|
| 32 |
+
from utils.google_utils import attempt_download
|
| 33 |
+
from utils.loss import ComputeLoss, ComputeLossOTA
|
| 34 |
+
from utils.plots import plot_images, plot_labels, plot_results, plot_evolution
|
| 35 |
+
from utils.torch_utils import ModelEMA, select_device, intersect_dicts, torch_distributed_zero_first, is_parallel
|
| 36 |
+
from utils.wandb_logging.wandb_utils import WandbLogger, check_wandb_resume
|
| 37 |
+
|
| 38 |
+
logger = logging.getLogger(__name__)
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def train(hyp, opt, device, tb_writer=None):
|
| 42 |
+
logger.info(colorstr('hyperparameters: ') + ', '.join(f'{k}={v}' for k, v in hyp.items()))
|
| 43 |
+
save_dir, epochs, batch_size, total_batch_size, weights, rank, freeze = \
|
| 44 |
+
Path(opt.save_dir), opt.epochs, opt.batch_size, opt.total_batch_size, opt.weights, opt.global_rank, opt.freeze
|
| 45 |
+
|
| 46 |
+
# Directories
|
| 47 |
+
wdir = save_dir / 'weights'
|
| 48 |
+
wdir.mkdir(parents=True, exist_ok=True) # make dir
|
| 49 |
+
last = wdir / 'last.pt'
|
| 50 |
+
best = wdir / 'best.pt'
|
| 51 |
+
results_file = save_dir / 'results.txt'
|
| 52 |
+
|
| 53 |
+
# Save run settings
|
| 54 |
+
with open(save_dir / 'hyp.yaml', 'w') as f:
|
| 55 |
+
yaml.dump(hyp, f, sort_keys=False)
|
| 56 |
+
with open(save_dir / 'opt.yaml', 'w') as f:
|
| 57 |
+
yaml.dump(vars(opt), f, sort_keys=False)
|
| 58 |
+
|
| 59 |
+
# Configure
|
| 60 |
+
plots = not opt.evolve # create plots
|
| 61 |
+
cuda = device.type != 'cpu'
|
| 62 |
+
init_seeds(2 + rank)
|
| 63 |
+
with open(opt.data) as f:
|
| 64 |
+
data_dict = yaml.load(f, Loader=yaml.SafeLoader) # data dict
|
| 65 |
+
is_coco = opt.data.endswith('coco.yaml')
|
| 66 |
+
|
| 67 |
+
# Logging- Doing this before checking the dataset. Might update data_dict
|
| 68 |
+
loggers = {'wandb': None} # loggers dict
|
| 69 |
+
if rank in [-1, 0]:
|
| 70 |
+
opt.hyp = hyp # add hyperparameters
|
| 71 |
+
run_id = torch.load(weights, map_location=device).get('wandb_id') if weights.endswith('.pt') and os.path.isfile(weights) else None
|
| 72 |
+
wandb_logger = WandbLogger(opt, Path(opt.save_dir).stem, run_id, data_dict)
|
| 73 |
+
loggers['wandb'] = wandb_logger.wandb
|
| 74 |
+
data_dict = wandb_logger.data_dict
|
| 75 |
+
if wandb_logger.wandb:
|
| 76 |
+
weights, epochs, hyp = opt.weights, opt.epochs, opt.hyp # WandbLogger might update weights, epochs if resuming
|
| 77 |
+
|
| 78 |
+
nc = 1 if opt.single_cls else int(data_dict['nc']) # number of classes
|
| 79 |
+
names = ['item'] if opt.single_cls and len(data_dict['names']) != 1 else data_dict['names'] # class names
|
| 80 |
+
assert len(names) == nc, '%g names found for nc=%g dataset in %s' % (len(names), nc, opt.data) # check
|
| 81 |
+
|
| 82 |
+
# Model
|
| 83 |
+
pretrained = weights.endswith('.pt')
|
| 84 |
+
if pretrained:
|
| 85 |
+
with torch_distributed_zero_first(rank):
|
| 86 |
+
attempt_download(weights) # download if not found locally
|
| 87 |
+
ckpt = torch.load(weights, map_location=device) # load checkpoint
|
| 88 |
+
model = Model(opt.cfg or ckpt['model'].yaml, ch=3, nc=nc, anchors=hyp.get('anchors')).to(device) # create
|
| 89 |
+
exclude = ['anchor'] if (opt.cfg or hyp.get('anchors')) and not opt.resume else [] # exclude keys
|
| 90 |
+
state_dict = ckpt['model'].float().state_dict() # to FP32
|
| 91 |
+
state_dict = intersect_dicts(state_dict, model.state_dict(), exclude=exclude) # intersect
|
| 92 |
+
model.load_state_dict(state_dict, strict=False) # load
|
| 93 |
+
logger.info('Transferred %g/%g items from %s' % (len(state_dict), len(model.state_dict()), weights)) # report
|
| 94 |
+
else:
|
| 95 |
+
model = Model(opt.cfg, ch=3, nc=nc, anchors=hyp.get('anchors')).to(device) # create
|
| 96 |
+
with torch_distributed_zero_first(rank):
|
| 97 |
+
check_dataset(data_dict) # check
|
| 98 |
+
train_path = data_dict['train']
|
| 99 |
+
test_path = data_dict['val']
|
| 100 |
+
|
| 101 |
+
# Freeze
|
| 102 |
+
freeze = [f'model.{x}.' for x in (freeze if len(freeze) > 1 else range(freeze[0]))] # parameter names to freeze (full or partial)
|
| 103 |
+
for k, v in model.named_parameters():
|
| 104 |
+
v.requires_grad = True # train all layers
|
| 105 |
+
if any(x in k for x in freeze):
|
| 106 |
+
print('freezing %s' % k)
|
| 107 |
+
v.requires_grad = False
|
| 108 |
+
|
| 109 |
+
# Optimizer
|
| 110 |
+
nbs = 64 # nominal batch size
|
| 111 |
+
accumulate = max(round(nbs / total_batch_size), 1) # accumulate loss before optimizing
|
| 112 |
+
hyp['weight_decay'] *= total_batch_size * accumulate / nbs # scale weight_decay
|
| 113 |
+
logger.info(f"Scaled weight_decay = {hyp['weight_decay']}")
|
| 114 |
+
|
| 115 |
+
pg0, pg1, pg2 = [], [], [] # optimizer parameter groups
|
| 116 |
+
for k, v in model.named_modules():
|
| 117 |
+
if hasattr(v, 'bias') and isinstance(v.bias, nn.Parameter):
|
| 118 |
+
pg2.append(v.bias) # biases
|
| 119 |
+
if isinstance(v, nn.BatchNorm2d):
|
| 120 |
+
pg0.append(v.weight) # no decay
|
| 121 |
+
elif hasattr(v, 'weight') and isinstance(v.weight, nn.Parameter):
|
| 122 |
+
pg1.append(v.weight) # apply decay
|
| 123 |
+
if hasattr(v, 'im'):
|
| 124 |
+
if hasattr(v.im, 'implicit'):
|
| 125 |
+
pg0.append(v.im.implicit)
|
| 126 |
+
else:
|
| 127 |
+
for iv in v.im:
|
| 128 |
+
pg0.append(iv.implicit)
|
| 129 |
+
if hasattr(v, 'imc'):
|
| 130 |
+
if hasattr(v.imc, 'implicit'):
|
| 131 |
+
pg0.append(v.imc.implicit)
|
| 132 |
+
else:
|
| 133 |
+
for iv in v.imc:
|
| 134 |
+
pg0.append(iv.implicit)
|
| 135 |
+
if hasattr(v, 'imb'):
|
| 136 |
+
if hasattr(v.imb, 'implicit'):
|
| 137 |
+
pg0.append(v.imb.implicit)
|
| 138 |
+
else:
|
| 139 |
+
for iv in v.imb:
|
| 140 |
+
pg0.append(iv.implicit)
|
| 141 |
+
if hasattr(v, 'imo'):
|
| 142 |
+
if hasattr(v.imo, 'implicit'):
|
| 143 |
+
pg0.append(v.imo.implicit)
|
| 144 |
+
else:
|
| 145 |
+
for iv in v.imo:
|
| 146 |
+
pg0.append(iv.implicit)
|
| 147 |
+
if hasattr(v, 'ia'):
|
| 148 |
+
if hasattr(v.ia, 'implicit'):
|
| 149 |
+
pg0.append(v.ia.implicit)
|
| 150 |
+
else:
|
| 151 |
+
for iv in v.ia:
|
| 152 |
+
pg0.append(iv.implicit)
|
| 153 |
+
if hasattr(v, 'attn'):
|
| 154 |
+
if hasattr(v.attn, 'logit_scale'):
|
| 155 |
+
pg0.append(v.attn.logit_scale)
|
| 156 |
+
if hasattr(v.attn, 'q_bias'):
|
| 157 |
+
pg0.append(v.attn.q_bias)
|
| 158 |
+
if hasattr(v.attn, 'v_bias'):
|
| 159 |
+
pg0.append(v.attn.v_bias)
|
| 160 |
+
if hasattr(v.attn, 'relative_position_bias_table'):
|
| 161 |
+
pg0.append(v.attn.relative_position_bias_table)
|
| 162 |
+
if hasattr(v, 'rbr_dense'):
|
| 163 |
+
if hasattr(v.rbr_dense, 'weight_rbr_origin'):
|
| 164 |
+
pg0.append(v.rbr_dense.weight_rbr_origin)
|
| 165 |
+
if hasattr(v.rbr_dense, 'weight_rbr_avg_conv'):
|
| 166 |
+
pg0.append(v.rbr_dense.weight_rbr_avg_conv)
|
| 167 |
+
if hasattr(v.rbr_dense, 'weight_rbr_pfir_conv'):
|
| 168 |
+
pg0.append(v.rbr_dense.weight_rbr_pfir_conv)
|
| 169 |
+
if hasattr(v.rbr_dense, 'weight_rbr_1x1_kxk_idconv1'):
|
| 170 |
+
pg0.append(v.rbr_dense.weight_rbr_1x1_kxk_idconv1)
|
| 171 |
+
if hasattr(v.rbr_dense, 'weight_rbr_1x1_kxk_conv2'):
|
| 172 |
+
pg0.append(v.rbr_dense.weight_rbr_1x1_kxk_conv2)
|
| 173 |
+
if hasattr(v.rbr_dense, 'weight_rbr_gconv_dw'):
|
| 174 |
+
pg0.append(v.rbr_dense.weight_rbr_gconv_dw)
|
| 175 |
+
if hasattr(v.rbr_dense, 'weight_rbr_gconv_pw'):
|
| 176 |
+
pg0.append(v.rbr_dense.weight_rbr_gconv_pw)
|
| 177 |
+
if hasattr(v.rbr_dense, 'vector'):
|
| 178 |
+
pg0.append(v.rbr_dense.vector)
|
| 179 |
+
|
| 180 |
+
if opt.adam:
|
| 181 |
+
optimizer = optim.Adam(pg0, lr=hyp['lr0'], betas=(hyp['momentum'], 0.999)) # adjust beta1 to momentum
|
| 182 |
+
else:
|
| 183 |
+
optimizer = optim.SGD(pg0, lr=hyp['lr0'], momentum=hyp['momentum'], nesterov=True)
|
| 184 |
+
|
| 185 |
+
optimizer.add_param_group({'params': pg1, 'weight_decay': hyp['weight_decay']}) # add pg1 with weight_decay
|
| 186 |
+
optimizer.add_param_group({'params': pg2}) # add pg2 (biases)
|
| 187 |
+
logger.info('Optimizer groups: %g .bias, %g conv.weight, %g other' % (len(pg2), len(pg1), len(pg0)))
|
| 188 |
+
del pg0, pg1, pg2
|
| 189 |
+
|
| 190 |
+
# Scheduler https://arxiv.org/pdf/1812.01187.pdf
|
| 191 |
+
# https://pytorch.org/docs/stable/_modules/torch/optim/lr_scheduler.html#OneCycleLR
|
| 192 |
+
if opt.linear_lr:
|
| 193 |
+
lf = lambda x: (1 - x / (epochs - 1)) * (1.0 - hyp['lrf']) + hyp['lrf'] # linear
|
| 194 |
+
else:
|
| 195 |
+
lf = one_cycle(1, hyp['lrf'], epochs) # cosine 1->hyp['lrf']
|
| 196 |
+
scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lf)
|
| 197 |
+
# plot_lr_scheduler(optimizer, scheduler, epochs)
|
| 198 |
+
|
| 199 |
+
# EMA
|
| 200 |
+
ema = ModelEMA(model) if rank in [-1, 0] else None
|
| 201 |
+
|
| 202 |
+
# Resume
|
| 203 |
+
start_epoch, best_fitness = 0, 0.0
|
| 204 |
+
if pretrained:
|
| 205 |
+
# Optimizer
|
| 206 |
+
if ckpt['optimizer'] is not None:
|
| 207 |
+
optimizer.load_state_dict(ckpt['optimizer'])
|
| 208 |
+
best_fitness = ckpt['best_fitness']
|
| 209 |
+
|
| 210 |
+
# EMA
|
| 211 |
+
if ema and ckpt.get('ema'):
|
| 212 |
+
ema.ema.load_state_dict(ckpt['ema'].float().state_dict())
|
| 213 |
+
ema.updates = ckpt['updates']
|
| 214 |
+
|
| 215 |
+
# Results
|
| 216 |
+
if ckpt.get('training_results') is not None:
|
| 217 |
+
results_file.write_text(ckpt['training_results']) # write results.txt
|
| 218 |
+
|
| 219 |
+
# Epochs
|
| 220 |
+
start_epoch = ckpt['epoch'] + 1
|
| 221 |
+
if opt.resume:
|
| 222 |
+
assert start_epoch > 0, '%s training to %g epochs is finished, nothing to resume.' % (weights, epochs)
|
| 223 |
+
if epochs < start_epoch:
|
| 224 |
+
logger.info('%s has been trained for %g epochs. Fine-tuning for %g additional epochs.' %
|
| 225 |
+
(weights, ckpt['epoch'], epochs))
|
| 226 |
+
epochs += ckpt['epoch'] # finetune additional epochs
|
| 227 |
+
|
| 228 |
+
del ckpt, state_dict
|
| 229 |
+
|
| 230 |
+
# Image sizes
|
| 231 |
+
gs = max(int(model.stride.max()), 32) # grid size (max stride)
|
| 232 |
+
nl = model.model[-1].nl # number of detection layers (used for scaling hyp['obj'])
|
| 233 |
+
imgsz, imgsz_test = [check_img_size(x, gs) for x in opt.img_size] # verify imgsz are gs-multiples
|
| 234 |
+
|
| 235 |
+
# DP mode
|
| 236 |
+
if cuda and rank == -1 and torch.cuda.device_count() > 1:
|
| 237 |
+
model = torch.nn.DataParallel(model)
|
| 238 |
+
|
| 239 |
+
# SyncBatchNorm
|
| 240 |
+
if opt.sync_bn and cuda and rank != -1:
|
| 241 |
+
model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model).to(device)
|
| 242 |
+
logger.info('Using SyncBatchNorm()')
|
| 243 |
+
|
| 244 |
+
# Trainloader
|
| 245 |
+
dataloader, dataset = create_dataloader(train_path, imgsz, batch_size, gs, opt,
|
| 246 |
+
hyp=hyp, augment=True, cache=opt.cache_images, rect=opt.rect, rank=rank,
|
| 247 |
+
world_size=opt.world_size, workers=opt.workers,
|
| 248 |
+
image_weights=opt.image_weights, quad=opt.quad, prefix=colorstr('train: '))
|
| 249 |
+
mlc = np.concatenate(dataset.labels, 0)[:, 0].max() # max label class
|
| 250 |
+
nb = len(dataloader) # number of batches
|
| 251 |
+
assert mlc < nc, 'Label class %g exceeds nc=%g in %s. Possible class labels are 0-%g' % (mlc, nc, opt.data, nc - 1)
|
| 252 |
+
|
| 253 |
+
# Process 0
|
| 254 |
+
if rank in [-1, 0]:
|
| 255 |
+
testloader = create_dataloader(test_path, imgsz_test, batch_size * 2, gs, opt, # testloader
|
| 256 |
+
hyp=hyp, cache=opt.cache_images and not opt.notest, rect=True, rank=-1,
|
| 257 |
+
world_size=opt.world_size, workers=opt.workers,
|
| 258 |
+
pad=0.5, prefix=colorstr('val: '))[0]
|
| 259 |
+
|
| 260 |
+
if not opt.resume:
|
| 261 |
+
labels = np.concatenate(dataset.labels, 0)
|
| 262 |
+
c = torch.tensor(labels[:, 0]) # classes
|
| 263 |
+
# cf = torch.bincount(c.long(), minlength=nc) + 1. # frequency
|
| 264 |
+
# model._initialize_biases(cf.to(device))
|
| 265 |
+
if plots:
|
| 266 |
+
#plot_labels(labels, names, save_dir, loggers)
|
| 267 |
+
if tb_writer:
|
| 268 |
+
tb_writer.add_histogram('classes', c, 0)
|
| 269 |
+
|
| 270 |
+
# Anchors
|
| 271 |
+
if not opt.noautoanchor:
|
| 272 |
+
check_anchors(dataset, model=model, thr=hyp['anchor_t'], imgsz=imgsz)
|
| 273 |
+
model.half().float() # pre-reduce anchor precision
|
| 274 |
+
|
| 275 |
+
# DDP mode
|
| 276 |
+
if cuda and rank != -1:
|
| 277 |
+
model = DDP(model, device_ids=[opt.local_rank], output_device=opt.local_rank,
|
| 278 |
+
# nn.MultiheadAttention incompatibility with DDP https://github.com/pytorch/pytorch/issues/26698
|
| 279 |
+
find_unused_parameters=any(isinstance(layer, nn.MultiheadAttention) for layer in model.modules()))
|
| 280 |
+
|
| 281 |
+
# Model parameters
|
| 282 |
+
hyp['box'] *= 3. / nl # scale to layers
|
| 283 |
+
hyp['cls'] *= nc / 80. * 3. / nl # scale to classes and layers
|
| 284 |
+
hyp['obj'] *= (imgsz / 640) ** 2 * 3. / nl # scale to image size and layers
|
| 285 |
+
hyp['label_smoothing'] = opt.label_smoothing
|
| 286 |
+
model.nc = nc # attach number of classes to model
|
| 287 |
+
model.hyp = hyp # attach hyperparameters to model
|
| 288 |
+
model.gr = 1.0 # iou loss ratio (obj_loss = 1.0 or iou)
|
| 289 |
+
model.class_weights = labels_to_class_weights(dataset.labels, nc).to(device) * nc # attach class weights
|
| 290 |
+
model.names = names
|
| 291 |
+
|
| 292 |
+
# Start training
|
| 293 |
+
t0 = time.time()
|
| 294 |
+
nw = max(round(hyp['warmup_epochs'] * nb), 1000) # number of warmup iterations, max(3 epochs, 1k iterations)
|
| 295 |
+
# nw = min(nw, (epochs - start_epoch) / 2 * nb) # limit warmup to < 1/2 of training
|
| 296 |
+
maps = np.zeros(nc) # mAP per class
|
| 297 |
+
results = (0, 0, 0, 0, 0, 0, 0) # P, R, mAP@.5, mAP@.5-.95, val_loss(box, obj, cls)
|
| 298 |
+
scheduler.last_epoch = start_epoch - 1 # do not move
|
| 299 |
+
scaler = amp.GradScaler(enabled=cuda)
|
| 300 |
+
compute_loss_ota = ComputeLossOTA(model) # init loss class
|
| 301 |
+
compute_loss = ComputeLoss(model) # init loss class
|
| 302 |
+
logger.info(f'Image sizes {imgsz} train, {imgsz_test} test\n'
|
| 303 |
+
f'Using {dataloader.num_workers} dataloader workers\n'
|
| 304 |
+
f'Logging results to {save_dir}\n'
|
| 305 |
+
f'Starting training for {epochs} epochs...')
|
| 306 |
+
torch.save(model, wdir / 'init.pt')
|
| 307 |
+
for epoch in range(start_epoch, epochs): # epoch ------------------------------------------------------------------
|
| 308 |
+
model.train()
|
| 309 |
+
|
| 310 |
+
# Update image weights (optional)
|
| 311 |
+
if opt.image_weights:
|
| 312 |
+
# Generate indices
|
| 313 |
+
if rank in [-1, 0]:
|
| 314 |
+
cw = model.class_weights.cpu().numpy() * (1 - maps) ** 2 / nc # class weights
|
| 315 |
+
iw = labels_to_image_weights(dataset.labels, nc=nc, class_weights=cw) # image weights
|
| 316 |
+
dataset.indices = random.choices(range(dataset.n), weights=iw, k=dataset.n) # rand weighted idx
|
| 317 |
+
# Broadcast if DDP
|
| 318 |
+
if rank != -1:
|
| 319 |
+
indices = (torch.tensor(dataset.indices) if rank == 0 else torch.zeros(dataset.n)).int()
|
| 320 |
+
dist.broadcast(indices, 0)
|
| 321 |
+
if rank != 0:
|
| 322 |
+
dataset.indices = indices.cpu().numpy()
|
| 323 |
+
|
| 324 |
+
# Update mosaic border
|
| 325 |
+
# b = int(random.uniform(0.25 * imgsz, 0.75 * imgsz + gs) // gs * gs)
|
| 326 |
+
# dataset.mosaic_border = [b - imgsz, -b] # height, width borders
|
| 327 |
+
|
| 328 |
+
mloss = torch.zeros(4, device=device) # mean losses
|
| 329 |
+
if rank != -1:
|
| 330 |
+
dataloader.sampler.set_epoch(epoch)
|
| 331 |
+
pbar = enumerate(dataloader)
|
| 332 |
+
logger.info(('\n' + '%10s' * 8) % ('Epoch', 'gpu_mem', 'box', 'obj', 'cls', 'total', 'labels', 'img_size'))
|
| 333 |
+
if rank in [-1, 0]:
|
| 334 |
+
pbar = tqdm(pbar, total=nb) # progress bar
|
| 335 |
+
optimizer.zero_grad()
|
| 336 |
+
for i, (imgs, targets, paths, _) in pbar: # batch -------------------------------------------------------------
|
| 337 |
+
ni = i + nb * epoch # number integrated batches (since train start)
|
| 338 |
+
imgs = imgs.to(device, non_blocking=True).float() / 255.0 # uint8 to float32, 0-255 to 0.0-1.0
|
| 339 |
+
|
| 340 |
+
# Warmup
|
| 341 |
+
if ni <= nw:
|
| 342 |
+
xi = [0, nw] # x interp
|
| 343 |
+
# model.gr = np.interp(ni, xi, [0.0, 1.0]) # iou loss ratio (obj_loss = 1.0 or iou)
|
| 344 |
+
accumulate = max(1, np.interp(ni, xi, [1, nbs / total_batch_size]).round())
|
| 345 |
+
for j, x in enumerate(optimizer.param_groups):
|
| 346 |
+
# bias lr falls from 0.1 to lr0, all other lrs rise from 0.0 to lr0
|
| 347 |
+
x['lr'] = np.interp(ni, xi, [hyp['warmup_bias_lr'] if j == 2 else 0.0, x['initial_lr'] * lf(epoch)])
|
| 348 |
+
if 'momentum' in x:
|
| 349 |
+
x['momentum'] = np.interp(ni, xi, [hyp['warmup_momentum'], hyp['momentum']])
|
| 350 |
+
|
| 351 |
+
# Multi-scale
|
| 352 |
+
if opt.multi_scale:
|
| 353 |
+
sz = random.randrange(imgsz * 0.5, imgsz * 1.5 + gs) // gs * gs # size
|
| 354 |
+
sf = sz / max(imgs.shape[2:]) # scale factor
|
| 355 |
+
if sf != 1:
|
| 356 |
+
ns = [math.ceil(x * sf / gs) * gs for x in imgs.shape[2:]] # new shape (stretched to gs-multiple)
|
| 357 |
+
imgs = F.interpolate(imgs, size=ns, mode='bilinear', align_corners=False)
|
| 358 |
+
|
| 359 |
+
# Forward
|
| 360 |
+
with amp.autocast(enabled=cuda):
|
| 361 |
+
pred = model(imgs) # forward
|
| 362 |
+
if 'loss_ota' not in hyp or hyp['loss_ota'] == 1:
|
| 363 |
+
loss, loss_items = compute_loss_ota(pred, targets.to(device), imgs) # loss scaled by batch_size
|
| 364 |
+
else:
|
| 365 |
+
loss, loss_items = compute_loss(pred, targets.to(device)) # loss scaled by batch_size
|
| 366 |
+
if rank != -1:
|
| 367 |
+
loss *= opt.world_size # gradient averaged between devices in DDP mode
|
| 368 |
+
if opt.quad:
|
| 369 |
+
loss *= 4.
|
| 370 |
+
|
| 371 |
+
# Backward
|
| 372 |
+
scaler.scale(loss).backward()
|
| 373 |
+
|
| 374 |
+
# Optimize
|
| 375 |
+
if ni % accumulate == 0:
|
| 376 |
+
scaler.step(optimizer) # optimizer.step
|
| 377 |
+
scaler.update()
|
| 378 |
+
optimizer.zero_grad()
|
| 379 |
+
if ema:
|
| 380 |
+
ema.update(model)
|
| 381 |
+
|
| 382 |
+
# Print
|
| 383 |
+
if rank in [-1, 0]:
|
| 384 |
+
mloss = (mloss * i + loss_items) / (i + 1) # update mean losses
|
| 385 |
+
mem = '%.3gG' % (torch.cuda.memory_reserved() / 1E9 if torch.cuda.is_available() else 0) # (GB)
|
| 386 |
+
s = ('%10s' * 2 + '%10.4g' * 6) % (
|
| 387 |
+
'%g/%g' % (epoch, epochs - 1), mem, *mloss, targets.shape[0], imgs.shape[-1])
|
| 388 |
+
pbar.set_description(s)
|
| 389 |
+
|
| 390 |
+
# Plot
|
| 391 |
+
if plots and ni < 10:
|
| 392 |
+
f = save_dir / f'train_batch{ni}.jpg' # filename
|
| 393 |
+
Thread(target=plot_images, args=(imgs, targets, paths, f), daemon=True).start()
|
| 394 |
+
# if tb_writer:
|
| 395 |
+
# tb_writer.add_image(f, result, dataformats='HWC', global_step=epoch)
|
| 396 |
+
# tb_writer.add_graph(torch.jit.trace(model, imgs, strict=False), []) # add model graph
|
| 397 |
+
elif plots and ni == 10 and wandb_logger.wandb:
|
| 398 |
+
wandb_logger.log({"Mosaics": [wandb_logger.wandb.Image(str(x), caption=x.name) for x in
|
| 399 |
+
save_dir.glob('train*.jpg') if x.exists()]})
|
| 400 |
+
|
| 401 |
+
# end batch ------------------------------------------------------------------------------------------------
|
| 402 |
+
# end epoch ----------------------------------------------------------------------------------------------------
|
| 403 |
+
|
| 404 |
+
# Scheduler
|
| 405 |
+
lr = [x['lr'] for x in optimizer.param_groups] # for tensorboard
|
| 406 |
+
scheduler.step()
|
| 407 |
+
|
| 408 |
+
# DDP process 0 or single-GPU
|
| 409 |
+
if rank in [-1, 0]:
|
| 410 |
+
# mAP
|
| 411 |
+
ema.update_attr(model, include=['yaml', 'nc', 'hyp', 'gr', 'names', 'stride', 'class_weights'])
|
| 412 |
+
final_epoch = epoch + 1 == epochs
|
| 413 |
+
if not opt.notest or final_epoch: # Calculate mAP
|
| 414 |
+
wandb_logger.current_epoch = epoch + 1
|
| 415 |
+
results, maps, times = test.test(data_dict,
|
| 416 |
+
batch_size=batch_size * 2,
|
| 417 |
+
imgsz=imgsz_test,
|
| 418 |
+
model=ema.ema,
|
| 419 |
+
single_cls=opt.single_cls,
|
| 420 |
+
dataloader=testloader,
|
| 421 |
+
save_dir=save_dir,
|
| 422 |
+
verbose=nc < 50 and final_epoch,
|
| 423 |
+
plots=plots and final_epoch,
|
| 424 |
+
wandb_logger=wandb_logger,
|
| 425 |
+
compute_loss=compute_loss,
|
| 426 |
+
is_coco=is_coco,
|
| 427 |
+
v5_metric=opt.v5_metric)
|
| 428 |
+
|
| 429 |
+
# Write
|
| 430 |
+
with open(results_file, 'a') as f:
|
| 431 |
+
f.write(s + '%10.4g' * 7 % results + '\n') # append metrics, val_loss
|
| 432 |
+
if len(opt.name) and opt.bucket:
|
| 433 |
+
os.system('gsutil cp %s gs://%s/results/results%s.txt' % (results_file, opt.bucket, opt.name))
|
| 434 |
+
|
| 435 |
+
# Log
|
| 436 |
+
tags = ['train/box_loss', 'train/obj_loss', 'train/cls_loss', # train loss
|
| 437 |
+
'metrics/precision', 'metrics/recall', 'metrics/mAP_0.5', 'metrics/mAP_0.5:0.95',
|
| 438 |
+
'val/box_loss', 'val/obj_loss', 'val/cls_loss', # val loss
|
| 439 |
+
'x/lr0', 'x/lr1', 'x/lr2'] # params
|
| 440 |
+
for x, tag in zip(list(mloss[:-1]) + list(results) + lr, tags):
|
| 441 |
+
if tb_writer:
|
| 442 |
+
tb_writer.add_scalar(tag, x, epoch) # tensorboard
|
| 443 |
+
if wandb_logger.wandb:
|
| 444 |
+
wandb_logger.log({tag: x}) # W&B
|
| 445 |
+
|
| 446 |
+
# Update best mAP
|
| 447 |
+
fi = fitness(np.array(results).reshape(1, -1)) # weighted combination of [P, R, mAP@.5, mAP@.5-.95]
|
| 448 |
+
if fi > best_fitness:
|
| 449 |
+
best_fitness = fi
|
| 450 |
+
wandb_logger.end_epoch(best_result=best_fitness == fi)
|
| 451 |
+
|
| 452 |
+
# Save model
|
| 453 |
+
if (not opt.nosave) or (final_epoch and not opt.evolve): # if save
|
| 454 |
+
ckpt = {'epoch': epoch,
|
| 455 |
+
'best_fitness': best_fitness,
|
| 456 |
+
'training_results': results_file.read_text(),
|
| 457 |
+
'model': deepcopy(model.module if is_parallel(model) else model).half(),
|
| 458 |
+
'ema': deepcopy(ema.ema).half(),
|
| 459 |
+
'updates': ema.updates,
|
| 460 |
+
'optimizer': optimizer.state_dict(),
|
| 461 |
+
'wandb_id': wandb_logger.wandb_run.id if wandb_logger.wandb else None}
|
| 462 |
+
|
| 463 |
+
# Save last, best and delete
|
| 464 |
+
torch.save(ckpt, last)
|
| 465 |
+
if best_fitness == fi:
|
| 466 |
+
torch.save(ckpt, best)
|
| 467 |
+
if (best_fitness == fi) and (epoch >= 200):
|
| 468 |
+
torch.save(ckpt, wdir / 'best_{:03d}.pt'.format(epoch))
|
| 469 |
+
if epoch == 0:
|
| 470 |
+
torch.save(ckpt, wdir / 'epoch_{:03d}.pt'.format(epoch))
|
| 471 |
+
elif ((epoch+1) % 25) == 0:
|
| 472 |
+
torch.save(ckpt, wdir / 'epoch_{:03d}.pt'.format(epoch))
|
| 473 |
+
elif epoch >= (epochs-5):
|
| 474 |
+
torch.save(ckpt, wdir / 'epoch_{:03d}.pt'.format(epoch))
|
| 475 |
+
if wandb_logger.wandb:
|
| 476 |
+
if ((epoch + 1) % opt.save_period == 0 and not final_epoch) and opt.save_period != -1:
|
| 477 |
+
wandb_logger.log_model(
|
| 478 |
+
last.parent, opt, epoch, fi, best_model=best_fitness == fi)
|
| 479 |
+
del ckpt
|
| 480 |
+
|
| 481 |
+
# end epoch ----------------------------------------------------------------------------------------------------
|
| 482 |
+
# end training
|
| 483 |
+
if rank in [-1, 0]:
|
| 484 |
+
# Plots
|
| 485 |
+
if plots:
|
| 486 |
+
plot_results(save_dir=save_dir) # save as results.png
|
| 487 |
+
if wandb_logger.wandb:
|
| 488 |
+
files = ['results.png', 'confusion_matrix.png', *[f'{x}_curve.png' for x in ('F1', 'PR', 'P', 'R')]]
|
| 489 |
+
wandb_logger.log({"Results": [wandb_logger.wandb.Image(str(save_dir / f), caption=f) for f in files
|
| 490 |
+
if (save_dir / f).exists()]})
|
| 491 |
+
# Test best.pt
|
| 492 |
+
logger.info('%g epochs completed in %.3f hours.\n' % (epoch - start_epoch + 1, (time.time() - t0) / 3600))
|
| 493 |
+
if opt.data.endswith('coco.yaml') and nc == 80: # if COCO
|
| 494 |
+
for m in (last, best) if best.exists() else (last): # speed, mAP tests
|
| 495 |
+
results, _, _ = test.test(opt.data,
|
| 496 |
+
batch_size=batch_size * 2,
|
| 497 |
+
imgsz=imgsz_test,
|
| 498 |
+
conf_thres=0.001,
|
| 499 |
+
iou_thres=0.7,
|
| 500 |
+
model=attempt_load(m, device).half(),
|
| 501 |
+
single_cls=opt.single_cls,
|
| 502 |
+
dataloader=testloader,
|
| 503 |
+
save_dir=save_dir,
|
| 504 |
+
save_json=True,
|
| 505 |
+
plots=False,
|
| 506 |
+
is_coco=is_coco,
|
| 507 |
+
v5_metric=opt.v5_metric)
|
| 508 |
+
|
| 509 |
+
# Strip optimizers
|
| 510 |
+
final = best if best.exists() else last # final model
|
| 511 |
+
for f in last, best:
|
| 512 |
+
if f.exists():
|
| 513 |
+
strip_optimizer(f) # strip optimizers
|
| 514 |
+
if opt.bucket:
|
| 515 |
+
os.system(f'gsutil cp {final} gs://{opt.bucket}/weights') # upload
|
| 516 |
+
if wandb_logger.wandb and not opt.evolve: # Log the stripped model
|
| 517 |
+
wandb_logger.wandb.log_artifact(str(final), type='model',
|
| 518 |
+
name='run_' + wandb_logger.wandb_run.id + '_model',
|
| 519 |
+
aliases=['last', 'best', 'stripped'])
|
| 520 |
+
wandb_logger.finish_run()
|
| 521 |
+
else:
|
| 522 |
+
dist.destroy_process_group()
|
| 523 |
+
torch.cuda.empty_cache()
|
| 524 |
+
return results
|
| 525 |
+
|
| 526 |
+
|
| 527 |
+
if __name__ == '__main__':
|
| 528 |
+
parser = argparse.ArgumentParser()
|
| 529 |
+
parser.add_argument('--weights', type=str, default='yolo7.pt', help='initial weights path')
|
| 530 |
+
parser.add_argument('--cfg', type=str, default='', help='model.yaml path')
|
| 531 |
+
parser.add_argument('--data', type=str, default='data/coco.yaml', help='data.yaml path')
|
| 532 |
+
parser.add_argument('--hyp', type=str, default='data/hyp.scratch.p5.yaml', help='hyperparameters path')
|
| 533 |
+
parser.add_argument('--epochs', type=int, default=300)
|
| 534 |
+
parser.add_argument('--batch-size', type=int, default=16, help='total batch size for all GPUs')
|
| 535 |
+
parser.add_argument('--img-size', nargs='+', type=int, default=[640, 640], help='[train, test] image sizes')
|
| 536 |
+
parser.add_argument('--rect', action='store_true', help='rectangular training')
|
| 537 |
+
parser.add_argument('--resume', nargs='?', const=True, default=False, help='resume most recent training')
|
| 538 |
+
parser.add_argument('--nosave', action='store_true', help='only save final checkpoint')
|
| 539 |
+
parser.add_argument('--notest', action='store_true', help='only test final epoch')
|
| 540 |
+
parser.add_argument('--noautoanchor', action='store_true', help='disable autoanchor check')
|
| 541 |
+
parser.add_argument('--evolve', action='store_true', help='evolve hyperparameters')
|
| 542 |
+
parser.add_argument('--bucket', type=str, default='', help='gsutil bucket')
|
| 543 |
+
parser.add_argument('--cache-images', action='store_true', help='cache images for faster training')
|
| 544 |
+
parser.add_argument('--image-weights', action='store_true', help='use weighted image selection for training')
|
| 545 |
+
parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
|
| 546 |
+
parser.add_argument('--multi-scale', action='store_true', help='vary img-size +/- 50%%')
|
| 547 |
+
parser.add_argument('--single-cls', action='store_true', help='train multi-class data as single-class')
|
| 548 |
+
parser.add_argument('--adam', action='store_true', help='use torch.optim.Adam() optimizer')
|
| 549 |
+
parser.add_argument('--sync-bn', action='store_true', help='use SyncBatchNorm, only available in DDP mode')
|
| 550 |
+
parser.add_argument('--local_rank', type=int, default=-1, help='DDP parameter, do not modify')
|
| 551 |
+
parser.add_argument('--workers', type=int, default=8, help='maximum number of dataloader workers')
|
| 552 |
+
parser.add_argument('--project', default='runs/train', help='save to project/name')
|
| 553 |
+
parser.add_argument('--entity', default=None, help='W&B entity')
|
| 554 |
+
parser.add_argument('--name', default='exp', help='save to project/name')
|
| 555 |
+
parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')
|
| 556 |
+
parser.add_argument('--quad', action='store_true', help='quad dataloader')
|
| 557 |
+
parser.add_argument('--linear-lr', action='store_true', help='linear LR')
|
| 558 |
+
parser.add_argument('--label-smoothing', type=float, default=0.0, help='Label smoothing epsilon')
|
| 559 |
+
parser.add_argument('--upload_dataset', action='store_true', help='Upload dataset as W&B artifact table')
|
| 560 |
+
parser.add_argument('--bbox_interval', type=int, default=-1, help='Set bounding-box image logging interval for W&B')
|
| 561 |
+
parser.add_argument('--save_period', type=int, default=-1, help='Log model after every "save_period" epoch')
|
| 562 |
+
parser.add_argument('--artifact_alias', type=str, default="latest", help='version of dataset artifact to be used')
|
| 563 |
+
parser.add_argument('--freeze', nargs='+', type=int, default=[0], help='Freeze layers: backbone of yolov7=50, first3=0 1 2')
|
| 564 |
+
parser.add_argument('--v5-metric', action='store_true', help='assume maximum recall as 1.0 in AP calculation')
|
| 565 |
+
opt = parser.parse_args()
|
| 566 |
+
|
| 567 |
+
# Set DDP variables
|
| 568 |
+
opt.world_size = int(os.environ['WORLD_SIZE']) if 'WORLD_SIZE' in os.environ else 1
|
| 569 |
+
opt.global_rank = int(os.environ['RANK']) if 'RANK' in os.environ else -1
|
| 570 |
+
set_logging(opt.global_rank)
|
| 571 |
+
#if opt.global_rank in [-1, 0]:
|
| 572 |
+
# check_git_status()
|
| 573 |
+
# check_requirements()
|
| 574 |
+
|
| 575 |
+
# Resume
|
| 576 |
+
wandb_run = check_wandb_resume(opt)
|
| 577 |
+
if opt.resume and not wandb_run: # resume an interrupted run
|
| 578 |
+
ckpt = opt.resume if isinstance(opt.resume, str) else get_latest_run() # specified or most recent path
|
| 579 |
+
assert os.path.isfile(ckpt), 'ERROR: --resume checkpoint does not exist'
|
| 580 |
+
apriori = opt.global_rank, opt.local_rank
|
| 581 |
+
with open(Path(ckpt).parent.parent / 'opt.yaml') as f:
|
| 582 |
+
opt = argparse.Namespace(**yaml.load(f, Loader=yaml.SafeLoader)) # replace
|
| 583 |
+
opt.cfg, opt.weights, opt.resume, opt.batch_size, opt.global_rank, opt.local_rank = '', ckpt, True, opt.total_batch_size, *apriori # reinstate
|
| 584 |
+
logger.info('Resuming training from %s' % ckpt)
|
| 585 |
+
else:
|
| 586 |
+
# opt.hyp = opt.hyp or ('hyp.finetune.yaml' if opt.weights else 'hyp.scratch.yaml')
|
| 587 |
+
opt.data, opt.cfg, opt.hyp = check_file(opt.data), check_file(opt.cfg), check_file(opt.hyp) # check files
|
| 588 |
+
assert len(opt.cfg) or len(opt.weights), 'either --cfg or --weights must be specified'
|
| 589 |
+
opt.img_size.extend([opt.img_size[-1]] * (2 - len(opt.img_size))) # extend to 2 sizes (train, test)
|
| 590 |
+
opt.name = 'evolve' if opt.evolve else opt.name
|
| 591 |
+
opt.save_dir = increment_path(Path(opt.project) / opt.name, exist_ok=opt.exist_ok | opt.evolve) # increment run
|
| 592 |
+
|
| 593 |
+
# DDP mode
|
| 594 |
+
opt.total_batch_size = opt.batch_size
|
| 595 |
+
device = select_device(opt.device, batch_size=opt.batch_size)
|
| 596 |
+
if opt.local_rank != -1:
|
| 597 |
+
assert torch.cuda.device_count() > opt.local_rank
|
| 598 |
+
torch.cuda.set_device(opt.local_rank)
|
| 599 |
+
device = torch.device('cuda', opt.local_rank)
|
| 600 |
+
dist.init_process_group(backend='nccl', init_method='env://') # distributed backend
|
| 601 |
+
assert opt.batch_size % opt.world_size == 0, '--batch-size must be multiple of CUDA device count'
|
| 602 |
+
opt.batch_size = opt.total_batch_size // opt.world_size
|
| 603 |
+
|
| 604 |
+
# Hyperparameters
|
| 605 |
+
with open(opt.hyp) as f:
|
| 606 |
+
hyp = yaml.load(f, Loader=yaml.SafeLoader) # load hyps
|
| 607 |
+
|
| 608 |
+
# Train
|
| 609 |
+
logger.info(opt)
|
| 610 |
+
if not opt.evolve:
|
| 611 |
+
tb_writer = None # init loggers
|
| 612 |
+
if opt.global_rank in [-1, 0]:
|
| 613 |
+
prefix = colorstr('tensorboard: ')
|
| 614 |
+
logger.info(f"{prefix}Start with 'tensorboard --logdir {opt.project}', view at http://localhost:6006/")
|
| 615 |
+
tb_writer = SummaryWriter(opt.save_dir) # Tensorboard
|
| 616 |
+
train(hyp, opt, device, tb_writer)
|
| 617 |
+
|
| 618 |
+
# Evolve hyperparameters (optional)
|
| 619 |
+
else:
|
| 620 |
+
# Hyperparameter evolution metadata (mutation scale 0-1, lower_limit, upper_limit)
|
| 621 |
+
meta = {'lr0': (1, 1e-5, 1e-1), # initial learning rate (SGD=1E-2, Adam=1E-3)
|
| 622 |
+
'lrf': (1, 0.01, 1.0), # final OneCycleLR learning rate (lr0 * lrf)
|
| 623 |
+
'momentum': (0.3, 0.6, 0.98), # SGD momentum/Adam beta1
|
| 624 |
+
'weight_decay': (1, 0.0, 0.001), # optimizer weight decay
|
| 625 |
+
'warmup_epochs': (1, 0.0, 5.0), # warmup epochs (fractions ok)
|
| 626 |
+
'warmup_momentum': (1, 0.0, 0.95), # warmup initial momentum
|
| 627 |
+
'warmup_bias_lr': (1, 0.0, 0.2), # warmup initial bias lr
|
| 628 |
+
'box': (1, 0.02, 0.2), # box loss gain
|
| 629 |
+
'cls': (1, 0.2, 4.0), # cls loss gain
|
| 630 |
+
'cls_pw': (1, 0.5, 2.0), # cls BCELoss positive_weight
|
| 631 |
+
'obj': (1, 0.2, 4.0), # obj loss gain (scale with pixels)
|
| 632 |
+
'obj_pw': (1, 0.5, 2.0), # obj BCELoss positive_weight
|
| 633 |
+
'iou_t': (0, 0.1, 0.7), # IoU training threshold
|
| 634 |
+
'anchor_t': (1, 2.0, 8.0), # anchor-multiple threshold
|
| 635 |
+
'anchors': (2, 2.0, 10.0), # anchors per output grid (0 to ignore)
|
| 636 |
+
'fl_gamma': (0, 0.0, 2.0), # focal loss gamma (efficientDet default gamma=1.5)
|
| 637 |
+
'hsv_h': (1, 0.0, 0.1), # image HSV-Hue augmentation (fraction)
|
| 638 |
+
'hsv_s': (1, 0.0, 0.9), # image HSV-Saturation augmentation (fraction)
|
| 639 |
+
'hsv_v': (1, 0.0, 0.9), # image HSV-Value augmentation (fraction)
|
| 640 |
+
'degrees': (1, 0.0, 45.0), # image rotation (+/- deg)
|
| 641 |
+
'translate': (1, 0.0, 0.9), # image translation (+/- fraction)
|
| 642 |
+
'scale': (1, 0.0, 0.9), # image scale (+/- gain)
|
| 643 |
+
'shear': (1, 0.0, 10.0), # image shear (+/- deg)
|
| 644 |
+
'perspective': (0, 0.0, 0.001), # image perspective (+/- fraction), range 0-0.001
|
| 645 |
+
'flipud': (1, 0.0, 1.0), # image flip up-down (probability)
|
| 646 |
+
'fliplr': (0, 0.0, 1.0), # image flip left-right (probability)
|
| 647 |
+
'mosaic': (1, 0.0, 1.0), # image mixup (probability)
|
| 648 |
+
'mixup': (1, 0.0, 1.0), # image mixup (probability)
|
| 649 |
+
'copy_paste': (1, 0.0, 1.0), # segment copy-paste (probability)
|
| 650 |
+
'paste_in': (1, 0.0, 1.0)} # segment copy-paste (probability)
|
| 651 |
+
|
| 652 |
+
with open(opt.hyp, errors='ignore') as f:
|
| 653 |
+
hyp = yaml.safe_load(f) # load hyps dict
|
| 654 |
+
if 'anchors' not in hyp: # anchors commented in hyp.yaml
|
| 655 |
+
hyp['anchors'] = 3
|
| 656 |
+
|
| 657 |
+
assert opt.local_rank == -1, 'DDP mode not implemented for --evolve'
|
| 658 |
+
opt.notest, opt.nosave = True, True # only test/save final epoch
|
| 659 |
+
# ei = [isinstance(x, (int, float)) for x in hyp.values()] # evolvable indices
|
| 660 |
+
yaml_file = Path(opt.save_dir) / 'hyp_evolved.yaml' # save best result here
|
| 661 |
+
if opt.bucket:
|
| 662 |
+
os.system('gsutil cp gs://%s/evolve.txt .' % opt.bucket) # download evolve.txt if exists
|
| 663 |
+
|
| 664 |
+
for _ in range(300): # generations to evolve
|
| 665 |
+
if Path('evolve.txt').exists(): # if evolve.txt exists: select best hyps and mutate
|
| 666 |
+
# Select parent(s)
|
| 667 |
+
parent = 'single' # parent selection method: 'single' or 'weighted'
|
| 668 |
+
x = np.loadtxt('evolve.txt', ndmin=2)
|
| 669 |
+
n = min(5, len(x)) # number of previous results to consider
|
| 670 |
+
x = x[np.argsort(-fitness(x))][:n] # top n mutations
|
| 671 |
+
w = fitness(x) - fitness(x).min() # weights
|
| 672 |
+
if parent == 'single' or len(x) == 1:
|
| 673 |
+
# x = x[random.randint(0, n - 1)] # random selection
|
| 674 |
+
x = x[random.choices(range(n), weights=w)[0]] # weighted selection
|
| 675 |
+
elif parent == 'weighted':
|
| 676 |
+
x = (x * w.reshape(n, 1)).sum(0) / w.sum() # weighted combination
|
| 677 |
+
|
| 678 |
+
# Mutate
|
| 679 |
+
mp, s = 0.8, 0.2 # mutation probability, sigma
|
| 680 |
+
npr = np.random
|
| 681 |
+
npr.seed(int(time.time()))
|
| 682 |
+
g = np.array([x[0] for x in meta.values()]) # gains 0-1
|
| 683 |
+
ng = len(meta)
|
| 684 |
+
v = np.ones(ng)
|
| 685 |
+
while all(v == 1): # mutate until a change occurs (prevent duplicates)
|
| 686 |
+
v = (g * (npr.random(ng) < mp) * npr.randn(ng) * npr.random() * s + 1).clip(0.3, 3.0)
|
| 687 |
+
for i, k in enumerate(hyp.keys()): # plt.hist(v.ravel(), 300)
|
| 688 |
+
hyp[k] = float(x[i + 7] * v[i]) # mutate
|
| 689 |
+
|
| 690 |
+
# Constrain to limits
|
| 691 |
+
for k, v in meta.items():
|
| 692 |
+
hyp[k] = max(hyp[k], v[1]) # lower limit
|
| 693 |
+
hyp[k] = min(hyp[k], v[2]) # upper limit
|
| 694 |
+
hyp[k] = round(hyp[k], 5) # significant digits
|
| 695 |
+
|
| 696 |
+
# Train mutation
|
| 697 |
+
results = train(hyp.copy(), opt, device)
|
| 698 |
+
|
| 699 |
+
# Write mutation results
|
| 700 |
+
print_mutation(hyp.copy(), results, yaml_file, opt.bucket)
|
| 701 |
+
|
| 702 |
+
# Plot results
|
| 703 |
+
plot_evolution(yaml_file)
|
| 704 |
+
print(f'Hyperparameter evolution complete. Best results saved as: {yaml_file}\n'
|
| 705 |
+
f'Command to train a new model with these hyperparameters: $ python train.py --hyp {yaml_file}')
|
| 706 |
+
|
| 707 |
+
# python train.py --workers 1 --device 0 --batch-size 1 --epochs 200 --img 1280 1280 --data data/coco.yaml --hyp data/hyp.scratch.custom.yaml --cfg cfg/training/yolov7.yaml --name junga --weights yolov7.pt
|
์๋ณธ์ด๋ฏธ์ง๊ฒฐ๊ณผ์ถ์ถ.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import pandas as pd
|
| 3 |
+
|
| 4 |
+
# ํด๋ ๊ฒฝ๋ก ์ค์
|
| 5 |
+
base_folder = r"D:\์ง์ \ํฌ์ธ๋ฉ\์ฌ์
๊ณผ์ \์ ๋ถ๊ณผ์ \01. 2024๋
์ ์กฐ์
AI์ตํฉ ๊ธฐ๋ฐ ์กฐ์ฑ ์ฌ์
\์ ์์ ๋ฐ\35. test์ฉ ์ด๋ฏธ์ง"
|
| 6 |
+
|
| 7 |
+
# ๋ฐ์ดํฐ ์ ์ฅ์ ์ํ ๋ฆฌ์คํธ
|
| 8 |
+
data = []
|
| 9 |
+
|
| 10 |
+
# ๊ฐ ํด๋๋ฅผ ์ํํ๋ฉฐ ํ์ผ๋ช
๊ณผ ์ ํ๋ช
(ํด๋๋ช
) ์์ง
|
| 11 |
+
for folder_name in os.listdir(base_folder):
|
| 12 |
+
folder_path = os.path.join(base_folder, folder_name)
|
| 13 |
+
if os.path.isdir(folder_path): # ํด๋์ธ์ง ํ์ธ
|
| 14 |
+
for file_name in os.listdir(folder_path):
|
| 15 |
+
file_path = os.path.join(folder_path, file_name)
|
| 16 |
+
if os.path.isfile(file_path): # ํ์ผ์ธ์ง ํ์ธ
|
| 17 |
+
data.append([file_name, folder_name])
|
| 18 |
+
|
| 19 |
+
# ๋ฐ์ดํฐํ๋ ์์ผ๋ก ๋ณํ
|
| 20 |
+
df = pd.DataFrame(data, columns=["ํ์ผ๋ช
", "์ ํ๋ช
"])
|
| 21 |
+
|
| 22 |
+
# ์์
ํ์ผ๋ก ์ ์ฅ
|
| 23 |
+
output_excel = "output.xlsx"
|
| 24 |
+
df.to_excel(output_excel, index=False)
|
| 25 |
+
|
| 26 |
+
print(f"์์
ํ์ผ์ด ์์ฑ๋์์ต๋๋ค: {output_excel}")
|
์ด๋ฏธ์ง ์ด๋.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import shutil
|
| 3 |
+
|
| 4 |
+
# ํด๋ ๊ฒฝ๋ก ์ค์
|
| 5 |
+
txt_folder = r'C:\Users\202408\Desktop\venv\์ ์์ ๋ฐ_์ด๋ฏธ์ง_๋ผ๋ฒจ๋ง\๋ฐ์ดํฐ ๋ผ๋ฒจ๋ง(v.0.2)\03_train_val\val\labeled_text(10.01-18)' # txt ํ์ผ๋ค์ด ์๋ ํด๋ ๊ฒฝ๋ก
|
| 6 |
+
labeled_image_folder = r"C:\Users\202408\Desktop\venv\์ ์์ ๋ฐ_์ด๋ฏธ์ง_๋ผ๋ฒจ๋ง\๋ฐ์ดํฐ ๋ผ๋ฒจ๋ง(v.0.2)\02_๋ผ๋ฒจ๋ง_ํ_ํ
์คํธ_๋ฐ_๊ด๋ จ_์ด๋ฏธ์ง_๋ฐ์ดํฐ\labeled_image(10.01-18)" # ์ด๋ฏธ์ง ํ์ผ๋ค์ด ์๋ ํด๋ ๊ฒฝ๋ก
|
| 7 |
+
destination_folder = r"C:\Users\202408\Desktop\venv\์ ์์ ๋ฐ_์ด๋ฏธ์ง_๋ผ๋ฒจ๋ง\๋ฐ์ดํฐ ๋ผ๋ฒจ๋ง(v.0.2)\03_train_val\val\labeled_image(10.01-18)" # ์ด๋ฏธ์ง๋ฅผ ์ด๋ํ ๋ชฉ์ ์ง ํด๋ ๊ฒฝ๋ก
|
| 8 |
+
|
| 9 |
+
# ํ์ผ ์ด๋์ ์ํ ํด๋๊ฐ ์์ผ๋ฉด ์์ฑ
|
| 10 |
+
if not os.path.exists(destination_folder):
|
| 11 |
+
os.makedirs(destination_folder)
|
| 12 |
+
|
| 13 |
+
# txt ํ์ผ ๋ฆฌ์คํธ ๊ฐ์ ธ์ค๊ธฐ (ํ์ฅ์๋ฅผ ์ ์ธํ ํ์ผ๋ช
)
|
| 14 |
+
txt_files = [os.path.splitext(f)[0] for f in os.listdir(txt_folder) if f.endswith('.txt')]
|
| 15 |
+
|
| 16 |
+
# ๊ฐ ์ ํ ์ด๋ฆ๋ณ ํด๋ ํ์
|
| 17 |
+
for root, dirs, files in os.walk(labeled_image_folder):
|
| 18 |
+
for txt_file in txt_files:
|
| 19 |
+
# ์ด๋ฏธ์ง ํ์ผ ํ์ฅ์ ์ค์ (์: jpg, png, jpeg)
|
| 20 |
+
image_extensions = ['.jpg', '.png', '.jpeg']
|
| 21 |
+
|
| 22 |
+
# ๋์ผํ ์ด๋ฆ์ ๊ฐ์ง ์ด๋ฏธ์ง ํ์ผ์ด ์๋์ง ํ์ธ
|
| 23 |
+
for ext in image_extensions:
|
| 24 |
+
image_file = os.path.join(root, txt_file + ext)
|
| 25 |
+
if os.path.exists(image_file):
|
| 26 |
+
# ์ด๋ฏธ์ง ํ์ผ์ ๋ชฉ์ ์ง ํด๋๋ก ์ด๋
|
| 27 |
+
shutil.copy2(image_file, os.path.join(destination_folder, os.path.basename(image_file)))
|
| 28 |
+
print(f"Moved: {image_file} to {destination_folder}")
|
์ ํdetect.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import cv2
|
| 3 |
+
import torch
|
| 4 |
+
import openpyxl
|
| 5 |
+
from datetime import datetime
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
from models.experimental import attempt_load
|
| 8 |
+
from utils.datasets import LoadImages
|
| 9 |
+
from utils.general import non_max_suppression, scale_coords
|
| 10 |
+
from utils.plots import plot_one_box
|
| 11 |
+
from utils.torch_utils import select_device
|
| 12 |
+
|
| 13 |
+
# YOLO ๋ํ
์
์ํ ํจ์
|
| 14 |
+
def perform_yolo_detection_on_folder(input_folder, output_folder, opt, excel_file):
|
| 15 |
+
# ๋ชจ๋ธ ์ด๊ธฐํ
|
| 16 |
+
device = select_device(opt.device)
|
| 17 |
+
model = attempt_load(opt.weights, map_location=device) # ๋ชจ๋ธ ๋ก๋
|
| 18 |
+
model.to(device).eval()
|
| 19 |
+
if device.type != 'cpu':
|
| 20 |
+
model.half() # GPU ์ฌ์ฉ ์ FP16 ๋ชจ๋ ํ์ฑํ
|
| 21 |
+
|
| 22 |
+
# ๋ฐ์ดํฐ์
๋ก๋
|
| 23 |
+
dataset = LoadImages(input_folder, img_size=opt.img_size)
|
| 24 |
+
names = model.module.names if hasattr(model, 'module') else model.names
|
| 25 |
+
|
| 26 |
+
# ์ถ๋ ฅ ํด๋ ์์ฑ
|
| 27 |
+
os.makedirs(output_folder, exist_ok=True)
|
| 28 |
+
|
| 29 |
+
# ์์
ํ์ผ ์ค๋น
|
| 30 |
+
if not os.path.exists(excel_file):
|
| 31 |
+
workbook = openpyxl.Workbook()
|
| 32 |
+
sheet = workbook.active
|
| 33 |
+
sheet.title = "Predictions"
|
| 34 |
+
sheet.append(["Filename", "Predicted Class"]) # ํค๋ ์ถ๊ฐ
|
| 35 |
+
workbook.save(excel_file)
|
| 36 |
+
workbook = openpyxl.load_workbook(excel_file)
|
| 37 |
+
sheet = workbook["Predictions"]
|
| 38 |
+
|
| 39 |
+
# ์ด๋ฏธ์ง ์์ธก ์์
|
| 40 |
+
for path, img, im0s, _ in dataset:
|
| 41 |
+
img = torch.from_numpy(img).to(device)
|
| 42 |
+
img = img.half() if device.type != 'cpu' else img.float() # uint8 to fp16/32
|
| 43 |
+
img /= 255.0 # Normalize to 0-1
|
| 44 |
+
if img.ndimension() == 3:
|
| 45 |
+
img = img.unsqueeze(0)
|
| 46 |
+
|
| 47 |
+
predicted_class = "None" # ์ด๊ธฐ๊ฐ
|
| 48 |
+
|
| 49 |
+
with torch.no_grad():
|
| 50 |
+
pred = model(img, augment=opt.augment)[0]
|
| 51 |
+
pred = non_max_suppression(pred, opt.conf_thres, opt.iou_thres, classes=None, agnostic=opt.agnostic_nms)
|
| 52 |
+
|
| 53 |
+
for i, det in enumerate(pred): # ๊ฐ ์ด๋ฏธ์ง์ ๋ํ
์
๊ฒฐ๊ณผ ์ฒ๋ฆฌ
|
| 54 |
+
if len(det):
|
| 55 |
+
det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0s.shape).round()
|
| 56 |
+
for *xyxy, conf, cls in reversed(det):
|
| 57 |
+
predicted_class = names[int(cls)] # ๊ฐ์ฅ ๋ง์ง๋ง ๋ํ
์
๊ฒฐ๊ณผ๋ก ํด๋์ค ์ค์
|
| 58 |
+
label = f"{predicted_class} {conf:.2f}"
|
| 59 |
+
plot_one_box(xyxy, im0s, label=label, color=(255, 0, 0), line_thickness=2)
|
| 60 |
+
|
| 61 |
+
# ๊ฒฐ๊ณผ ์ด๋ฏธ์ง ์ ์ฅ
|
| 62 |
+
save_path = os.path.join(output_folder, Path(path).name)
|
| 63 |
+
cv2.imwrite(save_path, im0s)
|
| 64 |
+
print(f"Processed {path}, saved result to {save_path}")
|
| 65 |
+
|
| 66 |
+
# ์์
์ ํ์ผ๋ช
๊ณผ ์์ธก ๊ฒฐ๊ณผ ์ ์ฅ
|
| 67 |
+
sheet.append([Path(path).name, predicted_class])
|
| 68 |
+
|
| 69 |
+
# ์์
ํ์ผ ์ ์ฅ
|
| 70 |
+
workbook.save(excel_file)
|
| 71 |
+
print(f"Predictions saved to {excel_file}")
|
| 72 |
+
|
| 73 |
+
# Opt ํด๋์ค ์ ์
|
| 74 |
+
class Opt:
|
| 75 |
+
def __init__(self, **kwargs):
|
| 76 |
+
self.weights = kwargs.get('weights', 'path_to_yolo_model.pt')
|
| 77 |
+
self.img_size = int(kwargs.get('img_size', 640))
|
| 78 |
+
self.conf_thres = float(kwargs.get('conf_thres', 0.25))
|
| 79 |
+
self.iou_thres = float(kwargs.get('iou_thres', 0.45))
|
| 80 |
+
self.device = kwargs.get('device', 'cpu') # '0' for GPU, 'cpu' for CPU
|
| 81 |
+
self.augment = kwargs.get('augment', False)
|
| 82 |
+
self.agnostic_nms = kwargs.get('agnostic_nms', False) # ํด๋์ค ๋
๋ฆฝ์ NMS ํ์ฑํ ์ฌ๋ถ
|
| 83 |
+
|
| 84 |
+
# ์คํ
|
| 85 |
+
if __name__ == "__main__":
|
| 86 |
+
input_folder = r"C:\Users\202408\Desktop\venv\yolov7\yolov7\junga_image_text_v03\test" # ์
๋ ฅ ์ด๋ฏธ์ง ํด๋ ๊ฒฝ๋ก
|
| 87 |
+
output_folder = r"C:\Users\202408\Desktop\venv\yolov7\yolov7\junga_image_text_v03\test_result" # ๊ฒฐ๊ณผ ์ ์ฅ ํด๋ ๊ฒฝ๋ก
|
| 88 |
+
excel_file = r"C:\Users\202408\Desktop\venv\yolov7\yolov7\junga_image_text_v03\test_result.xlsx" # ๊ฒฐ๊ณผ ์์
ํ์ผ ๊ฒฝ๋ก
|
| 89 |
+
|
| 90 |
+
opt = Opt(
|
| 91 |
+
weights=r"C:\Users\202408\Desktop\venv\yolov7\yolov7\junga_image_text_v03\best.pt",
|
| 92 |
+
img_size=640,
|
| 93 |
+
conf_thres=0.25,
|
| 94 |
+
iou_thres=0.45,
|
| 95 |
+
device='0' # GPU ์ฌ์ฉ (CPU ์ฌ์ฉ ์ 'cpu'๋ก ๋ณ๊ฒฝ)
|
| 96 |
+
)
|
| 97 |
+
|
| 98 |
+
perform_yolo_detection_on_folder(input_folder, output_folder, opt, excel_file)
|