golang 加載並運行tensorflow模型


  1. 流程
    go運行tensorflow模型流程

  2. 環境搭建
    1. 參考鏈接 官方文檔:
    https://tensorflow.google.cn/install/lang_go
    2. 注意配置其環境變量
    環境變量配置
    3. 拉取golang tensorflow api代碼包

           ```shell
           go get github.com/tensorflow/tensorflow/tensorflow/go
           ```
    
           1. 在安裝過程中遇到問題及解決
                1. 問題:
    
                  ```shell
                         running into this error for TF v2.3.1. is there a way to generate this package?
                              └─ $ ▶ go test github.com/tensorflow/tensorflow/tensorflow/go
                         saved_model.go:25:2: cannot find package "github.com/tensorflow/tensorflow/tensorflow/go/core/protobuf/for_core_protos_go_proto" in any of:
                                                                   /home/sdeoras/go/src/github.com/tensorflow/tensorflow/tensorflow/go/vendor/github.com/tensorflow/tensorflow/tensorflow/go/core/protobuf/for_core_protos_go_proto (vendor tree)
                         /usr/local/go/src/github.com/tensorflow/tensorflow/tensorflow/go/core/protobuf/for_core_protos_go_proto (from $GOROOT)
                         /home/sdeoras/go/src/github.com/tensorflow/tensorflow/tensorflow/go/core/protobuf/for_core_protos_go_proto (from $GOPATH)
                       ```
    
             2. 解決方案:
                  1. 在github上找到的解決方案:將加載的tensorflow 的go api包版本回退到1.15,將2.2步驟中配置的C的環境也回退到1.15版本
    
                         ```shell
                         Reverted to TF v1.15.0 and things are working fine:
                         downloaded c-lib from https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow-cpu-linux-x86_64-1.15.0.tar.gz
                         ```
    
                  2. 偶然間發現  tensorflow/tensorflow/go/genop/generate.sh 位置有個腳本,打開文件查看其內容,里面用到了protobuf生成代碼的命令,其實就是將tensorflow項目中的幾個地方的 .proto 文件轉為需要的go文件,然后再看 3.1.1中的問題,就是有一些文件沒找到,剛好可以對應上。
                      執行這個腳本的過程中可能會遇到一些錯誤(需要安裝protobuf的環境,可以把報的錯誤直接google或者百度即可),將生成代碼后的文件夾復制到3.1.1中報錯的路徑下即可解決該問題
    
  3. 示例

    package main
    
    /**
     * User: zhuchenglin
     */
    
    import (
    	"bufio"
    	"fmt"
    	"github.com/wangbin/jiebago"
    	"io"
    	"os"
    	"strings"
    	tf "github.com/tensorflow/tensorflow/tensorflow/go"
    )
    
    var rootPath ,_= os.Getwd()
    
    var seg jiebago.Segmenter
    
    
    func main()  {
    	// 將句子分詞
    	dictPath := rootPath + "/models/詞庫.txt"
    	err := seg.LoadDictionary(dictPath)
    	if err != nil {
    		fmt.Println(err.Error(), "========")
    		return
    	}
    	myDictPath := rootPath + "/models/不分詞的詞庫.txt"
    	err = seg.LoadUserDictionary(myDictPath)
    	if err != nil {
    		fmt.Println(err.Error(), "========")
    		return
    	}
    
    	content  := "文字信息"
    	content = strings.Trim(content, " \n \r ")
    	ch := seg.Cut(content, true)
    	words := make([]string, 0)
    	for word:= range ch{
    		words = append(words, word)
    	}
    	// fmt.Println(words)
    
    
    	// 加載詞庫
    	//對於python中有些數據處理的庫不存在,暫時采取了使用python按照對應的規則將模型詞庫結果輸出到txt文件中,然后用go通過讀取文件的方式讀取 數據格式化的規則
    	filePath := rootPath + "/models/模型詞庫.txt"
    	startLine := 1
    	allWords, err := ReadRecvFromTxt(filePath, startLine)
    
    	if err != nil {
    		fmt.Println(err.Error(), "===========")
    		return
    	}
    
    	// 格式化輸入數據
    	// 這個需要跟模型定義者去對格式化數據輸入的過程,要跟python數據格式化數據過程保持一致
    	// 定義輸入矩陣的最大長度
    	maxLength := x
    	inputArr := make([]float32, len(words))
    	for k, word := range words {
    		index, ok := allWords[word]
    //		fmt.Println(word, ok)
    		if ok {
    			inputArr[k] = float32(index.(int))
    		} else {
    			inputArr[k] = 0
    		}
    	}
    
    	inputData := [1][]float32{}
    	inputData[0] = inputArr
    
    	// 輸入數據矩陣
    	fmt.Println(inputData)
    
    	// 加載模型
    	modelPath := rootPath + "model/path/dir"
    	// 如果在python導出模型的文件時候沒有指定tag,默認就是serve
    	tags := []string{"serve"}
    	model, err := tf.LoadSavedModel(modelPath, tags, nil) // 載入模型
    	if err != nil {
    		fmt.Println(err.Error(),"-------------")
    		return
    	}
    
    	// 獲取模型里面的operations
    	//for _, op := range model.Graph.Operations() {
    		//log.Printf("Op name: %v, on device: %v", op.Name(), op.Device())
    		//log.Printf("opInputNum: %d, opOutputNum: %d, opType: %v, op name:%v, device: %v",op.NumInputs(),op.NumOutputs(),op.Type(), op.Name(), op.Device())
    		//log.Printf("Op name: %v", op.Name())
    	//}
    
    	// 將數據輸入到模型並得到結果
    	tensor, err := tf.NewTensor(inputData)
    	if err != nil{
    		fmt.Println(err.Error(),"==========---------=====")
    		return
    	}
    
    	// 當python 定義模型的時候,沒有指定對應輸入輸出操作層名稱時,下面的是默認的名稱
    	result, err := model.Session.Run(
    		map[tf.Output]*tf.Tensor{
    			model.Graph.Operation("serving_default_embedding_input").Output(0): tensor,
    		},
    		[]tf.Output{
    			model.Graph.Operation("StatefulPartitionedCall").Output(0),
    		}, nil,
    	)
    	if err != nil {
    		fmt.Println(err.Error(),"=======================")
    		return
    	}
    
    	// 最后輸出的結果跟算法模型的定義有關
    	fmt.Println(result)
    
    }
    
    
    // 從文件加載詞庫    可以根據文件的實際格式進行調整
    func ReadWordsFromTxt(filePath string) (map[string] interface{}, error)  {
    	f, err := os.Open(filePath)
    	if err != nil {
    		fmt.Println(err.Error())
    		return nil, err
    	}
    	defer f.Close()
    	allWords := make(map[string] interface{})
    	rd := bufio.NewReader(f)
    	for {
    		word , err := rd.ReadString('\n')
    		if err != nil || io.EOF == err {
    			break
    		}
    		word = strings.Trim(word, " \r\n ")
    		allWords[word] = true
    	}
    	return allWords , nil
    }
    
    // 從詞庫的txt文件中獲取詞庫   這個可以根據文件的實際格式進行調整
    func ReadRecvFromTxt(filePath string, startLine int) (map[string]interface{}, error) {
    	f, err := os.Open(filePath)
    	if err != nil {
    		fmt.Println(err.Error())
    		return nil, err
    	}
    	defer f.Close()
    
    	allWords := make(map[string]interface{})
    	rd := bufio.NewReader(f)
    	lineNum := 1
    	for {
    		line , err := rd.ReadString('\n')
    		if err != nil || io.EOF == err {
    			break
    		}
    		if lineNum >= startLine {
    			wordArr := strings.Split(line, " ")
    			word := strings.Trim(wordArr[0]," \n ")
    			allWords[word] = lineNum
    		}
    		lineNum ++
    	}
    	return allWords , nil
    }
    
  4. 優化

    1. 問題:在運行過程中cpu占用率過高(其過程是大量運算的過程)

    2. 解決(三方面):

      1. 更新所用tensorflow go api 及 tensorflow C庫 為最新版本,其C庫含有一些新的指令集(AVX AVX2、FMA等),可以加速運算

      2. 借助 go.uber.org/automaxprocs 庫 在docker+k8s的環境下優化 golang 的 runtime.gomaxprocs 參數,防止並行的任務過多,會造成頻繁系統調用創建大量系統線程,大概原理如下:

        在這個里面 cat /proc/self/mountinfo 可以查找到 資源的掛載信息
        
        cd /sys/fs/cgroup/cpu/
        
        cfs.cpu_period_us 文件記錄了調度周期,單位是 us;默認值一般是 100'000,即 100 ms
        cfs.cpu_quota_us 記錄了每個調度周期進程允許使用 cpu 的量,單位也是 us。
        值為 -1 表示無限制;對於 4C 的容器,這個值一般是 400'000
        

        詳情可參考 :https://www.bilibili.com/read/cv8081272

      3. 將模型運算任務放在GPU上去運行

        1. 需要安裝gpu驅動 (https://tensorflow.google.cn/install/gpu)
        2. 需要更換 tensorflow C庫 gpu版本

注:如需轉載請注明出處:https://www.cnblogs.com/zhuchenglin/p/15089360.html


免責聲明!

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



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