一、這里的案例相對比較簡單,主要就是通過學習驗證碼的識別來認識深度學習中我們一般在工作中,需要處理的東西會存在哪些東西。
二、因為我沒有數據集,沒有關系,這里自己寫了一個數據集,來做測試,為了方便我把這個數據集,寫成了*.tfrecords格式的文件。
三、生成數據集
1)生成驗證碼圖片
# 生成驗證碼訓練集 def gen_captcha(): captcha_char_list = list("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") font = ImageFont.truetype(font="font/msyh.ttf", size=36) for n in range(2000): image = Image.new('RGB', (200, 50), (255, 255, 255)) draw = ImageDraw.Draw(image) chars = "" for i in range(5): captcha_char = captcha_char_list[random.randint(0, 25)] chars += captcha_char draw.text((10 + i * 40, 0), captcha_char, (0, 0, 0), font=font) image.save(open("data/captcha/" + chars + ".png", 'wb'), 'png')
說明:我這里都是采用的白底黑字的方式來寫的,字體使用微軟的字體。如果需要做干擾,可以自己在網上查找教程
2)寫入tfrecords格式文件
# 將圖片數據和目標值寫到tfrecords文件中 def captcha_data_write(): # 獲取圖片名稱和所有數據 file_names, image_batch = get_image_name_batch() # 獲取目標值 target = get_target(file_names) # 寫入文件 write_tf_records(image_batch, target) def get_image_name_batch(): # 1、讀取圖片目錄,生成文件名稱列表 file_names = os.listdir("data/captcha") file_list = [os.path.join("data/captcha", file_name) for file_name in file_names] print(file_list) # 2、放入隊列(shuffle=False,一定要設置,不然會亂序) file_queue = tf.train.string_input_producer(file_list, shuffle=False) # 3、讀取圖片數據 reader = tf.WholeFileReader() key, value = reader.read(file_queue) # 4、解碼圖片數據 image = tf.image.decode_png(value) # 改變形狀(根據具體的圖片大小來) image.set_shape([50, 200, 3]) # 5、獲取圖片批次 image_batch = tf.train.batch([image], batch_size=2000, num_threads=1, capacity=2000) return file_names, image_batch def get_target(file_names): # 6、獲取目標值 labels = [file_name.split(".")[0] for file_name in file_names] print(labels) # 7、將目標值裝換成具體數值 captcha_char = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" # 轉換成字典,然后反轉(0:0, 1:1, ...,z:35) num_char = dict(enumerate(list(captcha_char))) char_num = dict(zip(num_char.values(), num_char.keys())) # 8、構建標簽列表 array = [] for label in labels: nums = [] for char in label: nums.append(char_num[char]) array.append(nums) # [[2, 11, 8, 2, 7] ...] print(array) # 9、轉換為tensor張量,注意這里的類型一定要給出來,這里我踩過坑,如果沒有,讀數據是存在問題的 target = tf.constant(array, dtype=tf.uint8) return target def write_tf_records(image_batch, target): # 10、上面主要准備圖片數據和目標值,下面主要是寫入到*.tfrecords中 with tf.Session() as sess: coord = tf.train.Coordinator() threads = tf.train.start_queue_runners(sess=sess, coord=coord) # 運算獲取圖片批次數據 image_batch = sess.run(image_batch) # 轉換一下數據為uint8 image_batch = tf.cast(image_batch, tf.uint8) # 寫入數據 with tf.python_io.TFRecordWriter("data/tf_records/captcha.tfrecords") as writer: for i in range(2000): # 全部使用string保存 image_string = image_batch[i].eval().tostring() label_string = target[i].eval().tostring() example = tf.train.Example(features=tf.train.Features(feature={ "image": tf.train.Feature(bytes_list=tf.train.BytesList(value=[image_string])), "label": tf.train.Feature(bytes_list=tf.train.BytesList(value=[label_string])) })) writer.write(example.SerializeToString()) print("寫入第%d數據" % (i + 1)) coord.request_stop() coord.join(threads)
四、訓練和測試
def captcha_train_test(n): # 讀取數據 image_batch, label_batch = tfrecords_read_decode() # 1、建立占位符(數據更具圖片和目標數據而定) with tf.variable_scope("data"): x = tf.placeholder(dtype=tf.uint8, shape=[None, 50, 200, 3], name="x") label = tf.placeholder(dtype=tf.uint8, shape=[None, 5], name="label") # 2、建立模型 y_predict = model(x) # y_predict為[None, 5 * 36] label_batch為[None, 5], 因此需要one-hot[None, 5, 36] y_true = tf.one_hot(label, depth=36, axis=2, on_value=1.0, name="one_hot") # 需要變形為[None 5 * 36] y_true_reshape = tf.reshape(y_true, [-1, 5 * 36], name="y_true_reshape") # 4、計算損失值 with tf.variable_scope("loss"): softmax_cross = tf.nn.softmax_cross_entropy_with_logits(labels=y_true_reshape, logits=y_predict) loss = tf.reduce_mean(softmax_cross) # 5、訓練 with tf.variable_scope("optimizer"): train_op = tf.train.GradientDescentOptimizer(learning_rate=0.001).minimize(loss=loss) # 6、計算准確率 with tf.variable_scope("accuracy"): # 因為這里的真實值是2維結果,所以需要把y_predict,轉化為2位數據 equal_list = tf.equal(tf.argmax(y_true, axis=2), tf.argmax(tf.reshape(y_predict, [-1, 5, 36]), 2)) accuracy = tf.reduce_mean(tf.cast(equal_list, tf.float32)) # 收集數據 tf.add_to_collection("y_predict", y_predict) if n == 1: # 7、會話 with tf.Session() as sess: # 變量初始化 sess.run(tf.global_variables_initializer()) coord = tf.train.Coordinator() threads = tf.train.start_queue_runners(sess=sess, coord=coord) saver = tf.train.Saver() # 如果模型存在,則加載模型 if os.path.exists("model/captcha/checkpoint"): saver.restore(sess, "model/captcha/captcha") for i in range(2000): # 讀取數據 # 訓練,記住這里的數據類型為uint8需要轉換為tf.float32 image_train, label_train = sess.run([image_batch, label_batch]) sess.run(train_op, feed_dict={x: image_train, label: label_train}) # 保存模型 if (i + 1) % 100 == 0: saver.save(sess, "model/captcha/captcha") acc = sess.run(accuracy, feed_dict={x: image_train, label: label_train}) print("第%d次,准確率%f" % ((i + 1), acc)) coord.request_stop() coord.join(threads) else: # 1、讀取指定目錄下圖片數據 file_names_test = os.listdir("data/captcha_test") file_list_test = [os.path.join("data/captcha_test", file_name) for file_name in file_names_test] file_queue_test = tf.train.string_input_producer(file_list_test, shuffle=False) # 2、讀取和解碼數據 reader = tf.WholeFileReader() key, value = reader.read(file_queue_test) image = tf.image.decode_png(value) image.set_shape([50, 200, 3]) # 3、批處理 image_batch_test = tf.train.batch([tf.cast(image, tf.uint8)], batch_size=len(file_names_test), capacity=len(file_names_test)) # 4、加載模型 with tf.Session() as sess: coord = tf.train.Coordinator() threads = tf.train.start_queue_runners(sess=sess, coord=coord) # 1、加載模型 saver = tf.train.Saver() saver.restore(sess, "model/captcha/captcha") # 4、預測[None, 5 * 36] predict = sess.run(y_predict, feed_dict={x: sess.run(image_batch_test)}) captcha_list = list("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") for i in range(len(file_names_test)): predict_reshape = tf.reshape(predict, [-1, 5, 36]) captcha = "" for j in range(5): captcha += captcha_list[tf.argmax(predict_reshape[i][j], 0).eval()] print("預測值:%s, 真實值:%s" % (captcha, file_names_test[i].split(".")[0])) coord.request_stop() coord.join(threads) def model(x): # # 第一層卷積 # with tf.variable_scope("conv_1"): # # 卷積[None, 50, 200, 3] -> [None, 50, 200, 32] # w_1 = gen_weight([5, 5, 3, 32]) # b_1 = gen_bias([32]) # # 在進行模型計算的時候需要使用tf.float32數據進行計算 # x_conv_1 = tf.nn.conv2d(tf.cast(x, tf.float32), filter=w_1, strides=[1, 1, 1, 1], padding="SAME") + b_1 # # 激活 # x_relu_1 = tf.nn.relu(x_conv_1) # # 池化[None, 50, 200, 32] -> [None, 25, 100, 32] # x_pool_1 = tf.nn.max_pool(x_relu_1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding="SAME") # # 第二層卷積 # with tf.variable_scope("conv_2"): # # 卷積[None, 25, 100, 32] -> [None, 25, 100, 64] # w_2 = gen_weight([5, 5, 32, 64]) # b_2 = gen_bias([64]) # x_conv_2 = tf.nn.conv2d(x_pool_1, filter=w_2, strides=[1, 1, 1, 1], padding="SAME") + b_2 # # 激活 # x_relu_2 = tf.nn.relu(x_conv_2) # # 池化[None, 25, 100, 64] -> [None, 13, 50, 64] # x_pool_2 = tf.nn.max_pool(x_relu_2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding="SAME") # # 全連接層 # with tf.variable_scope("full_connection"): # # 生成權重和偏置 # # 為什么是5 * 36主要是我們的字符串個數36個,使用one-hot.每個值為36個值,一個驗證碼5個值,所以為5 * 36個 # w_fc = gen_weight([13 * 50 * 64, 5 * 36]) # b_fc = gen_bias([5 * 36]) # # 修改數據形狀 # x_fc = tf.reshape(x_pool_2, shape=[-1, 13 * 50 * 64]) # # [None, 5 * 36] # y_predict = tf.matmul(x_fc, w_fc) + b_fc with tf.variable_scope("model"): # 生成權重和偏置 # 為什么是5 * 36主要是我們的字符串個數36個,使用one-hot.每個值為36個值,一個驗證碼5個值,所以為5 * 36個 w_fc = gen_weight([50 * 200 * 3, 5 * 36]) b_fc = gen_bias([5 * 36]) # 修改數據形狀 x_fc = tf.reshape(tf.cast(x, tf.float32), shape=[-1, 50 * 200 * 3]) # [None, 5 * 36] y_predict = tf.matmul(x_fc, w_fc) + b_fc return y_predict # 生成權重值 def gen_weight(shape): return tf.Variable(tf.random_normal(shape=shape, mean=0.0, stddev=1.0, dtype=tf.float32)) # 生成偏值 def gen_bias(shape): return tf.Variable(tf.constant(0.0, dtype=tf.float32, shape=shape)) # 讀取數據 def tfrecords_read_decode(): # 將文件加入隊列 file_queue = tf.train.string_input_producer(["data/tf_records/captcha.tfrecords"]) # 讀取tfrecords文件 reader = tf.TFRecordReader() key, value = reader.read(file_queue) # value的格式為example features = tf.parse_single_example(value, features={ "image": tf.FixedLenFeature([], tf.string), "label": tf.FixedLenFeature([], tf.string) }) # 解碼 image_data = tf.decode_raw(features["image"], tf.uint8) label_data = tf.decode_raw(features["label"], tf.uint8) # 改變形狀 image_reshape = tf.reshape(image_data, [50, 200, 3]) label_reshape = tf.reshape(label_data, [5]) # 獲取批次數據 image_batch, label_batch = tf.train.batch([image_reshape, label_reshape], batch_size=200, num_threads=1, capacity=200) return image_batch, label_batch
說明:因為我這里使用的是cpu計算,所以速度上面會很慢,為了看到效果,我這里,使用的全連接層,實際可以使用卷積神經網絡測試。
one-hot:中間有一步是進行了數據one-hot處理的,也就是[5, 23, 15, 20]-->[[0,0,0,0,1,...],[0,0,0,...,1,...],[0,0,0,...,1,...],[0,0,0,...,1,...]]這樣的形式。為什么要這樣做呢?目的是為了計算,在前面的類別我們說明過,計算得出的是概率,這里的5是驗證碼數據的下標,不是概率。我們為了計算概率就需要得出,預測中得出每一個值的概率,然后選取最大的值。
訓練結果:
測試結果:
可以看出,測試結果,還是存在錯誤的情況,不過從准確率上面來說還是很不錯了。