tensorflow的MNIST教程

(ps:根据自己的理解,提炼了一下官方文档的内容,错误的地方希望大佬们多多指正。。。。。)

 

0x01:数据集的获取和表示

数据集的获取,可以通过代码自动下载。这里的数据就是各种手写数字图片和图片对应的标签(告诉我们这个数字是几,比如下面的是5,0,4,1)。

 

 

 下载下来的数据集被分成两部分:60000行的训练数据集(mnist.train)和10000行的测试数据集(mnist.test),而每一个数据集都有两部分组成:一张包含手写数字的图片(xs)和一个对应的标签(ys)。训练数据集和测试数据集都包含xs和ys,比如训练数据集的图片是 mnist.train.images ,训练数据集的标签是 mnist.train.labels。根据图片像素点把图片展开为向量,再进一步操作,识别图片上的数值。那这60000个训练数据集是怎么表示的呢?在MNIST训练数据集中,mnist.train.images 是一个形状为 [60000, 784] 的张量(至于什么是张量,小伙伴们可以手都搜一下,加深一下印象),第一个维度数字用来索引图片,第二个维度数字用来索引每张图片中的像素点。在此张量里的每一个元素,都表示某张图片里的某个像素的强度值,值介于0和1之间。

 

 

 0x02:代码运行

代码分为两部分,一个是用于下载数据的 input_data.py 另一个是主程序 mnist.py

  1 # Copyright 2015 Google Inc. All Rights Reserved.
  2 #
  3 # Licensed under the Apache License, Version 2.0 (the "License");
  4 # you may not use this file except in compliance with the License.
  5 # You may obtain a copy of the License at
  6 #
  7 #     http://www.apache.org/licenses/LICENSE-2.0
  8 #
  9 # Unless required by applicable law or agreed to in writing, software
 10 # distributed under the License is distributed on an "AS IS" BASIS,
 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 # See the License for the specific language governing permissions and
 13 # limitations under the License.
 14 # ==============================================================================
 15 """Functions for downloading and reading MNIST data."""
 16 from __future__ import absolute_import
 17 from __future__ import division
 18 from __future__ import print_function
 19 import gzip
 20 import os
 21 import tensorflow.python.platform
 22 import numpy
 23 from six.moves import urllib
 24 from six.moves import xrange  # pylint: disable=redefined-builtin
 25 import tensorflow as tf
 26 SOURCE_URL = 'http://yann.lecun.com/exdb/mnist/'
 27 def maybe_download(filename, work_directory):
 28   """Download the data from Yann's website, unless it's already here."""
 29   if not os.path.exists(work_directory):
 30     os.mkdir(work_directory)
 31   filepath = os.path.join(work_directory, filename)
 32   if not os.path.exists(filepath):
 33     filepath, _ = urllib.request.urlretrieve(SOURCE_URL + filename, filepath)
 34     statinfo = os.stat(filepath)
 35     print('Successfully downloaded', filename, statinfo.st_size, 'bytes.')
 36   return filepath
 37 def _read32(bytestream):
 38   dt = numpy.dtype(numpy.uint32).newbyteorder('>')
 39   return numpy.frombuffer(bytestream.read(4), dtype=dt)[0]
 40 def extract_images(filename):
 41   """Extract the images into a 4D uint8 numpy array [index, y, x, depth]."""
 42   print('Extracting', filename)
 43   with gzip.open(filename) as bytestream:
 44     magic = _read32(bytestream)
 45     if magic != 2051:
 46       raise ValueError(
 47           'Invalid magic number %d in MNIST image file: %s' %
 48           (magic, filename))
 49     num_images = _read32(bytestream)
 50     rows = _read32(bytestream)
 51     cols = _read32(bytestream)
 52     buf = bytestream.read(rows * cols * num_images)
 53     data = numpy.frombuffer(buf, dtype=numpy.uint8)
 54     data = data.reshape(num_images, rows, cols, 1)
 55     return data
 56 def dense_to_one_hot(labels_dense, num_classes=10):
 57   """Convert class labels from scalars to one-hot vectors."""
 58   num_labels = labels_dense.shape[0]
 59   index_offset = numpy.arange(num_labels) * num_classes
 60   labels_one_hot = numpy.zeros((num_labels, num_classes))
 61   labels_one_hot.flat[index_offset + labels_dense.ravel()] = 1
 62   return labels_one_hot
 63 def extract_labels(filename, one_hot=False):
 64   """Extract the labels into a 1D uint8 numpy array [index]."""
 65   print('Extracting', filename)
 66   with gzip.open(filename) as bytestream:
 67     magic = _read32(bytestream)
 68     if magic != 2049:
 69       raise ValueError(
 70           'Invalid magic number %d in MNIST label file: %s' %
 71           (magic, filename))
 72     num_items = _read32(bytestream)
 73     buf = bytestream.read(num_items)
 74     labels = numpy.frombuffer(buf, dtype=numpy.uint8)
 75     if one_hot:
 76       return dense_to_one_hot(labels)
 77     return labels
 78 class DataSet(object):
 79   def __init__(self, images, labels, fake_data=False, one_hot=False,
 80                dtype=tf.float32):
 81     """Construct a DataSet.
 82     one_hot arg is used only if fake_data is true.  `dtype` can be either
 83     `uint8` to leave the input as `[0, 255]`, or `float32` to rescale into
 84     `[0, 1]`.
 85     """
 86     dtype = tf.as_dtype(dtype).base_dtype
 87     if dtype not in (tf.uint8, tf.float32):
 88       raise TypeError('Invalid image dtype %r, expected uint8 or float32' %
 89                       dtype)
 90     if fake_data:
 91       self._num_examples = 10000
 92       self.one_hot = one_hot
 93     else:
 94       assert images.shape[0] == labels.shape[0], (
 95           'images.shape: %s labels.shape: %s' % (images.shape,
 96                                                  labels.shape))
 97       self._num_examples = images.shape[0]
 98       # Convert shape from [num examples, rows, columns, depth]
 99       # to [num examples, rows*columns] (assuming depth == 1)
100       assert images.shape[3] == 1
101       images = images.reshape(images.shape[0],
102                               images.shape[1] * images.shape[2])
103       if dtype == tf.float32:
104         # Convert from [0, 255] -> [0.0, 1.0].
105         images = images.astype(numpy.float32)
106         images = numpy.multiply(images, 1.0 / 255.0)
107     self._images = images
108     self._labels = labels
109     self._epochs_completed = 0
110     self._index_in_epoch = 0
111   @property
112   def images(self):
113     return self._images
114   @property
115   def labels(self):
116     return self._labels
117   @property
118   def num_examples(self):
119     return self._num_examples
120   @property
121   def epochs_completed(self):
122     return self._epochs_completed
123   def next_batch(self, batch_size, fake_data=False):
124     """Return the next `batch_size` examples from this data set."""
125     if fake_data:
126       fake_image = [1] * 784
127       if self.one_hot:
128         fake_label = [1] + [0] * 9
129       else:
130         fake_label = 0
131       return [fake_image for _ in xrange(batch_size)], [
132           fake_label for _ in xrange(batch_size)]
133     start = self._index_in_epoch
134     self._index_in_epoch += batch_size
135     if self._index_in_epoch > self._num_examples:
136       # Finished epoch
137       self._epochs_completed += 1
138       # Shuffle the data
139       perm = numpy.arange(self._num_examples)
140       numpy.random.shuffle(perm)
141       self._images = self._images[perm]
142       self._labels = self._labels[perm]
143       # Start next epoch
144       start = 0
145       self._index_in_epoch = batch_size
146       assert batch_size <= self._num_examples
147     end = self._index_in_epoch
148     return self._images[start:end], self._labels[start:end]
149 def read_data_sets(train_dir, fake_data=False, one_hot=False, dtype=tf.float32):
150   class DataSets(object):
151     pass
152   data_sets = DataSets()
153   if fake_data:
154     def fake():
155       return DataSet([], [], fake_data=True, one_hot=one_hot, dtype=dtype)
156     data_sets.train = fake()
157     data_sets.validation = fake()
158     data_sets.test = fake()
159     return data_sets
160   TRAIN_IMAGES = 'train-images-idx3-ubyte.gz'
161   TRAIN_LABELS = 'train-labels-idx1-ubyte.gz'
162   TEST_IMAGES = 't10k-images-idx3-ubyte.gz'
163   TEST_LABELS = 't10k-labels-idx1-ubyte.gz'
164   VALIDATION_SIZE = 5000
165   local_file = maybe_download(TRAIN_IMAGES, train_dir)
166   train_images = extract_images(local_file)
167   local_file = maybe_download(TRAIN_LABELS, train_dir)
168   train_labels = extract_labels(local_file, one_hot=one_hot)
169   local_file = maybe_download(TEST_IMAGES, train_dir)
170   test_images = extract_images(local_file)
171   local_file = maybe_download(TEST_LABELS, train_dir)
172   test_labels = extract_labels(local_file, one_hot=one_hot)
173   validation_images = train_images[:VALIDATION_SIZE]
174   validation_labels = train_labels[:VALIDATION_SIZE]
175   train_images = train_images[VALIDATION_SIZE:]
176   train_labels = train_labels[VALIDATION_SIZE:]
177   data_sets.train = DataSet(train_images, train_labels, dtype=dtype)
178   data_sets.validation = DataSet(validation_images, validation_labels,
179                                  dtype=dtype)
180   data_sets.test = DataSet(test_images, test_labels, dtype=dtype)
181   return data_sets
input.py

 

 

# 这两行用来下载数据集
import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

import tensorflow as tf

# 设置变量
x = tf.placeholder("float", [None, 784])    # 占位符
w = tf.Variable(tf.zeros([784, 10]))        # 权重值
b = tf.Variable(tf.zeros([10]))             # 偏置值


# 创建模型,用tf.matmul(​​X,W)表示x乘以W
y = tf.nn.softmax(tf.matmul(x,w) + b)

# 用于输入正确值,为了下面计算交叉熵
y_ = tf.placeholder("float", [None,10])

# 计算交叉熵
# 用 tf.log 计算 y 的每个元素的对数,然后把 y_ 的每一个元素和 tf.log(y_) 的对应元素相乘再用 tf.reduce_sum 计算张量的所有元素的总和
cross_entropy = -tf.reduce_sum(y_ * tf.log(y))

# 进行训练,以0.01的学习速率最小化交叉熵
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)

# 初始化创建的变量
init = tf.initialize_all_variables()


# 在一个Session里面启动模型,并初始化变量
sess = tf.Session()
sess.run(init)

# 开始训练模型,训练1000次
for i in range(1000):
    batch_xs, batch_ys = mnist.train.next_batch(100)
    sess.run(train_step,feed_dict={x:batch_xs, y_:batch_ys})

# 用 tf.equal 来检测我们的预测是否真实标签匹配
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))

# 上一行代码会返回一组布尔2值,为了正确预测,把布尔值转换成浮点数
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

# 计算正确率
print(sess.run(accuracy, feed_dict={x:mnist.test.images, y_:mnist.test.labels}))
mnist.py

 

第一次运行主程序时会有点久,因为需要下载数据。最后,,会输出测试的准确率,大概91%左右。

 

0x03:图片识别过程

为了得到一张给定图片属于某个特定数字类的证据(evidence),我们对图片像素值进行加权求和。如果这个像素具有很强的证据说明这张图片不属于该类,那么相应的权值为负数,相反如果这个像素拥有有利的证据支持这张图片属于这个类,那么权值是正数。下面的图片显示了一个模型学习到的图片上每个像素对于特定数字类的权值。红色代表负数权值,蓝色代表正数权值。(我的理解是:将0-9抽象成10个数据类,红色的表示不属于这个数据类的“属性”,蓝色的表示属于这个数据类的“属性”,在一张图片中,如果符合蓝色“属性”的多些,则这张图片符合这个数据类的概率就高些(下面的softmax函数计算概率),个人理解,希望大佬们指正错误。。。)

我们也需要加入一个额外的偏置量(bias),因为输入往往会带有一些无关的干扰量。因此对于给定的输入图片 x 它代表的是数字 i 的证据可以表示为(这句比较重要):

 

(这个公式结合下面第一个彩色图一起看比较容易理解些。)

 其中  代表权重, 代表数字 i 类的偏置量,j 代表给定图片 x 的像素索引用于像素求和(还记得前面那个[60000, 784]的张量嘛)。然后用softmax函数可以把这些证据转换成概率 y(这句也是是关键)

这里的softmax可以看成是一个激励(activation)函数或者链接(link)函数,把我们定义的线性函数的输出转换成我们想要的格式,也就是关于10个数字类的概率分布。因此,给定一张图片,它对于每一个数字的吻合度可以被softmax函数转换成为一个概率值。softmax函数可以定义为:

展开等式右边的子式,可以得到:

 

但是更多的时候把softmax模型函数定义为前一种形式:把输入值当成幂指数求值,再正则化这些结果值。这个幂运算表示,更大的证据对应更大的假设模型(hypothesis)里面的乘数权重值。反之,拥有更少的证据意味着在假设模型里面拥有更小的乘数系数。假设模型里的权值不可以是0值或者负值。Softmax然后会正则化这些权重值,使它们的总和等于1,以此构造一个有效的概率分布。(更多的关于Softmax函数的信息,可以参考Michael Nieslen的书里面的这个部分,其中有关于softmax的可交互式的可视化解释。)

对于softmax回归模型可以用下面的图解释,对于输入的xs加权求和,再分别加上一个偏置量,最后再输入到softmax函数中:

 

 如果把它写成一个等式,我们可以得到:

 

 我们也可以用向量表示这个计算过程:用矩阵乘法和向量相加。这有助于提高计算效率。(也是一种更有效的思考方式)

 

 

更进一步,可以写成更加紧凑的方式:

 

 

参考文章:http://www.tensorfly.cn/tfdoc/tutorials/mnist_beginners.html

********************不积跬步无以至千里********************

posted @ 2019-10-13 17:08  梁十安  阅读(2697)  评论(0编辑  收藏  举报