(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

# 這兩行用來下載數據集 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}))
第一次運行主程序時會有點久,因為需要下載數據。最后,,會輸出測試的准確率,大概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
********************不積跬步無以至千里********************