最近在抽時間學習TensorFlow這個DL庫的使用,學的斷斷續續的,看官網上第一個案例就是訓練手寫字符識別。我以前在作Weibo.cn驗證碼識別的時候,本身搞了一個數據集,當時用的c++庫tiny-dnn進行訓練的(見:驗證碼破解技術四部曲之使用卷積神經網絡(四)),如今我把它移植到TensorFlow上試試。python
完整代碼見:weibo.cn/tensorflow-implc++
TensorFlow-1.0git
scikit-learn-0.18github
pillow數組
數據集下載地址:training_set.zip 網絡
解壓事後以下圖: session
我把同一類的圖片放到了一個文件夾裏,文件夾的名字也就是圖片的label,打開文件夾後能夠看到字符的圖片信息。 app
下面,咱們把數據加載到一個pickle文件裏面,它須要有train_dataset、train_labels、test_dataset、test_labels四個變量表明訓練集和測試集的數據和標籤。dom
此外,還須要有個label_map,用來把訓練的標籤和實際的標籤對應,好比說3對應字母M,4對應字母N。ide
此部分的代碼見:load_models.py。注:不少的代碼參考自udacity的deeplearning課程。
首先根據文件夾的來加載全部的數據,index表明訓練裏的標籤,label表明實際的標籤,使用PIL讀取圖片,並轉換成numpy數組。
import numpy as np import os from PIL import Image def load_dataset(): dataset = [] labelset = [] label_map = {} base_dir = "../trainer/training_set/" # 數據集的位置 labels = os.listdir(base_dir) for index, label in enumerate(labels): if label == "ERROR" or label == ".DS_Store": continue print "loading:", label, "index:", index try: image_files = os.listdir(base_dir + label) for image_file in image_files: image_path = base_dir + label + "/" + image_file im = Image.open(image_path).convert('L') dataset.append(np.asarray(im, dtype=np.float32)) labelset.append(index) label_map[index] = label except: pass return np.array(dataset), np.array(labelset), label_map dataset, labelset, label_map = load_dataset()
接下來,把數據打亂。
def randomize(dataset, labels): permutation = np.random.permutation(labels.shape[0]) shuffled_dataset = dataset[permutation, :, :] shuffled_labels = labels[permutation] return shuffled_dataset, shuffled_labels dataset, labelset = randomize(dataset, labelset)
而後使用scikit-learn的函數,把訓練集和測試集分開。
from sklearn.model_selection import train_test_split train_dataset, test_dataset, train_labels, test_labels = train_test_split(dataset, labelset)
在TensorFlow官網給的例子中,會把label進行One-Hot Encoding
,並把28*28的圖片轉換成了一維向量(784)。以下圖,查看官網例子的模型。
我也把數據轉換了一下,把32*32的圖片轉換成一維向量(1024),並對標籤進行One-Hot Encoding。
def reformat(dataset, labels, image_size, num_labels): dataset = dataset.reshape((-1, image_size * image_size)).astype(np.float32) # Map 1 to [0.0, 1.0, 0.0 ...], 2 to [0.0, 0.0, 1.0 ...] labels = (np.arange(num_labels) == labels[:, None]).astype(np.float32) return dataset, labels train_dataset, train_labels = reformat(train_dataset, train_labels, 32, len(label_map)) test_dataset, test_labels = reformat(test_dataset, test_labels, 32, len(label_map)) print "train_dataset:", train_dataset.shape print "train_labels:", train_labels.shape print "test_dataset:", test_dataset.shape print "test_labels:", test_labels.shape
轉換後,格式就和minist同樣了。
最後,把數據保存到save.pickle裏面。
save = { 'train_dataset': train_dataset, 'train_labels': train_labels, 'test_dataset': test_dataset, 'test_labels': test_labels, 'label_map': label_map } with open("save.pickle", 'wb') as f: pickle.dump(save, f)
加載完數據後,須要驗證一下數據是否正確。我選擇的方法很簡單,就是把trainset的第1個(或者第2個、第n個)圖片打開,看看它的標籤和看到的能不能對上。
import cPickle as pickle from PIL import Image import numpy as np def check_dataset(dataset, labels, label_map, index): data = np.uint8(dataset[index]).reshape((32, 32)) i = np.argwhere(labels[index] == 1)[0][0] im = Image.fromarray(data) im.show() print "label:", label_map[i] if __name__ == '__main__': with open("save.pickle", 'rb') as f: save = pickle.load(f) train_dataset = save['train_dataset'] train_labels = save['train_labels'] test_dataset = save['test_dataset'] test_labels = save['test_labels'] label_map = save['label_map'] # check if the image is corresponding to it's label check_dataset(train_dataset, train_labels, label_map, 0)
運行後,能夠看到第一張圖片是Y,標籤也是正確的。
數據加載好了以後,就能夠開始訓練了,訓練的網絡就使用TensorFlow官網在Deep MNIST for Experts裏提供的就行了。
此部分的代碼見:train.py。
先加載一下模型:
import cPickle as pickle import numpy as np import tensorflow as tf with open("save.pickle", 'rb') as f: save = pickle.load(f) train_dataset = save['train_dataset'] train_labels = save['train_labels'] test_dataset = save['test_dataset'] test_labels = save['test_labels'] label_map = save['label_map'] image_size = 32 num_labels = len(label_map) print "train_dataset:", train_dataset.shape print "train_labels:", train_labels.shape print "test_dataset:", test_dataset.shape print "test_labels:", test_labels.shape print "num_labels:", num_labels
minist的數據都是28*28的,把裏面的網絡改完了以後,以下:
def weight_variable(shape): initial = tf.truncated_normal(shape, stddev=0.1) return tf.Variable(initial) def bias_variable(shape): initial = tf.constant(0.1, shape=shape) return tf.Variable(initial) def conv2d(x, W): return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME') def max_pool_2x2(x): return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME') graph = tf.Graph() with graph.as_default(): x = tf.placeholder(tf.float32, shape=[None, image_size * image_size]) y_ = tf.placeholder(tf.float32, shape=[None, num_labels]) x_image = tf.reshape(x, [-1, 32, 32, 1]) # First Convolutional Layer W_conv1 = weight_variable([5, 5, 1, 32]) b_conv1 = bias_variable([32]) h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) h_pool1 = max_pool_2x2(h_conv1) # Second Convolutional Layer W_conv2 = weight_variable([5, 5, 32, 64]) b_conv2 = bias_variable([64]) h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2) h_pool2 = max_pool_2x2(h_conv2) # Densely Connected Layer W_fc1 = weight_variable([image_size / 4 * image_size / 4 * 64, 1024]) b_fc1 = bias_variable([1024]) h_pool2_flat = tf.reshape(h_pool2, [-1, image_size / 4 * image_size / 4 * 64]) h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1) # Dropout keep_prob = tf.placeholder(tf.float32) h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob) # Readout Layer W_fc2 = weight_variable([1024, num_labels]) b_fc2 = bias_variable([num_labels]) y_conv = tf.matmul(h_fc1_drop, W_fc2) + b_fc2 cross_entropy = tf.reduce_mean( tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y_conv)) train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy) correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1)) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
主要改動就是輸入層把28*28改爲了image_size*image_size(32*32),而後第三層的全鏈接網絡把7*7改爲了image_size/4*image_size/4(8*8),以及把10(手寫字符一共10類)改爲了num_labels。
而後訓練,我這裏把batch_size改爲了128,訓練批次改少了。
batch_size = 128 with tf.Session(graph=graph) as session: tf.global_variables_initializer().run() print("Initialized") for step in range(2001): offset = (step * batch_size) % (train_labels.shape[0] - batch_size) # Generate a minibatch. batch_data = train_dataset[offset:(offset + batch_size), :] batch_labels = train_labels[offset:(offset + batch_size), :] if step % 50 == 0: train_accuracy = accuracy.eval(feed_dict={ x: batch_data, y_: batch_labels, keep_prob: 1.0}) test_accuracy = accuracy.eval(feed_dict={ x: test_dataset, y_: test_labels, keep_prob: 1.0}) print("Step %d, Training accuracy: %g, Test accuracy: %g" % (step, train_accuracy, test_accuracy)) train_step.run(feed_dict={x: batch_data, y_: batch_labels, keep_prob: 0.5}) print("Test accuracy: %g" % accuracy.eval(feed_dict={ x: test_dataset, y_: test_labels, keep_prob: 1.0}))
運行,能夠看到識別率在不斷的上升。
最後,有了接近98%的識別率,只有4000個訓練數據,感受不錯了。