1
\$\begingroup\$

This is my attempt to write a numpy-optimized version of a nearest centroid classifier to classify some images from the MNIST data set of handwritten digits. I am somewhat new to numpy and was surprised by how succinctly this code could be written with the help of broadcasting and vectorized operations, but was wondering if I was still missing some possibly important improvements.


So first, the starting data are a set of 10000 training images and 1000 test images, each of which are represented as a vector of 784 pixels (corresponding to a 28x28 grayscale image). Normally these would be actual MNIST images of handwritten digits, but for simplicity, here are some "dummy" data:

import numpy as np

train_data = np.random.randint(256, size=(784, 10000), dtype="uint8")
test_data = np.random.randint(256, size=(784, 1000), dtype="uint8")

Note that the way the data is (would be, if it was the real data) arranged is such that the first tenth of each data set is a 0, the next tenth is a 1, and so on through 9 (this is important for how the data gets processed below).

Now, to process the data, I compute the mean for each digit in the training set, classify each image in both the training and test sets, and then compute and output both the training and test accuracies:

train_means = np.mean(train_data.reshape(784, 10, 1000), axis=2)

train_classes, test_classes = (np.argmin(np.sum(np.square(data[:,:,np.newaxis] - train_means[:,np.newaxis,:]), axis=0), axis=1) for data in (train_data, test_data))

train_acc, test_acc = (np.mean(np.equal(classes, np.repeat(np.arange(10), classes.size // 10))) for classes in (train_classes, test_classes))
print("training accuracy: {}\n testing accuracy: {}".format(train_acc, test_acc))

How can the above code be improved in terms of (1) efficiency, (2) memory, and (3) style / simplicity?

For instance, one concern I have is that the subtraction data[:,:,np.newaxis] - train_means[:,np.newaxis,:] creates a (784, 10000, 10) array, which is more memory than I need to use. Could I avoid allocating this much memory while not sacrificing any efficiency (and ideally any simplicity of code)?

Another concern is the comprehension I use to apply the procedures to both the training data and testing data. Would that be encouraged or considered convoluted (or maybe this is just irrelevant personal preference)?

Also: in general, is the code too condensed?

\$\endgroup\$
  • \$\begingroup\$ Please comment on question deficiencies after downvoting :) \$\endgroup\$ – Grayscale Oct 3 '19 at 5:39
  • 5
    \$\begingroup\$ Maybe the downvoter didn't see how to run this piece of code, and where the input comes from. A more detailed project setup description would be nice, in order to reproduce your results, and to demonstrate that this code is a complete program. \$\endgroup\$ – Roland Illig Oct 3 '19 at 6:11
  • \$\begingroup\$ I didn't downvote, but there's one sure thing that'd make your code better and it would be to split it on more than 3 lines. Right now, I'd guess people downvote your post because there's so little code and it's usually a sign of a bad question. \$\endgroup\$ – IEatBagels Oct 3 '19 at 12:52
  • 1
    \$\begingroup\$ @IEatBagels The program is not meant to be too complicated, but definitely if you think that it is too condensed and would be better split into more lines, that could be a helpful part of an answer. Hopefully the code being bad as in the question does not itself make the question bad... otherwise there would not seem to be much purpose in asking a good question! \$\endgroup\$ – Grayscale Oct 23 '19 at 18:53
  • 1
    \$\begingroup\$ @Grayscale No, don't worry. The code being bad (not necessarily yours) doesn't mean the question is bad :) I was supposing, in this particular case, but bad code shouldn't mean bad question, it's a code review after all! \$\endgroup\$ – IEatBagels Oct 24 '19 at 12:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.