<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                [https://log.zvz.im/2018/07/15/go-tensorflow/](https://log.zvz.im/2018/07/15/go-tensorflow/)? ? ??Go 語言學會 Tensorflow?機器學習的庫 # 使用 Go 語言學會 Tensorflow Tensorflow 并不是一個專門用于機器學習的庫,相反的,它是一個通用的用于圖計算的庫。它的核心部分是用 C++ 實現的,同時還有其它語言的接口庫。Go 語言版本的接口庫與 Python 版本的并不一樣,它不僅有助于我們使用 Go 語言調用 Tensorflow,同時有助于我們了解 Tensorflow 的底層實現。 [![](https://ws1.sinaimg.cn/large/7327fe71gy1fsnt4f7jeej209a07uq3u.jpg)](https://ws1.sinaimg.cn/large/7327fe71gy1fsnt4f7jeej209a07uq3u.jpg) ## [](https://log.zvz.im/2018/07/15/go-tensorflow/#%E6%8E%A5%E5%8F%A3%E5%BA%93 "接口庫")接口庫 Tensorflow 官方發布的代碼庫包含: * C++ 源代碼:Tensorflow 核心功能高層 & 底層操作的代碼實現。 * Python 接口庫 & Python 功能庫:接口庫是通過 C++ 代碼自動生成的,這樣我們可以使用 Python 直接調用到 C++ 的方法:numpy 核心代碼也是這樣實現的。功能庫則是對接口庫方法的組合調用,它實現了大家所熟知的高層 API 接口。 * Java 接口庫 * Go 接口庫 我作為一名 Go 開發者,且不是 Java 愛好者,很自然地選擇了使用 Go 版本的接口庫,研究它能完成哪些任務。 ## [](https://log.zvz.im/2018/07/15/go-tensorflow/#Go-%E6%8E%A5%E5%8F%A3%E5%BA%93 "Go 接口庫")Go 接口庫 首件值得注意的事,正如它的維護者們承認的,就是 Go 接口庫缺少對`變量`支持:這些接口被設計成用于**使用**訓練好的模型,而不是從零開始**訓練**模型。這在[Installing Tensorflow for Go](https://www.tensorflow.org/versions/master/install/install_go)中交待得很清楚。 > Tensorflow 提供了 Go 程序接口。這些接口特別適于加載 Python 庫所創建的模型,然后在 Go 應用中執行。 如果我們對于訓練機器學習模型不那么感興趣:那就恰好!不過,若你對訓練模型感興趣的話,這里有一點建議: > 作為一名真正的 Go 愛好者,應當尋求便宜之道!請使用 Python 來定義和訓練模型;之后,你總是能用 Go 來加載并使用它們的。 簡言之:Go 接口庫可以用來**導入并定義**常量圖;這里說的「常量」是指沒有訓練過程參與,所以沒有可用于訓練的變量。 讓我們立刻開始用 Go 來調用 Tensorflow:創建我們的第一個應用程序。 接下來,我假設你們已經安裝了 Go 環境,并且已經按照[README](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/go/README.md)編譯并安裝了 Tensorflow 的接口庫。 ## [](https://log.zvz.im/2018/07/15/go-tensorflow/#%E7%90%86%E8%A7%A3-Tensorflow-%E7%9A%84%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84 "理解 Tensorflow 的數據結構")理解 Tensorflow 的數據結構 我要在這里重申一下 Tensorflow 的定義(我為大家從[Tensorflow 站點](https://www.tensorflow.org/)的說明中劃出了重點): > TensorFlow? 是一個使用數據流圖進行數值計算的開源軟件庫。圖中的節點**代表**數學操作,而圖中的邊則**代表**節點間相互聯系的多維數據數組(張量)。 我們可以把 Tensorflow 看作是一種描述性語言,類似于 SQL,你可以用它描述你的需求,讓底層引擎(數據庫)解析你的 query 語句,檢查語法和語義錯誤,將其轉化為它的內部描述,優化并計算出結果:最后返回給你正確的結果。 所以,我們使用 API 接口時,實際是在描述一個圖:當我們將它放入一個`Session`中,并且開始`Run`時,圖的求值過程就開始了。 理解這些之后,讓我們嘗試定義一個計算圖,并且在一個`Session`中計算它。[API 文檔](https://godoc.org/github.com/tensorflow/tensorflow/tensorflow/go)能為我們清楚地提供`tensorflow`(縮寫`tf`)和`op`包的方法列表。 如你所見,這兩個包包含了我們對圖進行定義和計算所需要的一切。 前一個包包含了構建類似`Graph`本身等基礎「空」結構的方法,后一個則是最重要的包,它包含了從 C++ 實現里自動生成的接口方法。 假設我們想要計算矩陣 A 和 x 的乘積: ![](https://box.kancloud.cn/2cd4d7e589b8cbd0a5ee94d82670f2a6_548x158.png) 我假設讀者已經知道 tensorflow 圖定義的概念,知道什么是占位符而且知道它們如何工作。下面的代碼是用戶第一次使用 Python 接口時可能會做的嘗試。我們將其命名為`attempt1.go` ~~~ package main import ( "fmt" tf "github.com/tensorflow/tensorflow/tensorflow/go" "github.com/tensorflow/tensorflow/tensorflow/go/op" ) func main() { // 讓我們描述我們的需求:創建圖 // 我們想要定義兩個運行時使用的 placeholder // 第一個 placeholder A 是 [2, 2] 整數張量 // 第二個 placeholder x 是 [2, 1] 整數張量 // 然后計算 Y = Ax // 創建圖的節點:一個空節點,作為圖的根節點 root := op.NewScope() // 定義兩個占位符 A := op.Placeholder(root, tf.Int64, op.PlaceholderShape(tf.MakeShape(2, 2))) x := op.Placeholder(root, tf.Int64, op.PlaceholderShape(tf.MakeShape(2, 1))) // 定義可以接受 A & x 作為輸入的操作節點 product := op.MatMul(root, A, x) // 每次我們將 `Scope` 傳入一個操作時,我們都將這個操作置于這個作用域內。 // 如你所見,我們有一個通過 NewScope 創建空域: // 這個空域是我們所創建的圖的根,我們用 “/”表示它。 // 現在我們讓 tensorflow 通過我們的定義來構建圖。 // 實體的圖是通過我們用域和操作組合起來定義的“抽象”圖生成的。 graph, err := root.Finalize() if err != nil { // 處理這個錯誤沒有什么用處 // 如果我們對圖的定義做錯了,我們只能手動修正這些定義。 // 它很想一個 SQL 查詢過程:如果查詢語句錯了,我們只能重寫它 panic(err.Error()) } // 至此:我們的圖定義語法上就沒有問題了。 // 我們現在可以將其放入一個 Session 中使用了。 var sess *tf.Session sess, err = tf.NewSession(graph, &tf.SessionOptions{}) if err != nil { panic(err.Error()) } // 為了使用占位符,我們必須創建含有數值的張量傳入網絡中 var matrix, column *tf.Tensor // A = [ [1, 2], [-1, -2] ] if matrix, err = tf.NewTensor([2][2]int64{ {1, 2}, {-1, -2} }); err != nil { panic(err.Error()) } // x = [ [10], [100] ] if column, err = tf.NewTensor([2][1]int64{ {10}, {100} }); err != nil { panic(err.Error()) } var results []*tf.Tensor if results, err = sess.Run(map[tf.Output]*tf.Tensor{ A: matrix, x: column, }, []tf.Output{product}, nil); err != nil { panic(err.Error()) } for _, result := range results { fmt.Println(result.Value().([][]int64)) } } ~~~ 代碼內的注釋非常豐富,請大家仔細閱讀每行注釋。 如果是 Python 版 Tensorflow 的使用者,現在已經可以期待代碼編譯后能完美運行了。我們看看是否能如愿呢: `go run attempt1.go` 會得到如下結果: `panic: failed to add operation "Placeholder": Duplicate node name in graph: 'Placeholder'` 稍等,這里發生了什么?錯誤提示很明顯,有兩個同名的占位符都叫作“PlaceHolder“。 ## [](https://log.zvz.im/2018/07/15/go-tensorflow/#%E7%AC%AC%E4%B8%80%E8%AF%BE%EF%BC%9A%E8%8A%82%E7%82%B9-ID "第一課:節點 ID")第一課:節點 ID 使用 Python 接口時,每當我們調用定義操作的方法時,無論它是否已經被調用過,都會生成不同的節點。下面的代碼就會很順利的返回結果 3。 ~~~ import tensorflow as tf a = tf.placeholder(tf.int32, shape=()) b = tf.placeholder(tf.int32, shape=()) add = tf.add(a,b) sess = tf.InteractiveSession() print(sess.run(add, feed_dict={a: 1,b: 2})) ~~~ 要驗證這段程序創建了兩個不同的節點,我們只需要將占位符的名字打印出來:`print(a.name, b.name)`輸出`Placeholder:0 Placeholder_1:0`。 這里`b`占位符的名字是`Placeholder_1:0`同時`a`占位符的名字是`Placeholder:0`。 在 Go 版本里,則不同,之前程序就因為`A`和`x`都叫作`Placeholder`而導致運行失敗。我們可以總結如下: **Go 語言版 API 接口每次在我們調用定義操作的方法時,不會自動為節點生成新的名稱**:操作名稱是固定的,而且我們沒法改變它。 **問答時間:** * 關于 Tensorflow 系統我們學到了什么?對于一個圖來說,它的每一個節點都必須有唯一的名稱。節點是以各自的名字來區分的。 * 節點名稱是否與定義它的操作名稱相同?是的,更確切地講,不完全是,只是名稱的結尾部分相同。 為了說明第二個答案,讓我們來修復節點的重名問題。 ## [](https://log.zvz.im/2018/07/15/go-tensorflow/#%E7%AC%AC%E4%BA%8C%E8%AF%BE%EF%BC%9A%E4%BD%9C%E7%94%A8%E5%9F%9F "第二課:作用域")第二課:作用域 正如我們剛才看到,Python 版的 API 接口會在每次定義操作時,自動生成一個新的名字。從底層實現來看,Python 接口調用了 C++ 的`Scope`類的`WithOpName`方法。以下是此方法的文檔和形式聲明,來自[scope.h](https://github.com/tensorflow/tensorflow/blob/a5b1fb8e56ceda0ee2794ee05f5a7642157875c5/tensorflow/cc/framework/scope.h)頭文件: ~~~ /// Return a new scope. All ops created within the returned scope will have /// names of the form <name>/<op_name>[_<suffix]. Scope WithOpName(const string& op_name) const; ~~~ 我們可以注意到這個用于命名節點的方法,其返回值是一個`Scope`對象,由此一個節點的名稱,實際上是一個`Scope`域對象。一個`Scope`是一個**完整路徑**,從根`/`(空圖)起到`op_name`結束。 當我們增加一個從`/`到`op_name`有相同路徑的節點時,會導致在同一個域中的節點重復,此時`WithOpName`方法會為名稱添加一個后綴`_<suffix>`(`<suffix>`是一個計數器)。 知道這些以后,我們期望找到`Scope 類型`的`WithOpName`方法,來解決重復節點的問題。可惜的是,這個方法暫時還沒有實現。 取而代之的,在[文檔中的 Scope 類型](https://godoc.org/github.com/tensorflow/tensorflow/tensorflow/go/op#Scope)部分我們看到唯一能夠返回一個新的`Scope`的方法是`SubScope(namespace string)`。 引用文檔如下: > 調用 SubScope 方法會返回一個新的 Scope,使得所有加入圖中的操作都被置于命名空間 ‘namespace’ 中。如果命名空間與作用域中已有的命名空間重名,則會加上后綴。 使用后綴進行沖突管理與在 C++ 中使用`WithOpName`方法**不同**:`WithOpName`在同一個作用域內的操作名稱后加上`suffix`后綴(這樣`Placeholder`就變成了`Placeholder_1`),而 Go 使用的`SubScope`的方法則是**對作用域名稱**增加后綴名`suffix`。 這點差異會產生完全不同的圖,不過盡管不同(節點放在不同的作用域中),從計算角度看它們是等價的。 讓我們修改一下占位符的定義過程,定義兩個不同的節點,然后打印出`Scope`的名稱。 讓我們創建文件`attempt2.go`將下面幾行代碼 ~~~ A := op.Placeholder(root, tf.Int64, op.PlaceholderShape(tf.MakeShape(2, 2))) x := op.Placeholder(root, tf.Int64, op.PlaceholderShape(tf.MakeShape(2, 1))) ~~~ 改成 ~~~ // define 2 subscopes of the root subscopes, called "input". In this // way we expect to have a input/ and a input_1/ scope under the root scope A := op.Placeholder(root.SubScope("input"), tf.Int64, op.PlaceholderShape(tf.MakeShape(2, 2))) x := op.Placeholder(root.SubScope("input"), tf.Int64, op.PlaceholderShape(tf.MakeShape(2, 1))) fmt.Println(A.Op.Name(), x.Op.Name()) ~~~ 正常編譯并運行:`go run attempt2.go`。結果如下: ~~~ input/Placeholder input_1/Placeholder ~~~ **問答時間:** 關于 Tensorflow 系統我們學到了什么?一個節點可由它被定義的作用域所區分。作用域就是從圖的根節點直到操作節點的路徑。有兩種方式可以定義執行相同操作的節點:在不同的作用域中定義操作(Go 的方式)或者改變操作名稱(Python 自動實現或者我們可以使用 C++ 做到) 我們剛剛解決了節點名稱重復的問題,另一個問題又出現了。 ~~~ panic: failed to add operation "MatMul": Value for attr 'T' of int64 is not in the list of allowed values: half, float, double, int32, complex64, complex128 ~~~ 為什么`MatMul`節點定義會報錯?我們只是想讓兩個`tf.int64`矩陣相乘!看起來`int64`是`MatMul`唯一不能接受的參數類型。 > 屬性 ‘T’ 的取值 int64,不在允許的列表中:half,float,double,int32,complex32, complex64, complex128 這是什么列表?為什么我們可以將兩個`int32`類型的矩陣相乘卻不支持`int64`類型? 讓我們繼續研究這個問題,搞清楚到底發生了什么。 ## [](https://log.zvz.im/2018/07/15/go-tensorflow/#%E7%AC%AC%E4%B8%89%E8%AF%BE%EF%BC%9ATensorflow-%E7%B1%BB%E5%9E%8B%E4%BD%93%E7%B3%BB "第三課:Tensorflow 類型體系")第三課:Tensorflow 類型體系 讓我們深入到 C++ 源碼中,看一下`MatMul`操作的函數聲明: ~~~ REGISTER_OP("MatMul") .Input("a: T") .Input("b: T") .Output("product: T") .Attr("transpose_a: bool = false") .Attr("transpose_b: bool = false") .Attr("T: {half, float, double, int32, complex64, complex128}") .SetShapeFn(shape_inference::MatMulShape) .Doc(R"doc( Multiply the matrix "a" by the matrix "b". The inputs must be two-dimensional matrices and the inner dimension of "a" (after being transposed if transpose_a is true) must match the outer dimension of "b" (after being transposed if transposed_b is true). *Note*: The default kernel implementation for MatMul on GPUs uses cublas. transpose_a: If true, "a" is transposed before multiplication. transpose_b: If true, "b" is transposed before multiplication. )doc"); ~~~ 這行代碼定義了`MatMul`操作的接口:特別注意,我們使用`REGISTER_OP`宏聲明了操作的: * 名稱:`MatMul` * 參數:`a`,`b` * 屬性(可選參數):`transpose_a`,`transpose_b` * 模板`T`支持的類型:`half, float, double, int32, complex64, complex128` * 輸出形式:自動推理的 * 文檔 這個宏調用不包含任何 C++ 代碼,不過它告訴我們**當定義個一個操作時,即使它使用了模板,我們也必須指定對于指定類型(或屬性)`T`所支持的類型列表**。例如,屬性`.Attr("T: {half, float, double, int32, complex64, complex128}")`就限制了類型`T`必須是列表中的某一項。 我們可以從[教程](https://www.tensorflow.org/extend/adding_an_op)中看到,甚至在使用模板`T`的時候,我們也必須為每個支持的重載顯示地注冊到內核中。內核是以 CUAD 方式對 C/C++ 函數進行并行調用執行的。 `MatMul`的作者之所以決定只支持之前列出的參數類型,而不支持`int64`類型,可能有以下兩個原因: 1. 疏忽:這是有可能的,畢竟 Tensorflow 的代碼也是人寫的! 2. 為了支持那些不完全支持`int64`類型操作的設備,這樣內核的這些特定實現就不會到處都是,而導致在本可以支持的硬件上無法運行。 回到我們的報錯上來:修復的方法很明顯。我們必須要給`MatMul`方法傳遞它所支持的數據類型。 讓我們創建`attempt3.go`文件,將每一行用到`int64`的地方改成`int32`。 有件事要注意一下:**Go 語言的接口包定義了一套自有的類型,與 Go 原生類型基本上是 1:1 對應的關系。當我們向圖內填入參數時需要對照這個對應關系(比如,對于定義為`tf.Int32`的占位符要傳入`int32`類型的值)。從圖中讀取數據時也要準從相同的法則。**由張量計算返回的`*tf.Tensor`類型,自帶`Value()`方法,它可以返回一個`interface{}`類型的值,必須由我們去轉化為正確的類型(我們構建圖的時候可知此類型)。 正常執行`go run attempt3.go`。結果如下: ~~~ input/Placeholder input_1/Placeholder [[210] [-210]] ~~~ 棒極了! 這兒有一份完整的`attempt3`的代碼,你可以編譯并運行它。 ~~~ package main import ( "fmt" tf "github.com/tensorflow/tensorflow/tensorflow/go" "github.com/tensorflow/tensorflow/tensorflow/go/op" ) func main() { // Let's describe what we want: create the graph // We want to define two placeholder to fill at runtime // the first placeholder A will be a [2, 2] tensor of integers // the second placeholder x will be a [2, 1] tensor of intergers // Then we want to compute Y = Ax // Create the first node of the graph: an empty node, the root of our graph root := op.NewScope() // Define the 2 placeholders // define 2 subscopes of the root subscopes, called "input". In this // way we expect the have a input/ and a input_1/ scope under the root scope A := op.Placeholder(root.SubScope("input"), tf.Int32, op.PlaceholderShape(tf.MakeShape(2, 2))) x := op.Placeholder(root.SubScope("input"), tf.Int32, op.PlaceholderShape(tf.MakeShape(2, 1))) fmt.Println(A.Op.Name(), x.Op.Name()) // Define the operation node that accepts A & x as inputs product := op.MatMul(root, A, x) // Every time we passed a `Scope` to an operation, we placed that operation **under** // that scope. // As you can see, we have an empty scope (created with NewScope): the empty scope // is the root of our graph and thus we denote it with "/". // Now we ask tensorflow to build the graph from our definition. // The concrete graph is created from the "abstract" graph we defined using the combination // of scope and op. graph, err := root.Finalize() if err != nil { // It's useless trying to handle this error in any way: // if we defined the graph wrongly we have to manually fix the definition. // It's like a SQL query: if the query is not syntactically valid we have to rewrite it panic(err.Error()) } // If here: our graph is syntatically valid. // We can now place it within a Session and execute it. var sess *tf.Session sess, err = tf.NewSession(graph, &tf.SessionOptions{}) if err != nil { panic(err.Error()) } // In order to use placeholders, we have to create the Tensors containing the values to feed into // the network var matrix, column *tf.Tensor // A = [ [1, 2], [-1, -2] ] if matrix, err = tf.NewTensor([2][2]int32{{1, 2}, {-1, -2}}); err != nil { panic(err.Error()) } // x = [ [10], [100] ] if column, err = tf.NewTensor([2][1]int32{{10}, {100}}); err != nil { panic(err.Error()) } var results []*tf.Tensor if results, err = sess.Run(map[tf.Output]*tf.Tensor{ A: matrix, x: column, }, []tf.Output{product}, nil); err != nil { panic(err.Error()) } for _, result := range results { fmt.Println(result.Value().([][]int32)) } } ~~~ **問答時間:** 關于 Tensorflow 系統我們學到了什么?每個操作都有它自己的關聯核心實現。Tensorflow 可以看作是一種強類型的描述性語言。它不僅要遵守 C++ 的類型規則,它還得在注冊操作時指定執行時使用數據的類型。 ## [](https://log.zvz.im/2018/07/15/go-tensorflow/#%E6%80%BB%E7%BB%93 "總結")總結 使用 Go 語言定義圖并進行運算,帶給我們一次深入理解 Tensorflow 底層架構的機會。采取逐步試錯的方式,我們解決了這個簡單的問題,而且一步步學習到了關于圖,圖的節點以及類型體系的新知識。 翻譯自:[Understanding Tensorflow using Go](https://pgaleone.eu/tensorflow/go/2017/05/29/understanding-tensorflow-using-go/)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看