You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

mnist.py 10 kB

1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. import numpy as np
  2. import time
  3. import os
  4. import collections
  5. import matplotlib.pyplot as plt
  6. import uctc.nn as nn
  7. from utils import parameter_data, Dataset
  8. use_graphics = True
  9. class DigitClassificationModel(object):
  10. """
  11. A model for handwritten digit classification using the MNIST dataset.
  12. Each handwritten digit is a 28x28 pixel grayscale image, which is flattened
  13. into a 784-dimensional vector for the purposes of this model. Each entry in
  14. the vector is a floating point number between 0 and 1.
  15. The goal is to sort each digit into one of 10 classes (number 0 through 9).
  16. (See RegressionModel for more information about the APIs of different
  17. methods here. We recommend that you implement the RegressionModel before
  18. working on this part of the project.)
  19. """
  20. def __init__(self):
  21. # Initialize your model parameters here
  22. "*** YOUR CODE HERE ***"
  23. self.input_features = 784
  24. self.h1 = 200
  25. self.h2 = 100
  26. self.output_features = 10
  27. self.lr = 0.01
  28. self.batch_size = 100
  29. self.w1 = nn.Parameter(parameter_data(self.input_features, self.h1))
  30. self.b1 = nn.Parameter(parameter_data(1, self.h1))
  31. self.w2 = nn.Parameter(parameter_data(self.h1, self.h2))
  32. self.b2 = nn.Parameter(parameter_data(1, self.h2))
  33. self.w3 = nn.Parameter(parameter_data(self.h2, self.output_features))
  34. self.b3 = nn.Parameter(parameter_data(1, self.output_features))
  35. def run(self, x):
  36. """
  37. Runs the model for a batch of examples.
  38. Your model should predict a node with shape (batch_size x 10),
  39. containing scores. Higher scores correspond to greater probability of
  40. the image belonging to a particular class.
  41. Inputs:
  42. x: a node with shape (batch_size x 784)
  43. Output:
  44. A node with shape (batch_size x 10) containing predicted scores
  45. (also called logits)
  46. """
  47. "*** YOUR CODE HERE ***"
  48. l1 = nn.ReLU(nn.AddBias(nn.Linear(x, self.w1), self.b1))
  49. l2 = nn.ReLU(nn.AddBias(nn.Linear(l1, self.w2), self.b2))
  50. l3 = nn.AddBias(nn.Linear(l2, self.w3), self.b3)
  51. return l3
  52. def get_loss(self, x, y):
  53. """
  54. Computes the loss for a batch of examples.
  55. The correct labels `y` are represented as a node with shape
  56. (batch_size x 10). Each row is a one-hot vector encoding the correct
  57. digit class (0-9).
  58. Inputs:
  59. x: a node with shape (batch_size x 784)
  60. y: a node with shape (batch_size x 10)
  61. Returns: a loss node
  62. """
  63. "*** YOUR CODE HERE ***"
  64. return nn.SoftmaxLoss(self.run(x), y)
  65. def train(self, dataset):
  66. """
  67. Trains the model.
  68. """
  69. "*** YOUR CODE HERE ***"
  70. while True:
  71. for x, y in dataset.iterate_once(self.batch_size):
  72. loss = self.get_loss(x, y)
  73. g_w1, g_b1, g_w2, g_b2, g_w3, g_b3 = nn.gradients(loss, [self.w1, self.b1, self.w2, self.b2, self.w3, self.b3])
  74. self.w1.update(g_w1, self.lr)
  75. self.b1.update(g_b1, self.lr)
  76. self.w2.update(g_w2, self.lr)
  77. self.b2.update(g_b2, self.lr)
  78. self.w3.update(g_w3, self.lr)
  79. self.b3.update(g_b3, self.lr)
  80. accuracy = dataset.get_validation_accuracy()
  81. print(accuracy)
  82. if accuracy > 0.95:
  83. break
  84. def get_data_path(filename):
  85. path = os.path.join(
  86. os.path.dirname(__file__), os.pardir, "data", filename)
  87. if not os.path.exists(path):
  88. path = os.path.join(
  89. os.path.dirname(__file__), "data", filename)
  90. if not os.path.exists(path):
  91. path = os.path.join(
  92. os.path.dirname(__file__), filename)
  93. if not os.path.exists(path):
  94. raise Exception("Could not find data file: {}".format(filename))
  95. return path
  96. class DigitClassificationDataset(Dataset):
  97. def __init__(self, model: DigitClassificationModel):
  98. mnist_path = get_data_path("mnist.npz")
  99. with np.load(mnist_path) as data:
  100. train_images = data["train_images"]
  101. train_labels = data["train_labels"]
  102. test_images = data["test_images"]
  103. test_labels = data["test_labels"]
  104. assert len(train_images) == len(train_labels) == 60000
  105. assert len(test_images) == len(test_labels) == 10000
  106. self.dev_images = np.array(test_images[0::2], copy=True)
  107. self.dev_labels = np.array(test_labels[0::2], copy=True)
  108. self.test_images = np.array(test_images[1::2], copy=True)
  109. self.test_labels = np.array(test_labels[1::2], copy=True)
  110. train_labels_one_hot = np.zeros((len(train_images), 10))
  111. train_labels_one_hot[range(len(train_images)), train_labels] = 1
  112. super().__init__(train_images, train_labels_one_hot)
  113. self.model = model
  114. self.epoch = 0
  115. if use_graphics:
  116. width = 20 # Width of each row expressed as a multiple of image width
  117. samples = 100 # Number of images to display per label
  118. fig = plt.figure()
  119. ax = {}
  120. images = collections.defaultdict(list)
  121. texts = collections.defaultdict(list)
  122. for i in reversed(range(10)):
  123. ax[i] = plt.subplot2grid((30, 1), (3 * i, 0), 2, 1,
  124. sharex=ax.get(9))
  125. plt.setp(ax[i].get_xticklabels(), visible=i == 9)
  126. ax[i].set_yticks([])
  127. ax[i].text(-0.03, 0.5, i, transform=ax[i].transAxes,
  128. va="center")
  129. ax[i].set_xlim(0, 28 * width)
  130. ax[i].set_ylim(0, 28)
  131. for j in range(samples):
  132. images[i].append(ax[i].imshow(
  133. np.zeros((28, 28)), vmin=0, vmax=1, cmap="Greens",
  134. alpha=0.3))
  135. texts[i].append(ax[i].text(
  136. 0, 0, "", ha="center", va="top", fontsize="smaller"))
  137. ax[9].set_xticks(np.linspace(0, 28 * width, 11))
  138. ax[9].set_xticklabels(
  139. ["{:.1f}".format(num) for num in np.linspace(0, 1, 11)])
  140. ax[9].tick_params(axis="x", pad=16)
  141. ax[9].set_xlabel("Probability of Correct Label")
  142. status = ax[0].text(
  143. 0.5, 1.5, "", transform=ax[0].transAxes, ha="center",
  144. va="bottom")
  145. plt.show(block=False)
  146. self.width = width
  147. self.samples = samples
  148. self.fig = fig
  149. self.images = images
  150. self.texts = texts
  151. self.status = status
  152. self.last_update = time.time()
  153. def iterate_once(self, batch_size):
  154. self.epoch += 1
  155. for i, (x, y) in enumerate(super().iterate_once(batch_size)):
  156. yield x, y
  157. if time.time() - self.last_update > 1:
  158. dev_logits = self.model.run(nn.Constant(self.dev_images)).tensor()
  159. # dev_logits = np.array(dev_logits_raw.data()).reshape(5000, 10)
  160. # dev_predicted = np.argmax(dev_logits, axis=1)
  161. dev_argmax = nn.argmax(dev_logits, axis=1)
  162. dev_predicted = np.array(dev_argmax.data())
  163. # sftmax = np.array(nn.log_softmax(nn.pyarray_to_tensor(dev_logits)).data()).reshape(5000, 10)
  164. sftmax = nn.log_softmax(dev_logits)
  165. dev_probs = np.array(nn.exp(sftmax).data()).reshape(5000, 10)
  166. dev_accuracy = np.mean(dev_predicted == self.dev_labels)
  167. print("epoch: {:d}, batch: {:d}/{:d}, validation accuracy: "
  168. "{:.2%}".format(
  169. self.epoch, i, len(self.x) // batch_size, dev_accuracy))
  170. if use_graphics:
  171. self.status.set_text(
  172. "epoch: {:d}, batch: {:d}/{:d}, validation accuracy: "
  173. "{:.2%}".format(
  174. self.epoch, i, len(self.x) // batch_size, dev_accuracy))
  175. for i in range(10):
  176. predicted = dev_predicted[self.dev_labels == i]
  177. probs = dev_probs[self.dev_labels == i][:, i]
  178. linspace = np.linspace(
  179. 0, len(probs) - 1, self.samples).astype(int)
  180. indices = probs.argsort()[linspace]
  181. for j, (prob, image) in enumerate(zip(
  182. probs[indices],
  183. self.dev_images[self.dev_labels == i][indices])):
  184. self.images[i][j].set_data(image.reshape((28, 28)))
  185. left = prob * (self.width - 1) * 28
  186. if predicted[indices[j]] == i:
  187. self.images[i][j].set_cmap("Greens")
  188. self.texts[i][j].set_text("")
  189. else:
  190. self.images[i][j].set_cmap("Reds")
  191. self.texts[i][j].set_text(predicted[indices[j]])
  192. self.texts[i][j].set_x(left + 14)
  193. self.images[i][j].set_extent([left, left + 28, 0, 28])
  194. self.fig.canvas.draw_idle()
  195. self.fig.canvas.start_event_loop(1e-3)
  196. self.last_update = time.time()
  197. def get_validation_accuracy(self):
  198. # print(self.dev_images[:2].tolist())
  199. dev_logits = self.model.run(nn.Constant(self.dev_images)).tensor()
  200. dev_predicted = np.array(nn.argmax(dev_logits, axis=1).data())
  201. dev_accuracy = np.mean(dev_predicted == self.dev_labels)
  202. return dev_accuracy
  203. model = DigitClassificationModel()
  204. dataset = DigitClassificationDataset(model)
  205. model.train(dataset)