教程 | 如何利用C++搭建個人專屬的TensorFlow

在開始之前,首先看一下最終成型的代碼:

  1. 分支與特征后端(https://github.com/OneRaynyDay/autodiff/tree/eigen)
  2. 僅支持標量的分支(https://github.com/OneRaynyDay/autodiff/tree/master)

這個項目是我與 Minh Le 一起完成的。

為什么?

如果你修習的是計算機科學(CS)的人的話,你可能聽說過這個短語「不要自己動手____」幾千次了。它包含了加密、標準庫、解析器等等。我想到現在為止,它也應該包含了機器學習庫(ML library)。

不管現實是怎么樣的,這個震撼的課程都值得我們去學習。人們現在把 TensorFlow 和類似的庫當作理所當然了。他們把它看作黑盒子并讓它運行起來,但是并沒有多少人知道在這背后的運行原理。這只是一個非凸(Non-convex)的優化問題!請停止對代碼無意義的胡搞——僅僅只是為了讓代碼看上去像是正確的。
創一個小群,供大家學習交流聊天
如果有對學C++方面有什么疑惑問題的,或者有什么想說的想聊的大家可以一起交流學習一起進步呀。
也希望大家對學C++能夠持之以恒
C++愛好群,
如果你想要學好C++最好加入一個組織,這樣大家學習的話就比較方便,還能夠共同交流和分享資料,給你推薦一個學習的組織:快樂學習C++組織 可以點擊組織二字,可以直達請添加鏈接描述

教程 | 如何利用C++搭建個人專屬的TensorFlow

TensorFlow

在 TensorFlow 的代碼里,有一個重要的組件,允許你將計算串在一起,形成一個稱為「計算圖」的東西。這個計算圖是一個有向圖 G=(V,E),其中在某些節點處 u1,u2,…,un,v∈V,和 e1,e2,…,en∈E,ei=(ui,v)。我們知道,存在某種計算圖將 u1,…,un 映射到 vv。

舉個例子,如果我們有 x + y = z,那么 (x,z),(y,z)∈E。

這對于評估算術表達式非常有用,我們能夠在計算圖的匯點下找到結果。匯點是類似 v∈V,?e=(v,u) 這樣的頂點。從另一方面來說,這些頂點從自身到其他頂點并沒有定向邊界。同樣的,輸入源是 v∈V,?e=(u,v)。

對于我們來說,我們總是把值放在輸入源上,而值也將傳播到匯點上。

反向模式求微分

如果你覺得我的解釋不正確,可以參考下這些幻燈片的說明。

微分是 Tensorflow 中許多模型的核心需求,因為我們需要它來運行梯度下降。每一個從高中畢業的人都應該知道微分的意思。如果是基于基礎函數組成的復雜函數,則只需要求出函數的導數,然后應用鏈式法則。

超級簡潔的概述

如果我們有一個像這樣的函數:

對 x 求導:

對 y 求導:

其它的例子:

其導數是:

所以其梯度是:

鏈式法則,例如應用于 f(g(h(x))):

在 5 分鐘內倒轉模式

所以現在請記住我們運行計算圖時用的是有向無環結構(DAG/Directed Acyclic Graph),還有上一個例子用到的鏈式法則。正如下方所示的形式:

x -> h -> g -> f

作為一個圖,我們能夠在 f 獲得答案,然而,也可以反過來:

dx <- dh <- dg <- df

這樣它看起來就像鏈式法則了!我們需要沿著路徑把導數相乘以得到最終的結果。這是一個計算圖的例子:

這就將其簡化為一個圖的遍歷問題。有誰察覺到了這就是拓撲排序和深度優先搜索/寬度優先搜索?

沒錯,為了在兩種路徑都支持拓撲排序,我們需要包含一套父組一套子組,而匯點是另一個方向的來源。反之亦然。

執行

在開學前,Minh Le 和我開始設計這個項目。我們決定使用特征庫后端(Eigen library backend)進行線性代數運算,這個庫有一個叫做 MatrixXd 的矩陣類,用在我們的項目中:

class var {// Forward declarationstruct impl;public:
// For initialization of new vars by ptr var(std::shared_ptr<impl>);

var(double);
var(const MatrixXd&);
var(op_type, const std::vector<var>&);    
...// Access/Modify the current node value    MatrixXd getValue() const;
void setValue(const MatrixXd&);
op_type getOp() const;
void setOp(op_type);// Access internals (no modify)    std::vector<var>& getChildren() const;
std::vector<var> getParents() const;
...private: 
// PImpl idiom requires forward declaration of the class:    std::shared_ptr<impl> pimpl;};struct var::impl{public:
impl(const MatrixXd&);
impl(op_type, const std::vector<var>&);
MatrixXd val;
op_type op; 
std::vector<var> children;
std::vector<std::weak_ptr<impl>> parents;};

在這里,我們使用了一個叫「pImpl」的語法,意思是「執行的指針」。它有很多用途,比如接口的解耦實現,以及當在堆棧上有一個本地接口時實例化內存堆上的東西。「pImpl」的一些副作用是微弱的減慢運行時間,但是編譯時間縮短了很多。這允許我們通過多個函數調用/返回來保持數據結構的持久性。像這樣的樹形數據結構應該是持久的。

我們有一些枚舉來告訴我們目前正在進行哪些操作:

enum class op_type {
plus,
minus,
multiply,
divide,
exponent,
log,
polynomial,
dot,
...
none // no operators. leaf.};

執行此樹的評估的實際類稱為 expression:

class expression {public:
expression(var);
...
// Recursively evaluates the tree. double propagate();
...
// Computes the derivative for the entire graph. // Performs a top-down evaluation of the tree. void backpropagate(std::unordered_map<var, double>& leaves);
... private:
var root;};

在反向傳播里,我們的代碼能做類似以下所示的事情:

backpropagate(node, dprev):
derivative = differentiate(node)*dprev
for child in node.children:
backpropagate(child, derivative)

這幾乎就是在做一個深度優先搜索(DFS),你發現了嗎?

為什么是 C++?

在實際過程中,C++可能并不適合做這類事情。我們可以在像「Oaml」這樣的函數式語言中花費更少的時間開發。現在我明白為什么「Scala」被用于機器學習中,主要就是因為「Spark」。然而,使用 C++有很多好處。

Eigen(庫名)

舉例來說,我們可以直接使用一個叫「Eigen」的 TensorFlow 的線性代數庫。這是一個不假思索就被人用爛了的線性代數庫。有一種類似于我們的表達式樹的味道,我們構建表達式,它只會在我們真正需要的時候進行評估。然而,使用「Eigen」在編譯的時間內就能決定什么時候使用模版,這意味著運行的時間減少了。我對寫出「Eigen」的人抱有很大的敬意,因為查看模版的錯誤幾乎讓我眼瞎!

他們的代碼看起來類似這樣的:

Matrix A(...), B(...);
auto lazy_multiply = A.dot(B);
typeid(lazy_multiply).name(); // the class name is something like Dot_Matrix_Matrix.
Matrix(lazy_multiply); // functional-style casting forces evaluation of this matrix.

這個特征庫非常的強大,這就是它作為 TensortFlow 主要后端之一的原因,即除了這個慵懶的評估技術之外還有其它的優化。

運算符重載

在 Java 中開發這個庫很不錯——因為沒有 shared_ptrs、unique_ptrs、weak_ptrs;我們得到了一個真實的,有用的圖形計算器(GC=Graphing Calculator)。這大大節省了開發時間,更不必說更快的執行速度。然而,Java 不允許操作符重載,因此它們不能這樣:

// These 3 lines code up an entire neural network!
var sigm1 = 1 / (1 + exp(-1 dot(X, w1)));
var sigm2 = 1 / (1 + exp(-1
dot(sigm1, w2)));
var loss = sum(-1 (y log(sigm2) + (1-y) * log(1-sigm2)));

順便說一下,上面是實際使用的代碼。是不是非常的漂亮?我想說的是這甚至比 TensorFlow 里的 Python 封裝還更優美!我只是想表明,它們也是矩陣。

在 Java 中,有一連串的 add(), divide() 等等是非常難看的。更重要的是,這將讓用戶更多的關注在「PEMDAS」上,而 C++的操作符則有非常好的表現。

特征,而不是一連串的故障

在這個庫中,可以確定的是,TensorFlow 沒有定義清晰的 API,或者有但我不知道。例如,如果我們只想訓練一個特定子集的權重,我們可以只對我們感興趣的特定來源做反向傳播。這對于卷積神經網絡的遷移學習非常有用,因為很多時候,像 VGG19 這樣的大型網絡可以被截斷,然后附加一些額外的層,這些層的權重使用新領域的樣本來訓練。

基準

在 Python 的 TensorFlow 庫中,對虹膜數據集進行 10000 個「Epochs」的訓練以進行分類,并使用相同的超參數,我們有:

1.TensorFlow 的神經網絡: 23812.5 ms
2.「Scikit」的神經網絡:22412.2 ms
3.「Autodiff」的神經網絡,迭代,優化:25397.2 ms
4.「Autodiff」的神經網絡,迭代,無優化:29052.4 ms
5.「Autodiff」的神經網絡,帶有遞歸,無優化:28121.5 ms

令人驚訝的是,Scikit 是所有這些中最快的。這可能是因為我們沒有做龐大的矩陣乘法。也可能是 TensorFlow 需要額外的編譯步驟,如變量初始化等等。或者,也許我們不得不在 python 中運行循環,而不是在 C 中(Python 循環真的非常糟糕!)我自己也不是很確定。我完全明白這絕不是一種全面的基準測試,因為它只在特定的情況下應用了單個數據點。然而,這個庫的表現并不能代表當前最佳,所以希望各位讀者和我們共同完善

轉載于:https://blog.51cto.com/14209412/2354021

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/536906.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/536906.shtml
英文地址,請注明出處:http://en.pswp.cn/news/536906.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

docker kali安裝mysql_kali安裝docker(有效詳細的教程) ——vulhub漏洞復現 001

前記&#xff1a;博主有著多次安裝docker的豐富經驗&#xff0c;曾經為了在kali成功安裝docker花費不少時間。在kali2016.3一直到最新的kali2019.4都通吃&#xff01;所以跟著下面的步驟走&#xff0c;絕對不會出錯。(該機子此前沒裝過docker&#xff0c;并且配置好了kali更新源…

PDF文件如何轉成markdown格式

百度上根據pdf轉makrdown為關鍵字進行搜索&#xff0c;結果大多數是反過來的轉換&#xff0c;即markdown文本轉PDF格式。 但是PDF轉markdown的解決方案很少。 正好我工作上有這個需求&#xff0c;所以自己實現了一個解決方案。 下圖是一個用PDF XChange Editor打開的PDF文件&am…

kangle支不支持PHP_【轉載】PHP調用kangle的API

摘要&#xff1a;根據管理的API公布寫了一個類封裝了一個操作集合&#xff0c;這是一個kangleAPI的一個封...根據管理的API公布寫了一個類封裝了一個操作集合&#xff0c;這是一個kangleAPI的一個封裝吧&#xff0c;是在其他地方看到的&#xff0c;接口包含獲取easypanel的信息…

ES6 學習筆記(一)let,const和解構賦值

let和const let和const是es6新增的兩個變量聲明關鍵字&#xff0c;與var的不同點在于&#xff1a; &#xff08;1&#xff09;let和const都是塊級作用域&#xff0c;在{}內有效&#xff0c;這點在for循環中非常有用&#xff0c;只在循環體內有效。var為函數作用域。 &#xff0…

mysql數據庫容量和性能_新品速遞丨容量盤性能提升超 300%,數據庫支持 MySQL 8.0...

2關系型數據庫 MySQL Plus支持 MySQL 8.0 內核及 XtraBackup 物理在線遷移方式關系型數據庫服務 MySQL Plus 發布新版本 1.0.6 &#xff0c; 新增多項功能&#xff0c;提升了集群自動化運維能力。主要升級有&#xff1a;- 支持 MySQL 8.0 內核&#xff1a;根據官方測試&#xf…

10. Python面向對象

Python從設計之初就已經是一門面向對象的語言&#xff0c;正因為如此&#xff0c;在Python中創建一個類和對象是很容易的。如果接觸過java語言同學應該都知道&#xff0c;Java面向對象三大特征是&#xff1a;封裝、繼承、多態。Python面向對象也有一些特征&#xff0c;接下來我…

mysql聚簇索引 和主鍵的區別_[MySQL] innoDB引擎的主鍵與聚簇索引

MysqL的innodb引擎本身存儲的形式就必須是聚簇索引的形式,在磁盤上樹狀存儲的,但是不一定是根據主鍵聚簇的,有三種情形:1. 有主鍵的情況下,主鍵就是聚簇索引2. 沒有主鍵的情況下,第一個非空null的唯一索引就是聚簇索引3. 如果上面都沒有,那么就是有一個隱藏的row-id作為聚簇索引…

前端頁面:一直報Cannot set property 'height' of undefined

1、出現錯誤的例子&#xff0c;只拷貝了項目中關鍵出現問題的部分 例子中明明寫了styleheight:16px這個屬性&#xff0c;但是為什么還說height未定義呢 通過打印發現&#xff1a;cks.each(function () { autoTextAreaHeight($(this)); });中的$(this)取出來…

mysql表在線轉成分區表_11g普通表在線轉換分區表

本帖最后由 燈和樹 于 2016-5-4 14:58 編輯由于業務系統數據量增大&#xff0c;對其用戶表在線完成分區表轉換過程&#xff0c;記錄如下&#xff0c;11g數據庫支持。創建過渡分區表根據USER_ID創建分區表CREATE TABLE SDP_SMECD.TEST_T_USER_ID(USER_ID NUMBER(13) …

tiger4444/rabbit4444后綴勒索病毒怎么刪除 能否百分百恢復

上海某客戶中了tiger4444的勒索病毒&#xff0c;找到我們后&#xff0c;一天內全部恢復完成。說了很多關于勒索病毒的事情&#xff0c;也提醒過大家&#xff0c;可總是有人疏忽&#xff0c;致使中招后&#xff0c;丟錢丟面子&#xff0c;還丟工作。 那么要怎樣預防呢與處理呢&a…

mysql遠程一會不用卡住_連接遠程MySQL數據庫項目啟動時,不報錯但是卡住不繼續啟動的,...

連接遠程MySQL數據庫項目啟動時&#xff0c;不報錯但是卡住不繼續啟動的&#xff0c;2018-03-12 17:08:52.532DEBUG[localhost-startStop-1]o.s.beans.factory.support.DefaultListableBeanFactory.doGetBean():251 -Returning cached instance of singleton bean ‘org.spring…

GPT-5、開源、更強的ChatGPT!

年終歲尾&#xff0c;正值圣誕節熱鬧氣氛的OpenAI寫下了2024年的發展清單。 OpenAI聯合創始人兼首席執行官Sam Altman在社交平臺公布&#xff0c;AGI&#xff08;稍晚一些&#xff09;、GPT-5、更好的語音模型、更高的費率限制&#xff1b; 更好的GPTs&#xff1b;更好的推理…

CentOS_7 安裝MySql5.7

2019獨角獸企業重金招聘Python工程師標準>>> 下載mysql的源 wget http://dev.mysql.com/get/mysql57-community-release-el7-7.noarch.rpm 安裝yum庫 yum localinstall -y mysql57-community-release-el7-7.noarch.rpm 安裝MySQL yum install -y mysql-community-…

python查詢mysql decimal報錯_python讀取MySQL數據表時,使用ast模塊轉換decimal格式數據的坑...

概述MySQL中常用的數據格式有tinyint()、int()、float()、double()、decimal() 、varchar、enum()、datetime;小數格式中decimal比較常用&#xff0c;因為更加精確&#xff0c;這里就以decimal為例。從MySQL中讀取了一行數據&#xff0c;內容為&#xff1a;(17479, datetime.da…

性能測試總結(一)---基礎理論篇(轉載)

隨著軟件行業的快速發展&#xff0c;現代的軟件系統越來越復雜&#xff0c;功能越來越多&#xff0c;測試人員除了需要保證基本的功能測試質量&#xff0c;性能也隨越來越受到人們的關注。但是一提到性能測試&#xff0c;很多人就直接連想到Loadrunner。認為LR就等于性能測試&a…

java listen_JavaWeb之Filter、Listener

昨天和大家介紹了一下JSON的用法&#xff0c;其實JSON中主要是用來和數據庫交互數據的。今天給大家講解的是Filter和Listener的用法。一、Listenner監聽器1.1、定義Javaweb中的監聽器是用于監聽web常見對象HttpServletRequest,HttpSession,ServletContext。1.2、監聽器的作用監…

BFC的概念及作用

在了解什么是BFC之前&#xff0c;首先得明白什么是Box , Formatting Context &#xff08;一個決定如何渲染文檔的容器&#xff09;的概念 Box: CSS布局的基本單位 Box是 CSS 布局的對象和基本單位&#xff0c; 直觀點來說&#xff0c; 就是一個頁面是由很多個 Box組成的&#…

bitcount java_java-Long.bitCount()如何找到設置的位數?

讓我們以255為例.我們將這些位組合在一起.首先,我們從255開始,為0b1111.1111(二進制為8 1)第一行代碼是&#xff1a;i i - ((i > > > 1) & 0x5555555555555555L);這條線正在梳理每對1.由于我們有8個1,所以我們希望組合成對,并得到2,2,2,2之類的東西.由于它是二進…

Luogu P2463 [SDOI2008]Sandy的卡片

題目鏈接 \(Click\) \(Here\) 真的好麻煩啊。。事實證明&#xff0c;理解是理解&#xff0c;一定要認認真真把板子打牢&#xff0c;不然調鍋的時候真的會很痛苦。。&#xff08;最好是八分鐘能無腦把\(SA\)碼對的程度\(QAQ\)&#xff09; 這個題最開始我想的是\(RMQ\)遍歷每一個…

java log輸出到文件路徑_Java - 配置log4j的日志文件路徑 (附-獲取當前類路徑的多種方法)...

1 日志路徑帶來的痛點Java 項目中少不了要和log4j等日志框架打交道, 開發環境和生產環境下日志文件的輸出路徑總是不一致, 設置為絕對路徑的方式缺少了靈活性, 每次變更項目路徑都要修改文件, 目前想到的最佳實現方式是: 根據項目位置自動加載并配置文件路徑.本文借鑒 Tomcat 的…