- 참고 : 핸즈온 머신러닝 2판
텐서플로에서 데이터 적재와 전처리하기
메모리 용량에 맞지 않는 아주 큰 규모의 데이터셋으로 딥러닝 시스템을 훈련해야 하는 경우가 많음
다른 딥러닝 라이브러리를 사용해서는 대규모 데이터셋을 효율적으로 로드하고 전처리하도록 구현하기가 어려움
- 텐서플로에서 멀티스레딩, 큐, 배치, 프리페치 같은 상세한 사항을 모두 처리 해줌
데이터 API는 텍스트 파일, 고정 길이의 레코드를 가진 이진 파일, 텐서플로의 TFRecord 포멧을 사용하는 이진 파일에서 데이터를 읽을 수 있음
- 해당 포멧은 길이가 다른 레코드를 지원
- 일반적으로 프로토콜 버퍼를 담은 유연하고 효율적인 이진 포멧
- SQL 데이터베이스에서 읽는 기능도 지원
- 구글 Big Query 와 같은 다양한 데이터 소스에서 읽을 수 있는 오픈소스 제공
- TF 변환
- 훈련 전에 전체 훈련 세트에 대해 실행하는 전처리 함수를 작성할 수 있음, 그다음 텐서플로 함수로 변환하고 상용 환경에 배포된 다음 훈련된 모델과 협업하여 새로운 샘플에 대해 동적으로 전처리를 수행
- TF 데이터셋
- 각종 데이터셋을 다운로드할 수 있는 편리한 함수를 제공, Imagenet과 같은 대용량 데이터셋도 포함
데이터 API
tf.data.Dataset.from_tensor_slices()를 사용하는 예제
X = tf.range(10) # 셈플 데이터 텐서 dataset = tf.data.Dataset.from_tensor_slices(X) dataset >> <TensorSliceDataset shapes: (), types: tf.int32> for item in dataset: print(item) >> tf.Tensor(0, shape=(), dtype=int64) tf.Tensor(1, shape=(), dtype=int64) tf.Tensor(2, shape=(), dtype=int64) tf.Tensor(3, shape=(), dtype=int64) tf.Tensor(4, shape=(), dtype=int64) tf.Tensor(5, shape=(), dtype=int64) tf.Tensor(6, shape=(), dtype=int64) tf.Tensor(7, shape=(), dtype=int64) tf.Tensor(8, shape=(), dtype=int64) tf.Tensor(9, shape=(), dtype=int64)
- from_tensor_slices() 함수는 텐서를 받아 첫 번째 차원에 따라 X의 각 원소가 아이템으로 표현되는 tf.data.Dataset을 생성.
- 해당 텐서는 0~9 까지의 10개의 아이템을 가짐 (tf.data.Dataset.range(10)과 동일)
연쇄 변환
데이터셋이 준비되면 변환 메서드를 호출하여 여러 종류의 변환을 수행할 수 있음
각 메서드는 새로운 데이터셋을 반환하므로 다음과 같이 변환 메서드를 연결할 수 있음
dataset = dataset.repeat(3).batch(7) for item in dataset: print(item) >> tf.Tensor([0 1 2 3 4 5 6], shape=(7,), dtype=int64) tf.Tensor([7 8 9 0 1 2 3], shape=(7,), dtype=int64) tf.Tensor([4 5 6 7 8 9 0], shape=(7,), dtype=int64) tf.Tensor([1 2 3 4 5 6 7], shape=(7,), dtype=int64) tf.Tensor([8 9], shape=(2,), dtype=int64) dataset = dataset.map(lambda x: x * 2) >> 아이템 내부에 곱하기 2 dataset = dataset.unbatch() >> batch 해제 하나의 정수 텐서로 반환 dataset = dataset.filter(lambda x: x < 10) >> 필터링도 적용 가능 for item in dataset.take(3): print(item) >> tf.Tensor(0, shape=(), dtype=int64) tf.Tensor(2, shape=(), dtype=int64) tf.Tensor(4, shape=(), dtype=int64)
repeat() 메서드를 호출하면 원본 데이터셋의 아이템을 세 차례 반복하는 데이터셋 생성
batch() 메서드를 호출하면 다시 새로운 데이터셋이 생성
- batch 7 이여서 7개씩 묶음으로
- drop_remainder=True를 호출하면 길이가 모자란 마지막 배치를 삭제
take() 메서드로 가져올 개수 지정 가능
데이터 셔플링
경사 하강법은 훈련 세트에 있는 샘플이 독립적이고 동일한 분포일 때 최고의 성능을 발휘함
- 간단한 방법은 shuffle() 메서드를 사용해 샘플을 섞으면 됨
- 해당 메서드는 먼저 원본 데이터셋의 처음 아이템을 buffer_size 개수만큼 추출하여 버퍼에 채움
- 그다음 새로운 아이템이 요청되면 이 버퍼에서 랜덤하게 하나를 반환
- 그리고 원본 데이터셋에서 새로운 아이템을 추출하여 비워진 버퍼를 채움, 원본 데이터셋의 모든 아이템이 사용될 때 까지 반복 -> 버퍼가 비워질 때까지 계속하여 랜덤하게 아이템 반환
해당 메서드를 사용하기 위해서는 버퍼 크기를 지정해야 함, 버퍼 크기 충분히 크게 잡는게 중요
dataset = tf.data.Dataset.range(10).repeat(3) dataset = dataset.shuffle(buffer_size=3, seed=42).batch(7) for item in dataset: print(item) >> tf.Tensor([1 3 0 4 2 5 6], shape=(7,), dtype=int64) tf.Tensor([8 7 1 0 3 2 5], shape=(7,), dtype=int64) tf.Tensor([4 6 9 8 9 7 0], shape=(7,), dtype=int64) tf.Tensor([3 1 4 5 2 8 7], shape=(7,), dtype=int64) tf.Tensor([6 9], shape=(2,), dtype=int64)
여러 파일에서 한 줄씩 번갈아 읽기
캘리포니아 주택 데이터셋을 적재하고 섞은 다음 훈련 세트, 검증 세트, 테스트 세트로 나누었다고 가정
각 세트를 다음과 같은 csv 파일 여러 개로 분할
# 해당 CSV MedInc,HouseAge,AveRooms,AveBedrms,Population,AveOccup,Latitude,Longitude,MedianHouseValue 3.5214,15.0,3.0499445061043287,1.106548279689234,1447.0,1.6059933407325193,37.63,-122.43,1.442 5.3275,5.0,6.490059642147117,0.9910536779324056,3464.0,3.4433399602385686,33.69,-117.39,1.687 3.1,29.0,7.5423728813559325,1.5915254237288134,1328.0,2.2508474576271187,38.44,-122.98,1.621 7.1736,12.0,6.289002557544757,0.9974424552429667,1054.0,2.6956521739130435,33.55,-117.7,2.621 # train_filepaths >> ['datasets/housing/my_train_00.csv', 'datasets/housing/my_train_01.csv', 'datasets/housing/my_train_02.csv', .... ] # 입력 파이프라인 만들기 filepath_dataset = tf.data.Dataset.list_files(train_filepaths, seed=42) ## 기본적으로 list_files() 함수는 파일 경로를 섞은 데이터셋을 반환 ## 섞는 것을 방지하고 싶다면 shuffle = False 지정
iterleave() 메서드 사용해 한 번에 다섯 개의 파일을 한 줄씩 번갈아 읽음
n_readers = 5
dataset = filepath_dataset.interleave(
lambda filepath: tf.data.TextLineDataset(filepath).skip(1),
cycle_length=n_readers)
파일의 첫 줄은 header 임으로 skip으로 넘어감
- interleave() 메서드는 filepath_dataset에 있는 다섯 개의 파일 경로에서 데이터를 읽는 데이터셋을 생성
- 해당 메서드에 전달한 함수를 각 파일에 대해 호출하여 새로운 데이터셋을 생성
- 총 7개의 데이터셋이 생성 (파일 경로 데이터셋, 인터리브 데이터셋, 인터리브 데이터셋에 의해 생성된 5개의 TextLineDataset)
- 기본적으로 interleave() 메서드는 병렬화를 사용하지 않음
- 각 파일에서 한 번에 한 줄씩 순서대로 읽음, 여러 파일에서 병렬로 읽을때는 num_parallel_calls 매개변수에 원하는 스레드 수 지정
- 해당 매개변수를 tf.data.experimental.AUTOTUNE으로 지정하면 텐서플로가 가용한 CPU를 기반으로 동적으로 적절한 스레드 개수를 선택할 수 있음 (아직 실험적인 기능)
```python
for line in dataset.take(5):
print(line.numpy())
>> b'4.6477,38.0,5.03728813559322,0.911864406779661,745.0,2.5254237288135593,32.64,-117.07,1.504'
b'8.72,44.0,6.163179916317992,1.0460251046025104,668.0,2.794979079497908,34.2,-118.18,4.159'
b'3.8456,35.0,5.461346633416459,0.9576059850374065,1154.0,2.8778054862842892,37.96,-122.05,1.598'
b'3.3456,37.0,4.514084507042254,0.9084507042253521,458.0,3.2253521126760565,36.67,-121.7,2.526'
b'3.6875,44.0,4.524475524475524,0.993006993006993,457.0,3.195804195804196,34.04,-118.15,1.625'
- 위 내용은 CSV의 header 무시하고 첫 번째 행에 해당하며 순서는 랜덤
데이터 전처리
전처리 수행 예제
X_mean = scaler.mean_ X_std = scaler.scale_ n_inputs = 8 # X_train.shape[-1] @tf.function def preprocess(line): defs = [0.] * n_inputs + [tf.constant([], dtype=tf.float32)] fields = tf.io.decode_csv(line, record_defaults=defs) x = tf.stack(fields[:-1]) y = tf.stack(fields[-1:]) return (x - X_mean) / X_std, y
- mean, std는 미리 개산
- preprocess() 함수는 CSV 한 라인을 받아 파싱, 이를 위해 tf.io.decode_csv() 함수를 사용
- tf.io.decode_csv()
- 두 개의 매개변수를 입력 받음
- 첫 번째는 파싱할 라인과 두 번째는 CSV파일의 각 열에 대한 기본값을 담은 배열
- 이 배열은 텐서플로에게 각 열의 기본값뿐만 아니라 열 개수와 데이터 타입도 알려줌
- 스칼라 텐서의 리스트를 반환
- 1D 텐서 배열을 반환해야 하므로 마지막 열을 제외하고 모든 텐서에 대해 tf.stack() 함수를 호출
- 해당 함수는 모든 텐서를 쌓아 1D 배열을 만듬, 그 다음 타깃값에도 동일하게 적용
- tf.io.decode_csv()
- 마지막으로 입력 특성에서 평균을 빼고 표준편차로 나누어 스케일을 조정. 그다음 스케일로 조정된 특성과 타깃을 담은 튜플을 반환
데이터 적재와 전처리를 합치기
- 재사용 가능한 코드를 만들기 위해 지금까지 수행한 모든 것을 하나의 헬퍼 함수로 생성
- 해당 함수는 CSV 파일에서 데이터를 효율적으로 적재하고 전처리, 셔플링, 반복, 배치를 적용한 데이터셋을 만들어 반환
def csv_reader_dataset(filepaths, repeat=1, n_readers=5,
n_read_threads=None, shuffle_buffer_size=10000,
n_parse_threads=5, batch_size=32):
dataset = tf.data.Dataset.list_files(filepaths).repeat(repeat)
dataset = dataset.interleave(
lambda filepath: tf.data.TextLineDataset(filepath).skip(1),
cycle_length=n_readers, num_parallel_calls=n_read_threads)
dataset = dataset.shuffle(shuffle_buffer_size)
dataset = dataset.map(preprocess, num_parallel_calls=n_parse_threads)
dataset = dataset.batch(batch_size)
return dataset.prefetch(1)
프리배치
- 마지막 prefetch(1)를 호출하면 데이터셋은 항상 한 배치가 미리 준비되도록 함
- 훈련 알고리즘이 한 배치로 작업을 하는 동안 이 데이터셋이 동시에 다음 배치를 준비
- 해당 기능을 통해 성능을 크게 향상시킴
- 훈련 알고리즘이 한 배치로 작업을 하는 동안 이 데이터셋이 동시에 다음 배치를 준비
tf.keras와 데이터셋 사용하기
- csv_reader_dataset() 함수로 훈련 세트로 사용할 데이터셋을 생성할 수 있음
- tf.keras에서 반복을 처리하므로 반복을 지정할 필요가 없음
train_set = csv_reader_dataset(train_filepaths, repeat=None)
valid_set = csv_reader_dataset(valid_filepaths)
test_set = csv_reader_dataset(test_filepaths)
model = keras.models.Sequential([
keras.layers.Dense(30, activation="relu", input_shape=X_train.shape[1:]),
keras.layers.Dense(1),
])
model.compile(loss="mse", optimizer=keras.optimizers.SGD(learning_rate=1e-3))
batch_size = 32
model.fit(train_set, steps_per_epoch=len(X_train) // batch_size, epochs=10, validation_data=valid_set)
model.evaluate(test_set, steps=len(X_test) // batch_size)
new_set = test_set.map(lambda X, y: X) # we could instead just pass test_set, Keras would ignore the labels
X_new = X_test
model.predict(new_set, steps=len(X_new) // batch_size)
keras의 fit() 메서드에 X_train, y_train, X_valid, y_valid 대신 훈련 데이터셋과 검증 데이터셋을 전달하면 됨
하지만 대규모의 복잡한 데이터 구조를 지원하지 못함
TFRecord 포맷
- 대용량 데이터를 저장하고 효율적으로 읽기 위해 텐서플로가 사용하는 포멧인 TFRecord가 정의
- TFRecord는 크기가 다른 연속된 이진 레코드를 저장하는 단순한 이진 포맷
- tf.io.TFRecordWriter 클래스를 사용해 TFRecord를 손 쉽게 생성 가능
with tf.io.TFRecordWriter("my_data.tfrecord") as f:
f.write(b"This is the first record")
f.write(b"And this is the second record")
- tf.data.TFRecordDataset을 사용해 하나 이상의 TFRecord를 읽을 수 있음
filepaths = ["my_data.tfrecord"]
dataset = tf.data.TFRecordDataset(filepaths)
for item in dataset:
print(item)
>> tf.Tensor(b'This is the first record', shape=(), dtype=string)
tf.Tensor(b'And this is the second record', shape=(), dtype=string)
압축된 TFRecord 파일
- optopms 매개변수를 사용하여 압축된 TFRecord 파일 생성 가능
options = tf.io.TFRecordOptions(compression_type="GZIP")
with tf.io.TFRecordWriter("my_compressed.tfrecord", options) as f:
f.write(b"This is the first record")
f.write(b"And this is the second record")
- 압축된 TFRecord 파일을 읽으려면 압축 형식을 지정해야 함
dataset = tf.data.TFRecordDataset(["my_compressed.tfrecord"],
compression_type="GZIP")
텐서플로 프로토콜 버퍼
TFRecord 파일에서 사용하는 전형적인 주요 프로토콜 버퍼는 데이터셋에 있는 하나의 샘플을 표현하는 Example 프로토콜 버퍼
프로토콜 버퍼 정의
syntax = "proto3"; message BytesList { repeated bytes value = 1; } message FloatList { repeated float value = 1 [packed = true]; } message Int64List { repeated int64 value = 1 [packed = true]; } message Feature { oneof kind { BytesList bytes_list = 1; FloatList float_list = 2; Int64List int64_list = 3; } }; message Features { map<string, Feature> feature = 1; }; message Example { Features features = 1; };
tf.train.Example 객체 생성후 TFRecord 파일에 저장하는 방법
from tensorflow.train import BytesList, FloatList, Int64List from tensorflow.train import Feature, Features, Example person_example = Example( features=Features( feature={ "name": Feature(bytes_list=BytesList(value=[b"Alice"])), "id": Feature(int64_list=Int64List(value=[123])), "emails": Feature(bytes_list=BytesList(value=[b"a@b.com", b"c@d.com"])) })) with tf.io.TFRecordWriter("my_contacts.tfrecord") as f: f.write(person_example.SerializeToString())
Example 프로토콜 버퍼를 읽고 파싱하기
직렬화된 Example 프로토콜 버퍼를 읽기 위해서 tf.data.TFRecordDataset을 다시 한번 사용하고 tf.io.parse_single_example()을 사용해 Example 파싱
설명 dict 정의후 TFRecordDataset을 순회하면서 데이터셋에 포함된 직렬화된 Example 프로토콜 버퍼를 파싱하는 예제
feature_description = { "name": tf.io.FixedLenFeature([], tf.string, default_value=""), "id": tf.io.FixedLenFeature([], tf.int64, default_value=0), "emails": tf.io.VarLenFeature(tf.string), } for serialized_example in tf.data.TFRecordDataset(["my_contacts.tfrecord"]): parsed_example = tf.io.parse_single_example(serialized_example, feature_description)
- 고정 길이 특성은 보통의 텐서로 파싱되지만 가변 길이 특성은 희소 텐서로 파싱됨
- tf.sparse.to_dense()로 희소 텐서를 밀집 텐서로 변환할 수 있지만 희소 텐서의 값을 바로 참조하는 것이 더 간단함
tf.io.parse_single_example()로 하나씩 파싱하는 대신 tf.io.parse_example()을 사용하여 배치 단위로 파싱하는 예제
dataset = tf.data.TFRecordDataset(["my_contacts.tfrecord"]).batch(10) for serialized_examples in dataset: parsed_examples = tf.io.parse_example(serialized_examples, feature_description)
입력 특성 전처리
신경망을 위해 데이터를 준비하기 위해 모든 특성을 수치 특성으로 변환하고 정규화 하는 과정이 필요
- 데이터에 범주형 특성이나 텍스트 특성이 있다면 숫자로 변환해야 함
Lambda 층을 사용해 표준화를 수행하는 층을 구현하는 방법 예제
means = np.mean(X_train, axis=0, keepdims=True) stds = np.std(X_train, axis=0, keepdims=True) eps = keras.models.Sequential([ keras.layers.Lambda(lambda inputs : (inputs - means) / (stds + eps)), ... ])
- 또는 사용자 정의 층을 생성할수도 있음
class Standardization(keras.layers.Layer): def adapt(self, data_sample): self.means_ = np.mean(data_sample, axis=0, keepdims=True) self.stds_ = np.std(data_sample, axis=0, keepdims=True) def call(self, inputs): return (inputs - self.means_) / (self.stds_ + keras.backend.epsilon()) std_layer = Standardization() std_layer.adapt(data_sample) model = keras.Sequential() model.add(std_layer) ...
- 원하는 기능을 Layer로 만들어서 사용할 수 있음
원-핫 벡터를 사용해 범주형 특성 인코딩하기
범주형 데이터를 신경망에 넣기전에 인코딩을 수행
vocab = ["<1H OCEAN", "INLAND", "NEAR OCEAN", "NEAR BAY", "ISLAND"] indices = tf.range(len(vocab), dtype=tf.int64) table_init = tf.lookup.KeyValueTensorInitializer(vocab,indices) num_oov_buckets = 2 table = tf.lookup.StaticVocabularyTable(table_init, num_oov_buckets)
- 어휘 사전 정의, 범주에 해당하는 인덱스의 tensor 생성
- 범주 리스트와 해당 인덱스를 전달하여 룩업 테이블을 위해 초기화 객체 생성
- 초기화 객체와 OOV 버킷을 지정하여 룩업 테이블을 생성. 어휘 사전에 없는 범주를 찾으면 룩업 테이블이 계산한 이 범주의 해시값을 이용하여 oov 버킷 중 하나에 할당
룩업 테이블을 사용해 몇 개의 범주 특성을 원-핫 인코딩 하는 예제
categories = tf.constant(['NEAR BAY', 'DESERT', 'INLAND', 'INLAND']) cat_indices = table.lookup(categories) cat_one_hot = tf.one_hot(cat_indices, depth= len(vocab) + num_oov_buckets)
- 어휘 사전이 크면 임베딩을 사용하여 인코딩하는 것이 훨씬 효율적인 방식임
임베딩을 사용해 범주형 특성 인코딩하기
임베딩은 범주를 표현하는 훈련 가능한 밀집 벡터를 의미함
처음엔 임베딩이 랜덤하게 초기화되어 있음
- 'NEAR BAR' : [0.131, 0.890]과 같은 랜덤 벡터로 표현
- 'NEAR OCEAN' : [0.631, 0.791]과 같은 랜덤 벡터로 표현
임베딩을 훈련할 수 있기 때문에 훈련 도중에 점차 향상됨
비슷한 범주들은 경사 하강법이 더 가깝게 만듬
표현이 좋을수록 신경말이 정확한 예측을 만들기가 쉬움
이처럼 범주가 유용하게 표현되도록 임베딩이 훈련되는 경향을 표현 학습
케라스 임베딩 학습 예제
regular_inputs = keras.layers.Input(shape=[8]) categories = keras.layers.Input(shape=[], dtype=tf.string) cat_indices = keras.layers.Lambda(lambda cats : table.lookup(cats))(categories) cat_embed = keras.layers.Embedding(input_dim=6, output_dim=2)(cat_indices) encoded_inputs = keras.layers.concatenate([regular_inputs, cat_embed]) outputs = keras.layers.Dense(1)(encoded_inputs) model = keras.model.Model(inputs = [regular_inputs, categories], outputs = [outputs])
- 해당 모델은 두 개의 입력을 받음, 샘플마다 8개의 특성을 담은 입력과 하나의 범주형 입력
- Lambda 층을 통해 범주의 인덱스를 찾음 다음 임베딩에서 해당하는 인덱스를 찾음
- 임베딩과 일반 입력을 연결하여 신경망에 전달, 이후 완전연결층으로 끝
TF 변환
전처리는 계산 비용이 크기 때문에 훈련과 동시에 수행하는 것보다 사전에 처리하면 속도를 크게 높일 수 있음
데이터가 훈련하는 동안 epoch 마다 전처리되는 것이 아니라 훈련하기 전에 샘플마다 한 번씩 전처리 됨
훈련 전에 수행한 전처리 연산과 앱이나 브라우저에서 수행하는 전처리가 차이가 날 수 있음 이를 훈련/서빙 왜곡 이라고 하고 이는 성능 감소로 이루어질수 있음
전처리 연산을 딱 한번만 정의하기 위해 TF변환을 사용, TF 변환 같은 변환 함수를 사용하여 전처리 함수를 한 번만 정의, 필요시 어떤 텐서플로 연산도 사용할 수 있음
TF변환으로 두개의 특성을 전처리하는 함수 예제
try: import tensorflow_transform as tft def preprocess(inputs): # inputs is a batch of input features median_age = inputs["housing_median_age"] ocean_proximity = inputs["ocean_proximity"] standardized_age = tft.scale_to_z_score(median_age - tft.mean(median_age)) ocean_proximity_id = tft.compute_and_apply_vocabulary(ocean_proximity) return { "standardized_median_age": standardized_age, "ocean_proximity_id": ocean_proximity_id } except ImportError: print("TF Transform is not installed. Try running: pip3 install -U tensorflow-transform")
- 위 예제에서 housing_median_age 특성의 평균, 표준편차와 ocean_proximity 특성의 어휘 사전을 계산
- 이런식의 통계를 계산하는 컴포넌트를 애널라이저라고 부름
- 표준 데이터셋을 사용하고 싶다면 TFDS를 사용
텐서플로 데이터셋(TFDS) 프로젝트
TFDS는 tensorflow-datasets 라이브러리를 설치해야 함
MNIST 예제
import tensorflow_datasets as tfds ### 데이터 로드 datasets = tfds.load(name="mnist") mnist_train, mnist_test = datasets["train"], datasets["test"] ### 원하는 변환 적용 plt.figure(figsize=(6,3)) mnist_train = mnist_train.repeat(5).batch(32).prefetch(1) for item in mnist_train: images = item["image"] labels = item["label"] for index in range(5): plt.subplot(1, 5, index + 1) image = images[index, ..., 0] label = labels[index].numpy() plt.imshow(image, cmap="binary") plt.title(label) plt.axis("off") break # just showing part of the first batch
map 매서드를 통해 형태 변환
datasets = tfds.load(name="mnist")
mnist_train, mnist_test = datasets["train"], datasets["test"]
mnist_train = mnist_train.repeat(5).batch(32)
mnist_train = mnist_train.map(lambda items: (items["image"], items["label"]))
mnist_train = mnist_train.prefetch(1)
for images, labels in mnist_train.take(1):
print(images.shape)
print(labels.numpy())
해당과정으로 데이터를 바로 keras 모델에 적용하는 예제
datasets = tfds.load(name="mnist", batch_size=32, as_supervised=True)
mnist_train = datasets["train"].repeat().prefetch(1)
model = keras.models.Sequential([
keras.layers.Flatten(input_shape=[28, 28, 1]),
keras.layers.Lambda(lambda images: tf.cast(images, tf.float32)),
keras.layers.Dense(10, activation="softmax")])
model.compile(loss="sparse_categorical_crossentropy",
optimizer=keras.optimizers.SGD(learning_rate=1e-3),
metrics=["accuracy"])
model.fit(mnist_train, steps_per_epoch=60000 // 32, epochs=5)
'Study > Self Education' 카테고리의 다른 글
Pytorch 기본기 - 1 (0) | 2024.07.02 |
---|---|
핸즈온 머신러닝 - 14 (0) | 2024.06.19 |
핸즈온 머신러닝 - 12 (0) | 2024.06.19 |
핸즈온 머신러닝 - 11 (0) | 2024.06.19 |
핸즈온 머신러닝 - 10 (0) | 2024.06.19 |