이 글은 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
댓글 없음:
댓글 쓰기