728x90
반응형
Transform Composition은
- 여러 개의 개별 변환들을 전략적으로 조합한 Pipeline을 만들어서
- 더 다양하고 효과적인 데이터 증강을 수행 가능케 함.
v2에서는 다음과 같은 조합 방식을 제공.
- Compose(순차적 적용),
- RandomChoice(배타적 선택),
- RandomApply(조건부 그룹 적용),
- RandomOrder(순서 랜덤화 적용)
https://docs.pytorch.org/vision/main/transforms.html#composition
Transforming and augmenting images — Torchvision main documentation
Shortcuts
docs.pytorch.org
관련 gist
https://gist.github.com/dsaint31x/325b71e69a62e71b77a1ae96c3fe906c
dl_composition-torchvision-transforms-v2.ipynb
dl_composition-torchvision-transforms-v2.ipynb. GitHub Gist: instantly share code, notes, and snippets.
gist.github.com
Prerequisites
colab에서 다음의 코드들을 수행시켜서 이미지를 다운로드하고 아래의 예제 코드를 수행할 수 있도록 함:
img_path = "assets/astronaut.jpg"
img_url = "https://raw.githubusercontent.com/pytorch/vision/main/gallery/assets/astronaut.jpg"
!mkdir -p assets
!curl -o {img_path} {img_url}
from torchvision.io import decode_image
original_img = decode_image(img_path)
print(f" {type(original_img) = }\n \
{original_img.dtype = }\n \
{original_img.shape = }")
다음은 이미지 출력을 위한 plot 함수임:
더보기
# https://github.com/pytorch/vision/tree/main/gallery/
# 위의 torchvision관련 예제들의 display를 위한 plot함수를 그대로 가져옴.
import matplotlib.pyplot as plt
import torch
from torchvision.utils import draw_bounding_boxes, draw_segmentation_masks
from torchvision import tv_tensors
from torchvision.transforms.v2 import functional as F
def plot(imgs, figsize=None, row_title=None, **imshow_kwargs):
if not isinstance(imgs[0], list):
# Make a 2d grid even if there's just 1 row
imgs = [imgs]
num_rows = len(imgs)
num_cols = len(imgs[0])
_, axs = plt.subplots(nrows=num_rows, ncols=num_cols, squeeze=False, figsize=figsize)
for row_idx, row in enumerate(imgs):
for col_idx, img in enumerate(row):
boxes = None
masks = None
if isinstance(img, tuple):
img, target = img
if isinstance(target, dict):
boxes = target.get("boxes")
masks = target.get("masks")
elif isinstance(target, tv_tensors.BoundingBoxes):
boxes = target
else:
raise ValueError(f"Unexpected target type: {type(target)}")
img = F.to_image(img)
if img.dtype.is_floating_point and img.min() < 0:
# Poor man's re-normalization for the colors to be OK-ish. This
# is useful for images coming out of Normalize()
img -= img.min()
img /= img.max()
img = F.to_dtype(img, torch.uint8, scale=True)
if boxes is not None:
img = draw_bounding_boxes(img, boxes, colors="yellow", width=3)
if masks is not None:
img = draw_segmentation_masks(img, masks.to(torch.bool), colors=["green"] * masks.shape[0], alpha=.65)
ax = axs[row_idx, col_idx]
ax.imshow(img.permute(1, 2, 0).numpy(), **imshow_kwargs)
ax.set(xticklabels=[], yticklabels=[], xticks=[], yticks=[])
if row_title is not None:
for row_idx in range(num_rows):
axs[row_idx, 0].set(ylabel=row_title[row_idx])
plt.tight_layout()
Compose
역할:
- 여러 변환(transformations)들을 순차적으로 연결하여 하나의 Pipeline으로 구성.
- 변환들을 체인(chain) 형태로 조합하여 복합적인 전처리 과정 구현
- 첫 번째 변환의 출력이 두 번째 변환의 입력이 되는 순차 실행
- 데이터 전처리 Pipeline의 기본 구조 제공
사용 용도:
- 전체 데이터 전처리 Pipeline 구성
- Resize → Augmentation → Tensor로 변환 → Normalization
- 복잡한 transform sequence를 하나의 객체로 관리
- 학습용과 검증용 Pipeline 분리 구성
- 재사용 가능한 변환 조합 생성
- 코드 가독성 및 유지보수성 향상
- 실험에서 일관된 전처리 과정 보장
- 모듈화된 데이터 처리 워크플로우 구축
주요 파라미터:
transforms(Sequence[Callable]): 순서대로 적용할 변환들의 리스트list또는tuple형태로 변환들을 순서대로 나열- 각 변환은 callable 객체여야 함:
- v2에서
torch.nn.Module의 subclass 객체이길 권함.
- v2에서
- 순서가 중요함 (첫 번째부터 마지막까지 순차 실행)
변환 로직:
- 입력 데이터에 대해 변환들을 순서대로 적용
output = transform_n(...transform_2(transform_1(input)))- 중간 단계에서 오류 발생 시 전체 파이프라인 중단
- 각 변환의 출력 타입이 다음 변환의 입력 타입과 호환되어야 함
특징:
- Sequential execution: 변환들이 순서대로 연속 실행
- Type compatibility: v2에서는 다양한 타입 (이미지, bbox, mask 등) 지원
- No TorchScript support: TorchScript 사용 시
torch.nn.Sequential권장 - Deterministic order: 항상 동일한 순서로 변환 적용
- Error propagation: 하나의 변환 실패 시 전체 파이프라인 실패
- Memory efficient: 중간 결과를 저장하지 않고 즉시 다음 변환에 전달
사용 예시:
from torchvision.transforms.v2 import (
Compose, RandomResizedCrop, RandomHorizontalFlip,
ColorJitter, ToImage, ToDtype, Normalize, Resize, CenterCrop,
RandomRotation, RandomApply, GaussianBlur
)
import torch
# === 학습용 데이터 전처리 파이프라인 (v2) ===
train_transform = Compose([
# 1. 기하학적 변환 (데이터 증강)
RandomResizedCrop(224), # v2: bbox, mask도 함께 변환 가능
# → 스케일 불변성 학습, 객체 위치 다양화
RandomHorizontalFlip(p=0.5), # v2: 모든 TVTensor 타입 지원
# → 좌우 대칭성 학습, 데이터 2배 효과
# 2. 색상 변환 (robust 특징 학습)
ColorJitter( # v2: 더 빠른 구현
brightness=0.4, # brightness ±40% 변화
contrast=0.4, # contrast ±40% 변화
saturation=0.4, # saturation ±40% 변화
hue=0.1 # hue ±10% 변화
), # 다양한 조명 조건 변화에 robust한 모델
# 3. v2 텐서 변환 (새로운 방식)
ToImage(), # PIL/numpy → TV Image tensor
ToDtype(torch.float32, scale=True), # dtype 변환 + [0,255] → [0,1] 스케일링
# v2에서는 ToTensor() 대신 이 조합 사용
# 4. 표준화 (학습 안정성 향상)
Normalize( # v2: 배치 처리 개선
mean=[0.485, 0.456, 0.406], # RGB 채널별 평균값 (ImageNet 통계)
std=[0.229, 0.224, 0.225] # RGB 채널별 표준편차 (ImageNet 통계)
) # → 입력값 분포 안정화, 수렴 속도 향상
])
transformed_imgs = [train_transform(original_img) for _ in range(4)]
plot([original_img] + transformed_imgs)

# === 검증/테스트용 파이프라인 (v2, 일관된 전처리) ===
val_transform = Compose([
# 1. Deterministic Resize
Resize(256), # v2: antialias 옵션 개선
# → 입력 크기 표준화
# 2. 중앙 크롭 (가장 중요한 영역 추출)
CenterCrop(224), # v2: 다양한 입력 형태 지원
# → 객체가 중앙에 위치한다는 가정
# 3. v2 방식 텐서 변환
ToImage(), # v2의 새로운 변환 방식
ToDtype(torch.float32, scale=True), # 명시적 dtype 및 스케일 변환
Normalize( # 동일한 정규화 (필수!)
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]
)
])
transformed_imgs = [val_transform(original_img) for _ in range(4)]
plot([original_img] + transformed_imgs)

# === Object Detection용 파이프라인 (v2의 강점) ===
from torchvision.transforms.v2 import RandomIoUCrop, SanitizeBoundingBoxes
detection_transform = Compose([
RandomIoUCrop(), # v2 only: bbox 고려한 크롭
RandomHorizontalFlip(p=0.5), # bbox도 함께 변환
SanitizeBoundingBoxes(), # v2 only: 유효하지 않은 bbox 제거
ToImage(),
ToDtype(torch.float32, scale=True),
]) # 이미지+bbox+mask 동시 변환
from torchvision import tv_tensors # we'll describe this a bit later, bare with us
boxes = tv_tensors.BoundingBoxes(
[
[15, 10, 370, 510],
[275, 340, 510, 510],
[130, 345, 210, 425]
],
format="XYXY", canvas_size=original_img.shape[-2:])
# SanitizeBoundingBoxes가 기대하는 형태:
# 1. Dictionary: {"image": img, "boxes": boxes, "labels": labels}
# 2. Two-tuple: (img, {"boxes": boxes, "labels": labels})
input_data = {
"image": original_img,
"boxes": boxes,
"labels": torch.tensor([1, 2, 3]), # dummy label
}
transformed_data = [detection_transform(input_data) for _ in range(4)]
dispaly = [(input_data["image"], input_data["boxes"])]
for d in transformed_data:
dispaly.append((d["image"], d["boxes"]))
plot(dispaly,figsize=(10,2))

RandomChoice
역할:
- 제공된 변환들 중에서 하나를 랜덤하게 선택하여 적용하는 확률적 컨테이너 변환
- 여러 변환 옵션 중 실행 시마다 하나만 선택적으로 실행
- 변환 다양성을 위한 선택적 분기 구조 제공
- 상호 배타적인 변환들 중 하나를 확률적으로 적용
사용 용도:
- 서로 다른 증강 전략을 랜덤하게 선택 적용
- 상호 배타적인 변환들 중 하나만 적용 (예: 회전 vs 뒤집기)
- 데이터 증강의 다양성 증대 without 모든 변환 동시 적용
- 특정 변환들의 확률 가중치 조절
- 실험에서 다양한 증강 전략의 무작위 테스트
- 계산 비용 제어 (여러 변환 중 하나만 실행)
- 모델이 다양한 변환 패턴에 노출되도록 훈련
주요 파라미터:
transforms(Sequence[Callable]): 선택 가능한 변환들의 리스트- 각 변환은 독립적으로 실행 가능해야 함
- 동일한 입출력 타입을 가져야 함
p(Optional[List[float]]): 각 변환이 선택될 확률 가중치None: 모든 변환이 동일한 확률 (기본값)- 리스트 합이 1이 아니어도 자동으로 정규화됨
- 특정 변환에 더 높은 확률 부여 가능
변환 로직:
- 확률 분포에 따라 변환 중 하나를 랜덤 선택
- 선택된 변환만 실행하고 나머지는 무시
p가 None이면 uniform distribution으로 선택p가 주어지면 weighted random selection
특징:
- Mutually exclusive: 여러 변환 중 정확히 하나만 실행
- Stochastic selection: 매번 다른 변환이 선택될 수 있음
- Weighted probability: 변환별 선택 확률 조절 가능
- No TorchScript support: 스크립팅 불가능
- Single execution: 하나의 변환만 실행되므로 계산 효율적
- Dynamic behavior: 실행 시마다 다른 동작 가능
사용 예시:
from torchvision.transforms.v2 import (
RandomChoice, RandomHorizontalFlip, RandomVerticalFlip,
RandomRotation, ColorJitter, Compose, GaussianBlur,
RandomGrayscale, RandomPerspective, ElasticTransform,
RandomAffine, RandomAdjustSharpness, RandomSolarize
)
# === 기본: 균등 확률 선택 (v2) ===
basic_augment = RandomChoice([
RandomHorizontalFlip(p=1.0), # v2: TVTensor 지원
RandomVerticalFlip(p=1.0), # v2: 배치 처리 개선
RandomRotation(degrees=15), # v2: 더 빠른 구현
ColorJitter(brightness=0.3) # v2: 성능 최적화
]) # → 각각 25% 확률로 선택됨
# → 매번 정확히 하나의 변환만 적용
transformed_imgs = [basic_augment(original_img) for _ in range(4)]
plot([original_img] + transformed_imgs)

# === 가중 확률 선택 (v2 도메인 특성 반영) ===
weighted_augment = RandomChoice([
RandomHorizontalFlip(p=1.0), # v2: 모든 타입 호환
RandomRotation(degrees=10), # v2: interpolation 개선
ColorJitter(brightness=0.2), # v2: 메모리 효율성 향상
lambda x: x # Identity 변환 (원본 유지)
], p=[0.4, 0.2, 0.2, 0.2]) # 40%, 20%, 20%, 20% 확률 분배
# → 수평 뒤집기를 가장 선호
transformed_imgs = [weighted_augment(original_img) for _ in range(4)]
plot([original_img] + transformed_imgs)

# === v2 전용 변환들을 활용한 선택 ===
v2_exclusive_choice = RandomChoice([
# v2에서 크게 개선된 변환들
ElasticTransform( # v2: 성능 대폭 개선
alpha=50.0,
sigma=5.0
),
RandomPerspective( # v2: 더 정확한 구현
distortion_scale=0.2,
p=1.0
),
GaussianBlur( # v2: 다양한 커널 크기 지원
kernel_size=(3, 7),
sigma=(0.1, 2.0)
)
], p=[0.3, 0.3, 0.4]) # v2 최적화의 이점 활용
transformed_imgs = [v2_exclusive_choice(original_img) for _ in range(4)]
plot([original_img] + transformed_imgs)

# === 복합 데이터 타입에서의 활용 ===
multitype_choice = RandomChoice([
# 이미지만 변환
ColorJitter(brightness=0.3), # 색상은 이미지에만 적용
# 이미지+bbox+mask 모두 변환
RandomHorizontalFlip(p=1.0), # v2: 모든 타입 동시 변환
RandomRotation(degrees=15), # v2: bbox 좌표도 정확히 변환
]) # → v2의 멀티타입 지원 활용
transformed_imgs = [multitype_choice(original_img,boxes) for _ in range(4)]
plot([original_img] + transformed_imgs)

# === 수정된 실시간 데이터 로딩 (v2) ===
class V2Dataset(Dataset):
def __init__(self, image_paths, bbox_data=None):
self.image_paths = image_paths
self.bbox_data = bbox_data
# v2의 다양한 변환 전략
self.augment = RandomChoice([
Compose([ # 가벼운 v2 변환
RandomHorizontalFlip(p=1.0), # 33% × 100% = 33%
ColorJitter(brightness=0.2) # OK (p 파라미터 없음)
]),
Compose([ # 중간 v2 변환
RandomRotation(degrees=10), # OK (p 파라미터 없음)
GaussianBlur(kernel_size=3) # OK (p 파라미터 없음)
]),
Compose([ # 강한 v2 변환
RandomPerspective(
distortion_scale=0.1,
p=1.0,
), # 33% × 100% = 33%
RandomAdjustSharpness(
sharpness_factor=1.5,
p=1.0,
) # 33% × 100% = 33%
])
])
def __getitem__(self, idx):
image = load_image(self.image_paths[idx])
bbox = self.bbox_data[idx] if self.bbox_data else None
# v2: 이미지와 bbox를 동시에 변환
if bbox is not None:
return self.augment(image, bbox) # 멀티타입 입력 지원
else:
return self.augment(image) # 단일 타입 입력
- Dataset 작성의 경우로, 단순 transform으로 넘기지 않은 경우임.
RandomApply
역할:
- 여러 변환(transformations)들을 하나의 그룹으로 묶어 지정된 확률로 전체 그룹을 적용하거나 건너뛰는 컨테이너 변환
- 복합적인 데이터 증강 파이프라인을 확률적으로 제어
- 전체 변환 시퀀스를 단위로 하는 조건부 적용
- 여러 변환의 조합 효과를 일괄적으로 관리
사용 용도:
- 강한 데이터 증강을 선택적으로 적용하여 오버피팅 방지
- 복잡한 변환 조합의 적용 빈도 제어 (예: 색상 왜곡 + 블러 조합)
- 학습 초기에는 약한 증강, 후기에는 강한 증강 적용 전략
- 특정 변환 그룹이 특정 데이터에만 유효할 때 조건부 적용
- 계산 비용이 높은 변환들을 선택적으로 적용하여 효율성 향상
- 모델이 원본 이미지와 변환된 이미지 모두에 적응하도록 균형 조절
- 데이터 증강 강도를 동적으로 조절하는 커리큘럼 학습
주요 파라미터:
transforms(Sequence[Callable] | ModuleList): 적용할 변환들의 리스트list또는tuple: 일반적인 사용법torch.nn.ModuleList: TorchScript 호환성을 위한 권장 방식- 순서대로 연속 적용됨 (Compose와 유사)
p(float): 전체 변환 그룹이 적용될 확률0.5: 50% 확률로 전체 그룹 적용 (기본값)0.0: 항상 원본 유지 (그룹 비활성화)1.0: 항상 전체 그룹 적용
변환 로직:
- 확률
p에 따라 전체 Transform 그룹의 적용 여부 결정 - 적용 시: 모든 Transform을 순서대로 연속 실행
- 각각의 Transform의 확률이 있을 경우
- 독립적으로 개별 확률에 따라 개별 실행이 결정됨.
- 비적용 시: 원본 이미지 그대로 반환
- 다시 한번 강조하지만, 개별 Transform의 랜덤성은 각 변환 내부에서 독립적으로 처리 (중요!)
특징:
- Group-level randomness: 개별 변환이 아닌 그룹 단위로 확률 적용
- All-or-nothing: 그룹 내 모든 변환이 함께 적용되거나 모두 건너뜀
- 단 그룹내의 개별 Transform의 확률이 있을 경우 영향 받음.
- Compositional: 내부 변환들의 순차적 조합 효과
- TorchScript compatible: ModuleList 사용 시 스크립팅 가능
- Nested structure: 다른 RandomApply 내부에 중첩 사용 가능
- Performance control: 계산 비용 높은 변환의 선택적 적용
사용 예시:
from torchvision.transforms.v2 import (
RandomApply, ColorJitter, GaussianBlur, RandomGrayscale,
RandomPerspective, ElasticTransform, RandomAffine,
RandomAdjustSharpness, RandomSolarize, RandomPosterize,
ToImage, ToDtype
)
import torch
# === 기본: v2 색상 변환 그룹의 조건부 적용 ===
color_group = RandomApply([
ColorJitter( # v2: 더 빠른 색상 조정
brightness=0.4, # brightness 변화
contrast=0.4, # contrast 변화
saturation=0.4, # saturation 변화
hue=0.1 # hue 변화
),
GaussianBlur( # v2: 개선된 블러 구현
kernel_size=(3, 7), # 커널 크기 범위 지정 가능
sigma=(0.1, 2.0) # 시그마 범위 지정 가능
)
], p=0.3) # 30% 확률로 전체 그룹 적용
# 즉, 70%는 원본, 30%는 색상+블러
transformed_imgs = [color_group(original_img) for _ in range(4)]
plot([original_img] + transformed_imgs)

# === TorchScript 호환 버전 (v2 배포용) ===
scripted_transforms = RandomApply(
torch.nn.ModuleList([ # v2에서도 TorchScript 지원
ColorJitter(brightness=0.2), # 실제 적용 확률: 50% × 100% = 50%
# v2: 더 나은 성능, p 파라미터 없음
RandomGrayscale(p=0.2) # 실제 적용 확률: 50% × 20% = 10% 주의!
# v2: 배치 처리 지원
]),
p=0.5 # 50% 확률로 전체 그룹 적용
)
test_script = torch.jit.script(scripted_transforms) # v2에서도 스크립팅 가능
# torch.nn.ModuleList 덕분
transformed_imgs = [test_script(original_img) for _ in range(4)]
plot([original_img] + transformed_imgs)

# === v2에서 크게 개선된 변환들의 그룹 적용 ===
v2_strong_augment = RandomApply([
# v2에서 성능이 대폭 향상된 변환들
RandomPerspective( # 실제 적용 확률: 10% × 100% = 10%
distortion_scale=0.2, # v2: 더 정확하고 빠른 원근 변환
p=1.0 # 그룹 선택 시 100% 적용
),
ElasticTransform( # 실제 적용 확률: 10% × 100% = 10%
alpha=50.0, # v2: 메모리 효율성 대폭 개선
sigma=5.0 # p 파라미터 없음 (항상 적용)
),
RandomAffine( # 실제 적용 확률: 10% × 100% = 10%
degrees=10, # v2: 보간 품질 향상
translate=(0.1, 0.1), # p 파라미터 없음 (항상 적용)
scale=(0.9, 1.1)
)
], p=0.1) # 10%만 적용 (강한 변형)
transformed_imgs = [v2_strong_augment(original_img) for _ in range(4)]
plot([original_img] + transformed_imgs)

# === 멀티타입 데이터를 위한 v2 그룹 ===
multitype_group = RandomApply([
RandomHorizontalFlip(p=1.0), # 실제 적용 확률: 40% × 100% = 40%
# v2: image+bbox+mask 동시 변환
RandomRotation(degrees=15), # 실제 적용 확률: 40% × 100% = 40%
# v2: 모든 타입의 좌표 정확히 계산
# 이미지에만 적용되는 변환
ColorJitter(brightness=0.3) # 실제 적용 확률: 40% × 100% = 40%
# bbox/mask에는 영향 없음
], p=0.4) # v2의 멀티타입 지원 활용
transformed_imgs = [multitype_group(original_img,boxes) for _ in range(4)]
plot([original_img] + transformed_imgs)

# === v2 전용 고급 변환 그룹 ===
advanced_v2_group = RandomApply([
# v2에서 새로 추가되거나 크게 개선된 변환들
RandomAdjustSharpness( # v2: 선명도 조정 최적화
sharpness_factor= 2.0 # 범위 지정 가능
),
RandomSolarize( # v2: Solarization효과 개선
threshold=128
),
RandomPosterize( # v2: Posterization효과 최적화
bits=4
)
], p=0.25) # 25% 확률로 고급 효과 적용
# 실제 확률:
# - RandomAdjustSharpness: 25% × 50% = 12.5%
# - RandomSolarize: 25% × 50% = 12.5%
# - RandomPosterize: 25% × 50% = 12.5%
# 모두 의도한 것보다 절반으로 줄어듦!
transformed_imgs = [advanced_v2_group(original_img) for _ in range(4)]
plot([original_img] + transformed_imgs)

# === Object Detection 전용 v2 그룹 ===
from torchvision.transforms.v2 import RandomIoUCrop, SanitizeBoundingBoxes
detection_group = RandomApply([
RandomIoUCrop( # v2 only: IoU 기반 크롭
min_scale=0.8,
max_scale=1.0,
min_aspect_ratio=0.5,
max_aspect_ratio=2.0
),
SanitizeBoundingBoxes(), # v2 only: 유효하지 않은 bbox 정리
], p=0.6) # Detection 전용 증강
# 실제 확률:
# - RandomIoUCrop에서 Random은 크롭 위치/크기를 랜덤하게 선택한다는 의미: 100% 발생.
# - SanitizeBoundingBoxes 도 100%발생.
# 최종적으로 모두 60%확률로 일어남.
transformed_data = [detection_group(input_data) for _ in range(4)]
dispaly = [(input_data["image"], input_data["boxes"])]
for d in transformed_data:
dispaly.append((d["image"], d["boxes"]))
plot(dispaly)

# === 효율적인 전체 파이프라인 구성 ===
complete_v2_pipeline = Compose([
# 1. v2 기본 변환
RandomHorizontalFlip(p=0.5), # 실제 적용 확률: 50%
# 2. v2 조건부 색상 그룹 (p=0.7)
RandomApply([
ColorJitter(brightness=0.4, contrast=0.4), # 실제 적용 확률: 70% × 100% = 70%
RandomGrayscale(p=1.0) # 실제 적용 확률: 70% × 100% = 70%
], p=0.7),
# 3. v2 조건부 기하학적 그룹 (p=0.3)
RandomApply([
RandomRotation(degrees=10), # 실제 적용 확률: 30% × 100% = 30%
RandomPerspective(distortion_scale=0.1, p=1.0) # 실제 적용 확률: 30% × 100% = 30%
], p=0.3),
# 4. v2 텐서 변환
ToImage(), # 실제 적용 확률: 100% (항상 적용)
ToDtype(torch.float32, scale=True), # 실제 적용 확률: 100% (항상 적용)
])
transformed_imgs = [complete_v2_pipeline(original_img) for _ in range(4)]
plot([original_img] + transformed_imgs)

# === 배치 처리를 위한 v2 그룹 ===
batch_optimized_group = RandomApply([
# v2는 배치 텐서를 더 효율적으로 처리
ColorJitter(brightness=0.3, contrast=0.3), # 배치 단위 최적화
GaussianBlur(kernel_size=3), # 배치 블러 연산 최적화
RandomAdjustSharpness(sharpness_factor=1.2) # 배치 처리 지원
], p=0.5)
# 실제 확률:
# - ColorJitter: 50% × 100% = 50%
# - GaussianBlur: 50% × 100% = 50%
# - RandomAdjustSharpness: 50% × 50%(기본p) = 25% 주의!
RandomOrder (작성중)
역할:
- 여러 변환(transformations)들을 매번 다른 랜덤한 순서로 적용하는 컨테이너 변환
- 변환 순서에 따른 편향(order bias)을 제거하여 더 균형잡힌 데이터 증강
- 동일한 변환 조합으로 순서만 바꿔 다양한 결과 생성
- 변환간 상호작용 효과의 다양성 확보
- 순서 의존성이 강한 변환들의 공정한 조합
사용 용도:
- 색상 변환과 블러 효과의 순서에 따른 결과 차이 해결 (ColorJitter → Blur vs Blur → ColorJitter)
- 회색조 변환과 색상 조정의 순서 편향 제거 (Grayscale 전후 ColorJitter 효과 차이)
- 기하학적 변환들의 누적 왜곡 패턴을 랜덤화하여 다양성 증가
- 선명도/흐림 효과와 다른 변환들의 순서 의존성 해결
- Detection 모델에서 이미지+bbox+mask 변환의 순서 편향 제거
- 동일한 증강 강도로 더 많은 변화 조합 생성하여 데이터 효율성 향상
- 특정 변환 순서에 과적합되는 것을 방지
주요 파라미터:
transforms(Sequence[Callable]): 랜덤 순서로 적용할 변환들의 리스트- 모든 변환이 매번 적용됨 (100% 적용율)
- 순서만 매번 랜덤하게 셔플됨
- 개별 변환의 내부 확률(p)은 각각 독립적으로 작동
- n개 변환 → n! 가지 순서 조합 가능
변환 로직:
- 매 호출마다 주어진 변환 리스트의 순서를 랜덤하게 셔플
- 셔플된 순서대로 모든 변환을 순차적으로 적용
- 각 변환의 내부 랜덤성(확률 p)은 변환 순서와 독립적으로 작동
- 모든 변환이 반드시 적용됨 (건너뛰기 없음)
- 순서 셔플링은
torch.randperm기반으로 재현 가능
특징:
- Order randomization: 변환 적용 순서만 랜덤화, 변환 자체는 모두 적용
- Bias elimination: 고정 순서에 따른 편향 효과 제거
- Deterministic randomness: 시드 설정으로 재현 가능한 순서 생성
- No TorchScript support: 스크립팅 지원 안함 (여전한 제한사항)
- Full application: 그룹 내 모든 변환이 100% 적용 (확률 제어 불가)
- Nested compatibility: RandomApply 내부에서 사용 가능
- Multi-type support: v2의 이미지+bbox+mask 동시 변환 지원
- Production ready: 상용 환경에서 안전하게 사용 가능
사용 예시:
from torchvision.transforms.v2 import (
RandomOrder, ColorJitter,
GaussianBlur, RandomAdjustSharpness,
)
# === 기본 사용: 색상 변환 순서 랜덤화 ===
color_random_order = RandomOrder([
ColorJitter(brightness=0.4, contrast=0.4),
GaussianBlur(kernel_size=3, sigma=1.0),
RandomAdjustSharpness(sharpness_factor=1.5) # p=0.5 기본값
])
# 가능한 순서 조합: 3! = 6가지
# 1) ColorJitter → GaussianBlur → RandomAdjustSharpness
# 2) ColorJitter → RandomAdjustSharpness → GaussianBlur
# 3) GaussianBlur → ColorJitter → RandomAdjustSharpness
# 4) GaussianBlur → RandomAdjustSharpness → ColorJitter
# 5) RandomAdjustSharpness → ColorJitter → GaussianBlur
# 6) RandomAdjustSharpness → GaussianBlur → ColorJitter
# 실제 적용 확률:
# - ColorJitter: 100% (순서만 변함)
# - GaussianBlur: 100% (순서만 변함)
# - RandomAdjustSharpness: 50% (내부 p=0.5, 순서는 변함)
# === 확률 제어: RandomApply로 감싸기 ===
from torchvision.transforms.v2 import RandomApply
probabilistic_order = RandomApply([
RandomOrder([
ColorJitter(brightness=0.5, contrast=0.5),
RandomGrayscale(p=1.0),
GaussianBlur(kernel_size=5)
])
], p=0.7) # 70% 확률로 랜덤 순서 적용
# 실제 확률:
# - 70% 확률: 3개 변환을 랜덤 순서로 모두 적용
# - 30% 확률: 원본 이미지 그대로
# - ColorJitter: 70% × 100% = 70%
# - RandomGrayscale: 70% × 100% = 70%
# - GaussianBlur: 70% × 100% = 70%
# === Detection용: 멀티타입 순서 랜덤화 ===
from torchvision.transforms.v2 import RandomHorizontalFlip, RandomRotation
detection_order = RandomOrder([
RandomHorizontalFlip(p=1.0), # 이미지+bbox+mask
RandomRotation(degrees=15), # 이미지+bbox+mask
ColorJitter(brightness=0.3) # 이미지만
])
# 모든 변환이 랜덤 순서로 적용되지만
# 좌표 변환의 정확성은 v2가 보장
# === 순서 민감성 해결 예시 ===
# 문제: 회색조 변환 후 색상 조정은 효과 없음
from torchvision.transforms.v2 import Compose
bias_prone_fixed = Compose([
RandomGrayscale(p=1.0), # 먼저 회색조로
ColorJitter(brightness=0.5) # 색상 조정 효과 없음!
])
# 해결: 순서 랜덤화로 편향 제거
bias_free_random = RandomOrder([
RandomGrayscale(p=0.5), # 50% 확률
ColorJitter(brightness=0.5) # 순서에 따라 효과 다름
])
# RandomOrder로 두 순서 모두 경험:
# 1) RandomGrayscale → ColorJitter: 회색조 후 색상 (제한적 효과)
# 2) ColorJitter → RandomGrayscale: 색상 후 회색조 (풍부한 효과)
# === 강력한 증강 파이프라인 ===
from torchvision.transforms.v2 import RandomSolarize, RandomPosterize
strong_augmentation = RandomApply([
RandomOrder([
ColorJitter(brightness=0.6, contrast=0.6, saturation=0.4, hue=0.1),
GaussianBlur(kernel_size=(5, 9), sigma=(1.0, 3.0)),
RandomAdjustSharpness(sharpness_factor=2.0),
RandomSolarize(threshold=128),
RandomPosterize(bits=4)
])
], p=0.4) # 40% 확률로 강한 증강 적용
# 5개 변환의 5! = 120가지 순서 조합
# 매번 다른 순서로 적용되어 극도로 다양한 결과
# === 실용적인 단계별 파이프라인 ===
complete_pipeline = Compose([
# 1단계: 기하학적 변환 (순서 고정)
RandomHorizontalFlip(p=0.5),
# 2단계: 색상 변환 그룹 (순서 랜덤)
RandomApply([
RandomOrder([
ColorJitter(brightness=0.4, contrast=0.4),
RandomAdjustSharpness(sharpness_factor=1.5),
RandomGrayscale(p=0.2) # 20% 확률로 회색조
])
], p=0.8), # 80% 확률로 색상 그룹 적용
# 3단계: 효과 변환 그룹 (순서 랜덤)
RandomApply([
RandomOrder([
GaussianBlur(kernel_size=(3, 7)),
RandomSolarize(threshold=128),
RandomPosterize(bits=4)
])
], p=0.3), # 30% 확률로 효과 그룹 적용
# 4단계: 최종 변환 (순서 고정)
ToImage(),
ToDtype(torch.float32, scale=True)
])
# === 성능 최적화된 파이프라인 ===
# 빠른 변환을 앞에, 무거운 변환을 뒤에 배치
optimized_random_order = RandomOrder([
ColorJitter(brightness=0.3, contrast=0.3), # 빠름
RandomAdjustSharpness(sharpness_factor=1.2), # 중간
GaussianBlur(kernel_size=(5, 9)) # 무거움
])
# RandomOrder가 순서를 섞더라도 평균적으로 성능 예측 가능
# === 시드 기반 재현 가능한 사용 ===
import torch
# 재현 가능한 랜덤 순서
torch.manual_seed(42)
reproducible_order = RandomOrder([
ColorJitter(brightness=0.4),
GaussianBlur(kernel_size=3),
RandomAdjustSharpness(sharpness_factor=1.5)
])
# 같은 시드로 호출하면 같은 순서 보장
torch.manual_seed(42)
result1 = reproducible_order(image)
torch.manual_seed(42)
result2 = reproducible_order(image)
# result1과 result2는 동일한 변환 순서 적용
# === 커스텀 조건부 RandomOrder ===
class ConditionalRandomOrder:
"""확률 제어가 가능한 RandomOrder 래퍼"""
def __init__(self, transforms, p=1.0):
self.random_order = RandomOrder(transforms)
self.p = p
def __call__(self, *inputs):
if torch.rand(1) < self.p:
return self.random_order(*inputs)
return inputs[0] if len(inputs) == 1 else inputs
# 사용 예시
custom_order = ConditionalRandomOrder([
ColorJitter(brightness=0.3),
GaussianBlur(kernel_size=3),
RandomAdjustSharpness(sharpness_factor=1.5)
], p=0.6) # 60% 확률로 랜덤 순서 적용
제약사항:
- TorchScript 미지원: 여전히 스크립팅 불가능 (v0.21.0 기준.)
- 확률 제어 불가:
RandomApply로 감싸서 해결 필요 - 전체 적용: 모든 변환이 100% 적용되므로 계산 비용 고려
마이그레이션 가이드:
# 낮은 버전에서 베타 경고 하려면.
# torchvision.disable_beta_transforms_warning() # 더 이상 불필요728x90
'Python' 카테고리의 다른 글
| [PyTorch] Conversion-Torchvision.transfroms.v2 (0) | 2025.06.16 |
|---|---|
| [PyTorch] torchvision.transforms.v2 - Summary (작성중) (2) | 2025.06.16 |
| [PyTorch] Randomly-applied-torchvision.transforms.v2 (0) | 2025.06.15 |
| [PyTorch] Augmentation-torchvision.transforms.v2 (1) | 2025.06.15 |
| [PyTorch] Photometric-torchvision.transforms.v2 (0) | 2025.06.15 |