上次 踩坑日志之一 遺留的問題終於解決了,所以作者(也就是我)終於有臉出來寫第二篇了。
首先還是貼上 卷積算法的示例代碼地址 :https://github.com/tensorflow/models
這個庫里面主要是一些常用的模型用tensorflow實現之后的代碼。其中我用的是
models/tree/master/tutorials/image/cifar10 這個示例,上一篇也大致講過了。
關於上次遇到問題是:
雖然訓練了很多次,但是每次實際去用時都是相同的結果。這個問題主要原因是
在核心代碼文件cifar10.py里
tf.app.flags.DEFINE_integer('batch_size', 128,
"""Number of images to process in a batch.""")
被我改成 batch_size =1
一開始我誤以為這個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
https://github.com/g7rhythm/MYcifar255