人工智能(AI)庫TensorFlow 踩坑日記之二


上次 踩坑日志之一 遺留的問題終於解決了,所以作者(也就是我)終於有臉出來寫第二篇了。

   首先還是貼上 卷積算法的示例代碼地址 :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

 



 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM