🐬

【TF Tutorial】Chapter 1: Overview of TensorFlow

2024/07/13に公開

1. Introduction to TensorFlow and Keras

1.1 Overview of TensorFlow and Keras

TensorFlow:
An open-source machine learning framework developed by Google.
TensorFlow's flexible architecture allows you to deploy computation across a variety of platforms (CPUs, GPUs, TPUs).

Keras:
High-level neural networks API, written in Python.
It allows for easy and fast prototyping, and supports both convolutional networks and recurrent networks.

1.2 Installing TensorFlow

To start using TensorFlow, you'll need to install it.

pip install tensorflow

1.3 Understanding TensorFlow's Ecosystem

TensorFlow's ecosystem includes several libraries and tools to facilitate machine learning development:

TensorFlow Core: The low-level TensorFlow API.
Keras: High-level API for building and training models.
TensorFlow Datasets: Pre-built datasets for easy machine learning experimentation.
TensorFlow Hub: Pre-trained models for transfer learning.
TensorFlow Lite: Lightweight solution for mobile and embedded devices.
TensorFlow.js: TensorFlow for JavaScript, for use in web browsers and Node.js.
TensorBoard: Visualization toolkit for machine learning experimentation.

1.4 Import

Keras is included within TensorFlow as tf.keras.

import tensorflow as tf
from tensorflow.keras import layers, models

2. Dataset

2.1 Dataset

First, make the dataset.

import numpy as np
import tensorflow as tf

# Generate dummy data
num_samples = 1000
num_features = 20
num_classes = 3

x_data = np.random.random((num_samples, num_features))
y_data = np.random.randint(num_classes, size=(num_samples,))

# Convert labels to categorical (for classification)
y_data = tf.keras.utils.to_categorical(y_data, num_classes=num_classes)

# Split into training and testing sets
train_size = int(0.8 * num_samples)
x_train, x_test = x_data[:train_size], x_data[train_size:]
y_train, y_test = y_data[:train_size], y_data[train_size:]

# Create tf.data.Dataset objects
train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
test_dataset = tf.data.Dataset.from_tensor_slices((x_test, y_test))

# Shuffle and batch the training data
batch_size = 32
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(batch_size)
test_dataset = test_dataset.batch(batch_size)

This is an example of a way to make a dataset.
Briefly explanation:
tf.keras.utils.to_categorical: apply one-hot encoding to categorical data
from_tensor_slices: make dataset from array-like objects.
shuffle: shuffle the data by using buffer_size augment as pool size.
batch: make batch dataset with specified size.

example of from_tensor_slices how to work.

This is how to work of that function.

import tensorflow as tf
import numpy as np

# Create feature and label arrays
features = np.array([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]])
labels = np.array([0, 1, 1, 0, 0])

# Create a tf.data.Dataset from the feature and label arrays
dataset = tf.data.Dataset.from_tensor_slices((features, labels))

# Specify batch size
batch_size = 2
batched_dataset = dataset.batch(batch_size)

# Iterate through the batched dataset and print each batch
for batch in batched_dataset:
    features_batch, labels_batch = batch
    print("Features batch:")
    print(features_batch.numpy())
    print("Labels batch:")
    print(labels_batch.numpy())
    print()

・output

Features batch:
[[1 2]
 [3 4]]
Labels batch:
[0 1]

Features batch:
[[5 6]
 [7 8]]
Labels batch:
[1 0]

Features batch:
[[ 9 10]]
Labels batch:
[0]

3. Model

3.1 Define the model with 3 API

Define the model here, there are 3 ways to define the model.
・ Sequential API
・ Functional API
・ Subclassing API

・Sequential API

model = models.Sequential([
    layers.Flatten(input_shape=(28, 28)),  # Flatten the input
    layers.Dense(128, activation='relu'),  # Fully connected layer with ReLU activation
    layers.Dropout(0.2),                   # Dropout layer for regularization
    layers.Dense(10)                       # Output layer with 10 units (one for each digit)
])

# Same to above
# model = models.Sequential()
# model.add(layers.Flatten(input_shape=(28, 28)))
# model.add(layers.Dense(128, activation='relu'))
# model.add(layers.Dropout(0.2))
# model.add(layers.Dense(10))

・Functional API

from tensorflow.keras import Model, Input
from tensorflow.keras.layers import Flatten, Dense, Dropout

inputs = Input(shape=(28, 28))
x = Flatten()(inputs)
x = Dense(128, activation='relu')(x)
x = Dropout(0.2)(x)
outputs = Dense(10)(x)

model = Model(inputs=inputs, outputs=outputs)

・Subclassing API

import tensorflow as tf

class MyModel(tf.keras.Model):
    def __init__(self):
        super(MyModel, self).__init__()
        self.flatten = tf.keras.layers.Flatten()
        self.dense1 = tf.keras.layers.Dense(128, activation='relu')
        self.dropout = tf.keras.layers.Dropout(0.2)
        self.dense2 = tf.keras.layers.Dense(10)
    
    def call(self, inputs):
        x = self.flatten(inputs)
        x = self.dense1(x)
        x = self.dropout(x)
        return self.dense2(x)

model = MyModel()

When to use Sequential API:
・When building simple models that have a linear stack of layers.
・When each layer has exactly one input tensor and one output tensor.

When to use Functional API:
・When building complex models that require non-linear topology, such as multi-input models, multi-output models, or models with shared layers.
・When you need to manipulate multiple layers or outputs, such as branching networks.
・When working with models that include auxiliary outputs.

When to use Subclassing API:
・When you need to build highly customized or dynamic models.
call: Define the forward pass of your model here. This method is called automatically when you use functions like fit, evaluate, and train_on_batch.

3.2 Compile the model

・Simple Compile
We sepcify the optimizer, loss type and metrics for validation.

model.compile(optimizer='adam',
                loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
              metrics=['accuracy'])

・Weighted sum loss(Multiple loss)

Functional API
import tensorflow as tf
import numpy as np

# define the input layer
inputs = tf.keras.Input(shape=(32,))
# define the shared layer
x = tf.keras.layers.Dense(64, activation='relu')(inputs)
# define multiple outputs
output1 = tf.keras.layers.Dense(10, name='output1')(x)
output2 = tf.keras.layers.Dense(1, name='output2')(x)

# define the model
model = tf.keras.Model(inputs=inputs, outputs=[output1, output2])
model.compile(
    optimizer='adam',
    loss={
        'output1': tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
        'output2': tf.keras.losses.MeanSquaredError()
    },
    loss_weights={
        'output1': 0.7,
        'output2': 0.3
    },
    metrics={
        'output1': ['accuracy'],
        'output2': ['mse']
    }
)

train_data = np.random.random((1000, 32))
train_labels1 = np.random.randint(10, size=(1000,))
train_labels2 = np.random.random((1000, 1))

train_labels = {
    'output1': train_labels1,
    'output2': train_labels2
}

# train the model
model.fit(train_data, train_labels, epochs=5)

new_data = np.random.random((10, 32))
predictions = model.predict(new_data)

print(predictions)

The keys in metric dictionary should match the names of the outputs or layers in your model.

Subclassing API

Similar to Functional API.

import tensorflow as tf
import numpy as np

class MyModel(tf.keras.Model):
    def __init__(self):
        super(MyModel, self).__init__()
        self.dense1 = tf.keras.layers.Dense(64, activation='relu')
        self.dense2 = tf.keras.layers.Dense(10)
        self.dense3 = tf.keras.layers.Dense(1)

    def call(self, inputs):
        x = self.dense1(inputs)
        output1 = self.dense2(x)
        output2 = self.dense3(x)
        return output1, output2

def custom_loss(y_true, y_pred):
    loss1 = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)(y_true[0], y_pred[0])
    loss2 = tf.keras.losses.MeanSquaredError()(y_true[1], y_pred[1])
    return 0.7 * loss1 + 0.3 * loss2

model = MyModel()
model.compile(
    optimizer='adam',
    loss=custom_loss,
    metrics={'dense2': ['accuracy'], 'dense3': ['mse']}
)

train_data = np.random.random((1000, 32))
train_labels1 = np.random.randint(10, size=(1000,))
train_labels2 = np.random.random((1000, 1))
train_labels = [train_labels1, train_labels2]

# train the model
model.fit(train_data, train_labels, epochs=5)

new_data = np.random.random((10, 32))
predictions = model.predict(new_data)

print(predictions)

The keys in metric dictionary should match the names of the outputs or layers in your model.

4. Training

It's so simple.

model.fit(train_dataset, epochs=10, validation_data=test_dataset)

4.1 Augmentation

We can define dataroader(work like dataset) for augmentation.
・Example

from tensorflow.keras.preprocessing.image import ImageDataGenerator

# Create an ImageDataGenerator object with data augmentation parameters
datagen = ImageDataGenerator(
    rotation_range=20,
    width_shift_range=0.2,
    height_shift_range=0.2,
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True,
    fill_mode='nearest'
)

# Fit the generator to the training data
datagen.fit(x_train)

# Use the generator to train the model
model.fit(datagen.flow(x_train, y_train, batch_size=32),
          steps_per_epoch=len(x_train) // 32,
          epochs=5,
          validation_data=(x_test, y_test))

4.2 Callbacks

Callbacks are a way to add custom behavior to the training process. Keras provides several built-in callbacks such as ModelCheckpoint, EarlyStopping, ReduceLROnPlateau, and more.
・Example

from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping

# Create a ModelCheckpoint callback to save the best model
checkpoint = ModelCheckpoint('best_model.h5', save_best_only=True, monitor='val_loss', mode='min')

# Create an EarlyStopping callback to stop training when a monitored metric has stopped improving
early_stopping = EarlyStopping(monitor='val_loss', patience=5, mode='min')

# Train the model with callbacks
model.fit(x_train, y_train,
          epochs=50,
          batch_size=32,
          validation_split=0.2,
          callbacks=[checkpoint, early_stopping])

5. Evaluation

Also simple.

model.evaluate(x_val, y_val, verbose=2)

6. Prediction

Also...
This gets the output that is specified when the model is defined.

predictions = model.predict(test_data)

7. Custom training loop(option)

We can use training loop instead of .fit for more detail settings.

import tensorflow as tf

# Define the loss function and optimizer
loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
optimizer = tf.keras.optimizers.Adam()

# Training loop
epochs = 5
batch_size = 32
train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train)).shuffle(10000).batch(batch_size)
test_dataset = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(batch_size)

for epoch in range(epochs):
    print(f'Epoch {epoch + 1}/{epochs}')
    
    # Training step
    for step, (x_batch_train, y_batch_train) in enumerate(train_dataset):
        with tf.GradientTape() as tape:
            logits = model(x_batch_train, training=True)
            loss = loss_fn(y_batch_train, logits)
        
        gradients = tape.gradient(loss, model.trainable_variables)
        optimizer.apply_gradients(zip(gradients, model.trainable_variables))
        
        if step % 100 == 0:
            print(f'Step {step}: Loss = {loss.numpy()}')
    
    # Evaluation step
    test_loss = 0
    test_accuracy = tf.keras.metrics.SparseCategoricalAccuracy()
    for x_batch_test, y_batch_test in test_dataset:
        logits = model(x_batch_test, training=False)
        test_loss += loss_fn(y_batch_test, logits).numpy()
        test_accuracy.update_state(y_batch_test, logits)
    
    print(f'Test Loss: {test_loss / len(test_dataset)}')
    print(f'Test Accuracy: {test_accuracy.result().numpy()}')

8. .build and .summary(option)

tensorflow has .build method and .summary method.
Ill write a brief explanation of these because I could't understand these at first. build: define the model and input shape explicitly, and be needed for summary summary`: show the quick summary of model architecture.

I think the main function of .build is preparing to .summary. Also, these are not always necessary, so they are often not written.

Discussion