前面我們曾有篇文章中提到過關於用tensorflow訓練手寫28 28像素點的數字的識別,在那篇文章中我們把手寫數字圖像直接碾壓成了一個784列的數據進行識別,但實際上,這個圖像是28 28長寬結構的,我們這次使用CNN捲積神經網路來進行識別。 捲積神經網路我的理解是部分模仿了人眼的功能。 我們在看 ...
前面我們曾有篇文章中提到過關於用tensorflow訓練手寫2828像素點的數字的識別,在那篇文章中我們把手寫數字圖像直接碾壓成了一個784列的數據進行識別,但實際上,這個圖像是2828長寬結構的,我們這次使用CNN捲積神經網路來進行識別。
捲積神經網路我的理解是部分模仿了人眼的功能。
我們在看一個圖像時不是一個像素點一個像素點去分辨的,我們的眼睛天然地具有大局觀,我們看到某個圖像時自動地會把其中的細節部分給聚合起來進行識別,相反,如果我們用個放大鏡看到其中的各個像素點時反而不知道這是啥東西了。
因此捲積神經網路就把每個像素點的圖像進行一定程度上的模糊化,而怎麼進行模糊化呢?它是通過選擇一小片的區域範圍,把這小片中的圖像數據縮小其長寬,但增加其高度值。然後進行某種計算,最終達到有點類似模糊化圖像的目的,但這個模糊化的圖像中反而能夠比較容易識別出相應的邊界及形狀。
具體大家可以到網上搜索相關的理論知識,這裡不細講,只專註於如何在tensorflow中實現 CNN的功能。
之前在tensorflow分類-【老魚學tensorflow】中已經用一般的神經網路進行過手寫數字的識別,我們在那個程式的基礎上來進行,那篇文章的地址為:http://www.cnblogs.com/dreampursuer/p/8026866.html
import tensorflow as tf
# 準備數據
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('D:/todel/python/MNIST_data/', one_hot=True)
def add_layer(inputs, in_size, out_size, activation_function=None):
"""
添加層
:param inputs: 輸入數據
:param in_size: 輸入數據的列數
:param out_size: 輸出數據的列數
:param activation_function: 激勵函數
:return:
"""
# 定義權重,初始時使用隨機變數,可以簡單理解為在進行梯度下降時的隨機初始點,這個隨機初始點要比0值好,因為如果是0值的話,反覆計算就一直是固定在0中,導致可能下降不到其它位置去。
Weights = tf.Variable(tf.random_normal([in_size, out_size]))
# 偏置shape為1行out_size列
biases = tf.Variable(tf.zeros([1, out_size]) + 0.1)
# 建立神經網路線性公式:inputs * Weights + biases,我們大腦中的神經元的傳遞基本上也是類似這樣的線性公式,這裡的權重就是每個神經元傳遞某信號的強弱繫數,偏置值是指這個神經元的原先所擁有的電位高低值
Wx_plus_b = tf.matmul(inputs, Weights) + biases
if activation_function is None:
# 如果沒有設置激活函數,則直接就把當前信號原封不動地傳遞出去
outputs = Wx_plus_b
else:
# 如果設置了激活函數,則會由此激活函數來對信號進行傳遞或抑制
outputs = activation_function(Wx_plus_b)
return outputs
# 定義輸入數據
xs = tf.placeholder(tf.float32, [None, 28*28])
ys = tf.placeholder(tf.float32, [None, 10]) #10列,就是那個one hot結構的數據
# 定義層,輸入為xs,其有28*28列,輸出為10列one hot結構的數據,激勵函數為softmax,對於one hot類型的數據,一般激勵函數就使用softmax
prediction = add_layer(xs, 28*28, 10, activation_function=tf.nn.softmax)
# 定義loss值
cross_entropy = tf.reduce_mean(-tf.reduce_sum(ys * tf.log(prediction), axis=1))
train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)
sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)
def computer_accuracy(v_xs, v_ys):
"""
計算準確度
:param v_xs:
:param v_ys:
:return:
"""
# predication是從外部獲得的變數
global prediction
# 根據小批量輸入的值計算預測值
y_pre = sess.run(prediction, feed_dict={xs:v_xs})
correct_prediction = tf.equal(tf.argmax(y_pre, 1), tf.argmax(v_ys, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
result = sess.run(accuracy, feed_dict={xs:v_xs, ys:v_ys})
return result
for i in range(1000):
batch_xs, batch_ys = mnist.train.next_batch(100)
sess.run(train_step, feed_dict={xs: batch_xs, ys: batch_ys})
if i % 50 == 0:
# 每隔50條列印一下預測的準確率
print(computer_accuracy(mnist.test.images, mnist.test.labels))
添加必要的函數
生成權重變數
# 生成權重變數
def weight_variable(shape):
# 產生一個隨機變數
init = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(init)
定義bias變數
# 定義bias變數
def bias_variable(shape):
# bias的初始值比權重值稍微簡單點,直接用非0常量定義就可以
init = tf.constant(0.1, shape=shape)
return tf.Variable(init)
定義捲積神經網路層
# 定義捲積神經網路層
def conv2d(x, W):
# strides:結構為[1, x方向上的步長,y方向上的步長, 1],這裡x方向上的步長和y方向上的步長都設置為1
# padding可選值有VALID和SAME,VALID方式會在邊界處比原始圖片小一點,而SAME方式會在邊界處用0來補充,從而保持跟原始圖相同的大小
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
定義pooling
# 為了防止跨步太大丟失掉信息,我們會在中間建立一個pooling,使其跨度減小,但在pooling時跨度可以變大一點,這樣在最後的圖片生成時可以把大小減小下來但同時又儘可能保存了相關的信息
def max_pool_2x2(x):
# strides結構依然為:[1, x方向上的步長,y方向上的步長, 1],這裡x方向上的步長和y方向上的步長都設置為2,這樣在pool時把圖像的大小給減小了
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
編寫主程式部分
定義輸入數據
# 定義輸入數據
xs = tf.placeholder(tf.float32, [None, 28*28])
ys = tf.placeholder(tf.float32, [None, 10]) #10列,就是那個one hot結構的數據
keep_prob = tf.placeholder(tf.float32)
# 為了使用捲積神經網路,我們需要把原始的一維的數據變換成長寬表示的平面圖的數據,把xs的形狀變成[-1,28,28,1],-1代表先不考慮輸入的圖片例子多少這個維度,
# 後面的1是channel的數量,因為我們輸入的圖片是黑白的,因此channel是1,例如如果是RGB圖像,那麼channel就是3。
x_image = tf.reshape(xs, [-1, 28, 28, 1])
定義捲積層1
# 定義捲積層1,以5*5的面積進行掃描,因為黑白圖片channel是1所以輸入是1,輸出是32個高度的值
W_conv1 = weight_variable([5, 5, 1, 32])
# bias的大小是32個長度,因此我們傳入它的shape為[32]
b_conv1 = bias_variable([32])
# 定義好了Weight和bias,我們就可以定義捲積神經網路的第一個捲積層h_conv1=conv2d(x_image,W_conv1)+b_conv1,同時我們對h_conv1進行非線性處理,
# 也就是激活函數來處理嘍,這裡我們用的是tf.nn.relu(修正線性單元)來處理,要註意的是,因為採用了SAME的padding方式,輸出圖片的大小沒有變化依然是28x28,
# 只是厚度變厚了,因此現在的輸出大小就變成了28x28x32
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
# 最後我們再進行pooling的處理就ok啦,經過pooling的處理,輸出大小就變為了14x14x32
h_pool1 = max_pool_2x2(h_conv1)
定義捲積層2
# 定義捲積層2
# 掃描的面積還是定義成5*5,輸入大小為32,因為捲積層1中輸出為32就被設置為這裡的輸入大小了。輸出大小設定為64,也就是變得更高了
W_conv2 = weight_variable([5, 5, 32, 64])
# bias的大小是64個長度,因此我們傳入它的shape為[64]
b_conv2 = bias_variable([64])
# 定義好了Weight和bias,我們就可以定義捲積神經網路的第二個捲積層h_conv2=conv2d(h_pool1,W_conv2)+b_conv2,同時我們對h_conv2進行非線性處理,
# 也就是激活函數來處理嘍,這裡我們用的是tf.nn.relu(修正線性單元)來處理,要註意的是,因為採用了SAME的padding方式,輸出圖片的大小沒有變化依然是在第一層捲集層輸出時的14*14,
# 只是厚度變厚了,因此現在的輸出大小就變成了14x14x64
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
# 最後我們再進行pooling的處理就ok啦,經過pooling的處理,輸出大小就變為了7x7x64
h_pool2 = max_pool_2x2(h_conv2)
定義神經網路全連接層1
# 定義神經網路全連接層1
# 其形狀為h_pool2的輸出形狀7*7*64,輸出為1024個神經元
W_fc1 = weight_variable([7*7*64, 1024])
b_fc1 = bias_variable([1024])
# 把h_pool2的輸出的包含長寬平面的信息形狀轉換成一個維度的數據,相當於變平的操作:[n_samples, 7, 7, 64] => [n_samples, 7*7*64]
h_pool1_flat = tf.reshape(h_pool2, [-1, 7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool1_flat, W_fc1) + b_fc1)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
定義神經網路全連接層2
# 定義神經網路全連接層2
# 其輸入為全連接層1的輸出1024,輸出為0-9數字的one hot格式,因此為10列
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
prediction = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
定義loss值
# 定義loss值
cross_entropy = tf.reduce_mean(-tf.reduce_sum(ys * tf.log(prediction), axis=1))
# 對於比較龐大的系統可以用AdamOptimizer比較好一點
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
# train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)
執行
sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)
for i in range(1000):
batch_xs, batch_ys = mnist.train.next_batch(100)
sess.run(train_step, feed_dict={xs: batch_xs, ys: batch_ys, keep_prob: 0.5})
if i % 50 == 0:
# 每隔50條列印一下預測的準確率
print(computer_accuracy(mnist.test.images, mnist.test.labels))
全部代碼
import tensorflow as tf
def add_layer(inputs, in_size, out_size, activation_function=None):
"""
添加層
:param inputs: 輸入數據
:param in_size: 輸入數據的列數
:param out_size: 輸出數據的列數
:param activation_function: 激勵函數
:return:
"""
# 定義權重,初始時使用隨機變數,可以簡單理解為在進行梯度下降時的隨機初始點,這個隨機初始點要比0值好,因為如果是0值的話,反覆計算就一直是固定在0中,導致可能下降不到其它位置去。
Weights = tf.Variable(tf.random_normal([in_size, out_size]))
# 偏置shape為1行out_size列
biases = tf.Variable(tf.zeros([1, out_size]) + 0.1)
# 建立神經網路線性公式:inputs * Weights + biases,我們大腦中的神經元的傳遞基本上也是類似這樣的線性公式,這裡的權重就是每個神經元傳遞某信號的強弱繫數,偏置值是指這個神經元的原先所擁有的電位高低值
Wx_plus_b = tf.matmul(inputs, Weights) + biases
if activation_function is None:
# 如果沒有設置激活函數,則直接就把當前信號原封不動地傳遞出去
outputs = Wx_plus_b
else:
# 如果設置了激活函數,則會由此激活函數來對信號進行傳遞或抑制
outputs = activation_function(Wx_plus_b)
return outputs
def computer_accuracy(v_xs, v_ys):
"""
計算準確度
:param v_xs:
:param v_ys:
:return:
"""
# predication是從外部獲得的變數
global prediction
# 根據小批量輸入的值計算預測值
y_pre = sess.run(prediction, feed_dict={xs:v_xs, keep_prob: 1})
correct_prediction = tf.equal(tf.argmax(y_pre, 1), tf.argmax(v_ys, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
result = sess.run(accuracy, feed_dict={xs:v_xs, ys:v_ys, keep_prob: 1})
return result
# 生成權重變數
def weight_variable(shape):
# 產生一個隨機變數
init = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(init)
# 定義bias變數
def bias_variable(shape):
# bias的初始值比權重值稍微簡單點,直接用非0常量定義就可以
init = tf.constant(0.1, shape=shape)
return tf.Variable(init)
# 定義捲積神經網路層
def conv2d(x, W):
# strides:結構為[1, x方向上的步長,y方向上的步長, 1],這裡x方向上的步長和y方向上的步長都設置為1
# padding可選值有VALID和SAME,VALID方式會在邊界處比原始圖片小一點,而SAME方式會在邊界處用0來補充,從而保持跟原始圖相同的大小
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
# 為了防止跨步太大丟失掉信息,我們會在中間建立一個pooling,使其跨度減小,但在pooling時跨度可以變大一點,這樣在最後的圖片生成時可以把大小減小下來但同時又儘可能保存了相關的信息
def max_pool_2x2(x):
# strides結構依然為:[1, x方向上的步長,y方向上的步長, 1],這裡x方向上的步長和y方向上的步長都設置為2,這樣在pool時把圖像的大小給減小了
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
# 準備數據
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('D:/todel/python/MNIST_data/', one_hot=True)
# 定義輸入數據
xs = tf.placeholder(tf.float32, [None, 28*28])
ys = tf.placeholder(tf.float32, [None, 10]) #10列,就是那個one hot結構的數據
keep_prob = tf.placeholder(tf.float32)
# 為了使用捲積神經網路,我們需要把原始的一維的數據變換成長寬表示的平面圖的數據,把xs的形狀變成[-1,28,28,1],-1代表先不考慮輸入的圖片例子多少這個維度,
# 後面的1是channel的數量,因為我們輸入的圖片是黑白的,因此channel是1,例如如果是RGB圖像,那麼channel就是3。
x_image = tf.reshape(xs, [-1, 28, 28, 1])
# 定義捲積層1,以5*5的面積進行掃描,因為黑白圖片channel是1所以輸入是1,輸出是32個高度的值
W_conv1 = weight_variable([5, 5, 1, 32])
# bias的大小是32個長度,因此我們傳入它的shape為[32]
b_conv1 = bias_variable([32])
# 定義好了Weight和bias,我們就可以定義捲積神經網路的第一個捲積層h_conv1=conv2d(x_image,W_conv1)+b_conv1,同時我們對h_conv1進行非線性處理,
# 也就是激活函數來處理嘍,這裡我們用的是tf.nn.relu(修正線性單元)來處理,要註意的是,因為採用了SAME的padding方式,輸出圖片的大小沒有變化依然是28x28,
# 只是厚度變厚了,因此現在的輸出大小就變成了28x28x32
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
# 最後我們再進行pooling的處理就ok啦,經過pooling的處理,輸出大小就變為了14x14x32
h_pool1 = max_pool_2x2(h_conv1)
# 定義捲積層2
# 掃描的面積還是定義成5*5,輸入大小為32,因為捲積層1中輸出為32就被設置為這裡的輸入大小了。輸出大小設定為64,也就是變得更高了
W_conv2 = weight_variable([5, 5, 32, 64])
# bias的大小是64個長度,因此我們傳入它的shape為[64]
b_conv2 = bias_variable([64])
# 定義好了Weight和bias,我們就可以定義捲積神經網路的第二個捲積層h_conv2=conv2d(h_pool1,W_conv2)+b_conv2,同時我們對h_conv2進行非線性處理,
# 也就是激活函數來處理嘍,這裡我們用的是tf.nn.relu(修正線性單元)來處理,要註意的是,因為採用了SAME的padding方式,輸出圖片的大小沒有變化依然是在第一層捲集層輸出時的14*14,
# 只是厚度變厚了,因此現在的輸出大小就變成了14x14x64
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
# 最後我們再進行pooling的處理就ok啦,經過pooling的處理,輸出大小就變為了7x7x64
h_pool2 = max_pool_2x2(h_conv2)
# 定義神經網路全連接層1
# 其形狀為h_pool2的輸出形狀7*7*64,輸出為1024個神經元
W_fc1 = weight_variable([7*7*64, 1024])
b_fc1 = bias_variable([1024])
# 把h_pool2的輸出的包含長寬平面的信息形狀轉換成一個維度的數據,相當於變平的操作:[n_samples, 7, 7, 64] => [n_samples, 7*7*64]
h_pool1_flat = tf.reshape(h_pool2, [-1, 7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool1_flat, W_fc1) + b_fc1)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
# 定義神經網路全連接層2
# 其輸入為全連接層1的輸出1024,輸出為0-9數字的one hot格式,因此為10列
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
prediction = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
# 定義loss值
cross_entropy = tf.reduce_mean(-tf.reduce_sum(ys * tf.log(prediction), axis=1))
# 對於比較龐大的系統可以用AdamOptimizer比較好一點
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
# train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)
sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)
for i in range(1000):
batch_xs, batch_ys = mnist.train.next_batch(100)
sess.run(train_step, feed_dict={xs: batch_xs, ys: batch_ys, keep_prob: 0.5})
if i % 50 == 0:
# 每隔50條列印一下預測的準確率
print(computer_accuracy(mnist.test.images, mnist.test.labels))
輸出為:
0.0853
0.7785
0.8835
0.9084
0.9241
0.9316
0.9412
0.9463
0.9485
0.951
0.9561
0.9578
0.9599
0.9611
0.964
0.9644
0.966
0.9673
0.9687
0.9685
這次用捲積神經網路把結果提高了很多。