最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C#調(diào)用TensorFlow的實(shí)戰(zhàn)指南

 更新時(shí)間:2026年05月14日 08:54:35   作者:鐵血代碼俠  
TensorFlow 是 Google 開發(fā)的開源機(jī)器學(xué)習(xí)框架,廣泛應(yīng)用于深度學(xué)習(xí)、計(jì)算機(jī)視覺、自然語言處理等領(lǐng)域,然而,對于使用 .NET 技術(shù)棧的企業(yè)和開發(fā)者而言,如何在 C# 中利用 TensorFlow 的強(qiáng)大能力一直是個(gè)挑戰(zhàn),因此本文就給大家介紹了C#調(diào)用TensorFlow的實(shí)戰(zhàn)指南

一、引言

1.1 TensorFlow 生態(tài)概覽

TensorFlow 是 Google 開發(fā)的開源機(jī)器學(xué)習(xí)框架,廣泛應(yīng)用于深度學(xué)習(xí)、計(jì)算機(jī)視覺、自然語言處理等領(lǐng)域。其 Python API 是業(yè)界最成熟的深度學(xué)習(xí)開發(fā)接口之一。然而,對于使用 .NET 技術(shù)棧的企業(yè)和開發(fā)者而言,如何在 C# 中利用 TensorFlow 的強(qiáng)大能力一直是個(gè)挑戰(zhàn)。

1.2 為什么 C# 開發(fā)者需要 TensorFlow

  • 生產(chǎn)環(huán)境部署:很多企業(yè)后端基于 .NET 構(gòu)建,需要將 AI 模型直接集成到現(xiàn)有系統(tǒng)中
  • 類型安全:C# 的強(qiáng)類型系統(tǒng)可以在編譯期捕獲更多錯(cuò)誤
  • 性能優(yōu)勢:.NET 8+ 的 JIT 編譯器和 AOT 技術(shù)帶來出色的運(yùn)行時(shí)性能
  • 生態(tài)整合:與 ASP.NET Core、Blazor、MAUI 等 .NET 生態(tài)無縫集成

1.3 .NET 與 TensorFlow 的集成方式

在 .NET 中使用 TensorFlow 主要有以下途徑:

方式說明適用場景
TensorFlow.NET完整的 TensorFlow C# 綁定,支持訓(xùn)練和推理從零開始構(gòu)建深度學(xué)習(xí)模型
ML.NET + TensorFlow 后端ML.NET 將 TensorFlow 作為后端引擎傳統(tǒng) ML 場景復(fù)用 TF 模型
ONNX Runtime將 TF 模型轉(zhuǎn)為 ONNX 格式后加載跨框架模型部署

本文聚焦于 TensorFlow.NET —— 這是由 SciSharp 社區(qū)維護(hù)的官方 .NET 綁定庫,實(shí)現(xiàn)了 TensorFlow 的完整 API。

二、環(huán)境搭建

2.1 TensorFlow.NET 簡介

TensorFlow.NET(簡稱 TF.NET)是 .NET Standard 平臺上的 TensorFlow 綁定。它的目標(biāo)是讓 .NET 開發(fā)者能夠用 C# 或 F# 開發(fā)、訓(xùn)練和部署機(jī)器學(xué)習(xí)模型,同時(shí)保持與 Python TensorFlow API 的高度一致性。

項(xiàng)目地址:https://github.com/SciSharp/TensorFlow.NET 官方文檔:https://tensorflownet.readthedocs.io 當(dāng)前版本:0.150.0(對應(yīng) TensorFlow v2.10)

TF.NET 提供了兩個(gè)核心 NuGet 包:

  • TensorFlow.NET:底層綁定,對應(yīng) tf.* API
  • TensorFlow.Keras:高層 Keras API,對應(yīng) tf.keras.* API

2.2 NuGet 包安裝

創(chuàng)建 .NET 8 控制臺項(xiàng)目后,安裝以下包:

# 第一步:安裝核心庫
dotnet add package TensorFlow.NET --version 0.150.0
# 第二步:安裝 Keras 高層 API(可選,推薦)
dotnet add package TensorFlow.Keras
# 第三步:安裝運(yùn)行時(shí)支持包(根據(jù)你的平臺選擇其一)
# Windows/Linux CPU 版本
dotnet add package SciSharp.TensorFlow.Redist
# macOS CPU 版本
dotnet add package SciSharp.TensorFlow.Redist-OSX
# Windows GPU 版本(需要 CUDA 和 cuDNN)
dotnet add package SciSharp.TensorFlow.Redist-Windows-GPU
# Linux GPU 版本(需要 CUDA 和 cuDNN)
dotnet add package SciSharp.TensorFlow.Redist-Linux-GPU

2.3 項(xiàng)目結(jié)構(gòu)

一個(gè)典型的 TensorFlow.NET 項(xiàng)目結(jié)構(gòu)如下:

MyTfProject/
├── MyTfProject.csproj
├── Program.cs
├── models/              # 存放預(yù)訓(xùn)練模型
│   └── inception/
│       └── tensorflow_inception_graph.pb
└── images/              # 測試圖片
    └── test.jpg

2.4 GPU 支持配置(可選)

如果需要 GPU 加速,在 Windows 上需要:

  1. 安裝 NVIDIA CUDA Toolkit(推薦 11.2+)
  2. 安裝 cuDNN(與 CUDA 版本匹配)
  3. 將 CUDA bin 目錄添加到 PATH 環(huán)境變量
  4. 安裝 SciSharp.TensorFlow.Redist-Windows-GPU 包

注意:GPU 版本的包體積較大(約 500MB),首次下載需要一定時(shí)間。CPU 版本足以滿足大多數(shù)推理場景。

三、TensorFlow.NET 基礎(chǔ)

3.1 核心命名空間與導(dǎo)入

使用 TensorFlow.NET 時(shí),最常見的導(dǎo)入方式如下:

using static Tensorflow.Binding;
using static Tensorflow.KerasApi;
using Tensorflow;
using Tensorflow.NumPy;

其中: - Tensorflow.Binding 提供了 tf 靜態(tài)方法的快捷訪問 - Tensorflow.KerasApi 提供了 keras 靜態(tài)方法的快捷訪問 - Tensorflow.NumPy 提供了類似 Python NumPy 的 NDArray 操作

3.2 兩種執(zhí)行模式

TensorFlow.NET 支持兩種執(zhí)行模式:

3.2.1 Graph 模式(圖模式)

在 Graph 模式下,計(jì)算先被構(gòu)建為靜態(tài)計(jì)算圖,然后在 Session 中執(zhí)行。這是 TensorFlow 1.x 的經(jīng)典模式,適合模型部署和推理場景。

// 禁用 Eager Execution,啟用 Graph 模式
tf.compat.v1.disable_eager_execution();
var graph = tf.Graph().as_default();
graph.Import("model.pb");
using var sess = tf.Session(graph);
var result = sess.run(outputTensor, feedDict);

3.2.2 Eager Execution 模式(即時(shí)執(zhí)行)

Eager 模式是 TensorFlow 2.x 的默認(rèn)模式,操作立即執(zhí)行并返回值,更加直觀,適合開發(fā)和調(diào)試。

// 啟用 Eager Execution
tf.enable_eager_execution();
var x = tf.constant(new float[] { 1, 2, 3 });
var y = tf.constant(new float[] { 4, 5, 6 });
var z = x + y;
print(z.numpy());  // 輸出: [5 7 9]

四、模型推理(Inference)

模型推理是 TensorFlow.NET 最常用的場景——加載 Python 訓(xùn)練好的模型,在 .NET 應(yīng)用中進(jìn)行預(yù)測。本節(jié)介紹兩種主流方式。

4.1 加載 .pb 文件進(jìn)行推理

.pb(Protocol Buffer)格式是 TensorFlow 凍結(jié)模型的經(jīng)典格式,將模型結(jié)構(gòu)和權(quán)重打包為單個(gè)文件。

以下代碼示例來源于 TensorFlow.NET 官方示例倉庫中的 ImageRecognitionInception.cs,演示如何加載 Inception v3 模型進(jìn)行圖像分類推理:

using System;
using System.IO;
using System.Collections.Generic;
using Tensorflow;
using Tensorflow.NumPy;
using static Tensorflow.Binding;
public class InferenceExample
{
    public void Run()
    {
        // 使用 Graph 模式
        tf.compat.v1.disable_eager_execution();
        // 1. 創(chuàng)建計(jì)算圖并導(dǎo)入 .pb 模型
        var graph = tf.Graph().as_default();
        graph.Import("models/inception/tensorflow_inception_graph.pb");
        // 2. 獲取輸入和輸出操作的引用
        var input_operation = graph.OperationByName("input");
        var output_operation = graph.OperationByName("output");
        // 3. 加載標(biāo)簽文件
        var labels = File.ReadAllLines("models/inception/imagenet_comp_graph_label_strings.txt");
        // 4. 讀取并預(yù)處理輸入圖片
        var inputTensor = ReadTensorFromImageFile("images/test.jpg");
        // 5. 創(chuàng)建 Session 并執(zhí)行推理
        using var sess = tf.Session(graph);
        var results = sess.run(
            output_operation.outputs[0],
            (input_operation.outputs[0], inputTensor)
        );
        // 6. 處理結(jié)果
        results = np.squeeze(results);
        int idx = np.argmax(results);
        Console.WriteLine($"識別結(jié)果: {labels[idx]} (置信度: {results[idx]:P2})");
    }
    /// <summary>
    /// 讀取圖片文件并預(yù)處理為模型所需的 Tensor
    /// </summary>
    private NDArray ReadTensorFromImageFile(string file_name,
        int input_height = 224,
        int input_width = 224,
        int input_mean = 117,
        int input_std = 1)
    {
        // 在臨時(shí) Graph 中構(gòu)建圖片預(yù)處理流程
        var g = tf.Graph().as_default();
        var file_reader = tf.io.read_file(file_name, "file_reader");
        var decodeJpeg = tf.image.decode_jpeg(file_reader, channels: 3, name: "DecodeJpeg");
        var cast = tf.cast(decodeJpeg, tf.float32);
        var dims_expander = tf.expand_dims(cast, 0);
        var resize = tf.constant(new int[] { input_height, input_width });
        var bilinear = tf.image.resize_bilinear(dims_expander, resize);
        var sub = tf.subtract(bilinear, new float[] { input_mean });
        var normalized = tf.divide(sub, new float[] { input_std });
        using var sess = tf.Session(g);
        return sess.run(normalized);
    }
}

關(guān)鍵步驟解析:

  1. graph.Import():將 .pb 文件中的計(jì)算圖導(dǎo)入當(dāng)前 Graph 對象
  2. graph.OperationByName():通過名稱獲取圖中特定操作的引用
  3. sess.run():在 Session 中執(zhí)行計(jì)算,傳入輸入 Tensor 和待獲取的輸出 Tensor
  4. 圖片預(yù)處理:使用 TensorFlow 自身的圖像操作(讀取、解碼、縮放、歸一化)確保輸入格式與模型訓(xùn)練時(shí)一致

4.2 對象檢測推理示例

以下示例來源于官方示例倉庫中的 DetectInMobilenet.cs,展示了使用 MobileNet SSD 模型進(jìn)行對象檢測的完整流程:

using System;
using System.IO;
using System.Linq;
using System.Drawing;
using Tensorflow;
using Tensorflow.NumPy;
using static Tensorflow.Binding;
public class ObjectDetectionExample
{
    public float MIN_SCORE = 0.5f;
    public void Run()
    {
        tf.compat.v1.disable_eager_execution();
        // 導(dǎo)入 MobileNet SSD 模型
        var graph = new Graph().as_default();
        graph.Import("ssd_mobilenet_v1_coco_2018_01_28/frozen_inference_graph.pb");
        // 獲取輸入輸出 Tensor 引用
        var imgTensor = graph.OperationByName("image_tensor");
        var tensorNum = graph.OperationByName("num_detections");
        var tensorBoxes = graph.OperationByName("detection_boxes");
        var tensorScores = graph.OperationByName("detection_scores");
        var tensorClasses = graph.OperationByName("detection_classes");
        // 讀取輸入圖片
        var imgArr = ReadTensorFromImageFile("images/input.jpg");
        // 執(zhí)行推理
        Tensor[] outTensors = new Tensor[] { tensorNum, tensorBoxes, tensorScores, tensorClasses };
        using var sess = tf.Session(graph);
        var results = sess.run(outTensors, new FeedItem(imgTensor, imgArr));
        // 解析結(jié)果
        var scores = results[2].ToArray<float>();
        var boxes = results[1].ToArray<float>();
        var ids = np.squeeze(results[3]).ToArray<float>();
        for (int i = 0; i < scores.Length; i++)
        {
            if (scores[i] > MIN_SCORE)
            {
                // 解析邊界框坐標(biāo)
                float top = boxes[i * 4] * imageHeight;
                float left = boxes[i * 4 + 1] * imageWidth;
                float bottom = boxes[i * 4 + 2] * imageHeight;
                float right = boxes[i * 4 + 3] * imageWidth;
                Console.WriteLine($"檢測到對象: 類別ID={ids[i]}, 置信度={scores[i]:P2}");
                Console.WriteLine($"  邊界框: ({left}, {top}) - ({right}, {bottom})");
            }
        }
    }
    private NDArray ReadTensorFromImageFile(string file_name)
    {
        var graph = tf.Graph().as_default();
        var file_reader = tf.io.read_file(file_name, "file_reader");
        var decodeJpeg = tf.image.decode_jpeg(file_reader, channels: 3, name: "DecodeJpeg");
        var casted = tf.cast(decodeJpeg, TF_DataType.TF_UINT8);
        var dims_expander = tf.expand_dims(casted, 0);
        using var sess = tf.Session(graph);
        return sess.run(dims_expander);
    }
}

4.3 加載 SavedModel 格式

SavedModel 是 TensorFlow 2.x 推薦的模型保存格式,包含模型結(jié)構(gòu)、權(quán)重和服務(wù)簽名。

using Tensorflow;
using static Tensorflow.Binding;
public void LoadSavedModel()
{
    // 使用 SavedModel 加載
    var sess = tf.Session();
    // 從指定目錄加載 SavedModel
    tf.saved_model.load(sess, tags: new[] { "serve" }, export_dir: "saved_model_dir");
    // 通過簽名運(yùn)行推理
    var inputTensor = ...; // 準(zhǔn)備輸入
    var result = sess.run(outputTensor, (inputPlaceholder, inputTensor));
}

注意:SavedModel 的 API 在 TensorFlow.NET 中的支持程度取決于版本。對于 TensorFlow 2.x 訓(xùn)練的模型,建議使用 .pb 格式(通過 Python 端的 tf.compat.v1.graph_util.convert_variables_to_constants 轉(zhuǎn)換)以確保最佳兼容性。

4.4 性能優(yōu)化建議

批量推理:將多張圖片合并為一個(gè) batch 進(jìn)行推理,充分利用 GPU 并行計(jì)算能力。

// 假設(shè) singleImages 是多張圖片 Tensor 的列表
var batchTensor = tf.stack(singleImages.ToArray());  // 合并為 [batch, H, W, C]
var results = sess.run(outputTensor, (inputTensor, batchTensor));

異步推理:使用 Task.Run 將推理操作放在后臺線程執(zhí)行,避免阻塞 UI 線程。

public async Task<string> PredictAsync(byte[] imageData)
{
    return await Task.Run(() =>
    {
        var tensor = PreprocessImage(imageData);
        var result = sess.run(outputTensor, (inputTensor, tensor));
        return PostProcessResult(result);
    });
}

Session 復(fù)用:創(chuàng)建 Session 的開銷較大,應(yīng)在應(yīng)用生命周期內(nèi)復(fù)用同一個(gè) Session 實(shí)例。

五、Keras 高層 API 使用

TensorFlow.NET 內(nèi)置了 Keras 高層接口(TensorFlow.Keras 包),讓模型構(gòu)建和訓(xùn)練變得簡潔直觀。

5.1 Keras Sequential API

Sequential API 適用于層與層之間線性堆疊的模型。以下示例來源于官方示例倉庫 ImageClassificationKeras.cs

using System.Collections.Generic;
using Tensorflow;
using Tensorflow.Keras.Engine;
using static Tensorflow.Binding;
using static Tensorflow.KerasApi;
public class KerasSequentialExample
{
    Model model;
    int batch_size = 32;
    int epochs = 10;
    public void Run()
    {
        // 啟用 Eager 模式
        tf.enable_eager_execution();
        BuildModel();
        Train();
    }
    public void BuildModel()
    {
        int num_classes = 5;  // 5 種花卉分類
        var layers = keras.layers;
        // 使用 Sequential API 構(gòu)建模型
        var myLayers = new List<ILayer>
        {
            // 歸一化層:將像素值從 [0, 255] 縮放到 [0, 1]
            layers.Rescaling(1.0f / 255, input_shape: (64, 64, 3)),
            // 卷積層 + 池化層
            layers.Conv2D(16, 3, padding: "same", activation: keras.activations.Relu),
            layers.MaxPooling2D(),
            // 展平層
            layers.Flatten(),
            // 全連接層
            layers.Dense(128, activation: keras.activations.Relu),
            // 輸出層(logits,未經(jīng) softmax)
            layers.Dense(num_classes)
        };
        model = keras.Sequential(myLayers);
        // 編譯模型
        model.compile(
            optimizer: keras.optimizers.Adam(),
            loss: keras.losses.SparseCategoricalCrossentropy(from_logits: true),
            metrics: new[] { "accuracy" }
        );
        // 打印模型摘要
        model.summary();
    }
    public void Train()
    {
        // 訓(xùn)練模型(需先準(zhǔn)備 train_ds 和 val_ds 數(shù)據(jù)集)
        model.fit(train_ds, validation_data: val_ds, epochs: epochs);
    }
}

5.2 Keras Functional API

Functional API 適用于具有多輸入、多輸出或殘差連接等復(fù)雜拓?fù)涞哪P汀R韵率纠齺碓从诠俜绞纠齻}庫 MnistFnnKerasFunctional.cs

using Tensorflow.Keras.Engine;
using Tensorflow.NumPy;
using static Tensorflow.Binding;
using static Tensorflow.KerasApi;
public class KerasFunctionalExample
{
    IModel model;
    NDArray x_train, y_train, x_test, y_test;
    public void Run()
    {
        tf.enable_eager_execution();
        PrepareData();
        BuildModel();
        Train();
    }
    public void PrepareData()
    {
        // 加載 MNIST 數(shù)據(jù)集
        (x_train, y_train, x_test, y_test) = keras.datasets.mnist.load_data();
        x_train = x_train.reshape((60000, 784)) / 255f;
        x_test = x_test.reshape((10000, 784)) / 255f;
    }
    public void BuildModel()
    {
        var layers = keras.layers;
        // 定義輸入
        var inputs = keras.Input(shape: 784);
        // 第一全連接層
        var outputs = layers.Dense(64, activation: keras.activations.Relu).Apply(inputs);
        // 第二全連接層
        outputs = layers.Dense(64, activation: keras.activations.Relu).Apply(outputs);
        // 輸出層
        outputs = layers.Dense(10).Apply(outputs);
        // 構(gòu)建模型
        model = keras.Model(inputs, outputs, name: "mnist_model");
        model.summary();
        // 編譯模型
        model.compile(
            loss: keras.losses.SparseCategoricalCrossentropy(from_logits: true),
            optimizer: keras.optimizers.RMSprop(),
            metrics: new[] { "accuracy" }
        );
    }
    public void Train()
    {
        model.fit(x_train, y_train, batch_size: 64, epochs: 2, validation_split: 0.2f);
        model.evaluate(x_test, y_test, verbose: 2);
        // 保存模型
        model.save("mnist_model");
        // 重新加載模型
        // model = keras.models.load_model("mnist_model");
    }
}

5.3 數(shù)據(jù)集預(yù)處理

Keras 提供了便捷的圖像數(shù)據(jù)加載工具 image_dataset_from_directory

// 從目錄結(jié)構(gòu)自動(dòng)加載圖片數(shù)據(jù)集
// 目錄結(jié)構(gòu)要求:
// data_dir/
//   class_a/
//     img1.jpg, img2.jpg, ...
//   class_b/
//     img1.jpg, img2.jpg, ...
var train_ds = keras.preprocessing.image_dataset_from_directory(
    data_dir,
    validation_split: 0.2f,
    subset: "training",
    seed: 123,
    image_size: (64, 64),
    batch_size: 32
);
var val_ds = keras.preprocessing.image_dataset_from_directory(
    data_dir,
    validation_split: 0.2f,
    subset: "validation",
    seed: 123,
    image_size: (64, 64),
    batch_size: 32
);
// 數(shù)據(jù)增強(qiáng):打亂順序 + 預(yù)取優(yōu)化
train_ds = train_ds.shuffle(1000).prefetch(buffer_size: -1);
val_ds = val_ds.prefetch(buffer_size: -1);

六、遷移學(xué)習(xí)(Transfer Learning)

6.1 什么是遷移學(xué)習(xí)

遷移學(xué)習(xí)是深度學(xué)習(xí)中的一項(xiàng)重要技術(shù)。其核心思想是:利用在大規(guī)模數(shù)據(jù)集(如 ImageNet)上預(yù)訓(xùn)練好的模型,將其學(xué)到的特征提取能力遷移到新的、數(shù)據(jù)量較小的目標(biāo)任務(wù)上。

為什么要使用遷移學(xué)習(xí)?

  • 減少訓(xùn)練數(shù)據(jù)需求:只需幾百張目標(biāo)類別圖片即可達(dá)到不錯(cuò)的效果
  • 加速訓(xùn)練:預(yù)訓(xùn)練模型已經(jīng)學(xué)習(xí)到了通用的邊緣、紋理等低級特征
  • 提升準(zhǔn)確率:相比從零訓(xùn)練,遷移學(xué)習(xí)通常在小型數(shù)據(jù)集上表現(xiàn)更好

6.2 遷移學(xué)習(xí)的兩種策略

策略方法適用場景
特征提取凍結(jié)預(yù)訓(xùn)練模型的所有層,只訓(xùn)練新增的分類頭目標(biāo)數(shù)據(jù)集很小,與源域相似
微調(diào)(Fine-tuning)解凍預(yù)訓(xùn)練模型的部分頂層,與新分類頭一起訓(xùn)練目標(biāo)數(shù)據(jù)集較大,與源域差異較大

6.3 使用 SciSharp ModelWizard 進(jìn)行遷移學(xué)習(xí)

TensorFlow.NET 的 SciSharp 子庫提供了一個(gè)高級封裝 ModelWizard,可以大幅簡化遷移學(xué)習(xí)流程。以下示例來源于官方示例倉庫 TransferLearningWithInceptionV3.cs

using SciSharp.Models;
using SciSharp.Models.ImageClassification;
using System;
using System.IO;
using Tensorflow.Keras.Utils;
using static Tensorflow.Binding;
public class TransferLearningExample
{
    float accuracy;
    public void Run()
    {
        PrepareData();
        Train();
        Test();
        Predict();
        Console.WriteLine($"測試準(zhǔn)確率: {accuracy:P2}");
    }
    /// <summary>
    /// 準(zhǔn)備數(shù)據(jù):下載花卉數(shù)據(jù)集
    /// </summary>
    public void PrepareData()
    {
        string fileName = "flower_photos.tgz";
        string dataDir = "image_classification_v1";
        string url = $"http://download.tensorflow.org/example_images/{fileName}";
        Web.Download(url, dataDir, fileName);
        Compress.ExtractTGZ(Path.Join(dataDir, fileName), dataDir);
    }
    /// <summary>
    /// 使用 ModelWizard 訓(xùn)練遷移學(xué)習(xí)模型
    /// 底層使用 InceptionV3 作為預(yù)訓(xùn)練基座
    /// </summary>
    public void Train()
    {
        var wizard = new ModelWizard();
        var task = wizard.AddImageClassificationTask<TransferLearning>(new TaskOptions
        {
            DataDir = @"image_classification_v1\flower_photos",
        });
        task.Train(new TrainingOptions
        {
            TrainingSteps = 100
        });
    }
    /// <summary>
    /// 測試模型準(zhǔn)確率
    /// </summary>
    public void Test()
    {
        var wizard = new ModelWizard();
        var task = wizard.AddImageClassificationTask<TransferLearning>(new TaskOptions
        {
            DataDir = @"image_classification_v1\flower_photos",
            ModelPath = @"image_classification_v1\saved_model.pb"
        });
        var result = task.Test(new TestingOptions { });
        accuracy = result.Accuracy;
    }
    /// <summary>
    /// 使用訓(xùn)練好的模型進(jìn)行預(yù)測
    /// </summary>
    public void Predict()
    {
        var wizard = new ModelWizard();
        var task = wizard.AddImageClassificationTask<TransferLearning>(new TaskOptions
        {
            ModelPath = @"image_classification_v1\saved_model.pb"
        });
        var imgPath = Path.Join("image_classification_v1", "flower_photos", 
            "daisy", "5547758_eea9edfd54_n.jpg");
        var input = ImageUtil.ReadImageFromFile(imgPath);
        var result = task.Predict(input);
        Console.WriteLine($"預(yù)測結(jié)果: {result.Label}");
    }
}

這個(gè)示例做了什么?

  1. 下載 TensorFlow 官方的花卉數(shù)據(jù)集(5 個(gè)類別:daisy、dandelion、roses、sunflowers、tulips)
  2. 使用 ModelWizard + TransferLearning 模板,自動(dòng)完成:
    • 加載 InceptionV3 預(yù)訓(xùn)練模型
    • 凍結(jié)特征提取層
    • 添加新的分類層(5 類輸出)
    • 訓(xùn)練新的分類層
  3. 保存訓(xùn)練好的模型為 .pb 文件
  4. 加載模型進(jìn)行測試和預(yù)測

6.4 手動(dòng)實(shí)現(xiàn)遷移學(xué)習(xí)(Keras 方式)

如果 ModelWizard 不能滿足需求,也可以手動(dòng)實(shí)現(xiàn)遷移學(xué)習(xí)流程:

using System.Collections.Generic;
using Tensorflow;
using Tensorflow.Keras.Engine;
using static Tensorflow.Binding;
using static Tensorflow.KerasApi;
public class ManualTransferLearning
{
    Model model;
    IModel base_model;  // 類級別字段,供 BuildTransferLearningModel 和 FineTune 共用
    public void BuildTransferLearningModel(int num_classes = 5)
    {
        tf.enable_eager_execution();
        var layers = keras.layers;
        // 1. 加載預(yù)訓(xùn)練的 MobileNetV2 模型(不含頂層)
        base_model = keras.applications.MobileNetV2(
            input_shape: (224, 224, 3),
            include_top: false,
            weights: "imagenet"
        );
        // 2. 凍結(jié)預(yù)訓(xùn)練層
        base_model.trainable = false;
        // 3. 構(gòu)建新模型
        var inputs = keras.Input(shape: (224, 224, 3));
        // 數(shù)據(jù)預(yù)處理
        var x = layers.Rescaling(1.0f / 127.5f, input_shape: (224, 224, 3)).Apply(inputs);
        x = layers.Rescaling(-1, offset: 1).Apply(x);  // 歸一化到 [-1, 1]
        // 通過預(yù)訓(xùn)練基座提取特征
        x = base_model.Apply(x);
        // 全局平均池化
        x = layers.GlobalAveragePooling2D().Apply(x);
        // Dropout 防止過擬合
        x = layers.Dropout(0.2f).Apply(x);
        // 輸出層
        var outputs = layers.Dense(num_classes).Apply(x);
        model = keras.Model(inputs, outputs, name: "transfer_learning_model");
        model.compile(
            optimizer: keras.optimizers.Adam(),
            loss: keras.losses.SparseCategoricalCrossentropy(from_logits: true),
            metrics: new[] { "accuracy" }
        );
        model.summary();
    }
    /// <summary>
    /// 微調(diào):解凍部分頂層繼續(xù)訓(xùn)練
    /// 注意:base_model 是類級別字段,在 BuildTransferLearningModel 中初始化
    /// </summary>
    public void FineTune()
    {
        // 解凍最后 20 層
        base_model.trainable = true;
        foreach (var layer in base_model.layers.TakeLast(20))
        {
            layer.trainable = true;
        }
        // 重新編譯模型(微調(diào)需要更小的學(xué)習(xí)率)
        model.compile(
            optimizer: keras.optimizers.Adam(1e-5f),
            loss: keras.losses.SparseCategoricalCrossentropy(from_logits: true),
            metrics: new[] { "accuracy" }
        );
        // 繼續(xù)訓(xùn)練
        model.fit(train_ds, validation_data: val_ds, epochs: 10);
    }
}

說明keras.applications.MobileNetV2 的使用依賴于 TensorFlow.Keras 包中預(yù)訓(xùn)練權(quán)重的支持。如果本地沒有預(yù)訓(xùn)練權(quán)重緩存,首次加載時(shí)會(huì)自動(dòng)下載。

七、模型導(dǎo)出與部署

7.1 Keras 模型保存

// 保存模型(包含結(jié)構(gòu)和權(quán)重)
model.save("my_model");
// 重新加載模型
var loadedModel = keras.models.load_model("my_model");

7.2 導(dǎo)出為 SavedModel 格式

// Keras 模型默認(rèn)以 SavedModel 格式保存
// 目錄結(jié)構(gòu):
// my_model/
//   saved_model.pb
//   variables/
//     variables.data-00000-of-00001
//     variables.index
//   assets/

7.3 .NET 應(yīng)用中的部署

部署時(shí)需要考慮以下幾點(diǎn):

  1. 運(yùn)行時(shí)依賴:確保目標(biāo)機(jī)器上安裝了正確的 SciSharp.TensorFlow.Redist 包
  2. 模型路徑:模型文件應(yīng)隨應(yīng)用一起發(fā)布,使用相對路徑或絕對路徑加載
  3. 內(nèi)存管理:TensorFlow Session 占用較多內(nèi)存,應(yīng)復(fù)用而非每次創(chuàng)建
  4. 線程安全tf.Session 不是線程安全的,多線程場景需要使用 lock 或線程局部 Session
// 線程安全的推理服務(wù)示例
public class InferenceService : IDisposable
{
    private readonly Session _session;
    private readonly Tensor _inputTensor;
    private readonly Tensor _outputTensor;
    private readonly object _lock = new object();
    public InferenceService(string modelPath)
    {
        tf.compat.v1.disable_eager_execution();
        var graph = tf.Graph().as_default();
        graph.Import(modelPath);
        _session = tf.Session(graph);
        _inputTensor = graph.OperationByName("input");
        _outputTensor = graph.OperationByName("output");
    }
    public NDArray Predict(NDArray input)
    {
        lock (_lock)
        {
            return _session.run(_outputTensor, (_inputTensor, input));
        }
    }
    public void Dispose()
    {
        _session?.close();
    }
}

7.4 Docker 容器化部署

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 80
# 安裝 TensorFlow 運(yùn)行時(shí)依賴
RUN apt-get update && apt-get install -y \
    libgomp1 \
    && rm -rf /var/lib/apt/lists/*
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["MyApp.csproj", "."]
RUN dotnet restore
COPY . .
RUN dotnet publish -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=build /app/publish .
COPY models/ ./models/
ENTRYPOINT ["dotnet", "MyApp.dll"]

八、常見問題與最佳實(shí)踐

8.1 版本兼容性

TensorFlow.NET 版本對應(yīng) TensorFlow 版本備注
0.150.x2.10當(dāng)前最新穩(wěn)定版
0.6x2.6舊版,部分 API 有差異
0.15.x1.15僅支持 TensorFlow 1.x 模式

重要:TensorFlow.NET 的維護(hù)團(tuán)隊(duì)目前資源有限,新功能和 bug 修復(fù)主要依賴社區(qū) PR。建議鎖定特定版本,避免自動(dòng)升級導(dǎo)致兼容性問題。

8.2 內(nèi)存管理

TensorFlow 的 Tensor 和 Session 都實(shí)現(xiàn)了 IDisposable,使用后應(yīng)及時(shí)釋放:

// 正確做法:使用 using 語句
using var sess = tf.Session(graph);
var result = sess.run(outputTensor, (inputTensor, input));
// sess 會(huì)在作用域結(jié)束時(shí)自動(dòng)釋放

8.3 調(diào)試技巧

  1. 打印 Tensor 信息print(tensor.shape) 或 print(tensor.numpy())
  2. 模型摘要model.summary() 查看各層參數(shù)
  3. Graph 可視化:導(dǎo)出 GraphDef 后用 TensorBoard 查看
  4. 異常排查:TensorFlow.NET 的異常信息通常不夠詳細(xì),建議在關(guān)鍵步驟添加 try-catch 并打印中間變量

8.4 性能調(diào)優(yōu)

  • 使用 GPU:對于推理任務(wù),GPU 可以帶來 5-10 倍的速度提升
  • 批處理:合并多個(gè)請求為一個(gè) batch 推理
  • 模型量化:將 FP32 模型轉(zhuǎn)為 FP16 或 INT8 以減小模型體積和提升推理速度
  • AOT 編譯:使用 .NET 8 的 AOT 功能減少啟動(dòng)時(shí)間

九、總結(jié)

TensorFlow.NET 為 .NET 開發(fā)者提供了一條通往深度學(xué)習(xí)的可行路徑。從簡單的模型推理到完整的遷移學(xué)習(xí)流程,TF.NET 都能勝任。

核心要點(diǎn)回顧:

  1. 環(huán)境搭建:安裝 TensorFlow.NET + TensorFlow.Keras + SciSharp.TensorFlow.Redist
  2. 模型推理:使用 Graph 模式加載 .pb 文件,通過 sess.run() 執(zhí)行推理
  3. Keras 建模:支持 Sequential 和 Functional 兩種 API,可以完整訓(xùn)練模型
  4. 遷移學(xué)習(xí):使用 ModelWizard 或手動(dòng) Keras 方式加載預(yù)訓(xùn)練模型并進(jìn)行微調(diào)
  5. 部署:注意線程安全和內(nèi)存管理,推薦 Docker 容器化部署

TensorFlow.NET 雖然不是 .NET AI 生態(tài)的唯一選擇(還有 ML.NET、ONNX Runtime 等),但對于需要從 TensorFlow 生態(tài)遷移或復(fù)用模型的團(tuán)隊(duì)來說,它是最直接、最完整的解決方案。

以上就是C#調(diào)用TensorFlow的實(shí)戰(zhàn)指南的詳細(xì)內(nèi)容,更多關(guān)于C#調(diào)用TensorFlow的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • c#使用win32api實(shí)現(xiàn)獲取光標(biāo)位置

    c#使用win32api實(shí)現(xiàn)獲取光標(biāo)位置

    本文給大家匯總了2個(gè)使用C#實(shí)現(xiàn)獲取光標(biāo)位置的代碼,非常的簡單實(shí)用,第二種方法更為全面,推薦給大家。
    2016-02-02
  • C# Base 64 編碼/解碼實(shí)現(xiàn)代碼

    C# Base 64 編碼/解碼實(shí)現(xiàn)代碼

    這篇文章主要介紹了C# Base 64 編碼/解碼實(shí)現(xiàn)代碼,需要的朋友可以參考下
    2016-02-02
  • 在winform中嵌入第三方軟件窗體的實(shí)踐分享

    在winform中嵌入第三方軟件窗體的實(shí)踐分享

    這篇文章主要介紹了在winform中如何嵌入第三方軟件窗體的實(shí)踐分享,文中通過代碼示例和圖文給大家介紹的非常詳細(xì),具有一定參考價(jià)值,需要的朋友可以參考下
    2024-03-03
  • C#實(shí)現(xiàn)自定義windows系統(tǒng)日志的方法

    C#實(shí)現(xiàn)自定義windows系統(tǒng)日志的方法

    這篇文章主要介紹了C#實(shí)現(xiàn)自定義windows系統(tǒng)日志的方法,涉及C#針對windows系統(tǒng)日志的創(chuàng)建、讀寫及刪除技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2015-08-08
  • C# dump系統(tǒng)lsass內(nèi)存和sam注冊表詳細(xì)

    C# dump系統(tǒng)lsass內(nèi)存和sam注冊表詳細(xì)

    這篇文章主要介紹了C# dump系統(tǒng)lsass內(nèi)存和sam注冊表,在這里選擇 C# 的好處是體積小,結(jié)合 loadAssembly 方便免殺,希望對讀者們有所幫助
    2021-09-09
  • C#使用iTextSharp獲取PDF文件書簽信息的操作方法

    C#使用iTextSharp獲取PDF文件書簽信息的操作方法

    C# iTextSharp是一個(gè)用于處理PDF文件的源庫,它提供了一系列的功能,包括創(chuàng)建PDF文件,以及提取和操作PDF文件中的內(nèi)容,本文給大家介紹了C#使用iTextSharp獲取PDF文件書簽信息的操作方法,需要的朋友可以參考下
    2024-04-04
  • C#中Activator的具體使用

    C#中Activator的具體使用

    本文主要介紹了C#中Activator的具體使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-12-12
  • asp.net新聞列表生成靜態(tài)頁之批量和單頁生成

    asp.net新聞列表生成靜態(tài)頁之批量和單頁生成

    web程序的高訪問量、大數(shù)據(jù)量、高效的用戶體驗(yàn)度,使靜態(tài)頁技術(shù)在越來越多的網(wǎng)站上發(fā)揮作用。這篇文章主要介紹asp.net新聞列表生成靜態(tài)頁之批量和單頁生成,有需要的朋友可以參考下
    2015-08-08
  • C# 使用 Castle 實(shí)現(xiàn) AOP及如何用 Autofac 集成 Castle

    C# 使用 Castle 實(shí)現(xiàn) AOP及如何用 Autofac 集成 Castle

    這篇文章主要介紹了C# 使用 Castle 實(shí)現(xiàn) AOP及如何用 Autofac 集成 Castle,幫助大家更好的理解和學(xué)習(xí)使用c#,感興趣的朋友可以了解下
    2021-02-02
  • C# HTTP認(rèn)證方式詳解與代碼實(shí)現(xiàn)

    C# HTTP認(rèn)證方式詳解與代碼實(shí)現(xiàn)

    在C#中,HTTP認(rèn)證是客戶端與服務(wù)器之間進(jìn)行身份驗(yàn)證的一種機(jī)制,常見的HTTP認(rèn)證方式包括:Basic認(rèn)證、Digest認(rèn)證、OAuth、Bearer Token等,下面我們將從工作原理、優(yōu)缺點(diǎn)對比、代碼實(shí)現(xiàn)、案例實(shí)戰(zhàn)四個(gè)方面詳細(xì)介紹這些認(rèn)證方式,需要的朋友可以參考下
    2025-03-03

最新評論

松江区| 漠河县| 富裕县| 建昌县| 彰化市| 盘锦市| 钟山县| 通渭县| 贡山| 闽清县| 九龙坡区| 星座| 曲沃县| 罗山县| 平塘县| 安庆市| 尉犁县| 平陆县| 中山市| 抚州市| 奉化市| 潜江市| 绍兴县| 井研县| 清河县| 宜川县| 利川市| 新晃| 崇州市| 东明县| 双峰县| 永修县| 红桥区| 昌宁县| 上犹县| 惠东县| 遂川县| 宜宾市| 濮阳市| 普格县| 泗阳县|