rank-1, rank-5rank-N 정확도의 개념에 대해 논의하겠습니다. 특히 컴퓨터 비전 및 이미지 분류 공간에서 딥러닝 문헌을 읽을 때 ranked accuracy라는 개념을 접하게 될 것입니다. 예를 들어 ImageNet 데이터 세트에서 평가된 머신 러닝 방법을 제시하는 거의 모든 논문은 rank-1 및 rank-5 정확도 측면에서 결과를 제시합니다.

 

rank-1 rank-5 정확도는 정확히 무엇인지 그리고 기존의 정확도 (, 정밀도) 어떻게 다른지 알아보겠습니다. ranked accuracyFlowers-17 CALTECH-101 데이터 세트에서 훈련 기계 학습 모델에 적용합니다.

 

Ranked Accuracy

왼쪽 : 신경망이 분류하려고하는 개구리의 입력 이미지. 오른쪽 : 자동차의 입력 이미지.

 

 

ranked accuracy는 다음 예를 통해 설명할 수 있습니다. 비행기, 자동차, 새, 고양이, 사슴, 개, 개구리, 말, 배, 트럭 등 10가지 클래스가 포함 된 CIFAR-10 데이터 세트에서 훈련 된 신경망을 평가한다고 가정 해 보겠습니다. 

 

왼쪽 : 개구리그림에 대해 신경망이 반환 한 클래스 레이블 확률. 오른쪽 : 자동차에 대해 네트워크에서 반환 한 클래스 레이블 확률

입력 이미지(개구리)가 주어지면 신경망에 각 클래스 레이블에 대한 확률을 계산하도록 합니다. 그러면 신경망은 표(왼쪽)에 나열된 클래스 레이블 확률을 반환합니다.

 

입력 이미지 (개구리)가 주어지면 신경망에 각 클래스 레이블에 대한 확률을 계산하도록 요청합니다. 그러면 신경망은 표 (왼쪽)에 나열된 클래스 레이블 확률을 반환합니다.

확률이 가장 큰 클래스 레이블은 실제로 정확한 예측 인 frog(97.3 %)입니다. 이 과정을 반복한다면 :
1단계 : 데이터 세트의 각 입력 이미지에 대한 클래스 레이블 확률 계산합니다.
2단계 : 실측 레이블이 확률이 가장 큰 예측 클래스 레이블과 같은지 결정합니다.
3단계 : 2단계가 참인 횟수를 계산합니다.

우리는 rank-1 정확도에 도달하는 것이 목표입니다. 따라서 rank-1 정확도는 최상위 예측이 실측 레이블과 일치하는 예측의 백분율입니다. 이것이 우리가 계산하는 데 사용되는 "표준"유형의 정확도입니다. 올바른 예측의 총 수를 데이터 포인트 수로 나눕니다.

그런 다음 이 개념을 rank-5 정확도로 확장 할 수 있습니다. 1위 예측에만 신경을 쓰지 않고 상위 5개 예측까지 고려합니다. 그러면 평가 프로세스는 다음과 같습니다.

1 단계 : 데이터 세트의 입력 이미지에 대한 클래스 레이블 확률을 계산합니다.
2단계 : 예측된 클래스 레이블 확률을 내림차순으로 정렬하여 확률이 더 높은 레이블이 목록의 맨 앞에 배치되도록합니다.
3단계 : 2단계의 상위 5개 예측 레이블에 실측 레이블이 있는지 확인합니다.
4단계 : 3단계가 참인 횟수를 계산합니다.

rank-5는 단순히 rank-1 정확도의 확장입니다. 네트워크에서 상위 5 개의 예측을 고려하기 때문입니다. 예를 들어 임의의 신경망을 기반으로 CIFAR-10 범주로 분류 할 입력 이미지를 다시 생각해 보겠습니다 (자동차). 네트워크를 통과 한 후 표(오른쪽)에 자세히 설명 된 클래스 레이블 확률을 얻습니다.

 

우리의 이미지는 분명히 자동차입니다. 그러나 우리 네트워크는 트럭을 최고의 예측으로 판단했습니다.
– 이 것은 rank-1 정확도에 대한 잘못된 예측으로 간주됩니다. 그러나 네트워크의 상위 5개 예측을 살펴보면 자동차가 실제로 2위 예측이라는 것을 알 수 있으며 이는 rank-5를 계산할 때 정확합니다. 이 접근 방식은 임의의 랭크 N 정확도로 쉽게 확장 될 수 있습니다. 그러나 우리는 일반적으로 rank-1 및 rank-5 정확도만 계산합니다. 이는 왜 rank-5 정확도를 계산해야 하는가?에 대한 의문이 생길 수 있습니다.

CIFAR-10 데이터 세트의 경우 rank-5 정확도를 계산하는 것은 약간 어리석은 일이지만 크고 까다로운 데이터 세트, 특히 세분화 된 분류의 경우 특정 CNN에서 rank-5개 예측을 보는 것이 도움이 되는 경우가 많습니다. 아마도 우리가 rank-1 및 rank-5 정확도를 계산하는 이유에 대한 가장 좋은 예는 Szegedy et al. 왼쪽에는 시베리안 허스키, 오른쪽에는 에스키모 개가 있습니다. 대부분의 인간은 두 동물의 차이를 인식하지 못할 것입니다. 그러나이 두 클래스는 ImageNet 데이터 세트에서 유효한 레이블입니다.

왼쪽 : 시베리안 허스키. 오른쪽 : 에스키모 개.

유사한 특성을 가진 많은 클래스 레이블을 포함하는 대규모 데이터 세트로 작업 할 때 네트워크 성능을 확인하기 위해 rank-1 정확도의 확장으로 rank-5 정확도를 종종 검사합니다. 이상적인 세계에서 rank-1 정확도는 rank-5 정확도와 동일한 비율로 증가하지만 까다롭거나 실제 데이터 세트에서는 항상 그런 것은 아닙니다.

따라서 학습이 진행중에도 여전히 "학습"중인지 확인하기 위해 rank-5 정확도도 조사합니다. rank-1 정확도가 훈련이 끝날 무렵 정체되는 경우 일 수 있지만, 우리가 만든 네트워크가 더 많은 차별적 기능을 학습함에 따라 rank-5 정확도는 계속 향상됩니다 (그러나 rank-1 예측을 추월 할만큼 차별적이지는 않음). 

사전 훈련 된 CNN을 사용하여 소수의 데이터 세트에서 특성을 추출 했으므로 특히 VGG16Animals, CALTECH-101 또는 _Flowers-17_이 아닌 ImageNet에서 훈련되었다는 점을 고려할 때 이러한 특성이 실제로 얼마나 발휘되는지 확인해보겠습니다.

간단한 선형 모델이 이러한 특징을 사용하여 이미지를 분류할 때 얼마나 그 효과는 얼마나 될까요?
새 파일을 열고 이름을 train_model.py로 지정하고 다음 코드를 작성합니다.

# USAGE
# python train_model.py --db ../datasets/animals/hdf5/features.hdf5 \
#    --model animals.cpickle
# python train_model.py --db ../datasets/caltech-101/hdf5/features.hdf5 \
#    --model caltech101.cpickle
# python train_model.py --db ../datasets/flowers17/hdf5/features.hdf5 \
#    --model flowers17.cpickle

# import the necessary packages
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import classification_report
import argparse
import pickle
import h5py

GridSearchCV클래스는 매개 변수를 LogisticRegression 분류기로 바꾸는 데 사용됩니다. 학습 후에는 pickle을 사용하여 LogisticRegression 모델을 디스크에 직렬화합니다.

# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-d", "--db", required=True,
    help="path HDF5 database")
ap.add_argument("-m", "--model", required=True,
    help="path to output model")
ap.add_argument("-j", "--jobs", type=int, default=-1,
    help="# of jobs to run when tuning hyperparameters")
args = vars(ap.parse_args())

스크립트에는 두 개의 명령 줄 인수와 세 번째 선택적 인수가 필요합니다.

  1. --db : 추출된 기능 및 클래스 레이블이 포함 된 HDF5 데이터 세트의 경로입니다.
  2. --model : 여기서 출력 로지스틱 회귀 분류기에 대한 경로를 제공합니다.
  3. --jobs : 하이퍼 파라미터를 로지스틱 회귀 모델에 맞게 조정하기 위해 그리드 검색을 실행할 때 동시 작업 수를 지정하는 데 사용되는 선택적 정수입니다.

HDF5 데이터 세트를 열고 학습/테스트 분할 위치를 결정하겠습니다.

# open the HDF5 database for reading then determine the index of
# the training and testing split, provided that this data was
# already shuffled *prior* to writing it to disk
db = h5py.File(args["db"], "r")
i = int(db["labels"].shape[0] * 0.75)

이 장의 앞부분에서 언급했듯이, 우리는 관련 이미지 / 특징 벡터를 HDF5 데이터 세트에 쓰기 전에 의도적으로 이미지 경로를 셔플했습니다.이 이유는 라인 22와 23에서 명확 해집니다.

데이터 세트가 너무 커서 메모리에 넣을 수 없기 때문에 훈련 및 테스트 분할을 결정하는 효율적인 방법이 필요합니다. HDF5 데이터 세트에 얼마나 많은 항목이 있는지 알고 있기 때문에 (그리고 데이터의 75 %를 훈련에 사용하고 25 %를 평가에 사용하고자 함을 알고 있으므로) 데이터베이스에 75 % 인덱스 i를 간단히 계산할 수 있습니다. 인덱스 i 이전의 모든 데이터는 훈련 데이터로 간주됩니다. i 이후의 모든 데이터는 데이터를 테스트합니다.

훈련 및 테스트 분할을 고려하여 로지스틱 회귀 분류기를 훈련 해 보겠습니다.

# define the set of parameters that we want to tune then start a
# grid search where we evaluate our model for each value of C
print("[INFO] tuning hyperparameters...")
params = {"C": [0.1, 1.0, 10.0, 100.0, 1000.0, 10000.0]}
model = GridSearchCV(LogisticRegression(solver="lbfgs",
    multi_class="auto"), params, cv=3, n_jobs=args["jobs"])
model.fit(db["features"][:i], db["labels"][:i])
print("[INFO] best hyperparameters: {}".format(model.best_params_))

# evaluate the model
print("[INFO] evaluating...")
preds = model.predict(db["features"][i:])
print(classification_report(db["labels"][i:], preds,
    target_names=db["label_names"]))

28-31 행은 최적 값이 무엇인지 결정하기 위해 로지스틱 회귀 분류기의 엄격함 인 매개 변수 C에 대한 그리드 검색을 실행합니다. Logistic Regression에 대한 자세한 검토는이 책의 범위를 벗어나므로 Logistic Regression 분류기 [13]에 대한 철저한 검토는 Andrew Ng의 메모를 참조하십시오.

배열 슬라이스를 통해 훈련 데이터와 훈련 레이블을 어떻게 표시하는지 확인하십시오.

# evaluate the model
print("[INFO] evaluating...")
preds = model.predict(db["features"][i:])
print(classification_report(db["labels"][i:], preds,
    target_names=db["label_names"]))

다시 말하지만, 인덱스 i 이전의 모든 데이터는 훈련 세트의 일부입니다. 최상의 하이퍼 파라미터가 발견되면 테스트 데이터에서 분류자를 평가합니다 (36-38 행).

여기에서 테스트 데이터와 테스트 레이블은 배열 슬라이스를 통해 액세스됩니다.

인덱스 i 이후의 모든 것은 테스트 세트의 일부입니다. HDF5 데이터 세트가 디스크에 있고 (메모리에 맞추기에는 너무 커서) NumPy 배열 인 것처럼 처리 할 수 있습니다. 이는 HDF5 및 h5py를 딥 러닝 및 머신에 함께 사용하는 것의 큰 장점 중 하나입니다. 학습 과제.

마지막으로 LogisticRegression 모델을 디스크에 저장하고 데이터베이스를 닫습니다.

# serialize the model to disk
print("[INFO] saving model...")
f = open(args["model"], "wb")
f.write(pickle.dumps(model.best_estimator_))
f.close()

# close the database
db.close()

 

'keras > 2. Feature Extractors' 카테고리의 다른 글

3. The Feature Extraction Process  (0) 2020.08.24
2. Writing Features to an HDF5 Dataset  (0) 2020.08.22
1. Networks as Feature Extractors  (0) 2020.08.22

임의의 이미지 데이터세트에서 특징을 추출하는데 사용할수있는 Python스크립트를 정의해보겠습니다(입력데이터세트가 특정디렉토리구조를 따르는경우). 새파일을 열고 이름을 extract_features.py로지정하여 작업을 시작하겠습니다.

 

사전 훈련 된 VGG16 네트워크의 Keras 구현을 가져와 기능 추출기로 사용하겠습니다. LabelEncoder() 클래스는 클래스 레이블을 문자열에서 정수로 변환하는 데 사용됩니다. 또한 7 행에서 HDF5DatasetWriter를 가져 와서 CNN에서 추출한 기능을 HDF5 데이터 세트에 쓸 수 있습니다.

 

tqdm 모듈을 이용하여 진행 표시줄을 추가하겠습니다.

# USAGE
# python extract_features.py --dataset datasets/animals/images \
#     --output datasets/animals/hdf5/features.hdf5
# python extract_features.py --dataset datasets/caltech-101/images \
#     --output datasets/caltech-101/hdf5/features.hdf5
# python extract_features.py --dataset datasets/flowers17/images \
#     --output datasets/flowers17/hdf5/features.hdf5

# import the necessary packages
from keras.applications import VGG16
from keras.applications import imagenet_utils
from keras.preprocessing.image import img_to_array
from keras.preprocessing.image import load_img
from sklearn.preprocessing import LabelEncoder
from inout.hdf5datasetwriter import HDF5DatasetWriter
from imutils import paths
from tqdm import tqdm
import numpy as np
import argparse
import random
import os

extract_features.py 스크립트에는 두 개의 명령 줄 인수와 두 개의 선택적 인수가 필요합니다. --dataset argument는 기능을 추출하려는 이미지의 입력 디렉토리 경로를 제어합니다. --output argument는 출력 HDF5 데이터 파일의 경로를 결정합니다.

 

그런 다음 --batch-size를 제공할 수 있습니다. 이것은 한 번에 VGG16을 통해 전달되는 배치의 이미지 수입니다. 여기에서는 32의 값이 합리적이지만 시스템에 충분한 메모리가 있으면 값을 늘릴 수 있습니다. --buffer-size 스위치는 HDF5 데이터 세트용 버퍼를 쓰기 전에 메모리에 저장할 추출 된 특징의 수를 제어합니다. 다시 말하지만, 컴퓨터에 충분한 메모리가 있으면 버퍼 크기를 늘릴 수 있습니다.

 

다음 단계는 디스크에서 이미지 경로를 가져 와서 정리하고 레이블을 인코딩하는 것입니다.

# store the batch size in a convenience variable
bs = args["batch_size"]

# grab the list of images that we'll be describing then randomly
# shuffle them to allow for easy training and testing splits via
# array slicing during training time
print("[INFO] loading images...")
imagePaths = list(paths.list_images(args["dataset"]))
random.shuffle(imagePaths)

# extract the class labels from the image paths then encode the
# labels
labels = [p.split(os.path.sep)[-2] for p in imagePaths]
le = LabelEncoder()
labels = le.fit_transform(labels)

데이터 세트의 모든 이미지에 대한 파일인 imagePaths를 가져옵니다. 그런 다음 이 데이터셋을 셔플합니다. 파일 경로가 디렉토리 구조를 갖는다 고 가정하고 파일 경로에서 클래스 레이블 이름을 추출합니다.

# load the VGG16 network
print("[INFO] loading network...")
model = VGG16(weights="imagenet", include_top=False)

# initialize the HDF5 dataset writer, then store the class label
# names in the dataset
dataset = HDF5DatasetWriter(
    (len(imagePaths), 512 * 7 * 7),
    args["output"],
    dataKey="features",
    bufSize=args["buffer_size"],
)
dataset.storeClassLabels(le.classes_)

우리의 데이터 셋이 이 디렉토리 구조를 따르는 경우, 구분 기호 (Unix 컴퓨터에서는 '/', Windows에서는 '')를 기반으로 경로를 배열로 분할 한 다음 배열의 마지막에서 두 번째 항목(이 작업은 특정 이미지의 클래스 레이블을 생성)합니다. 레이블이 주어지면 정수로 인코딩합니다 (학습 프로세스 중에 원-핫 인코딩을 수행합니다).

 

이제 VGG16 네트워크 가중치를 로드하고 HDF5DatasetWriter를 인스턴스화 할 수 있습니다.

 

디스크에서 사전 훈련된 VGG16 네트워크를 로드합니다. 그러나 include_top = False 매개 변수는 최종 완전 연결 계층이 아키텍처에 포함되지 않아야 함을 나타냅니다. 따라서 네트워크를 통해 이미지를 순방향 전파 할 때 FC 계층의 소프트 맥스 분류기에 의해 생성된 확률보다는 최종 POOL 계층 이후의 특성 값을 얻습니다.

 

HDF5DatasetWriter를 인스턴스화합니다. 첫 번째 매개 변수는 데이터 세트의 차원으로, 각각 512x7x7 = 25,088크기의 특징 벡터를 갖는 총 이미지 len(imagePaths)이 있습니다. 그런 다음 레이블 인코더에 따라 클래스 레이블의 문자열 이름을 저장합니다.

이제 실제 특징 추출을 시작하겠습니다.

image_iter = np.arange(0, len(imagePaths), bs)
# loop over the images in batches
for i in tqdm(
    image_iter, total=len(image_iter), desc="Extracting Features"
):
    # extract the batch of images and labels, then initialize the
    # list of actual images that will be passed through the network
    # for feature extraction
    batchPaths = imagePaths[i : i + bs]
    batchLabels = labels[i : i + bs]
    batchImages = []

--batch-size 배치로 imagePaths를 반복하기 시작합니다. 해당 배치에 대한 이미지 경로와 레이블을 추출하고, VGG16에로드 및 공급 될 이미지를 저장하는 목록을 초기화합니다.

특징 추출을 위해 이미지를 준비하는 것은 CNN을 통해 분류 할 이미지를 준비하는 것과 정확히 동일합니다.

    for (j, imagePath) in enumerate(batchPaths):
        # load the input image using the Keras helper utility
        # while ensuring the image is resized to 224x224 pixels
        image = load_img(imagePath, target_size=(224, 224))
        image = img_to_array(image)

        # preprocess the image by (1) expanding the dimensions and
        # (2) subtracting the mean RGB pixel intensity from the
        # ImageNet dataset
        image = np.expand_dims(image, axis=0)
        image = imagenet_utils.preprocess_input(image)

        # add the image to the batch
        batchImages.append(image)

배치의 각 이미지 경로를 반복합니다. 각 이미지는 디스크에서 로드되고 Keras 호환 어레이로 변환됩니다. 그런 다음 이미지를 전처리 한 다음 batchImages에 추가합니다.

 

batchImages의 이미지에 대한 특징 벡터를 얻으려면 모델의 .predict 메서드를 호출하기 만하면됩니다.

    # pass the images through the network and use the outputs as
    # our actual features
    batchImages = np.vstack(batchImages)
    features = model.predict(batchImages, batch_size=bs)

    # reshape the features so that each image is represented by
    # a flattened feature vector of the `MaxPooling2D` outputs
    features = features.reshape((features.shape[0], 512 * 7 * 7))

    # add the features and labels to our HDF5 dataset
    dataset.add(features, batchLabels)


# close the dataset
dataset.close()

NumPy.vstack 방법을 사용하여 이미지가 모양 (N, 224, 224, 3)을 갖도록 이미지를 "수직으로 스택"합니다. 여기서 N은 배치의 크기입니다.

 

네트워크를 통해 batchImages를 전달하면 실제 특성 벡터가 생성됩니다. VGG16의 헤드에서 완전히 연결된 레이어를 잘라 냈으므로 이제 최종 최대 풀링 작업 후에 값이 남습니다. 그러나 POOL의 출력은 모양 (N, 512, 7, 7)을 가지며, 이는 각각 크기가 7 x 7512개의 필터가 있음을 의미합니다. 이러한 값을 특성 벡터로 취급하려면, 모양 (N, 25088)을 가진 배열로 변환해야합니다. HDF5 데이터 세트에 기능과 batchLabels를 추가합니다.

 

최종 코드 블록은 HDF5 데이터 세트 닫기를 처리합니다.

python extract_features.py --dataset ~/data/animals/images/ \
--output datasets/animals/hdfs/features.hdf5

python extract_features.py --dataset ~/data/caltech-101/images/ \
--output datasets/caltech-101/hdfs/features.hdf5

python extract_features.py --dataset ~/data/flowers17/images/ \
--output datasets/flowers17/hdfs/features.hdf5

이작업은 cpu환경에서는 매우 오래 걸리므로 gpu환경에서 하는 것을 추천합니다.

 

feature extractor가 진행되는 과정

feature_extractors.tar.gz
0.18MB

 

VGG16 (또는 다른 CNN)을 기능 추출기로 만들기 전에 먼저 약간의 인프라를 개발해야합니다. 특히, 이름에서 알 수 있듯이 NumPy 배열 (기능, 원시 이미지 등)의 입력 세트를 가져와 HDF5 형식으로 작성하는 역할을하는 HDF5DatasetWriter라는 Python 클래스를 정의해야합니다.

이 클래스 내에서 기능을 빌드하려면 두 개의 Python 패키지 만 필요합니다. 내장된 os 모듈과 h5py이므로 HDF5 바인딩에 액세스 할 수 있습니다.

거기에서 생성자를 정의하겠습니다.

# import the necessary packages
import h5py
import os


class HDF5DatasetWriter:
    def __init__(self, dims, outputPath, dataKey="images", bufSize=1000):
        # check to see if the output path exists, and if so, raise
        # an exception
        if os.path.exists(outputPath):
            raise ValueError(
                "The supplied `outputPath` already "
                "exists and cannot be overwritten. Manually delete "
                "the file before continuing.",
                outputPath,
            )

        # open the HDF5 database for writing and create two datasets:
        # one to store the images/features and another to store the
        # class labels
        self.db = h5py.File(outputPath, "w")
        self.data = self.db.create_dataset(dataKey, dims, dtype="float")
        self.labels = self.db.create_dataset("labels", (dims[0],), dtype="int")

        # store the buffer size, then initialize the buffer itself
        # along with the index into the datasets
        self.bufSize = bufSize
        self.buffer = {"data": [], "labels": []}
        self.idx = 0

HDF5DatasetWriter의 생성자는 4개의 매개 변수를 허용하며 그 중 2개는 선택 사항입니다. dims 매개 변수는 데이터 세트에 저장할 데이터의 크기 또는 모양을 제어합니다. dimsNumPy.shape라고 생각할 수 있습니다. 28 × 28 = 784 MNIST 데이터 세트의 (플래 팅 된) 원시 픽셀 강도를 저장하는 경우 MNIST에 각각 784의 차원을 가진 70,000 개의 예제가 있으므로 dims = (70000, 784) 입니다. CIFAR-10 이미지의 경우 CIFAR-10 데이터 세트에 총 60,000 개의 이미지가 있으며 각 이미지는 32x32x3 RGB 이미지로 표시되므로 dims = (60000, 32, 32, 3)입니다.

 

전이 학습 및 특징 추출의 맥락에서 VGG16 아키텍처를 사용하고 최종 POOL 계층 이후에 출력을 가져옵니다. 최종 POOL 레이어의 출력은 512x 7 x 7이며, 이 차원을 펼치면 길이가 25,088 인 특징 벡터를 생성합니다. 따라서 특징 추출에 VGG16을 사용하는 경우 dims = (N, 25088)을 설정합니다. 여기서 N은 데이터 세트의 총 이미지 수입니다.

 

HDF5DatasetWriter 생성자의 다음 매개 변수는 outputPath입니다. 이것은 출력 HDF5 파일이 디스크에 저장 될 경로입니다. 선택적 dataKey는 알고리즘이 학습 할 데이터를 저장할 데이터 세트의 이름입니다. 대부분의 경우 원시 이미지를 HDF5 형식으로 저장하므로이 값의 기본값은 "image"입니다. CNN에서 추출한 기능을 파일에 저장하고 있음을 나타 내기 위해 dataKey = "features"를 설정합니다.

마지막으로 bufSize는 인 메모리 버퍼의 크기를 제어하며 기본적으로 1,000 개의 특징 벡터 / 이미지로 설정됩니다. bufSize에 도달하면 버퍼를 HDF5 데이터 세트로 넘깁니다.

그런 다음 outputPath가 이미 존재하는지 확인합니다. 이 경우 기존 데이터베이스를 덮어 쓰지 않기 때문에 최종 사용자에게 오류가 발생합니다.


제공된 outputPath를 사용하여 쓰기위한 HDF5 파일을 엽니다. dataKey 이름과 제공된 값을 사용하여 데이터 세트를 만듭니다. 여기에 원시 이미지 / 추출 된 기능이 저장됩니다. 두 번째 데이터 세트를 생성합니다. 이 데이터 세트는 데이터 세트의 각 레코드에 대한 (정수) 클래스 레이블을 저장한 다음 버퍼를 초기화합니다.

다음으로 버퍼에 데이터를 추가하는 데 사용되는 add 메서드입니다.

    def add(self, rows, labels):
        # add the rows and labels to the buffer
        self.buffer["data"].extend(rows)
        self.buffer["labels"].extend(labels)

        # check to see if the buffer needs to be flushed to disk
        if len(self.buffer["data"]) >= self.bufSize:
            self.flush()

add 메서드에는 두 개의 매개 변수가 필요합니다. 데이터 세트에 추가 할 과 해당하는 클래스 라벨입니다. 행과 레이블은 모두 각 버퍼에 추가됩니다. 버퍼가 가득 차면 flush 메소드를 호출하여 파일을 작성하고 재설정합니다.

플러시 방법에 대해 이제 함수를 정의하겠습니다.

 

    def flush(self):
        # write the buffers to disk then reset the buffer
        i = self.idx + len(self.buffer["data"])
        self.data[self.idx : i] = self.buffer["data"]
        self.labels[self.idx : i] = self.buffer["labels"]
        self.idx = i
        self.buffer = {"data": [], "labels": []}

 

또한 storeClassLabels라는 편리한 유틸리티 함수를 정의합니다.이 함수가 호출되면 별도의 데이터 세트에 클래스 레이블의 원래 문자열 이름을 저장합니다.

    def storeClassLabels(self, classLabels):
        # create a dataset to store the actual class label names,
        # then store the class labels
        dt = h5py.special_dtype(vlen=str)  # `vlen=unicode` for Py2.7
        labelSet = self.db.create_dataset("label_names", (len(classLabels),), dtype=dt)
        labelSet[:] = classLabels

 

마지막으로 마지막 함수 close는 버퍼에 남아있는 데이터를 HDF5에 쓰고 데이터 세트를 닫는데 사용됩니다.

    def close(self):
        # check to see if there are any other entries in the buffer
        # that need to be flushed to disk
        if len(self.buffer["data"]) > 0:
            self.flush()

        # close the dataset
        self.db.close()

HDF5DatasetWriter는 머신 러닝이나 딥러닝과 전혀 관련이 없습니다. 단순히 데이터를 HDF5 형식으로 저장하는 데 사용되는 클래스일 뿐입니다. 딥러닝을 계속하면서 새로운 문제를 설정할 때 초기 작업의 대부분은 작업할 수 있는 형식으로 데이터를 가져 오는 것임을 알게 될 것입니다. 조작하기 쉬운 형식의 데이터가 있으면 머신러닝 및 딥러닝 기술을 데이터에 적용하기가 훨씬 쉬워집니다.

즉, HDF5DatasetWriter 클래스는 딥 러닝 및 컴퓨터 비전에 특화되지 않은 유틸리티 클래스입니다.


hdf5datasetwriter.py
0.00MB

 

이제 전이 학습의 개념, 사전 학습 된 모델을 'shortcut'으로 사용하여 원래 학습되지 않은 데이터에서 패턴을 학습하는 기능을 알아보겠습니다.

 

Transfer learning(전이 학습)은 다른 훈련 패러다임을 제안합니다. 기존의 사전 훈련된 분류기를 사용할 수 있고 이를 새로운 분류 작업의 시작점으로 사용할 수 있다면 탁월한 성능을 발휘할 것 같습니다.

 

ImageNet 같은 대규모 데이터 세트에서 훈련된 심층 신경망은 전이 학습 작업에서 탁월함을 입증했습니다. 이러한 네트워크는 1,000 개의 개별 객체 클래스를 인식하기 위해 풍부하고 차별화된 기능 세트를 학습합니다. 이러한 필터는 CNN 원래 훈련된 이외의 분류 작업에 재사용 있습니다.

 

일반적으로 컴퓨터 비전을위한 딥 러닝에 적용될 때 두 가지 유형의 전이 학습이 있습니다.
1. 네트워크를 임의의 기능 추출기로 취급합니다.
2. 기존 네트워크의 완전히 연결된 계층을 제거하고 CNN 위에 새 Fully Connected 계층 세트를 배치하고 이러한 가중치 (및 선택적으로 이전 계층)를 미세 조정하여 객체 클래스를 인식합니다.

이 장에서는 네트워크를 특징 추출기로 취급하는 첫 번째 전이 학습 방법에 주로 초점을 맞출 것입니다. 그런 다음 5 장에서 특정 분류 작업에 대한 네트워크의 가중치를 미세 조정하는 방법에 대해 설명합니다.

Extracting Features with a Pre-trained CNN

지금까지 우리는 Convolutional Neural Networks를 이미지 분류기로 취급했습니다.
1. 네트워크에 이미지를 입력합니다.
2. 이미지가 네트워크를 통해 전달됩니다.
3. 우리는 네트워크 끝에서 최종 분류 확률을 얻습니다.

그러나 이미지가 전체 네트워크를 통해 전파되도록 해야한다는 "규칙"은 없습니다. 대신 활성화 또는 풀링 레이어와 같은 임의의 레이어에서 전파를 중지하고 이때 네트워크에서 값을 추출한 다음 이를 특징 벡터로 사용할 수 있습니다. 예를 들어 Simonyan과 Zisserman의 VGG16 네트워크 아키텍처를 살펴 보겠습니다.

왼쪽 : 1,000 개의 ImageNet 클래스 레이블 각각에 대한 확률을 출력하는 원래 VGG16 네트워크 아키텍처. 오른쪽 : VGG16에서 FC 레이어를 제거하고 대신 최종 POOL 레이어의 출력을 반환합니다. 이 출력은 추출 된 기능으로 사용됩니다.

 

네트워크의 레이어와 함께 각 레이어에 대한 볼륨의 입력 및 출력 모양도 포함했습니다. 네트워크를 특징 추출기로 취급 할 때, 우리는 본질적으로 임의의 지점 (일반적으로 완전히 연결된 계층 이전에 있지만 실제로는 특정 데이터 세트에 따라 다름)에서 네트워크를 "잘라냅니다".

 

이제 우리 네트워크의 마지막 계층은 최대 풀링 계층 (그림의 오른쪽)으로, 7x7x512의 출력 모양을 가지므로 각각 크기가 7x7 인 512 개의 필터가 있음을 의미합니다. FC 헤드가 제거 된 상태에서이 네트워크를 통해 이미지를 전달하려면 이미지 콘텐츠를 기반으로 활성화되거나 활성화되지 않은 512, 7x7 활성화가 남게됩니다. 따라서 실제로이 7x7x512 = 25,088 값을 가져 와서 이미지의 내용을 정량화하는 특징 벡터로 취급 할 수 있습니다.

전체 이미지 데이터 세트 (VGG16이 학습되지 않은 데이터 세트 포함)에 대해이 프로세스를 반복하면 N 개의 이미지로 구성된 디자인 매트릭스가 남게됩니다. 각 이미지에는 콘텐츠를 정량화하는 데 사용되는 25,088 개의 열이 있습니다 (예 : 특징 벡터) . 특징 벡터가 주어지면 선형 SVM, 로지스틱 회귀 분류기 또는 랜덤 포레스트와 같은 기성 기계 학습 모델을 이러한 기능 위에 훈련시켜 새로운 이미지 클래스를 인식하는 분류기를 얻을 수 있습니다.

CNN 자체는 이러한 새로운 클래스를 인식 할 수 없습니다. 대신 CNN을 중간 기능 추출기로 사용하고 있습니다. 다운 스트림 머신 러닝 분류기는 CNN에서 추출 된 기능의 기본 패턴을 학습합니다.

사전 훈련 된 CNN (특히 VGG16) 및 Keras 라이브러리를 사용하여 Animals, CALTECH-101 및 Flowers-17과 같은 이미지 데이터 세트에서 95 % 이상의 분류 정확도를 얻을 수 있습니다. 이러한 데이터 세트에는 VGG16이 훈련 된 이미지가 포함되어 있지 않지만 전이 학습을 적용하여 적은 노력으로 매우 정확한 이미지 분류기를 구축 할 수 있습니다. 비결은 이러한 기능을 추출하여 효율적인 방식으로 저장하는 것입니다. 이 작업을 수행하려면 HDF5가 필요합니다.

 

What Is HDF5?

HDF5는 HDF5 그룹에서 만든 이진 데이터 형식으로, 데이터 집합 행에 대한 쉬운 액세스 및 계산을 용이하게하면서 디스크에 거대한 숫자 데이터 집합 (메모리에 저장하기에 너무 큼)을 저장합니다. HDF5의 데이터는 파일 시스템이 데이터를 저장하는 방식과 유사하게 계층 적으로 저장됩니다. 데이터는 먼저 그룹으로 정의되며, 그룹은 데이터 세트 및 기타 그룹을 보유 할 수있는 컨테이너와 같은 구조입니다. 그룹이 정의되면 그룹 내에서 데이터 세트를 만들 수 있습니다. 데이터 세트는 동종 데이터 유형 (정수, 플로트, 유니 코드 등)의 다차원 배열 (즉, NumPy 배열)로 생각할 수 있습니다. 

 

HDF5는 C언어로 작성되었습니다. 그러나 h5py 모듈 (h5py.org)을 사용하면 Python 프로그래밍 언어를 사용하여 기본 C API에 액세스 할 수 있습니다. h5py를 매우 멋지게 만드는 것은 데이터와의 상호 작용이 쉽다는 것입니다. HDF5 데이터 세트에 엄청난 양의 데이터를 저장하고 NumPy와 같은 방식으로 데이터를 조작 할 수 있습니다. 예를 들어 표준 Python 구문을 사용하여 메모리에 로드 된 단순한 NumPy 배열 인 것처럼 디스크에 저장된 멀티 테라 바이트 데이터 세트의 행에 액세스하고 분할 할 수 있습니다. 특수 데이터 구조 덕분에 이러한 슬라이스 및 행 액세스가 빠릅니다. h5py와 함께 HDF5를 사용하는 경우 데이터를 주 메모리에 맞추기에는 너무 크지만 여전히 똑같이 액세스하고 조작할 수 있는 거대한 NumPy 배열로 생각할 수 있습니다.

 

무엇보다도 HDF5 형식이 표준화되어 HDF5 형식으로 저장된 데이터 세트는 본질적으로 이식 가능하며 C, MATLAB 및 Java와 같은 다른 프로그래밍 언어를 사용하는 다른 개발자가 액세스 할 수 있습니다.

 

입력 데이터를 효율적으로 받아들이고 HDF5 데이터 세트에 쓸 수 있는 사용자 지정 Python 클래스를 작성합니다. 이 클래스는 두 가지 용도로 사용됩니다.

 

1. VGG16에서 추출한 기능을 효율적으로 HDF5 데이터 세트에 작성하여 전이 학습을 적용하는 방법을 촉진합니다.

2. 빠른 훈련을 용이하게하기 위해 원래 이미지에서 HDF5  데이터 세트를 생성 있습니다.

 

3개의 데이터 세트가있는 HDF5 파일의 예. 첫 번째 데이터 세트에는 CALTECH-101의 label_names가 포함되어 있습니다. 그런 다음 각 이미지를 해당 클래스 레이블에 매핑하는 레이블이 있습니다. 마지막으로 특징 데이터 세트에는 CNN에서 추출한 이미지 정량화가 포함됩니다.

 

Data Augmentation 적용해보겠습니다. Data Augmentation 어떻게 Overfitting 방지하면서 분류 정확도를 높일 있는지 확인해보겠습니다.  새 파일을 열고 이름을 minivggnet_flowers17_data_aug.py 지정한 다음 작업을 시작해 보겠습니다.

 

필요한 라이브러리를 로드합니다.

from sklearn.preprocessing import LabelBinarizer
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from libs.nn.conv.minivggnet import MiniVGGNet
from preprocessing.aspectawarepreprocessor import AspectAwarePreprocessor
from preprocessing.imagetoarraypreprocessor import ImageToArrayPreprocessor
from preprocessing.simpledatasetloader import SimpleDatasetLoader
from keras.preprocessing.image import ImageDataGenerator
from keras.optimizers import SGD
import matplotlib.pyplot as plt
import argparse
from imutils import paths
import numpy as np
import os

 

필요한 argument를 세팅합니다.

다음으로 명령 인수를 기준으로 이미지 경로에서 클래스 이름을 추출해 보겠습니다.

# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-d", "--dataset", required=True, help="path to input dataset")
args = vars(ap.parse_args())

# grab the list of images that we'll be describing, then extract
# the class label names from the image paths
print("[INFO] loading images...")
imagePaths = list(paths.list_images(args["dataset"]))
classNames = [pt.split(os.path.sep)[-2] for pt in imagePaths]
classNames = [str(x) for x in np.unique(classNames)]

 

디스크에서 데이터 세트를로드하고, 훈련 / 테스트 분할을 구성하고, 레이블을 인코딩합니다.

# initialize the image preprocessors
aap = AspectAwarePreprocessor(64, 64)
iap = ImageToArrayPreprocessor()

# load the dataset from disk then scale the raw pixel intensities
# to the range [0, 1]
sdl = SimpleDatasetLoader(preprocessors=[aap, iap])
(data, labels) = sdl.load(imagePaths, verbose=500)
data = data.astype("float") / 255.0

# partition the data into training and testing splits using 75% of
# the data for training and the remaining 25% for testing
(trainX, testX, trainY, testY) = train_test_split(
    data, labels, test_size=0.25, random_state=42
)

# convert the labels from integers to vectors
trainY = LabelBinarizer().fit_transform(trainY)
testY = LabelBinarizer().fit_transform(testY)

 

다음 코드 블록은 ImageDataGenerator 초기화하므로 매우 중요합니다.

aug = ImageDataGenerator(
    rotation_range=30,
    width_shift_range=0.1,
    height_shift_range=0.1,
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True,
    fill_mode="nearest",
)

 

여기에서 이미지는 다음과 같습니다.

 

1. 무작위로 ± 30도 회전

2. 0.1의 비율로 수평 및 수직 이동

3. 0.2만큼 전단

4. [0.8, 1.2] 범위에서 균일하게 샘플링하여 확대

5. 무작위로 수평으로 플리핑 정확한 데이터 세트에 따라 이러한 데이터 증가 값을 조정하고 싶을 것입니다.

응용 프로그램에 따라 [10,30] 사이의 회전 범위를 보는 것이 일반적입니다. 수평 및 수직 이동은 일반적으로 [0.1, 0.2] 범위에 속합니다 (확대 / 축소 값도 동일). 이미지를 수평으로 훑어 보는 것이 클래스 레이블을 변경하지 않는 한, 항상 수평 훑어보기도 포함해야합니다.

 

# initialize the optimizer and model
print("[INFO] compiling model...")
opt = SGD(lr=0.05)
model = MiniVGGNet.build(width=64, height=64, depth=3, classes=len(classNames))
model.compile(loss="categorical_crossentropy", optimizer=opt, metrics=["accuracy"])

 

그러나 네트워크를 훈련시키는 사용되는 코드는 현재 이미지 생성기를 사용하고 있으므로 약간 변경해야합니다.

# train the network
print("[INFO] training network...")
H = model.fit(
    aug.flow(trainX, trainY, batch_size=32),
    validation_data=(testX, testY),
    steps_per_epoch=len(trainX) // 32,
    epochs=100,
    verbose=1,
)

모델의 .fit 메서드를 호출하는 합니다. 이제 첫 번째 매개 변수는 학습 데이터에서 새로운 학습 샘플을 생성하는 데 사용되는 데이터 증가 함수 인 aug.flow입니다. aug.flow에서는 학습 데이터와 해당 레이블을 전달해야합니다. 또한 생성기가 네트워크를 훈련 할 때 적절한 배치를 구성 할 수 있도록 배치 크기를 제공해야합니다.

 

그런 다음 validation_data를 (testX, testY)의 튜플로 제공합니다.이 데이터는 모든 generation이 끝날 때까지 유효성 검사에 사용됩니다. steps_per_epoch 매개 변수는 epoch 당 배치 수를 제어합니다. 총 훈련 샘플 수를 배치 크기로 나누고 정수로 변환하여 적절한 steps_per_epoch 값을 프로그래밍 방식으로 결정할 수 있습니다. 마지막으로 epochs는 네트워크가 훈련되어야하는 총 epoch 수를 제어합니다 (이 경우 100 epoch).

 

네트워크를 훈련 평가하고 해당 정확도 / 손실 플롯을 플로팅합니다.

# evaluate the network
print("[INFO] evaluating network...")
predictions = model.predict(testX, batch_size=32)
print(
    classification_report(
        testY.argmax(axis=1), predictions.argmax(axis=1), target_names=classNames
    )
)

# plot the training loss and accuracy
plt.style.use("ggplot")
plt.figure()
plt.plot(np.arange(0, 100), H.history["loss"], label="train_loss")
plt.plot(np.arange(0, 100), H.history["val_loss"], label="val_loss")
plt.plot(np.arange(0, 100), H.history["accuracy"], label="train_acc")
plt.plot(np.arange(0, 100), H.history["val_accuracy"], label="val_acc")
plt.title("Training Loss and Accuracy")
plt.xlabel("Epoch #")
plt.ylabel("Loss/Accuracy")
plt.legend()
plt.show()

 

위코드는 아래 명령어로 실행시킬 수 있습니다.

 python minivggnet_flowers17_data_aug.py --dataset dataset/flowers17/images/

 

Data Augmentation을 통한 Flower-17 학습과정 및 loss&accuracy plot

Data Augmentation는 훈련 데이터에서 사용하는 정규화 기술의 한 유형입니다. 이름에서 알 수 있듯이 Data Augmentation은 일련의 무작위 변환, 회전, 전단 등을 적용하여 훈련 데이터를 무작위로 생성합니다. 이러한 단순 변환을 적용해도 입력 이미지의 클래스 레이블은 변경되지 않습니다. 그러나 각 증강 이미지는 학습 알고리즘이 이전에 보지 못한 "새로운"이미지로 간주 될 수 있습니다. 따라서 우리의 훈련 알고리즘은 새로운 훈련 샘플과 함께 지속적으로 제공되어 더 강력하고 차별적인 패턴을 학습 할 수 있습니다.

 

"자연스러운"교육 샘플을 수집하는 것이 항상 더 좋지만, 데이터 확대는 작은 데이터 세트 제한을 극복하는 데 사용할 수 있습니다. 자체 훈련의 경우 실행하는 거의 모든 실험에 데이터 증가를 적용해야합니다. CPU가 이제 입력을 무작위로 변환하는 책임이 있기 때문에 약간의 성능 저하를 가져야합니다. 그러나 이러한 성능 저하는 스레드를 사용하고 네트워크 훈련을 담당하는 스레드로 전달되기 전에 백그라운드에서 데이터를 보강하여 완화됩니다.

 

Data_Augmenation.tar.gz
0.44MB

'keras > 1.Data Augmentation' 카테고리의 다른 글

2. Flower-17  (0) 2020.08.20
1. Data Augmentation  (0) 2020.08.20

Flowers-17 데이터 세트

17 개의 서로 다른 꽃의 품종을 인식하는 세분화 된 분류 과제입니다. 이미지 데이터 세트는 매우 작으며 총 1,360개의 이미지에 대해 클래스 당 80개의 이미지 만 있습니다. 컴퓨터 비전 작업에 딥러닝을 적용 할 때 일반적인 경우는 클래스당 1,000 ~ 5,000개의 데이터를 갖는 것이므로 여기서는 확실히 큰 부족함이 있습니다.

모든 범주가 매우 유사하기 때문에 Flowers-17을 세분화 된 분류 작업이라고 부릅니다 (즉, 꽃의 품종). 사실, 우리는 이러한 각 범주를 하위 범주로 생각할 수 있습니다. 범주는 확실히 다르지만 상당한 양의 공통 구조를 공유합니다.

Flowers-17 데이터 세트의 5 개 (총 17 개 중) 클래스 샘플. 각 클래스는 특정 꽃의 종을 나타냅니다. (pyimagesearch)

세분화 된 분류 작업은 우리의 기계 학습 모델이 매우 유사한 클래스를 구별하기 위해 극도로 구별되는 기능을 학습해야 함을 의미하기 때문에 딥러닝 실무자에게 가장 어려운 경향이 있습니다. 이 세분화된 분류 작업은 제한된 훈련 데이터를 고려할 때 더욱 문제가됩니다.

 

Aspect-aware Preprocessing

지금까지는 영상비를 무시하고 고정 된 크기로 크기를 조정하여 이미지를 전처리했습니다. 일부 상황, 특히 기본 벤치 마크 데이터 세트의 경우 그렇게하는 것이 허용됩니다.

그러나 더 까다로운 데이터 세트의 경우 고정된 크기로 크기를 조정해야하지만 종횡비는 유지해야합니다. 

왼쪽 : 원본 입력 이미지 (410 × 310). 중간 : 가로 세로 비율을 무시하고 이미지 크기를 256 × 256 픽셀로 조정합니다. 이제 이미지가 찌그러지고 왜곡 된 것처럼 보입니다. 오른쪽 : 가로 세로 비율을 유지하면서 이미지 크기를 256 × 256으로 조정합니다.

왼쪽에는 고정 된 너비와 높이로 크기를 조정해야하는 입력 이미지가 있습니다. 가로 세로 비율을 무시하고 이미지 크기를 256 × 256 픽셀 (중간)로 조정하여 원하는 크기에 맞도록 이미지를 효과적으로 찌그러 뜨리고 왜곡합니다. 더 나은 접근 방식은 이미지의 종횡비 (오른쪽)를 고려하는 것입니다. 여기서 너비가 256 픽셀이되도록 더 짧은 치수를 따라 크기를 조정 한 다음 높이를 따라 이미지를 잘라 높이가 256 픽셀이되도록합니다.

자르는 동안 이미지의 일부를 효과적으로 버렸지 만 이미지의 원래 종횡비도 유지했습니다. 일관된 종횡비를 유지하면 Convolutional Neural Network가보다 차별적이고 일관된 기능을 학습 할 수 있습니다. 

aspect-aware 전처리가 어떻게 구현되는지 확인하기 위해 AspectAwarePreprocessor를 만들어보겠습니다.

생성자는 이미지 크기를 조정할 때 사용되는 보간 방법과 함께 두 개의 매개 변수 (대상 출력 이미지의 원하는 너비 및 높이)가 필요합니다. 

# import the necessary packages 
import imutils 
import cv2

class AspectAwarePreprocessor:
    def __init__(self, width, height, inter=cv2.INTER_AREA):
        # store the target image width, height, and interpolation
        # method used when resizing 
        self.width = width 
        self.height = height 
        self.inter = inter

 

그런 다음 아래에서 전처리 기능을 정의 할 수 있습니다.

    def preprocess(self, image):
        # grab the dimensions of the image and then initialize 
        # the deltas to use when cropping
        (h, w) = image.shape[:2] # 입력 이미지의 너비와 높이를 잡고

        # 더 큰 치수를 따라자를 때 사용할 델타 오프셋을 결정
        dW = 0
        dH = 0

즉, AspectAwarePreprocessor()는 2단계 알고리즘입니다.

1. Step # 1 : 가장 짧은 치수를 결정하고 이에 따라 크기를 조정합니다.
2. Step # 2 : 대상 너비와 높이를 얻기 위해 가장 큰 치수를 따라 이미지를 자릅니다.

다음 코드 블록은 너비가 높이보다 작은 지 확인하고, 그렇다면 너비를 따라 크기를 조정합니다.

        # if the width is smaller than the height, then resize 
        # along the width (i.e., the smaller dimension) and then 
        # update the deltas to crop the height to the desired # dimension        
        if w < h:
            image = imutils.resize(image, width=self.width, inter=self.inter)
            dH = int((image.shape[0] - self.height) / 2.0)

 

그렇지 않고 높이가 너비보다 작 으면 높이를 따라 크기를 조정합니다.

        else:
            image = imutils.resize(image, height=self.height, inter=self.inter)
            dW = int((image.shape[1] - self.width) / 2.0)

 

이제 이미지의 크기가 조정되었으므로 너비와 높이를 다시 잡고 델타를 사용하여 이미지 중앙을 잘라야합니다.

        # now that our images have been resized, we need to 
        # re-grab the width and height, followed by performing 
        # the crop
        (h, w) = image.shape[:2]
        image = image[dH:h - dH, dW:w - dW]

        # finally, resize the image to the provided spatial 
        # dimensions to ensure our output image is always a fixed 
        # size
        
        return cv2.resize(image, (self.width, self.height), interpolation=self.inter)

잘라낼 (반올림 오류로 인해) 이미지 대상 이미지 크기가 ± 1 픽셀만큼 벗어날 있습니다. 따라서 출력 이미지가 원하는 너비와 높이를 갖도록 cv2.resize 호출합니다. 그런 다음 전처리 이미지가 호출 함수로 반환됩니다. AspectAwarePreprocessor() 구현 했으므로 이제 Flowers-17 데이터 세트에서 MiniVGGNet 아키텍처를 학습 작동하도록하겠습니다.

 

aspectawarepreprocessor.py
0.00MB

 

 

'keras > 1.Data Augmentation' 카테고리의 다른 글

3. Flowers-17: With Data Augmentation  (0) 2020.08.21
1. Data Augmentation  (0) 2020.08.20

Goodfellow et al.에 따르면, 정규화는“일반화 오류를 줄이기위한 학습 알고리즘에 대한 모든 수정이지만 훈련 오류는 아닙니다" 다시 말해정규화는 훈련 오류를 약간 증가시키는 대신 테스트 오류를 ​​줄이려는 목적이 강합니다.

실무적으로는 다음과 같은 다른 유형의 정규화가 있습니다.

  1. 네트워크 아키텍처 자체를 수정합니다.
  2. 훈련을 위해 네트워크로 전달되는 데이터를 확장합니다.

 

드롭 아웃은 일반화 가능성을 높여 네트워크 아키텍처를 수정하는 좋은 예입니다. 여기에 이전 계층에서 다음 계층으로 노드를 무작위로 연결 해제하는 계층을 삽입하여 단일 노드가 주어진 클래스를 표현하는 방법을 모르게 합니다.

 

What is Data Augmentation?

 

Data Augmentation은 클래스 레이블이 변경되지 않도록 임의의 jitter 및 perturbation을 적용하여 원본에서 새로운 훈련 샘플을 생성하는 데 사용되는 광범위한 기술을 말합니다. Data Augmentation를 적용 할 때 우리의 목표는 모델의 일반화 가능성을 높이는 것입니다. 네트워크가 지속적으로 약간 수정 된 새 버전의 입력 데이터 포인트를 보고 있다는 점은 강력한 더 파워풀한 모델을 얻을 수 있음을 암시합니다. 테스트시에는 데이터 증대를 적용하지 않고 훈련 된 네트워크를 평가하지 않습니다. 대부분의 경우 훈련 정확도가 약간 떨어지더라도 테스트 정확도가 향상되는 것을 확인할 수 있습니다.

왼쪽 : 정확히 정규 분포를 따르는 250 개의 데이터 포인트 샘플. 오른쪽 : 소량의 무작위 "jitter"를 분포에 추가 이러한 유형의 데이터 증가는 네트워크의 일반화 가능성을 높일 수 있습니다. (pyimagesearch)

평균이 0이고 단위 분산이있는 정규 분포의 그림(왼쪽)을 생각해보겠습니다. 이 데이터에 대한 기계 학습 모델을 학습하면 분포를 정확하게 모델링 할 수 있지만 실제 애플리케이션에서는 데이터가 이러한 깔끔한 분포를 거의 따르지 않습니다.

대신 분류기의 일반화 가능성을 높이기 위해 무작위 분포 (오른쪽)에서 가져온 일부 값 ε을 추가하여 분포를 따라 무작위로 jitter 지점을 찾을 수 있습니다. 컴퓨터 비전에서 무작위와 같은 간단한 기하학적 변환을 적용하여 원본 이미지에서 추가 훈련 데이터를 얻을 수 있습니다.

 

1. Translations
2. Rotations
3. Changes in scale
4. Shearing
5. Horizontal (and in some cases, vertical) flips

 

이러한 변환의 (작은) 양을 입력 이미지에 적용하면 모양이 약간 변경되지만 클래스 레이블은 변경되지 않으므로 데이터 확장이 매우 자연스럽고 쉬운 방법이됩니다. 이런 방법은 컴퓨터 비전 작업을위한 딥 러닝에 적용합니다. 컴퓨터 비전에 적용되는 데이터 증대를위한보다 진보 된 기술에는 주어진 색 공간에서 색상의 randomized perturbation, nonlinear geometric distortions이 있습니다.

 

Visualizing Data Augmentation

# import the necessary packages
from keras.preprocessing.image import ImageDataGenerator
from keras.preprocessing.image import img_to_array
from keras.preprocessing.image import load_img
from imutils import paths
import argparse
import numpy as np

 

argparse() 모듈은 terminal에서 실행할 때 argument를 쉽게 parsing하는 모듈입니다. 일반적인 tool(ex. Pycharm, jupyter notebook)에서 직접적으로 실행하게 되면 error가 발생하게 됩니다.

# construct the argument parse ad parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True)
ap.add_argument("-o", "--output", required=True)
ap.add_argument("-p", "--prefix", type=str, default="image")
args = vars(ap.parse_args())

 

 

image를 불러오는 코드입니다.

print("[INFO] loading example image...")
image = load_img(args["image"])
image = img_to_array(image)
image = np.expand_dims(image, axis=0)  # 맨 앞 1차원 추가

 

 

애플리케이션에서 가장 많이 사용할 증가 매개 변수에 초점을 맞추겠습니다.
# rotation_range 매개 변수는 임의 회전의 각도 범위를 제어합니다. 여기에서 입력 이미지를 무작위로 ± 30도 회전 할 수 있습니다.
# width_shift_range 및 height_shift_range는 각각 수평 및 수직 이동에 사용됩니다. 매개 변수 값은 주어진 차원의 일부입니다 (이 경우 10 %).
# shear_range는 이미지를 기울일 수있는 라디안으로 시계 반대 방향의 각도를 제어합니다.
# 그런 다음 [1-zoom_range, 1 + zoom_range] 값의 균일 한 분포에 따라 이미지를 "확대" 또는 "축소"할 수 있는 포인트 값인 zoom_range가 있습니다.
# 마지막으로 horizontal_flip 논리값은 주어진 입력이 훈련 프로세스 동안 수평으로 전환될 여부를 체크합니다.
# 대부분의 컴퓨터 비전 응용 프로그램의 경우 이미지의 수평 플랩은 결과 클래스 레이블을 변경하지 않습니다.
# 우리의 목표는 입력 이미지를 약간 수정하여 클래스 레이블 자체를 변경하지 않고 새 학습 샘플을 생성하는 것이므로 이러한 유형의 데이터 증가를 적용 할 때 주의해야 합니다.

aug = ImageDataGenerator(
    rotation_range=30,
    width_shift_range=0.1,
    height_shift_range=0.1,
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True,
    fill_mode="nearest",
)

 

 

생성된 Iterator로 이미지를 생성할 수 있습니다.

print("[INFO] generating images...")
imageGen = aug.flow(
    image,
    batch_size=1,
    save_to_dir=args["output"],
    save_prefix=args["prefix"],
    save_format="jpg",
)

total = 0
# 그런 다음 imageGen 생성기의 각 이미지를 반복하기 시작합니다. 
# 내부적으로 imageGen은 루프를 통해 요청 될 때마다 새로운 학습 샘플을 자동으로 생성합니다.
# 그런 다음 디스크에 기록 된 총 데이터 증가 예제 수를 늘리고 
# 예제 10 개에 도달하면 스크립트 실행을 중지합니다.
# loop over examples from our image data augmentation generator
for image in imageGen:
    # increment our counter
    total += 1

    # if we have reached 10 examples, break from the loop
    if total == 10:
        break

 

 

실행코드는 다음과 같습니다.

python augmentation.py -i jemma.png -o output -p image

데이터를 증폭할 원본 이미지
증폭된 강아지 이미지 사진 10개

각 이미지가 어떻게 무작위로 회전하고, 기울이고, 확대되고, 수평으로 튕겨져 있는지 확인합니다. 각각의 경우 이미지는 원래 클래스 레이블을 유지합니다.(dog); 그러나 각 이미지가 약간 수정되어 훈련 할 때 배울 수있는 새로운 패턴을 신경망에 제공합니다. 입력 이미지는 지속적으로 변경되기 때문에 (클래스 레이블은 동일하게 유지됨) 데이터 보강이없는 학습과 비교할 때 학습 정확도가 감소하는 것이 일반적입니다.

또한 Data Augmentation는 Overfitting을 줄이는 데 도움이 될 수 있으며, 동시에 모델이 새로운 입력 샘플에 더 잘 일반화되도록 보장합니다. 또한 딥러닝을 적용하기에는 예제가 너무 적은 데이터 세트로 작업 할 때 Data Augmentation을 활용하여 추가 훈련 데이터를 생성 할 수 있으므로 딥러닝 네트워크를 훈련하는 데 필요한 수동 레이블 데이터의 양을 줄일 수 있습니다.

augmentation.py
0.00MB
jemma.png
0.26MB

'keras > 1.Data Augmentation' 카테고리의 다른 글

3. Flowers-17: With Data Augmentation  (0) 2020.08.21
2. Flower-17  (0) 2020.08.20

+ Recent posts