Tensorflow搭建卷積神經網絡識別手寫英語字母

更新記錄:python

2018年2月5日 初始文章版本git


 

近幾天須要進行英語手寫體識別,查閱了不少資料,可是大多數資料都是針對MNIST數據集的,而且主要識別手寫數字。爲了知足實際的英文手寫識別需求,須要從訓練集構造到神經網絡搭建各個方面對現有代碼進行修改。網絡

神經網絡的結構:ide

1.輸入28*28=784維行向量函數

2.卷積層:卷積核大小5*5,共32個,激活函數ReLu測試

3.池化層:最大值池化,2*2窗口字體

4.卷積層:卷積核大小5*5,共64個,激活函數ReLu優化

5.池化層:最大值池化,2*2窗口ui

6.全鏈接層(多層感知機)spa

 

訓練代碼:

  1 import tensorflow as tf
  2 import numpy as np
  3 import xlrd
  4 # 開始讀取訓練數據
  5 data = xlrd.open_workbook('train_set.xlsx')
  6 table = data.sheets()[0]
  7 nrows = table.nrows
  8 ncols = table.ncols
  9 c1 = np.arange(0, nrows, 1)
 10 datamatrix = np.zeros((nrows, ncols))
 11 for x in range(ncols):
 12     cols = table.col_values(x)
 13     cols1 = np.matrix(cols)   # 把list轉換爲矩陣進行矩陣操做
 14     datamatrix[:, x] = cols1  # 把數據進行存儲
 15 x_data = datamatrix
 16 
 17 table = data.sheets()[1]
 18 nrows = table.nrows
 19 ncols = table.ncols
 20 c1 = np.arange(0, nrows, 1)
 21 datamatrix = np.zeros((nrows, ncols))
 22 for x in range(ncols):
 23     cols = table.col_values(x)
 24     cols1 = np.matrix(cols)   # 把list轉換爲矩陣進行矩陣操做
 25     datamatrix[:, x] = cols1  # 把數據進行存儲
 26 y_data = datamatrix
 27 # 完成訓練數據讀取
 28 # 開始定義神經網絡結構
 29 
 30 # 定義佔位符x和y_
 31 x = tf.placeholder(tf.float32, shape=[None, 784])
 32 y_ = tf.placeholder(tf.float32, shape=[None, 26])
 33 
 34 
 35 # 開始定義用於初始化的兩個函數
 36 def weight_variable(shape):
 37     initial = tf.truncated_normal(shape, stddev=0.1)
 38     return tf.Variable(initial)
 39 
 40 
 41 def bias_variable(shape):
 42     initial = tf.constant(0.1, shape=shape)
 43     return tf.Variable(initial)
 44 
 45 # 完成初始化函數定義
 46 
 47 
 48 # 開始定義卷積和池化的函數
 49 # 卷積使用1步長(stride size),0邊距(padding size)的模板,保證輸出和輸入大小相同
 50 # 池化用簡單傳統的2x2大小的模板作max pooling,所以輸出的長寬會變爲輸入的一半
 51 
 52 
 53 def conv2d(x, W):
 54     return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
 55 
 56 
 57 def max_pool_2x2(x):
 58     return tf.nn.max_pool(x, ksize=[1,2,2,1], strides=[1, 2, 2, 1], padding='SAME')
 59 # 完成卷積池化函數定義
 60 
 61 
 62 # 開始定義神經網絡結構定義
 63 # 第一層卷積,卷積在每一個5x5的patch中算出32個特徵
 64 W_conv1 = weight_variable([5, 5, 1, 32])
 65 b_conv1 = bias_variable([32])
 66 x_image = tf.reshape(x, [-1, 28, 28, 1])
 67 # 第二、第3維對應圖片的寬、高,最後一維表明圖片的顏色通道數(由於是灰度圖因此這裏的通道數爲1,若是是rgb彩色圖,則爲3)
 68 h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
 69 h_pool1 = max_pool_2x2(h_conv1)
 70 
 71 # 第二層卷積,每一個5x5的patch會獲得64個特徵
 72 W_conv2 = weight_variable([5, 5, 32, 64])
 73 b_conv2 = bias_variable([64])
 74 h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
 75 h_pool2 = max_pool_2x2(h_conv2)
 76 
 77 # 有1024個神經元的全鏈接層,此時圖片大小爲7*7
 78 W_fc1 = weight_variable([7*7*64, 1024])
 79 b_fc1 = bias_variable([1024])
 80 h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
 81 h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
 82 
 83 # 爲了減小過擬合,在輸出層以前加入dropout。用一個placeholder表明一個神經元的輸出在dropout中保持不變的機率。
 84 # 這樣能夠在訓練過程當中啓用dropout,在測試過程當中關閉dropout。
 85 keep_prob = tf.placeholder(tf.float32)
 86 h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
 87 # softmax輸出層
 88 W_fc2 = weight_variable([1024, 26])
 89 b_fc2 = bias_variable([26])
 90 y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
 91 # 應爲 y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
 92 # 完成神經網絡結構定義
 93 
 94 # 開始定義訓練和評估操做
 95 # 用更加複雜的ADAM優化器來作梯度最速降低,在feed_dict中加入額外的參數keep_prob來控制dropout比例
 96 cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y_conv))
 97 train_step = tf.train.AdamOptimizer(1e-6).minimize(cross_entropy)
 98 correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))
 99 accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
100 # 完成訓練和評估操做的定義
101 
102 # 開始定義儲存器操做並裝載已經訓練過的神經網絡
103 saver = tf.train.Saver(write_version=tf.train.SaverDef.V1)
104 sess = tf.InteractiveSession()
105 saver.restore(sess, "cnnres/model.ckpt")
106 # sess.run(tf.global_variables_initializer())
107 # 完成定義儲存器操做和裝載神經網絡
108 
109 # 開始對訓練集進行循環訓練
110 for k in range(20):
111     for i in range(55):  # 爲減小訓練時間,下降迭代次數
112         x_datap = x_data[i*26:(i+1)*26, 0:28*28]
113         y_datap = y_data[i*26:(i+1)*26, 0:26]
114         for j in range(3000):
115             if j % 100 == 0:
116                 train_accuracy = accuracy.eval(feed_dict={x: x_data, y_: y_data, keep_prob: 1.0})
117                 print("step %d, training accuracy %g"%(i, train_accuracy))
118             train_step.run(feed_dict={x: x_datap, y_: y_datap, keep_prob: 0.5})
119 #        if train_accuracy >= 0.942:
120 #            train_step = tf.train.AdamOptimizer(1e-6).minimize(cross_entropy)
121 #        if train_accuracy <= 0.9:
122 #            train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
123         if train_accuracy >= 0.95:
124             saver_path = saver.save(sess, "cnnres/model.ckpt")
125             print('Save the par in', saver_path)
126 # 完成訓練和儲存過程

 

備註:

1.因爲GUP運算能力的限制,須要將訓練集每次取出一部分進行訓練,可是對模型的準確度進行評估時應當feed所有數據。

相關代碼:

x_datap = x_data[i*26:(i+1)*26, 0:28*28]
y_datap = y_data[i*26:(i+1)*26, 0:26]
train_accuracy = accuracy.eval(feed_dict={x: x_data, y_: y_data, keep_prob: 1.0})
train_step.run(feed_dict={x: x_datap, y_: y_datap, keep_prob: 0.5})
2.考慮到對訓練集的讀取是順序的,所以訓練集必須隨機亂序,絕對不能按照字母表排序,不然將會出現嚴重的過擬合。

3.神經網絡的訓練結果被儲存在cnnres/model.ckpt中

4.在精確度達到0.8以前將步長定爲1E-4,在精確度達到0.9以後將步長定爲1E-6,不要將步長設定爲小於這個值,不然訓練進展極爲緩慢。

訓練集構成:

儲存在train_set.xlsx中。共有兩張表,第一張表每一行有28*28=784列,對應一個784維輸入向量;第二張表每一行有26列,該行與第一張表同一行的預期輸出結果對應,在第x列值爲1,其他列值爲0,表示第一張表同一行的預期輸出結果是字母表中第x個字母。

訓練集的生成代碼:

import numpy as np
from PIL import Image
import xlsxwriter


# 開始讀取測試圖片


def ImageToMatrix(filename):
    im = Image.open(filename)
    width, height = im.size
    im = im.convert("L")
    data = im.getdata()
    data = np.matrix(data, dtype='float')/255.0
    new_data = np.reshape(data, (height, width))
    return new_data

def ImageToMatrix2(ima):
    width, height = ima.size
    ima = ima.convert("L")
    data = ima.getdata()
    data = np.matrix(data, dtype='float')/255.0
    new_data = np.reshape(data, (height, width))
    return new_data

def MatrixToImage(data):
    data = data*255
    new_im = Image.fromarray(data.astype(np.uint8))
    return new_im


# 循環讀取測試圖片並寫入
# 開始進行寫excel的準備
book = xlsxwriter.Workbook(r'train_set.xlsx')
sheet1 = book.add_worksheet('train_input1')
sheet2 = book.add_worksheet('train_input2')
sheet3 = book.add_worksheet('train_input3')
sheet4 = book.add_worksheet('train_input4')
# 完成寫excel的準備
for i in range(1, 1430+1):
    test_pic = ImageToMatrix(str(i)+'.png')
# 完成測試圖片讀取
# -------------------------
# 開始處理測試圖片
# 開始尋找圖片四邊
    hang, lie = np.shape(test_pic)
    for top in range(0, hang):
        if np.min(test_pic[top, :]) != 1:
            break
    for bot in range(hang-1, 0, -1):
        if np.min(test_pic[bot, :]) != 1:
            break
    for left in range(0, lie):
        if np.min(test_pic[:, left]) != 1:
            break
    for right in range(lie - 1, 0, -1):
        if np.min(test_pic[:, right]) != 1:
            break
    new_test_pic = test_pic[top:bot, left:right]
# 完成圖片四邊尋找
# 開始進行圖片尺寸轉換
    pic = MatrixToImage(new_test_pic)
    pic2 = pic.resize((28, 28))
    test_datap = ImageToMatrix2(pic2)
    test_data = np.reshape(test_datap, (1, 784))
# 完成圖片尺寸轉換
# 對行向量進行儲存
    for j in range(0, 200):
        sheet1.write(i-1, j, test_data[0, j])
        sheet2.write(i - 1, j, test_data[0, j+200])
        sheet3.write(i - 1, j, test_data[0, j+400])
        if j+600 <= 783:
            sheet4.write(i - 1, j, test_data[0, j+600])
    print(i)
book.close()

 

備註:此處因爲python提供的xlsxwriter庫存以下限制:每一行最多能夠寫256列,所以必須將這個786維的向量分別寫到4張表的同一行,再進行手工合併。而第二張預期輸出表須要使用其它方法進行構造,此處不給出相關代碼。

 

測試代碼:

  1 import tensorflow as tf
  2 import numpy as np
  3 from PIL import Image
  4 import matplotlib.pyplot as plt
  5 # 開始讀取測試圖片
  6 
  7 
  8 def ImageToMatrix(filename):
  9     im = Image.open(filename)
 10     width, height = im.size
 11     im = im.convert("L")
 12     data = im.getdata()
 13     data = np.matrix(data, dtype='float')/255.0
 14     new_data = np.reshape(data, (height, width))
 15     return new_data
 16 
 17 def ImageToMatrix2(ima):
 18     width, height = ima.size
 19     ima = ima.convert("L")
 20     data = ima.getdata()
 21     data = np.matrix(data, dtype='float')/255.0
 22     new_data = np.reshape(data, (height, width))
 23     return new_data
 24 
 25 def MatrixToImage(data):
 26     data = data*255
 27     new_im = Image.fromarray(data.astype(np.uint8))
 28     return new_im
 29 
 30 
 31 test_pic = ImageToMatrix('test.png')
 32 
 33 # 完成測試圖片讀取
 34 # -------------------------
 35 # 開始處理測試圖片
 36 # 開始尋找圖片四邊
 37 hang, lie = np.shape(test_pic)
 38 for top in range(0, hang):
 39     if np.min(test_pic[top, :]) != 1:
 40         break
 41 for bot in range(hang-1, 0, -1):
 42     if np.min(test_pic[bot, :]) != 1:
 43         break
 44 for left in range(0, lie):
 45     if np.min(test_pic[:, left]) != 1:
 46         break
 47 for right in range(lie - 1, 0, -1):
 48     if np.min(test_pic[:, right]) != 1:
 49         break
 50 new_test_pic = test_pic[top:bot, left:right]
 51 # 完成圖片四邊尋找
 52 # 開始進行圖片尺寸轉換
 53 pic = MatrixToImage(new_test_pic)
 54 pic2 = pic.resize((28, 28))
 55 test_data = ImageToMatrix2(pic2)
 56 test_data = np.reshape(test_data, (1, 784))
 57 # 完成圖片尺寸轉換
 58 
 59 # 完成測試圖片的處理
 60 # --------------------------
 61 # 開始定義神經網絡結構
 62 
 63 # 定義佔位符x和y_
 64 x = tf.placeholder(tf.float32, shape=[None, 784])
 65 y_ = tf.placeholder(tf.float32, shape=[None, 26])
 66 
 67 
 68 # 開始定義用於初始化的兩個函數
 69 def weight_variable(shape):
 70     initial = tf.truncated_normal(shape, stddev=0.1)
 71     return tf.Variable(initial)
 72 
 73 
 74 def bias_variable(shape):
 75     initial = tf.constant(0.1, shape=shape)
 76     return tf.Variable(initial)
 77 
 78 # 完成初始化函數定義
 79 
 80 
 81 # 開始定義卷積和池化的函數
 82 # 卷積使用1步長(stride size),0邊距(padding size)的模板,保證輸出和輸入大小相同
 83 # 池化用簡單傳統的2x2大小的模板作max pooling,所以輸出的長寬會變爲輸入的一半
 84 
 85 
 86 def conv2d(x, W):
 87     return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
 88 
 89 
 90 def max_pool_2x2(x):
 91     return tf.nn.max_pool(x, ksize=[1,2,2,1], strides=[1, 2, 2, 1], padding='SAME')
 92 # 完成卷積池化函數定義
 93 
 94 
 95 # 開始定義神經網絡結構定義
 96 # 第一層卷積,卷積在每一個5x5的patch中算出32個特徵
 97 W_conv1 = weight_variable([5, 5, 1, 32])
 98 b_conv1 = bias_variable([32])
 99 x_image = tf.reshape(x, [-1, 28, 28, 1])
100 # 第二、第3維對應圖片的寬、高,最後一維表明圖片的顏色通道數(由於是灰度圖因此這裏的通道數爲1,若是是rgb彩色圖,則爲3)
101 h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
102 h_pool1 = max_pool_2x2(h_conv1)
103 
104 # 第二層卷積,每一個5x5的patch會獲得64個特徵
105 W_conv2 = weight_variable([5, 5, 32, 64])
106 b_conv2 = bias_variable([64])
107 h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
108 h_pool2 = max_pool_2x2(h_conv2)
109 
110 # 有1024個神經元的全鏈接層,此時圖片大小爲7*7
111 W_fc1 = weight_variable([7*7*64, 1024])
112 b_fc1 = bias_variable([1024])
113 h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
114 h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
115 
116 # 爲了減小過擬合,在輸出層以前加入dropout。用一個placeholder表明一個神經元的輸出在dropout中保持不變的機率。
117 # 這樣能夠在訓練過程當中啓用dropout,在測試過程當中關閉dropout。
118 keep_prob = tf.placeholder(tf.float32)
119 h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
120 # softmax輸出層
121 W_fc2 = weight_variable([1024, 26])
122 b_fc2 = bias_variable([26])
123 y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
124 # 應爲 y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
125 # 完成神經網絡結構定義
126 
127 # 開始定義訓練和評估操做
128 # 用更加複雜的ADAM優化器來作梯度最速降低,在feed_dict中加入額外的參數keep_prob來控制dropout比例
129 cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y_conv))
130 train_step = tf.train.AdamOptimizer(1e-6).minimize(cross_entropy)
131 correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))
132 accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
133 # 完成訓練和評估操做的定義
134 
135 # 開始定義儲存器操做並裝載已經訓練過的神經網絡
136 saver = tf.train.Saver(write_version=tf.train.SaverDef.V1)
137 sess = tf.InteractiveSession()
138 saver.restore(sess, "cnnres/model.ckpt")
139 # sess.run(tf.global_variables_initializer())
140 # 完成定義儲存器操做和裝載神經網絡
141 
142 # 開始對神經網絡進行輸入測試
143 res = sess.run(y_conv, feed_dict={x: test_data, keep_prob: 1.0})
144 temp = np.argmax(res)
145 letter = chr(97+temp)
146 print('The test letter is '+letter)
147 # 完成測試

 

備註:

1.要求已經被訓練完成的模型儲存在cnnres/model.ckpt

2.預測函數爲:res = sess.run(y_conv, feed_dict={x: test_data, keep_prob: 1.0})

不要試圖計算y_佔位符的值,那是用於訓練的,不是用於結果預測的

對CNN的效果備註和研究:

1.在訓練集爲較粗字體的狀況下測試圖片必須相應採用較粗字體,不然結果不好

相關文章
相關標籤/搜索