이미지 Dataset에 대한 평균과 표준편차 구하기

사진 이미지는 촬영된 주변 환경에 따라 그 명도나 채도 등이 서로 다릅니다. 이 사진 이미지를 대상으로 하는 머신러닝을 수행하기 전에 이미지들을 동일한 환경으로 맞춰주는 후처리로 전체 이미지에 대한 화소값의 평균과 표준편차를 구해 이 값을 이미지들에 일괄적으로 적용합니다.

아래의 코드는 PyTorch에서 Dataset에 대한 평균과 표준편차를 구하기 위한 코드입니다.

transform = transforms.Compose([
    transforms.ToTensor()
])

dataset = torchvision.datasets.CIFAR10(root='./data/cifar10', train=True, download=True, transform=transform) 

mean = dataset.train_data.mean(axis=(0,1,2))
std = dataset.train_data.std(axis=(0,1,2))

mean = mean / 255
std = std / 255

실제 mean과 std의 값은 각각 [0.4913, 0.4821, 0.4465], [0.2470, 0.2434, 0.2615]와 유사한데, 실제 CIFAR10 데이터를 이용한 딥러닝 예제 코드에서 상수값으로 입력되는 바로 그 값입니다. 실제로 이 평균과 편차는 다음 코드 예시를 통해 적용됩니다.

train_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize(mean, std)
])

train_dataset = torchvision.datasets.CIFAR10(root='./data/cifar10', train=True, download=True, transform=train_transform) 

사람에 대한 Detection, Segmentation @A.I-TestBed

딥러닝 모델을 활용하여 어떤 이미지에서 사람의 위치를 찾아 내는 방식은 Detection과 Segmentation으로 분류할 수 있으며, Segmentation은 다시 Instance Segmentation과 Semantic Segmentation으로 나뉩니다.

Detection은 이미지에서 사람의 위치를 사각형 영역으로 잡아주는 방식이고 Segmentation은 이미지에서 사람에 해당하는 위치를 화소(Pixel)단위로 잡아줍니다. Segmentation 방식 중 Instance는 하나의 이미지에 여러명의 사람이 있다면 각 사람(각 Instance 별로)에 대해 분리해 픽셀 위치를 잡아주고, Semantic은 이미지에서 사람이라는 의미(Semantic)를 가지는 픽셀들을 잡아줍니다.

Segmentation 방식은 Detection 보다 학습과 메모리 소비가 훨씬 더 많이 소요되며, 훈련 데이터 중 레이블을 만들기가 훨씬 어렵습니다. Detection의 레이블은 사람에 해당하는 사각형 영역만을 지정하면 되지만, Segmentation의 레이블은 사람에 해당하는 픽셀을 모두 지정해줘야 하기 때문입니다.

Detection 방식의 모델은 매우 다양한데, 대표적으로 R-CNN 등이 있으며, Segmentation 방식의 모델에는 R-CNN을 통해 먼저 위치를 사각형 영역으로 잡고 다시 이 사각형 영역에 대해서 사람에 해당하는 픽셀을 잡는 Mask R-CNN이 있으며, 그냥 처음부터 이미지의 모든 픽셀에 대해 사람인지를 잡는 FCN 모델이 있습니다. Mask R-CNN 모델은 Instance Segmentation 방식이고 FCN 모델은 Semantic Segmentation 방식입니다. FCN 모델은 Semantic Segmentation 방식의 가장 기본이 되는 모델로 이 모델을 기본으로 Semantic Segmentation 방식을 더 개선한 다양한 모델이 파생되었습니다.

아래의 동영상은 머신러닝을 테스트하기 위한 TestBed 웹페이지로써 Detection과 Segmentation에 대한 기능을 보여줍니다.

참고로 위의 동영상에서 Detection과 Instance Segmentation의 결과에 대해 추론(hypothesis)값이 90% 이상으로 잡았습니다. 이 값을 좀더 내린다면 사람으로 잡지 못한 이미지의 부분에 대해서도 검출될 것입니다.

Accuracy, Precision, Recall

어떤 문제가 True, False인지를 예측할 때, 얼마나 정확하게 예측하는지의 척도로써 사용되는 3가지입니다. 먼저 Accuracy는 다음과 같습니다.

다음은 Precision입니다.

다음은 Recall입니다.

위의 공식에서 언급된 TP, FP, FN, FN은 다음과 같습니다. 정답이 참인지 거짓인지에 따라 T(True), F(False)로 표기하고 추정이 참인지 거짓인지에 따라 P(Positive), N(Negative)로 표기한 것입니다.

정리하면 Accuracy는 전체 예측에 대해 옳바르게 예측한 비율이고, Precision은 True라고 예측한 것 중에서 실제로 True인 것에 대한 비율입니다. Recall은 실제 True인 것 중에서 True라고 예측한 것에 대한 비율입니다.

감기 예측 모델을 예로 들면 Accuracy는 전체 예측에 대해 얼마나 올바르게 예측했는지에 대한 지표이고, Precision은 얼마나 정확하게 감기라고 예측하는지에 대한 지표이며, Recall은 실제 감기인 사람에 대해서 얼마나 정확하게 감기라고 예측하는지에 대한 지표입니다.

[Python] 알파벳을 인덱스로 구성하기

먼저 다음과 같은 문장이 있다고 하자.

sample = 'I will go.'

위의 문장을 구성하는 알파벳 중에서 중복되지 않는 고유한 알파벳만을 추출하면..

uniq_chars = set(sample) # {'w', ' ', 'o', 'i', 'l', '.', 'g', 'I'}

위의 추출된 결과는 set이므로 이를 list로 만들면..

idx2char = list(uniq_chars) # ['w', ' ', 'o', 'i', 'l', '.', 'g', 'I']

인덱스 값을 Value로, 해당 인덱스의 알파벳을 Key로 구성된 데이터는 다음처럼 얻을 수 있다.

char2idx = {c: i for i, c in enumerate(idx2char)} # {'w': 0, ' ': 1, 'o': 2, 'i': 3, 'l': 4, '.': 5, 'g': 6, 'I': 7}

이제 처음 문장(sample 변수)을 인덱스 값으로 구성된 list는 다음과 같다.

sample_idx = [char2idx[c] for c in sample] # [7, 1, 0, 3, 4, 4, 1, 6, 2, 5]

적은 수의 데이터를 이용한 머신러닝

이미지와 라벨 데이터 각각 50개를 이용한 머신러닝에 대한 실험입니다. Segmentation 방식이며 BOX 검출을 위한 모델은 Fast R-CNN을 사용했습니다. 하이퍼파라메터 중 에폭수 만을 변경하여 테스트했고 러닝이 완료되면 50개의 훈련 데이터 중에 하나로 시험했습니다. 시험 데이터와 훈련 데이터는 나누는게 일반적이나, 워낙 훈련 데이터가 소량인지라 시각적으로 시험 결과를 부각시키기 위함입니다. 글 중에는 주관적인 직관으로 작성된 내용도 포함하고 있습니다.

가장 먼저 학습을 전혀 하지 않은, 즉 에폭이 0인 경우에도 시험을 했습니다. 아래는 에폭이 0인 경우와 1인 경우에 대한 결과입니다.

Segmentation을 위한 모델은 Mask R-CNN을 이용했는데, 이 모델은 가장 먼저 특징점을 찾습니다. 찾을 특징점의 최대 개수는 100로 지정하였으므로, 학습이 전혀 되지 않아도 일단 100개를 찾게되었지만 검출 결과는 사람이 아닙니다. 하지만 특징점 추출은 이미지의 Gradient가 가장 심한 곳이 대부분일 것으므로 사람과 배경 경계, 건물의 창문 모서리 등과 같은 코너(Corner) 지점에서 검출이 되는 것으로 예상할 수 있습니다. 주목할 점은 검출 대상이 사람인지에 대한 최대 확률은 약 50%로 나왔다는 점입니다. 이는 전혀 학습되지 않을 경우 해당 픽셀이 사람인지 아닌지에 대한 결정은 단순 확률로써 50% 이기 때문입니다. 이제 학습을 1 에폭 수행했을 경우 픽셀 검출이 사람으로 특정되는 것을 볼 수 있습니다. 검출 대상 개수는 총 77개로 줄어들었지만 해당 검출 대상이 사람이라는 확률은 약 28%가 도출되었습니다. 또한 검출 픽셀이 대상의 중심으로 몰려있다는 점이 주목할만합니다. 에폭수를 늘리면 검출대상과 검출 대상이 사람인지에 대한 확률이 올라갈 것으로 기대하고 에폭 수를 3과 10을로 지정하여 학습을 시켰고, 그 결과는 다음과 같습니다.

검출 대상 결과는 좀더 사람의 형태에 확장되면서 접근하고 있습니다. 검출 대상도 에폭수에 비례하여 줄어들었구요. 그러나 검출 픽셀이 사람인지에 대한 확률은 3 에폭에서는 약 33%이고 10 에폭에서는 약 27%입니다. 오히려 더 줄어 들었습니다. 너무 적은 데이터만을 가지고 학습 한 결과라고 예상합니다. 확인을 위해 에폭을 20과 100으로 잡아 학습 시킨 결과는 아래와 같습니다.

20 에폭에서는 검출 개수가 더 줄었지만, 100 에폭에서는 오히려 검출 개수가 늘어났습니다. 또한 검출 대상이 사람인지에 대한 확률값이 각각 약 32%과 약 29%로 증가는 전혀 없다고, 아니 오히려 떨어졌습니다. 이 역시 훈련 데이터가 너무 적기 때문인 것으로 판단됩니다.

일단 소량의 데이터를 통해 학습 시킨 이유는 모델의 검증을 위해서입니다. 학습 각 단계마다 손실값이 줄어들고 있는지 등을 빠르게 확인하기 위해서인데.. 실제로 이 실험의 경우 에폭이 증가 할수록 손실값은 줄어드는 경향을 보였고 특정 에폭지점 이상부터는 손실값이 더 이상 감소하지 않는 것을 볼 수 있었습니다. 이런 손실값의 경향에 따라 모델은 어느 정도 접합하다라고 판단할 수 있고, 데이터를 더 추가하여 학습 시키면 더 나은 결과를 얻을 수 있을 것입니다. 데이터를 더 이상 추가할 수 없는 경우에는 하이퍼 파라메터를 변경하면서 정확도를 향상 시킬 수 있습니다.