日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

听说用 C# 写 TensorFlow 更高效?

發布時間:2023/12/4 C# 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 听说用 C# 写 TensorFlow 更高效? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

經過半年嘔心瀝血的努力,SciSharp STACK終于把Tensorflow .NET綁定升級到可以使用 tensorflow 2.3, 新版本最大的優勢是實現了Eager模式, 這個特性是讓.NET C#/ F#成為機器學習模型開發工具的重要前置條件。

NugGet包下載:

https://www.nuget.org/packages/TensorFlow.NET/0.20.0?。

歡迎大家入坑體驗新版本,提供反饋。?

同時作為一個最受歡迎的華人工程師主導開發的.NET機器學習項目,當然要最照顧國內的開發者,所以提供了最詳盡的中文開發者文檔:

https://github.com/SciSharp/TensorFlow.NET-Tutorials 。

和最實用的完整代碼示例:

https://github.com/SciSharp/SciSharp-Stack-Examples?。

微軟ML.NET項目組表示會同步跟進

https://github.com/dotnet/machinelearning/issues/5401。

如果你喜歡我們的項目,請記得收藏和點贊一下哦:

https://github.com/SciSharp/TensorFlow.NET 。

如果有同學對tensorflow運行在龍芯上的事情感興趣的話,可以一起推進這個事情。

我們目前對龍芯.net還不太了解,希望通過tensorflow .net的移植來加深對龍芯的了解。


為什么說 C# 開發更高效?

我們通過一個相同數據集的1000輪的線性回歸的例子的運行,我們對比 C# 和 Python 的運行速度和內存占用,發現 C# 的速度大約是 Python 的2倍,而內存的使用,C# 只占到 Python 的1/4 ,可以說 TensorFlow 的 C# 版本在速度和性能上同時超過了 Python 版本,因此,在工業現場或者實際應用時,TensorFlow.NET 除了部署上的便利,更有性能上的杰出優勢。

下述2個圖是具體的對比運行示意圖:


我用 C# 寫 DNN 案例分享

-- 基于 TensorFlow.NET 2.3 開發

(文章較長,干貨較多,讀者也可以直接訪問GitHub)

1. 深度神經網絡(DNN)介紹

2006年, 深度學習鼻祖Hinton在《SCIENCE 》上發表了一篇論文” Reducing the Dimensionality of Data with Neural Networks “,這篇論文揭開了深度學習的序幕。這篇論文提出了兩個主要觀點:(1)、多層人工神經網絡模型有很強的特征學習能力,深度學習模型學習得到的特征數據對原數據有更本質的代表性,這將大大便于分類和可視化問題;(2)、對于深度神經網絡很難訓練達到最優的問題,可以采用逐層訓練方法解決,將上層訓練好的結果作為下層訓練過程中的初始化參數。

深度神經網絡(Deep Neural Networks,簡稱DNN)是深度學習的基礎,想要學好深度學習,首先我們要理解DNN模型。

DNN模型結構:

深度神經網絡(Deep Neural Networks,DNN)可以理解為有很多隱藏層的神經網絡,又被稱為深度前饋網絡(DFN),多層感知機(Multi-Layer Perceptron,MLP),其具有多層的網絡結構,如下圖所示:

DNN模型按照層的位置不同,可以分為3種神經網絡層:輸入層、隱藏層和輸出層。一般來說,第一層為輸入層,最后一層為輸出層,中間為單個或多個隱藏層(某些模型的結構可能不同)。

層與層之間是全連接的,也就是說,第i層的任意一個神經元一定與第i+1層的任意一個神經元相連。雖然DNN看起來很復雜,但是從小的局部結構來看,它還是和普通的感知機一樣,即一個線性函數搭配一個激活函數。

DNN前向傳播:

利用若干個權重系數矩陣W,偏置向量 b 來和輸入向量 X 進行一系列線性運算和激活運算,從輸入層開始,一層層地向后計算,一直到運算到輸出層,得到輸出結果的值。

DNN反向傳播:

深度學習的過程即找到網絡各層中最優的線性系數矩陣 W 和偏置向量 b,讓所有的輸入樣本通過網絡計算后,預測的輸出盡可能等于或者接近實際的輸出樣本,這樣的過程一般稱為反向傳播。

我們需要定義一個損失函數,來衡量預測輸出值和實際輸出值之間的差異。接著對這個損失函數進行優化,通過不斷迭代對線性系數矩陣 W 和 偏置向量 b 進行更新,讓損失函數最小化,不斷逼近最小極值 或 滿足我們預期的需求。在DNN中, 損失函數優化極值求解的過程最常見的一般是通過梯度下降法來一步步迭代完成的。

DNN在TensorFlow2.0中的一般流程:

  • Step - 1:數據加載、歸一化和預處理;

  • Step - 2:搭建深度神經網絡模型;

  • Step - 3:定義損失函數和準確率函數;

  • Step - 4:模型訓練;

  • Step - 5:模型預測推理,性能評估。

  • DNN中的過擬合:

    隨著網絡的層數加深,模型的訓練過程中會出現 梯度爆炸、梯度消失、欠擬合和過擬合,我們來說說比較常見的過擬合。過擬合一般是指模型的特征維度過多、參數過多,模型過于復雜,導致參數數量大大高于訓練數據,訓練出的網絡層過于完美地適配訓練集,但對新的未知的數據集的預測能力很差。即過度地擬合了訓練數據,而沒有考慮到模型的泛化能力。

    一般的解決方法:

    • 獲取更多數據:從數據源獲得更多數據,或做數據增強;

    • 數據預處理:清洗數據、減少特征維度、類別平衡;

    • 正則化:限制權重過大、網絡層數過多,避免模型過于復雜;

    • 多種模型結合:集成學習的思想;

    • Dropout:隨機從網絡中去掉一部分隱藏神經元;

    • 中止方法:限制訓練時間、次數,及早停止。

    接下來,我們通過2種 TensorFlow2.x 推薦的方式 Eager 和 Keras 的代碼來演示 DNN 下的 MNIST 訓練集的訓練和推理,其中的線性函數和交叉熵損失函數等細節說明,請讀者參考“6. MNIST手寫字符分類 Logistic Regression”一章節,這里不再贅述。

    2. TensorFlow.NET 代碼實操 1 - DNN with Eager

    Eager 模式下的 DNN 模型訓練流程簡述如下:

    按照上述流程,我們進入代碼實操階段。

    ① 新建項目,配置環境和引用:

    新建項目。

    選擇 .NET Core 框架。

    輸入項目名,DNN_Eager。

    確認 .NET Core 版本為 3.0 及以上。

    選擇目標平臺為 x64 。

    使用 NuGet 安裝 TensorFlow.NET 和 SciSharp.TensorFlow.Redist,如果需要使用 GPU,則安裝 SciSharp.TensorFlow.Redist-Windows-GPU。

    添加項目引用。

    using NumSharp; using System.Linq; using Tensorflow; using Tensorflow.Keras.Optimizers; using static Tensorflow.Binding;

    ② 定義網絡權重變量和超參數:

    int num_classes = 10; // MNIST 的字符類別 0~9 總共 10 類 int num_features = 784; // 輸入圖像的特征尺寸,即像素28*28=784// 超參數 float learning_rate = 0.001f;// 學習率 int training_steps = 1000;// 訓練輪數 int batch_size = 256;// 批次大小 int display_step = 100;// 訓練數據 顯示周期// 神經網絡參數 int n_hidden_1 = 128; // 第1層隱藏層的神經元數量 int n_hidden_2 = 256; // 第2層隱藏層的神經元數量IDatasetV2 train_data;// MNIST 數據集 NDArray x_test, y_test, x_train, y_train;// 數據集拆分為訓練集和測試集 IVariableV1 h1, h2, wout, b1, b2, bout;// 待訓練的權重變量 float accuracy_test = 0f;// 測試集準確率

    ③ 載入MNIST數據,并進行預處理:

    數據下載 或 從本地加載 → 數據展平 → 歸一化 → 轉換 Dataset → 無限復制(方便后面take) / 亂序 / 生成批次 / 預加載 → 預處理后的數據提取需要的訓練份數。

    ((x_train, y_train), (x_test, y_test)) = tf.keras.datasets.mnist.load_data();// 下載 或 加載本地 MNIST (x_train, x_test) = (x_train.reshape((-1, num_features)), x_test.reshape((-1, num_features)));// 輸入數據展平 (x_train, x_test) = (x_train / 255f, x_test / 255f);// 歸一化train_data = tf.data.Dataset.from_tensor_slices(x_train, y_train);//轉換為 Dataset 格式 train_data = train_data.repeat().shuffle(5000).batch(batch_size).prefetch(1).take(training_steps);// 數據預處理

    ④ 初始化網絡權重變量和優化器:

    隨機初始化網絡權重變量,并打包成數組方便后續梯度求導作為參數。

    // 隨機初始化網絡權重變量,并打包成數組方便后續梯度求導作為參數。 var random_normal = tf.initializers.random_normal_initializer(); h1 = tf.Variable(random_normal.Apply(new InitializerArgs((num_features, n_hidden_1)))); h2 = tf.Variable(random_normal.Apply(new InitializerArgs((n_hidden_1, n_hidden_2)))); wout = tf.Variable(random_normal.Apply(new InitializerArgs((n_hidden_2, num_classes)))); b1 = tf.Variable(tf.zeros(n_hidden_1)); b2 = tf.Variable(tf.zeros(n_hidden_2)); bout = tf.Variable(tf.zeros(num_classes)); var trainable_variables = new IVariableV1[] { h1, h2, wout, b1, b2, bout };

    采用隨機梯度下降優化器。

    // 采用隨機梯度下降優化器 var optimizer = tf.optimizers.SGD(learning_rate);

    ⑤ 搭建DNN網絡模型,訓練并周期顯示訓練過程:

    搭建4層的全連接神經網絡,隱藏層采用 sigmoid 激活函數,輸出層采用 softmax 輸出預測的概率分布。

    // 搭建網絡模型 Tensor neural_net(Tensor x) {// 第1層隱藏層采用128個神經元。var layer_1 = tf.add(tf.matmul(x, h1.AsTensor()), b1.AsTensor());// 使用 sigmoid 激活函數,增加層輸出的非線性特征layer_1 = tf.nn.sigmoid(layer_1);// 第2層隱藏層采用256個神經元。var layer_2 = tf.add(tf.matmul(layer_1, h2.AsTensor()), b2.AsTensor());// 使用 sigmoid 激活函數,增加層輸出的非線性特征layer_2 = tf.nn.sigmoid(layer_2);// 輸出層的神經元數量和標簽類型數量相同var out_layer = tf.matmul(layer_2, wout.AsTensor()) + bout.AsTensor();// 使用 Softmax 函數將輸出類別轉換為各類別的概率分布return tf.nn.softmax(out_layer); }

    創建交叉熵損失函數。

    // 交叉熵損失函數 Tensor cross_entropy(Tensor y_pred, Tensor y_true) {// 將標簽轉換為One-Hot格式y_true = tf.one_hot(y_true, depth: num_classes);// 保持預測值在 1e-9 和 1.0 之間,防止值下溢出現log(0)報錯y_pred = tf.clip_by_value(y_pred, 1e-9f, 1.0f);// 計算交叉熵損失return tf.reduce_mean(-tf.reduce_sum(y_true * tf.math.log(y_pred))); }

    應用 TensorFlow 2.x 中的自動求導機制,創建梯度記錄器,自動跟蹤網絡中的梯度,自動求導進行梯度下降和網絡權重變量的更新優化。每隔一定周期,打印出當前輪次網絡的訓練性能數據 loss 和 accuracy 。關于自動求導機制,請參考“6. MNIST手寫字符分類 Logistic Regression”一章節。

    // 運行優化器 void run_optimization(OptimizerV2 optimizer, Tensor x, Tensor y, IVariableV1[] trainable_variables) {using var g = tf.GradientTape();var pred = neural_net(x);var loss = cross_entropy(pred, y);// 計算梯度var gradients = g.gradient(loss, trainable_variables);// 更新模型權重 w 和 bvar a = zip(gradients, trainable_variables.Select(x => x as ResourceVariable));optimizer.apply_gradients(zip(gradients, trainable_variables.Select(x => x as ResourceVariable))); }// 模型預測準確度 Tensor accuracy(Tensor y_pred, Tensor y_true) {// 使用 argmax 提取預測概率最大的標簽,和實際值比較,計算模型預測的準確度var correct_prediction = tf.equal(tf.argmax(y_pred, 1), tf.cast(y_true, tf.int64));return tf.reduce_mean(tf.cast(correct_prediction, tf.float32), axis: -1); }// 訓練模型 foreach (var (step, (batch_x, batch_y)) in enumerate(train_data, 1)) {// 運行優化器 進行模型權重 w 和 b 的更新run_optimization(optimizer, batch_x, batch_y, trainable_variables);if (step % display_step == 0){var pred = neural_net(batch_x);var loss = cross_entropy(pred, batch_y);var acc = accuracy(pred, batch_y);print($"step: {step}, loss: {(float)loss}, accuracy: {(float)acc}");} }

    ⑥ 測試集上性能評估:

    在測試集上對訓練后的模型進行預測準確率性能評估。

    // 在測試集上對訓練后的模型進行預測準確率性能評估 {var pred = neural_net(x_test);accuracy_test = (float)accuracy(pred, y_test);print($"Test Accuracy: {accuracy_test}"); }

    完整的控制臺運行代碼如下:

    using NumSharp; using System.Linq; using Tensorflow; using Tensorflow.Keras.Optimizers; using static Tensorflow.Binding;namespace DNN_Eager {class Program{static void Main(string[] args){DNN_Eager dnn = new DNN_Eager();dnn.Main();}}class DNN_Eager{int num_classes = 10; // MNIST 的字符類別 0~9 總共 10 類int num_features = 784; // 輸入圖像的特征尺寸,即像素28*28=784// 超參數float learning_rate = 0.001f;// 學習率int training_steps = 1000;// 訓練輪數int batch_size = 256;// 批次大小int display_step = 100;// 訓練數據 顯示周期// 神經網絡參數int n_hidden_1 = 128; // 第1層隱藏層的神經元數量int n_hidden_2 = 256; // 第2層隱藏層的神經元數量IDatasetV2 train_data;// MNIST 數據集NDArray x_test, y_test, x_train, y_train;// 數據集拆分為訓練集和測試集IVariableV1 h1, h2, wout, b1, b2, bout;// 待訓練的權重變量float accuracy_test = 0f;// 測試集準確率public void Main(){((x_train, y_train), (x_test, y_test)) = tf.keras.datasets.mnist.load_data();// 下載 或 加載本地 MNIST(x_train, x_test) = (x_train.reshape((-1, num_features)), x_test.reshape((-1, num_features)));// 輸入數據展平(x_train, x_test) = (x_train / 255f, x_test / 255f);// 歸一化train_data = tf.data.Dataset.from_tensor_slices(x_train, y_train);//轉換為 Dataset 格式train_data = train_data.repeat().shuffle(5000).batch(batch_size).prefetch(1).take(training_steps);// 數據預處理// 隨機初始化網絡權重變量,并打包成數組方便后續梯度求導作為參數。var random_normal = tf.initializers.random_normal_initializer();h1 = tf.Variable(random_normal.Apply(new InitializerArgs((num_features, n_hidden_1))));h2 = tf.Variable(random_normal.Apply(new InitializerArgs((n_hidden_1, n_hidden_2))));wout = tf.Variable(random_normal.Apply(new InitializerArgs((n_hidden_2, num_classes))));b1 = tf.Variable(tf.zeros(n_hidden_1));b2 = tf.Variable(tf.zeros(n_hidden_2));bout = tf.Variable(tf.zeros(num_classes));var trainable_variables = new IVariableV1[] { h1, h2, wout, b1, b2, bout };// 采用隨機梯度下降優化器var optimizer = tf.optimizers.SGD(learning_rate);// 訓練模型foreach (var (step, (batch_x, batch_y)) in enumerate(train_data, 1)){// 運行優化器 進行模型權重 w 和 b 的更新run_optimization(optimizer, batch_x, batch_y, trainable_variables);if (step % display_step == 0){var pred = neural_net(batch_x);var loss = cross_entropy(pred, batch_y);var acc = accuracy(pred, batch_y);print($"step: {step}, loss: {(float)loss}, accuracy: {(float)acc}");}}// 在測試集上對訓練后的模型進行預測準確率性能評估{var pred = neural_net(x_test);accuracy_test = (float)accuracy(pred, y_test);print($"Test Accuracy: {accuracy_test}");}}// 運行優化器void run_optimization(OptimizerV2 optimizer, Tensor x, Tensor y, IVariableV1[] trainable_variables){using var g = tf.GradientTape();var pred = neural_net(x);var loss = cross_entropy(pred, y);// 計算梯度var gradients = g.gradient(loss, trainable_variables);// 更新模型權重 w 和 bvar a = zip(gradients, trainable_variables.Select(x => x as ResourceVariable));optimizer.apply_gradients(zip(gradients, trainable_variables.Select(x => x as ResourceVariable)));}// 模型預測準確度Tensor accuracy(Tensor y_pred, Tensor y_true){// 使用 argmax 提取預測概率最大的標簽,和實際值比較,計算模型預測的準確度var correct_prediction = tf.equal(tf.argmax(y_pred, 1), tf.cast(y_true, tf.int64));return tf.reduce_mean(tf.cast(correct_prediction, tf.float32), axis: -1);}// 搭建網絡模型Tensor neural_net(Tensor x){// 第1層隱藏層采用128個神經元。var layer_1 = tf.add(tf.matmul(x, h1.AsTensor()), b1.AsTensor());// 使用 sigmoid 激活函數,增加層輸出的非線性特征layer_1 = tf.nn.sigmoid(layer_1);// 第2層隱藏層采用256個神經元。var layer_2 = tf.add(tf.matmul(layer_1, h2.AsTensor()), b2.AsTensor());// 使用 sigmoid 激活函數,增加層輸出的非線性特征layer_2 = tf.nn.sigmoid(layer_2);// 輸出層的神經元數量和標簽類型數量相同var out_layer = tf.matmul(layer_2, wout.AsTensor()) + bout.AsTensor();// 使用 Softmax 函數將輸出類別轉換為各類別的概率分布return tf.nn.softmax(out_layer);}// 交叉熵損失函數Tensor cross_entropy(Tensor y_pred, Tensor y_true){// 將標簽轉換為One-Hot格式y_true = tf.one_hot(y_true, depth: num_classes);// 保持預測值在 1e-9 和 1.0 之間,防止值下溢出現log(0)報錯y_pred = tf.clip_by_value(y_pred, 1e-9f, 1.0f);// 計算交叉熵損失return tf.reduce_mean(-tf.reduce_sum(y_true * tf.math.log(y_pred)));}} }

    運行結果如下:

    step: 100, loss: 562.84094, accuracy: 0.2734375 step: 200, loss: 409.87466, accuracy: 0.51171875 step: 300, loss: 234.70618, accuracy: 0.70703125 step: 400, loss: 171.07526, accuracy: 0.8046875 step: 500, loss: 147.40372, accuracy: 0.86328125 step: 600, loss: 123.477295, accuracy: 0.8671875 step: 700, loss: 105.51019, accuracy: 0.8984375 step: 800, loss: 106.7933, accuracy: 0.87109375 step: 900, loss: 75.033554, accuracy: 0.921875 Test Accuracy: 0.8954

    我們可以看到,loss 在不斷地下降,accuracy 不斷提高,最終的測試集的準確率為 0.8954,略低于 訓練集上的準確率 0.9219,基本屬于一個比較合理的訓練結果。

    3. TensorFlow.NET Keras 模型構建的三種方式

    TensorFlow.NET 2.x 提供了3種定義 Keras 模型的方式:

  • Sequential API (序列模型):按層順序創建模型

  • Functional API (函數式模型):函數式API創建任意結構模型

  • Model Subclassing (自定義模型):Model子類化創建自定義模型

  • 推薦的使用優先級:

    優先使用 Sequential API 進行模型的快速搭建,如果無法滿足需求(共享層或多輸入等),再考慮采用 Functional API 創建自定義結構的模型,如果仍然無法滿足需求(需要自定義控制 Train 過程或研發創新想法),最后也可以考慮 Model Subclassing。

    針對各種場景,TensorFlow.NET 都提供了對應的快速解決方案,接下來我們來詳細說明下這3種模型搭建的方式。

    3.1 Sequential API (序列模型)

    這是 Keras 最簡單的構建模型方式(也許是所有框架中最簡單構建方式),它順序地把所有模型層依次定義,然后使用內置的訓練循環 model.fit 對模型進行訓練, 搭建模型和訓練的過程就像“搭建樂高積木”一樣簡單。

    但序列模型是 layer-by-layer 的,某些場景的使用略有限制:

    • 無法共享網絡層

    • 不能創建多分支結構

    • 不能有多個輸入

    這種方式特別適用于經典網絡模型,如:LeNet,AlexNet,VGGNet ,模型結構如圖所示:

    Sequential 方式一般的代碼流程如下:

    //TODO: TensorFlow.NET的代碼示例待完成后添加

    3.2 Functional API (函數式模型)

    簡單的 Sequential 堆疊方式有時候不能表示任意結構的神經網絡。為此, Keras 提供了 Functional API, 幫助我們建立更為復雜和靈活的模型,它可以處理非線性拓撲、具有共享層的模型和具有多個輸入或輸出的模型。其使用方法是將層作為可調用的對象并返回張量,并將輸入向量和輸出向量提供給 Model 的 inputs 和 outputs 參數。

    Functional API 有如下更強的功能:

    • 定義更復雜的模型

    • 支持多輸入多輸出

    • 可以定義模型分支,比如inception block , resnet block

    • 方便layer共享

    實際上,任意的 Sequential 模型 都可以使用 Functional 方式實現,Functional 方式特別適用于一些復雜的網絡模型,如:ResNet,GoogleNet/Inception,Xception,SqueezeNet 等,模型結構如圖所示:

    Functional 方式一般的代碼流程如下:

    //TODO: TensorFlow.NET的代碼示例待完成后添加

    3.3 Model Subclassing (自定義模型)

    Functional API 通過繼承 Model 來編寫自己的模型類,如果仍然無法滿足需求,則可以通過 Model 子類化創建自定義模型,主要為自定義層(可以繼承 Layer 類)、自定義損失函數(可以繼承 Loss 類)和自定義評價函數(可以繼承 Metric 類)。

    從開發人員的角度來看,這種工作方式是擴展框架定義的模型類,實例化層,然后編寫模型的正向傳遞。TensorFlow.NET 2.x通過 Keras Subclassing API 支持這種開箱即用的方式,在 Keras 中 Model 類作為基本的類,可以在此基礎上,進行任意的自定義操作,對模型的所有部分(包括訓練過程)進行控制。

    我們先來了解下 Keras 模型類的自定義示意圖:

    然后,我們通過示例代碼來了解 Model Subclassing 方式的具體流程。

    自定義模型需要繼承 Tensorflow.Keras.Engine.Model 類,然后在構造函數中初始化所需要的層(可以使用 keras 的層或者繼承 Layer 進行自定義層),并重載 call() 方法進行模型的調用,建立輸入和輸出之間的函數關系。

    代碼結構如下:

    public class MyModel : Model {Layer myLayer1;Layer myLayer2;Layer output;public MyModel(ModelArgs args) :base(args){// First layer.myLayer1 = Layer.xxx;// Second layer.myLayer2 = Layer.xxx;output = Layer.xxx;}// Set forward pass.protected override Tensor call(Tensor inputs){inputs = myLayer1.Apply(inputs);inputs = myLayer2.Apply(inputs);inputs = output.Apply(inputs);return inputs;} }

    以上的代碼說明了自定義模型的方法,類似地也可以自定義層、損失函數和評估函數。

    ps:實際上,通過Mode Subclassing 方式自定義的模型,也可以使用 Sequential 或者 Functional API,其中自定義的 Layer 需要添加 get_config 方法以序列化組合模型。

    4. TensorFlow.NET 代碼實操 2 - DNN with Keras

    Keras 方式的 DNN 實現流程和上述第2節中的 Eager 方式類似,差異部分主要是使用了 Keras 的全連接層(Dense)替代了 Eager 方式中的 “線性變換+激活函數”。

    TensorFlow.NET 2.x 主要推薦使用 Keras 進行模型搭建和訓練,Keras 是一個高級神經網絡 API,可以實現簡單、快速和靈活地搭建網絡模型。自從2017年 TensorFlow 1.2 開始,Keras 就從一個獨立運行后端,變為 TensorFlow 的核心內置 API,一直到 TensorFlow 2.0 發布后,Keras 由 TensorFlow 官方推薦給廣大開發者,替代 ?TF-Slim 作為官方默認的深度學習開發的首選框架。

    Keras 有2個比較重要的概念:模型(Model)和層(Layer)。層(Layer)將常用的神經網絡層進行了封裝(全連接層、卷積層、池化層等),模型(Model)將各個層進行連接,并封裝成一個完整的網絡模型。模型調用的時候,使用 y_pred = model (x) 的形式即可。

    Keras 在 Tensorflow.Keras.Engine.Layer 下內置了深度學習中常用的網絡層,同時也支持繼承并自定義層。

    模型(Model)作為類的方式構造,通過繼承 Tensorflow.Keras.Engine.Model 這個類在定義自己的模型。在繼承類中,我們需要重寫該類的構造函數進行初始化(初始化模型需要的層和組織結構),并通過重載 call( ) 方法來進行模型的調用,同時支持增加自定義方法。

    本次 DNN 案例中,我們主要使用 Keras 中的全連接層。全連接層(Fully-connected Layer,Tensorflow.Keras.Engine.Layer.Dense)是 Keras 中最基礎和常用的層之一,對輸入矩陣 x 進行 f ( x w + b)的線性變換 + 激活函數操作。Dense 層的函數如下圖所示:

    Dense 層主要有下述2個參數:

    • 參數1:units,int 類型,輸出張量的維度;int units, Activation activation;

    • 參數2:activation,Tensorflow.Keras.Activation 類型,激活函數(常用的激活函數有 Linear,Relu,Sigmoid,Tanh)。

    接下來我們通過代碼來逐步實操 Keras 下的 DNN 。

    ① 新建項目,配置環境和引用:

    新建項目。

    選擇 .NET Core 框架。

    輸入項目名,DNN_Keras。

    確認 .NET Core 版本為 3.0 及以上。

    選擇目標平臺為 x64 。

    使用 NuGet 安裝 TensorFlow.NET 和 SciSharp.TensorFlow.Redist,如果需要使用 GPU,則安裝 SciSharp.TensorFlow.Redist-Windows-GPU。

    添加項目引用。

    using NumSharp; using System; using System.Linq; using Tensorflow; using Tensorflow.Keras; using Tensorflow.Keras.ArgsDefinition; using Tensorflow.Keras.Engine; using static Tensorflow.Binding;

    ② 定義網絡層參數、訓練數據和超參數:

    int num_classes = 10; // 0 to 9 digits int num_features = 784; // 28*28 image size// Training parameters. float learning_rate = 0.1f; int display_step = 100; int batch_size = 256; int training_steps = 1000;// Train Variables float accuracy; IDatasetV2 train_data; NDArray x_test, y_test, x_train, y_train;

    ③ 載入MNIST數據,并進行預處理:

    數據下載 或 從本地加載 → 數據展平 → 歸一化 → 轉換 Dataset → 無限復制(方便后面take) / 亂序 / 生成批次 / 預加載 → 預處理后的數據提取需要的訓練份數。

    // Prepare MNIST data. ((x_train, y_train), (x_test, y_test)) = tf.keras.datasets.mnist.load_data(); // Flatten images to 1-D vector of 784 features (28*28). (x_train, x_test) = (x_train.reshape((-1, num_features)), x_test.reshape((-1, num_features))); // Normalize images value from [0, 255] to [0, 1]. (x_train, x_test) = (x_train / 255f, x_test / 255f);// Use tf.data API to shuffle and batch data. train_data = tf.data.Dataset.from_tensor_slices(x_train, y_train); train_data = train_data.repeat().shuffle(5000).batch(batch_size).prefetch(1).take(training_steps);

    ④ 搭建 Keras 網絡模型:

    Model Subclassing 方式搭建 Keras 的 DNN 網絡模型,輸入層參數。

    // Build neural network model. var neural_net = new NeuralNet(new NeuralNetArgs{NumClasses = num_classes,NeuronOfHidden1 = 128,Activation1 = tf.keras.activations.Relu,NeuronOfHidden2 = 256,Activation2 = tf.keras.activations.Relu});

    繼承 Model 類,搭建全連接神經網絡 Dense Neural Net。在構造函數中創建網絡的層結構,并重載 call( ) 方法,指定輸入和輸出之間的函數關系。

    // Model Subclassing public class NeuralNet : Model {Layer fc1;Layer fc2;Layer output;public NeuralNet(NeuralNetArgs args) :base(args){// First fully-connected hidden layer.fc1 = Dense(args.NeuronOfHidden1, activation: args.Activation1);// Second fully-connected hidden layer.fc2 = Dense(args.NeuronOfHidden2, activation: args.Activation2);output = Dense(args.NumClasses);}// Set forward pass.protected override Tensor call(Tensor inputs, bool is_training = false, Tensor state = null){inputs = fc1.Apply(inputs);inputs = fc2.Apply(inputs);inputs = output.Apply(inputs);if (!is_training)inputs = tf.nn.softmax(inputs);return inputs;} }// Network parameters. public class NeuralNetArgs : ModelArgs {/// <summary>/// 1st layer number of neurons./// </summary>public int NeuronOfHidden1 { get; set; }public Activation Activation1 { get; set; }/// <summary>/// 2nd layer number of neurons./// </summary>public int NeuronOfHidden2 { get; set; }public Activation Activation2 { get; set; }public int NumClasses { get; set; } }

    ⑤ 訓練模型并周期顯示訓練過程:

    交叉熵損失函數和準確率評估函數。

    // Cross-Entropy Loss. Func<Tensor, Tensor, Tensor> cross_entropy_loss = (x, y) => {// Convert labels to int 64 for tf cross-entropy function.y = tf.cast(y, tf.int64);// Apply softmax to logits and compute cross-entropy.var loss = tf.nn.sparse_softmax_cross_entropy_with_logits(labels: y, logits: x);// Average loss across the batch.return tf.reduce_mean(loss); };// Accuracy metric. Func<Tensor, Tensor, Tensor> accuracy = (y_pred, y_true) => {// Predicted class is the index of highest score in prediction vector (i.e. argmax).var correct_prediction = tf.equal(tf.argmax(y_pred, 1), tf.cast(y_true, tf.int64));return tf.reduce_mean(tf.cast(correct_prediction, tf.float32), axis: -1); };

    創建隨機梯度下降(SDG)優化器和執行方法。

    // Stochastic gradient descent optimizer. var optimizer = tf.optimizers.SGD(learning_rate);// Optimization process. Action<Tensor, Tensor> run_optimization = (x, y) => {// Wrap computation inside a GradientTape for automatic differentiation.using var g = tf.GradientTape();// Forward pass.var pred = neural_net.Apply(x, is_training: true);var loss = cross_entropy_loss(pred, y);// Compute gradients.var gradients = g.gradient(loss, neural_net.trainable_variables);// Update W and b following gradients.optimizer.apply_gradients(zip(gradients, neural_net.trainable_variables.Select(x => x as ResourceVariable))); };

    應用 TensorFlow 2.x 中的自動求導機制,自動求導進行梯度下降和網絡權重變量的更新優化。每隔一定周期,打印出當前輪次網絡的訓練性能數據 loss 和 accuracy 。關于自動求導機制,請參考“6. MNIST手寫字符分類 Logistic Regression”一章節。

    // Run training for the given number of steps. foreach (var (step, (batch_x, batch_y)) in enumerate(train_data, 1)) {// Run the optimization to update W and b values.run_optimization(batch_x, batch_y);if (step % display_step == 0){var pred = neural_net.Apply(batch_x, is_training: true);var loss = cross_entropy_loss(pred, batch_y);var acc = accuracy(pred, batch_y);print($"step: {step}, loss: {(float)loss}, accuracy: {(float)acc}");} }

    ⑥ 測試集上性能評估:

    在測試集上對訓練后的模型進行預測準確率性能評估。

    // Test model on validation set. {var pred = neural_net.Apply(x_test, is_training: false);this.accuracy = (float)accuracy(pred, y_test);print($"Test Accuracy: {this.accuracy}"); }

    完整的控制臺運行代碼如下:

    using NumSharp; using System; using System.Linq; using Tensorflow; using Tensorflow.Keras; using Tensorflow.Keras.ArgsDefinition; using Tensorflow.Keras.Engine; using static Tensorflow.Binding;namespace DNN_Keras {class Program{static void Main(string[] args){DNN_Keras dnn = new DNN_Keras();dnn.Main();}}class DNN_Keras{int num_classes = 10; // 0 to 9 digitsint num_features = 784; // 28*28 image size// Training parameters.float learning_rate = 0.1f;int display_step = 100;int batch_size = 256;int training_steps = 1000;// Train Variablesfloat accuracy;IDatasetV2 train_data;NDArray x_test, y_test, x_train, y_train;public void Main(){// Prepare MNIST data.((x_train, y_train), (x_test, y_test)) = tf.keras.datasets.mnist.load_data();// Flatten images to 1-D vector of 784 features (28*28).(x_train, x_test) = (x_train.reshape((-1, num_features)), x_test.reshape((-1, num_features)));// Normalize images value from [0, 255] to [0, 1].(x_train, x_test) = (x_train / 255f, x_test / 255f);// Use tf.data API to shuffle and batch data.train_data = tf.data.Dataset.from_tensor_slices(x_train, y_train);train_data = train_data.repeat().shuffle(5000).batch(batch_size).prefetch(1).take(training_steps);// Build neural network model.var neural_net = new NeuralNet(new NeuralNetArgs{NumClasses = num_classes,NeuronOfHidden1 = 128,Activation1 = tf.keras.activations.Relu,NeuronOfHidden2 = 256,Activation2 = tf.keras.activations.Relu});// Cross-Entropy Loss.Func<Tensor, Tensor, Tensor> cross_entropy_loss = (x, y) =>{// Convert labels to int 64 for tf cross-entropy function.y = tf.cast(y, tf.int64);// Apply softmax to logits and compute cross-entropy.var loss = tf.nn.sparse_softmax_cross_entropy_with_logits(labels: y, logits: x);// Average loss across the batch.return tf.reduce_mean(loss);};// Accuracy metric.Func<Tensor, Tensor, Tensor> accuracy = (y_pred, y_true) =>{// Predicted class is the index of highest score in prediction vector (i.e. argmax).var correct_prediction = tf.equal(tf.argmax(y_pred, 1), tf.cast(y_true, tf.int64));return tf.reduce_mean(tf.cast(correct_prediction, tf.float32), axis: -1);};// Stochastic gradient descent optimizer.var optimizer = tf.optimizers.SGD(learning_rate);// Optimization process.Action<Tensor, Tensor> run_optimization = (x, y) =>{// Wrap computation inside a GradientTape for automatic differentiation.using var g = tf.GradientTape();// Forward pass.var pred = neural_net.Apply(x, is_training: true);var loss = cross_entropy_loss(pred, y);// Compute gradients.var gradients = g.gradient(loss, neural_net.trainable_variables);// Update W and b following gradients.optimizer.apply_gradients(zip(gradients, neural_net.trainable_variables.Select(x => x as ResourceVariable)));};// Run training for the given number of steps.foreach (var (step, (batch_x, batch_y)) in enumerate(train_data, 1)){// Run the optimization to update W and b values.run_optimization(batch_x, batch_y);if (step % display_step == 0){var pred = neural_net.Apply(batch_x, is_training: true);var loss = cross_entropy_loss(pred, batch_y);var acc = accuracy(pred, batch_y);print($"step: {step}, loss: {(float)loss}, accuracy: {(float)acc}");}}// Test model on validation set.{var pred = neural_net.Apply(x_test, is_training: false);this.accuracy = (float)accuracy(pred, y_test);print($"Test Accuracy: {this.accuracy}");}}// Model Subclassingpublic class NeuralNet : Model{Layer fc1;Layer fc2;Layer output;public NeuralNet(NeuralNetArgs args) :base(args){// First fully-connected hidden layer.fc1 = Dense(args.NeuronOfHidden1, activation: args.Activation1);// Second fully-connected hidden layer.fc2 = Dense(args.NeuronOfHidden2, activation: args.Activation2);output = Dense(args.NumClasses);}// Set forward pass.protected override Tensor call(Tensor inputs, bool is_training = false, Tensor state = null){inputs = fc1.Apply(inputs);inputs = fc2.Apply(inputs);inputs = output.Apply(inputs);if (!is_training)inputs = tf.nn.softmax(inputs);return inputs;}}// Network parameters.public class NeuralNetArgs : ModelArgs{/// <summary>/// 1st layer number of neurons./// </summary>public int NeuronOfHidden1 { get; set; }public Activation Activation1 { get; set; }/// <summary>/// 2nd layer number of neurons./// </summary>public int NeuronOfHidden2 { get; set; }public Activation Activation2 { get; set; }public int NumClasses { get; set; }}} }

    運行結果如下:

    The file C:\Users\Administrator\AppData\Local\Temp\mnist.npz already exists step: 100, loss: 0.4122764, accuracy: 0.9140625 step: 200, loss: 0.28498638, accuracy: 0.921875 step: 300, loss: 0.21436812, accuracy: 0.93359375 step: 400, loss: 0.23279168, accuracy: 0.91796875 step: 500, loss: 0.23876348, accuracy: 0.91015625 step: 600, loss: 0.1752773, accuracy: 0.95703125 step: 700, loss: 0.14060633, accuracy: 0.97265625 step: 800, loss: 0.14577743, accuracy: 0.95703125 step: 900, loss: 0.15461099, accuracy: 0.953125 Test Accuracy: 0.9522

    我們可以看到,loss 在不斷地下降,accuracy 不斷提高,最終的測試集的準確率為 0.9522,略低于 訓練集上的準確率 0.9531,基本屬于一個比較合理的訓練結果。


    5. 代碼下載地址

    DNN_Eager 代碼下載鏈接地址(或掃描下面的二維碼):

    https://github.com/SciSharp/TensorFlow.NET-Tutorials/blob/master/PracticeCode/DNN_Eager/DNN_Eager/Program.cs

    DNN_Keras 代碼下載鏈接地址(或掃描下面的二維碼):

    https://github.com/SciSharp/TensorFlow.NET-Tutorials/blob/master/PracticeCode/DNN_Keras/DNN_Keras/Program.cs

    總結

    以上是生活随笔為你收集整理的听说用 C# 写 TensorFlow 更高效?的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。