Because neural network code libraries (TensorFlow, Keras, PyTorch) constantly change, I redo a set of basic examples every few months to make sure no breaking changes have been introduced. A few days ago, I coded up an example of the Iris Dataset problem using the latest version of PyTorch. So, I figured I’d revisit the Iris problem using the current version of Keras/TensorFlow (version 2.8) on Windows 11.
I installed TensorFlow + Keras for Windows CPU using the .whl file at https://pypi.org/project/tensorflow-cpu/#files — tensorflow_cpu-2.8.0-cp37-cp37m-win_amd64.whl (because I was using Anaconda 2020.02 with Python 3.7.6).

Side-by-side comparison of PyTorch 1.10 (left) and Keras 2.8 (right). Very similar results.
See https://jamesmccaffreyblog.com/2022/04/20/the-iris-dataset-example-with-pytorch-1-10-on-windows-11/ for the Iris data used and the PyTorch code.
I implemented the Keras code to try and make it as similar as possible to my PyTorch version. I couldn’t replicate the PyTorch version exactly, but I got pretty close. The Keras neural network is 4-7-3 with tanh() hidden activation and softmax() output activation. I used uniform weight initialization and zero bias initialization. For training, I used a batch size of 4, and stochastic gradient descent with a learning rate = 0.01.
Overall, I slightly prefer using PyTorch over Keras, but I like both libraries. PyTorch works at a lower level which means you have to write a bit more code than when using Keras. But PyTorch is much more flexible than Keras. Because Keras is essentially a wrapper over TensorFlow, you can get maximum flexibility by using TensorFlow directly — but I much prefer using PyTorch to TensorFlow.
It’s impossible to get good statistics, but it’s my strong impression that the use of PyTorch is increasing more rapidly than the use of Keras/TensorFlow. PyTorch and Keras/TensorFlow are different enough that it’s usually not feasible to use both in a work environment, so all my colleagues have picked one or the other — I’d estimate about 70% of the groups I work with use PyTorch and 30% use Keras/TensorFlow.

Keras, TensorFlow, and PyTorch neural networks use different styles. Movies can have different styles too. Here are three of my favorite movie versions of “Treasure Island” written by Robert Lewis Stevenson in 1883. Left: The 1950 version with Robert Newton as Long John Silver. Center: The 1934 version with Wallace Beery as Silver. Right: The 1996 Muppet version with Tim Curry as Silver.
Keras demo code:
# iris_tfk.py
# Anaconda3-2020.02 (Python 3.7.6)
# TensorFlow 2.8.0 (includes KerasTF 2.8.0)
# Windows 10/11
import os
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'
import numpy as np
import tensorflow as tf
from tensorflow import keras as K
# -----------------------------------------------------------
class MyLogger(K.callbacks.Callback):
def __init__(self, n):
self.n = n # print loss & acc every n epochs
def on_epoch_end(self, epoch, logs={}):
if epoch % self.n == 0:
curr_loss = logs.get('loss')
curr_acc = logs.get('accuracy') * 100
print("epoch = %4d | loss = %0.6f | acc = %0.2f%%" % \
(epoch, curr_loss, curr_acc))
# -----------------------------------------------------------
def main():
# 0. get started
print("\nBegin Iris dataset using Keras/TensorFlow ")
np.random.seed(1)
tf.random.set_seed(1)
print("\nLoading Iris train and test data into memory ")
# 5.0, 3.5, 1.3, 0.3, 0
# setosa = 0, versicolor = 1, virginica = 2
# 1. load data
train_file = ".\\Data\\iris_train.txt" # ordinal encoded
train_x = np.loadtxt(train_file, usecols=[0,1,2,3],
delimiter=",", comments="#", dtype=np.float32)
train_y = np.loadtxt(train_file, usecols=[4],
delimiter=",", comments="#", dtype=np.int64)
train_y = K.utils.to_categorical(train_y) # to one-hot
test_file = ".\\Data\\iris_test.txt"
test_x = np.loadtxt(test_file, usecols=[0,1,2,3],
delimiter=",", comments="#", dtype=np.float32)
test_y = np.loadtxt(test_file, usecols=[4],
delimiter=",", comments="#", dtype=np.int64)
test_y = K.utils.to_categorical(test_y)
# -----------------------------------------------------------
# 2. create network
print("\nCreating 4-7-3 neural network ")
net = K.models.Sequential()
initer = K.initializers.RandomUniform(-0.01, +0.01)
net.add(K.layers.Dense(units=7, input_dim=4,
activation='tanh', kernel_initializer=initer,
bias_initializer='zeros'))
net.add(K.layers.Dense(units=3, activation='softmax',
kernel_initializer=initer, bias_initializer='zeros'))
opt = K.optimizers.SGD(learning_rate=0.01)
net.compile(loss='categorical_crossentropy',
optimizer=opt, metrics=['accuracy'])
my_logger = MyLogger(n=10)
# -----------------------------------------------------------
# 3. train model
print("\nbat_size = 4 ")
print("loss = categorical_crossentropy ")
print("optimizer = SGD")
print("max_epochs = 50 ")
print("lrn_rate = 0.010 ")
print("\nStarting training ")
h = net.fit(train_x, train_y, batch_size=4,
epochs=50, verbose=0, callbacks=[my_logger]) # 1 = chatty
print("Done ")
# -----------------------------------------------------------
# 4. evaluate model accuracy
print("\nComputing model accuracy")
eval = net.evaluate(test_x, test_y, verbose=0)
print("Accuracy on test data: %0.4f " % eval[1] )
# -----------------------------------------------------------
# 4b. inspect model
# np.set_printoptions(precision=4, suppress=True)
# wts = net.get_weights()
# print("\nTrained model weights and biases: ")
# for ar in wts:
# print(ar)
# print("")
# 5. make a prediction
print("\nPredicting species for [6.1, 3.1, 5.1, 1.1]: ")
np.set_printoptions(precision=4)
X = np.array([[6.1, 3.1, 5.1, 1.1]], dtype=np.float32)
probs = net.predict(X)
print(probs) # pseudo-probs
# 6. save model (full model approach)
print("\nSaving trained model ")
# net.save_weights(".\\Models\\iris_model.h5")
# net.save(".\\Models\\iris_model.h5")
print("\nEnd Iris demo")
if __name__=="__main__":
main()
Training data:
# iris_train.txt # # https://archive.ics.uci.edu/ml/datasets/iris # sepal len, sepal wid, petal len, petal wid, species # 0 = setosa, 1 = versicolor, 2 = virginica # 5.1, 3.5, 1.4, 0.2, 0 4.9, 3.0, 1.4, 0.2, 0 4.7, 3.2, 1.3, 0.2, 0 4.6, 3.1, 1.5, 0.2, 0 5.0, 3.6, 1.4, 0.2, 0 5.4, 3.9, 1.7, 0.4, 0 4.6, 3.4, 1.4, 0.3, 0 5.0, 3.4, 1.5, 0.2, 0 4.4, 2.9, 1.4, 0.2, 0 4.9, 3.1, 1.5, 0.1, 0 5.4, 3.7, 1.5, 0.2, 0 4.8, 3.4, 1.6, 0.2, 0 4.8, 3.0, 1.4, 0.1, 0 4.3, 3.0, 1.1, 0.1, 0 5.8, 4.0, 1.2, 0.2, 0 5.7, 4.4, 1.5, 0.4, 0 5.4, 3.9, 1.3, 0.4, 0 5.1, 3.5, 1.4, 0.3, 0 5.7, 3.8, 1.7, 0.3, 0 5.1, 3.8, 1.5, 0.3, 0 5.4, 3.4, 1.7, 0.2, 0 5.1, 3.7, 1.5, 0.4, 0 4.6, 3.6, 1.0, 0.2, 0 5.1, 3.3, 1.7, 0.5, 0 4.8, 3.4, 1.9, 0.2, 0 5.0, 3.0, 1.6, 0.2, 0 5.0, 3.4, 1.6, 0.4, 0 5.2, 3.5, 1.5, 0.2, 0 5.2, 3.4, 1.4, 0.2, 0 4.7, 3.2, 1.6, 0.2, 0 4.8, 3.1, 1.6, 0.2, 0 5.4, 3.4, 1.5, 0.4, 0 5.2, 4.1, 1.5, 0.1, 0 5.5, 4.2, 1.4, 0.2, 0 4.9, 3.1, 1.5, 0.1, 0 5.0, 3.2, 1.2, 0.2, 0 5.5, 3.5, 1.3, 0.2, 0 4.9, 3.1, 1.5, 0.1, 0 4.4, 3.0, 1.3, 0.2, 0 5.1, 3.4, 1.5, 0.2, 0 7.0, 3.2, 4.7, 1.4, 1 6.4, 3.2, 4.5, 1.5, 1 6.9, 3.1, 4.9, 1.5, 1 5.5, 2.3, 4.0, 1.3, 1 6.5, 2.8, 4.6, 1.5, 1 5.7, 2.8, 4.5, 1.3, 1 6.3, 3.3, 4.7, 1.6, 1 4.9, 2.4, 3.3, 1.0, 1 6.6, 2.9, 4.6, 1.3, 1 5.2, 2.7, 3.9, 1.4, 1 5.0, 2.0, 3.5, 1.0, 1 5.9, 3.0, 4.2, 1.5, 1 6.0, 2.2, 4.0, 1.0, 1 6.1, 2.9, 4.7, 1.4, 1 5.6, 2.9, 3.6, 1.3, 1 6.7, 3.1, 4.4, 1.4, 1 5.6, 3.0, 4.5, 1.5, 1 5.8, 2.7, 4.1, 1.0, 1 6.2, 2.2, 4.5, 1.5, 1 5.6, 2.5, 3.9, 1.1, 1 5.9, 3.2, 4.8, 1.8, 1 6.1, 2.8, 4.0, 1.3, 1 6.3, 2.5, 4.9, 1.5, 1 6.1, 2.8, 4.7, 1.2, 1 6.4, 2.9, 4.3, 1.3, 1 6.6, 3.0, 4.4, 1.4, 1 6.8, 2.8, 4.8, 1.4, 1 6.7, 3.0, 5.0, 1.7, 1 6.0, 2.9, 4.5, 1.5, 1 5.7, 2.6, 3.5, 1.0, 1 5.5, 2.4, 3.8, 1.1, 1 5.5, 2.4, 3.7, 1.0, 1 5.8, 2.7, 3.9, 1.2, 1 6.0, 2.7, 5.1, 1.6, 1 5.4, 3.0, 4.5, 1.5, 1 6.0, 3.4, 4.5, 1.6, 1 6.7, 3.1, 4.7, 1.5, 1 6.3, 2.3, 4.4, 1.3, 1 5.6, 3.0, 4.1, 1.3, 1 5.5, 2.5, 4.0, 1.3, 1 6.3, 3.3, 6.0, 2.5, 2 5.8, 2.7, 5.1, 1.9, 2 7.1, 3.0, 5.9, 2.1, 2 6.3, 2.9, 5.6, 1.8, 2 6.5, 3.0, 5.8, 2.2, 2 7.6, 3.0, 6.6, 2.1, 2 4.9, 2.5, 4.5, 1.7, 2 7.3, 2.9, 6.3, 1.8, 2 6.7, 2.5, 5.8, 1.8, 2 7.2, 3.6, 6.1, 2.5, 2 6.5, 3.2, 5.1, 2.0, 2 6.4, 2.7, 5.3, 1.9, 2 6.8, 3.0, 5.5, 2.1, 2 5.7, 2.5, 5.0, 2.0, 2 5.8, 2.8, 5.1, 2.4, 2 6.4, 3.2, 5.3, 2.3, 2 6.5, 3.0, 5.5, 1.8, 2 7.7, 3.8, 6.7, 2.2, 2 7.7, 2.6, 6.9, 2.3, 2 6.0, 2.2, 5.0, 1.5, 2 6.9, 3.2, 5.7, 2.3, 2 5.6, 2.8, 4.9, 2.0, 2 7.7, 2.8, 6.7, 2.0, 2 6.3, 2.7, 4.9, 1.8, 2 6.7, 3.3, 5.7, 2.1, 2 7.2, 3.2, 6.0, 1.8, 2 6.2, 2.8, 4.8, 1.8, 2 6.1, 3.0, 4.9, 1.8, 2 6.4, 2.8, 5.6, 2.1, 2 7.2, 3.0, 5.8, 1.6, 2 7.4, 2.8, 6.1, 1.9, 2 7.9, 3.8, 6.4, 2.0, 2 6.4, 2.8, 5.6, 2.2, 2 6.3, 2.8, 5.1, 1.5, 2 6.1, 2.6, 5.6, 1.4, 2 7.7, 3.0, 6.1, 2.3, 2 6.3, 3.4, 5.6, 2.4, 2 6.4, 3.1, 5.5, 1.8, 2 6.0, 3.0, 4.8, 1.8, 2 6.9, 3.1, 5.4, 2.1, 2
Test data:
# iris_test.txt # # https://archive.ics.uci.edu/ml/datasets/iris # sepal len, width, petal len, width # setosa = 0, versicolor = 1, virginica = 2 # 5.0, 3.5, 1.3, 0.3, 0 4.5, 2.3, 1.3, 0.3, 0 4.4, 3.2, 1.3, 0.2, 0 5.0, 3.5, 1.6, 0.6, 0 5.1, 3.8, 1.9, 0.4, 0 4.8, 3.0, 1.4, 0.3, 0 5.1, 3.8, 1.6, 0.2, 0 4.6, 3.2, 1.4, 0.2, 0 5.3, 3.7, 1.5, 0.2, 0 5.0, 3.3, 1.4, 0.2, 0 5.5, 2.6, 4.4, 1.2, 1 6.1, 3.0, 4.6, 1.4, 1 5.8, 2.6, 4.0, 1.2, 1 5.0, 2.3, 3.3, 1.0, 1 5.6, 2.7, 4.2, 1.3, 1 5.7, 3.0, 4.2, 1.2, 1 5.7, 2.9, 4.2, 1.3, 1 6.2, 2.9, 4.3, 1.3, 1 5.1, 2.5, 3.0, 1.1, 1 5.7, 2.8, 4.1, 1.3, 1 6.7, 3.1, 5.6, 2.4, 2 6.9, 3.1, 5.1, 2.3, 2 5.8, 2.7, 5.1, 1.9, 2 6.8, 3.2, 5.9, 2.3, 2 6.7, 3.3, 5.7, 2.5, 2 6.7, 3.0, 5.2, 2.3, 2 6.3, 2.5, 5.0, 1.9, 2 6.5, 3.0, 5.2, 2.0, 2 6.2, 3.4, 5.4, 2.3, 2 5.9, 3.0, 5.1, 1.8, 2
.NET Test Automation Recipes
Software Testing
SciPy Programming Succinctly
Keras Succinctly
R Programming
2026 Visual Studio Live
2025 Summer MLADS Conference
2026 DevIntersection Conference
2025 Machine Learning Week
2025 Ai4 Conference
2026 G2E Conference
2026 iSC West Conference
You must be logged in to post a comment.