"Keras:데이터 전처리"의 두 판 사이의 차이

Pywiki
둘러보기로 가기 검색하러 가기
82번째 줄: 82번째 줄:
  
 
= 데이터 제너레이터 =
 
= 데이터 제너레이터 =
데이터의 크기가 커지면 이게 RAM에 다 안올라가서 에러가 뜬다. 때문에 제너레이털로 데이터를 입력해주는 편이 좋다.
+
데이터의 크기가 커지면 이게 RAM에 다 안올라가서 에러가 뜬다.(시계열 데이터는 window의 수많큼 데이터가 커지고, image같은 것들은 하드에 저장했던걸 불러오는데, 이걸 RAM에 올리는 건..힘들다.)
 +
 
 +
때문에 제너레이터로 데이터를 입력해주는 편이 제한된 RAM을 잘 활용할 수 있다.
  
 
=== 유의 ===
 
=== 유의 ===
제너레이터는 다 쓰면 학습이 이루어지지 않기에, 다 쓰게 되면 다시 처음으로 loopback 해주는 기능을 함께 넣는 편이 좋다.
+
 
 +
* 제너레이터는 다 쓰면 학습이 이루어지지 않기에, 다 쓰게 되면 다시 처음으로 loopback 해주는 기능을 함께 넣는 편이 좋다.
 +
* 사용자가 처음부터 임의로 짠 제너레이터는... 끝지점을 설정하기도 어렵고, 기타 세부사항을 놓치는 경우가 많다.
 +
 
 +
- 최근엔 케라스에서 제너레이터의 기본 틀을 제공한다. tf.keras.utils.Sequence를 상속하는 클레스를 만들어주는 것으로부터 시작한다.(멀티프로세싱이나 이런저런 세부사항을 지키기에 안전하기에 권장된다.)
 +
 
 +
== 케라스 제공 클래스로 만들기 ==
 +
쓰레드로 진행되어 CPU나 GPU에 바로 먹여진다.
 +
 
 +
=== 필수 메서드 ===
 +
__getitem__과 __len__ 메서드가 필수적으로 정의되어야 한다. 아, __init__은 클래서에선 기본이고.
 +
{| class="wikitable"
 +
!메서드
 +
!설명
 +
!코드예시
 +
|-
 +
|<code>__init__</code>
 +
|객체가 처음 만들어질 때 변수를 어떻게 할당할지.
 +
|<syntaxhighlight lang="python">
 +
class CIFAR10Sequence(Sequence):
 +
 
 +
    def __init__(self, x_set, y_set, batch_size):
 +
        self.x, self.y = x_set, y_set
 +
        self.batch_size = batch_size
 +
</syntaxhighlight>
 +
|-
 +
|<code>__getitem__</code>
 +
|배치 하나의 데이터를 뱉어낼 수 있어야 한다.
 +
x, y 형태로.
 +
 
 +
x는 넘파이 배열 (배치사이즈, 인풋) 형태로,
 +
 
 +
y는 2개의 배열이 합쳐진 튜플로.(예시를 보니 그냥 레이블을 뱉어내면 되는듯.)
 +
|<syntaxhighlight lang="python">
 +
def __getitem__(self, idx):
 +
        batch_x = self.x[idx * self.batch_size:(idx + 1) *
 +
        self.batch_size]
 +
        batch_y = self.y[idx * self.batch_size:(idx + 1) *
 +
        self.batch_size]
 +
 
 +
        return np.array([
 +
            resize(imread(file_name), (200, 200))
 +
              for file_name in batch_x]), np.array(batch_y)
 +
</syntaxhighlight>
 +
|-
 +
|<code>__len__</code>
 +
|제너레이터의 길이를 반환한다.
 +
일반적으로 총 길이를 배치사이즈로 나눈 후 숫자 올림 한 것.
 +
 
 +
항상 int 타입을 반환해야 한다.
 +
|<syntaxhighlight lang="python">
 +
def __len__(self):
 +
        return math.ceil(len(self.x) / self.batch_size)
 +
</syntaxhighlight>
 +
|}
 +
 
 +
=== 정의된 메서드 ===
 +
{| class="wikitable"
 +
!메서드
 +
!설명
 +
!코드예시
 +
|-
 +
|on_epoch_end
 +
|각 에폭의 끝에서 수행할 일을 지정한다. 데이터셋을 바꾼다든가...
 +
|<syntaxhighlight lang="python">
 +
def on_epoch_end(self):
 +
    if self.shuffle:
 +
        self.df = self.df.sample(frac=1).reset_index(drop=True)
 +
</syntaxhighlight>
 +
|}
 +
 
 +
=== 사용 ===
 +
x, y = generator[133] 형태로 사용할 수 있다. 확인에도 직관적이어서 좋다.
 +
 
 
[[분류:Keras]]
 
[[분류:Keras]]
 
[[분류:데이터 전처리]]
 
[[분류:데이터 전처리]]

2022년 2월 7일 (월) 17:12 판

1 개요

케라스 자체적으로도 데이터 전처리를 제공한다.

2 전처리

의도 설명 방법
정규화 평균 0, 표준편차 1로 정규화한다.
from tensorflow.keras.layers import Normalization

training_data = np.random.randint(0, 256, size=(64, 200, 200, 3)).astype("float32")  # 예시데이터

normalizer = Normalization(axis=-1)     # 정규화 객체 생성.
normalizer.adapt(training_data)         # 정규화 방법 지정.
normalized_data = normalizer(training_data) # 데이터에 정규화 진행.
이미지 전처리 이미지를 처리할 땐 이미지를 자르거나 수치의 제한을 설정하는 과정이 필요할 때가 있다.


케라스에선 이미지처리를 위한 도구가 있다.

from tensorflow.keras.layers import CenterCrop
from tensorflow.keras.layers import Rescaling

# Example image data, with values in the [0, 255] range
training_data = np.random.randint(0, 256, size=(64, 200, 200, 3)).astype("float32")

cropper = CenterCrop(height=150, width=150)
scaler = Rescaling(scale=1.0 / 255)

output_data = scaler(cropper(training_data))
print("shape:", output_data.shape)  # 64개의 150,150 크기의 3채널 데이터임을 알려준다.
print("min:", np.min(output_data))  # 가장 작은 데이터는 0
print("max:", np.max(output_data))  # 가장 큰 데이터는 1
이미지 사진파일을 읽고, 컨텐츠를 RGB로 디코딩, 텐서로 변환, 픽셀 정규화를 한번에 해주는 도구가 있다.
from keras.preprocessing.image import ImageDataGenerator

# 모든 이미지를 1/255로 스케일을 조정합니다
train_datagen = ImageDataGenerator(rescale=1./255)
test_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(
        train_dir,  # 타깃 디렉터리
        target_size=(150, 150),  # 모든 이미지를 150 × 150 크기로 바꿉니다
        batch_size=20,
        class_mode='binary')  # binary_crossentropy 손실을 사용할 땐 이진 레이블.

validation_generator = test_datagen.flow_from_directory(
        validation_dir,
        target_size=(150, 150),
        batch_size=20,
        class_mode='binary')
이미지 증식 이미지의 학습엔 많은 이미지가 필요한데, 천당 단위의 수준으론 너무 적다. 때문에 회전, 반전 등을 통해 이미지를 증폭한다.

(나중에 이용할 때 제대로 찾아 정리하자...)


근본적으로 원본 이미지에서 달라진 게 아니기 때문에 과적합의 문제를 피해갈 수는 없다.

datagen = ImageDataGenerator(
      rotation_range=40,        # 회전
      width_shift_range=0.2,    # 가로이동
      height_shift_range=0.2,   # 세로이동
      shear_range=0.2,          # 전단변환
      zoom_range=0.2,           # 확대
      horizontal_flip=True,     # 좌우반전
      fill_mode='nearest')      # 빈 공간을 어떻게 채울지.(nearest는 주변픽셀로 채운다.)

3 데이터 제너레이터

데이터의 크기가 커지면 이게 RAM에 다 안올라가서 에러가 뜬다.(시계열 데이터는 window의 수많큼 데이터가 커지고, image같은 것들은 하드에 저장했던걸 불러오는데, 이걸 RAM에 올리는 건..힘들다.)

때문에 제너레이터로 데이터를 입력해주는 편이 제한된 RAM을 잘 활용할 수 있다.

3.1 유의

  • 제너레이터는 다 쓰면 학습이 이루어지지 않기에, 다 쓰게 되면 다시 처음으로 loopback 해주는 기능을 함께 넣는 편이 좋다.
  • 사용자가 처음부터 임의로 짠 제너레이터는... 끝지점을 설정하기도 어렵고, 기타 세부사항을 놓치는 경우가 많다.

- 최근엔 케라스에서 제너레이터의 기본 틀을 제공한다. tf.keras.utils.Sequence를 상속하는 클레스를 만들어주는 것으로부터 시작한다.(멀티프로세싱이나 이런저런 세부사항을 지키기에 안전하기에 권장된다.)

3.2 케라스 제공 클래스로 만들기

쓰레드로 진행되어 CPU나 GPU에 바로 먹여진다.

3.2.1 필수 메서드

__getitem__과 __len__ 메서드가 필수적으로 정의되어야 한다. 아, __init__은 클래서에선 기본이고.

메서드 설명 코드예시
__init__ 객체가 처음 만들어질 때 변수를 어떻게 할당할지.
class CIFAR10Sequence(Sequence):

    def __init__(self, x_set, y_set, batch_size):
        self.x, self.y = x_set, y_set
        self.batch_size = batch_size
__getitem__ 배치 하나의 데이터를 뱉어낼 수 있어야 한다.

x, y 형태로.

x는 넘파이 배열 (배치사이즈, 인풋) 형태로,

y는 2개의 배열이 합쳐진 튜플로.(예시를 보니 그냥 레이블을 뱉어내면 되는듯.)

def __getitem__(self, idx):
        batch_x = self.x[idx * self.batch_size:(idx + 1) *
        self.batch_size]
        batch_y = self.y[idx * self.batch_size:(idx + 1) *
        self.batch_size]

        return np.array([
            resize(imread(file_name), (200, 200))
               for file_name in batch_x]), np.array(batch_y)
__len__ 제너레이터의 길이를 반환한다.

일반적으로 총 길이를 배치사이즈로 나눈 후 숫자 올림 한 것.

항상 int 타입을 반환해야 한다.

def __len__(self):
        return math.ceil(len(self.x) / self.batch_size)

3.2.2 정의된 메서드

메서드 설명 코드예시
on_epoch_end 각 에폭의 끝에서 수행할 일을 지정한다. 데이터셋을 바꾼다든가...
def on_epoch_end(self):
    if self.shuffle:
        self.df = self.df.sample(frac=1).reset_index(drop=True)

3.2.3 사용

x, y = generator[133] 형태로 사용할 수 있다. 확인에도 직관적이어서 좋다.