The Iris Dataset Example with PyTorch 1.10 on Windows 11

The field of machine learning changes continuously. There are new algorithms and library updates every few weeks. I have a set of standard examples I run every now and then to make sure that no breaking changes have occurred in PyTorch. One of my machines was upgraded from Windows 10 to Windows 11, and PyTorch 1.10.x had been released, so I figured I’d better run my Iris Dataset example.

Note: code slighlty updated on 04/22/2022

The goal of the Iris problem is to predict the species of an iris flower (setosa = 0, versicolor = 1, virginica = 2) from sepal length and width, and petal length and width. A sepal is a leaf-like structure.

My demo worked as expected. The PyTorch code is very dense, meaning there are a lot of alternatives for each block of statements. For example, I use uniform_(-0.01, +0.01) weight initialization. There are many alternatives including normal_(), xavier_uniform_(), and kaiming_uniform_(). I use log_softmax() activation with NLLLoss() loss. An equivalent design is no activation (sometimes called identity activation) with CrossEntropyLoss(). And so on, and so on, and so on.

# iris_nn.py
# PyTorch 1.10.0-CPU Anaconda3-2020.02  Python 3.7.6
# Windows 10/11 

import numpy as np
import torch as T
device = T.device('cpu')  # apply to Tensor or Module

# -----------------------------------------------------------

class IrisDataset(T.utils.data.Dataset):
  def __init__(self, src_file, num_rows=None):
    # 5.0, 3.5, 1.3, 0.3, 0
    tmp_x = np.loadtxt(src_file, max_rows=num_rows,
      usecols=range(0,4), delimiter=",", comments="#",
      dtype=np.float32)
    tmp_y = np.loadtxt(src_file, max_rows=num_rows,
      usecols=4, delimiter=",", comments="#",
      dtype=np.int64)

    self.x_data = T.tensor(tmp_x, dtype=T.float32).to(device)
    self.y_data = T.tensor(tmp_y, dtype=T.int64).to(device)

  def __len__(self):
    return len(self.x_data)

  def __getitem__(self, idx):
    preds = self.x_data[idx]
    spcs = self.y_data[idx] 
    sample = { 'predictors' : preds, 'species' : spcs }
    return sample  # as Dictionary

# -----------------------------------------------------------

class Net(T.nn.Module):
  def __init__(self):
    super(Net, self).__init__()  # Python 3.2 and earlier
    # super().__init__()  # shortcut syntax 3.3 and later
    self.hid1 = T.nn.Linear(4, 7)  # 4-7-3
    self.oupt = T.nn.Linear(7, 3)
    
    lo = -0.10; hi = +0.10
    T.nn.init.uniform_(self.hid1.weight, lo, hi)
    T.nn.init.zeros_(self.hid1.bias)
    T.nn.init.uniform_(self.oupt.weight, lo, hi)
    T.nn.init.zeros_(self.oupt.bias)
    
  def forward(self, x):
    z = T.tanh(self.hid1(x))
    z = T.log_softmax(self.oupt(z), dim=1)  # NLLLoss() 
    return z

# -----------------------------------------------------------

def accuracy(model, dataset):
  # assumes model.eval()
  dataldr = T.utils.data.DataLoader(dataset, batch_size=1,
    shuffle=False)
  n_correct = 0; n_wrong = 0
  for (_, batch) in enumerate(dataldr):
    X = batch['predictors'] 
    Y = batch['species']  # already 1D shaped by Dataset
    with T.no_grad():
      oupt = model(X)  # logits form

    big_idx = T.argmax(oupt)
    # if big_idx.item() == Y.item():
    if big_idx == Y:
      n_correct += 1
    else:
      n_wrong += 1

  acc = (n_correct * 1.0) / (n_correct + n_wrong)
  return acc

# -----------------------------------------------------------

def main():
  # 0. get started
  print("\nBegin Iris dataset using PyTorch demo ")
  T.manual_seed(1)
  np.random.seed(1)
  
  # 1. create DataLoader objects
  print("\nCreating Iris train and test Datasets ")

  train_file = ".\\Data\\iris_train.txt"  
  test_file = ".\\Data\\iris_test.txt"  

  train_ds = IrisDataset(train_file)  # 120 items
  test_ds = IrisDataset(test_file)    # 30 

  bat_size = 4
  train_ldr = T.utils.data.DataLoader(train_ds,
    batch_size=bat_size, shuffle=True)
  
# -----------------------------------------------------------

  # 2. create network
  print("\nCreating 4-7-3 neural network ")
  net = Net().to(device)

  # 3. train model
  max_epochs = 50
  ep_log_interval = 10
  lrn_rate = 0.01

  loss_func = T.nn.NLLLoss()  # assumes log_softmax()
  optimizer = T.optim.SGD(net.parameters(), lr=lrn_rate)

  print("\nbat_size = %3d " % bat_size)
  print("loss = " + str(loss_func))
  print("optimizer = SGD")
  print("max_epochs = %3d " % max_epochs)
  print("lrn_rate = %0.3f " % lrn_rate)

  print("\nStarting training")
  net.train()
  for epoch in range(0, max_epochs):
    epoch_loss = 0  # for one full epoch
    for (batch_idx, batch) in enumerate(train_ldr):
      X = batch['predictors']  # [10,4]
      Y = batch['species']  # OK; alreay flattened
      optimizer.zero_grad()
      oupt = net(X)
      loss_val = loss_func(oupt, Y)  # a tensor
      epoch_loss += loss_val.item()  # accumulate
      loss_val.backward()  # compute gradients
      optimizer.step()     # update weights and biases

    if epoch % ep_log_interval == 0:
      print("epoch = %4d  |  loss = %8.4f  | " % \
        (epoch, epoch_loss), end="")
      net.eval()
      train_acc = accuracy(net, train_ds)
      print(" acc = %8.4f " % train_acc)
      net.train()
  print("Done ")

# -----------------------------------------------------------

  # 4. evaluate model accuracy
  print("\nComputing model accuracy")
  net.eval()
  acc = accuracy(net, test_ds)  # item-by-item
  print("Accuracy on test data = %0.4f" % acc)

# -----------------------------------------------------------

  # 4b. inspect model
  # print("\nTrained model weights and biases: ")
  # print(net.hid1.weight)
  # print(net.hid1.bias)
  # print(net.oupt.weight)
  # print(net.oupt.bias)

# -----------------------------------------------------------
  
  # 5. make a prediction
  print("\nPredicting species for [6.1, 3.1, 5.1, 1.1]: ")
  x = np.array([[6.1, 3.1, 5.1, 1.1]], dtype=np.float32)
  x = T.tensor(x, dtype=T.float32).to(device) 

  with T.no_grad():
    logits = net(x)      # as log_softmax
  probs = T.exp(logits)    # pseudo-probs
  T.set_printoptions(precision=4)
  print(probs)

# -----------------------------------------------------------

  # 6. save model (state_dict approach)
  print("\nSaving trained model state")
  fn = ".\\Models\\iris_model.pt"
  # T.save(net.state_dict(), fn)

  # saved_model = Net()
  # saved_model.load_state_dict(T.load(fn))
  # use saved_model to make prediction(s)

  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, sepal wid, petal len, petal wid, species
# 0 = setosa, 1 = versicolor, 2 = virginica
#
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
This entry was posted in Miscellaneous, PyTorch. Bookmark the permalink.

1 Response to The Iris Dataset Example with PyTorch 1.10 on Windows 11

  1. Pingback: The Iris Dataset Example with Keras 2.8 on Windows 11 | James D. McCaffrey

Comments are closed.