本篇文章主要介紹如何創(chuàng)建卷積神經(jīng)網(wǎng)絡(CNN:Convolutional Neural Network)用來訓練模型,識別手寫數(shù)字圖片。
Tensorflow的layer模型可以幫助我們方便的構建神經(jīng)網(wǎng)絡,包括密集層dense layer layer(full-connected layer全連接層)或卷積層convolutional layer。
下載數(shù)據(jù)集

這個數(shù)據(jù)集包含了6000個訓練樣本和1000個測試樣本,都是手寫數(shù)字0~9,灰度圖片28像素x28像素。
MNIST dataset 手寫數(shù)字集圖片官方地址
百度網(wǎng)盤下載,密碼:w0sk
代碼框架
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import numpy as np
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
tf.logging.set_verbosity(tf.logging.INFO) #設定輸出日志的模式
#我們的程序代碼將放在這里
#這個文件能夠直接運行,也可以作為模塊被其他文件載入
if __name__ == "__main__":
tf.app.run()
關于卷積神經(jīng)網(wǎng)絡
卷積神經(jīng)網(wǎng)絡Convolutional neural networks (CNNs) 是目前圖像識別領域最先進的模型結構。CNNs對圖像應用各種過濾并從中學習得到高級結構,進而實現(xiàn)圖像分類。
卷積神經(jīng)網(wǎng)絡包含3個組成部分:
-
Convolutional layers卷積層 對圖像應用特定數(shù)量的卷積過濾。對于每個子區(qū)域,神經(jīng)層都會在輸出特征映射中產(chǎn)生單個值,卷積層通常會將一個ReLU(Rectified Linear Units修正線性單元)激活函數(shù)應用到輸出,進而在模型內(nèi)產(chǎn)生一個非線性的結果。
修正線性單元 (ReLU, Rectified Linear Unit),一種激活函數(shù),?(x)=max(0,x),其規(guī)則如下:如果輸入為負數(shù)或 0,則輸出 0;如果輸入為正數(shù),則輸出等于輸入。ReLU屬于非飽和算法non-saturating neurons,就是說沒有把輸出值限定在某個特定區(qū)間,與它對應的飽和算法有sigmod:?(x)=(1+e-x)-1;tanh:?(x)=|tanh(x)|,?(x)=tanh(x);
激活函數(shù) (activation function),一種函數(shù)(例如ReLU或 S型函數(shù)),用于對上一層的所有輸入求加權和,然后生成一個輸出值(通常為非線性值),并將其傳遞給下一層。
-
Pooling layers池化層,對卷積層提取的圖像數(shù)據(jù)進行向下采樣,減少特征維度,加快處理速度。常用的池化算法是最大化池化max pooling,它把輸入的圖像分成不重疊的子矩形區(qū)域,對每個子區(qū)域,保留最大值,忽略其他值,也控制了整體的過采樣。理論基礎是特征之間的關聯(lián)比局部特征更加重要。
-
Dense layer密集層(full-connected layer全連接層),基于卷積層提取的特征和池化層的向下采樣,進行分類classification。
RoI pooling to size 2x2. In this example region proposal (an input parameter) has size 7x5.
標準意義上講,CNN網(wǎng)絡是組合堆疊多個卷機模塊,進行特征提取。每個卷積模塊包含一個卷積層以及跟隨其后的池化層。最后的卷積模塊跟隨著一個或多個密集層以進行分類。CNN網(wǎng)絡中最后的密集層為每個可能的分類設定了一個節(jié)點,并對這每個節(jié)點使用sigmod函數(shù)輸出0~1之間的值,表示該圖形屬于某個分類的可能性。
構建CNN MNIST分類器
- Convolutional Layer卷積層 #1: 應用 32 5x5 濾鏡提取5x5像素子區(qū)域,使用ReLU激活函數(shù)
- Pooling Layer池化層 #1: 執(zhí)行最大化池化2x2濾鏡,步幅stride=2確保區(qū)域不重疊
- Convolutional Layer卷積層 #2: 應用64 5x5過濾,使用ReLU激活函數(shù)
- Pooling Layer池化層 #2: Again, performs max pooling with a 執(zhí)行最大池化2x2過濾,步幅2
- Dense Layer密集層 #1: 1,024個神經(jīng)元, 丟棄正則率0.4 (訓練過程中可能性低于0.4的元素將被丟棄)
- Dense Layer密集層 #2 (Logits Layer邏輯層): 10個神經(jīng)元, 對應0~9數(shù)字
tf.layers對象包含了創(chuàng)建以上三種神經(jīng)層的方法:
-
conv2d(),創(chuàng)建2維的卷積層,參數(shù)包含:過濾數(shù)量,過濾核心尺寸,填充padding,激活函數(shù)。 -
max_pooling2d(), 使用最大化算法創(chuàng)建一個2維池化層。參數(shù)包含:過濾尺寸、步幅。 -
dense(),創(chuàng)建密集層,參數(shù)包含:神經(jīng)元數(shù)量、激活函數(shù)。
這些方法都接收一個張量tensor作為輸入,然后經(jīng)過變換再輸出一個新的張量,這樣就可以一環(huán)一環(huán)相連起來。
接下來我們向代碼結構文件中添加cnn_model_fn(features, labels, mode)函數(shù),它接收MNIST數(shù)據(jù)的特征、標簽和mode(TRAIN,EVAL,PREDICT),返回train訓練、loss損失函數(shù)、predict預測三個方法。這是Tensorflow Estimator的標準接口。
更多自定義Estimator的詳情請看Tensorflow-Estimator-自定義估算器以及Tensorflow-Estimator-估算器-翻譯整理兩篇文章。
添加后的代碼如下:
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import os
import numpy as np
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
tf.logging.set_verbosity(tf.logging.INFO) #設定輸出日志的模式
#我們的程序代碼將放在這里
def cnn_model_fn(features, labels, mode):
#輸入層,-1表示自動計算,這里是圖片批次大小,寬高各28,最后1表示顏色單色
input_layer = tf.reshape(features["x"], [-1, 28, 28, 1])
#1號卷積層,過濾32次,核心區(qū)域5x5,激活函數(shù)relu
conv1 = tf.layers.conv2d(
inputs=input_layer,#接收上面創(chuàng)建的輸入層輸出的張量
filters=32,
kernel_size=[5, 5],
padding="same",
activation=tf.nn.relu)
#1號池化層,接收1號卷積層輸出的張量
pool1 = tf.layers.max_pooling2d(inputs=conv1, pool_size=[2, 2], strides=2)
#2號卷積層
conv2 = tf.layers.conv2d(
inputs=pool1,#繼續(xù)1號池化層的輸出
filters=64,
kernel_size=[5, 5],
padding="same",
activation=tf.nn.relu)
#2號池化層
pool2 = tf.layers.max_pooling2d(inputs=conv2, pool_size=[2, 2], strides=2)
#對2號池化層的輸入變換張量形狀
pool2_flat = tf.reshape(pool2, [-1, 7 * 7 * 64])
#密度層
dense = tf.layers.dense(inputs=pool2_flat, units=1024, activation=tf.nn.relu)
#丟棄層進行簡化
dropout = tf.layers.dropout(
inputs=dense, rate=0.4, training=mode == tf.estimator.ModeKeys.TRAIN)
#使用密度層作為最終輸出,unit可能的分類數(shù)量
logits = tf.layers.dense(inputs=dropout, units=10)
#預測和評價使用的輸出數(shù)據(jù)內(nèi)容
predictions = {
#產(chǎn)生預測,argmax輸出第一個軸向的最大數(shù)值
"classes": tf.argmax(input=logits, axis=1),
#輸出可能性
"probabilities": tf.nn.softmax(logits, name="softmax_tensor")
}
#以下是根據(jù)mode切換的三個不同的方法,都返回tf.estimator.EstimatorSpec對象
#預測
if mode == tf.estimator.ModeKeys.PREDICT:
return tf.estimator.EstimatorSpec(mode=mode, predictions=predictions)
#損失函數(shù)(訓練與評價使用),稀疏柔性最大值交叉熵
loss = tf.losses.sparse_softmax_cross_entropy(labels=labels, logits=logits)
#訓練,使用梯度下降優(yōu)化器,
if mode == tf.estimator.ModeKeys.TRAIN:
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.001)
train_op = optimizer.minimize(
loss=loss,
global_step=tf.train.get_global_step())
return tf.estimator.EstimatorSpec(mode=mode, loss=loss, train_op=train_op)
#評價函數(shù)(上面兩個mode之外else)添加評價度量(for EVAL mode)
eval_metric_ops = {
"accuracy": tf.metrics.accuracy(
labels=labels, predictions=predictions["classes"])}
return tf.estimator.EstimatorSpec(
mode=mode, loss=loss, eval_metric_ops=eval_metric_ops)
#這個文件能夠直接運行,也可以作為模塊被其他文件載入
if __name__ == "__main__":
tf.app.run()
這時候以上代碼還不能運行。以下將逐個代碼塊詳細解說。
Input Layer輸入層
tf.layers中創(chuàng)建神經(jīng)層的方法參數(shù)格式是:
[batch_size,imgage_width,imgage_height,channels]
- batch_size批次尺寸,在訓練過程中梯度下降所使用的樣本子集的大小。
- image_width和image_height,圖像的寬高。
- channels通道,圖片像素的顏色通道數(shù),一般是3(RGB),在這里是黑白圖片只有1個顏色。
MNIST的圖片是28x28單色,[batch_size,28,28,1],我們用-1表示依靠features['x']數(shù)量動態(tài)計算得到這個數(shù)值,那么把它變換一下:
input_layer = tf.reshape(features["x"], [-1, 28, 28, 1])
在這里batch_size作為一個超參數(shù)hyperparameters,如果我們使用batch_size=5,那么input_layers的形狀就是[5,28,28,1],它將包含5x28x28x1=3920個值,每個值表示一個顏色,每784個值表示一張圖,5張為一批次。
Convolutional layer #1卷積層1號
conv1 = tf.layers.conv2d(
inputs=input_layer,
filters=32,
kernel_size=[5, 5],
padding="same",
activation=tf.nn.relu)
我們使用了32次5x5像素過濾filters,使用ReLU作為激活函數(shù)。由于承接了input_layer的輸出,所以進入的張量形狀是[batch_size,28,28,1].
padding參數(shù)有兩個可選,默認的"valid"或"same",為了保持輸出的寬高不變,我們選擇了"same",Tensorflow會自動用0補齊以確保圖像是28x28像素。(如果不使用padding,那么在28x28個網(wǎng)格中只有24x24的區(qū)域可以放置5x5的面片,如下圖所示黃色區(qū)域,也就是會產(chǎn)生24x24的張量。)

激活函數(shù)ReLU將應用在卷積輸出的張量上。最終從conv2d層輸出的張量形狀是[batch_size,28,28,32],這里的32是指我們進行了32次過濾產(chǎn)生的不同結果。
Pooling Layer #1池化層1號
pool1 = tf.layers.max_pooling2d(inputs=conv1, pool_size=[2, 2], strides=2)
conv1作為輸入層,將會輸入的張量形狀是[batch_size,28,28,32]。
pool_size設定了最大池化過濾的尺寸[width,height]就是[2,2]。
stredes表示過濾的步長,我們這里設定了stredes=2,表示濾鏡對子區(qū)域subregions進行提取是按照寬高分別間隔2像素進行的,由于過濾尺寸也是[2,2],所以不會產(chǎn)生重疊也不會產(chǎn)生丟失。


max_pooling層pool1輸出的張量形狀是[batch_size,14,14,32],因為2x2過濾把寬高減少了一半。
Convolutional layer #2 & Pooling layer #2卷積層2號和池化層2號
conv2 = tf.layers.conv2d(
inputs=pool1,
filters=64,
kernel_size=[5, 5],
padding="same",
activation=tf.nn.relu)
pool2 = tf.layers.max_pooling2d(inputs=conv2, pool_size=[2, 2], strides=2)
2號卷積層我們使用了64次5x5區(qū)域的過濾,仍然是ReLU激活函數(shù)。而2號池化層我們?nèi)匀皇褂昧薣2,2]區(qū)域2步幅間隔的設定。
注意2號卷積層設定了padding="same"且64次過濾,所以輸出的張量形狀是[batch_size,14,14,64]。
池化層2號則再次把維度降低到[batch_size,7,7,64]。
Dense layer密集層
接下來我們將添加一個1024神經(jīng)元ReLU激活的密集層(全連接層),基于上面卷積層和池化層得到的結果進行分類。
首先,我們把上面pool2池化層得到的特征映射features map([batch_size,7,7,64])展開,轉(zhuǎn)換到[batch_size,features]。
pool2_flat = tf.reshape(pool2, [-1, 7 * 7 * 64])
-1表示這個維度根據(jù)計算得到,在這里我們將得到一個[batch_size,3136]的張量(7764=3136)。
然后我們再把這個結果傳遞到密集層Dense layer:
dense = tf.layers.dense(inputs=pool2_flat, units=1024, activation=tf.nn.relu)
為了改進我們模型的結果,增加一個dropout丟棄規(guī)則,
dropout = tf.layers.dropout(
inputs=dense, rate=0.4, training=mode == tf.estimator.ModeKeys.TRAIN)
這里rate=0.4表示在訓練過程中,40%的元素會被隨機丟棄。注意training參數(shù)限定了盡在訓練模式下才執(zhí)行隨機丟棄,預測和評價時候不丟棄。
最終,由于使用了1024個神經(jīng)元,dropout輸出的張量形狀是[batch_size,1024]。
Logits layer邏輯層
邏輯層是我們神經(jīng)網(wǎng)絡的最后一層,他將輸出預測的原始數(shù)據(jù)。我們使用dense創(chuàng)建了10個神經(jīng)元的密集層,表示0~9個數(shù)字,使用默認的linear線性激活函數(shù)。
logits = tf.layers.dense(inputs=dropout, units=10)
這樣,我們的CNN卷積神經(jīng)網(wǎng)絡最后輸出的張量形狀就是[batch_size,10]。
生成預測函數(shù)
以上,邏輯層生成的張量是[batch_size,10]。我們用兩個函數(shù)將這個原始數(shù)據(jù)轉(zhuǎn)為兩個不同的格式,作為最終的預測結果:
- 每個樣本的預測分類predicted class,0~9中的一個數(shù)字。
- 每個樣本屬于每個分類的可能性probabilities,樣本可能是0,可能是1,可能是2,可能是...
tf.argmax(input=logits, axis=1)
對于我們這個案例,每個樣本的預測分類,就是邏輯層輸出張量中對應行中值最高的元素。我們使用argmax方法取得它:
tf.argmax(input=logits, axis=1)
注意這里axis等于1而不是0,因為我們從邏輯層獲得的張量形狀是[batch_size,10],也就是10對應的列中每個元素,選取值最大的元素返回。
我們把預測結果封裝到一個字典,放入EstimatorSpec對象:
predictions = {
"classes": tf.argmax(input=logits, axis=1),
"probabilities": tf.nn.softmax(logits, name="softmax_tensor")
}
if mode == tf.estimator.ModeKeys.PREDICT:
return tf.estimator.EstimatorSpec(mode=mode, predictions=predictions)
Calculate loss計算損失函數(shù)
對于訓練和預測,我們需要定義一個損失函數(shù)loss function來評估我們的模型預測的有多么準確。對于多個類別的分類問題,交叉熵算法是最常用的測量標準。
onehot_labels = tf.one_hot(indices=tf.cast(labels, tf.int32), depth=10)
loss = tf.losses.softmax_cross_entropy(
onehot_labels=onehot_labels, logits=logits)
這里cast是轉(zhuǎn)變數(shù)據(jù)類型的方法,labels的數(shù)據(jù)類似[1, 9, 6,7,3...],我們把它轉(zhuǎn)為one_hot獨熱格式的張量:
[[0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
...]
tf.one_hot帶有兩個參數(shù):
- indices索引,獨熱數(shù)組哪一個熱,比如上面第一行對應的labels是1,就是第1個是1(其他都是0);
- depth深度,獨熱數(shù)組有多少個元素,在這里就是有多少個分類。
然后,我們計算onehot_labels和邏輯層輸出的張量[batch_size,10]的這兩者的柔性最大交叉熵。
loss = tf.losses.softmax_cross_entropy(
onehot_labels=onehot_labels, logits=logits)
tf.losses.softmax_cross_entropy方法返回一個標量scalar張量。
設置訓練操作Configure the training Op
在以上,我們定義了loss函數(shù)用于評估我們預測模型最終logits層輸出的結果與實際標簽labels的差異度,下面讓我們的模型在訓練的時候反復優(yōu)化這個差異度,讓模型達到最佳。
我們使用隨機梯度下降函數(shù)stochastic gradient desent作為優(yōu)化算法,使用學習率learning_rate=0.1:
if mode == tf.estimator.ModeKeys.TRAIN:
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.001)
train_op = optimizer.minimize(
loss=loss,
global_step=tf.train.get_global_step())
return tf.estimator.EstimatorSpec(mode=mode, loss=loss, train_op=train_op)
在這里,我們看到,所謂的訓練操作train_op就是優(yōu)化函數(shù)optimizer根據(jù)損失函數(shù)loss,一步一步嘗試構造更好的模型結構。
添加評價度量Evaluation metrics
eval_metric_ops = {
"accuracy": tf.metrics.accuracy(
labels=labels, predictions=predictions["classes"])}
return tf.estimator.EstimatorSpec(
mode=mode, loss=loss, eval_metric_ops=eval_metric_ops)
訓練和評價CNN MNIST分類器
上面我們已經(jīng)完成了CNN MNIST模型函數(shù)的編寫,下面我們繼續(xù)實現(xiàn)模型的訓練和預測。
載入訓練和測試數(shù)據(jù)
首先從把從百度網(wǎng)盤(密碼:w0sk)下載的MNIST_data文件夾(包含四個文件)放在和我們的代碼相同目錄下。

然后我們添加main主函數(shù),代碼載入數(shù)據(jù)集
dir_path = os.path.dirname(os.path.realpath(__file__))
data_path=os.path.join(dir_path,'MNIST_data')
def main(args):
#載入訓練和測試數(shù)據(jù)
mnist = input_data.read_data_sets(data_path)
train_data = mnist.train.images #得到np.array
train_labels = np.asarray(mnist.train.labels, dtype=np.int32)
eval_data = mnist.test.images #得到np.array
eval_labels = np.asarray(mnist.test.labels, dtype=np.int32)
你也可以參照 這篇文章 自己手工從官方下載這個數(shù)據(jù)集。
創(chuàng)建估算器Estimator
Estimator是Tensorflow的一個高級接口high-level API,專門用來對模型進行訓練、和評估和預測的。
在main里面添加下面的代碼
#創(chuàng)建估算器
mnist_classifier = tf.estimator.Estimator(
model_fn=cnn_model_fn, model_dir="/tmp/mnist_convnet_model")
這里使用的兩個參數(shù):
- model_fn,我們在上面創(chuàng)建神經(jīng)網(wǎng)絡CNN的主要函數(shù),里面實現(xiàn)了訓練、評價和預測功能。
- model_dir,這是設置我們訓練出來的模型存儲的目錄,只要這里設置一次,以后Tensorflow都會自動從這里讀取我們訓練好的模型,不需要我們添加任何其他代碼。當然也可以再訓練,會覆蓋更新。
設置日志鉤子logging hook
CNN訓練需要花一點時間,呆看屏幕沒有任何反應等待會很難熬,我們可以讓Tensorflow在訓練的時候輸出一些有用的日志信息。
#設置輸出預測的日志
tensors_to_log = {"probabilities": "softmax_tensor"}
logging_hook = tf.train.LoggingTensorHook(
tensors=tensors_to_log, every_n_iter=50)
我們在這里用tensors_to_log字典來存儲需要打印出來的數(shù)據(jù),注意這里的softmax_tensor,實際是我們在上面定義過的名稱(實際上我們在編寫整個計算模型時候定義的張量name都可以):
predictions = {
"classes": tf.argmax(input=logits, axis=1),
"probabilities": tf.nn.softmax(logits, name="softmax_tensor")
}
然后,我們創(chuàng)建了鉤子函數(shù)LoggingTensorHook,并關聯(lián)到我們的需要輸出的張量,每50次迭代打印一次。
訓練模型
為了訓練模型,我們必須創(chuàng)建一個喂食數(shù)據(jù)的函數(shù)train_input_fn,向main函數(shù)添加以下代碼:
#訓練喂食函數(shù)
train_input_fn = tf.estimator.inputs.numpy_input_fn(
x={"x": train_data},
y=train_labels,
batch_size=100,
num_epochs=None,
shuffle=True)
我們使用了numpy_input_fn方法,它的參數(shù)里面xy對應了我們的訓練特征數(shù)據(jù)和訓練標簽數(shù)據(jù),batch_size表示每步數(shù)step最少訓練100個樣本,num_epochs=None周期數(shù)不設定表示我們一直訓練直到到達指定步數(shù)為止(訓練時候設定)。shuffle=true洗牌為真表示我們將隨機調(diào)整樣品順序。
我們再向main函數(shù)添加代碼啟動訓練:
#啟動訓練
mnist_classifier.train(
input_fn=train_input_fn,
steps=20000,
hooks=[logging_hook])
在這里我們設定了步數(shù)20000,并且把日志鉤子logging_hook傳遞進去。
不要著急,我們先繼續(xù)添加一些其他內(nèi)容。
評價模型Evaluate model
訓練完成后,我們希望使用測試數(shù)據(jù)集來評估一下我們的模型的精確度,看它是否足夠好。繼續(xù)向main函數(shù)添加下面的代碼:
#評價喂食函數(shù)
eval_input_fn = tf.estimator.inputs.numpy_input_fn(
x={"x": eval_data},
y=eval_labels,
num_epochs=1,
shuffle=False)
#啟動評價并輸出結果
eval_results = mnist_classifier.evaluate(input_fn=eval_input_fn)
print(eval_results)
同樣,我們也需要一個評價喂食函數(shù),不斷把需要評價的圖片數(shù)據(jù)輸入到模型,然后再把模型輸出的預測結果和我們真實的標簽對比,這樣就能知道我們的模型是不是足夠準確。
注意在這里我沒設置周期是1,不進行反復訓練;設置shuffle=False也不隨機調(diào)整順序。
運行吧!
整個過程可能需要一些時間,不要著急,訓練20000步數(shù)會很慢,請耐心等候。
續(xù),十多分鐘過去了,目前才到step3000多...求祝福,求光環(huán)...
續(xù),我也不知道最終用了多久,大概1個多小時,這是運行后的結果,精度接近97%:
INFO:tensorflow:Saving dict for global step 20004: accuracy = 0.9691, global_step = 20004, loss = 0.10082044
{'accuracy': 0.9691, 'loss': 0.10082044, 'global_step': 20004}
下面是完整代碼,如果你的代碼運行中出現(xiàn)問題,可以復制它:
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import os
import numpy as np
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
tf.logging.set_verbosity(tf.logging.INFO) #設定輸出日志的模式
#我們的程序代碼將放在這里
def cnn_model_fn(features, labels, mode):
#輸入層,-1表示自動計算,這里是圖片批次大小,寬高各28,最后1表示顏色單色
input_layer = tf.reshape(features["x"], [-1, 28, 28, 1])
#1號卷積層,過濾32次,核心區(qū)域5x5,激活函數(shù)relu
conv1 = tf.layers.conv2d(
inputs=input_layer,#接收上面創(chuàng)建的輸入層輸出的張量
filters=32,
kernel_size=[5, 5],
padding="same",
activation=tf.nn.relu)
#1號池化層,接收1號卷積層輸出的張量
pool1 = tf.layers.max_pooling2d(inputs=conv1, pool_size=[2, 2], strides=2)
#2號卷積層
conv2 = tf.layers.conv2d(
inputs=pool1,#繼續(xù)1號池化層的輸出
filters=64,
kernel_size=[5, 5],
padding="same",
activation=tf.nn.relu)
#2號池化層
pool2 = tf.layers.max_pooling2d(inputs=conv2, pool_size=[2, 2], strides=2)
#對2號池化層的輸入變換張量形狀
pool2_flat = tf.reshape(pool2, [-1, 7 * 7 * 64])
#密度層
dense = tf.layers.dense(inputs=pool2_flat, units=1024, activation=tf.nn.relu)
#丟棄層進行簡化
dropout = tf.layers.dropout(
inputs=dense, rate=0.4, training=mode == tf.estimator.ModeKeys.TRAIN)
#使用密度層作為最終輸出,unit可能的分類數(shù)量
logits = tf.layers.dense(inputs=dropout, units=10)
#預測和評價使用的輸出數(shù)據(jù)內(nèi)容
predictions = {
#產(chǎn)生預測,argmax輸出第一個軸向的最大數(shù)值
"classes": tf.argmax(input=logits, axis=1),
#輸出可能性
"probabilities": tf.nn.softmax(logits, name="softmax_tensor")
}
#以下是根據(jù)mode切換的三個不同的方法,都返回tf.estimator.EstimatorSpec對象
#預測
if mode == tf.estimator.ModeKeys.PREDICT:
return tf.estimator.EstimatorSpec(mode=mode, predictions=predictions)
#損失函數(shù)(訓練與評價使用),稀疏柔性最大值交叉熵
loss = tf.losses.sparse_softmax_cross_entropy(labels=labels, logits=logits)
#訓練,使用梯度下降優(yōu)化器,
if mode == tf.estimator.ModeKeys.TRAIN:
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.001)
train_op = optimizer.minimize(
loss=loss,
global_step=tf.train.get_global_step())
return tf.estimator.EstimatorSpec(mode=mode, loss=loss, train_op=train_op)
#評價函數(shù)(上面兩個mode之外else)添加評價度量(for EVAL mode)
eval_metric_ops = {
"accuracy": tf.metrics.accuracy(
labels=labels, predictions=predictions["classes"])}
return tf.estimator.EstimatorSpec(
mode=mode, loss=loss, eval_metric_ops=eval_metric_ops)
dir_path = os.path.dirname(os.path.realpath(__file__))
data_path=os.path.join(dir_path,'MNIST_data')
def main(args):
#載入訓練和測試數(shù)據(jù)
mnist = input_data.read_data_sets(data_path)
train_data = mnist.train.images #得到np.array
train_labels = np.asarray(mnist.train.labels, dtype=np.int32)
eval_data = mnist.test.images #得到np.array
eval_labels = np.asarray(mnist.test.labels, dtype=np.int32)
#創(chuàng)建估算器
mnist_classifier = tf.estimator.Estimator(
model_fn=cnn_model_fn, model_dir="/tmp/mnist_convnet_model")
#設置輸出預測的日志
tensors_to_log = {"probabilities": "softmax_tensor"}
logging_hook = tf.train.LoggingTensorHook(
tensors=tensors_to_log, every_n_iter=50)
#訓練喂食函數(shù)
train_input_fn = tf.estimator.inputs.numpy_input_fn(
x={"x": train_data},
y=train_labels,
batch_size=100,
num_epochs=None,
shuffle=True)
#啟動訓練
mnist_classifier.train(
input_fn=train_input_fn,
steps=20000,
hooks=[logging_hook])
#評價喂食函數(shù)
eval_input_fn = tf.estimator.inputs.numpy_input_fn(
x={"x": eval_data},
y=eval_labels,
num_epochs=1,
shuffle=False)
#啟動評價并輸出結果
eval_results = mnist_classifier.evaluate(input_fn=eval_input_fn)
print(eval_results)
#這個文件能夠直接運行,也可以作為模塊被其他文件載入
if __name__== "__main__":
tf.app.run()
探索人工智能的新邊界
如果您發(fā)現(xiàn)文章錯誤,請不吝留言指正;
如果您覺得有用,請點喜歡;
如果您覺得很有用,感謝轉(zhuǎn)發(fā)~
END


