Quiz-4 (2024.10.22) // 범위: 05wk-1 까지

Author

최규빈

Published

October 22, 2024

항목 허용 여부 비고
강의노트 참고 허용 수업 중 제공된 강의노트나 본인이 정리한 자료를 참고 가능
구글 검색 허용 인터넷을 통한 자료 검색 및 정보 확인 가능
생성 모형 사용 허용 안함 인공지능 기반 도구(GPT 등) 사용 불가
import datasets
import transformers
import torch
import torchvision.transforms
import evaluate
import numpy as np
/home/cgb3/anaconda3/envs/hf/lib/python3.12/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
  from .autonotebook import tqdm as notebook_tqdm

아래는 datasets 라이브러리의 load_dataset 함수를 사용하여 beans라는 데이터셋을 불러오는 코드이다.

beans = datasets.load_dataset('beans')
beans
DatasetDict({
    train: Dataset({
        features: ['image_file_path', 'image', 'labels'],
        num_rows: 1034
    })
    validation: Dataset({
        features: ['image_file_path', 'image', 'labels'],
        num_rows: 133
    })
    test: Dataset({
        features: ['image_file_path', 'image', 'labels'],
        num_rows: 128
    })
})

beans 데이터셋은 train(1034개), validation(133개), test(128개)로 나뉘며, 각각 모델 학습, 검증, 성능 평가에 사용된다. 각 데이터셋은 이미지 파일 경로(image_file_path), 실제 이미지 데이터(image), 그리고 이미지에 대한 질병 라벨(labels)로 구성되어 있다. 이 라벨은 각 이미지가 어떤 질병을 나타내는지에 대한 정보를 제공한다.

라벨 0은 “각진 잎 반점(angular_leaf_spot)”이라는 질병으로, 이는 콩 잎에 특정 형태의 반점이 생기는 현상을 의미한다. 라벨 1은 “콩 녹병(bean_rust)”으로, 콩 잎에 붉은 녹병이 나타나는 질병이다. 마지막으로 라벨 2는 “healthy(건강한 잎)”을 나타내며, 이 라벨이 붙은 이미지는 질병 없이 건강한 콩 잎을 뜻한다. 이러한 라벨을 통해 데이터셋의 각 이미지가 어떤 질병을 가지고 있는지 쉽게 파악할 수 있다.

id2label = {0: 'angular_leaf_spot', 1: 'bean_rust', 2: 'healthy'}
label2id = {'angular_leaf_spot': 0, 'bean_rust': 1, 'healthy': 2}

1. beans 자료분석 – 100점

배점: (1),(2),(3),(4) – 5점, (5),(6) – 30점 (7),(8) – 10점

(1) beans['train'][10] 에 포함된 이미지의 크기와 라벨을 확인하라.

(풀이)

beans['train'][10]['image'].size, beans['train'][10]['labels']
((500, 500), 0)

(2) torchvision.transforms.RandomResizedCrop을 사용하여 beans['train'][10]에 있는 이미지를 (224, 224) 크기로 변환하는 코드를 작성하라.

(풀이)

f1 = torchvision.transforms.RandomCrop(224) 
f1(beans['train'][10]['image'])

(3) torchvision.transforms.ToTensor를 사용하여 beans['train'][10]에 포함된 이미지를 텐서(Tensor)로 변환하는 코드를 작성하라.

(풀이)

f2 = torchvision.transforms.ToTensor()
f2(beans['train'][10]['image'])
tensor([[[0.4706, 0.5255, 0.4549,  ..., 0.4902, 0.4471, 0.3529],
         [0.5529, 0.5529, 0.4510,  ..., 0.6118, 0.5294, 0.4667],
         [0.4157, 0.5490, 0.6157,  ..., 0.6706, 0.6000, 0.5333],
         ...,
         [0.7490, 0.6549, 0.5137,  ..., 0.3686, 0.3255, 0.2784],
         [0.7255, 0.6667, 0.5608,  ..., 0.3961, 0.3412, 0.2824],
         [0.6510, 0.6627, 0.5843,  ..., 0.3804, 0.3490, 0.3020]],

        [[0.2863, 0.3412, 0.2784,  ..., 0.3255, 0.2824, 0.1961],
         [0.3686, 0.3686, 0.2745,  ..., 0.4314, 0.3608, 0.2980],
         [0.2275, 0.3608, 0.4353,  ..., 0.4824, 0.4118, 0.3490],
         ...,
         [0.7608, 0.6863, 0.5882,  ..., 0.2863, 0.2588, 0.2314],
         [0.7294, 0.6941, 0.6275,  ..., 0.3137, 0.2745, 0.2353],
         [0.6510, 0.6824, 0.6471,  ..., 0.2980, 0.2824, 0.2471]],

        [[0.2078, 0.2627, 0.2039,  ..., 0.2706, 0.2275, 0.1490],
         [0.2902, 0.2902, 0.1922,  ..., 0.3686, 0.2941, 0.2353],
         [0.1412, 0.2745, 0.3451,  ..., 0.3961, 0.3333, 0.2784],
         ...,
         [0.4902, 0.3922, 0.2667,  ..., 0.2039, 0.1961, 0.1843],
         [0.4706, 0.4196, 0.3216,  ..., 0.2314, 0.2118, 0.1882],
         [0.4078, 0.4235, 0.3529,  ..., 0.2157, 0.2196, 0.2039]]])

(4) torchvision.transforms.Normalize를 사용하여 이미지의 각 채널에 아래와 같은 정규화를 수행하는 변환을 선언하라:

  • 빨강 채널: \(r \to \frac{r - 0.5}{0.5}\)
  • 초록 채널: \(g \to \frac{g - 0.5}{0.5}\)
  • 파랑 채널: \(b \to \frac{b - 0.5}{0.5}\)

그리고 torchvision.transforms.Compose를 활용하여 아래 순서대로 변환을 수행하는 코드를 작성하라.

  • 첫 번째: 이미지를 (224, 224) 크기로 무작위로 자르기 (RandomResizedCrop)
  • 두 번째: 이미지를 텐서로 변환 (ToTensor)
  • 세 번째: 각 채널별로 주어진 방식대로 정규화 (Normalize)

이를 이용하여 beans['train'][10] 에 포함된 이미지를 변환하라.

(풀이)

f3 = torchvision.transforms.Normalize([0.5,0.5,0.5], [0.5,0.5,0.5])
f = torchvision.transforms.Compose([f1, f2, f3])
f(beans['train'][10]['image'])
tensor([[[ 0.0431,  0.3725,  0.6863,  ..., -0.2549, -0.2471, -0.2157],
         [ 0.1059,  0.2157,  0.4980,  ..., -0.2941, -0.2784, -0.2157],
         [ 0.1451,  0.1451,  0.3804,  ..., -0.2941, -0.2941, -0.2549],
         ...,
         [-0.6235, -0.4510, -0.0431,  ...,  0.6706,  0.6549,  0.6471],
         [-0.6549, -0.3882, -0.0431,  ...,  0.6863,  0.6784,  0.7255],
         [-0.6549, -0.6471, -0.0902,  ...,  0.6000,  0.6000,  0.6314]],

        [[-0.3647, -0.0745,  0.2157,  ...,  0.1373,  0.1529,  0.1608],
         [-0.3255, -0.2706, -0.0118,  ...,  0.1059,  0.1216,  0.1765],
         [-0.2706, -0.2941, -0.0902,  ...,  0.1059,  0.1059,  0.1451],
         ...,
         [-0.3255, -0.0902,  0.3961,  ...,  0.9294,  0.9137,  0.8431],
         [-0.3569, -0.0275,  0.3961,  ...,  0.9059,  0.8980,  0.9216],
         [-0.3490, -0.2863,  0.3333,  ...,  0.7961,  0.7882,  0.8275]],

        [[-0.5373, -0.3020, -0.0510,  ..., -0.4039, -0.3882, -0.4353],
         [-0.4824, -0.4667, -0.2471,  ..., -0.4353, -0.4039, -0.3961],
         [-0.3804, -0.4745, -0.3098,  ..., -0.4196, -0.4196, -0.3961],
         ...,
         [-0.6941, -0.5765, -0.3176,  ...,  0.3098,  0.2941,  0.1922],
         [-0.7255, -0.5294, -0.2863,  ...,  0.3020,  0.2941,  0.2863],
         [-0.7176, -0.7882, -0.2784,  ...,  0.1608,  0.1765,  0.1922]]])

(5) 아래의 코드를 이용하여 모델을 선언하라.

model = transformers.AutoModelForImageClassification.from_pretrained(
    "google/vit-base-patch16-224-in21k",
    num_labels=3,
    id2label=id2label,
    label2id=label2id,
)
Some weights of ViTForImageClassification were not initialized from the model checkpoint at google/vit-base-patch16-224-in21k and are newly initialized: ['classifier.bias', 'classifier.weight']
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.

위의 모델을 이용하여 beans['train'][10]의 이미지에 대한 loss를 구하라.

(풀이1)model(...)를 이용

model_input = {
    #'pixel_values':f(beans['train'][10]['image']).reshape(1,3,224,224),
    'pixel_values':torch.stack([f(beans['train'][10]['image'])],axis=0), 
    'labels':torch.tensor([0])
}
model(**model_input)
ImageClassifierOutput(loss=tensor(1.0682, grad_fn=<NllLossBackward0>), logits=tensor([[ 0.0297, -0.0544,  0.0205]], grad_fn=<AddmmBackward0>), hidden_states=None, attentions=None)

(풀이2)model(**data_collator(...))를 이용1 – (\(\star\)) 추천풀이

data_collator = transformers.DefaultDataCollator()
data_collator_input = [{'pixel_values':f(beans['train'][10]['image']), 'labels':torch.tensor(0)}]
model(**data_collator(data_collator_input))
ImageClassifierOutput(loss=tensor(1.0359, grad_fn=<NllLossBackward0>), logits=tensor([[ 0.1254,  0.0804, -0.0237]], grad_fn=<AddmmBackward0>), hidden_states=None, attentions=None)

(풀이3)model(**data_collator(...))를 이용2 – (\(\star\)) 추천풀이

def transforms(examples_dct):
    examples_dct["pixel_values"] = [f(img) for img in examples_dct["image"]]
    del examples_dct["image"]
    return examples_dct
beans_transformed = beans.with_transform(transforms)
data_collator = transformers.DefaultDataCollator()
data_collator_input = [beans_transformed['train'][10]]  
model(**data_collator(data_collator_input))
ImageClassifierOutput(loss=tensor(1.0484, grad_fn=<NllLossBackward0>), logits=tensor([[ 0.0066, -0.1082, -0.0327]], grad_fn=<AddmmBackward0>), hidden_states=None, attentions=None)

(풀이4) – trainer를 이용 (지금상황에서 적합한 풀이는 아닌것 같은데 억지로 풀어보겠습니다..)

def transforms(examples_dct):
    examples_dct["pixel_values"] = [f(img) for img in examples_dct["image"]]
    del examples_dct["image"]
    return examples_dct
beans_transformed = beans.with_transform(transforms)
data_collator = transformers.DefaultDataCollator()
trainer_dummy = transformers.Trainer(
    model=model,
    #args=training_args,
    data_collator=data_collator,
    #train_dataset=beans_transformed["train"],
    #eval_dataset=beans_transformed["validation"],
    #tokenizer=image_processor,
    #compute_metrics=compute_metrics,
)
trainer_dummy.predict(
    datasets.Dataset.from_dict(beans_transformed['train'][10:11])
)
PredictionOutput(predictions=array([[ 0.03504263, -0.08715277,  0.02764845]], dtype=float32), label_ids=array([0]), metrics={'test_loss': 1.056959629058838, 'test_model_preparation_time': 0.0023, 'test_runtime': 0.3726, 'test_samples_per_second': 2.684, 'test_steps_per_second': 2.684})

(6) beans 데이터를 학습하는 코드를 작성하고 모델을 훈련하라.

주의

  1. train 데이터를 훈련용으로, validation 데이터를 검증용으로 사용하라.
  2. 검증자료에 대한 정확도가 90%이상일 경우만 정답으로 인정한다.

(풀이)

## Step1 
#food = datasets.load_dataset("food101", split="train[:5000]").train_test_split(test_size=0.2)
image_processor = transformers.AutoImageProcessor.from_pretrained("google/vit-base-patch16-224-in21k")
def transforms(examples):
    examples["pixel_values"] = [f(img) for img in examples["image"]]
    del examples["image"]
    return examples
beans_transformed = beans.with_transform(transforms)
## Step2 
model = transformers.AutoModelForImageClassification.from_pretrained(
    "google/vit-base-patch16-224-in21k",
    num_labels=3,
    id2label=id2label,
    label2id=label2id,
)
## Step3 
data_collator = transformers.DefaultDataCollator()
accuracy = evaluate.load("accuracy")
def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    predictions = np.argmax(predictions, axis=1)
    return accuracy.compute(predictions=predictions, references=labels)
training_args = transformers.TrainingArguments(
    output_dir="my_awesome_beans_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=2,
    warmup_ratio=0.1,
    logging_steps=10,
    load_best_model_at_end=True,
    metric_for_best_model="accuracy",
    push_to_hub=False,
    report_to="none"
)
trainer = transformers.Trainer(
    model=model,
    args=training_args,
    data_collator=data_collator,
    train_dataset=beans_transformed["train"],
    eval_dataset=beans_transformed["validation"],
    tokenizer=image_processor,
    compute_metrics=compute_metrics,
)
trainer.train()
## Step4 
Fast image processor class <class 'transformers.models.vit.image_processing_vit_fast.ViTImageProcessorFast'> is available for this model. Using slow image processor class. To use the fast image processor class set `use_fast=True`.
Some weights of ViTForImageClassification were not initialized from the model checkpoint at google/vit-base-patch16-224-in21k and are newly initialized: ['classifier.bias', 'classifier.weight']
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
[32/32 00:27, Epoch 1/2]
Epoch Training Loss Validation Loss Accuracy
0 1.018900 0.670571 0.924812
1 0.564200 0.490068 0.954887

TrainOutput(global_step=32, training_loss=0.7599691525101662, metrics={'train_runtime': 27.98, 'train_samples_per_second': 73.91, 'train_steps_per_second': 1.144, 'total_flos': 1.5824006103590093e+17, 'train_loss': 0.7599691525101662, 'epoch': 1.9692307692307693})

(7) 학습된 모델을 사용하여 train, validation, test 데이터셋 각각에 대한 정확도를 계산하라.

(풀이)

trainer.predict(beans_transformed['train']).metrics['test_accuracy']
0.9197292069632496
trainer.predict(beans_transformed['validation']).metrics['test_accuracy']
0.9323308270676691
trainer.predict(beans_transformed['test']).metrics['test_accuracy']
0.8984375

(8) beans['train'][10]의 이미지에 대한 loss를 구하라. (5) 과 비교하여 개선점이 있는가?

(풀이1)

trainer.predict(
    datasets.Dataset.from_dict(beans_transformed['train'][10:11])
)
PredictionOutput(predictions=array([[ 0.51362973,  0.22127132, -0.7328805 ]], dtype=float32), label_ids=array([0]), metrics={'test_loss': 0.710007905960083, 'test_accuracy': 1.0, 'test_runtime': 0.1001, 'test_samples_per_second': 9.99, 'test_steps_per_second': 9.99})

(풀이2)

data_collator_input = [beans_transformed['train'][10]]
model.to("cpu")
model(**data_collator(data_collator_input))
ImageClassifierOutput(loss=tensor(0.7825, grad_fn=<NllLossBackward0>), logits=tensor([[ 0.3677,  0.1649, -0.6252]], grad_fn=<AddmmBackward0>), hidden_states=None, attentions=None)