<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      C# 深度學習框架 TorchSharp 原生訓練模型和圖像識別-自定義網絡模型和識別手寫數字

      教程名稱:使用 C# 入門深度學習

      作者:癡者工良

      教程地址:https://torch.whuanle.cn

      電子書倉庫:https://github.com/whuanle/cs_pytorch

      Maomi.Torch 項目倉庫:https://github.com/whuanle/Maomi.Torch

      使用 Torch 訓練模型

      本章主要參考《破解深度學習》的第四章,在本章將會實現一個數字分類器,主要包括數據加載和處理、模型訓練和保存、預訓練模型加載,但是內容跟 開始使用 Torch 一章差不多,只是數據集和網絡定義不一樣,通過本章的案例幫助讀者進一步了解 TorchSharp 以及掌握模型訓練的步驟和基礎。

      本章代碼請參考 example2.3。


      搭建神經網絡的一般步驟:

      image-20241204201648009


      在上一篇中我們通過示例已經學習到相關的過程,所以本章會在之前的基礎上繼續講解一些細節和步驟。


      在上一章中,我們學習了如何下載和加載數據集,如果將數據集里面的圖片導出,我們可以發現里面都是單個數字。

      你可以使用 Maomi.Torch 包中的擴展方法將數據集轉存到本地目錄中。

      for (int i = 0; i < training_data.Count; i++)
      {
          var dic = training_data.GetTensor(i);
          var img = dic["data"];
          var label = dic["label"];
      
          img.SaveJpeg("imgs/{i}.jpg");
      }
      

      如圖所示:

      image-20241204203025017


      每個圖片的大小是 28*28=784,所以神經網絡的輸入層的大小是 784。

      image-20241204204305065


      我們直接知道,由于數據集的圖片都是 0-9 的數字,都是灰度圖像(沒有彩色),因此模型訓練結果的輸出應該是 10 個,也就是神經網絡的輸出層神經元個數是 10。


      神經網絡的輸入層是要固定大小是,表示神經元的個數輸入是固定的,不是隨時可以擴充的,也就是一個神經網絡不能輸入任意大小的圖像,這些圖像都要經過一定的算法出來,生成與神經網絡輸入層對應大小的圖像。

      定義神經網絡

      第一步,定義我們的網絡模型,這是一個全連接網絡,由激活函數和三個線性層組成。

      該網絡模型沒有指定輸入層和輸出層的大小,這樣該模型可以適配不同的圖像分類任務,開發者在訓練和加載模式時,指定輸入層和輸出層大小即可。

      代碼如下所示:

      using TorchSharp;
      using static TorchSharp.torch;
      
      using nn = TorchSharp.torch.nn;
      
      public class MLP : nn.Module<Tensor, Tensor>, IDisposable
      {
          private readonly int _inputSize;
          private readonly int _hiddenSize;
          private readonly int _numClasses;
      
          private TorchSharp.Modules.Linear fc1;
          private TorchSharp.Modules.ReLU relu;
          private TorchSharp.Modules.Linear fc2;
          private TorchSharp.Modules.Linear fc3;
      
          /// <summary></summary>
          /// <param name="inputSize">輸入層大小,圖片的寬*高.</param>
          /// <param name="hiddenSize">隱藏層大小.</param>
          /// <param name="outputSize">輸出層大小,例如有多少個分類.</param>
          /// <param name="device"></param>
          public MLP(int inputSize, int hiddenSize, int outputSize) : base(nameof(MLP))
          {
              _inputSize = inputSize;
              _hiddenSize = hiddenSize;
              _numClasses = outputSize;
      
              // 定義激活函數和線性層
              relu = nn.ReLU();
              fc1 = nn.Linear(inputSize, hiddenSize);
              fc2 = nn.Linear(hiddenSize, hiddenSize);
              fc3 = nn.Linear(hiddenSize, outputSize);
      
              RegisterComponents();
          }
      
          public override torch.Tensor forward(torch.Tensor input)
          {
              // 一層一層傳遞
              // 第一層讀取輸入,然后傳遞給激活函數,
              // 第二層讀取第一層的輸出,然后傳遞給激活函數,
              // 第三層讀取第二層的輸出,然后生成輸出結果
              var @out = fc1.call(input);
              @out = relu.call(@out);
              @out = fc2.call(@out);
              @out = relu.call(@out);
              @out = fc3.call(@out);
              return @out;
          }
      
          protected override void Dispose(bool disposing)
          {
              base.Dispose(disposing);
              fc1.Dispose();
              relu.Dispose();
              fc2.Dispose();
              fc3.Dispose();
          }
      }
      

      首先 fc1 作為第一層網絡,輸入的圖像需要轉換為一維結構,主要用于接收數據、數據預處理。由于繪圖太麻煩了,這里用文字簡單說明一下,例如圖像是 28*28,也就是每行有 28 個像素,一共 28 行,那么使用一個 784 大小的數組可以將圖像的每一行首尾連在一起,放到一個一維數組中。

      由于圖像都是灰度圖像,一個黑白像素值在 0-255 之間(byte 類型),如果使用 [0.0,1.0] 之間表示黑白(float32 類型),那么輸入像素表示為灰度,值為 0.0 表示白色,值為 1.0 表示黑色,中間數值表示灰度。

      大多數情況下,或者說在本教程中,圖像的像素都是使用 float32 類型表示,即 torch.Tensor 存儲的圖像信息都是 float32 類型表示一個像素。

      image-20250205141415174

      圖來自《深入淺出神經網絡與深度學習》。


      fc2 是隱藏層,在本章示范的網絡模型中,隱藏層只有一層,大小是 15 個神經元,承擔者特征提取、非線性變換等職責,隱藏層的神經元數量是不定的,主要是根據經驗來設置,然后根據訓練的模型性能來調整。


      fc3 是輸出層,根據提取的特征將輸出推送到 10 個神經元中,每個神經元表示一個數值,每個神經元都會接收到消息,但是因為不同數字的特征和權重值不一樣,所以每個神經元的值都不一樣,接收到的值就是表示當前數字的可能性概率。


      加載數據集

      加載數據集的代碼示例如下,由于上一章已經講解過,因此這里就不再贅述。

      // 1. 加載數據集
      
      // 從 MNIST 數據集下載數據或者加載已經下載的數據
      using var train_data = datasets.MNIST("./mnist/data", train: true, download: true, target_transform: transforms.ConvertImageDtype(ScalarType.Float32));
      using var test_data = datasets.MNIST("./mnist/data", train: false, download: true, target_transform: transforms.ConvertImageDtype(ScalarType.Float32));
      
      Console.WriteLine("Train data size: " + train_data.Count);
      Console.WriteLine("Test data size: " + test_data.Count);
      
      var batch_size = 100;
      // 分批加載圖像,打亂順序
      var train_loader = torch.utils.data.DataLoader(train_data, batchSize: batch_size, shuffle: true, defaultDevice);
      
      // 分批加載圖像,不打亂順序
      var test_loader = torch.utils.data.DataLoader(test_data, batchSize: batch_size, shuffle: false, defaultDevice);
      

      創建網絡模型

      由于 MNIST 數據集的圖像都是 28*28 的,因此我們創建網絡模型實例時,定義輸入層為 784 大小。

      // 輸入層大小,按圖片的寬高計算
      var input_size = 28 * 28;
      
      // 隱藏層大小,大小不固定,可以自己調整
      var hidden_size = 15;
      
      // 手動配置分類結果個數
      var num_classes = 10;
      
      var model = new MLP(input_size, hidden_size, num_classes);
      model.to(defaultDevice);
      

      定義損失函數

      創建損失函數和優化器,這個學習率的大小也是依據經驗和性能進行設置,沒有什么規律,學習率的作用可以參考梯度下降算法中的知識。

      // 創建損失函數
      var criterion = nn.CrossEntropyLoss();
      
      // 學習率
      var learning_rate = 0.001;
      
      // 優化器
      var optimizer = optim.Adam(model.parameters(), lr: learning_rate);
      

      訓練

      開始訓練模型,對數據集進行 10 輪訓練,每輪訓練都輸出訓練結果,這里不使用一張張圖片測試準確率,而是一次性識別所有圖片(一萬張),然后計算平均準確率。

      foreach (var epoch in Enumerable.Range(0, num_epochs))
      {
          model.train();
          int i = 0;
          foreach (var item in train_loader)
          {
              var images = item["data"];
              var lables = item["label"];
      
              images = images.reshape(-1, 28 * 28);
              var outputs = model.call(images);
      
              var loss = criterion.call(outputs, lables);
      
              optimizer.zero_grad();
      
              loss.backward();
      
              optimizer.step();
      
              i++;
              if ((i + 1) % 300 == 0)
              {
                  Console.WriteLine("Epoch [{(epoch + 1)}/{num_epochs}], Step [{(i + 1)}/{train_data.Count / batch_size}], Loss: {loss.ToSingle():F4}");
              }
          }
      
          model.eval();
          using (torch.no_grad())
          {
              long correct = 0;
              long total = 0;
      
              foreach (var item in test_loader)
              {
                  var images = item["data"];
                  var labels = item["label"];
      
                  images = images.reshape(-1, 28 * 28);
                  var outputs = model.call(images);
      
                  var (_, predicted) = torch.max(outputs, 1);
                  total += labels.size(0);
                  correct += (predicted == labels).sum().item<long>();
              }
              Console.WriteLine("Accuracy of the network on the 10000 test images: {100 * correct / total} %");
          }
      }
      

      保存訓練后的模型:

      model.save("mnist_mlp_model.dat");
      

      訓練信息:

      image-20250205144041513

      識別手寫圖像

      如下示例圖像所示,是一個手寫數字。

      0


      重新加載模型:

      model.save("mnist_mlp_model.dat");
      model.load("mnist_mlp_model.dat");
      
      
      // 把模型轉為評估模式
      model.eval();
      

      使用 Maomi.Torch 導入圖片并轉為 Tensor,然后將 28*28 轉換為以為的 784

      由于加載圖像的時候默認是彩色的,所以需要將其轉換為灰度圖像,即 channels=1

      // 加載圖片為張量
      var image = MM.LoadImage("5.jpg", channels: 1);
      image = image.to(defaultDevice);
      image = image.reshape(-1, 28 * 28);
      

      識別圖像并輸出結果:

      using (torch.no_grad())
      {
          var oputput = model.call(image);
          var prediction = oputput.argmax(dim: 1, keepdim: true);
          Console.WriteLine("Predicted Digit: " + prediction.item<long>().ToString());
      }
      

      當然,對應彩色的圖像,也可以這樣通過灰度轉換處理,再進行層歸一化,即可獲得對應結構的 torch.Tensor。

      image = image.reshape(-1, 28 * 28);
      
      var transform = transforms.ConvertImageDtype(ScalarType.Float32);
      var img = transform.call(image).unsqueeze(0);
      

      再如下圖所示,隨便搞了個數字,圖像是 212*212,圖像格式是 jpg。

      注意,由于數據集的圖片都是 jpg 格式,因此要識別的圖像,也需要使用 jpg 格式。

      6


      如下代碼所示,首先使用 Maomi.Torch 加載圖片,然后調整圖像大小為 28*28,以區配網絡模型的輸入層大小。

      // 加載圖片為張量
      image = MM.LoadImage("6.jpg", channels: 1);
      image = image.to(defaultDevice);
      
      // 將圖像轉換為 28*28 大小
      image = transforms.Resize(28, 28).call(image);
      image = image.reshape(-1, 28 * 28);
      
      using (torch.no_grad())
      {
          var oputput = model.call(image);
          var prediction = oputput.argmax(dim: 1, keepdim: true);
          Console.WriteLine("Predicted Digit: " + prediction.item<long>().ToString());
      }
      
      posted @ 2025-02-07 08:34  癡者工良  閱讀(1072)  評論(2)    收藏  舉報
      主站蜘蛛池模板: 国产蜜臀av在线一区在线 | 国产超碰人人爽人人做人人添| 国产999精品2卡3卡4卡| 少妇真人直播免费视频| jlzz大jlzz大全免费| 国产色婷婷亚洲99精品小说| 理论片午午伦夜理片久久| 公主岭市| 国产精品日韩专区第一页| 中文有无人妻vs无码人妻激烈| 4hu四虎永久免费地址ww416| 日本污视频在线观看| 国产精品自偷一区在线观看| 中文字幕无码视频手机免费看 | 精品人妻中文字幕有码在线| 国产成人综合色就色综合| 国产欧美日韩综合精品二区| 嫩草欧美曰韩国产大片| 国产午夜亚洲精品福利| 亚洲午夜久久久影院伊人| 欧洲美熟女乱又伦免费视频| 亚洲国产精品久久久久婷婷图片| 黄色舔女人逼一区二区三区| 日本另类αv欧美另类aⅴ| av无码精品一区二区乱子| 国产短视频一区二区三区| 免费观看的AV毛片的网站不卡 | 色伦专区97中文字幕| 美女裸体视频永久免费| 国产视频一区二区在线看| 国产亚洲亚洲国产一二区| 高h纯肉无码视频在线观看| 成人午夜福利精品一区二区| 无套内谢少妇一二三四| 99久久久国产精品免费蜜臀| 久久久精品2019中文字幕之3 | 成年女人午夜毛片免费视频| 亚洲精品国产第一区二区| 亚洲欧洲日韩国内精品| 麻豆一区二区三区香蕉视频| 日本一卡二卡不卡视频查询|