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에서 추출한 이미지 정량화가 포함됩니다.

 

+ Recent posts