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

********************不積跬步無以至千里********************


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM