做外包网站网站开发网络公司兼职
做外包网站,网站开发网络公司兼职,做请帖的网站,网站建设是前端吗炮栋下静创建一个控制台项目#xff0c;示例代码参考 example2.2#xff0c;通过 nuget 引入以下类库#xff1a;
TorchSharp
TorchSharp-cuda-windows
TorchVision
Maomi.Torch
首先添加以下代码#xff0c;查找最适合当前设备的工作方式#xff0c;主要是选择 GPU 开发框…炮栋下静创建一个控制台项目示例代码参考 example2.2通过 nuget 引入以下类库TorchSharpTorchSharp-cuda-windowsTorchVisionMaomi.Torch首先添加以下代码查找最适合当前设备的工作方式主要是选择 GPU 开发框架例如 CUDA、MPSCPU有 GPU 就用 GPU没有 GPU 降级为 CPU。using Maomi.Torch;Device defaultDevice MM.GetOpTimalDevice();torch.set_default_device(defaultDevice);Console.WriteLine(当前正在使用 {defaultDevice});下载数据集训练模型最重要的一步是准备数据但是准备数据集是一个非常繁杂和耗时间的事情对于初学者来说也不现实所以 Pytorch 官方在框架集成了一些常见的数据集开发者可以直接通过 API 使用这些提前处理好的数据集和标签。Pytorch 使用 torch.utils.data.Dataset 表示数据集抽象接口存储了数据集的样本和对应标签torch.utils.data.DataLoader 表示加载数据集的抽象接口主要是提供了迭代器。这两套接口是非常重要的对于开发者自定义的数据集需要实现这两套接口自定义加载数据集方式。Pytorch 有三大领域的类库分别是 TorchText、TorchVision、TorchAudio这三个库都自带了一些常用开源数据集但是 .NET 里社区仓库只提供了 TorchVision生态严重落后于 Pytorch。TorchVision 是一个工具集可以从 Fashion-MNIST 等下载数据集以及进行一些数据类型转换等功能。在本章中使用的数据集叫 FashionMNISTPytorch 还提供了很多数据集感兴趣的读者参考https://pytorch.org/vision/stable/datasets.html现在开始讲解如何通过 TorchSharp 框架加载 FashionMNIST 数据集首先添加引用using TorchSharp;using static TorchSharp.torch;using datasets TorchSharp.torchvision.datasets;using transforms TorchSharp.torchvision.transforms;然后通过接口加载训练数据集和测试数据集// 指定训练数据集var training_data datasets.FashionMNIST(root: data, // 数据集在那个目录下train: true, // 加载该数据集用于训练download: true, // 如果数据集不存在是否下载target_transform: transforms.ConvertImageDtype(ScalarType.Float32) // 指定特征和标签转换将标签转换为Float32);// 指定测试数据集var test_data datasets.FashionMNIST(root: data, // 数据集在那个目录下train: false, // 加载该数据集用于训练download: true, // 如果数据集不存在是否下载target_transform: transforms.ConvertImageDtype(ScalarType.Float32) // 指定特征和标签转换将标签转换为Float32);部分参数解释如下root 是存放训练/测试数据的路径。train 指定训练或测试数据集。downloadTrue 如果 root 中没有数据则从互联网下载数据。transform 和 target_transform 指定特征和标签转换。注意与 Python 版本有所差异 Pytorch 官方给出了 ToTensor() 函数用于将图像转换为 torch.Tensor 张量类型但是由于 C# 版本并没有这个函数因此只能手动指定一个转换器。启动项目会自动下载数据集接着在程序运行目录下会自动创建一个 data 目录里面是数据集文件包括用于训练的数据和测试的数据集。image-20241202120839339文件内容如下所示子目录 test_data 里面的是测试数据集用于检查模型训练情况和优化。│ t10k-images-idx3-ubyte.gz│ t10k-labels-idx1-ubyte.gz│ train-images-idx3-ubyte.gz│ train-labels-idx1-ubyte.gz│└───test_datat10k-images-idx3-ubytet10k-labels-idx1-ubytetrain-images-idx3-ubytetrain-labels-idx1-ubyte显示图片数据集是 Dataset 类型继承了 Dataset 类型Dataset 本质是列表我们把 Dataset 列表的 item 称为数据每个 item 都是一个字典类型每个字典由 data、label 两个 key 组成。在上一节已经编写好如何加载数据集将训练数据和测试数据分开加载为了了解 Dataset 读者可以通过以下代码将数据集的结构打印到控制台。for (int i 0; i training_data.Count; i){var dic training_data.GetTensor(i);var img dic[data];var label dic[label];label.print();}通过观察控制台可以知道每个数据元素都是一个字典每个字典由 data、label 两个 key 组成,dic[data] 是一个图片而 label 就是表示该图片的文本值是什么。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];if (i 2){break;}img.ShowImage();}使用 Maomi.ScottPlot.Winforms 库还可以通过 img.ShowImageToForm() 接口通过窗口的形式显示图片。你也可以直接转存为图片img.SavePng(data/{i}.png);image-20250204215615584加载数据集由于 FashionMNIST 数据集有 6 万张图片一次性加载所有图片比较消耗内存并且一次性训练对 GPU 的要求也很高因此我们需要分批处理数据集。torch.utils.data 中有数据加载器可以帮助我们分批加载图片集到内存中开发时使用迭代器直接读取不需要关注分批情况。如下面所示分批加载数据集批处理大小是 64 张图片。// 分批加载图像打乱顺序var train_loader torch.utils.data.DataLoader(training_data, batchSize: 64, shuffle: true, device: defaultDevice);// 分批加载图像不打乱顺序var test_loader torch.utils.data.DataLoader(test_data, batchSize: 64, shuffle: false, device: defaultDevice);注意分批是在 DataLoader 内部发生的我们可以理解为缓冲区大小对于开发者来说并不需要关注分批情况。定义网络接下来定义一个神经网络神经网络有多个层通过神经网络来训练数据通过数据的训练可以的出参数、权重等信息这些信息会被保存到模型中加载模型时必须要有对应的网络结构比如神经网络的层数要相同、每层的结构一致。该网络通过接受 28*28 大小的图片经过处理后输出 10 个分类值每个分类结果都带有其可能的概率概率最高的就是识别结果。将以下代码存储到 NeuralNetwork.cs 中。using TorchSharp.Modules;using static TorchSharp.torch;using nn TorchSharp.torch.nn;public class NeuralNetwork : nn.Module{// 传递给基类的参数是模型的名称public NeuralNetwork() : base(nameof(NeuralNetwork)){flatten nn.Flatten();linear_relu_stack nn.Sequential(nn.Linear(28 * 28, 512),nn.ReLU(),nn.Linear(512, 512),nn.ReLU(),nn.Linear(512, 10));// C# 版本需要调用这个函数将模型的组件注册到模型中RegisterComponents();}Flatten flatten;Sequential linear_relu_stack;public override Tensor forward(Tensor input){// 将输入一层层处理并传递给下一层var x flatten.call(input);var logits linear_relu_stack.call(x);return logits;}}注意网络中只能定义字段不要定义属性不要使用 _ 开头定义字段然后继续在 Program 里继续编写代码初始化神经网络并使用 GPU 来加载网络。var model new NeuralNetwork();model.to(defaultDevice);优化模型参数为了训练模型需要定义一个损失函数和一个优化器损失函数的主要作用是衡量模型的预测结果与真实标签之间的差异即误差或损失有了损失函数后通过优化器可以指导模型参数的调整使预测结果能够逐步靠近真实值从而提高模型的性能。Pytorch 自带很多损失函数这里使用计算交叉熵损失的损失函数。// 定义损失函数、优化器和学习率var loss_fn nn.CrossEntropyLoss();var optimizer torch.optim.SGD(model.parameters(), learningRate : 1e-3);同时优化器也很重要是用于调整模型参数以最小化损失函数的模块。因为损失函数比较多但是优化器就那么几个所以这里简单列一下 Pytorch 中自带的一些优化器。SGD随机梯度下降通过按照损失函数的梯度进行线性步长更新权重Adam自适应矩估计 基于一阶和二阶矩估计的优化算法它能自适应地调整学习率对大多数问题效果较好RMSprop适用于处理非平稳目标能够自动进行学习率的调整AdamW带权重衰减的 Adam 在 Adam 的基础上添加了权重衰减weight decay防止过拟合。训练模型接下来讲解训练模型的步骤如下代码所示。下面是详细步骤每读取一张图片就使用神经网络进行识别(.call() 函数)pred 为识别结果通过损失函数判断网络的识别结果和标签值的误差通过损失函数反向传播计算网络的梯度等通过 SGD 优化器按照损失函数的梯度进行线性步长更新权重optimizer.step() 会调整模型的权重根据计算出来的梯度来更新模型的参数使模型逐步接近优化目标。因为数据是分批处理的因此计算当前批次的梯度后需要使用 optimizer.zero_grad() 重置当前所有梯度。计算训练成果即打印当前训练进度和损失值。static void Train(DataLoader dataloader, NeuralNetwork model, CrossEntropyLoss loss_fn, SGD optimizer){var size dataloader.dataset.Count;model.train();int batch 0;foreach (var item in dataloader){var x item[data];var y item[label];// 第一步// 训练当前图片var pred model.call(x);// 通过损失函数得出与真实结果的误差var loss loss_fn.call(pred, y);// 第二步反向传播loss.backward();// 计算梯度并优化参数optimizer.step();// 清空优化器当前的梯度optimizer.zero_grad();// 每 100 次打印损失值和当前训练的图片数量if (batch % 100 0){loss loss.item();// Pytorch 框架会在 x.shape[0] 存储当前批的位置var current (batch 1) * x.shape[0];Console.WriteLine(loss: {loss.item(),7} [{current,5}/{size,5}]);}batch;}}torch.Tensor 类型的 .shape 属性比较特殊是一个数组类型主要用于存储当前类型的结构要结合上下文才能判断例如在当前训练中x.shape 值是 [64,1,28,28]shape[1] 是图像的通道1 是灰色3 是彩色(RGB三通道)shape[2]、shape[3] 分别是图像的长度和高度。通过上面步骤可以看出“训练” 是一个字面意思跟人类的学习不一样这里是先使用模型识别一个图片然后计算误差更新模型参数和权重然后进入下一次调整。训练模型的同时我们还需要评估模型的准确率等信息评估时需要使用测试图片来验证训练结果。static void Test(DataLoader dataloader, NeuralNetwork model, CrossEntropyLoss loss_fn){var size (int)dataloader.dataset.Count;var num_batches (int)dataloader.Count;// 将模型设置为评估模式model.eval();var test_loss 0F;var correct 0F;using (var n torch.no_grad()){foreach (var item in dataloader){var x item[data];var y item[label];// 使用已训练的参数预测测试数据var pred model.call(x);// 计算损失值test_loss loss_fn.call(pred, y).item();correct (pred.argmax(1) y).type(ScalarType.Float32).sum().item();}}test_loss / num_batches;correct / size;Console.WriteLine(Test Error: \n Accuracy: {(100 * correct):F1}%, Avg loss: {test_loss:F8} \n);}下图是后面训练打印的日志可以看出准确率是逐步上升的。image-20250205090040316在 Program 中添加训练代码我们使用训练数据集进行五轮训练每轮训练都输出识别结果。// 训练的轮数var epochs 5;foreach (var epoch in Enumerable.Range(0, epochs)){Console.WriteLine(Epoch {epoch 1}\n-------------------------------);Train(train_loader, model, loss_fn, optimizer);Test(train_loader, model, loss_fn);}Console.WriteLine(Done!);保存和加载模型经过训练后的模型可以直接保存和加载代码很简单如下所示model.save(model.dat);Console.WriteLine(Saved PyTorch Model State to model.dat);model.load(model.dat);使用模型识别图片要使用模型识别图片只需要使用 var pred model.call(x); 即可但是因为模型并不能直接输出识别结果而是根据网络结构输出到每个神经元中每个神经元都表示当前概率。在前面定义的网络中nn.Linear(512, 10)) 会输出 10 个分类结果每个分类结果都带有概率那么我们将概率最高的一个结果拿出来就相当于图片的识别结果了。代码如下所示步骤讲解如下因为模型和网络并不使用字符串表示每个分类结果所以需要手动配置分类表。然后从测试数据集中选取第一个图片和标签识别图片并获得序号。从分类字符串中通过序号获得分类名称。var classes new string[] {T-shirt/top,Trouser,Pullover,Dress,Coat,Sandal,Shirt,Sneaker,Bag,Ankle boot,};// 设置为评估模式model.eval();// 加载测试数据中的第一个图片以及其标签var x test_data.GetTensor(0)[data];var y test_data.GetTensor(0)[label];using (torch.no_grad()){x x.to(defaultDevice);var pred model.call(x);var predicted classes[pred[0].argmax(0).ToInt32()];var actual classes[y.ToInt32()];Console.WriteLine(Predicted: \{predicted}\, Actual: \{actual}\);}当然使用 Maomi.Torch 的接口可以很方便读取图片使用模型识别var img MM.LoadImage(0.png);using (torch.no_grad()){img img.to(defaultDevice);var pred model.call(img);// 转换为归一化的概率var array torch.nn.functional.softmax(pred, dim: 0);var max array.ToFloat32Array().Max();var predicted classes[pred[0].argmax(0).ToInt32()];Console.WriteLine(识别结果 {predicted}概率 {max * 100}%);