PyTorch Introductory Experiments

by Trevor Lee in Design > Software

28 Views, 1 Favorites, 0 Comments

PyTorch Introductory Experiments

pytorch-experiments-smaller.png

In this project, I will revisit some of my previous basic TensorFlow Deep Learning experiments / training exercises

  1. Trying Out TensorFlow Lite Hello World Model With ESP32 and DumbDisplay
  2. Mnist Dataset -- From Training to Running With ESP32 / ESP32S3
  3. Sliding Puzzle 'Next Move' Suggesting Simple DL Model With ESP32 TensorFlow Lite

Nevertheless this time, those experiments will be retried (reimplemented)

  1. The experiments will be using PyTorch as the deep learning framework (and still be with "dense" layers only)
  2. They will not be targeted for microcontroller; but still be demonstrable, with the help of DumbDisplay using regular Python and PyTorch

Installation With VSCode

This project is developed with VSCode (with Python extension); here, I will assume VSCode development environment as well.

To try along, please clone this project -- PyTorchIntroductoryExperiments -- from GitHub to your local machine, and open the folder with VSCode.

Create a virtual environment by selecting the command Python: Select Interpreter from the command palette and choose to create a new virtual environment.

In the process, you will be given an option to also install the required dependent packages specified in requirements.txt file. In case you missed installing those dependent package, you still can install them, including MicroPython DumbDisplay Library, with an opened terminal (virtual environment activated) by running pip like

pip install -r requirements.txt

I have tested the Python code of the project to work with Python 3.12.8

For older version of Python, when installing the packages, you might see error like

ModuleNotFoundError: No module named 'setuptools.config.expand'; 'setuptools.config' is not a package

try upgrade pip like

python -m pip install --upgrade pip

then run

pip install -r requirements.txt

again.

When open the Jupyter Notebook, the needed components will be installed when necessary.

"Hello World" Deep Learning Model Training

sine_chart.png
sine_cosine_chart.png

The "Hello World" of Deep Learning here refers to the training of a DL model for the sine Mathematical function -- train_sine.ipynb.

I guess modeling the Mathematical function sine is considered the "Hello World" of Deep Learning because

  1. The training data is very easy to generate.
  2. The architecture of the model can simply be just a few "dense" layers.
  3. When it comes to implementation, the Mathematical function sine might not be as trival as it sounds, and therefore a good demonstration of the Deep Learning "magic".

Moreover, just for fun, I extended the architecture of the sine model to output cosine values at the same time -- train_sine_cosine.ipynb.

Highlights of "Hello World" DL Training

The target of this "Hello World" model is the sine Mathematic function for the input range from 0 to 2π (0° to 360°).

Hence, to create the data for the training the model, can generate randomized data like

x_values = np.random.uniform(low=0, high=2*math.pi, size=SAMPLES)
np.random.shuffle(x_values)
y_values = np.sin(x_values)

Since the default datatype for PyTorch is float32, first convert the data to float32, and reshape them as

x_values = x_values.astype(np.float32).reshape(-1, 1)
y_values = y_values.astype(np.float32).reshape(-1, 1)

Notes:

  1. x_values is a two-dimensional matrix, with a single column, as the input to the model is a single value (the input angle in radian)
  2. y_values is also a two-dimensional matrix, with a single column, as the output of the model is also a single value (the sine value of the input angle)

Then, 70% of the data will be treated as "train" data set, and the rest is treated as "test" data set

train_split = int(0.7 * SAMPLES)

x_train, x_test = np.split(x_values, [train_split])
y_train, y_test = np.split(y_values, [train_split])


... please refer to Highlights of train_sine.ipynb and Highlights of train_sine_cosine.ipynb for more

Mnist Dataset DL Training and Demo App

mnist-test.png

The popular Mnist dataset is also frequently used as introductory demonstration to Deep Learning -- train_mnist.ipynb.

In this project, not only will a simple DL training of Mnist dataset be presented, demonstration UI will be realized wireless on your Android mobile phone with the help of DumbDisplay -- start_dd_mnist.py

UI is coded with (and driven by) Python using MicroPython DumbDisplay Library package; and is realized wirelessly on your Android mobile phone with DumbDisplay Android App

Nowadays, code generation with AI is a common practice. Indeed, I did prompt LLM to generate a Jupyter Notebook to train a PyTorch DL model for the Mnist dataset -- train_mnist_ai.ipynb.

The AI-generated DL model is more complex (and certainly more standard) than the one presented in train_mnist.ipynb. At least, mine basically only involves "dense" layers, while the AI-generated one involves "convolutional" layers.

The actual model architecture (the simple version) is captured by the class MnistModel defined in the file model_mnist.py

class MnistModel(nn.Module):
.. def __init__(self):
... super().__init__()
... self.linear_relu_stack = nn.Sequential(
.... nn.Linear(784, 64),
.... nn.ReLU(),
.... nn.Dropout(0.3),
.... nn.Linear(64, 64),
.... nn.ReLU(),
.... nn.Linear(64, 10)
... )

.. def forward(self, x):
... logits = self.linear_relu_stack(x)
... return F.log_softmax(logits, dim=1)

Please refer to train_mnist.ipynb for the complete training exercise.

Demo UI for the Trained Mnist DL Model

dd_connect_wifi_00.jpg
dd_connect_wifi_01.jpg
dd_connect_wifi_02.jpg
dd_connect_wifi_03.jpg

The Python script start_dd_mnist.py starts the DumbDisplay program which uses MicroPython DumbDisplay Library to drive a wireless UI on your Android mobile phone with DumbDisplay Android App.

After starting the Python script start_dd_mnist.py, you should see from VSCode terminal that it waits for connection from DumbDisplay Android App.

**********
*****
*** reading model output/mnist_model.pth ...
*****
**********



**********
*****
*** starting mnist with model output/mnist_model.pth ...
*****
**********

connecting socket ... listing on 192.168.0.46:10201 ...

You can open the DumbDisplay Android App installed in your Android phone -- DumbDisplay Android App -- and make connection (as shown by above screen-shots)


When connected, the VSCode should show something like

... connected 192.168.0.46:10201 from ('192.168.0.98', 41578)

Demo UI for the Trained Mnist DL Model (CONT)

dd-mnist.jpg

Once connected, on the [black] canvas of the UI, draw a dight you want the DL model to recognize, like the digit 8, and press the >>> button to trigger inference of the drawn digit data (stored in the memory of the running Python process)

The inference with PyTorch is actually performed by the following Python function mnist_inference

def mnist_inference(inference_data, model) -> int:
. try:
.. x = np.array(inference_data).reshape((1, 784))
.. torch_x = torch.tensor(x, dtype=torch.float32).reshape(1, 784)
.. output = model(torch_x)
.. pred = output.argmax()
.. return pred.item()
. except Exception as e:
.. print(f"XXX error during inference: {e}")
.. raise e

Note that mnist_inference is just a callback function for the DumbDisplay "driver" Python code implemented as an example of MicroPython DumbDisplay Library

Now, draw the digit 9 on the canvas ...

If you want to clear what have drawn, press the clear button.

The center button toggles whether the drawn digit will be auto centered before calling mnist_inference for inference.

It is interesting to see that even without auto-centering, the digit recognition is pretty good, especially with the AI generated model -- start_dd_mnist_ai.py.

Sliding Puzzle DL Training and Demo App

sliding-puzzle.jpg

The DL model presented in this project for the classical Sliding Puzzle game is a simple and naive "next move" suggesting model -- train_sliding_puzzle.ipynb

I came up with this simple and naive "next move" suggesting model by referencing to the above-mentioned "Hello World" and Mnist DL models, just for fun.

Say, for a 4x4 board.

  1. There will be 16 tiles, with the 0th tile being the empty space; e.g. the board "orientation" can be represented as
| 0 | 1 | 2 | 3 |
| 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 |
  1. With respect to the empty space (i.e. the 0th tile), there can be 4 possible moves (but some might be invalid)
  2. 0: from left
  3. 1: from right
  4. 2: from top
  5. 3: from bottom
  6. For example, the above solved board can be randomized by a single step of the move 1 (from right) to
| 1 | 0 | 2 | 3 |
| 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 |
  1. The "next move" of this randomized board is apparently an "undo" move to undo the randomization step, in this case, the move 0 (from left)
  2. 1 => undo with 0
  3. 0 => undo with 1
  4. 2 => undo with 3
  5. 3 => undo with 2
  6. In other words, the "undo" moves are the "next moves" toward solving a randomized board
  7. The way to capture the "undo" moves can be as easy as
  8. during a randomization step, a valid "move" is select
  9. the "undo" move is recorded as the "next move" for the randomized board "orientation"
  10. so, for a board randomized by 5 steps, there will be 5 "next moves" recorded -- one for each board "orientation"
  11. The board "orientations" is the input to the DL model. The "undo" moves is the output of the DL model.

The actual model architecture is captured by the class SlidingPuzzleModel defined in the file model_sliding_puzzle.py

class SlidingPuzzleModel(nn.Module):
. def __init__(self, tile_count):
.. super().__init__()
.. self.linear_relu_stack = nn.Sequential(
... nn.Linear(tile_count * tile_count, 256),
... nn.ReLU(),
... nn.Dropout(0.3),
... nn.Linear(256, 256),
... nn.ReLU(),
... nn.Linear(256, 4)
.. )

def forward(self, x):
. logits = self.linear_relu_stack(x)
. return F.softmax(logits, dim=1)

Please refer to train_sliding_puzzle.ipynb for the complete training exercise, which is a bit more involving, mostly because generation of the training data.

Demo UI for the Trained Sliding Puzzle Model

dd-sliding-puzzle.jpg

The demo UI Python script this time is start_dd_sliding_puzzle.py, which is mostly based on the example of MicroPython DumbDisplay Library

After starting the Python script start_dd_sliding_puzzle.py, you should see from VSCode terminal that it waits for connections from the DumbDisplay Android App.

**********
*****
*** reading model output/sp_model_4.pth ...
*****
**********



**********
*****
*** starting sliding puzzle with model output/sp_model_4.pth ...
*****
**********

connecting socket ... listing on 192.168.0.46:10201 ...

You can open the DumbDisplay Android App and make connection like previously.


Once connect, double-press the sliding puzzle game board to randomize it by 5 steps. You can try to solve the puzzle manually by moving / sliding the appropriate tile to the empty space. If stuck, press the suggest button to have the DL model suggest the "next move" for you.


When "next move" is needed, the following Python function suggest_next_move will be called

def suggest_next_move(board_manager: BoardManager, model, tile_count) -> int:
. x = torch.tensor([board_manager.board_tiles], dtype=torch.float32) / (tile_count * tile_count)
. prediction = model(x)
. move_ans = prediction.argmax(dim=1).item()
. return move_ans


Messed up or not, double-press the 🔄 Reset 🔄 button to reset the game; but this time, press the continuous button to have "next move" suggested [and made] continuously.

If things go well, the game should be solved in 5 suggested "next moves".

Once solved, double-press the board to randomize it again; but this time, it will be randomized by 10 steps (5 more than last time).

Once solved again, double-press the board again ...

It is interesting to see how randomized the board is, the DL model can still suggest the correct "next moves" to solve it. My experience is, around 15 to 20 randomize steps.

If you are interested, try tuning the model to see if it can achieve more randomize steps!

Have Fun!

Peace be with you! May God bless you! Jesus loves you! Amazing Grace!