TensorFlow-手書き数字認識(2)

この記事は、以前のTensorFlow-手書き数字認識(1)に基づいて改善されており、主に次の3つのポイントを達成しています。

  • ブレークポイントでトレーニングを再開します

  • 実際の写真をテストする

  • TFRecords形式のデータセットを作成する

ブレークポイントでトレーニングを再開します

前のコードは、モデルがトレーニングされるたびにトレーニングを再開し、前のトレーニング結果が上書きされるため、非常に不便です。

backwork.pyにckpt操作を追加すると、ブレークポイントトレーニング機能を実現できます。

コード


with tf.Session() as sess:
        init_op = tf.global_variables_initializer()
        sess.run(init_op)

        ckpt = tf.train.get_checkpoint_state(MODEL_SAVE_PATH)
        if ckpt and ckpt.model_checkpoint_path:
            saver.restore(sess, ckpt.model_checkpoint_path)

        for i in range(STEPS):
            xs, ys = sess.run([img_batch, label_batch])
            _, loss_value, step = sess.run([train_op, loss, global_step], feed_dict={x: xs, y_: ys})
            if i % 100 == 0:
                print("After %d training step(s), loss on training batch is %g." % (step, loss_value))
                saver.save(sess, os.path.join(MODEL_SAVE_PATH, MODEL_NAME), global_step=global_step)

注釈:

  • tf.train.get_checkpoint_state(checkpoint_dir、latest_filename = None)

この関数は、ブレークポイントフォルダーに有効なブレークポイント状態ファイルが含まれている場合、それが返されることを示します。

  • checkpoint_dir:ブレークポイントファイルが保存されているディレクトリを示します

  • latest_filename = None:ブレークポイントファイルのオプションの名前。デフォルトは「チェックポイント」です。

  • saver.restore(sess、ckpt.model_checkpoint_path)

この関数は、現在のセッションを再開し、ckptの値をwとbに割り当てることを意味します。

  • sess:現在のセッションを示し、以前に保存された結果がこのセッションにロードされます

  • ckpt.model_checkpoint_path:モデルが保存されている場所を示します。モデルの名前を指定する必要はありません。チェックポイントファイルをチェックして、誰が最新で何が呼び出されているかを確認します。

コード検証

コード実行効果:

 RESTART: G:\TestProject\python\tensorflow\...\mnist_backward.py
After 16203 training step(s), loss on training batch is 0.155758.
After 16303 training step(s), loss on training batch is 0.173135.
After 16403 training step(s), loss on training batch is 0.159716.

プログラムは以前のトレーニングデータに従ってからトレーニングできることがわかります。

実際の画像を入力し、予測結果を出力します

最後のコードは、MNIST独自のデータセット内のデータのみをトレーニングに使用できます。
今回は、mnist_app.py関数を記述することで、実際の画像データの予測を実現します。

入力と出力の状況を分析します。

  • ネットワーク入力:1次元配列(784ピクセル)

  • ピクセル:0-1の間の浮動小数点数(0に近いほど黒く、1に近いほど白い)

  • ネットワーク出力:1次元配列(10の確率確率)、配列の最大要素に対応するインデックス番号が予測結果です

入力画像インターフェース機能を定義する


def application():
    testNum = input("input the number of test pictures:")
    for i in range(testNum):
        testPic = raw_input("the path of test picture:")
        testPicArr = pre_pic(testPic)
        preValue = restore_model(testPicArr)
        print "The prediction number is:", preValue

タスクは2つの機能で完了します

  • testPicArr = pre_pic(testPic)手書きのデジタル画像を前処理します

  • preValue = restore_model(testPicArr)ニューラルネットワークの入力要件を満たす画像をリカレントニューラルネットワークモデルにフィードし、予測値を出力します

特定のコードの実装:

  • 画像前処理機能


サイズ変更、グレースケール変換、2値化操作を含む前処理関数def pre_pic(picName):
img = Image.open(picName)#テストする画像をロード(白い背景)
reIm = img.resize((28,28)、Image .ANTIALIAS)#サイズを28x28に
調整しますim_arr = np.array(reIm.convert( 'L'))
threshold = 50
range(28)内のiのバイナリしきい値
range(28)内のjの場合:
im_arr [i] [j] = 255-im_arr [i] [j]#反色(
黑底if(im_arr [i] [j] <threshold):#
黑底白字im_arr [i] [j] = 0
else:
im_arr [ i] [j] = 255

nm_arr = im_arr.reshape([1, 784]) #图片转成1行
nm_arr = nm_arr.astype(np.float32)
img_ready = np.multiply(nm_arr, 1.0/255.0) #取值范围限制在0~1之间

return img_ready

* 获取训练参数进行预测函数

def restore_model(testPicArr):
with tf.Graph()。as_default()as tg:
x = tf.placeholder(tf.float32、[None、mnist_forward.INPUT_NODE])
y = mnist_forward.forward(x、None)
preValue = tf .argmax(y、1)

    variable_averages = tf.train.ExponentialMovingAverage(mnist_backward.MOVING_AVERAGE_DECAY)
    variables_to_restore = variable_averages.variables_to_restore()
    saver = tf.train.Saver(variables_to_restore)

    with tf.Session() as sess:
        ckpt = tf.train.get_checkpoint_state(mnist_backward.MODEL_SAVE_PATH)
        if ckpt and ckpt.model_checkpoint_path:
            saver.restore(sess, ckpt.model_checkpoint_path)
            preValue = sess.run(preValue, feed_dict={x:testPicArr})
            return preValue
        else:
            print("No checkpoint file found")
            return -1
注解:

1)main 函数中调用的application()函数:输入要识别的几张图片(注意要给出待识别图片的路径和名称)。

2)代码处理过程:

* 模型的要求是黑底白字,但输入的图是白底黑字,所以需要对每个像素点的值改为 255 减去原值以得到互补的反色。

* 对图片做二值化处理(这样以滤掉噪声,另外调试中可适当调节阈值)。

* 把图片形状拉成1行784列,并把值变为浮点型(因为要求像素点是 0-1之间的浮点数)。

* 接着让现有的 RGB 图从0-255之间的数变为 0-1 之间的浮点数。

* 运行完成后返回到 main 函数。

* 计算求得输出 y,y的最大值所对应的列表索引号就是预测结果。

**代码验证**

1)运行 mnist_backward.py 首先对模型进行训练

再起動:G:\ TestProject \ python \ tensorflow ... \ mnist_backward.py
16203トレーニングステップの後、トレーニングバッチの損失は0.155758です。
16303トレーニングステップの後、トレーニングバッチの損失は0.173135です。
16403トレーニングステップの後、トレーニングバッチの損失は0.159716です。


2)运行 mnist_test.py 使用测试集,监测模型的准确率

再起動:G:\ TestProject \ python \ tensorflow ... \ mnist_test.py
16703トレーニングステップの後、テスト精度= 0.9798


3)运行 mnist_app.py 输入1~10之间的数(表示循环验证的图片数量)

再起動:G:\ TestProject \ python \ tensorflow ... \ mnist_app.py
テスト画像の数を入力します:5テスト画像
のパス:pic \ 0.png
予測番号は次のとおりです:[0]
テスト画像のパス: pic \ 1.png
予測番号:[3]
テスト画像のパス:pic \ 5.png
予測番号:[5]
テスト画像のパス:pic \ 8.png
予測番号:[8 ]
テスト画像のパス:pic \ 9.png
予測番号は次のとおりです。[9]


**制作数据集,实现特定应用**

上次的程序使用的MNIST整理好的特定格式的数据,如果想要用自己的图片进行模型训练,
就需要自己制作数据集。

数据集的制作的不仅仅是将图片整理在一起,通过转换成特定的格式,可以加速图片读取的效率。

下面将MNIST数据集转换成tfrecords格式,该方法也可以将普通图片转换为该格式。

**编写数据集生成读取文件(mnist_ generateds.py)**

**tfrecords文件**

* tfrecords :一种二进制文件,可先将图片和标签制作成该格式的文件,使用tfrecords进行数据读取会提高内存利用率

* tf.train.Example:用来存储训练数据,训练数据的特征用键值对的形式表示

* SerializeToString( ):把数据序列化成字符串存储

**生成tfrecords文件**

读取原始图片和标签文件,转换为tfrecord格式

def write_tfRecord(tfRecordName、image_path、label_path):
writer = tf.python_io.TFRecordWriter(tfRecordName)#ライターを
作成するnum_pic = 0
f = open(label_path、 'r'
)#ラベルファイルの内容を開く= f.readlines()#読み取り入力(形式:2028_7.jpg 7)
f.close()
コンテンツのコンテンツ:各画像と対応するラベル
値を
トラバース= content.split ()#分割:画像名+対応するラベルimg_path = image_path + value [ 0]
img = Image.open(img_path)#対応する画像ファイルを
開くimg_raw = img.tobytes()
labels = [0] * 10labels
[int(value [1])] = 1
各画像とラベルを例
例= tf.train.Example(機能= tf.train.Features(機能= {
'img_raw':tf.train.Feature(bytes_list = tf.train.BytesList(値= [img_raw]))、
'label':tf.train.Feature(int64_list = tf.train.Int64List(value = labels))
}))
writer.write(example.SerializeToString())#♪example。行序列化
num_pic + = 1
print( "the画像の数: "、num_pic)
writer.close()#関闭writer
print(" write tfrecord success ")

注解:

* writer = tf.python_io.TFRecordWriter( tfRecordName) :新建一个 writer

* for循环:遍历每张图和标签

* writer.write(example.SerializeToString()):把 example 进行序列化

* writer.close():关闭 writer

保存tfrecord格式文件

def generate_tfRecord():
isExists = os.path.exists(data_path)#データセットの保存に使用されるパスが存在するかどうかを確認します存在し
ない場合isExists:
os.makedirs(data_path)
print( 'ディレクトリは正常に作成されました')
else:
print ( 'ディレクトリはすでに存在します')
write_tfRecord(tfRecord_train、image_train_path、label_train_path)
write_tfRecord(tfRecord_test、image_test_path、label_test_path)


**解析 tfrecords 文件**

获取tfrecords文件接口函数

get_tfrecord DEF(NUM、isTrain = True):
IF isTrain:
tfRecord_path = tfRecord_train
else:
tfRecord_path = tfRecord_test
IMG、label = read_tfRecord(tfRecord_path)
img_batch、label_batch tf.train.shuffle_batch =([IMG、label]、
batch_size 、
NUM_THREADS = 2、
容量= 1000、
min_after_dequeue = 700)#
返される画像とラベルはランダムに選択されますbatch_sizeグループ
return img_batch、label_batch


注解:

tf.train.shuffle_batch(tensors、batch_size、capacity、
min_after_dequeue、num_threads = 1、seed = None、
enqueue_many = False、shapes = None、allow_smaller_final_batch = False、
shared_name = None、name = None)


* tensors: 待乱序处理的列表中的样本(图像和标签)

* batch_size: 从队列中提取的新批量大小

* capacity:队列中元素的最大数量

* min_after_dequeue: 出队后队列中的最小数量元素,用于确保元素的混合级别

* num_threads: 排列 tensors 的线程数

* seed:用于队列内的随机洗牌

* enqueue_many: tensor 中的每个张量是否是一个例子

* shapes: 每个示例的形状

* allow_smaller_final_batch: (可选)布尔值。如果为 True,则在队列中剩余数量不足时允许最终批次更小。

* shared_name:(可选)如果设置,该队列将在多个会话中以给定名称共享。

* name:操作的名称(可选)

读取tfrecords文件

def read_tfRecord(tfRecord_path):
filename_queue = tf.train.string_input_producer([tfRecord path]、shuffle = True)
reader = tf.TFRecordReader()#新しいリーダーを作成します
、serialized_example = reader.read(filename_queue)
features = tf.parse_single_example( serialized_example、
features = {
'label':tf.FixedLenFeature([10]、tf.int64)、
'img_raw':tf.FixedLenFeature([]、tf.string)
})
img = tf.decode_raw(features ['img_raw' ]、tf.uint8)#img_raw文字列を8ビットの符号なし整数に
変換img.set_shape([784])#形状を1行784列に変更
img = tf.cast(img、tf.float32)*(1
。/255)#0から1までの浮動小数点数になるlabel = tf.cast(features ['label']、tf.float32)#ラベルリストを浮動小数点数に変更
return img、label#画像とラベルを返す(get_tfrecordに戻る)


注解:

tf.train.string_input_producer(string_tensor、num_epochs = None、
shuffle = True、seed = None、capacity = 32、
shared_name = None、name = None、cancel_op = None)


该函数会生成一个先入先出的队列,文件阅读器会使用它来读取数据

* string_tensor: 存储图像和标签信息的 TFRecord 文件名列表

* num_epochs: 循环读取的轮数(可选)

* shuffle:布尔值(可选),如果为 True,则在每轮随机打乱读取顺序

* seed:随机读取时设置的种子(可选)

* capacity:设置队列容量

* shared_name:(可选) 如果设置,该队列将在多个会话中以给定名称共享。

所有具有此队列的设备都可以通过 shared_name 访问它。
在分布式设置中使用这种方法意味着每个名称只能被访问此操作的其中一个会话看到。

* name:操作的名称(可选)

* cancel_op:取消队列(None)

_, serialized_example = reader.read(filename_queue)
把读出的每个样本保存在 serialized_example 中进行解序列化,标签和图片的键名应该和制作 tfrecords 的键名相同,其中标签给出几分类

tf.parse_single_example(serialized,features,name=None,example_names=None)
该函数可以将 tf.train.Example 协议内存块(protocol buffer)解析为张量。

* serialized: 一个标量字符串张量

* features: 一个字典映射功能键 FixedLenFeature 或 VarLenFeature值,也就是在协议内存块中储存的

* name:操作的名称(可选)

* example_names: 标量字符串联的名称(可选)

**反向传播文件修改图片标签获取的接口( mnist_backward .py)**

**利用多线程提高图片和标签的批获取效率**

将批获取的操作放到线程协调器开启和关闭之间

* 开启线程协调器:

coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(sess = sess、coord = coord)


* 关闭线程协调器:

coord.request_stop()
coord.join(threads)


注解:

tf.train.start_queue_runners(sess = None、coord = None、daemon = True、
start = True、collection = tf.GraphKeys.QUEUE_RUNNERS)


这个函数将会启动输入队列的线程,填充训练样本到队列中,以便出队操作可以从队列中拿到样本。
这种情况下最好配合使用一个tf.train.Coordinator,这样可以在发生错误的情况下正确地关闭这些线程。

* sess:用于运行队列操作的会话。默认为默认会话

* coord:可选协调器,用于协调启动的线程

* daemon: 守护进程,线程是否应该标记为守护进程,这意味着它们不会阻止程序退出

* start:设置为 False 只创建线程,不启动它们

* collection:指定图集合以获取启动队列的 GraphKey,默认为GraphKeys.QUEUE_RUNNERS

修改后的mnist_backward.py的关键部分:

...
import mnist_generateds#【1】
...
train_num_examples = 60000#【2】トレーニングセット内の画像の数

def backward():
...
saver = tf.train.Saver()
img_batch、label_batch = mnist_generateds.get_tfrecord(BATCH_SIZE、isTrain = True)#【3】

with tf.Session() as sess:
    init_op = tf.global_variables_initializer()
    sess.run(init_op)

    ckpt = tf.train.get_checkpoint_state(MODEL_SAVE_PATH)
    if ckpt and ckpt.model_checkpoint_path:
        saver.restore(sess, ckpt.model_checkpoint_path)

    coord = tf.train.Coordinator()#【4】 开启线程协调器
    threads = tf.train.start_queue_runners(sess=sess, coord=coord)#【5】

    for i in range(STEPS):
        xs, ys = sess.run([img_batch, label_batch])#【6】
        _, loss_value, step = sess.run([train_op, loss, global_step], feed_dict={x: xs, y_: ys})
        if i % 100 == 0:
            print("After %d training step(s), loss on training batch is %g." % (step, loss_value))
            saver.save(sess, os.path.join(MODEL_SAVE_PATH, MODEL_NAME), global_step=global_step)

    coord.request_stop()#【7】 关闭线程协调器
    coord.join(threads)#【8】

注解:

* train_num_examples=60000

在梯度下降学习率中需要计算多少轮更新一次学习率,这个值是:总样本数/batch size

* 之前:用mnist.train.num_examples表示总样本数;

* 现在:手动给出训练的总样本数,这个数是6万。

* image_batch, label_batch=mnist_generateds.get_tfrecord(BATCH_SIZE,isTrain=True)

* 之前:用mnist.train.next_batch函数读出图片和标签喂给网络;

* 现在:用函数get_tfrecord替换,一次批获取batch_size张图片和标签。

* isTrain:用来区分训练阶段和测试阶段,True 表示**训练**,False表示**测试**。

* xs,ys=sess.run([img_batch,label_batch])

* 之前:使用函数xs,ys=mnist.train.next_batch(BATCH_SIZE)

* 现在:在sess.run中执行图片和标签的批获取。

**测试文件修改图片标签获取的接口(mnist_test.py)**

修改后的mnist_test.py的关键部分:

#coding:utf-8
...
TEST_NUM = 10000#【1】

def test():
with tf.Graph()。as_default()as g:
...
img_batch、label_batch = mnist_generateds.get_tfrecord(TEST_NUM、isTrain = False)#【2】

    while True:
        with tf.Session() as sess:
            ckpt = tf.train.get_checkpoint_state(mnist_backward.MODEL_SAVE_PATH)
            if ckpt and ckpt.model_checkpoint_path:
                saver.restore(sess, ckpt.model_checkpoint_path)
                global_step = ckpt.model_checkpoint_path.split('/')[-1].split('-')[-1]

                coord = tf.train.Coordinator()#【3】
                threads = tf.train.start_queue_runners(sess=sess, coord=coord)#【4】

                xs, ys = sess.run([img_batch, label_batch])#【5】
                accuracy_score = sess.run(accuracy, feed_dict={x: xs, y_: ys})
                print("After %s training step(s), test accuracy = %g" % (global_step, accuracy_score))

                coord.request_stop()#【6】
                coord.join(threads)#【7】

            else:
                print('No checkpoint file found')
                return
        time.sleep(TEST_INTERVAL_SECS)

注解:

* TEST_NUM=10000

* 之前:用 mnist.test.num_examples 表示总样本数

* 现在:手动给出测试的总样本数,这个数是1万

* image_batch, label_batch=mnist_generateds.get_tfrecord(TEST_NUM,isTrain=False)

* 之前:用 mnist.test.next_batch 函数读出图片和标签喂给网络

* 现在:用函数 get_tfrecord 替换读取所有测试集 1 万张图片

isTrain:用来区分训练阶段和测试阶段,True 表示训练,False 表示测试

* xs,ys=sess.run([img_batch,label_batch])

* 之前:使用函数 xs,ys=mnist.test.next_batch(BATCH_SIZE)

* 现在:在 sess.run 中执行图片和标签的批获取

**代码验证**

运行测试代码 mnist_test.py

再起動:G:\ TestProject \ python \ tensorflow ... \ mnist_test.py
16703トレーニングステップ後、テスト精度= 0.9794
16703トレーニングステップ後、テスト精度= 0.9797
16703トレーニングステップ後、テスト精度= 0.979516703
トレーニングステップ後、テスト精度= 0.9792


运行测试代码 mnist_app.py

再起動:G:\ TestProject \ python \ tensorflow ... \ mnist_app.py
テスト画像の数を入力します:5テスト画像
のパス:pic \ 0.png
予測番号は次のとおりです:[0]
テスト画像のパス: pic \ 1.png
予測番号:[3]
テスト画像のパス:pic \ 5.png
予測番号:[5]
テスト画像のパス:pic \ 8.png
予測番号:[8 ]
テスト画像のパス:pic \ 9.png
予測番号は次のとおりです。[9]



可以看出和之前的结果一样,代码可用。

注:以上测试图片用的是下面教程中自带的图片,测试结果100%准确,我自己用Windows画图板手写了0~9的数字,准确度只有50%左右,可能是我手写字体和MNIST库中的风格差异较大,或是目前的网络还不够好,下一篇通过搭建CNN网络继续测试。

参考:人工智能实践:Tensorflow笔记

おすすめ

転載: blog.51cto.com/15060517/2641120