2018年6月2日 星期六

Golang: 使用 Tensorflow 影像標籤辨識模型

Tensorflow 之 Tensor 意指張量,可以說是一資料的向量資料,通俗的說就是一包一包的 tensor,即使不知道如何訓練 Tensorflow 等機器學習的模型,也可以單方面的只應用一個機器學習模型來進行辨識,此篇將說明記載如何將任意一個機器學習模型用來使用於自己的專案中。


Tensorflow 模型解釋


若是在訓練 tensorflow 模型階段,應該幾乎都有接觸過 tensorboard 這個 tensorflow 附加的視覺化訓練過程的工具,如果沒有,也可以參考看看下圖。


這張圖片是來自 Tensorflow 官方的 Tensorboard 介紹的圖片,其中圖片裡面的那些流程示意,其實就是機器學習過程中的圖 (Graph),這些圖用來表示神經網路的流程路線。

若看過機器學習的一些圖片,應該會看到數個 input leayer 以及很多 hidden layer 和一個(通常) output layer,實際上 input layer 就是神經網路的感知器,也是程式可以用來輸入資料的點,最後對應到的 output layer 就是神經網路最後的輸出,中間的 hidden layer (隱藏層),是用來做各種非線性分類的部分。 (關於線性分類和非線性分類,可以到 Tensorflow Playground 玩玩看)

進入重點,在上圖中的右邊看到的 Inputs 和 Outputs 中有一些列表,列中這些名稱就是對應的神經網路在這張圖 (Graph) 的位置,程式端中也會用這個 Graph 中的位置輸入資料和取得輸出資料。

安裝 Tensorflow Golang API 環境


Golang 使用 Tensorflow API 要透過 Tensorflow 提供的 C++ Library ,所以用官方的方式安裝為下,或可以直接參考官方網站的安裝方式: https://www.tensorflow.org/install/install_go

下載:
TF_TYPE="cpu" # Change to "gpu" for GPU support
 TARGET_DIRECTORY='/usr/local'
 curl -L \
   "https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow-${TF_TYPE}-$(go env GOOS)-x86_64-1.8.0.tar.gz" |
 sudo tar -C $TARGET_DIRECTORY -xz
安裝 go 的模組:
go get github.com/tensorflow/tensorflow/tensorflow/go

測試 go 模組:
go test github.com/tensorflow/tensorflow/tensorflow/go

如果 go 模組測試這邊出現被 Killed 的情況,可能是 go 的版本太舊了,升級過 1.8 版以上的 go 就會正常。

下載及使用模型


本篇測試使用兩個官方提供的影像分類的模型,分別為:



下載後,請把解壓縮的檔案放到專案目錄下的 model 資料夾中。

以上模型的描述,以 Python 或 C++ 呈現的教學在這個網址可以詳細閱讀:
https://www.tensorflow.org/tutorials/image_recognition

以下章節為說明,完整程式碼請見最下面的章節 <流程>、<完整程式碼>。

輸入資料的形式


要輸入資料,首先必須把原本的資料包裝成一個 Tensor(張量),Tensor 裡面就包含有圖(Graph) 在裡面,在本篇即是把一張要用來辨識的圖片包裝成 Tensor,其中本篇使用的程式碼中,包成 tensor 時有 4 主要參數:

  • H (高)
  • W (寬)
  • Mean (均值)
  • Scale (縮放係數)

設定方式是要注意去看模型下載處的文件是否提到如何設定,例如像是本篇使用的 inception_v3_2016_08_28_frozen 模型的官方說明是:


所以這個模型我調整圖寬高 mean 和 scale 值我分別使用是 299,299,0,255。

包成 Tensor 的程式則是 (以下全省略 err ,有需要請自行加上):

//把輸入的圖片打包變成 Tensor
func makeTensorFromImage(imgFile *bytes.Buffer, imgFormat string) (*tf.Tensor, error) {
        //把輸入圖片資料放進一個新的 Tensor
 tensor, _ := tf.NewTensor(imgFile.String())

        //為輸入的 Tensor 建立一個給 Tensor 的圖,並給予輸入點和輸出點
 graph, input, output, _ := makeTransformImageGraph(imgFormat)

 session, _ := tf.NewSession(graph, nil)
 defer session.Close()

        //標準化 Tensor 格式
 normalized, _ := session.Run(
  map[tf.Output]*tf.Tensor{input: tensor},
  []tf.Output{output},
  nil)
 return normalized[0], nil
}

//建立一個圖 (Graph)
func makeTransformImageGraph(imgFormat string) (graph *tf.Graph, input, output tf.Output, err error) {

        //定義使用圖的參數

        //inception_v3_2016_08_28_frozen.pb 這個模型請使用這個
 const (
  H, W  = 299, 299
  Mean  = float32(0)
  Scale = float32(255)
 )

        //tensorflow_inception_graph.pb 這個模型請使用這個
        const (
  H, W  = 224, 224
  Mean  = float32(117)
  Scale = float32(1)
 )


 s := op.NewScope()
 input = op.Placeholder(s, tf.String)
 // Decode PNG or JPEG
 var decode tf.Output
 if imgFormat == "png" {
  decode = op.DecodePng(s, input, op.DecodePngChannels(3))
 } else {
  decode = op.DecodeJpeg(s, input, op.DecodeJpegChannels(3))
 }
 // Div and Sub perform (value-Mean)/Scale for each pixel
 output = op.Div(s,
  op.Sub(s,
   // 重新調整資料大小變成 H,W 的數值
   op.ResizeBilinear(s,
    // Create a batch containing a single image
    op.ExpandDims(s,
     // Use decoded pixel values
     op.Cast(s, decode, tf.Float),
     op.Const(s.SubScope("make_batch"), int32(0))),
    op.Const(s.SubScope("size"), []int32{H, W})),
   op.Const(s.SubScope("mean"), Mean)),
  op.Const(s.SubScope("scale"), Scale))
 graph, err = s.Finalize()
 return graph, input, output, err
}


輸出資料的形式


資料輸出的形式是變成一個標籤文字的機率,因為每個標籤都會有機率,為了限制傳輸大小,要自己寫程式過濾成只回傳最佳的 5 個結果(過濾器請參考最後的 gist 程式說明)

//把剛才的輸入圖片包裝成 tensor
tensor, _ := makeTensorFromImage(&imageBuffer, imageName[:1][0])

//新建一個輸入進神經網路模型的會話, graph 就是這個神經網路的原始模型
session, _ := tf.NewSession(graph, nil)

//最後要關閉會話
defer session.Close()

output, err := session.Run(
 map[tf.Output]*tf.Tensor{
                //在這裡定義 "input" 字串為輸入點的名稱,有些文建會告訴你寫 import/input 事實上在這邊 "可能" 不用加上 import
                //本文章兩個模型的名稱都叫 input   
                graph.Operation("input").Output(0): tensor,
 },
 []tf.Output{

                //同上部分,但 inception_v3_2016_08_28_frozen.pb 這個模型是用這行
  graph.Operation("InceptionV3/Predictions/Reshape_1").Output(0),

                //tensorflow_inception_graph.pb 這個模型用這行
                graph.Operation("output").Output(0),
 },nil)
if err != nil {
 log.Fatal(err)
}


流程


  1. 安裝 Tensorflow C++ 環境
  2. 安裝 Tensorflow for Go
  3. 下載模型放到 model 資料夾中
  4. 把 gist 裡面的 index.html 放到 public 資料夾中
  5. 修改輸入端轉換成 Tensor 的參數 (W,H,Mean,Scale)
  6. 修改輸出端、輸入端的名稱 

完整程式碼 (包含兩個不同模型的程式碼 a 是 inception_v3_2016_08_28_frozen, b 是 tensorflow_inception_graph):
https://gist.github.com/hpcslag/3e13f48b61ff5869863633d32fc5c25d


Reference:
https://www.tensorflow.org/tutorials/image_recognition
https://stackoverflow.com/questions/44425676/go-test-github-com-tensorflow-tensorflow-tensorflow-go-failed
https://blog.metaflow.fr/tensorflow-how-to-freeze-a-model-and-serve-it-with-a-python-api-d4f3596b3adc
https://www.tensorflow.org/programmers_guide/graph_viz
https://blog.csdn.net/Leo_Xu06/article/details/79054326
https://outcrawl.com/image-recognition-api-go-tensorflow/
https://www.tensorflow.org/install/install_go
https://github.com/tensorflow/tensorflow/blob/master/tensorflow/examples/label_image/label_image.py
https://www.tensorflow.org/extend/tool_developers/
https://godoc.org/github.com/tensorflow/tensorflow/tensorflow/go#example-PartialRun

沒有留言:

張貼留言

© ERIC RILEY , 自由無須告知轉貼
Background Japanese Sayagata by Olga Libby