上次 踩坑日志之一 遗留的问题终于解决了,因此做者(也就是我)终于有脸出来写第二篇了。html
首先仍是贴上 卷积算法的示例代码地址 :https://github.com/tensorflow/modelspython
这个库里面主要是一些经常使用的模型用tensorflow实现以后的代码。其中我用的是git
models/tree/master/tutorials/image/cifar10 这个示例,上一篇也大体讲过了。github
关于上次遇到问题是:算法
虽然训练了不少次,可是每次实际去用时都是相同的结果。这个问题主要缘由是api
在核心代码文件cifar10.py里 浏览器
tf.app.flags.DEFINE_integer('batch_size', 128,
"""Number of images to process in a batch.""")
被我改为 batch_size =1app
一开始我误觉得这个batch要跟训练文件的.bin 文件里面的图片数量对应,其实否则。这个batch_size 是为了用分布式
cifar10_input.py
images, label_batch = tf.train.batch( [image, label], batch_size=batch_size, num_threads=num_preprocess_threads, capacity=min_queue_examples + 3 * batch_size)
建立一个图片跟标签的队列,每一个队列128个元素,便于分布式处理。性能
因为改为1以后多是影响是训练效果。致使总体的loss很高,因此识别率不好。有待进一步验证。
2018-03-11 修正
batch_size 做用就是一次性训练这么屡次以后才开始作梯度降低,这样loss 的波动不会太大。
2018-06-19 补充
https://blog.csdn.net/ycheng_sjtu/article/details/49804041
看完这篇文章以后终于对batch_size 有了一个更深入的理解。就是越小的batch会致使局部的梯度波动大,难以收敛。
另一个缘由极可能是最致命的
上一篇讲到label的对应方式是
# Create a queue that produces the filenames to read.
filename_queue = tf.train.string_input_producer(filenames)
label 也是用string_input_producer 作了另一条字符串队列
由于label 是分类名称,也是图片所在文件夹的名称,因此我在外面把图片文件夹名称都丢到一个label的string队列里,而后里面作出队 depuqeue。
这实际上是错误的,由于两条队列要完美保持一致,并且还不能加shuffle 参数 这个参数能够随机获取图片文件,以便训练模型效果更具有泛化能力。
# Create a queue that produces the filenames to read.
filename_queue = tf.train.string_input_producer(filenames,name="filename_queue_hcq",shuffle=True)
shuffle=true 仍是要加的。
label的获取方式就得另外想办法。
把 cifar10_input.py 方法 read_cifar10 改造以下:
def read_cifar10(filename_queue): class CIFAR10Record(object): pass reader = tf.WholeFileReader() key, value = reader.read(filename_queue) image0 = tf.image.decode_jpeg(value,3) esized_image = tf.image.resize_images(image0, [32, 32], method=tf.image.ResizeMethod.AREA) result = ImageNetRecord() re2=splitfilenames(tf.constant( filenames),len(filenames)) key=splitfilenames(tf.reshape(key,[1],name="key_debug"),1) label=diff(re2,key)
其中 splitilenames ,diff 方法是我新增的,主要是为了把文件所在目录的路劲切出来
def splitfilenames(inputs,allstringlen): a = tf.string_split(inputs, "/\\") bigin = tf.cast(tf.size(a.values) / allstringlen -2, tf.int32) slitsinglelen = tf.cast(tf.size(a.values) / allstringlen, tf.int32) val = tf.reshape(a.values, [allstringlen, slitsinglelen]) re2 = tf.cast(tf.slice(val, [0, bigin], [allstringlen, 1]),tf.string) re2 =tf.reshape(re2,[allstringlen]) re2 =tf.unique(re2).y return re2
好比”H:\imagenet\fortest\n01440764“ 切出来 “n01330764”。 这个方法是支持批量处理的。
之因此写的这么麻烦。是由于输入量是tensor,因此全部操做都必须按照tensorflow的api写。
diff方法(代码在下面) 是为了断定key 的分类名在全部分类里面的文件排序位置(数字0-1000之内)。用这个位置做为label。
这里 读者估计有一个疑问
“为啥不直接用分类名‘n01330764’做为label标签去训练呢?”
这里也是迫于无奈,由于原始代码cifar10的后续功能有2个限制,1,label必须是int型,2label最大值不能大于分类总数。因此不能简单把“n”删除而后转成数字 1330764 。
不然会出各类错。修正这2个问题明显比我新增一个diff方法改动更大。
虽然不太优雅,各位看官轻拍。
def diff(re2,key): keys = tf.fill([tf.size(re2)],key[0]) numpoi= tf.cast(tf.equal(re2, keys),tf.int32) numpoi=tf.argmax(numpoi) return numpoi
2018-06-19 修正
后来这里取label的方法仍是换成文件夹按字母排序后的位置做为label了。这样保险不少,并且性能也好一些。
好了,到止为止,train(训练过程)的代码就改完了,能够开始训练了。
cifar10_eval.py 这边须要改个地方。
if len(sys.argv)==1: SinglePicPath = "/tmp/8.jpg"
else: SinglePicPath =sys.argv[1]
经过参数传入 单图片的地址,用来放到生产环境执行识别程序。
先跑一下8.jpg 测试一下
得出来结果是0 之因此有这么多,是由于
cifar10_eval 原来的代码用了一部分跟训练代码一致的过程,其中训练代码中batchsize=128,致使虽然输入只有1张图,输出的结果仍是有128个。有点多余,不过取其中一个做为结果就能够了。
(这里能够在把batchsize改成1,只在运行时用1)
而后我用C# MVC写了一个页面。用来上传图片,而后输出中文结果。
主要核心代码是(C#):
Thumbnails.Of(file).ZoomMethod(Thumbnails.ThumbnailZoomMethod.CUT).Resize(32, 32).ToFiles(imagepath); var p = new Process(); //C:\Users\Administrator\AppData\Local\Programs\Python\Python35\python.exe
p.StartInfo.FileName = @"C:\Users\Administrator\AppData\Local\Programs\Python\Python35\python.exe"; p.StartInfo.Arguments = @"H:\tensorflow_cnn\tensorflow_cnn\tutorials\image\cifar10\cifar10_runsingle.py " + imagepath; p.StartInfo.WorkingDirectory = @"H:\"; p.StartInfo.RedirectStandardOutput = true; p.StartInfo.RedirectStandardError = true; p.StartInfo.UseShellExecute = false; p.Start(); string output = p.StandardOutput.ReadToEnd(); string errmsg = p.StandardError.ReadToEnd(); p.WaitForExit(); p.Close(); var num = Regex.Replace(output, @".*\[array\(\[(\d+),.*\].*", "$1", RegexOptions.Singleline); string result;
主要是把图片改成 32*32 而后用Process 拉起python 去执行 cifar10_runsingle.py (这个是cifar10_eval.py 改造后的)。
而后用正则把 结果的数字切出来。
剩下就是把位置好比 0替换成“n01330764”
“n01330764” 再替换成中文,上一篇有下载中文的连接。
测试一下
------------------------------------------
--------------------------------------
-------------------
由于把eval(评估代码)改为执行代码了,因此eval 测试集(在imagenet下载自带的)反而没应用上了。暂时不能判断识别率高低。这是下一步要改造的地方。
不过imagenet 的数量级太大,正正经经训练一次,估计要好久好久好久。
另外还要切换成GPU模式,估计不太难。有教程。
期待踩坑日志之三吧,烧年们。
另外说一点关于tensorboard的,这东西真是厉害。安装方便。直接命令行
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple tensorflow-tensorboard
-i 地址是用了清华的pip镜像 这样安装比较快不容易出错。
而后
logdir 指向
wt = tf.summary.FileWriter("/tmp/broaddata3")
代码中设置的 summary的地址。
而后打开浏览器查看。
这里有个坑须要注意, tensorboard的运行盘符必须跟log所在盘符一致,不然一直提示拿不到文件。
这里能够看到我新增了一个label_sum 的曲线图,能够看到确实拿到不一样的label了,并且最小是从0开始的。
此致敬礼。
更新:
2019-03-22
因为以前的例子都是一张一张读取图片,最后我仍是修改成打包进Bin文件,而后训练时再一次性读取
源码路径以下 使用方法查看项目 ReadME