이미지 분류
이미지 분류 (Image classification)
이미지 분류는 이미지에 라벨(label)이나 클래스(class)를 할당하는 작업이에요. 텍스트나 오디오 분류와 달리 입력은 이미지를 구성하는 픽셀 값(pixel values)이죠. 이미지 분류에는 자연재해 후 피해 탐지, 작물 건강 모니터링, 의료 이미지에서 질병 징후 스크리닝을 돕는 것 등 다양한 응용 분야가 있어요.
이 가이드에서 배울 내용은 이렇습니다:
이 작업과 호환되는 모든 아키텍처와 체크포인트를 보려면 task-page를 확인해 보세요.
시작하기 전에 필요한 라이브러리가 모두 설치돼 있는지 확인하세요:
pip install transformers datasets evaluate accelerate pillow torchvision scikit-learn trackio
모델을 커뮤니티와 공유하고 업로드하려면 Hugging Face 계정에 로그인하길 권장해요. 요청이 오면 토큰을 입력해 로그인합니다:
>>> from huggingface_hub import notebook_login
>>> notebook_login()
Food-101 데이터셋 불러오기
🤗 Datasets 라이브러리에서 Food-101 데이터셋의 더 작은 하위 집합부터 불러와 볼게요. 전체 데이터셋으로 훈련 시간을 더 쓰기 전에, 작은 집합으로 실험해서 모든 게 잘 동작하는지 확인할 기회를 갖는 거예요.
>>> from datasets import load_dataset
>>> food = load_dataset("ethz/food101", split="train[:5000]")
데이터셋의 train 스플릿을 [~datasets.Dataset.train_test_split] 메서드로 훈련·테스트 세트로 나눕니다:
>>> food = food.train_test_split(test_size=0.2)
그다음 예시를 하나 살펴볼게요:
>>> food["train"][0]
{'image': <PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=512x512 at 0x7F52AFC8AC50>,
'label': 79}
데이터셋의 각 예시에는 두 개의 필드가 있어요:
image: 음식 항목의 PIL 이미지label: 음식 항목의 라벨 클래스
모델이 라벨 id에서 라벨 이름을 얻기 쉽게 하기 위해, 라벨 이름을 정수로·또는 그 반대로 매핑하는 딕셔너리를 만들겠습니다:
>>> labels = food["train"].features["label"].names
>>> label2id, id2label = dict(), dict()
>>> for i, label in enumerate(labels):
... label2id[label] = str(i)
... id2label[str(i)] = label
이제 라벨 id를 라벨 이름으로 바꿀 수 있어요:
>>> id2label[str(79)]
'prime_rib'
전처리
다음 단계는 ViT 이미지 프로세서를 불러와 이미지를 텐서로 처리하는 거예요:
>>> from transformers import AutoImageProcessor
>>> checkpoint = "google/vit-base-patch16-224-in21k"
>>> image_processor = AutoImageProcessor.from_pretrained(checkpoint)
과적합(overfitting)에 더 강건한 모델을 만들기 위해 이미지에 몇 가지 변환을 적용해 봅니다. 여기서는 torchvision의 transforms 모듈을 쓸 건데, 원하는 이미지 라이브러리를 써도 됩니다.
이미지의 일부를 무작위로 크롭하고, 리사이즈한 뒤 이미지 평균과 표준편차로 정규화합니다:
>>> from torchvision.transforms import RandomResizedCrop, Compose, Normalize, ToTensor
>>> normalize = Normalize(mean=image_processor.image_mean, std=image_processor.image_std)
>>> size = (
... image_processor.size["shortest_edge"]
... if "shortest_edge" in image_processor.size
... else (image_processor.size["height"], image_processor.size["width"])
... )
>>> _transforms = Compose([RandomResizedCrop(size), ToTensor(), normalize])
그다음 변환을 적용하고 모델의 입력인 pixel_values를 반환하는 전처리 함수를 만듭니다:
>>> def transforms(examples):
... examples["pixel_values"] = [_transforms(img.convert("RGB")) for img in examples["image"]]
... del examples["image"]
... return examples
전체 데이터셋에 전처리 함수를 적용하려면 🤗 Datasets의 [~datasets.Dataset.with_transform] 메서드를 쓰세요. 데이터셋의 요소를 불러올 때 그 순간 변환이 적용됩니다:
>>> food = food.with_transform(transforms)
이제 [DefaultDataCollator]로 예시 배치를 만듭니다. 🤗 Transformers의 다른 데이터 콜레이터와 달리 DefaultDataCollator는 패딩 같은 추가 전처리를 적용하지 않아요.
>>> from transformers import DefaultDataCollator
>>> data_collator = DefaultDataCollator()
평가
훈련 중에 지표(metric)를 포함하면 모델 성능을 평가하는 데 자주 도움이 돼요. 🤗 Evaluate 라이브러리로 평가 방법을 빠르게 불러올 수 있습니다. 이 작업에서는 accuracy 지표를 로드합니다 (지표를 로드하고 계산하는 방법을 더 배우려면 🤗 Evaluate quick tour를 확인해 보세요):
>>> import evaluate
>>> accuracy = evaluate.load("accuracy")
그다음 예측과 라벨을 [~evaluate.EvaluationModule.compute]에 넘겨 정확도를 계산하는 함수를 만듭니다:
>>> import numpy as np
>>> def compute_metrics(eval_pred):
... predictions, labels = eval_pred
... predictions = np.argmax(predictions, axis=1)
... return accuracy.compute(predictions=predictions, references=labels)
compute_metrics 함수가 준비됐으니, 훈련을 설정할 때 다시 쓰게 될 거예요.
훈련
[Trainer]로 모델을 파인튜닝하는 방법이 익숙하지 않다면 여기의 기본 튜토리얼을 먼저 확인해 보세요!
모델 훈련을 시작할 준비가 됐어요! [AutoModelForImageClassification]로 ViT를 로드합니다. 라벨 개수, 기대하는 라벨 개수, 라벨 매핑을 지정해 주세요:
>>> from transformers import AutoModelForImageClassification, TrainingArguments, Trainer
>>> model = AutoModelForImageClassification.from_pretrained(
... checkpoint,
... num_labels=len(labels),
... id2label=id2label,
... label2id=label2id,
... )
이 시점에 남은 단계는 세 개뿐이에요:
- [
TrainingArguments]에 훈련 하이퍼파라미터를 정의합니다. 사용하지 않는 컬럼을 제거하면image컬럼이 사라질 수 있으니 주의해야 해요.image컬럼이 없으면pixel_values를 만들 수 없거든요. 이 동작을 막으려면remove_unused_columns=False로 설정하세요! 유일한 필수 파라미터는 모델을 저장할 위치를 정하는output_dir이에요.push_to_hub=True로 설정하면 이 모델을 Hub에 push합니다 (모델을 업로드하려면 Hugging Face에 로그인해야 해요). [Trainer]는 각 에폭이 끝날 때 정확도를 평가하고 훈련 체크포인트를 저장해요. - 훈련 인자를 모델, 데이터셋, 토크나이저, 데이터 콜레이터,
compute_metrics함수와 함께 [Trainer]로 넘깁니다. - [
~Trainer.train]을 호출해 모델을 파인튜닝합니다.
>>> training_args = TrainingArguments(
... output_dir="my_awesome_food_model",
... remove_unused_columns=False,
... eval_strategy="epoch",
... save_strategy="epoch",
... learning_rate=5e-5,
... per_device_train_batch_size=16,
... gradient_accumulation_steps=4,
... per_device_eval_batch_size=16,
... num_train_epochs=3,
... warmup_steps=0.1,
... logging_steps=10,
... report_to="trackio",
... run_name="food101",
... load_best_model_at_end=True,
... metric_for_best_model="accuracy",
... push_to_hub=True,
... )
>>> trainer = Trainer(
... model=model,
... args=training_args,
... data_collator=data_collator,
... train_dataset=food["train"],
... eval_dataset=food["test"],
... processing_class=image_processor,
... compute_metrics=compute_metrics,
... )
>>> trainer.train()
훈련이 끝나면 [~transformers.Trainer.push_to_hub] 메서드로 모델을 Hub에 공유해 누구나 쓸 수 있게 하세요:
>>> trainer.push_to_hub()
이미지 분류용 모델을 파인튜닝하는 더 자세한 예시는 해당 PyTorch notebook을 확인해 보세요.
추론
좋아요, 이제 모델을 파인튜닝했으니 추론에 쓸 수 있어요!
추론을 실행할 이미지를 불러옵니다:
>>> ds = load_dataset("ethz/food101", split="validation[:10]")
>>> image = ds["image"][0]
파인튜닝한 모델을 추론에 시험해 보는 가장 간단한 방법은 [pipeline]에 쓰는 거예요. 모델로 이미지 분류용 pipeline을 만들고 이미지를 넘겨 보세요:
>>> from transformers import pipeline
>>> classifier = pipeline("image-classification", model="my_awesome_food_model")
>>> classifier(image)
[{'score': 0.31856709718704224, 'label': 'beignets'},
{'score': 0.015232225880026817, 'label': 'bruschetta'},
{'score': 0.01519392803311348, 'label': 'chicken_wings'},
{'score': 0.013022331520915031, 'label': 'pork_chop'},
{'score': 0.012728818692266941, 'label': 'prime_rib'}]
원한다면 pipeline의 결과를 수동으로 재현할 수도 있어요:
이미지를 전처리하고 input을 PyTorch 텐서로 반환할 이미지 프로세서를 로드합니다:
>>> from transformers import AutoImageProcessor
>>> import torch
>>> image_processor = AutoImageProcessor.from_pretrained("my_awesome_food_model")
>>> inputs = image_processor(image, return_tensors="pt")
입력을 모델에 넘기고 로짓(logits)을 얻습니다:
>>> from transformers import AutoModelForImageClassification
>>> model = AutoModelForImageClassification.from_pretrained("my_awesome_food_model")
>>> with torch.no_grad():
... logits = model(**inputs).logits
가장 높은 확률의 예측 라벨을 얻고, 모델의 id2label 매핑으로 라벨로 변환합니다:
>>> predicted_label = logits.argmax(-1).item()
>>> model.config.id2label[predicted_label]
'beignets'
더 알아보기 (Learn more)
- task-page: 이미지 분류와 호환되는 모든 아키텍처·체크포인트 목록.
- PyTorch notebook: 이미지 분류 파인튜닝의 더 자세한 예시.
- 🤗 Evaluate quick tour: 지표 로드·계산 방법.