From 6bdea63ab81b6b3dae4649478d29563e2160bc13 Mon Sep 17 00:00:00 2001 From: Martin Pitt Date: Fri, 28 Aug 2020 08:18:29 +0200 Subject: [PATCH] Read MNIST db into numpy arrays, display --- README.md | 6 ++++- mnist.py | 23 ++++++++++++++++++ read_display_mnist.py | 14 +++++++++++ screenshots/mnist-visualize-training-data.png | Bin 0 -> 23179 bytes 4 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 mnist.py create mode 100755 read_display_mnist.py create mode 100644 screenshots/mnist-visualize-training-data.png diff --git a/README.md b/README.md index ad14945..fa1c484 100644 --- a/README.md +++ b/README.md @@ -27,9 +27,13 @@ plt.imshow(grad, cmap='gray') plt.show() plt.imshow(np.sin(np.linspace(0,10000,10000)).reshape(100,100) ** 2, cmap='gray') -# does not work with QT_QPA_PLATFORM=wayland +# non-blocking does not work with QT_QPA_PLATFORM=wayland plt.show(block=False) plt.close() ``` - Get the handwritten digits training data with `./download-mnist.sh` + + - Read the MNIST database into numpy arrays with `./read_display_mnist.py`. Plot the first ten images and show their labels, to make sure the data makes sense: + + ![visualize training data](screenshots/mnist-visualize-training-data.png) diff --git a/mnist.py b/mnist.py new file mode 100644 index 0000000..1a3870f --- /dev/null +++ b/mnist.py @@ -0,0 +1,23 @@ +import struct + +import numpy as np + +def load(images_file, labels_file): + # see http://yann.lecun.com/exdb/mnist/ for the file format + with open(images_file, 'rb') as f: + # validate magic + assert struct.unpack('>I', f.read(4))[0] == 0x803 + num = struct.unpack('>I', f.read(4))[0] + rows = struct.unpack('>I', f.read(4))[0] + cols = struct.unpack('>I', f.read(4))[0] + images = np.frombuffer(f.read(), dtype=np.uint8, count = num * rows * cols) + # split them up into an array of flat image vectors, so that first axis corresponds to labels + images = images.reshape(num, rows * cols) + + with open(labels_file, 'rb') as f: + # validate magic + assert struct.unpack('>I', f.read(4))[0] == 0x801 + num = struct.unpack('>I', f.read(4))[0] + labels = np.frombuffer(f.read(), dtype=np.uint8, count=num) + + return images, labels, rows, cols diff --git a/read_display_mnist.py b/read_display_mnist.py new file mode 100755 index 0000000..fd85ee3 --- /dev/null +++ b/read_display_mnist.py @@ -0,0 +1,14 @@ +#!/usr/bin/python3 + +import numpy as np +import matplotlib.pyplot as plt + +import mnist + +train_images, train_labels, rows, cols = mnist.load('train-images-idx3-ubyte', 'train-labels-idx1-ubyte') + +# show the first bunch of training data +for i in range(10): + print(f'train image #{i}: label {train_labels[i]}') + plt.imshow(train_images[i].reshape(rows, cols), cmap='gray') + plt.show() diff --git a/screenshots/mnist-visualize-training-data.png b/screenshots/mnist-visualize-training-data.png new file mode 100644 index 0000000000000000000000000000000000000000..fd21a111da4c5587cad477b79d08e435af745d3c GIT binary patch literal 23179 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 qqrBd0sJq)5sutk?Q_z+)Kd7fv?spigv%|4{q;