Approximate analytical functions with Tensorflow¶

Purpose:

  • A simple program to approximate Analytical Functions with DNN & TF2
In [1]:
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import numpy as np
import math
from random import uniform
print(tf.__version__)
2024-03-24 12:47:00.981467: E external/local_xla/xla/stream_executor/cuda/cuda_dnn.cc:9261] Unable to register cuDNN factory: Attempting to register factory for plugin cuDNN when one has already been registered
2024-03-24 12:47:00.981585: E external/local_xla/xla/stream_executor/cuda/cuda_fft.cc:607] Unable to register cuFFT factory: Attempting to register factory for plugin cuFFT when one has already been registered
2024-03-24 12:47:01.027426: E external/local_xla/xla/stream_executor/cuda/cuda_blas.cc:1515] Unable to register cuBLAS factory: Attempting to register factory for plugin cuBLAS when one has already been registered
2024-03-24 12:47:01.127643: I tensorflow/core/platform/cpu_feature_guard.cc:182] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.
To enable the following instructions: AVX2 FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.
2024-03-24 12:47:02.296774: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Could not find TensorRT
2.15.0

Training data¶

In [2]:
# generate training data

dim_x = 2
dim_y = 1
no_samples = 10000
filename = "mymodel"
In [3]:
####################### Training data ##############
## x coord
aPnts = np.empty([no_samples, dim_x])  
for iI in range(no_samples):
    for iJ in range(dim_x):
        aPnts[iI][iJ] = uniform(-1.0, 1.0)
data = aPnts #np.random.random((no_samples, dim_x))

## y value
aTres = np.empty([no_samples,])
for iI in range(no_samples):
    aTres[iI] = math.cos(0.5 * math.pi * aPnts[iI][0]) * math.cos(0.5 * math.pi * aPnts[iI][1])
labels = aTres #np.random.random((no_samples,dim_y ))
In [4]:
###################### Test data ###################

aPnts2 = np.empty([no_samples, dim_x])  
for iI in range(no_samples):
    for iJ in range(dim_x):
        aPnts2[iI][iJ] = uniform(-1.0, 1.0)
data2 = aPnts2 #np.random.random((no_samples, dim_x))

## y value
aTres2 = np.empty([no_samples,])
for iI in range(no_samples):
    aTres2[iI] = math.cos(0.5 * math.pi * aPnts2[iI][0]) * math.cos(0.5 * math.pi * aPnts2[iI][1])
labels2 = aTres2 #np.random.random((no_samples,dim_y ))

Model¶

In [5]:
######### Model ####################################
model = tf.keras.Sequential([
# Adds a densely-connected layer with 64 units to the model:
layers.Dense(64, activation='relu', input_shape=(dim_x,)),
# Add another:
layers.Dense(64, activation='relu'),
# Add an output layer with 10 output units:
layers.Dense(dim_y)])

# Configure a model for mean-squared error regression.
model.compile(optimizer=tf.keras.optimizers.Adam(0.01),
              loss='mse',       # mean squared error
              metrics=['mae'])  # mean absolute error

Fit Model¶

In [6]:
model.fit(data, labels, epochs=10, batch_size=32)
Epoch 1/10
313/313 [==============================] - 1s 1ms/step - loss: 0.0059 - mae: 0.0305
Epoch 2/10
313/313 [==============================] - 0s 1ms/step - loss: 1.7282e-04 - mae: 0.0102
Epoch 3/10
313/313 [==============================] - 0s 1ms/step - loss: 2.5821e-04 - mae: 0.0116
Epoch 4/10
313/313 [==============================] - 0s 1ms/step - loss: 1.3998e-04 - mae: 0.0088
Epoch 5/10
313/313 [==============================] - 0s 1ms/step - loss: 1.6116e-04 - mae: 0.0092
Epoch 6/10
313/313 [==============================] - 0s 1ms/step - loss: 2.8646e-04 - mae: 0.0127
Epoch 7/10
313/313 [==============================] - 0s 1ms/step - loss: 1.1732e-04 - mae: 0.0085
Epoch 8/10
313/313 [==============================] - 0s 1ms/step - loss: 2.5600e-04 - mae: 0.0124
Epoch 9/10
313/313 [==============================] - 0s 1ms/step - loss: 1.8339e-04 - mae: 0.0101
Epoch 10/10
313/313 [==============================] - 0s 1ms/step - loss: 9.1849e-05 - mae: 0.0072
Out[6]:
<keras.src.callbacks.History at 0x7f730873b910>

Test accuracy¶

In [7]:
test_loss, test_acc = model.evaluate(data2,  labels2, verbose=2)
print('\nTest accuracy:', test_acc)
313/313 - 0s - loss: 1.5903e-04 - mae: 0.0097 - 342ms/epoch - 1ms/step

Test accuracy: 0.00965808890759945

Predict individual values¶

In [8]:
predictions = model.predict(data2)
x = data2[0]

x1 = x[0,]
y1 = x[1,]
print(" point to test")
print (x[0,], "  ",x[1,])
#x.shape
#print(x.shape, data2.shape)

## Analytical solution:
res = math.cos(0.5 * math.pi * x1) * math.cos(0.5 * math.pi * y1)
print("NN prediction: " , predictions[0], "Analytical solution", res, "difference" ,abs(predictions[0]-res))
313/313 [==============================] - 0s 1ms/step
 point to test
-0.7142146426558194    -0.8596113282000879
NN prediction:  [0.10459869] Analytical solution 0.0949293054604226 difference [0.00966938]

A second model with gradient tape¶

In [9]:
inputs = keras.Input(shape=(dim_x,), name='x-coord')
x = layers.Dense(64, activation='relu', name='dense_1')(inputs)
x = layers.Dense(64, activation='relu', name='dense_2')(x)
outputs = layers.Dense(dim_y, name='predictions')(x)
model2 = keras.Model(inputs=inputs, outputs=outputs)

# Instantiate an optimizer.
optimizer = keras.optimizers.Adam(learning_rate=0.01)

# Instantiate a loss function.
loss_fn = tf.keras.losses.MeanSquaredError(reduction=tf.keras.losses.Reduction.SUM)

# Prepare the training dataset.
batch_size = 32
train_dataset = tf.data.Dataset.from_tensor_slices((data, labels))
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(batch_size)

epochs = 10
for epoch in range(epochs):
  print('Start of epoch %d' % (epoch,))

  # Iterate over the batches of the dataset.
  for step, (x_batch_train, y_batch_train) in enumerate(train_dataset):

    # Open a GradientTape to record the operations run
    # during the forward pass, which enables autodifferentiation.
    with tf.GradientTape() as tape:

      # Run the forward pass of the layer.
      # The operations that the layer applies
      # to its inputs are going to be recorded
      # on the GradientTape.
      logits = model2(x_batch_train, training=True)  # Logits for this minibatch

      # Compute the loss value for this minibatch.
      loss_value = loss_fn(y_batch_train, logits)

    # Use the gradient tape to automatically retrieve
    # the gradients of the trainable variables with respect to the loss.
    grads = tape.gradient(loss_value, model2.trainable_weights)

    # Run one step of gradient descent by updating
    # the value of the variables to minimize the loss.
    optimizer.apply_gradients(zip(grads, model2.trainable_weights))

    # Log every 200 batches.
    if step % 200 == 0:
        print('Training loss (for one batch) at step %s: %s' % (step, float(loss_value)))
        print('Seen so far: %s samples' % ((step + 1) * 64))
        
Start of epoch 0
Training loss (for one batch) at step 0: 0.20812422037124634
Seen so far: 64 samples
Training loss (for one batch) at step 200: 0.00012190269626444206
Seen so far: 12864 samples
Start of epoch 1
Training loss (for one batch) at step 0: 0.0007948163547553122
Seen so far: 64 samples
Training loss (for one batch) at step 200: 0.000352168339304626
Seen so far: 12864 samples
Start of epoch 2
Training loss (for one batch) at step 0: 0.00015973587869666517
Seen so far: 64 samples
Training loss (for one batch) at step 200: 0.0002913200587499887
Seen so far: 12864 samples
Start of epoch 3
Training loss (for one batch) at step 0: 0.0004951511509716511
Seen so far: 64 samples
Training loss (for one batch) at step 200: 8.653287659399211e-05
Seen so far: 12864 samples
Start of epoch 4
Training loss (for one batch) at step 0: 6.681459490209818e-05
Seen so far: 64 samples
Training loss (for one batch) at step 200: 7.516606274293736e-05
Seen so far: 12864 samples
Start of epoch 5
Training loss (for one batch) at step 0: 0.0003778417012654245
Seen so far: 64 samples
Training loss (for one batch) at step 200: 0.00010433907300466672
Seen so far: 12864 samples
Start of epoch 6
Training loss (for one batch) at step 0: 0.00010343497706344351
Seen so far: 64 samples
Training loss (for one batch) at step 200: 8.243530464824289e-05
Seen so far: 12864 samples
Start of epoch 7
Training loss (for one batch) at step 0: 8.312951831612736e-05
Seen so far: 64 samples
Training loss (for one batch) at step 200: 8.677344885654747e-05
Seen so far: 12864 samples
Start of epoch 8
Training loss (for one batch) at step 0: 0.0006261839298531413
Seen so far: 64 samples
Training loss (for one batch) at step 200: 0.00012080401938874274
Seen so far: 12864 samples
Start of epoch 9
Training loss (for one batch) at step 0: 7.248859765240923e-05
Seen so far: 64 samples
Training loss (for one batch) at step 200: 7.007949170656502e-05
Seen so far: 12864 samples

Predict individual values¶

In [10]:
predictions2 = model2.predict(data2)
x = data2[0]

x1 = x[0,]
y1 = x[1,]
print(" point to test")
print (x[0,], "  ",x[1,])
#x.shape
#print(x.shape, data2.shape)

## Analytical solution:
res = math.cos(0.5 * math.pi * x1) * math.cos(0.5 * math.pi * y1)
print("NN prediction: " , predictions2[0], "Analytical solution", res, "difference" ,abs(predictions2[0]-res))
313/313 [==============================] - 0s 793us/step
 point to test
-0.7142146426558194    -0.8596113282000879
NN prediction:  [0.09218344] Analytical solution 0.0949293054604226 difference [0.00274587]
In [ ]: