2022년 8월 5일 금요일

tensorflow keras / train / Regression 회귀

 

이 글은 pytorch 로 구현한 아래 내용을 tensorflow keras 로 구현한 내용입니다.

https://swlock.blogspot.com/2022/07/machine-learning-pytorch-train.html


입력 X 를 3개를 입력받고 출력은 1개인 선형 회귀 입니다.


x, y 의 생성

data_count=5000 x1,x2,x3,y 의 데이터를 5000개 생성합니다.

numpy의 random.rand 함수를 이용해서 생성합니다.

np.random.rand(m,n) n*m matrix 로 0~1 랜덤 생성 됩니다.

그 다음으로 궁극적으로 알아내야 하는 미지의 함수입니다.

y = 1 + 2 * x_one_d + .1 * np.random.randn(data_count, 1)

뒤쪽 np.random.randn(data_count, 1)은 y값의 변화를 주기 위한 내용입니다.

결국 y = 1 + 2 * x1 * x2 * x3 라고 생각하면 됩니다.

import numpy as np

####################################### 데이터 준비
data_count = 5000
np.random.seed(42)
# np.random.rand(m,n) n*m matrix 로 0~1 랜덤 생성
x = np.random.rand(data_count, 3)
'''
[[0.37454012 0.95071431 0.73199394]
 [0.59865848 0.15601864 0.15599452]
 [0.05808361 0.86617615 0.60111501]
 ...
'''
x_one_d = x[:,0:1] * x[:,1:2] * x[:,2:3]
'''
[[2.60648878e-01]  # <= [0.37454012 * 0.95071431 * 0.73199394]
 [1.45701819e-02]
 [3.02424805e-02]
 ...
'''

# y는 원하는 값 목표값인 label로 표현합니다.
y = 1 + 2 * x_one_d + .1 * np.random.randn(data_count, 1)
'''
[[1.52585494]
 [0.96398033]
 [1.27487937]
 ...
'''
# 저장전 확인
print(x.shape,type(x),x[0])
print(y.shape,type(y),y[0])

# 저장
np.savetxt("x_data.csv", x, delimiter=',')
np.savetxt("y_data.csv", y, delimiter=',')

# 저장된것 확인
x_data = np.loadtxt("x_data.csv", delimiter=',', skiprows=0, max_rows=1)
print(x_data.shape,type(x_data),x_data)
if x_data.shape==():
    x_data = np.array([x_data]) 
print(x_data.shape,type(x_data),x_data)
y_data = np.loadtxt("y_data.csv", delimiter=',', skiprows=0, max_rows=1)
print(y_data.shape,type(y_data),y_data)
if y_data.shape==():
    y_data = np.array([y_data]) 
print(y_data.shape,type(y_data),y_data)

실행하고 나면 x_data.csv, y_data.csv 파일이 생성됩니다.


file dataloader

파일에서 데이터 로딩하는 부분은 이전 포스팅을 참고 하시기 바랍니다.

https://swlock.blogspot.com/2022/08/tensorflow-keras-sequence-dataloader.html


tensorflow keras Learning basic

여기에서는 아주 간단한 Linear Model 두 개를 연결해서 만들었습니다.


전체 코드

python 3.8 환경, CPU 환경에서 시험하였습니다.


from tensorflow import keras
from tensorflow.keras.utils import Sequence
from tensorflow.keras import layers
import tensorflow as tf
import numpy as np
import math

batch_size = 100
lr = 1e-1
n_epochs = 5
train_data_ratio = 0.8


class CustomDataset:
    def __init__(self, x_tensor_filename, y_tensor_filename, base_idx=None, cnt=None):
        self.x_fn = x_tensor_filename
        self.y_fn = y_tensor_filename
        self.base_idx = base_idx
        self.cnt = cnt
        if self.base_idx is None:
            with open(y_tensor_filename, 'r') as fp:
                for count, line in enumerate(fp):
                    pass
            self.total_len = count + 1
        else:
            self.total_len = cnt

    def __getitem__(self, index):
        """
        주어진 인덱스 index 에 해당하는 샘플을 데이터셋에서 불러오고 반환합니다.
        """
        if self.base_idx is not None:
            index = index + self.base_idx
        x_data = np.loadtxt(self.x_fn, delimiter=',', skiprows=index, max_rows=1)
        # [0.37454012 0.95071431 0.73199394]
        if x_data.shape == ():
            x_data = np.array([x_data])
        y_data = np.loadtxt(self.y_fn, delimiter=',', skiprows=index, max_rows=1)
        # 1.5258549401766552
        if y_data.shape == ():
            y_data = np.array([y_data])
        # [1.5258549401766552]
        return x_data, y_data

    def __len__(self):
        """
        데이터셋의 샘플 개수를 반환합니다.
        """
        return self.total_len


class CustomDataloader(Sequence):
    def __init__(self, _dataset, batch_size=1, shuffle=False):
        self.dataset = _dataset
        self.batch_size = batch_size
        self.total_len = math.ceil(len(self.dataset) / self.batch_size)
        self.shuffle = shuffle
        self.indexer = np.arange(len(self.dataset))
        self.on_epoch_end()

    def __getitem__(self, index):
        indexer = self.indexer[index * self.batch_size:(index + 1) * self.batch_size]
        batch_x = [self.dataset[i][0] for i in indexer]
        batch_y = [self.dataset[i][1] for i in indexer]
        return np.array(batch_x), np.array(batch_y)

    def __len__(self):
        return self.total_len

    def on_epoch_end(self):
        if self.shuffle:
            np.random.shuffle(self.indexer)
            print("shuffle")


temp_dataset = CustomDataset("x_data.csv", "y_data.csv")
train_dataset_cnt = int(len(temp_dataset) * train_data_ratio)
val_dataset_cnt = len(temp_dataset) - train_dataset_cnt
train_dataset = CustomDataset("x_data.csv", "y_data.csv", 0, train_dataset_cnt)
val_dataset = CustomDataset("x_data.csv", "y_data.csv", train_dataset_cnt, val_dataset_cnt)
print(len(temp_dataset), train_dataset_cnt, val_dataset_cnt)
train_loader = CustomDataloader(train_dataset, batch_size=batch_size, shuffle=True)
val_loader = CustomDataloader(val_dataset, batch_size=batch_size, shuffle=True)

inputs = keras.Input(shape=(None, 3))
x = layers.Dense(6, activation="linear")(inputs)
x = layers.Dense(1, activation="linear")(x)
outputs = x
model = keras.Model(inputs, outputs)
model.summary()

# https://keras.io/api/optimizers/
# https://keras.io/api/losses/
INPUT_OPTIMIZER = tf.keras.optimizers.SGD(learning_rate=lr)
LOSS = tf.keras.losses.MeanSquaredError(reduction="auto", name="mean_squared_error")
model.compile(optimizer=INPUT_OPTIMIZER, loss=LOSS)
model.fit(train_loader, batch_size=batch_size, epochs=n_epochs, validation_data=val_loader)



# y = 1 + 2*0.1*0.2*0.1
x_prd = np.array([[0.1, 0.2, 0.1]])
print("expect:", 1 + 2 * 0.1 * 0.2 * 0.1)
print(model.predict(x_prd))



실행 결과

5000 4000 1000
shuffle
shuffle
Model: "model"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 input_1 (InputLayer)        [(None, None, 3)]         0         
                                                                 
 dense (Dense)               (None, None, 6)           24        
                                                                 
 dense_1 (Dense)             (None, None, 1)           7         
                                                                 
=================================================================
Total params: 31
Trainable params: 31
Non-trainable params: 0
_________________________________________________________________
Epoch 1/5
WARNING:tensorflow:Model was constructed with shape (None, None, 3) for input KerasTensor(type_spec=TensorSpec(shape=(None, None, 3), dtype=tf.float32, name='input_1'), name='input_1', description="created by layer 'input_1'"), but it was called on an input with incompatible shape (None, None).
WARNING:tensorflow:Model was constructed with shape (None, None, 3) for input KerasTensor(type_spec=TensorSpec(shape=(None, None, 3), dtype=tf.float32, name='input_1'), name='input_1', description="created by layer 'input_1'"), but it was called on an input with incompatible shape (None, None).
40/40 [==============================] - ETA: 0s - loss: 0.2153WARNING:tensorflow:Model was constructed with shape (None, None, 3) for input KerasTensor(type_spec=TensorSpec(shape=(None, None, 3), dtype=tf.float32, name='input_1'), name='input_1', description="created by layer 'input_1'"), but it was called on an input with incompatible shape (None, None).
shuffle
40/40 [==============================] - 11s 269ms/step - loss: 0.2153 - val_loss: 0.0621
shuffle
Epoch 2/5
40/40 [==============================] - ETA: 0s - loss: 0.0514shuffle
40/40 [==============================] - 10s 260ms/step - loss: 0.0514 - val_loss: 0.0423
shuffle
Epoch 3/5
40/40 [==============================] - ETA: 0s - loss: 0.0393shuffle
40/40 [==============================] - 10s 260ms/step - loss: 0.0393 - val_loss: 0.0365
shuffle
Epoch 4/5
40/40 [==============================] - ETA: 0s - loss: 0.0355shuffle
40/40 [==============================] - 10s 264ms/step - loss: 0.0355 - val_loss: 0.0337
shuffle
Epoch 5/5
40/40 [==============================] - ETA: 0s - loss: 0.0339shuffle
40/40 [==============================] - 10s 255ms/step - loss: 0.0339 - val_loss: 0.0324
shuffle
expect: 1.004
WARNING:tensorflow:Model was constructed with shape (None, None, 3) for input KerasTensor(type_spec=TensorSpec(shape=(None, None, 3), dtype=tf.float32, name='input_1'), name='input_1', description="created by layer 'input_1'"), but it was called on an input with incompatible shape (None, 3).
1/1 [==============================] - 0s 41ms/step
[[0.7554034]]


코드 설명

사용자가 조절해야 하는 parameter 입니다.

batch_size = 100
 한번에 처리하는 batch size 입니다.
 메모리에 한번에 올라가는 양이됩니다. 
 훈련값 갱신도 batch 크기 단위로 이루어 집니다.
lr = 1e-1
 learning rate(훈련 비율) 이 값이 작으면 훈련하는데 시간이 오래걸립니다.
 반대로 크면 훈련이 정상적으로 되지 않고 진동하게 됩니다.
n_epochs = 5
 epoch 라고 하는데 훈련 데이터를 전체적으로 훈련하는것을 1 epoch라고 합니다.
 수치가 높으면 훈련을 여러번하게 되는데 여러번해도 효율이 좋아지지 않는다면 수치를 낮추면 됩니다.
 즉 100으로 하나 5로 하나 차이가 없다면 5로 하면 됩니다.
train_data_ratio = 0.8
 이건 data셋을 훈련용과 test용을 나누는 비율입니다.
 0.8은 data셋 중 80%가 훈련용으로 사용하게 됩니다.

아래 class는 파일로부터 데이터를 읽어오는 코드로 file dataloader아래 링크에서 좀 더 자세한 정보를 확인하시면 됩니다.

https://swlock.blogspot.com/2022/08/tensorflow-keras-sequence-dataloader.html

아래에 있는 다음 코드들도 위 링크 설명 참고하면 됩니다.

temp_dataset = CustomDataset("x_data.csv", "y_data.csv")
train_dataset_cnt = int(len(temp_dataset) * train_data_ratio)
val_dataset_cnt = len(temp_dataset) - train_dataset_cnt
train_dataset = CustomDataset("x_data.csv", "y_data.csv", 0, train_dataset_cnt)
val_dataset = CustomDataset("x_data.csv", "y_data.csv", train_dataset_cnt, val_dataset_cnt)
print(len(temp_dataset), train_dataset_cnt, val_dataset_cnt)
train_loader = CustomDataloader(train_dataset, batch_size=batch_size, shuffle=True)
val_loader = CustomDataloader(val_dataset, batch_size=batch_size, shuffle=True)

데이터set을 train과 test 용으로 구분을 하기 위한 코드입니다.


모델

아래와 같은 형태로  model을 만듭니다. https://keras.io/api/layers/ 여기 링크에서 여러가지 layer 정보를 얻을 수 있습니다.

inputs = keras.Input(shape=(None, 3))
x = layers.Dense(6, activation="linear")(inputs)
x = layers.Dense(1, activation="linear")(x)
outputs = x
model = keras.Model(inputs, outputs)
model.summary()

모델을 훈련 시키기

keras에서는 pytorch 보다는 간단합니다. model.fit 만 호출해주면 내부 루프가 알아서 돌기 때문에 호출시 인자만 적절히 넘겨주면 됩니다.

# https://keras.io/api/optimizers/
# https://keras.io/api/losses/
INPUT_OPTIMIZER = tf.keras.optimizers.SGD(learning_rate=lr)
LOSS = tf.keras.losses.MeanSquaredError(reduction="auto", name="mean_squared_error")
model.compile(optimizer=INPUT_OPTIMIZER, loss=LOSS)
model.fit(train_loader, batch_size=batch_size, epochs=n_epochs, validation_data=val_loader)


실제 목표 값?

이제 값을 예측할 때가 되었습니다. 즉 3개의 변수 x1=0.1,x2=0.2,x3=0.1 이 주어 질 때 y는 어떻게 될까요? 당연히 계산 공식은 주어지지 않습니다. 훈련된 모델에 해당 값을 넣어서 y값을 알아 내야 합니다.

# y = 1 + 2*0.1*0.2*0.1
x_prd = np.array([[0.1, 0.2, 0.1]])
print("expect:", 1 + 2 * 0.1 * 0.2 * 0.1)
print(model.predict(x_prd))

model.predict() 함수에 인자를 넣으면 결과가 나옵니다.

expect: 1.004
WARNING:tensorflow:Model was constructed with shape (None, None, 3) for input KerasTensor(type_spec=TensorSpec(shape=(None, None, 3), dtype=tf.float32, name='input_1'), name='input_1', description="created by layer 'input_1'"), but it was called on an input with incompatible shape (None, 3).
1/1 [==============================] - 0s 41ms/step
[[0.7554034]]

0.75이라... 터무니 없는 값은 아니지만 그렇다고 썩 마음에 드는 수치는 아닙니다.

이렇게 나오는 이유는 입력 데이터에 대한 데이터들이 많지 않다고 볼 수 있습니다. 즉 입력으로 넣은 0.1,0.2,0.1 이라는 데이터가 우리가 훈련은 거의 하지 않았던 영역이라고 볼 수 있는것 입니다.


모델 저장

https://www.tensorflow.org/guide/keras/save_and_serialize?hl=ko

Keras에서는 두가지 방식을 이용 가능 합니다.

  • model.save() 또는 tf.keras.models.save_model()
  • tf.keras.models.load_model()

model.fit 함수 뒤에 save 함수를 호출 하면 됩니다.

model.fit(train_loader, batch_size=batch_size, epochs=n_epochs, validation_data=val_loader)
model.save("model.keras")


load_model() 예제

import numpy as np
import tensorflow as tf

if __name__ == '__main__':
    model = tf.keras.models.load_model("model.keras")
    model.summary()

    # test
    x_prd = np.array([[0.1, 0.2, 0.1]])  # y = 1 + 2*0.1*0.2*0.1
    print("expect:", 1 + 2 * 0.1 * 0.2 * 0.1)
    print(model.predict(x_prd))


전체 소스

sourcecode/keras/01_basic at main · donarts/sourcecode · GitHub



댓글 없음:

댓글 쓰기